2 * Routines for dissection of files in the format specified by RFC 7468.
4 * Wireshark - Network traffic analyzer
5 * By Gerald Combs <gerald@wireshark.org>
6 * Copyright 1998 Gerald Combs
8 * SPDX-License-Identifier: GPL-2.0-or-later
13 #include <epan/packet.h>
14 #include <wiretap/wtap.h>
15 #include <wsutil/array.h>
17 void proto_register_rfc7468(void);
18 void proto_reg_handoff_rfc7468(void);
20 static int proto_rfc7468
;
22 static int ett_rfc7468
;
23 static int ett_rfc7468_preeb
;
24 static int ett_rfc7468_data
;
25 static int ett_rfc7468_posteb
;
27 static int hf_rfc7468_preeb_label
;
28 static int hf_rfc7468_ber_data
;
29 static int hf_rfc7468_posteb_label
;
31 static dissector_handle_t rfc7468_handle
;
32 static dissector_handle_t ber_handle
;
34 static dissector_table_t rfc7468_label_table
;
37 line_is_eb(const unsigned char *line
, int linelen
, const char *prefix
,
38 size_t prefixlen
, const unsigned char **labelpp
, int *labellenp
)
40 static const char suffix
[] = "-----";
41 #define suffixlen (sizeof suffix - 1)
42 const unsigned char *labelp
;
46 * Is this line an encapulation boundary of the type specified by the
49 * First, it must be big enough to include the prefix at the beginning
50 * and the suffix at the end.
52 if ((size_t)linelen
< prefixlen
+ suffixlen
) {
54 * No - it's too short.
60 * It is, but it must begin with the prefix.
62 if (memcmp(line
, prefix
, prefixlen
) != 0) {
64 * No - it doesn't begin with the prefix.
70 * It does, but it must also end with the suffix.
72 if (memcmp(line
+ linelen
- suffixlen
, suffix
, suffixlen
) != 0) {
74 * No - it doesn't end with the suffix.
80 * It begins with the prefix and ends with the suffix. Check
81 * the label, if there is one.
83 labelp
= line
+ prefixlen
;
84 labellen
= (int)(linelen
- (prefixlen
+ suffixlen
));
86 *labellenp
= labellen
;
88 /* The label is empty. */
93 * The first character of the label must be 0x21-0x2C or 0x2E-0x7F,
94 * i.e., printable ASCII other than SP or '-'.
96 if (*labelp
== ' ' || *labelp
== '-')
102 * The rest of the characters must be printable ASCII.
104 for (int i
= 0; i
< labellen
; i
++, labelp
++) {
105 if (*labelp
< 0x20 || *labelp
> 0x7E) {
106 /* Not printable ASCII. */
114 line_is_blank(const unsigned char *line
, int linelen
)
116 const unsigned char *p
;
119 for (int i
= 0; i
< linelen
; i
++, p
++) {
120 if (*p
!= ' ' && *p
!= '\t') {
121 /* Not space or tab */
128 static const char preeb_prefix
[] = "-----BEGIN ";
129 #define preeb_prefix_len (sizeof preeb_prefix - 1)
130 static const char posteb_prefix
[] = "-----END ";
131 #define posteb_prefix_len (sizeof posteb_prefix - 1)
134 dissect_rfc7468(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
)
139 const unsigned char *line
;
140 const unsigned char *labelp
= NULL
;
143 proto_tree
*rfc7468_tree
, *preeb_tree
, *posteb_tree
;
144 proto_item
*rfc7468_item
, *ti
;
147 rfc7468_item
= proto_tree_add_item(tree
, proto_rfc7468
, tvb
, offset
, -1, ENC_NA
);
148 rfc7468_tree
= proto_item_add_subtree(rfc7468_item
, ett_rfc7468
);
150 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "rfc7468");
153 * First, process the text lines prior to the pre-encapsulation
154 * boundary; they're explanatory text lines.
156 while (tvb_offset_exists(tvb
, offset
)) {
157 linelen
= tvb_find_line_end(tvb
, offset
, -1, &next_offset
, false);
159 /* No complete line was found. Nothing more to do. */
160 return tvb_captured_length(tvb
);
164 * Get a buffer that refers to the line.
166 * Note that "tvb_find_line_end()" will return a value that
167 * is not longer than what's in the buffer, so the
168 * "tvb_get_ptr()" call won't throw an exception.
170 line
= tvb_get_ptr(tvb
, offset
, linelen
);
173 * Is this line a pre-encapulation boundary?
175 if (line_is_eb(line
, linelen
, preeb_prefix
, sizeof preeb_prefix
- 1,
176 &labelp
, &labellen
)) {
178 * Yes - we're finished with the explanatory text lines.
184 * Add this line to the dissection.
186 proto_tree_add_format_text(rfc7468_tree
, tvb
, offset
, next_offset
- offset
);
189 * Step to the next line.
191 offset
= next_offset
;
195 * This line is the pre-encapsulation boundary.
196 * Put it into the protocol tree, and create a subtree under it.
198 ti
= proto_tree_add_format_text(rfc7468_tree
, tvb
, offset
, next_offset
- offset
);
199 preeb_tree
= proto_item_add_subtree(ti
, ett_rfc7468_preeb
);
202 * Extract the label, and put it in that subtree.
204 label
= wmem_strndup(pinfo
->pool
, labelp
, labellen
);
205 proto_tree_add_item(preeb_tree
, hf_rfc7468_preeb_label
, tvb
,
206 offset
+ (int)preeb_prefix_len
, labellen
, ENC_ASCII
);
208 col_add_fstr(pinfo
->cinfo
, COL_INFO
, "Label: %s", label
);
211 * Step to the next line.
213 offset
= next_offset
;
216 * Skip over any blank lines before the base64 information.
218 while (tvb_offset_exists(tvb
, offset
)) {
219 linelen
= tvb_find_line_end(tvb
, offset
, -1, &next_offset
, false);
221 /* No complete line was found. We're done. */
222 return tvb_captured_length(tvb
);
226 * Get a buffer that refers to the line.
228 * Note that "tvb_find_line_end()" will return a value that
229 * is not longer than what's in the buffer, so the
230 * "tvb_get_ptr()" call won't throw an exception.
232 line
= tvb_get_ptr(tvb
, offset
, linelen
);
235 * Is the line entirely blank (space or tab)?
237 if (!line_is_blank(line
, linelen
)) {
245 * Add this line to the dissection.
247 proto_tree_add_format_text(rfc7468_tree
, tvb
, offset
, next_offset
- offset
);
250 * Step to the next line.
252 offset
= next_offset
;
256 * OK, this should be base64-encoded binary data.
258 uint8_t *databuf
= NULL
;
259 size_t databufsize
= 0;
260 int base64_state
= 0;
261 unsigned base64_save
= 0;
262 unsigned datasize
= 0;
263 while (tvb_offset_exists(tvb
, offset
)) {
264 linelen
= tvb_find_line_end(tvb
, offset
, -1, &next_offset
, false);
267 * No complete line was found. Nothing more to do.
269 return tvb_captured_length(tvb
);
273 * Get a buffer that refers to the line.
275 * Note that "tvb_find_line_end()" will return a value that
276 * is not longer than what's in the buffer, so the
277 * "tvb_get_ptr()" call won't throw an exception.
279 line
= tvb_get_ptr(tvb
, offset
, linelen
);
282 * Is this line a post-encapulation boundary?
284 if (line_is_eb(line
, linelen
, posteb_prefix
, sizeof posteb_prefix
- 1,
285 &labelp
, &labellen
)) {
287 * Yes - we're done with the base64 data.
293 * Add this line to the dissection.
295 proto_tree_add_format_text(rfc7468_tree
, tvb
, offset
, next_offset
- offset
);
298 * Decode it and add that to the buffer.
299 * First, grow the buffer as needed.
301 databufsize
+= (linelen
/ 4) * 3 + 3;
302 databuf
= (uint8_t *)wmem_realloc(pinfo
->pool
, databuf
, databufsize
);
305 * Now decode into it.
307 unsigned decodesize
= (unsigned)g_base64_decode_step(line
, linelen
,
311 datasize
+= decodesize
;
314 * Step to the next line.
316 offset
= next_offset
;
320 * Make a tvbuff for the data, and put it into the protocol tree,
326 data_tvb
= tvb_new_child_real_data(tvb
, databuf
, datasize
, datasize
);
327 add_new_data_source(pinfo
, data_tvb
, "Base64-encoded data");
330 * Try to decode it based on the label.
332 if (dissector_try_string_with_data(rfc7468_label_table
, label
, data_tvb
, pinfo
,
333 tree
, true, NULL
) == 0) {
334 proto_tree
*data_tree
;
337 * No known dissector; decode it as BER.
339 ti
= proto_tree_add_item(tree
, hf_rfc7468_ber_data
, data_tvb
, 0, -1, ENC_NA
);
340 data_tree
= proto_item_add_subtree(ti
, ett_rfc7468_data
);
341 call_dissector(ber_handle
, data_tvb
, pinfo
, data_tree
);
346 * This line is the post-encapsulation boundary.
347 * Put it into the protocol tree, and create a subtree under it.
349 ti
= proto_tree_add_format_text(rfc7468_tree
, tvb
, offset
, next_offset
- offset
);
350 posteb_tree
= proto_item_add_subtree(ti
, ett_rfc7468_posteb
);
353 * Extract the label, and put it in that subtree.
355 proto_tree_add_item(posteb_tree
, hf_rfc7468_posteb_label
, tvb
,
356 offset
+ (int)posteb_prefix_len
, labellen
, ENC_ASCII
);
358 return tvb_captured_length(tvb
);
362 // Arbitrary value - we don't want to read all of a huge non-RFC 7468 file
363 // only to find no pre-encapsulation boundary.
365 #define MAX_EXPLANATORY_TEXT_LINES 20
368 dissect_rfc7468_heur(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
)
373 const unsigned char *line
;
374 const unsigned char *labelp
;
379 * Look for a pre-encapsulation boundary.
380 * Process up to MAX_EXPLANATORY_TEXT_LINES worth of lines that don't
381 * look like pre-encapsulation boundaries.
384 for (unsigned int i
= 0; i
< MAX_EXPLANATORY_TEXT_LINES
; i
++) {
385 linelen
= tvb_find_line_end(tvb
, offset
, -1, &next_offset
, false);
388 * No complete line was found; we ran out of file data
389 * and didn't find a pre-encapsulation boundary, so this
390 * isn't an RFC 7468 file.
396 * Get a buffer that refers to the line.
398 * Note that "tvb_find_line_end()" will return a value that
399 * is not longer than what's in the buffer, so the
400 * "tvb_get_ptr()" call won't throw an exception.
402 line
= tvb_get_ptr(tvb
, offset
, linelen
);
405 * Is this line a pre-encapulation boundary?
407 if (line_is_eb(line
, linelen
, preeb_prefix
, sizeof preeb_prefix
- 1,
408 &labelp
, &labellen
)) {
410 * Yes - we're done looking.
417 * Step to the next line.
419 offset
= next_offset
;
423 * Did we find a pre-encapsulation boundary?
426 return false; /* no */
429 * OK, it's an RFC 7468 file. Dissect it.
431 dissect_rfc7468(tvb
, pinfo
, tree
, data
);
436 proto_register_rfc7468(void)
438 static hf_register_info hf
[] = {
439 { &hf_rfc7468_preeb_label
,
440 { "Pre-encapsulation boundary label", "rfc7468.preeb_label", FT_STRING
, BASE_NONE
,
441 NULL
, 0, NULL
, HFILL
} },
442 { &hf_rfc7468_ber_data
,
443 { "BER data", "rfc7468.ber_data", FT_NONE
, BASE_NONE
,
444 NULL
, 0, NULL
, HFILL
} },
445 { &hf_rfc7468_posteb_label
,
446 { "Post-encapsulation boundary label", "rfc7468.posteb_label", FT_STRING
, BASE_NONE
,
447 NULL
, 0, NULL
, HFILL
} },
450 static int *ett
[] = {
457 proto_rfc7468
= proto_register_protocol("RFC 7468 file format", "rfc7468", "rfc7468");
459 proto_register_field_array(proto_rfc7468
, hf
, array_length(hf
));
460 proto_register_subtree_array(ett
, array_length(ett
));
462 rfc7468_label_table
= register_dissector_table("rfc7468.preeb_label", "FFF",
463 proto_rfc7468
, FT_STRING
,
464 STRING_CASE_INSENSITIVE
);
466 rfc7468_handle
= register_dissector("rfc7468", dissect_rfc7468
, proto_rfc7468
);
470 proto_reg_handoff_rfc7468(void)
472 heur_dissector_add("wtap_file", dissect_rfc7468_heur
, "RFC 7468 file", "rfc7468_wtap", proto_rfc7468
, HEURISTIC_ENABLE
);
473 dissector_add_uint("wtap_encap", WTAP_ENCAP_RFC7468
, rfc7468_handle
);
475 ber_handle
= find_dissector("ber");
480 * Editor modelines - https://www.wireshark.org/tools/modelines.html
485 * indent-tabs-mode: nil
488 * vi: set shiftwidth=4 tabstop=8 expandtab:
489 * :indentSize=4:tabSize=8:noTabs=true: