1 /* packet-multipart.c
2  * Routines for multipart media encapsulation dissection
3  * Copyright 2004, Anders Broman.
4  * Copyright 2004, Olivier Biot.
5  *
6  * Refer to the AUTHORS file or the AUTHORS section in the man page
7  * for contacting the author(s) of this file.
8  *
9  * Wireshark - Network traffic analyzer
10  * By Gerald Combs <gerald@wireshark.org>
11  * Copyright 1998 Gerald Combs
12  *
13  *
14  * SPDX-License-Identifier: GPL-2.0-or-later
15  *
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
23  *
24  * Part of the code is modeled from the SIP and HTTP dissectors
25  *
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 ]
33  *
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
41  *
42  * Note that line-end is often a LF instead of a CRLF.
43 */
44 
45 #include "config.h"
46 
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"
53 
54 #include "packet-dcerpc.h"
55 #include "packet-gssapi.h"
56 #include "packet-http.h"
57 
58 void proto_register_multipart(void);
59 void proto_reg_handoff_multipart(void);
60 
61 /* Dissector table for media requiring special attention in multipart
62  * encapsulation. */
63 static dissector_table_t multipart_media_subdissector_table;
64 
65 /* Initialize the protocol and registered fields */
66 static int proto_multipart = -1;
67 
68 /* Generated from convert_proto_tree_add_text.pl */
69 static int hf_multipart_trailer = -1;
70 static int hf_multipart_boundary = -1;
71 static int hf_multipart_first_boundary = -1;
72 static int hf_multipart_last_boundary = -1;
73 static int hf_multipart_preamble = -1;
74 
75 /* Initialize the subtree pointers */
76 static gint ett_multipart = -1;
77 static gint ett_multipart_main = -1;
78 static gint ett_multipart_body = -1;
79 
80 /* Generated from convert_proto_tree_add_text.pl */
81 static expert_field ei_multipart_no_required_parameter = EI_INIT;
82 static expert_field ei_multipart_decryption_not_possible = EI_INIT;
83 
84 /* Not sure that compact_name exists for multipart, but choose to keep
85  * the structure from SIP dissector, all the content- is also from SIP */
86 
87 
88 typedef struct {
89         const char *name;
90         const char *compact_name;
91 } multipart_header_t;
92 
93 static const multipart_header_t multipart_headers[] = {
94     { "Unknown-header", NULL },     /* Pad so that the real headers start at index 1 */
95     { "Content-Description", NULL },
96     { "Content-Disposition", NULL },
97     { "Content-Encoding", "e" },
98     { "Content-Id", NULL },
99     { "Content-Language", NULL },
100     { "Content-Length", "l" },
101     { "Content-Transfer-Encoding", NULL },
102     { "Content-Type", "c" },
103     { "OriginalContent", NULL }
104 };
105 
106 #define POS_CONTENT_DESCRIPTION         1
107 #define POS_CONTENT_DISPOSITION         2
108 #define POS_CONTENT_ENCODING            3
109 #define POS_CONTENT_ID                  4
110 #define POS_CONTENT_LANGUAGE            5
111 #define POS_CONTENT_LENGTH              6
112 #define POS_CONTENT_TRANSFER_ENCODING   7
113 #define POS_CONTENT_TYPE                8
114 #define POS_ORIGINALCONTENT             9
115 
116 /* Initialize the header fields */
117 static gint hf_multipart_type = -1;
118 static gint hf_multipart_part = -1;
119 static gint hf_multipart_sec_token_len = -1;
120 
121 static gint hf_header_array[] = {
122     -1, /* "Unknown-header" - Pad so that the real headers start at index 1 */
123     -1, /* "Content-Description" */
124     -1, /* "Content-Disposition" */
125     -1, /* "Content-Encoding" */
126     -1, /* "Content-Id" */
127     -1, /* "Content-Language" */
128     -1, /* "Content-Length" */
129     -1, /* "Content-Transfer-Encoding" */
130     -1, /* "Content-Type" */
131     -1, /* "OriginalContent" */
132 };
133 
134 /* Define media_type/Content type table */
135 static dissector_table_t media_type_dissector_table;
136 
137 /* Data and media dissector handles */
138 static dissector_handle_t media_handle;
139 static dissector_handle_t gssapi_handle;
140 
141 /* Determines if bodies with no media type dissector should be displayed
142  * as raw text, may cause problems with images sound etc
143  * TODO improve to check for different content types ?
144  */
145 static gboolean display_unknown_body_as_text = FALSE;
146 static gboolean remove_base64_encoding = FALSE;
147 #ifdef HAVE_ZLIB
148 static gboolean uncompress_data = TRUE;
149 #endif
150 
151 typedef struct {
152     const char *type; /* Type of multipart */
153     char *boundary; /* Boundary string (enclosing quotes removed if any) */
154     guint boundary_length; /* Length of the boundary string */
155     char *protocol; /* Protocol string if encrypted multipart (enclosing quotes removed if any) */
156     guint protocol_length; /* Length of the protocol string  */
157     char *orig_content_type; /* Content-Type of original message */
158     char *orig_parameters; /* Parameters for Content-Type of original message */
159 } multipart_info_t;
160 
161 
162 
163 static gint
164 find_first_boundary(tvbuff_t *tvb, gint start, const guint8 *boundary,
165         gint boundary_len, gint *boundary_line_len, gboolean *last_boundary);
166 static gint
167 find_next_boundary(tvbuff_t *tvb, gint start, const guint8 *boundary,
168         gint boundary_len, gint *boundary_line_len, gboolean *last_boundary);
169 static gint
170 process_preamble(proto_tree *tree, tvbuff_t *tvb, multipart_info_t *m_info,
171         gboolean *last_boundary);
172 static gint
173 process_body_part(proto_tree *tree, tvbuff_t *tvb,
174         http_message_info_t *input_message_info, multipart_info_t *m_info,
175         packet_info *pinfo, gint start, gint idx,
176         gboolean *last_boundary);
177 static gint
178 is_known_multipart_header(const char *header_str, guint len);
179 
180 
181 /* Return a tvb that contains the binary representation of a base64
182    string */
183 
184 static tvbuff_t *
base64_decode(packet_info * pinfo,tvbuff_t * b64_tvb,char * name)185 base64_decode(packet_info *pinfo, tvbuff_t *b64_tvb, char *name)
186 {
187     char *data;
188     tvbuff_t *tvb;
189     data = tvb_get_string_enc(pinfo->pool, b64_tvb, 0, tvb_reported_length(b64_tvb), ENC_ASCII);
190 
191     tvb = base64_to_tvb(b64_tvb, data);
192     add_new_data_source(pinfo, tvb, name);
193 
194     return tvb;
195 }
196 
197 /*
198  * Unfold and clean up a MIME-like header, and process LWS as follows:
199  *      o Preserves LWS in quoted text
200  *      o Remove LWS before and after a separator
201  *      o Remove trailing LWS
202  *      o Replace other LWS with a single space
203  * Set value to the start of the value
204  * Return the cleaned-up RFC2822 header (buffer must be freed).
205  */
206 static char *
unfold_and_compact_mime_header(wmem_allocator_t * pool,const char * lines,gint * first_colon_offset)207 unfold_and_compact_mime_header(wmem_allocator_t *pool, const char *lines, gint *first_colon_offset)
208 {
209     const char *p = lines;
210     char c;
211     char *ret, *q;
212     char sep_seen = 0; /* Did we see a separator ":;," */
213     char lws = FALSE; /* Did we see LWS (incl. folding) */
214     gint colon = -1;
215 
216     if (! lines) return NULL;
217 
218     c = *p;
219     ret = (char *)wmem_alloc(pool, strlen(lines) + 1);
220     q = ret;
221 
222     while (c) {
223         if (c == ':') {
224             lws = FALSE; /* Prevent leading LWS from showing up */
225             if (colon == -1) {/* First colon */
226                 colon = (gint) (q - ret);
227             }
228             *(q++) = sep_seen = c;
229             p++;
230         } else if (c == ';' || c == ',' || c == '=') {
231             lws = FALSE; /* Prevent leading LWS from showing up */
232             *(q++) = sep_seen = c;
233             p++;
234         } else if (c == ' ' || c == '\t') {
235             lws = TRUE;
236             p++;
237         } else if (c == '\n') {
238             lws = FALSE; /* Skip trailing LWS */
239             if ((c = *(p+1))) {
240                 if (c == ' ' || c == '\t') { /* Header unfolding */
241                     lws = TRUE;
242                     p += 2;
243                 } else {
244                     *q = c = 0; /* Stop */
245                 }
246             }
247         } else if (c == '\r') {
248             lws = FALSE;
249             if ((c = *(p+1))) {
250                 if (c == '\n') {
251                     if ((c = *(p+2))) {
252                         if (c == ' ' || c == '\t') { /* Header unfolding */
253                             lws = TRUE;
254                             p += 3;
255                         } else {
256                             *q = c = 0; /* Stop */
257                         }
258                     }
259                 } else if (c == ' ' || c == '\t') { /* Header unfolding */
260                     lws = TRUE;
261                     p += 2;
262                 } else {
263                     *q = c = 0; /* Stop */
264                 }
265             }
266         } else if (c == '"') { /* Start of quoted-string */
267             lws = FALSE;
268             *(q++) = c;
269             while (c) {
270                 c = *(q++) = *(++p);
271                 if (c == '\\') {
272                     /* First part of a quoted-pair; copy the other part,
273                        without checking if it's a quote */
274                     c = *(q++) = *(++p);
275                 } else {
276                     if (c == '"') {
277                         p++; /* Skip closing quote */
278                         break;
279                     }
280                 }
281             }
282             /* if already zero terminated now, rewind one char to avoid an "off by one" */
283             if(c == 0) {
284                 q--;
285             }
286         } else { /* Regular character */
287             if (sep_seen) {
288                 sep_seen = 0;
289             } else {
290                 if (lws) {
291                     *(q++) = ' ';
292                 }
293             }
294             lws = FALSE;
295             *(q++) = c;
296             p++; /* OK */
297         }
298 
299         if (c) {
300             c = *p;
301         }
302     }
303     *q = 0;
304 
305     *first_colon_offset = colon;
306     return (ret);
307 }
308 
309 /* Retrieve the media information from pinfo->private_data,
310  * and compute the boundary string and its length.
311  * Return a pointer to a filled-in multipart_info_t, or NULL on failure.
312  *
313  * Boundary delimiters must not appear within the encapsulated material,
314  * and must be no longer than 70 characters, not counting the two
315  * leading hyphens. (quote from rfc2046)
316  */
317 static multipart_info_t *
get_multipart_info(packet_info * pinfo,http_message_info_t * message_info)318 get_multipart_info(packet_info *pinfo, http_message_info_t *message_info)
319 {
320     char *start_boundary, *start_protocol = NULL;
321     multipart_info_t *m_info = NULL;
322     const char *type = pinfo->match_string;
323     char *parameters;
324     gint dummy;
325 
326     /*
327      * We need both a content type AND parameters
328      * for multipart dissection.
329      */
330     if (type == NULL) {
331         return NULL;
332     }
333     if (message_info == NULL) {
334         return NULL;
335     }
336     if (message_info->media_str == NULL) {
337         return NULL;
338     }
339 
340     /* Clean up the parameters */
341     parameters = unfold_and_compact_mime_header(pinfo->pool, message_info->media_str, &dummy);
342 
343     start_boundary = ws_find_media_type_parameter(pinfo->pool, parameters, "boundary");
344     if (!start_boundary) {
345         return NULL;
346     }
347 
348     if (strncmp(type, "multipart/encrypted", sizeof("multipart/encrypted") - 1) == 0) {
349         start_protocol = ws_find_media_type_parameter(pinfo->pool, parameters, "protocol");
350         if (!start_protocol) {
351             return NULL;
352         }
353     }
354 
355     /*
356      * There is a value for the boundary string
357      */
358     m_info = wmem_new(pinfo->pool, multipart_info_t);
359     m_info->type = type;
360     m_info->boundary = start_boundary;
361     m_info->boundary_length = (guint)strlen(start_boundary);
362     if(start_protocol) {
363         m_info->protocol = start_protocol;
364         m_info->protocol_length = (guint)strlen(start_protocol);
365     } else {
366         m_info->protocol = NULL;
367         m_info->protocol_length = -1;
368     }
369     m_info->orig_content_type = NULL;
370     m_info->orig_parameters = NULL;
371 
372     return m_info;
373 }
374 
375 /*
376  * The first boundary does not implicitly contain the leading
377  * line-end sequence.
378  *
379  * Return the offset to the 1st byte of the boundary delimiter line.
380  * Set boundary_line_len to the length of the entire boundary delimiter.
381  * Set last_boundary to TRUE if we've seen the last-boundary delimiter.
382  */
383 static gint
find_first_boundary(tvbuff_t * tvb,gint start,const guint8 * boundary,gint boundary_len,gint * boundary_line_len,gboolean * last_boundary)384 find_first_boundary(tvbuff_t *tvb, gint start, const guint8 *boundary,
385         gint boundary_len, gint *boundary_line_len, gboolean *last_boundary)
386 {
387     gint offset = start, next_offset, line_len, boundary_start;
388 
389     while (tvb_offset_exists(tvb, offset + 2 + boundary_len)) {
390         boundary_start = offset;
391         if (((tvb_strneql(tvb, offset, (const guint8 *)"--", 2) == 0)
392                     && (tvb_strneql(tvb, offset + 2, boundary,  boundary_len) == 0)))
393         {
394             /* Boundary string; now check if last */
395             if ((tvb_reported_length_remaining(tvb, offset + 2 + boundary_len + 2) >= 0)
396                     && (tvb_strneql(tvb, offset + 2 + boundary_len,
397                             (const guint8 *)"--", 2) == 0)) {
398                 *last_boundary = TRUE;
399             } else {
400                 *last_boundary = FALSE;
401             }
402             /* Look for line end of the boundary line */
403             line_len =  tvb_find_line_end(tvb, offset, -1, &offset, FALSE);
404             if (line_len == -1) {
405                 *boundary_line_len = -1;
406             } else {
407                 *boundary_line_len = offset - boundary_start;
408             }
409             return boundary_start;
410         }
411         line_len =  tvb_find_line_end(tvb, offset, -1, &next_offset, FALSE);
412         if (line_len == -1) {
413             return -1;
414         }
415         offset = next_offset;
416     }
417 
418     return -1;
419 }
420 
421 /*
422  * Unless the first boundary, subsequent boundaries include a line-end sequence
423  * before the dashed boundary string.
424  *
425  * Return the offset to the 1st byte of the boundary delimiter line.
426  * Set boundary_line_len to the length of the entire boundary delimiter.
427  * Set last_boundary to TRUE if we've seen the last-boundary delimiter.
428  */
429 static gint
find_next_boundary(tvbuff_t * tvb,gint start,const guint8 * boundary,gint boundary_len,gint * boundary_line_len,gboolean * last_boundary)430 find_next_boundary(tvbuff_t *tvb, gint start, const guint8 *boundary,
431         gint boundary_len, gint *boundary_line_len, gboolean *last_boundary)
432 {
433     gint offset = start, next_offset, line_len, boundary_start;
434 
435     while (tvb_offset_exists(tvb, offset + 2 + boundary_len)) {
436         line_len =  tvb_find_line_end(tvb, offset, -1, &next_offset, FALSE);
437         if (line_len == -1) {
438             return -1;
439         }
440         boundary_start = offset + line_len;
441         if (((tvb_strneql(tvb, next_offset, (const guint8 *)"--", 2) == 0)
442                     && (tvb_strneql(tvb, next_offset + 2, boundary, boundary_len) == 0)))
443         {
444             /* Boundary string; now check if last */
445             if ((tvb_reported_length_remaining(tvb, next_offset + 2 + boundary_len + 2) >= 0)
446                     && (tvb_strneql(tvb, next_offset + 2 + boundary_len,
447                             (const guint8 *)"--", 2) == 0)) {
448                 *last_boundary = TRUE;
449             } else {
450                 *last_boundary = FALSE;
451             }
452             /* Look for line end of the boundary line */
453             line_len =  tvb_find_line_end(tvb, next_offset, -1, &offset, FALSE);
454             if (line_len == -1) {
455                 *boundary_line_len = -1;
456             } else {
457                 *boundary_line_len = offset - boundary_start;
458             }
459             return boundary_start;
460         /* check if last before CRLF; some ignore the standard, so there is no CRLF before the boundary */
461         } else if ((tvb_strneql(tvb, boundary_start - 2, (const guint8 *)"--", 2) == 0)
462                     && (tvb_strneql(tvb, boundary_start - (2 + boundary_len), boundary, boundary_len) == 0)
463                     && (tvb_strneql(tvb, boundary_start - (2 + boundary_len + 2),
464                             (const guint8 *)"--", 2) == 0)) {
465             boundary_start -= 2 + boundary_len + 2;
466             *boundary_line_len = next_offset - boundary_start;
467             *last_boundary = TRUE;
468             return boundary_start;
469         }
470         offset = next_offset;
471     }
472 
473     return -1;
474 }
475 
476 /*
477  * Process the multipart preamble:
478  *      [ preamble line-end ] dashed-boundary transport-padding line-end
479  *
480  * Return the offset to the start of the first body-part.
481  */
482 static gint
process_preamble(proto_tree * tree,tvbuff_t * tvb,multipart_info_t * m_info,gboolean * last_boundary)483 process_preamble(proto_tree *tree, tvbuff_t *tvb, multipart_info_t *m_info,
484         gboolean *last_boundary)
485 {
486     gint boundary_start, boundary_line_len;
487 
488     const guint8 *boundary = (guint8 *)m_info->boundary;
489     gint boundary_len = m_info->boundary_length;
490 
491     boundary_start = find_first_boundary(tvb, 0, boundary, boundary_len,
492             &boundary_line_len, last_boundary);
493     if (boundary_start == 0) {
494        proto_tree_add_item(tree, hf_multipart_first_boundary, tvb, boundary_start, boundary_line_len, ENC_NA|ENC_ASCII);
495         return boundary_start + boundary_line_len;
496     } else if (boundary_start > 0) {
497         if (boundary_line_len > 0) {
498             gint body_part_start = boundary_start + boundary_line_len;
499             proto_tree_add_item(tree, hf_multipart_preamble, tvb, 0, boundary_start, ENC_NA);
500             proto_tree_add_item(tree, hf_multipart_first_boundary, tvb, boundary_start, boundary_line_len, ENC_NA|ENC_ASCII);
501             return body_part_start;
502         }
503     }
504     return -1;
505 }
506 
507 static void
dissect_kerberos_encrypted_message(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,gssapi_encrypt_info_t * encrypt)508 dissect_kerberos_encrypted_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gssapi_encrypt_info_t* encrypt)
509 {
510     tvbuff_t *kerberos_tvb;
511     gint offset = 0, len;
512     guint8 *data;
513 
514     proto_tree_add_item(tree, hf_multipart_sec_token_len, tvb, offset, 4, ENC_LITTLE_ENDIAN);
515     offset += 4;
516     len = tvb_reported_length_remaining(tvb, offset);
517 
518     DISSECTOR_ASSERT(tvb_bytes_exist(tvb, offset, len));
519 
520     data = (guint8 *)tvb_memdup(pinfo->pool, tvb, offset, len);
521     kerberos_tvb = tvb_new_child_real_data(tvb, data, len, len);
522 
523     add_new_data_source(pinfo, kerberos_tvb, "Kerberos Data");
524     call_dissector_with_data(gssapi_handle, kerberos_tvb, pinfo, tree, encrypt);
525 }
526 
527 /*
528  * Process a multipart body-part:
529  *      MIME-part-headers [ line-end *OCTET ]
530  *      line-end dashed-boundary transport-padding line-end
531  *
532  * If applicable, call a media subdissector.
533  *
534  * Return the offset to the start of the next body-part.
535  */
536 static gint
process_body_part(proto_tree * tree,tvbuff_t * tvb,http_message_info_t * input_message_info,multipart_info_t * m_info,packet_info * pinfo,gint start,gint idx,gboolean * last_boundary)537 process_body_part(proto_tree *tree, tvbuff_t *tvb,
538         http_message_info_t *input_message_info, multipart_info_t *m_info,
539         packet_info *pinfo, gint start, gint idx,
540         gboolean *last_boundary)
541 {
542     proto_tree *subtree;
543     proto_item *ti;
544     gint offset = start, next_offset = 0;
545     http_message_info_t message_info = { input_message_info->type, NULL, NULL, NULL };
546     gint body_start, boundary_start, boundary_line_len;
547 
548     gchar *content_type_str = NULL;
549     gchar *content_trans_encoding_str = NULL;
550 #ifdef HAVE_ZLIB
551     gchar *content_encoding_str = NULL;
552 #endif
553     char *filename = NULL;
554     char *mimetypename = NULL;
555     gboolean last_field = FALSE;
556     gboolean is_raw_data = FALSE;
557 
558     const guint8 *boundary = (guint8 *)m_info->boundary;
559     gint boundary_len = m_info->boundary_length;
560 
561     ti = proto_tree_add_item(tree, hf_multipart_part, tvb, start, 0, ENC_ASCII|ENC_NA);
562     subtree = proto_item_add_subtree(ti, ett_multipart_body);
563 
564     /* find the next boundary to find the end of this body part */
565     boundary_start = find_next_boundary(tvb, offset, boundary, boundary_len,
566             &boundary_line_len, last_boundary);
567 
568     if (boundary_start <= 0) {
569         return -1;
570     }
571 
572     /*
573      * Process the MIME-part-headers
574      */
575 
576     while (!last_field)
577     {
578         gint colon_offset;
579         char *hdr_str;
580         char *header_str;
581 
582         /* Look for the end of the header (denoted by cr)
583          * 3:d argument to imf_find_field_end() maxlen; must be last offset in the tvb.
584          */
585         next_offset = imf_find_field_end(tvb, offset, tvb_reported_length_remaining(tvb, offset)+offset, &last_field);
586         /* the following should never happen */
587         /* If cr not found, won't have advanced - get out to avoid infinite loop! */
588         /*
589         if (next_offset == offset) {
590             break;
591         }
592         */
593         if (last_field && (next_offset+2) <= boundary_start) {
594             /* Add the extra CRLF of the last field */
595             next_offset += 2;
596         } else if((next_offset-2) == boundary_start) {
597             /* if CRLF is the start of next boundary it belongs to the boundary and not the field,
598                so it's the last field without CRLF */
599             last_field = TRUE;
600             next_offset -= 2;
601         } else if (next_offset > boundary_start) {
602             /* if there is no CRLF between last field and next boundary - trim it! */
603             next_offset = boundary_start;
604         }
605 
606         hdr_str = tvb_get_string_enc(pinfo->pool, tvb, offset, next_offset - offset, ENC_ASCII);
607 
608         colon_offset = 0;
609         header_str = unfold_and_compact_mime_header(pinfo->pool, hdr_str, &colon_offset);
610         if (colon_offset <= 0) {
611             /* if there is no colon it's no header, so break and add complete line to the body */
612             next_offset = offset;
613             break;
614         } else {
615             gint hf_index;
616 
617             hf_index = is_known_multipart_header(header_str, colon_offset);
618 
619             if (hf_index == -1) {
620                 if(isprint_string(header_str)) {
621                     proto_tree_add_format_text(subtree, tvb, offset, next_offset - offset);
622                 } else {
623                     /* if the header name is unknown and not printable, break and add complete line to the body */
624                     next_offset = offset;
625                     break;
626                 }
627             } else {
628                 char *value_str = wmem_strdup(pinfo->pool, header_str + colon_offset + 1);
629 
630                 proto_tree_add_string_format(subtree,
631                       hf_header_array[hf_index], tvb,
632                       offset, next_offset - offset,
633                       (const char *)value_str, "%s",
634                       tvb_format_text(pinfo->pool, tvb, offset, next_offset - offset));
635 
636                 switch (hf_index) {
637                     case POS_ORIGINALCONTENT:
638                         {
639                             char *semicolonp;
640                             /* The Content-Type starts at colon_offset + 1 or after the type parameter */
641                             char* type_str = ws_find_media_type_parameter(pinfo->pool, value_str, "type");
642                             if(type_str != NULL) {
643                                 value_str = type_str;
644                             }
645 
646                             semicolonp = strchr(value_str, ';');
647 
648                             if (semicolonp != NULL) {
649                                 *semicolonp = '\0';
650                                 m_info->orig_parameters = wmem_strdup(pinfo->pool,
651                                                              semicolonp + 1);
652                             }
653 
654                             m_info->orig_content_type = wmem_ascii_strdown(pinfo->pool, value_str, -1);
655                         }
656                         break;
657                     case POS_CONTENT_TYPE:
658                         {
659                             /* The Content-Type starts at colon_offset + 1 */
660                             char *semicolonp = strchr(value_str, ';');
661 
662                             if (semicolonp != NULL) {
663                                 *semicolonp = '\0';
664                                 message_info.media_str = wmem_strdup(pinfo->pool, semicolonp + 1);
665                             } else {
666                                 message_info.media_str = NULL;
667                             }
668 
669                             content_type_str = wmem_ascii_strdown(pinfo->pool, value_str, -1);
670 
671                             /* Show content-type in root 'part' label */
672                             proto_item_append_text(ti, " (%s)", content_type_str);
673 
674                             /* find the "name" parameter in case we don't find a content disposition "filename" */
675                             mimetypename = ws_find_media_type_parameter(pinfo->pool, message_info.media_str, "name");
676 
677                             if(strncmp(content_type_str, "application/octet-stream",
678                                     sizeof("application/octet-stream")-1) == 0) {
679                                 is_raw_data = TRUE;
680                             }
681 
682                             /* there are only 2 body parts possible and each part has specific content types */
683                             if(m_info->protocol && idx == 0
684                                 && (is_raw_data || g_ascii_strncasecmp(content_type_str, m_info->protocol,
685                                                         strlen(m_info->protocol)) != 0))
686                             {
687                                 return -1;
688                             }
689                         }
690                         break;
691                     case POS_CONTENT_ENCODING:
692                         {
693                             /* The Content-Encoding starts at colon_offset + 1 */
694                             char *crp = strchr(value_str, '\r');
695 
696                             if (crp != NULL) {
697                                 *crp = '\0';
698                             }
699 #ifdef HAVE_ZLIB
700                             content_encoding_str = wmem_ascii_strdown(pinfo->pool, value_str, -1);
701 #endif
702                         }
703                         break;
704                     case POS_CONTENT_TRANSFER_ENCODING:
705                         {
706                             /* The Content-Transferring starts at colon_offset + 1 */
707                             char *crp = strchr(value_str, '\r');
708 
709                             if (crp != NULL) {
710                                 *crp = '\0';
711                             }
712 
713                             content_trans_encoding_str = wmem_ascii_strdown(pinfo->pool, value_str, -1);
714                         }
715                         break;
716                     case POS_CONTENT_DISPOSITION:
717                         {
718                             /* find the "filename" parameter */
719                             filename = ws_find_media_type_parameter(pinfo->pool, value_str, "filename");
720                         }
721                         break;
722                     case POS_CONTENT_ID:
723                         message_info.content_id = wmem_strdup(pinfo->pool, value_str);
724                         break;
725                     default:
726                         break;
727                 }
728             }
729         }
730         offset = next_offset;
731     }
732 
733     body_start = next_offset;
734 
735     /*
736      * Process the body
737      */
738 
739     {
740         gint body_len = boundary_start - body_start;
741         tvbuff_t *tmp_tvb = tvb_new_subset_length(tvb, body_start, body_len);
742         /*
743          * If multipart subtype is encrypted the protcol string was set.
744          *
745          * See MS-WSMV section 2.2.9.1.2.1 "HTTP Headers":
746          *
747          *  https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-wsmv/b79927c2-96be-4801-aa68-180db95593f9
748          *
749          * There are only 2 body parts possible, and each part has specific
750          * content types.
751          */
752         if(m_info->protocol && idx == 1 && is_raw_data)
753         {
754             gssapi_encrypt_info_t  encrypt;
755 
756             memset(&encrypt, 0, sizeof(encrypt));
757             encrypt.decrypt_gssapi_tvb=DECRYPT_GSSAPI_NORMAL;
758 
759             dissect_kerberos_encrypted_message(tmp_tvb, pinfo, subtree, &encrypt);
760 
761             if(encrypt.gssapi_decrypted_tvb){
762                     tmp_tvb = encrypt.gssapi_decrypted_tvb;
763                     is_raw_data = FALSE;
764                     content_type_str = m_info->orig_content_type;
765                     message_info.media_str = m_info->orig_parameters;
766             } else if(encrypt.gssapi_encrypted_tvb) {
767                     tmp_tvb = encrypt.gssapi_encrypted_tvb;
768                     proto_tree_add_expert(tree, pinfo, &ei_multipart_decryption_not_possible, tmp_tvb, 0, -1);
769             }
770         }
771 
772         if (!is_raw_data &&
773             content_type_str) {
774 
775             /*
776              * subdissection
777              */
778             gboolean dissected;
779 
780             /*
781              * Try and remove any content transfer encoding so that each sub-dissector
782              * doesn't have to do it itself
783              *
784              */
785 
786             if(content_trans_encoding_str && remove_base64_encoding) {
787 
788                 if(!g_ascii_strncasecmp(content_trans_encoding_str, "base64", 6))
789                     tmp_tvb = base64_decode(pinfo, tmp_tvb, filename ? filename : (mimetypename ? mimetypename : content_type_str));
790 
791             }
792 
793 #ifdef HAVE_ZLIB
794             if(content_encoding_str && uncompress_data) {
795 
796                 if(g_ascii_strncasecmp(content_encoding_str,"gzip",4) == 0 ||
797                    g_ascii_strncasecmp(content_encoding_str,"deflate",7) == 0 ||
798                    g_ascii_strncasecmp(content_encoding_str,"x-gzip",6) == 0 ||
799                    g_ascii_strncasecmp(content_encoding_str,"x-deflate",9) == 0){
800                    /* The body is gzip:ed */
801                     tvbuff_t *uncompress_tvb = tvb_uncompress(tmp_tvb, 0, body_len);
802                     if (uncompress_tvb) {
803                         tmp_tvb = uncompress_tvb;
804                         add_new_data_source(pinfo, tmp_tvb, "gunzipped data");
805                     }
806                 }
807             }
808 #endif
809 
810             /*
811              * First try the dedicated multipart dissector table
812              */
813             dissected = dissector_try_string(multipart_media_subdissector_table,
814                         content_type_str, tmp_tvb, pinfo, subtree, &message_info);
815             if (! dissected) {
816                 /*
817                  * Fall back to the default media dissector table
818                  */
819                 dissected = dissector_try_string(media_type_dissector_table,
820                         content_type_str, tmp_tvb, pinfo, subtree, &message_info);
821             }
822             if (! dissected) {
823                 const char *save_match_string = pinfo->match_string;
824                 pinfo->match_string = content_type_str;
825                 call_dissector_with_data(media_handle, tmp_tvb, pinfo, subtree, &message_info);
826                 pinfo->match_string = save_match_string;
827             }
828             message_info.media_str = NULL; /* Shares same memory as content_type_str */
829         } else {
830             call_data_dissector(tmp_tvb, pinfo, subtree);
831         }
832         proto_item_set_len(ti, boundary_start - start);
833         if (*last_boundary == TRUE) {
834            proto_tree_add_item(tree, hf_multipart_last_boundary, tvb, boundary_start, boundary_line_len, ENC_NA|ENC_ASCII);
835         } else {
836            proto_tree_add_item(tree, hf_multipart_boundary, tvb, boundary_start, boundary_line_len, ENC_NA|ENC_ASCII);
837         }
838 
839         return boundary_start + boundary_line_len;
840     }
841 }
842 
843 /*
844  * Call this method to actually dissect the multipart body.
845  * NOTE - Only do so if a boundary string has been found!
846  */
dissect_multipart(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,void * data)847 static int dissect_multipart(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
848 {
849     proto_tree *subtree;
850     proto_item *ti;
851     proto_item *type_ti;
852     http_message_info_t *message_info = (http_message_info_t *)data;
853     multipart_info_t *m_info = get_multipart_info(pinfo, message_info);
854     gint header_start = 0;
855     gint body_index = 0;
856     gboolean last_boundary = FALSE;
857 
858     if (m_info == NULL) {
859         /*
860          * We can't get the required multipart information
861          */
862         proto_tree_add_expert(tree, pinfo, &ei_multipart_no_required_parameter, tvb, 0, -1);
863         call_data_dissector(tvb, pinfo, tree);
864         return tvb_reported_length(tvb);
865     }
866 
867     /* Add stuff to the protocol tree */
868     ti = proto_tree_add_item(tree, proto_multipart,
869           tvb, 0, -1, ENC_NA);
870     subtree = proto_item_add_subtree(ti, ett_multipart);
871     proto_item_append_text(ti, ", Type: %s, Boundary: \"%s\"",
872           m_info->type, m_info->boundary);
873 
874     /* Show multi-part type as a generated field */
875     type_ti = proto_tree_add_string(subtree, hf_multipart_type,
876           tvb, 0, 0, pinfo->match_string);
877     proto_item_set_generated(type_ti);
878 
879     /*
880      * Make no entries in Protocol column and Info column on summary display,
881      * but stop sub-dissectors from clearing entered text in summary display.
882      */
883     col_set_fence(pinfo->cinfo, COL_INFO);
884 
885     /*
886      * Process the multipart preamble
887      */
888     header_start = process_preamble(subtree, tvb, m_info, &last_boundary);
889     if (header_start == -1) {
890         call_data_dissector(tvb, pinfo, subtree);
891         return tvb_reported_length(tvb);
892     }
893     /*
894      * Process the encapsulated bodies
895      */
896     while (last_boundary == FALSE) {
897         header_start = process_body_part(subtree, tvb, message_info, m_info,
898                 pinfo, header_start, body_index++, &last_boundary);
899         if (header_start == -1) {
900             return tvb_reported_length(tvb);
901         }
902     }
903     /*
904      * Process the multipart trailer
905      */
906     if (tvb_reported_length_remaining(tvb, header_start) > 0) {
907        proto_tree_add_item(subtree, hf_multipart_trailer, tvb, header_start, -1, ENC_NA);
908     }
909 
910     return tvb_reported_length(tvb);
911 }
912 
913 /* Returns index of method in multipart_headers */
914 static gint
is_known_multipart_header(const char * header_str,guint len)915 is_known_multipart_header(const char *header_str, guint len)
916 {
917     guint i;
918 
919     for (i = 1; i < array_length(multipart_headers); i++) {
920         if (len == strlen(multipart_headers[i].name) &&
921             g_ascii_strncasecmp(header_str, multipart_headers[i].name, len) == 0)
922             return i;
923         if (multipart_headers[i].compact_name != NULL &&
924             len == strlen(multipart_headers[i].compact_name) &&
925             g_ascii_strncasecmp(header_str, multipart_headers[i].compact_name, len) == 0)
926             return i;
927     }
928 
929     return -1;
930 }
931 
932 /*
933  * Register the protocol with Wireshark.
934  *
935  * This format is required because a script is used to build the C function
936  * that calls all the protocol registration.
937  */
938 
939 void
proto_register_multipart(void)940 proto_register_multipart(void)
941 {
942 
943 /* Setup list of header fields  See Section 1.6.1 for details */
944     static hf_register_info hf[] = {
945         { &hf_multipart_type,
946           {   "Type",
947               "mime_multipart.type",
948               FT_STRING, BASE_NONE, NULL, 0x00,
949               "MIME multipart encapsulation type", HFILL
950           }
951         },
952         { &hf_multipart_part,
953           {   "Encapsulated multipart part",
954               "mime_multipart.part",
955               FT_STRING, BASE_NONE, NULL, 0x00,
956               NULL, HFILL
957           }
958         },
959         { &hf_multipart_sec_token_len,
960           {   "Length of security token",
961               "mime_multipart.header.sectoken-length",
962               FT_UINT32, BASE_DEC, NULL, 0x00,
963               "Length of the Kerberos BLOB which follows this token", HFILL
964           }
965         },
966         { &hf_header_array[POS_CONTENT_DESCRIPTION],
967           {   "Content-Description",
968               "mime_multipart.header.content-description",
969               FT_STRING, BASE_NONE, NULL, 0x00,
970               "Content-Description Header", HFILL
971           }
972         },
973         { &hf_header_array[POS_CONTENT_DISPOSITION],
974           {   "Content-Disposition",
975               "mime_multipart.header.content-disposition",
976               FT_STRING, BASE_NONE, NULL, 0x00,
977               "RFC 2183: Content-Disposition Header", HFILL
978           }
979         },
980         { &hf_header_array[POS_CONTENT_ENCODING],
981           {   "Content-Encoding",
982               "mime_multipart.header.content-encoding",
983               FT_STRING, BASE_NONE, NULL, 0x00,
984               "Content-Encoding Header", HFILL
985           }
986         },
987         { &hf_header_array[POS_CONTENT_ID],
988           {   "Content-Id",
989               "mime_multipart.header.content-id",
990               FT_STRING, BASE_NONE, NULL, 0x00,
991               "RFC 2045: Content-Id Header", HFILL
992           }
993         },
994         { &hf_header_array[POS_CONTENT_LANGUAGE],
995           {   "Content-Language",
996               "mime_multipart.header.content-language",
997               FT_STRING, BASE_NONE, NULL, 0x00,
998               "Content-Language Header", HFILL
999           }
1000         },
1001         { &hf_header_array[POS_CONTENT_LENGTH],
1002           {   "Content-Length",
1003               "mime_multipart.header.content-length",
1004               FT_STRING, BASE_NONE, NULL, 0x0,
1005               "Content-Length Header", HFILL
1006           }
1007         },
1008         { &hf_header_array[POS_CONTENT_TRANSFER_ENCODING],
1009           {   "Content-Transfer-Encoding",
1010               "mime_multipart.header.content-transfer-encoding",
1011               FT_STRING, BASE_NONE, NULL, 0x00,
1012               "RFC 2045: Content-Transfer-Encoding Header", HFILL
1013           }
1014         },
1015         { &hf_header_array[POS_CONTENT_TYPE],
1016           {   "Content-Type",
1017               "mime_multipart.header.content-type",
1018               FT_STRING, BASE_NONE,NULL,0x0,
1019               "Content-Type Header", HFILL
1020           }
1021         },
1022         { &hf_header_array[POS_ORIGINALCONTENT],
1023           {   "OriginalContent",
1024               "mime_multipart.header.originalcontent",
1025               FT_STRING, BASE_NONE,NULL,0x0,
1026               "Original Content-Type Header", HFILL
1027           }
1028         },
1029 
1030       /* Generated from convert_proto_tree_add_text.pl */
1031       { &hf_multipart_first_boundary, { "First boundary", "mime_multipart.first_boundary", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1032       { &hf_multipart_preamble, { "Preamble", "mime_multipart.preamble", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1033       { &hf_multipart_last_boundary, { "Last boundary", "mime_multipart.last_boundary", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1034       { &hf_multipart_boundary, { "Boundary", "mime_multipart.boundary", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1035       { &hf_multipart_trailer, { "Trailer", "mime_multipart.trailer", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1036 
1037     };
1038 
1039     /*
1040      * Preferences
1041      */
1042     module_t *multipart_module;
1043     expert_module_t* expert_multipart;
1044 
1045 
1046     /*
1047      * Setup protocol subtree array
1048      */
1049     static gint *ett[] = {
1050         &ett_multipart,
1051         &ett_multipart_main,
1052         &ett_multipart_body,
1053     };
1054 
1055     static ei_register_info ei[] = {
1056         { &ei_multipart_no_required_parameter, { "mime_multipart.no_required_parameter", PI_PROTOCOL, PI_ERROR, "The multipart dissector could not find a required parameter.", EXPFILL }},
1057         { &ei_multipart_decryption_not_possible, { "mime_multipart.decryption_not_possible", PI_UNDECODED, PI_WARN, "The multipart dissector could not decrypt the message.", EXPFILL }},
1058     };
1059 
1060     /*
1061      * Register the protocol name and description
1062      */
1063     proto_multipart = proto_register_protocol(
1064         "MIME Multipart Media Encapsulation",
1065         "MIME multipart",
1066         "mime_multipart");
1067 
1068     /*
1069      * Required function calls to register
1070      * the header fields and subtrees used.
1071      */
1072     proto_register_field_array(proto_multipart, hf, array_length(hf));
1073     proto_register_subtree_array(ett, array_length(ett));
1074     expert_multipart = expert_register_protocol(proto_multipart);
1075     expert_register_field_array(expert_multipart, ei, array_length(ei));
1076 
1077     multipart_module = prefs_register_protocol(proto_multipart, NULL);
1078 
1079     prefs_register_bool_preference(multipart_module,
1080                                    "display_unknown_body_as_text",
1081                                    "Display bodies without media type as text",
1082                                    "Display multipart bodies with no media type dissector"
1083                                    " as raw text (may cause problems with binary data).",
1084                                    &display_unknown_body_as_text);
1085 
1086     prefs_register_bool_preference(multipart_module,
1087                                    "remove_base64_encoding",
1088                                    "Remove base64 encoding from bodies",
1089                                    "Remove any base64 content-transfer encoding from bodies. "
1090                                    "This supports export of the body and its further dissection.",
1091                                    &remove_base64_encoding);
1092 
1093 #ifdef HAVE_ZLIB
1094     prefs_register_bool_preference(multipart_module,
1095                                    "uncompress_data",
1096                                    "Uncompress parts which are compressed",
1097                                    "Uncompress parts which are compressed. GZIP for example. "
1098                                    "This supports export of the body and its further dissection.",
1099                                    &uncompress_data);
1100 #endif
1101 
1102     /*
1103      * Dissectors requiring different behavior in cases where the media
1104      * is contained in a multipart entity should register their multipart
1105      * dissector in the dissector table below, which is similar to the
1106      * "media_type" dissector table defined in the HTTP dissector code.
1107      */
1108     multipart_media_subdissector_table = register_dissector_table(
1109         "multipart_media_type",
1110         "Internet media type (for multipart processing)",
1111         proto_multipart, FT_STRING, BASE_NONE);
1112 }
1113 
1114 
1115 /* If this dissector uses sub-dissector registration add a registration routine.
1116    This format is required because a script is used to find these routines and
1117    create the code that calls these routines.
1118 */
1119 void
proto_reg_handoff_multipart(void)1120 proto_reg_handoff_multipart(void)
1121 {
1122     dissector_handle_t multipart_handle;
1123 
1124     /*
1125      * When we cannot display the data, call the data dissector.
1126      * When there is no dissector for the given media, call the media dissector.
1127      */
1128     media_handle = find_dissector_add_dependency("media", proto_multipart);
1129     gssapi_handle = find_dissector_add_dependency("gssapi", proto_multipart);
1130 
1131     /*
1132      * Get the content type and Internet media type table
1133      */
1134     media_type_dissector_table = find_dissector_table("media_type");
1135 
1136     /*
1137      * Handle for multipart dissection
1138      */
1139     multipart_handle = create_dissector_handle(
1140             dissect_multipart, proto_multipart);
1141 
1142     dissector_add_string("media_type",
1143             "multipart/mixed", multipart_handle);
1144     dissector_add_string("media_type",
1145             "multipart/related", multipart_handle);
1146     dissector_add_string("media_type",
1147             "multipart/alternative", multipart_handle);
1148     dissector_add_string("media_type",
1149             "multipart/form-data", multipart_handle);
1150     dissector_add_string("media_type",
1151             "multipart/report", multipart_handle);
1152     dissector_add_string("media_type",
1153             "multipart/signed", multipart_handle);
1154     dissector_add_string("media_type",
1155             "multipart/encrypted", multipart_handle);
1156 
1157     /*
1158      * Supply an entry to use for unknown multipart subtype.
1159      * See RFC 2046, section 5.1.3
1160      */
1161     dissector_add_string("media_type",
1162             "multipart/", multipart_handle);
1163 }
1164 
1165 /*
1166  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
1167  *
1168  * Local variables:
1169  * c-basic-offset: 4
1170  * tab-width: 8
1171  * indent-tabs-mode: nil
1172  * End:
1173  *
1174  * vi: set shiftwidth=4 tabstop=8 expandtab:
1175  * :indentSize=4:tabSize=8:noTabs=true:
1176  */
1177