Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / epan / dissectors / packet-multipart.c
blob6e0dd5c4923b283ab119078d9f180c883f1b442a
1 /* packet-multipart.c
2 * Routines for multipart media encapsulation dissection
3 * Copyright 2004, Anders Broman.
4 * Copyright 2004, Olivier Biot.
6 * Refer to the AUTHORS file or the AUTHORS section in the man page
7 * for contacting the author(s) of this file.
9 * Wireshark - Network traffic analyzer
10 * By Gerald Combs <gerald@wireshark.org>
11 * Copyright 1998 Gerald Combs
14 * SPDX-License-Identifier: GPL-2.0-or-later
16 * References for "media-type multipart/mixed :
17 * https://www.iana.org/assignments/media-types/index.html
18 * https://tools.ietf.org/html/rfc2045
19 * https://tools.ietf.org/html/rfc2046
20 * https://tools.ietf.org/html/rfc2047
21 * https://tools.ietf.org/html/rfc2048
22 * https://tools.ietf.org/html/rfc2049
24 * Part of the code is modeled from the SIP and HTTP dissectors
26 * General format of a MIME multipart document:
27 * [ preamble line-end ]
28 * dash-boundary transport-padding line-end
29 * body-part
30 * *encapsulation
31 * close-delimiter transport-padding
32 * [ line-end epilogue ]
34 * Where:
35 * dash-boundary := "--" boundary
36 * encapsulation := delimiter transport-padding line-end body-part
37 * delimiter := line-end body-part
38 * close-delimiter := delimiter "--"
39 * body-part := MIME-part-headers [ line-end *OCTET ]
40 * transport-padding := *LWSP-char
42 * Note that line-end is often a LF instead of a CRLF.
45 #include "config.h"
47 #include <epan/packet.h>
48 #include <epan/expert.h>
49 #include <epan/media_params.h>
50 #include <epan/prefs.h>
51 #include <wsutil/str_util.h>
52 #include "packet-imf.h"
54 #include <wsutil/array.h>
56 #include "packet-gssapi.h"
57 #include "packet-media-type.h"
59 void proto_register_multipart(void);
60 void proto_reg_handoff_multipart(void);
62 /* Dissector table for media requiring special attention in multipart
63 * encapsulation. */
64 static dissector_table_t multipart_media_subdissector_table;
66 /* Initialize the protocol and registered fields */
67 static int proto_multipart;
69 /* Generated from convert_proto_tree_add_text.pl */
70 static int hf_multipart_trailer;
71 static int hf_multipart_boundary;
72 static int hf_multipart_first_boundary;
73 static int hf_multipart_last_boundary;
74 static int hf_multipart_preamble;
76 /* Initialize the subtree pointers */
77 static int ett_multipart;
78 static int ett_multipart_main;
79 static int ett_multipart_body;
81 /* Generated from convert_proto_tree_add_text.pl */
82 static expert_field ei_multipart_no_required_parameter;
83 static expert_field ei_multipart_decryption_not_possible;
85 /* Not sure that compact_name exists for multipart, but choose to keep
86 * the structure from SIP dissector, all the content- is also from SIP */
89 typedef struct {
90 const char *name;
91 const char *compact_name;
92 } multipart_header_t;
94 static const multipart_header_t multipart_headers[] = {
95 { "Unknown-header", NULL }, /* Pad so that the real headers start at index 1 */
96 { "Content-Description", NULL },
97 { "Content-Disposition", NULL },
98 { "Content-Encoding", "e" },
99 { "Content-Id", NULL },
100 { "Content-Language", NULL },
101 { "Content-Length", "l" },
102 { "Content-Transfer-Encoding", NULL },
103 { "Content-Type", "c" },
104 { "OriginalContent", NULL }
107 #define POS_CONTENT_DESCRIPTION 1
108 #define POS_CONTENT_DISPOSITION 2
109 #define POS_CONTENT_ENCODING 3
110 #define POS_CONTENT_ID 4
111 #define POS_CONTENT_LANGUAGE 5
112 #define POS_CONTENT_LENGTH 6
113 #define POS_CONTENT_TRANSFER_ENCODING 7
114 #define POS_CONTENT_TYPE 8
115 #define POS_ORIGINALCONTENT 9
117 /* Initialize the header fields */
118 static int hf_multipart_type;
119 static int hf_multipart_part;
120 static int hf_multipart_sec_token_len;
121 static int hf_header_array[array_length(multipart_headers)];
123 /* Define media_type/Content type table */
124 static dissector_table_t media_type_dissector_table;
126 /* Data and media dissector handles */
127 static dissector_handle_t multipart_handle;
128 static dissector_handle_t media_handle;
129 static dissector_handle_t gssapi_handle;
131 /* Determines if bodies with no media type dissector should be displayed
132 * as raw text, may cause problems with images sound etc
133 * TODO improve to check for different content types ?
135 static bool display_unknown_body_as_text;
136 static bool remove_base64_encoding;
137 #if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG)
138 static bool uncompress_data = true;
139 #endif
141 typedef struct {
142 const char *type; /* Type of multipart */
143 char *boundary; /* Boundary string (enclosing quotes removed if any) */
144 unsigned boundary_length; /* Length of the boundary string */
145 char *protocol; /* Protocol string if encrypted multipart (enclosing quotes removed if any) */
146 unsigned protocol_length; /* Length of the protocol string */
147 char *orig_content_type; /* Content-Type of original message */
148 char *orig_parameters; /* Parameters for Content-Type of original message */
149 } multipart_info_t;
153 static int
154 find_first_boundary(tvbuff_t *tvb, int start, const uint8_t *boundary,
155 int boundary_len, int *boundary_line_len, bool *last_boundary);
156 static int
157 find_next_boundary(tvbuff_t *tvb, int start, const uint8_t *boundary,
158 int boundary_len, int *boundary_line_len, bool *last_boundary);
159 static int
160 process_preamble(proto_tree *tree, tvbuff_t *tvb, multipart_info_t *m_info,
161 bool *last_boundary);
162 static int
163 process_body_part(proto_tree *tree, tvbuff_t *tvb,
164 media_content_info_t *input_content_info, multipart_info_t *m_info,
165 packet_info *pinfo, int start, int idx,
166 bool *last_boundary);
167 static int
168 is_known_multipart_header(const char *header_str, unsigned len);
171 /* Return a tvb that contains the binary representation of a base64
172 string */
174 static tvbuff_t *
175 base64_decode(packet_info *pinfo, tvbuff_t *b64_tvb, char *name)
177 char *data;
178 tvbuff_t *tvb;
179 data = tvb_get_string_enc(pinfo->pool, b64_tvb, 0, tvb_reported_length(b64_tvb), ENC_ASCII);
181 tvb = base64_to_tvb(b64_tvb, data);
182 add_new_data_source(pinfo, tvb, name);
184 return tvb;
188 * Unfold and clean up a MIME-like header, and process LWS as follows:
189 * o Preserves LWS in quoted text
190 * o Remove LWS before and after a separator
191 * o Remove trailing LWS
192 * o Replace other LWS with a single space
193 * Set value to the start of the value
194 * Return the cleaned-up RFC2822 header (buffer must be freed).
196 static char *
197 unfold_and_compact_mime_header(wmem_allocator_t *pool, const char *lines, int *first_colon_offset)
199 const char *p = lines;
200 char c;
201 char *ret, *q;
202 char sep_seen = 0; /* Did we see a separator ":;," */
203 char lws = false; /* Did we see LWS (incl. folding) */
204 int colon = -1;
206 if (! lines) return NULL;
208 c = *p;
209 ret = (char *)wmem_alloc(pool, strlen(lines) + 1);
210 q = ret;
212 while (c) {
213 if (c == ':') {
214 lws = false; /* Prevent leading LWS from showing up */
215 if (colon == -1) {/* First colon */
216 colon = (int) (q - ret);
218 *(q++) = sep_seen = c;
219 p++;
220 } else if (c == ';' || c == ',' || c == '=') {
221 lws = false; /* Prevent leading LWS from showing up */
222 *(q++) = sep_seen = c;
223 p++;
224 } else if (c == ' ' || c == '\t') {
225 lws = true;
226 p++;
227 } else if (c == '\n') {
228 lws = false; /* Skip trailing LWS */
229 if ((c = *(p+1))) {
230 if (c == ' ' || c == '\t') { /* Header unfolding */
231 lws = true;
232 p += 2;
233 } else {
234 *q = c = 0; /* Stop */
237 } else if (c == '\r') {
238 lws = false;
239 if ((c = *(p+1))) {
240 if (c == '\n') {
241 if ((c = *(p+2))) {
242 if (c == ' ' || c == '\t') { /* Header unfolding */
243 lws = true;
244 p += 3;
245 } else {
246 *q = c = 0; /* Stop */
249 } else if (c == ' ' || c == '\t') { /* Header unfolding */
250 lws = true;
251 p += 2;
252 } else {
253 *q = c = 0; /* Stop */
256 } else if (c == '"') { /* Start of quoted-string */
257 lws = false;
258 *(q++) = c;
259 while (c) {
260 c = *(q++) = *(++p);
261 if (c == '\\') {
262 /* First part of a quoted-pair; copy the other part,
263 without checking if it's a quote */
264 c = *(q++) = *(++p);
265 } else {
266 if (c == '"') {
267 p++; /* Skip closing quote */
268 break;
272 /* if already zero terminated now, rewind one char to avoid an "off by one" */
273 if(c == 0) {
274 q--;
276 } else { /* Regular character */
277 if (sep_seen) {
278 sep_seen = 0;
279 } else {
280 if (lws) {
281 *(q++) = ' ';
284 lws = false;
285 *(q++) = c;
286 p++; /* OK */
289 if (c) {
290 c = *p;
293 *q = 0;
295 *first_colon_offset = colon;
296 return (ret);
299 /* Retrieve the media information from pinfo->private_data,
300 * and compute the boundary string and its length.
301 * Return a pointer to a filled-in multipart_info_t, or NULL on failure.
303 * Boundary delimiters must not appear within the encapsulated material,
304 * and must be no longer than 70 characters, not counting the two
305 * leading hyphens. (quote from rfc2046)
307 static multipart_info_t *
308 get_multipart_info(packet_info *pinfo, media_content_info_t *content_info)
310 char *start_boundary, *start_protocol = NULL;
311 multipart_info_t *m_info = NULL;
312 const char *type = pinfo->match_string;
313 char *parameters;
314 int dummy;
317 * We need both a content type AND parameters
318 * for multipart dissection.
320 if (type == NULL) {
321 return NULL;
323 if (content_info == NULL) {
324 return NULL;
326 if (content_info->media_str == NULL) {
327 return NULL;
330 /* Clean up the parameters */
331 parameters = unfold_and_compact_mime_header(pinfo->pool, content_info->media_str, &dummy);
333 start_boundary = ws_find_media_type_parameter(pinfo->pool, parameters, "boundary");
334 if (!start_boundary) {
335 return NULL;
338 if (strncmp(type, "multipart/encrypted", sizeof("multipart/encrypted") - 1) == 0) {
339 start_protocol = ws_find_media_type_parameter(pinfo->pool, parameters, "protocol");
340 if (!start_protocol) {
341 return NULL;
346 * There is a value for the boundary string
348 m_info = wmem_new(pinfo->pool, multipart_info_t);
349 m_info->type = type;
350 m_info->boundary = start_boundary;
351 m_info->boundary_length = (unsigned)strlen(start_boundary);
352 if(start_protocol) {
353 m_info->protocol = start_protocol;
354 m_info->protocol_length = (unsigned)strlen(start_protocol);
355 } else {
356 m_info->protocol = NULL;
357 m_info->protocol_length = -1;
359 m_info->orig_content_type = NULL;
360 m_info->orig_parameters = NULL;
362 return m_info;
366 * The first boundary does not implicitly contain the leading
367 * line-end sequence.
369 * Return the offset to the 1st byte of the boundary delimiter line.
370 * Set boundary_line_len to the length of the entire boundary delimiter.
371 * Set last_boundary to true if we've seen the last-boundary delimiter.
373 static int
374 find_first_boundary(tvbuff_t *tvb, int start, const uint8_t *boundary,
375 int boundary_len, int *boundary_line_len, bool *last_boundary)
377 int offset = start, next_offset, line_len, boundary_start;
379 while (tvb_offset_exists(tvb, offset + 2 + boundary_len)) {
380 boundary_start = offset;
381 if (((tvb_strneql(tvb, offset, (const uint8_t *)"--", 2) == 0)
382 && (tvb_strneql(tvb, offset + 2, boundary, boundary_len) == 0)))
384 /* Boundary string; now check if last */
385 if ((tvb_reported_length_remaining(tvb, offset + 2 + boundary_len + 2) >= 0)
386 && (tvb_strneql(tvb, offset + 2 + boundary_len,
387 (const uint8_t *)"--", 2) == 0)) {
388 *last_boundary = true;
389 } else {
390 *last_boundary = false;
392 /* Look for line end of the boundary line */
393 line_len = tvb_find_line_end(tvb, offset, -1, &offset, false);
394 if (line_len == -1) {
395 *boundary_line_len = -1;
396 } else {
397 *boundary_line_len = offset - boundary_start;
399 return boundary_start;
401 line_len = tvb_find_line_end(tvb, offset, -1, &next_offset, false);
402 if (line_len == -1) {
403 return -1;
405 offset = next_offset;
408 return -1;
412 * Unless the first boundary, subsequent boundaries include a line-end sequence
413 * before the dashed boundary string.
415 * Return the offset to the 1st byte of the boundary delimiter line.
416 * Set boundary_line_len to the length of the entire boundary delimiter.
417 * Set last_boundary to true if we've seen the last-boundary delimiter.
419 static int
420 find_next_boundary(tvbuff_t *tvb, int start, const uint8_t *boundary,
421 int boundary_len, int *boundary_line_len, bool *last_boundary)
423 int offset = start, next_offset, line_len, boundary_start;
425 while (tvb_offset_exists(tvb, offset + 2 + boundary_len)) {
426 line_len = tvb_find_line_end(tvb, offset, -1, &next_offset, false);
427 if (line_len == -1) {
428 return -1;
430 boundary_start = offset + line_len;
431 if (((tvb_strneql(tvb, next_offset, (const uint8_t *)"--", 2) == 0)
432 && (tvb_strneql(tvb, next_offset + 2, boundary, boundary_len) == 0)))
434 /* Boundary string; now check if last */
435 if ((tvb_reported_length_remaining(tvb, next_offset + 2 + boundary_len + 2) >= 0)
436 && (tvb_strneql(tvb, next_offset + 2 + boundary_len,
437 (const uint8_t *)"--", 2) == 0)) {
438 *last_boundary = true;
439 } else {
440 *last_boundary = false;
442 /* Look for line end of the boundary line */
443 line_len = tvb_find_line_end(tvb, next_offset, -1, &offset, false);
444 if (line_len == -1) {
445 *boundary_line_len = -1;
446 } else {
447 *boundary_line_len = offset - boundary_start;
449 return boundary_start;
450 /* check if last before CRLF; some ignore the standard, so there is no CRLF before the boundary */
451 } else if ((tvb_strneql(tvb, boundary_start - 2, (const uint8_t *)"--", 2) == 0)
452 && (tvb_strneql(tvb, boundary_start - (2 + boundary_len), boundary, boundary_len) == 0)
453 && (tvb_strneql(tvb, boundary_start - (2 + boundary_len + 2),
454 (const uint8_t *)"--", 2) == 0)) {
455 boundary_start -= 2 + boundary_len + 2;
456 *boundary_line_len = next_offset - boundary_start;
457 *last_boundary = true;
458 return boundary_start;
460 offset = next_offset;
463 return -1;
467 * Process the multipart preamble:
468 * [ preamble line-end ] dashed-boundary transport-padding line-end
470 * Return the offset to the start of the first body-part.
472 static int
473 process_preamble(proto_tree *tree, tvbuff_t *tvb, multipart_info_t *m_info,
474 bool *last_boundary)
476 int boundary_start, boundary_line_len;
478 const uint8_t *boundary = (uint8_t *)m_info->boundary;
479 int boundary_len = m_info->boundary_length;
481 boundary_start = find_first_boundary(tvb, 0, boundary, boundary_len,
482 &boundary_line_len, last_boundary);
483 if (boundary_start == 0) {
484 proto_tree_add_item(tree, hf_multipart_first_boundary, tvb, boundary_start, boundary_line_len, ENC_NA|ENC_ASCII);
485 return boundary_start + boundary_line_len;
486 } else if (boundary_start > 0) {
487 if (boundary_line_len > 0) {
488 int body_part_start = boundary_start + boundary_line_len;
489 proto_tree_add_item(tree, hf_multipart_preamble, tvb, 0, boundary_start, ENC_NA);
490 proto_tree_add_item(tree, hf_multipart_first_boundary, tvb, boundary_start, boundary_line_len, ENC_NA|ENC_ASCII);
491 return body_part_start;
494 return -1;
497 static void
498 dissect_kerberos_encrypted_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gssapi_encrypt_info_t* encrypt)
500 tvbuff_t *kerberos_tvb;
501 int offset = 0, len;
502 uint8_t *data;
504 proto_tree_add_item(tree, hf_multipart_sec_token_len, tvb, offset, 4, ENC_LITTLE_ENDIAN);
505 offset += 4;
506 len = tvb_reported_length_remaining(tvb, offset);
508 DISSECTOR_ASSERT(tvb_bytes_exist(tvb, offset, len));
510 data = (uint8_t *)tvb_memdup(pinfo->pool, tvb, offset, len);
511 kerberos_tvb = tvb_new_child_real_data(tvb, data, len, len);
513 add_new_data_source(pinfo, kerberos_tvb, "Kerberos Data");
514 call_dissector_with_data(gssapi_handle, kerberos_tvb, pinfo, tree, encrypt);
518 * Process a multipart body-part:
519 * MIME-part-headers [ line-end *OCTET ]
520 * line-end dashed-boundary transport-padding line-end
522 * If applicable, call a media subdissector.
524 * Return the offset to the start of the next body-part.
526 static int
527 process_body_part(proto_tree *tree, tvbuff_t *tvb,
528 media_content_info_t *input_content_info, multipart_info_t *m_info,
529 packet_info *pinfo, int start, int idx,
530 bool *last_boundary)
532 proto_tree *subtree;
533 proto_item *ti;
534 int offset = start, next_offset = 0;
535 media_content_info_t content_info = { input_content_info->type, NULL, NULL, NULL };
536 int body_start, boundary_start, boundary_line_len;
538 char *content_type_str = NULL;
539 char *content_trans_encoding_str = NULL;
540 #if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG)
541 char *content_encoding_str = NULL;
542 #endif
543 char *filename = NULL;
544 char *mimetypename = NULL;
545 bool last_field = false;
546 bool is_raw_data = false;
548 const uint8_t *boundary = (uint8_t *)m_info->boundary;
549 int boundary_len = m_info->boundary_length;
551 ti = proto_tree_add_item(tree, hf_multipart_part, tvb, start, 0, ENC_ASCII);
552 subtree = proto_item_add_subtree(ti, ett_multipart_body);
554 /* find the next boundary to find the end of this body part */
555 boundary_start = find_next_boundary(tvb, offset, boundary, boundary_len,
556 &boundary_line_len, last_boundary);
558 if (boundary_start <= 0) {
559 return -1;
563 * Process the MIME-part-headers
566 while (!last_field)
568 int colon_offset;
569 char *hdr_str;
570 char *header_str;
572 /* Look for the end of the header (denoted by cr)
573 * 3:d argument to imf_find_field_end() maxlen; must be last offset in the tvb.
575 next_offset = imf_find_field_end(tvb, offset, tvb_reported_length_remaining(tvb, offset)+offset, &last_field);
576 /* the following should never happen */
577 /* If cr not found, won't have advanced - get out to avoid infinite loop! */
579 if (next_offset == offset) {
580 break;
583 if (last_field && (next_offset+2) <= boundary_start) {
584 /* Add the extra CRLF of the last field */
585 next_offset += 2;
586 } else if((next_offset-2) == boundary_start) {
587 /* if CRLF is the start of next boundary it belongs to the boundary and not the field,
588 so it's the last field without CRLF */
589 last_field = true;
590 next_offset -= 2;
591 } else if (next_offset > boundary_start) {
592 /* if there is no CRLF between last field and next boundary - trim it! */
593 next_offset = boundary_start;
596 hdr_str = tvb_get_string_enc(pinfo->pool, tvb, offset, next_offset - offset, ENC_ASCII);
598 colon_offset = 0;
599 header_str = unfold_and_compact_mime_header(pinfo->pool, hdr_str, &colon_offset);
600 if (colon_offset <= 0) {
601 /* if there is no colon it's no header, so break and add complete line to the body */
602 next_offset = offset;
603 break;
604 } else {
605 int hf_index;
607 hf_index = is_known_multipart_header(header_str, colon_offset);
609 if (hf_index == -1) {
610 if(isprint_string(header_str)) {
611 proto_tree_add_format_text(subtree, tvb, offset, next_offset - offset);
612 } else {
613 /* if the header name is unknown and not printable, break and add complete line to the body */
614 next_offset = offset;
615 break;
617 } else {
618 char *value_str = wmem_strdup(pinfo->pool, header_str + colon_offset + 1);
620 proto_tree_add_string_format(subtree,
621 hf_header_array[hf_index], tvb,
622 offset, next_offset - offset,
623 (const char *)value_str, "%s",
624 tvb_format_text(pinfo->pool, tvb, offset, next_offset - offset));
626 switch (hf_index) {
627 case POS_ORIGINALCONTENT:
629 char *semicolonp;
630 /* The Content-Type starts at colon_offset + 1 or after the type parameter */
631 char* type_str = ws_find_media_type_parameter(pinfo->pool, value_str, "type");
632 if(type_str != NULL) {
633 value_str = type_str;
636 semicolonp = strchr(value_str, ';');
638 if (semicolonp != NULL) {
639 *semicolonp = '\0';
640 m_info->orig_parameters = wmem_strdup(pinfo->pool,
641 semicolonp + 1);
644 m_info->orig_content_type = wmem_ascii_strdown(pinfo->pool, value_str, -1);
646 break;
647 case POS_CONTENT_TYPE:
649 /* The Content-Type starts at colon_offset + 1 */
650 char *semicolonp = strchr(value_str, ';');
652 if (semicolonp != NULL) {
653 *semicolonp = '\0';
654 content_info.media_str = wmem_strdup(pinfo->pool, semicolonp + 1);
655 } else {
656 content_info.media_str = NULL;
659 content_type_str = wmem_ascii_strdown(pinfo->pool, value_str, -1);
661 /* Show content-type in root 'part' label */
662 proto_item_append_text(ti, " (%s)", content_type_str);
664 /* find the "name" parameter in case we don't find a content disposition "filename" */
665 mimetypename = ws_find_media_type_parameter(pinfo->pool, content_info.media_str, "name");
667 if(strncmp(content_type_str, "application/octet-stream",
668 sizeof("application/octet-stream")-1) == 0) {
669 is_raw_data = true;
672 /* there are only 2 body parts possible and each part has specific content types */
673 if(m_info->protocol && idx == 0
674 && (is_raw_data || g_ascii_strncasecmp(content_type_str, m_info->protocol,
675 strlen(m_info->protocol)) != 0))
677 return -1;
680 break;
681 case POS_CONTENT_ENCODING:
683 /* The Content-Encoding starts at colon_offset + 1 */
684 char *crp = strchr(value_str, '\r');
686 if (crp != NULL) {
687 *crp = '\0';
689 #if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG)
690 content_encoding_str = wmem_ascii_strdown(pinfo->pool, value_str, -1);
691 #endif
693 break;
694 case POS_CONTENT_TRANSFER_ENCODING:
696 /* The Content-Transferring starts at colon_offset + 1 */
697 char *crp = strchr(value_str, '\r');
699 if (crp != NULL) {
700 *crp = '\0';
703 content_trans_encoding_str = wmem_ascii_strdown(pinfo->pool, value_str, -1);
705 break;
706 case POS_CONTENT_DISPOSITION:
708 /* find the "filename" parameter */
709 filename = ws_find_media_type_parameter(pinfo->pool, value_str, "filename");
711 break;
712 case POS_CONTENT_ID:
713 content_info.content_id = wmem_strdup(pinfo->pool, value_str);
714 break;
715 default:
716 break;
720 offset = next_offset;
723 body_start = next_offset;
726 * Process the body
730 int body_len = boundary_start - body_start;
731 tvbuff_t *tmp_tvb = tvb_new_subset_length(tvb, body_start, body_len);
733 * If multipart subtype is encrypted the protcol string was set.
735 * See MS-WSMV section 2.2.9.1.2.1 "HTTP Headers":
737 * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-wsmv/b79927c2-96be-4801-aa68-180db95593f9
739 * There are only 2 body parts possible, and each part has specific
740 * content types.
742 if(m_info->protocol && idx == 1 && is_raw_data)
744 gssapi_encrypt_info_t encrypt;
746 memset(&encrypt, 0, sizeof(encrypt));
747 encrypt.decrypt_gssapi_tvb=DECRYPT_GSSAPI_NORMAL;
749 dissect_kerberos_encrypted_message(tmp_tvb, pinfo, subtree, &encrypt);
751 if(encrypt.gssapi_decrypted_tvb){
752 tmp_tvb = encrypt.gssapi_decrypted_tvb;
753 is_raw_data = false;
754 content_type_str = m_info->orig_content_type;
755 content_info.media_str = m_info->orig_parameters;
756 } else if(encrypt.gssapi_encrypted_tvb) {
757 tmp_tvb = encrypt.gssapi_encrypted_tvb;
758 proto_tree_add_expert(tree, pinfo, &ei_multipart_decryption_not_possible, tmp_tvb, 0, -1);
762 if (!is_raw_data &&
763 content_type_str) {
766 * subdissection
768 bool dissected;
771 * Try and remove any content transfer encoding so that each sub-dissector
772 * doesn't have to do it itself
776 if(content_trans_encoding_str && remove_base64_encoding) {
778 if(!g_ascii_strncasecmp(content_trans_encoding_str, "base64", 6))
779 tmp_tvb = base64_decode(pinfo, tmp_tvb, filename ? filename : (mimetypename ? mimetypename : content_type_str));
783 #if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG)
784 if(content_encoding_str && uncompress_data) {
786 if(g_ascii_strncasecmp(content_encoding_str,"gzip",4) == 0 ||
787 g_ascii_strncasecmp(content_encoding_str,"deflate",7) == 0 ||
788 g_ascii_strncasecmp(content_encoding_str,"x-gzip",6) == 0 ||
789 g_ascii_strncasecmp(content_encoding_str,"x-deflate",9) == 0){
790 /* The body is gzip:ed */
791 tvbuff_t *uncompress_tvb = tvb_child_uncompress_zlib(tmp_tvb, tmp_tvb, 0, body_len);
792 if (uncompress_tvb) {
793 tmp_tvb = uncompress_tvb;
794 add_new_data_source(pinfo, tmp_tvb, "gunzipped data");
798 #endif
801 * First try the dedicated multipart dissector table
803 dissected = dissector_try_string_with_data(multipart_media_subdissector_table,
804 content_type_str, tmp_tvb, pinfo, subtree, true, &content_info);
805 if (! dissected) {
807 * Fall back to the default media dissector table
809 dissected = dissector_try_string_with_data(media_type_dissector_table,
810 content_type_str, tmp_tvb, pinfo, subtree, true, &content_info);
812 if (! dissected) {
813 const char *save_match_string = pinfo->match_string;
814 pinfo->match_string = content_type_str;
815 call_dissector_with_data(media_handle, tmp_tvb, pinfo, subtree, &content_info);
816 pinfo->match_string = save_match_string;
818 content_info.media_str = NULL; /* Shares same memory as content_type_str */
819 } else {
820 call_data_dissector(tmp_tvb, pinfo, subtree);
822 proto_item_set_len(ti, boundary_start - start);
823 if (*last_boundary == true) {
824 proto_tree_add_item(tree, hf_multipart_last_boundary, tvb, boundary_start, boundary_line_len, ENC_NA|ENC_ASCII);
825 } else {
826 proto_tree_add_item(tree, hf_multipart_boundary, tvb, boundary_start, boundary_line_len, ENC_NA|ENC_ASCII);
829 return boundary_start + boundary_line_len;
834 * Call this method to actually dissect the multipart body.
835 * NOTE - Only do so if a boundary string has been found!
837 static int dissect_multipart(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
839 proto_tree *subtree;
840 proto_item *ti;
841 proto_item *type_ti;
842 media_content_info_t *content_info = (media_content_info_t *)data;
843 multipart_info_t *m_info = get_multipart_info(pinfo, content_info);
844 int header_start = 0;
845 int body_index = 0;
846 bool last_boundary = false;
848 if (m_info == NULL) {
850 * We can't get the required multipart information
852 proto_tree_add_expert(tree, pinfo, &ei_multipart_no_required_parameter, tvb, 0, -1);
853 call_data_dissector(tvb, pinfo, tree);
854 return tvb_reported_length(tvb);
857 /* Add stuff to the protocol tree */
858 ti = proto_tree_add_item(tree, proto_multipart,
859 tvb, 0, -1, ENC_NA);
860 subtree = proto_item_add_subtree(ti, ett_multipart);
861 proto_item_append_text(ti, ", Type: %s, Boundary: \"%s\"",
862 m_info->type, m_info->boundary);
864 /* Show multi-part type as a generated field */
865 type_ti = proto_tree_add_string(subtree, hf_multipart_type,
866 tvb, 0, 0, pinfo->match_string);
867 proto_item_set_generated(type_ti);
870 * Make no entries in Protocol column and Info column on summary display,
871 * but stop sub-dissectors from clearing entered text in summary display.
873 col_set_fence(pinfo->cinfo, COL_INFO);
876 * Process the multipart preamble
878 header_start = process_preamble(subtree, tvb, m_info, &last_boundary);
879 if (header_start == -1) {
880 call_data_dissector(tvb, pinfo, subtree);
881 return tvb_reported_length(tvb);
884 * Process the encapsulated bodies
886 while (last_boundary == false) {
887 header_start = process_body_part(subtree, tvb, content_info, m_info,
888 pinfo, header_start, body_index++, &last_boundary);
889 if (header_start == -1) {
890 return tvb_reported_length(tvb);
894 * Process the multipart trailer
896 if (tvb_reported_length_remaining(tvb, header_start) > 0) {
897 proto_tree_add_item(subtree, hf_multipart_trailer, tvb, header_start, -1, ENC_NA);
900 return tvb_reported_length(tvb);
903 /* Returns index of method in multipart_headers */
904 static int
905 is_known_multipart_header(const char *header_str, unsigned len)
907 unsigned i;
909 for (i = 1; i < array_length(multipart_headers); i++) {
910 if (len == strlen(multipart_headers[i].name) &&
911 g_ascii_strncasecmp(header_str, multipart_headers[i].name, len) == 0)
912 return i;
913 if (multipart_headers[i].compact_name != NULL &&
914 len == strlen(multipart_headers[i].compact_name) &&
915 g_ascii_strncasecmp(header_str, multipart_headers[i].compact_name, len) == 0)
916 return i;
919 return -1;
923 * Register the protocol with Wireshark.
925 * This format is required because a script is used to build the C function
926 * that calls all the protocol registration.
929 void
930 proto_register_multipart(void)
933 /* Setup list of header fields See Section 1.6.1 for details */
934 static hf_register_info hf[] = {
935 { &hf_multipart_type,
936 { "Type",
937 "mime_multipart.type",
938 FT_STRING, BASE_NONE, NULL, 0x00,
939 "MIME multipart encapsulation type", HFILL
942 { &hf_multipart_part,
943 { "Encapsulated multipart part",
944 "mime_multipart.part",
945 FT_STRING, BASE_NONE, NULL, 0x00,
946 NULL, HFILL
949 { &hf_multipart_sec_token_len,
950 { "Length of security token",
951 "mime_multipart.header.sectoken-length",
952 FT_UINT32, BASE_DEC, NULL, 0x00,
953 "Length of the Kerberos BLOB which follows this token", HFILL
956 { &hf_header_array[POS_CONTENT_DESCRIPTION],
957 { "Content-Description",
958 "mime_multipart.header.content-description",
959 FT_STRING, BASE_NONE, NULL, 0x00,
960 "Content-Description Header", HFILL
963 { &hf_header_array[POS_CONTENT_DISPOSITION],
964 { "Content-Disposition",
965 "mime_multipart.header.content-disposition",
966 FT_STRING, BASE_NONE, NULL, 0x00,
967 "RFC 2183: Content-Disposition Header", HFILL
970 { &hf_header_array[POS_CONTENT_ENCODING],
971 { "Content-Encoding",
972 "mime_multipart.header.content-encoding",
973 FT_STRING, BASE_NONE, NULL, 0x00,
974 "Content-Encoding Header", HFILL
977 { &hf_header_array[POS_CONTENT_ID],
978 { "Content-Id",
979 "mime_multipart.header.content-id",
980 FT_STRING, BASE_NONE, NULL, 0x00,
981 "RFC 2045: Content-Id Header", HFILL
984 { &hf_header_array[POS_CONTENT_LANGUAGE],
985 { "Content-Language",
986 "mime_multipart.header.content-language",
987 FT_STRING, BASE_NONE, NULL, 0x00,
988 "Content-Language Header", HFILL
991 { &hf_header_array[POS_CONTENT_LENGTH],
992 { "Content-Length",
993 "mime_multipart.header.content-length",
994 FT_STRING, BASE_NONE, NULL, 0x0,
995 "Content-Length Header", HFILL
998 { &hf_header_array[POS_CONTENT_TRANSFER_ENCODING],
999 { "Content-Transfer-Encoding",
1000 "mime_multipart.header.content-transfer-encoding",
1001 FT_STRING, BASE_NONE, NULL, 0x00,
1002 "RFC 2045: Content-Transfer-Encoding Header", HFILL
1005 { &hf_header_array[POS_CONTENT_TYPE],
1006 { "Content-Type",
1007 "mime_multipart.header.content-type",
1008 FT_STRING, BASE_NONE,NULL,0x0,
1009 "Content-Type Header", HFILL
1012 { &hf_header_array[POS_ORIGINALCONTENT],
1013 { "OriginalContent",
1014 "mime_multipart.header.originalcontent",
1015 FT_STRING, BASE_NONE,NULL,0x0,
1016 "Original Content-Type Header", HFILL
1020 /* Generated from convert_proto_tree_add_text.pl */
1021 { &hf_multipart_first_boundary, { "First boundary", "mime_multipart.first_boundary", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1022 { &hf_multipart_preamble, { "Preamble", "mime_multipart.preamble", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1023 { &hf_multipart_last_boundary, { "Last boundary", "mime_multipart.last_boundary", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1024 { &hf_multipart_boundary, { "Boundary", "mime_multipart.boundary", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1025 { &hf_multipart_trailer, { "Trailer", "mime_multipart.trailer", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1030 * Preferences
1032 module_t *multipart_module;
1033 expert_module_t* expert_multipart;
1037 * Setup protocol subtree array
1039 static int *ett[] = {
1040 &ett_multipart,
1041 &ett_multipart_main,
1042 &ett_multipart_body,
1045 static ei_register_info ei[] = {
1046 { &ei_multipart_no_required_parameter, { "mime_multipart.no_required_parameter", PI_PROTOCOL, PI_ERROR, "The multipart dissector could not find a required parameter.", EXPFILL }},
1047 { &ei_multipart_decryption_not_possible, { "mime_multipart.decryption_not_possible", PI_UNDECODED, PI_WARN, "The multipart dissector could not decrypt the message.", EXPFILL }},
1051 * Register the protocol name and description
1053 proto_multipart = proto_register_protocol("MIME Multipart Media Encapsulation", "MIME multipart", "mime_multipart");
1056 * Required function calls to register
1057 * the header fields and subtrees used.
1059 proto_register_field_array(proto_multipart, hf, array_length(hf));
1060 proto_register_subtree_array(ett, array_length(ett));
1061 expert_multipart = expert_register_protocol(proto_multipart);
1062 expert_register_field_array(expert_multipart, ei, array_length(ei));
1064 multipart_module = prefs_register_protocol(proto_multipart, NULL);
1066 prefs_register_bool_preference(multipart_module,
1067 "display_unknown_body_as_text",
1068 "Display bodies without media type as text",
1069 "Display multipart bodies with no media type dissector"
1070 " as raw text (may cause problems with binary data).",
1071 &display_unknown_body_as_text);
1073 prefs_register_bool_preference(multipart_module,
1074 "remove_base64_encoding",
1075 "Remove base64 encoding from bodies",
1076 "Remove any base64 content-transfer encoding from bodies. "
1077 "This supports export of the body and its further dissection.",
1078 &remove_base64_encoding);
1080 #if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG)
1081 prefs_register_bool_preference(multipart_module,
1082 "uncompress_data",
1083 "Uncompress parts which are compressed",
1084 "Uncompress parts which are compressed. GZIP for example. "
1085 "This supports export of the body and its further dissection.",
1086 &uncompress_data);
1087 #endif
1090 * Dissectors requiring different behavior in cases where the media
1091 * is contained in a multipart entity should register their multipart
1092 * dissector in the dissector table below, which is similar to the
1093 * "media_type" dissector table defined in the HTTP dissector code.
1095 multipart_media_subdissector_table = register_dissector_table(
1096 "multipart_media_type",
1097 "Internet media type (for multipart processing)",
1098 proto_multipart, FT_STRING, STRING_CASE_INSENSITIVE);
1101 * Handle for multipart dissection
1103 multipart_handle = register_dissector("mime_multipart",
1104 dissect_multipart, proto_multipart);
1108 /* If this dissector uses sub-dissector registration add a registration routine.
1109 This format is required because a script is used to find these routines and
1110 create the code that calls these routines.
1112 void
1113 proto_reg_handoff_multipart(void)
1116 * When we cannot display the data, call the data dissector.
1117 * When there is no dissector for the given media, call the media dissector.
1119 media_handle = find_dissector_add_dependency("media", proto_multipart);
1120 gssapi_handle = find_dissector_add_dependency("gssapi", proto_multipart);
1123 * Get the content type and Internet media type table
1125 media_type_dissector_table = find_dissector_table("media_type");
1127 dissector_add_string("media_type",
1128 "multipart/mixed", multipart_handle);
1129 dissector_add_string("media_type",
1130 "multipart/related", multipart_handle);
1131 dissector_add_string("media_type",
1132 "multipart/alternative", multipart_handle);
1133 dissector_add_string("media_type",
1134 "multipart/form-data", multipart_handle);
1135 dissector_add_string("media_type",
1136 "multipart/report", multipart_handle);
1137 dissector_add_string("media_type",
1138 "multipart/signed", multipart_handle);
1139 dissector_add_string("media_type",
1140 "multipart/encrypted", multipart_handle);
1143 * Supply an entry to use for unknown multipart subtype.
1144 * See RFC 2046, section 5.1.3
1146 dissector_add_string("media_type",
1147 "multipart/", multipart_handle);
1151 * Editor modelines - https://www.wireshark.org/tools/modelines.html
1153 * Local variables:
1154 * c-basic-offset: 4
1155 * tab-width: 8
1156 * indent-tabs-mode: nil
1157 * End:
1159 * vi: set shiftwidth=4 tabstop=8 expandtab:
1160 * :indentSize=4:tabSize=8:noTabs=true: