]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-mail: message-parser - Support limiting max number of MIME parts
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Thu, 23 Apr 2020 14:09:33 +0000 (17:09 +0300)
committerAki Tuomi <aki.tuomi@open-xchange.com>
Wed, 27 May 2020 05:28:17 +0000 (08:28 +0300)
The default is to allow 10000 MIME parts. When it's reached, no more
MIME boundary lines will be recognized, so the rest of the mail belongs
to the last added MIME part.

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

index 4bb0c3dbfd78a517d2cc837ef9b65571c53fbbdc..1f9c66b8273cba26d3864bc7426acfc1f5c31ebf 100644 (file)
@@ -24,10 +24,12 @@ struct message_parser_ctx {
        struct message_part *parts, *part;
        const char *broken_reason;
        unsigned int nested_parts_count;
+       unsigned int total_parts_count;
 
        enum message_header_parser_flags hdr_flags;
        enum message_parser_flags flags;
        unsigned int max_nested_mime_parts;
+       unsigned int max_total_mime_parts;
 
        char *last_boundary;
        struct message_boundary *boundaries;
index 44b313f2c7225d3d9ca3acb18258f4339376c239..14ec2a274593a69838175cbf841ff21e06897729 100644 (file)
@@ -158,7 +158,9 @@ message_part_append(struct message_parser_ctx *ctx)
 
        ctx->part = part;
        ctx->nested_parts_count++;
+       ctx->total_parts_count++;
        i_assert(ctx->nested_parts_count < ctx->max_nested_mime_parts);
+       i_assert(ctx->total_parts_count <= ctx->max_total_mime_parts);
 }
 
 static void message_part_finish(struct message_parser_ctx *ctx)
@@ -241,6 +243,12 @@ boundary_line_find(struct message_parser_ctx *ctx,
                return -1;
        }
 
+       if (ctx->total_parts_count >= ctx->max_total_mime_parts) {
+               /* can't add any more MIME parts. just stop trying to find
+                  more boundaries. */
+               return -1;
+       }
+
        /* need to find the end of line */
        data += 2;
        size -= 2;
@@ -731,6 +739,9 @@ message_parser_init_int(struct istream *input,
        ctx->max_nested_mime_parts = set->max_nested_mime_parts != 0 ?
                set->max_nested_mime_parts :
                MESSAGE_PARSER_DEFAULT_MAX_NESTED_MIME_PARTS;
+       ctx->max_total_mime_parts = set->max_total_mime_parts != 0 ?
+               set->max_total_mime_parts :
+               MESSAGE_PARSER_DEFAULT_MAX_TOTAL_MIME_PARTS;
        ctx->input = input;
        i_stream_ref(input);
        return ctx;
@@ -747,6 +758,7 @@ message_parser_init(pool_t part_pool, struct istream *input,
        ctx->parts = ctx->part = p_new(part_pool, struct message_part, 1);
        ctx->next_part = &ctx->part->children;
        ctx->parse_next_block = parse_next_header_init;
+       ctx->total_parts_count = 1;
        i_array_init(&ctx->next_part_stack, 4);
        return ctx;
 }
index 7f6ea04936d9504956691c1aa2158b0359a0154f..f19e526284da193f9f5ab1db6ab4c18e9739c434 100644 (file)
@@ -18,6 +18,7 @@ enum message_parser_flags {
 };
 
 #define MESSAGE_PARSER_DEFAULT_MAX_NESTED_MIME_PARTS 100
+#define MESSAGE_PARSER_DEFAULT_MAX_TOTAL_MIME_PARTS 10000
 
 struct message_parser_settings {
        enum message_header_parser_flags hdr_flags;
@@ -26,6 +27,9 @@ struct message_parser_settings {
        /* Maximum nested MIME parts.
           0 = MESSAGE_PARSER_DEFAULT_MAX_NESTED_MIME_PARTS. */
        unsigned int max_nested_mime_parts;
+       /* Maximum MIME parts in total.
+          0 = MESSAGE_PARSER_DEFAULT_MAX_TOTAL_MIME_PARTS. */
+       unsigned int max_total_mime_parts;
 };
 
 struct message_parser_ctx;
index c7236cbe5087842b101ac06b199aa27ff1206422..33ce79462b35ac642b21487630008c4e8ee8ed02 100644 (file)
@@ -969,6 +969,91 @@ static const char input_msg[] =
        test_end();
 }
 
+static void test_message_parser_mime_part_limit(void)
+{
+static const char input_msg[] =
+"Content-Type: multipart/mixed; boundary=\"1\"\n"
+"\n"
+"--1\n"
+"Content-Type: multipart/mixed; boundary=\"2\"\n"
+"\n"
+"--2\n"
+"Content-Type: text/plain\n"
+"\n"
+"1\n"
+"--2\n"
+"Content-Type: text/plain\n"
+"\n"
+"22\n"
+"--1\n"
+"Content-Type: text/plain\n"
+"\n"
+"333\n";
+       const struct message_parser_settings parser_set = {
+               .max_total_mime_parts = 4,
+       };
+       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 mime part limit");
+       pool = pool_alloconly_create("message parser", 10240);
+       input = test_istream_create(input_msg);
+
+       parser = message_parser_init(pool, input, &parser_set);
+       while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ;
+       test_assert(ret < 0);
+       message_parser_deinit(&parser, &parts);
+
+       part = parts;
+       test_assert(part->children_count == 3);
+       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 == 15);
+       test_assert(part->body_size.physical_size == 148);
+       test_assert(part->body_size.virtual_size == 148+15);
+
+       part = parts->children;
+       test_assert(part->children_count == 2);
+       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 == 12);
+       test_assert(part->body_size.physical_size == 99);
+       test_assert(part->body_size.virtual_size == 99+12);
+
+       part = parts->children->children;
+       test_assert(part->children_count == 0);
+       test_assert(part->flags == (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_IS_MIME));
+       test_assert(part->header_size.lines == 2);
+       test_assert(part->header_size.physical_size == 26);
+       test_assert(part->header_size.virtual_size == 26+2);
+       test_assert(part->body_size.lines == 0);
+       test_assert(part->body_size.physical_size == 1);
+       test_assert(part->body_size.virtual_size == 1);
+
+       part = parts->children->children->next;
+       test_assert(part->children_count == 0);
+       test_assert(part->flags == (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_IS_MIME));
+       test_assert(part->header_size.lines == 2);
+       test_assert(part->header_size.physical_size == 26);
+       test_assert(part->header_size.virtual_size == 26+2);
+       test_assert(part->body_size.lines == 5);
+       test_assert(part->body_size.physical_size == 37);
+       test_assert(part->body_size.virtual_size == 37+5);
+
+       test_parsed_parts(input, parts);
+       i_stream_unref(&input);
+       pool_unref(&pool);
+       test_end();
+}
+
 int main(void)
 {
        static void (*const test_functions[])(void) = {
@@ -986,6 +1071,7 @@ int main(void)
                test_message_parser_no_eoh,
                test_message_parser_mime_part_nested_limit,
                test_message_parser_mime_part_nested_limit_rfc822,
+               test_message_parser_mime_part_limit,
                NULL
        };
        return test_run(test_functions);