Coverage Report

Created: 2025-02-15 06:25

/src/wireshark/wiretap/rfc7468.c
Line
Count
Source (jump to first uncovered line)
1
/* rfc7468.c
2
 *
3
 * Implements loading of files in the format specified by RFC 7468.
4
 *
5
 * SPDX-License-Identifier: GPL-2.0-or-later
6
 */
7
8
#include "config.h"
9
10
#include "rfc7468.h"
11
12
#include "file_wrappers.h"
13
#include "wtap-int.h"
14
15
#include <wsutil/buffer.h>
16
17
#include <glib.h>
18
19
#include <string.h>
20
21
static int rfc7468_file_type_subtype = -1;
22
23
void register_rfc7468(void);
24
25
enum line_type {
26
    LINE_TYPE_PREEB,
27
    LINE_TYPE_POSTEB,
28
    LINE_TYPE_OTHER,
29
};
30
31
const char PREEB_BEGIN[] = "-----BEGIN ";
32
0
#define PREEB_BEGIN_LEN (sizeof PREEB_BEGIN - 1)
33
const char POSTEB_BEGIN[] = "-----END ";
34
0
#define POSTEB_BEGIN_LEN (sizeof POSTEB_BEGIN - 1)
35
36
static bool rfc7468_read_line(FILE_T fh, enum line_type *line_type,
37
    Buffer *buf, int* err, char** err_info)
38
0
{
39
    /* Make the chunk size large enough that most lines can fit in a single chunk.
40
       Strict RFC 7468 syntax only allows up to 64 characters per line, but we provide
41
       some leeway to accommodate nonconformant producers and explanatory text.
42
       The 3 extra bytes are for the trailing CR+LF and NUL terminator. */
43
0
    char line_chunk[128 + 3];
44
0
    char *line_chunk_end;
45
46
0
    if (!(line_chunk_end = file_getsp(line_chunk, sizeof line_chunk, fh))) {
47
0
        *err = file_error(fh, err_info);
48
0
        return false;
49
0
    }
50
51
    // First chunk determines the line type.
52
0
    if (memcmp(line_chunk, PREEB_BEGIN, PREEB_BEGIN_LEN) == 0)
53
0
        *line_type = LINE_TYPE_PREEB;
54
0
    else if (memcmp(line_chunk, POSTEB_BEGIN, POSTEB_BEGIN_LEN) == 0)
55
0
        *line_type = LINE_TYPE_POSTEB;
56
0
    else
57
0
        *line_type = LINE_TYPE_OTHER;
58
59
0
    for (;;) {
60
0
        size_t line_chunk_len = line_chunk_end - line_chunk;
61
0
        if (line_chunk_len > INT_MAX - ws_buffer_length(buf)) {
62
0
            *err = WTAP_ERR_BAD_FILE;
63
0
            *err_info = g_strdup_printf(
64
0
                "File contains an encoding larger than the maximum of %d bytes",
65
0
                INT_MAX);
66
0
            return false;
67
0
        }
68
69
0
        ws_buffer_append(buf, line_chunk, line_chunk_len);
70
71
0
        if (line_chunk_end[-1] == '\n' || file_eof(fh))
72
0
            break;
73
74
0
        if (!(line_chunk_end = file_getsp(line_chunk, sizeof line_chunk, fh))) {
75
0
            *err = file_error(fh, err_info);
76
0
            return false;
77
0
        }
78
0
    }
79
80
0
    return true;
81
0
}
82
83
static bool rfc7468_read_impl(FILE_T fh, wtap_rec *rec,
84
    int *err, char **err_info)
85
0
{
86
0
    ws_buffer_clean(&rec->data);
87
88
0
    bool saw_preeb = false;
89
90
0
    for (;;) {
91
0
        enum line_type line_type;
92
93
0
        if (!rfc7468_read_line(fh, &line_type, &rec->data, err, err_info)) {
94
0
            if (*err != 0 || !saw_preeb) return false;
95
96
0
            *err = WTAP_ERR_BAD_FILE;
97
0
            *err_info = g_strdup("Missing post-encapsulation boundary at end of file");
98
0
            return false;
99
0
        }
100
101
0
        if (saw_preeb) {
102
0
            if (line_type == LINE_TYPE_POSTEB) break;
103
0
        } else {
104
0
            if (line_type == LINE_TYPE_PREEB) saw_preeb = true;
105
0
        }
106
0
    }
107
108
0
    rec->rec_type = REC_TYPE_PACKET;
109
0
    rec->presence_flags = 0;
110
0
    rec->ts.secs = 0;
111
0
    rec->ts.nsecs = 0;
112
0
    rec->rec_header.packet_header.caplen = (uint32_t)ws_buffer_length(&rec->data);
113
0
    rec->rec_header.packet_header.len = (uint32_t)ws_buffer_length(&rec->data);
114
115
0
    return true;
116
0
}
117
118
static bool rfc7468_read(wtap *wth, wtap_rec *rec,
119
    int *err, char **err_info, int64_t *data_offset)
120
0
{
121
0
    *data_offset = file_tell(wth->fh);
122
123
0
    return rfc7468_read_impl(wth->fh, rec, err, err_info);
124
0
}
125
126
static bool rfc7468_seek_read(wtap *wth, int64_t seek_off, wtap_rec *rec,
127
    int *err, char **err_info)
128
0
{
129
0
    if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) < 0)
130
0
        return false;
131
132
0
    return rfc7468_read_impl(wth->random_fh, rec, err, err_info);
133
0
}
134
135
wtap_open_return_val rfc7468_open(wtap *wth, int *err, char **err_info)
136
0
{
137
    /* To detect whether this file matches our format, we need to find the
138
       first pre-encapsulation boundary, which may be located anywhere in the file,
139
       since it may be preceded by explanatory text. However, we don't want to
140
       read the entire file to find it, since the file may be huge, and detection
141
       needs to be fast. Therefore, we'll assume that if the boundary exists,
142
       it's located within a small initial chunk of the file. The size of
143
       the chunk was chosen arbitrarily. */
144
0
    char initial_chunk[2048];
145
0
    int initial_chunk_size = file_read(&initial_chunk, sizeof initial_chunk, wth->fh);
146
147
0
    if (initial_chunk_size < 0) {
148
0
        *err = file_error(wth->fh, err_info);
149
0
        return WTAP_OPEN_ERROR;
150
0
    }
151
152
0
    char *chunk_end_ptr = initial_chunk + initial_chunk_size;
153
154
    // Try to find a line that starts with PREEB_BEGIN in the initial chunk.
155
0
    for (char *line_ptr = initial_chunk; ; ) {
156
0
        if ((unsigned)(chunk_end_ptr - line_ptr) < PREEB_BEGIN_LEN)
157
0
            return WTAP_OPEN_NOT_MINE;
158
159
0
        if (memcmp(line_ptr, PREEB_BEGIN, PREEB_BEGIN_LEN) == 0)
160
0
            break;
161
162
        // Try next line.
163
0
        char *lf_ptr = memchr(line_ptr, '\n', chunk_end_ptr - line_ptr);
164
0
        if (!lf_ptr)
165
0
            return WTAP_OPEN_NOT_MINE;
166
0
        line_ptr = lf_ptr + 1;
167
0
    }
168
169
0
    if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
170
0
        return WTAP_OPEN_ERROR;
171
172
0
    wth->file_type_subtype = rfc7468_file_type_subtype;
173
0
    wth->file_encap = WTAP_ENCAP_RFC7468;
174
175
0
    wth->snapshot_length = 0;
176
0
    wth->file_tsprec = WTAP_TSPREC_SEC;
177
178
0
    wth->subtype_read = rfc7468_read;
179
0
    wth->subtype_seek_read = rfc7468_seek_read;
180
181
0
    return WTAP_OPEN_MINE;
182
0
}
183
184
static const struct supported_block_type rfc7468_blocks_supported[] = {
185
    /*
186
     * We provide one "packet" for each encoded structure in the file,
187
     * and don't support any options.
188
     */
189
    { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
190
};
191
192
static const struct file_type_subtype_info rfc7468_info = {
193
    "RFC 7468 files", "rfc7468", NULL, NULL,
194
    false, BLOCKS_SUPPORTED(rfc7468_blocks_supported),
195
    NULL, NULL, NULL
196
};
197
198
void register_rfc7468(void)
199
14
{
200
14
    rfc7468_file_type_subtype = wtap_register_file_type_subtype(&rfc7468_info);
201
202
    /*
203
     * Register name for backwards compatibility with the
204
     * wtap_filetypes table in Lua.
205
     */
206
14
    wtap_register_backwards_compatibility_lua_name("RFC7468",
207
14
                                                   rfc7468_file_type_subtype);
208
14
}
209
210
/*
211
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
212
 *
213
 * Local Variables:
214
 * c-basic-offset: 4
215
 * tab-width: 8
216
 * indent-tabs-mode: nil
217
 * End:
218
 *
219
 * vi: set shiftwidth=4 tabstop=8 expandtab:
220
 * :indentSize=4:tabSize=8:noTabs=true:
221
 */