aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-multipart.c
diff options
context:
space:
mode:
authorDiablosOffens <DiablosOffens@gmx.de>2015-11-29 12:13:40 +0100
committerAnders Broman <a.broman58@gmail.com>2016-02-04 09:49:41 +0000
commit8b0c2c3837c54b9196effdb06113c959b0a04af9 (patch)
tree2439ce93d74c2ee0a76a13504636ccc5c0ae269e /epan/dissectors/packet-multipart.c
parent8d0455c910d004c4d64162c2182c9f8be6fd4396 (diff)
Multipart: add dissection of multipart/encrypted and also fix problems to find the right boundaries
Bug: 11978 Change-Id: Ia31e1b451bfae268e8ede84bddffd5dae8d97d8e Signed-off-by: Alexis La Goutte <alexis.lagoutte@gmail.com> Reviewed-on: https://code.wireshark.org/review/12281 Petri-Dish: Anders Broman <a.broman58@gmail.com> Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org> Reviewed-by: Anders Broman <a.broman58@gmail.com>
Diffstat (limited to 'epan/dissectors/packet-multipart.c')
-rw-r--r--epan/dissectors/packet-multipart.c235
1 files changed, 197 insertions, 38 deletions
diff --git a/epan/dissectors/packet-multipart.c b/epan/dissectors/packet-multipart.c
index 3248e564c1..8698d5f4d1 100644
--- a/epan/dissectors/packet-multipart.c
+++ b/epan/dissectors/packet-multipart.c
@@ -60,8 +60,12 @@
#include <epan/packet.h>
#include <epan/expert.h>
#include <epan/prefs.h>
+#include <wsutil/str_util.h>
#include "packet-imf.h"
+#include "packet-dcerpc.h"
+#include "packet-gssapi.h"
+
void proto_register_multipart(void);
void proto_reg_handoff_multipart(void);
@@ -85,7 +89,8 @@ static gint ett_multipart_main = -1;
static gint ett_multipart_body = -1;
/* Generated from convert_proto_tree_add_text.pl */
-static expert_field ei_multipart_no_required_boundary_parameter = EI_INIT;
+static expert_field ei_multipart_no_required_parameter = EI_INIT;
+static expert_field ei_multipart_decryption_not_possible = EI_INIT;
/* Not sure that compact_name exists for multipart, but choose to keep
* the structure from SIP dissector, all the content- is also from SIP */
@@ -105,6 +110,7 @@ static const multipart_header_t multipart_headers[] = {
{ "Content-Length", "l" },
{ "Content-Transfer-Encoding", NULL },
{ "Content-Type", "c" },
+ { "OriginalContent", NULL }
};
#define POS_CONTENT_DISPOSITION 1
@@ -114,10 +120,12 @@ static const multipart_header_t multipart_headers[] = {
#define POS_CONTENT_LENGTH 5
#define POS_CONTENT_TRANSFER_ENCODING 6
#define POS_CONTENT_TYPE 7
+#define POS_ORIGINALCONTENT 8
/* Initialize the header fields */
static gint hf_multipart_type = -1;
static gint hf_multipart_part = -1;
+static gint hf_multipart_sec_token_len = -1;
static gint hf_header_array[] = {
-1, /* "Unknown-header" - Pad so that the real headers start at index 1 */
@@ -128,6 +136,7 @@ static gint hf_header_array[] = {
-1, /* "Content-Length" */
-1, /* "Content-Transfer-Encoding" */
-1, /* "Content-Type" */
+ -1, /* "OriginalContent" */
};
/* Define media_type/Content type table */
@@ -136,6 +145,7 @@ static dissector_table_t media_type_dissector_table;
/* Data and media dissector handles */
static dissector_handle_t data_handle;
static dissector_handle_t media_handle;
+static dissector_handle_t gssapi_handle;
/* Determines if bodies with no media type dissector should be displayed
* as raw text, may cause problems with images sound etc
@@ -149,6 +159,10 @@ typedef struct {
const char *type; /* Type of multipart */
char *boundary; /* Boundary string (enclosing quotes removed if any) */
guint boundary_length; /* Length of the boundary string */
+ char *protocol; /* Protocol string if encrypted multipart (enclosing quotes removed if any) */
+ guint protocol_length; /* Length of the protocol string */
+ char *orig_content_type; /* Content-Type of original message */
+ char *orig_parameters; /* Parameters for Content-Type of original message */
} multipart_info_t;
@@ -160,11 +174,11 @@ static gint
find_next_boundary(tvbuff_t *tvb, gint start, const guint8 *boundary,
gint boundary_len, gint *boundary_line_len, gboolean *last_boundary);
static gint
-process_preamble(proto_tree *tree, tvbuff_t *tvb, const guint8 *boundary,
- gint boundary_len, gboolean *last_boundary);
+process_preamble(proto_tree *tree, tvbuff_t *tvb, multipart_info_t *m_info,
+ gboolean *last_boundary);
static gint
-process_body_part(proto_tree *tree, tvbuff_t *tvb, const guint8 *boundary,
- gint boundary_len, packet_info *pinfo, gint start,
+process_body_part(proto_tree *tree, tvbuff_t *tvb, multipart_info_t *m_info,
+ packet_info *pinfo, gint start, gint idx,
gboolean *last_boundary);
static gint
is_known_multipart_header(const char *header_str, guint len);
@@ -395,8 +409,8 @@ static char *find_parameter(char *parameters, const char *key, int *retlen)
static multipart_info_t *
get_multipart_info(packet_info *pinfo, const char *str)
{
- const char *start;
- int len = 0;
+ const char *start_boundary, *start_protocol = NULL;
+ int len_boundary = 0, len_protocol = 0;
multipart_info_t *m_info = NULL;
const char *type = pinfo->match_string;
char *parameters;
@@ -413,19 +427,34 @@ get_multipart_info(packet_info *pinfo, const char *str)
/* Clean up the parameters */
parameters = unfold_and_compact_mime_header(str, &dummy);
- start = find_parameter(parameters, "boundary=", &len);
+ start_boundary = find_parameter(parameters, "boundary=", &len_boundary);
- if(!start) {
+ if(!start_boundary) {
return NULL;
}
+ if(strncmp(type, "multipart/encrypted", sizeof("multipart/encrypted")-1) == 0) {
+ start_protocol = find_parameter(parameters, "protocol=", &len_protocol);
+ if(!start_protocol) {
+ return NULL;
+ }
+ }
/*
* There is a value for the boundary string
*/
m_info = (multipart_info_t *)g_malloc(sizeof(multipart_info_t));
m_info->type = type;
- m_info->boundary = g_strndup(start, len);
- m_info->boundary_length = len;
+ m_info->boundary = g_strndup(start_boundary, len_boundary);
+ m_info->boundary_length = len_boundary;
+ if(start_protocol) {
+ m_info->protocol = g_strndup(start_protocol, len_protocol);
+ m_info->protocol_length = len_protocol;
+ } else {
+ m_info->protocol = NULL;
+ m_info->protocol_length = -1;
+ }
+ m_info->orig_content_type = NULL;
+ m_info->orig_parameters = NULL;
return m_info;
}
@@ -435,6 +464,9 @@ cleanup_multipart_info(void *data)
{
multipart_info_t *m_info = (multipart_info_t *)data;
if (m_info) {
+ if (m_info->protocol) {
+ g_free(m_info->protocol);
+ }
g_free(m_info->boundary);
g_free(m_info);
}
@@ -525,6 +557,15 @@ find_next_boundary(tvbuff_t *tvb, gint start, const guint8 *boundary,
*boundary_line_len = offset - boundary_start;
}
return boundary_start;
+ /* check if last before CRLF; some ignore the standard, so there is no CRLF before the boundary */
+ } else if ((tvb_strneql(tvb, boundary_start - 2, (const guint8 *)"--", 2) == 0)
+ && (tvb_strneql(tvb, boundary_start - (2 + boundary_len), boundary, boundary_len) == 0)
+ && (tvb_strneql(tvb, boundary_start - (2 + boundary_len + 2),
+ (const guint8 *)"--", 2) == 0)) {
+ boundary_start -= 2 + boundary_len + 2;
+ *boundary_line_len = next_offset - boundary_start;
+ *last_boundary = TRUE;
+ return boundary_start;
}
offset = next_offset;
}
@@ -539,11 +580,14 @@ find_next_boundary(tvbuff_t *tvb, gint start, const guint8 *boundary,
* Return the offset to the start of the first body-part.
*/
static gint
-process_preamble(proto_tree *tree, tvbuff_t *tvb, const guint8 *boundary,
- gint boundary_len, gboolean *last_boundary)
+process_preamble(proto_tree *tree, tvbuff_t *tvb, multipart_info_t *m_info,
+ gboolean *last_boundary)
{
gint boundary_start, boundary_line_len;
+ const guint8 *boundary = (guint8 *)m_info->boundary;
+ gint boundary_len = m_info->boundary_length;
+
boundary_start = find_first_boundary(tvb, 0, boundary, boundary_len,
&boundary_line_len, last_boundary);
if (boundary_start == 0) {
@@ -563,6 +607,28 @@ process_preamble(proto_tree *tree, tvbuff_t *tvb, const guint8 *boundary,
return -1;
}
+static void
+dissect_kerberos_encrypted_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gssapi_encrypt_info_t* encrypt)
+{
+ tvbuff_t *kerberos_tvb;
+ gint offset = 0, len;
+ guint8 *data;
+
+ proto_tree_add_item(tree, hf_multipart_sec_token_len, tvb, offset, sizeof(guint32), ENC_LITTLE_ENDIAN);
+ offset += sizeof(guint32);
+ len = tvb_reported_length_remaining(tvb, offset);
+
+ DISSECTOR_ASSERT(tvb_bytes_exist(tvb, offset, len));
+
+ data = (guint8 *) g_malloc(len);
+ tvb_memcpy(tvb, data, offset, len);
+ kerberos_tvb = tvb_new_child_real_data(tvb, data, len, len);
+ tvb_set_free_cb(kerberos_tvb, g_free);
+
+ add_new_data_source(pinfo, kerberos_tvb, "Kerberos Data");
+ call_dissector_with_data(gssapi_handle, kerberos_tvb, pinfo, tree, encrypt);
+}
+
/*
* Process a multipart body-part:
* MIME-part-headers [ line-end *OCTET ]
@@ -573,8 +639,8 @@ process_preamble(proto_tree *tree, tvbuff_t *tvb, const guint8 *boundary,
* Return the offset to the start of the next body-part.
*/
static gint
-process_body_part(proto_tree *tree, tvbuff_t *tvb, const guint8 *boundary,
- gint boundary_len, packet_info *pinfo, gint start,
+process_body_part(proto_tree *tree, tvbuff_t *tvb, multipart_info_t *m_info,
+ packet_info *pinfo, gint start, gint idx,
gboolean *last_boundary)
{
proto_tree *subtree;
@@ -589,10 +655,22 @@ process_body_part(proto_tree *tree, tvbuff_t *tvb, const guint8 *boundary,
char *mimetypename = NULL;
int len = 0;
gboolean last_field = FALSE;
+ gboolean is_raw_data = FALSE;
+
+ const guint8 *boundary = (guint8 *)m_info->boundary;
+ gint boundary_len = m_info->boundary_length;
ti = proto_tree_add_item(tree, hf_multipart_part, tvb, start, 0, ENC_ASCII|ENC_NA);
subtree = proto_item_add_subtree(ti, ett_multipart_body);
+ /* find the next boundary to find the end of this body part */
+ boundary_start = find_next_boundary(tvb, offset, boundary, boundary_len,
+ &boundary_line_len, last_boundary);
+
+ if (boundary_start <= 0) {
+ return -1;
+ }
+
/*
* Process the MIME-part-headers
*/
@@ -607,20 +685,33 @@ process_body_part(proto_tree *tree, tvbuff_t *tvb, const guint8 *boundary,
* 3:d argument to imf_find_field_end() maxlen; must be last offset in the tvb.
*/
next_offset = imf_find_field_end(tvb, offset, tvb_reported_length_remaining(tvb, offset)+offset, &last_field);
+ /* the following should never happen */
/* If cr not found, won't have advanced - get out to avoid infinite loop! */
+ /*
if (next_offset == offset) {
break;
}
- if (last_field) {
+ */
+ if (last_field && (next_offset+2) <= boundary_start) {
/* Add the extra CRLF of the last field */
next_offset += 2;
+ } else if((next_offset-2) == boundary_start) {
+ /* if CRLF is the start of next boundary it belongs to the boundary and not the field,
+ so it's the last field without CRLF */
+ last_field = TRUE;
+ next_offset -= 2;
+ } else if (next_offset > boundary_start) {
+ /* if there is no CRLF between last field and next boundary - trim it! */
+ next_offset = boundary_start;
}
hdr_str = tvb_get_string_enc(wmem_packet_scope(), tvb, offset, next_offset - offset, ENC_ASCII);
header_str = unfold_and_compact_mime_header(hdr_str, &colon_offset);
if (colon_offset <= 0) {
- proto_tree_add_format_text(subtree, tvb, offset, next_offset - offset);
+ /* if there is no colon it's no header, so break and add complete line to the body */
+ next_offset = offset;
+ break;
} else {
gint hf_index;
@@ -629,7 +720,13 @@ process_body_part(proto_tree *tree, tvbuff_t *tvb, const guint8 *boundary,
hf_index = is_known_multipart_header(header_str, colon_offset);
if (hf_index == -1) {
- proto_tree_add_format_text(subtree, tvb, offset, next_offset - offset);
+ if(isprint_string(hdr_str)) {
+ proto_tree_add_format_text(subtree, tvb, offset, next_offset - offset);
+ } else {
+ /* if the header name is unkown and not printable, break and add complete line to the body */
+ next_offset = offset;
+ break;
+ }
} else {
char *value_str = header_str + colon_offset + 1;
@@ -640,6 +737,27 @@ process_body_part(proto_tree *tree, tvbuff_t *tvb, const guint8 *boundary,
tvb_format_text(tvb, offset, next_offset - offset));
switch (hf_index) {
+ case POS_ORIGINALCONTENT:
+ {
+ gint semicolon_offset;
+ /* The Content-Type starts at colon_offset + 1 or after the type parameter */
+ char* type_str = find_parameter(value_str, "type=", NULL);
+ if(type_str != NULL) {
+ value_str = type_str;
+ }
+
+ semicolon_offset = index_of_char(
+ value_str, ';');
+
+ if (semicolon_offset > 0) {
+ value_str[semicolon_offset] = '\0';
+ m_info->orig_parameters = wmem_strdup(wmem_packet_scope(),
+ value_str + semicolon_offset + 1);
+ }
+
+ m_info->orig_content_type = wmem_ascii_strdown(wmem_packet_scope(), value_str, -1);
+ }
+ break;
case POS_CONTENT_TYPE:
{
/* The Content-Type starts at colon_offset + 1 */
@@ -662,9 +780,20 @@ process_body_part(proto_tree *tree, tvbuff_t *tvb, const guint8 *boundary,
if((mimetypename = find_parameter(parameters, "name=", &len)) != NULL) {
mimetypename = g_strndup(mimetypename, len);
}
- }
+ if(strncmp(content_type_str, "application/octet-stream",
+ sizeof("application/octet-stream")-1) == 0) {
+ is_raw_data = TRUE;
+ }
+ /* there are only 2 body parts possible and each part has specific content types */
+ if(m_info->protocol && idx == 0
+ && (is_raw_data || g_ascii_strncasecmp(content_type_str, m_info->protocol,
+ strlen(m_info->protocol)) != 0))
+ {
+ return -1;
+ }
+ }
break;
case POS_CONTENT_TRANSFER_ENCODING:
{
@@ -700,13 +829,34 @@ process_body_part(proto_tree *tree, tvbuff_t *tvb, const guint8 *boundary,
* Process the body
*/
- boundary_start = find_next_boundary(tvb, body_start, boundary, boundary_len,
- &boundary_line_len, last_boundary);
- if (boundary_start > 0) {
+ {
gint body_len = boundary_start - body_start;
tvbuff_t *tmp_tvb = tvb_new_subset_length(tvb, body_start, body_len);
+ /* if multipart subtype is encrypted the protcol string was set */
+ /* see: https://msdn.microsoft.com/en-us/library/cc251581.aspx */
+ /* there are only 2 body parts possible and each part has specific content types */
+ if(m_info->protocol && idx == 1 && is_raw_data)
+ {
+ gssapi_encrypt_info_t encrypt;
+
+ memset(&encrypt, 0, sizeof(encrypt));
+ encrypt.decrypt_gssapi_tvb=DECRYPT_GSSAPI_NORMAL;
+
+ dissect_kerberos_encrypted_message(tmp_tvb, pinfo, subtree, &encrypt);
+
+ if(encrypt.gssapi_decrypted_tvb){
+ tmp_tvb = encrypt.gssapi_decrypted_tvb;
+ is_raw_data = FALSE;
+ content_type_str = m_info->orig_content_type;
+ parameters = m_info->orig_parameters;
+ } else if(encrypt.gssapi_encrypted_tvb) {
+ tmp_tvb = encrypt.gssapi_encrypted_tvb;
+ proto_tree_add_expert(tree, pinfo, &ei_multipart_decryption_not_possible, tmp_tvb, 0, -1);
+ }
+ }
- if (content_type_str) {
+ if (!is_raw_data &&
+ content_type_str) {
/*
* subdissection
@@ -760,11 +910,6 @@ process_body_part(proto_tree *tree, tvbuff_t *tvb, const guint8 *boundary,
return boundary_start + boundary_line_len;
}
-
- g_free(filename);
- g_free(mimetypename);
-
- return -1;
}
/*
@@ -778,20 +923,17 @@ static int dissect_multipart(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree
proto_item *type_ti;
multipart_info_t *m_info = get_multipart_info(pinfo, (const char*)data);
gint header_start = 0;
- guint8 *boundary;
- gint boundary_len;
+ gint body_index = 0;
gboolean last_boundary = FALSE;
if (m_info == NULL) {
/*
* We can't get the required multipart information
*/
- proto_tree_add_expert(tree, pinfo, &ei_multipart_no_required_boundary_parameter, tvb, 0, -1);
+ proto_tree_add_expert(tree, pinfo, &ei_multipart_no_required_parameter, tvb, 0, -1);
call_dissector(data_handle, tvb, pinfo, tree);
return tvb_reported_length(tvb);
}
- boundary = (guint8 *)m_info->boundary;
- boundary_len = m_info->boundary_length;
/* Clean up the memory if an exception is thrown */
/* CLEANUP_PUSH(cleanup_multipart_info, m_info); */
@@ -816,8 +958,7 @@ static int dissect_multipart(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree
/*
* Process the multipart preamble
*/
- header_start = process_preamble(subtree, tvb, boundary,
- boundary_len, &last_boundary);
+ header_start = process_preamble(subtree, tvb, m_info, &last_boundary);
if (header_start == -1) {
call_dissector(data_handle, tvb, pinfo, subtree);
/* Clean up the dynamically allocated memory */
@@ -828,8 +969,8 @@ static int dissect_multipart(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree
* Process the encapsulated bodies
*/
while (last_boundary == FALSE) {
- header_start = process_body_part(subtree, tvb, boundary, boundary_len,
- pinfo, header_start, &last_boundary);
+ header_start = process_body_part(subtree, tvb, m_info,
+ pinfo, header_start, body_index++, &last_boundary);
if (header_start == -1) {
/* Clean up the dynamically allocated memory */
cleanup_multipart_info(m_info);
@@ -893,6 +1034,13 @@ proto_register_multipart(void)
NULL, HFILL
}
},
+ { &hf_multipart_sec_token_len,
+ { "Length of security token",
+ "mime_multipart.header.sectoken-length",
+ FT_UINT32, BASE_DEC, NULL, 0x00,
+ "Length of the Kerberos BLOB which follows this token", HFILL
+ }
+ },
{ &hf_header_array[POS_CONTENT_DISPOSITION],
{ "Content-Disposition",
"mime_multipart.header.content-disposition",
@@ -942,6 +1090,13 @@ proto_register_multipart(void)
"Content-Type Header", HFILL
}
},
+ { &hf_header_array[POS_ORIGINALCONTENT],
+ { "OriginalContent",
+ "mime_multipart.header.originalcontent",
+ FT_STRING, BASE_NONE,NULL,0x0,
+ "Original Content-Type Header", HFILL
+ }
+ },
/* Generated from convert_proto_tree_add_text.pl */
{ &hf_multipart_first_boundary, { "First boundary", "mime_multipart.first_boundary", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
@@ -969,7 +1124,8 @@ proto_register_multipart(void)
};
static ei_register_info ei[] = {
- { &ei_multipart_no_required_boundary_parameter, { "mime_multipart.no_required_boundary_parameter", PI_PROTOCOL, PI_ERROR, "The multipart dissector could not find the required boundary parameter.", EXPFILL }},
+ { &ei_multipart_no_required_parameter, { "mime_multipart.no_required_parameter", PI_PROTOCOL, PI_ERROR, "The multipart dissector could not find a required parameter.", EXPFILL }},
+ { &ei_multipart_decryption_not_possible, { "mime_multipart.decryption_not_possible", PI_UNDECODED, PI_WARN, "The multipart dissector could not decrypt the message.", EXPFILL }},
};
/*
@@ -1033,6 +1189,7 @@ proto_reg_handoff_multipart(void)
*/
data_handle = find_dissector("data");
media_handle = find_dissector("media");
+ gssapi_handle = find_dissector("gssapi");
/*
* Get the content type and Internet media type table
@@ -1057,6 +1214,8 @@ proto_reg_handoff_multipart(void)
"multipart/report", multipart_handle);
dissector_add_string("media_type",
"multipart/signed", multipart_handle);
+ dissector_add_string("media_type",
+ "multipart/encrypted", multipart_handle);
/*
* Supply an entry to use for unknown multipart subtype.