Example #1
0
/* Retrieve the media information from pinfo->private_data,
 * and compute the boundary string and its length.
 * Return a pointer to a filled-in multipart_info_t, or NULL on failure.
 *
 * Boundary delimiters must not appear within the encapsulated material,
 * and must be no longer than 70 characters, not counting the two
 * leading hyphens. (quote from rfc2046)
 */
static multipart_info_t *
get_multipart_info(packet_info *pinfo, const char *str)
{
    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;
    gint dummy;

    if ((type == NULL) || (str == NULL)) {
        /*
         * We need both a content type AND parameters
         * for multipart dissection.
         */
        return NULL;
    }

    /* Clean up the parameters */
    parameters = unfold_and_compact_mime_header(str, &dummy);

    start_boundary = find_parameter(parameters, "boundary=", &len_boundary);

    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_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;
}
/* Retrieve the media information from pinfo->private_data,
 * and compute the boundary string and its length.
 * Return a pointer to a filled-in multipart_info_t, or NULL on failure.
 * 
 * Boundary delimiters must not appear within the encapsulated material,
 * and must be no longer than 70 characters, not counting the two
 * leading hyphens. (quote from rfc2046)
 */
static multipart_info_t *
get_multipart_info(packet_info *pinfo)
{
	const char *start;
	int len = 0;
	multipart_info_t *m_info = NULL;
	const char *type = pinfo->match_string;
	char *parameters;
	gint dummy;

	if ((type == NULL) || (pinfo->private_data == NULL)) {
		/*
		 * We need both a content type AND parameters
		 * for multipart dissection.
		 */
		return NULL;
	}

	/* Clean up the parameters */
	parameters = unfold_and_compact_mime_header(pinfo->private_data, &dummy);

	start = find_parameter(parameters, "boundary=", &len);

	if(!start) {
		g_free(parameters);
		return NULL;
	}
	
	/*
	 * There is a value for the boundary string
	 */
	m_info = g_malloc(sizeof(multipart_info_t));
	m_info->type = type;
	m_info->boundary = g_strndup(start, len);
	m_info->boundary_length = len;
	g_free(parameters);

	return m_info;
}
/*
 * Process a multipart body-part:
 *      MIME-part-headers [ line-end *OCTET ]
 *      line-end dashed-boundary transport-padding line-end
 *
 * If applicable, call a media subdissector.
 *
 * 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,
        gboolean *last_boundary)
{
    proto_tree *subtree;
    proto_item *ti;
    gint offset = start, next_offset = 0;
    char *parameters = NULL;
    gint body_start, boundary_start, boundary_line_len;

    gchar *content_type_str = NULL;
    gchar *content_encoding_str = NULL;
    char *filename = NULL;
    char *mimetypename = NULL;
    int  len = 0;
    gboolean last_field = FALSE;

    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);

    /*
     * Process the MIME-part-headers
     */

    while (!last_field)
    {
        gint colon_offset;
        char *hdr_str;
        char *header_str;

        /* Look for the end of the header (denoted by cr)
         * 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_length_remaining(tvb, offset)+offset, &last_field);
        /* If cr not found, won't have advanced - get out to avoid infinite loop! */
        if (next_offset == offset) {
            break;
        }

        hdr_str = tvb_get_string(wmem_packet_scope(), tvb, offset, next_offset - offset);

        header_str = unfold_and_compact_mime_header(hdr_str, &colon_offset);
        if (colon_offset <= 0) {
           proto_tree_add_text(subtree, tvb, offset, next_offset - offset,
                 "%s",
                 tvb_format_text(tvb, offset, next_offset - offset));
        } else {
            gint hf_index;

            /* Split header name from header value */
            header_str[colon_offset] = '\0';
            hf_index = is_known_multipart_header(header_str, colon_offset);

            if (hf_index == -1) {
               proto_tree_add_text(subtree, tvb, offset,
                     next_offset - offset,
                     "%s",
                     tvb_format_text(tvb, offset, next_offset - offset));
            } else {
                char *value_str = header_str + colon_offset + 1;

                proto_tree_add_string_format(subtree,
                      hf_header_array[hf_index], tvb,
                      offset, next_offset - offset,
                      (const char *)value_str, "%s",
                      tvb_format_text(tvb, offset, next_offset - offset));

                switch (hf_index) {
                    case POS_CONTENT_TYPE:
                        {
                            /* The Content-Type starts at colon_offset + 1 */
                            gint semicolon_offset = index_of_char(
                                    value_str, ';');

                            if (semicolon_offset > 0) {
                                value_str[semicolon_offset] = '\0';
                                parameters = wmem_strdup(wmem_packet_scope(), value_str + semicolon_offset + 1);
                            } else {
                                parameters = NULL;
                            }

                            content_type_str = wmem_ascii_strdown(wmem_packet_scope(), value_str, -1);

                            /* Show content-type in root 'part' label */
                            proto_item_append_text(ti, " (%s)", content_type_str);

                            /* find the "name" parameter in case we don't find a content disposition "filename" */
                            if((mimetypename = find_parameter(parameters, "name=", &len)) != NULL) {
                              mimetypename = g_strndup(mimetypename, len);
                            }
                        }


                        break;
                        case POS_CONTENT_TRANSFER_ENCODING:
                        {
                            /* The Content-Transfeing starts at colon_offset + 1 */
                            gint cr_offset = index_of_char(value_str, '\r');

                            if (cr_offset > 0) {
                                value_str[cr_offset] = '\0';
                            }

                            content_encoding_str = wmem_ascii_strdown(wmem_packet_scope(), value_str, -1);
                        }
                        break;
                        case POS_CONTENT_DISPOSITION:
                            {
                            /* find the "filename" parameter */
                            if((filename = find_parameter(value_str, "filename=", &len)) != NULL) {
                                filename = g_strndup(filename, len);
                            }
                        }
                        break;
                    default:
                        break;
                }
            }
        }
        offset = next_offset;
    }

    body_start = next_offset;

    /*
     * 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(tvb, body_start,
                body_len, body_len);

        if (content_type_str) {

            /*
             * subdissection
             */
            void *save_private_data = pinfo->private_data;
            gboolean dissected;

            /*
             * Try and remove any content transfer encoding so that each sub-dissector
             * doesn't have to do it itself
             *
             */

            if(content_encoding_str && remove_base64_encoding) {

                if(!g_ascii_strncasecmp(content_encoding_str, "base64", 6))
                    tmp_tvb = base64_decode(pinfo, tmp_tvb, filename ? filename : (mimetypename ? mimetypename : content_type_str));

            }

            pinfo->private_data = parameters;
            /*
             * First try the dedicated multipart dissector table
             */
            dissected = dissector_try_string(multipart_media_subdissector_table,
                        content_type_str, tmp_tvb, pinfo, subtree, NULL);
            if (! dissected) {
                /*
                 * Fall back to the default media dissector table
                 */
                dissected = dissector_try_string(media_type_dissector_table,
                        content_type_str, tmp_tvb, pinfo, subtree, NULL);
            }
            if (! dissected) {
                const char *save_match_string = pinfo->match_string;
                pinfo->match_string = content_type_str;
                call_dissector(media_handle, tmp_tvb, pinfo, subtree);
                pinfo->match_string = save_match_string;
            }
            pinfo->private_data = save_private_data;
            parameters = NULL; /* Shares same memory as content_type_str */
        } else {
            call_dissector(data_handle, tmp_tvb, pinfo, subtree);
        }
        proto_item_set_len(ti, boundary_start - start);
        if (*last_boundary == TRUE) {
           proto_tree_add_text(tree, tvb,
                 boundary_start, boundary_line_len,
                 "Last boundary: %s",
                 tvb_format_text(tvb, boundary_start,
                    boundary_line_len));
        } else {
           proto_tree_add_text(tree, tvb,
                 boundary_start, boundary_line_len,
                 "Boundary: %s",
                 tvb_format_text(tvb, boundary_start,
                    boundary_line_len));
        }

        g_free(filename);
        g_free(mimetypename);

        return boundary_start + boundary_line_len;
    }

    g_free(filename);
    g_free(mimetypename);

    return -1;
}
Example #4
0
/*
 * Process a multipart body-part:
 *      MIME-part-headers [ line-end *OCTET ]
 *      line-end dashed-boundary transport-padding line-end
 *
 * If applicable, call a media subdissector.
 *
 * Return the offset to the start of the next body-part.
 */
static gint
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;
    proto_item *ti;
    gint offset = start, next_offset = 0;
    char *parameters = NULL;
    gint body_start, boundary_start, boundary_line_len;

    gchar *content_type_str = NULL;
    gchar *content_encoding_str = NULL;
    char *filename = NULL;
    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
     */

    while (!last_field)
    {
        gint colon_offset;
        char *hdr_str;
        char *header_str;

        /* Look for the end of the header (denoted by cr)
         * 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 && (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) {
            /* 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;

            /* Split header name from header value */
            header_str[colon_offset] = '\0';
            hf_index = is_known_multipart_header(header_str, colon_offset);

            if (hf_index == -1) {
                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;

                proto_tree_add_string_format(subtree,
                                             hf_header_array[hf_index], tvb,
                                             offset, next_offset - offset,
                                             (const char *)value_str, "%s",
                                             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 */
                    gint semicolon_offset = index_of_char(
                                                value_str, ';');

                    if (semicolon_offset > 0) {
                        value_str[semicolon_offset] = '\0';
                        parameters = wmem_strdup(wmem_packet_scope(), value_str + semicolon_offset + 1);
                    } else {
                        parameters = NULL;
                    }

                    content_type_str = wmem_ascii_strdown(wmem_packet_scope(), value_str, -1);

                    /* Show content-type in root 'part' label */
                    proto_item_append_text(ti, " (%s)", content_type_str);

                    /* find the "name" parameter in case we don't find a content disposition "filename" */
                    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:
                {
                    /* The Content-Transferring starts at colon_offset + 1 */
                    gint cr_offset = index_of_char(value_str, '\r');

                    if (cr_offset > 0) {
                        value_str[cr_offset] = '\0';
                    }

                    content_encoding_str = wmem_ascii_strdown(wmem_packet_scope(), value_str, -1);
                }
                break;
                case POS_CONTENT_DISPOSITION:
                {
                    /* find the "filename" parameter */
                    if((filename = find_parameter(value_str, "filename=", &len)) != NULL) {
                        filename = g_strndup(filename, len);
                    }
                }
                break;
                default:
                    break;
                }
            }
        }
        offset = next_offset;
    }

    body_start = next_offset;

    /*
     * Process the body
     */

    {
        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 (!is_raw_data &&
                content_type_str) {

            /*
             * subdissection
             */
            gboolean dissected;

            /*
             * Try and remove any content transfer encoding so that each sub-dissector
             * doesn't have to do it itself
             *
             */

            if(content_encoding_str && remove_base64_encoding) {

                if(!g_ascii_strncasecmp(content_encoding_str, "base64", 6))
                    tmp_tvb = base64_decode(pinfo, tmp_tvb, filename ? filename : (mimetypename ? mimetypename : content_type_str));

            }

            /*
             * First try the dedicated multipart dissector table
             */
            dissected = dissector_try_string(multipart_media_subdissector_table,
                                             content_type_str, tmp_tvb, pinfo, subtree, parameters);
            if (! dissected) {
                /*
                 * Fall back to the default media dissector table
                 */
                dissected = dissector_try_string(media_type_dissector_table,
                                                 content_type_str, tmp_tvb, pinfo, subtree, parameters);
            }
            if (! dissected) {
                const char *save_match_string = pinfo->match_string;
                pinfo->match_string = content_type_str;
                call_dissector_with_data(media_handle, tmp_tvb, pinfo, subtree, parameters);
                pinfo->match_string = save_match_string;
            }
            parameters = NULL; /* Shares same memory as content_type_str */
        } else {
            call_dissector(data_handle, tmp_tvb, pinfo, subtree);
        }
        proto_item_set_len(ti, boundary_start - start);
        if (*last_boundary == TRUE) {
            proto_tree_add_item(tree, hf_multipart_last_boundary, tvb, boundary_start, boundary_line_len, ENC_NA|ENC_ASCII);
        } else {
            proto_tree_add_item(tree, hf_multipart_boundary, tvb, boundary_start, boundary_line_len, ENC_NA|ENC_ASCII);
        }

        g_free(filename);
        g_free(mimetypename);

        return boundary_start + boundary_line_len;
    }
}