]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-mail: message-parser assert-crashfix
authorTimo Sirainen <timo.sirainen@dovecot.fi>
Sat, 30 Apr 2016 11:19:02 +0000 (14:19 +0300)
committerTimo Sirainen <timo.sirainen@dovecot.fi>
Sat, 30 Apr 2016 11:19:02 +0000 (14:19 +0300)
Crashes when multipart MIME header is missing end-of-headers line and the
boundary begins with the same prefix as one of the parent boundaries.
Broken by 7a12331c6

src/lib-mail/message-parser.c
src/lib-mail/test-message-parser.c

index 7f8e588d8333d03ed979c057a7a8653b6295798a..3a1aca7938325b8bf14c381233a3b27aee2af161 100644 (file)
@@ -174,6 +174,8 @@ message_part_append(pool_t pool, struct message_part *parent)
        struct message_part *p, *part, **list;
 
        i_assert(parent != NULL);
+       i_assert((parent->flags & (MESSAGE_PART_FLAG_MULTIPART |
+                                  MESSAGE_PART_FLAG_MESSAGE_RFC822)) != 0);
 
        part = p_new(pool, struct message_part, 1);
        part->parent = parent;
@@ -314,6 +316,8 @@ static int parse_part_finish(struct message_parser_ctx *ctx,
        struct message_part *part;
        size_t line_size;
 
+       i_assert(ctx->last_boundary == NULL);
+
        /* get back to parent MIME part, summing the child MIME part sizes
           into parent's body sizes */
        for (part = ctx->part; part != boundary->part; part = part->parent) {
@@ -625,8 +629,8 @@ static int parse_next_header(struct message_parser_ctx *ctx,
                   Content-Type. */
                i_assert(!ctx->multipart);
                part->flags = 0;
-               ctx->last_boundary = NULL;
        }
+       ctx->last_boundary = NULL;
 
        if (!ctx->part_seen_content_type ||
            (part->flags & MESSAGE_PART_FLAG_IS_MIME) == 0) {
index d682fcfcce071d44cac367df7dfec391b70071d4..3ea3907fd1a0345edbd6c3804deb7ce4d8661d97 100644 (file)
@@ -499,6 +499,81 @@ static const char input_msg[] =
        test_end();
 }
 
+static void test_message_parser_continuing_truncated_mime_boundary(void)
+{
+static const char input_msg[] =
+"Content-Type: multipart/mixed; boundary=\"a\"\n"
+"\n"
+"--a\n"
+"Content-Type: multipart/mixed; boundary=\"ab\"\n"
+"MIME-Version: 1.0\n"
+"--ab\n"
+"Content-Type: text/plain\n"
+"\n"
+"--ab--\n"
+"--a--\n\n";
+       struct message_parser_ctx *parser;
+       struct istream *input;
+       struct message_part *parts, *part;
+       struct message_block block;
+       pool_t pool;
+       int ret;
+
+       test_begin("message parser continuing truncated mime boundary");
+       pool = pool_alloconly_create("message parser", 10240);
+       input = test_istream_create(input_msg);
+
+       parser = message_parser_init(pool, input, 0, 0);
+       while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ;
+       test_assert(ret < 0);
+       message_parser_deinit(&parser, &parts);
+
+       part = parts;
+       test_assert(part->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
+       test_assert(part->header_size.lines == 2);
+       test_assert(part->header_size.physical_size == 45);
+       test_assert(part->header_size.virtual_size == 45+2);
+       test_assert(part->body_size.lines == 9);
+       test_assert(part->body_size.physical_size == 112);
+       test_assert(part->body_size.virtual_size == 112+9);
+
+       part = parts->children;
+       test_assert(part->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
+       test_assert(part->physical_pos == 49);
+       test_assert(part->header_size.lines == 1);
+       test_assert(part->header_size.physical_size == 45+17);
+       test_assert(part->header_size.virtual_size == 45+17+1);
+       test_assert(part->body_size.lines == 0);
+       test_assert(part->body_size.physical_size == 0);
+       test_assert(part->children == NULL);
+
+       /* this will not be a child, since the header was truncated. I guess
+          we could make it, but it would complicate the message-parser even
+          more. */
+       part = parts->children->next;
+       test_assert(part->flags == (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_IS_MIME));
+       test_assert(part->physical_pos == 117);
+       test_assert(part->header_size.lines == 1);
+       test_assert(part->header_size.physical_size == 25);
+       test_assert(part->header_size.virtual_size == 25+1);
+       test_assert(part->body_size.lines == 0);
+       test_assert(part->body_size.physical_size == 0);
+       test_assert(part->children == NULL);
+
+       part = parts->children->next->next;
+       test_assert(part->flags == (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_IS_MIME));
+       test_assert(part->header_size.lines == 0);
+       test_assert(part->header_size.physical_size == 0);
+       test_assert(part->body_size.lines == 0);
+       test_assert(part->body_size.physical_size == 0);
+       test_assert(part->children == NULL);
+       test_assert(part->next == NULL);
+
+       i_stream_unref(&input);
+       pool_unref(&pool);
+       test_end();
+}
+
 static void test_message_parser_no_eoh(void)
 {
        static const char input_msg[] = "a:b\n";
@@ -537,6 +612,7 @@ int main(void)
                test_message_parser_duplicate_mime_boundary,
                test_message_parser_garbage_suffix_mime_boundary,
                test_message_parser_continuing_mime_boundary,
+               test_message_parser_continuing_truncated_mime_boundary,
                test_message_parser_no_eoh,
                NULL
        };