/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 | | */ |