From: Timo Sirainen Date: Thu, 23 Apr 2020 14:09:33 +0000 (+0300) Subject: lib-mail: message-parser - Support limiting max number of MIME parts X-Git-Tag: 2.3.11.2~45 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=b0041cda0a24d4c642307f77018bf65a14208e00;p=thirdparty%2Fdovecot%2Fcore.git lib-mail: message-parser - Support limiting max number of MIME parts 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. --- diff --git a/src/lib-mail/message-parser-private.h b/src/lib-mail/message-parser-private.h index 4bb0c3dbfd..1f9c66b827 100644 --- a/src/lib-mail/message-parser-private.h +++ b/src/lib-mail/message-parser-private.h @@ -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; diff --git a/src/lib-mail/message-parser.c b/src/lib-mail/message-parser.c index 44b313f2c7..14ec2a2745 100644 --- a/src/lib-mail/message-parser.c +++ b/src/lib-mail/message-parser.c @@ -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; } diff --git a/src/lib-mail/message-parser.h b/src/lib-mail/message-parser.h index 7f6ea04936..f19e526284 100644 --- a/src/lib-mail/message-parser.h +++ b/src/lib-mail/message-parser.h @@ -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; diff --git a/src/lib-mail/test-message-parser.c b/src/lib-mail/test-message-parser.c index c7236cbe50..33ce79462b 100644 --- a/src/lib-mail/test-message-parser.c +++ b/src/lib-mail/test-message-parser.c @@ -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);