diff -Nru dovecot-2.2.22/debian/changelog dovecot-2.2.22/debian/changelog --- dovecot-2.2.22/debian/changelog 2019-08-28 16:47:22.000000000 +0000 +++ dovecot-2.2.22/debian/changelog 2020-08-06 15:29:55.000000000 +0000 @@ -1,3 +1,20 @@ +dovecot (1:2.2.22-1ubuntu2.13) xenial-security; urgency=medium + + * SECURITY UPDATE: DoS via deeply nested MIME parts + - debian/patches/CVE-2020-12100/*.patch: backports of upstream patches + to fix the issue. + - CVE-2020-12100 + * SECURITY UPDATE: DoS via incorrect NTLM message buffer size + - debian/patches/CVE-2020-12673/*.patch: check buffer length in + src/lib-ntlm/ntlm-message.c. + - CVE-2020-12673 + * SECURITY UPDATE: DoS via zero-length message + - debian/patches/CVE-2020-12674/*.patch: fail on zero-length buffer in + src/auth/mech-rpa.c. + - CVE-2020-12674 + + -- Marc Deslauriers Thu, 06 Aug 2020 09:40:07 -0400 + dovecot (1:2.2.22-1ubuntu2.12) xenial-security; urgency=medium * SECURITY REGRESSION: updating CVE-2019-11500-3.patch with the right check diff -Nru dovecot-2.2.22/debian/patches/CVE-2020-12100/0001-lib-mail-message-parser-Add-a-message_part_finish-he.patch dovecot-2.2.22/debian/patches/CVE-2020-12100/0001-lib-mail-message-parser-Add-a-message_part_finish-he.patch --- dovecot-2.2.22/debian/patches/CVE-2020-12100/0001-lib-mail-message-parser-Add-a-message_part_finish-he.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.2.22/debian/patches/CVE-2020-12100/0001-lib-mail-message-parser-Add-a-message_part_finish-he.patch 2020-08-06 15:24:53.000000000 +0000 @@ -0,0 +1,65 @@ +Backport of: + +From 667d353b0f217372e8cc43ea4fe13466689c7ed0 Mon Sep 17 00:00:00 2001 +From: Timo Sirainen +Date: Thu, 23 Apr 2020 11:33:31 +0300 +Subject: [PATCH 01/13] lib-mail: message-parser - Add a message_part_finish() + helper function + +--- + src/lib-mail/message-parser.c | 25 ++++++++++++------------- + 1 file changed, 12 insertions(+), 13 deletions(-) + +--- a/src/lib-mail/message-parser.c ++++ b/src/lib-mail/message-parser.c +@@ -193,6 +193,13 @@ message_part_append(pool_t pool, struct + return part; + } + ++static void message_part_finish(struct message_parser_ctx *ctx) ++{ ++ message_size_add(&ctx->part->parent->body_size, &ctx->part->body_size); ++ message_size_add(&ctx->part->parent->body_size, &ctx->part->header_size); ++ ctx->part = ctx->part->parent; ++} ++ + static void parse_next_body_multipart_init(struct message_parser_ctx *ctx) + { + struct message_boundary *b; +@@ -310,17 +317,14 @@ static int parse_part_finish(struct mess + struct message_boundary *boundary, + struct message_block *block_r, bool first_line) + { +- struct message_part *part; + size_t line_size; + + /* 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) { +- message_size_add(&part->parent->body_size, &part->body_size); +- message_size_add(&part->parent->body_size, &part->header_size); ++ while (ctx->part != boundary->part) { ++ message_part_finish(ctx); ++ i_assert(ctx->part != NULL); + } +- i_assert(part != NULL); +- ctx->part = part; + + if (boundary->epilogue_found) { + /* this boundary isn't needed anymore */ +@@ -1120,13 +1124,8 @@ int message_parser_parse_next_block(stru + i_assert(ctx->input->eof || ctx->input->closed || + ctx->input->stream_errno != 0 || + ctx->broken_reason != NULL); +- while (ctx->part->parent != NULL) { +- message_size_add(&ctx->part->parent->body_size, +- &ctx->part->body_size); +- message_size_add(&ctx->part->parent->body_size, +- &ctx->part->header_size); +- ctx->part = ctx->part->parent; +- } ++ while (ctx->part->parent != NULL) ++ message_part_finish(ctx); + } + + if (block_r->size == 0) { diff -Nru dovecot-2.2.22/debian/patches/CVE-2020-12100/0002-lib-mail-message-parser-Change-message_part_append-t.patch dovecot-2.2.22/debian/patches/CVE-2020-12100/0002-lib-mail-message-parser-Change-message_part_append-t.patch --- dovecot-2.2.22/debian/patches/CVE-2020-12100/0002-lib-mail-message-parser-Change-message_part_append-t.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.2.22/debian/patches/CVE-2020-12100/0002-lib-mail-message-parser-Change-message_part_append-t.patch 2020-08-06 15:26:02.000000000 +0000 @@ -0,0 +1,60 @@ +Backport of: + +From de0da7bc8df55521db8fa787f88e293618c96386 Mon Sep 17 00:00:00 2001 +From: Timo Sirainen +Date: Thu, 23 Apr 2020 11:34:22 +0300 +Subject: [PATCH 02/13] lib-mail: message-parser - Change message_part_append() + to do all work internally + +--- + src/lib-mail/message-parser.c | 13 +++++++------ + 1 file changed, 7 insertions(+), 6 deletions(-) + +--- a/src/lib-mail/message-parser.c ++++ b/src/lib-mail/message-parser.c +@@ -167,14 +167,15 @@ static int message_parser_read_more(stru + return 1; + } + +-static struct message_part * +-message_part_append(pool_t pool, struct message_part *parent) ++static void ++message_part_append(struct message_parser_ctx *ctx) + { ++ struct message_part *parent = ctx->part; + struct message_part *p, *part, **list; + + i_assert(parent != NULL); + +- part = p_new(pool, struct message_part, 1); ++ part = p_new(ctx->part_pool, struct message_part, 1); + part->parent = parent; + for (p = parent; p != NULL; p = p->parent) + p->children_count++; +@@ -190,7 +191,7 @@ message_part_append(pool_t pool, struct + list = &(*list)->next; + + *list = part; +- return part; ++ ctx->part = part; + } + + static void message_part_finish(struct message_parser_ctx *ctx) +@@ -218,7 +219,7 @@ static void parse_next_body_multipart_in + static int parse_next_body_message_rfc822_init(struct message_parser_ctx *ctx, + struct message_block *block_r) + { +- ctx->part = message_part_append(ctx->part_pool, ctx->part); ++ message_part_append(ctx); + return parse_next_header_init(ctx, block_r); + } + +@@ -268,7 +269,7 @@ boundary_line_find(struct message_parser + static int parse_next_mime_header_init(struct message_parser_ctx *ctx, + struct message_block *block_r) + { +- ctx->part = message_part_append(ctx->part_pool, ctx->part); ++ message_part_append(ctx); + ctx->part->flags |= MESSAGE_PART_FLAG_IS_MIME; + + return parse_next_header_init(ctx, block_r); diff -Nru dovecot-2.2.22/debian/patches/CVE-2020-12100/0003-lib-mail-message-parser-Optimize-updating-children_c.patch dovecot-2.2.22/debian/patches/CVE-2020-12100/0003-lib-mail-message-parser-Optimize-updating-children_c.patch --- dovecot-2.2.22/debian/patches/CVE-2020-12100/0003-lib-mail-message-parser-Optimize-updating-children_c.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.2.22/debian/patches/CVE-2020-12100/0003-lib-mail-message-parser-Optimize-updating-children_c.patch 2020-08-06 15:26:10.000000000 +0000 @@ -0,0 +1,36 @@ +From a9800b436fcf1f9633c2b136a9c5cb7a486a8a52 Mon Sep 17 00:00:00 2001 +From: Timo Sirainen +Date: Thu, 23 Apr 2020 11:36:48 +0300 +Subject: [PATCH 03/13] lib-mail: message-parser - Optimize updating + children_count + +--- + src/lib-mail/message-parser.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +--- a/src/lib-mail/message-parser.c ++++ b/src/lib-mail/message-parser.c +@@ -171,14 +171,12 @@ static void + message_part_append(struct message_parser_ctx *ctx) + { + struct message_part *parent = ctx->part; +- struct message_part *p, *part, **list; ++ struct message_part *part, **list; + + i_assert(parent != NULL); + + part = p_new(ctx->part_pool, struct message_part, 1); + part->parent = parent; +- for (p = parent; p != NULL; p = p->parent) +- p->children_count++; + + /* set child position */ + part->physical_pos = +@@ -198,6 +196,7 @@ static void message_part_finish(struct m + { + message_size_add(&ctx->part->parent->body_size, &ctx->part->body_size); + message_size_add(&ctx->part->parent->body_size, &ctx->part->header_size); ++ ctx->part->parent->children_count += 1 + ctx->part->children_count; + ctx->part = ctx->part->parent; + } + diff -Nru dovecot-2.2.22/debian/patches/CVE-2020-12100/0004-lib-mail-message-parser-Optimize-appending-new-part-.patch dovecot-2.2.22/debian/patches/CVE-2020-12100/0004-lib-mail-message-parser-Optimize-appending-new-part-.patch --- dovecot-2.2.22/debian/patches/CVE-2020-12100/0004-lib-mail-message-parser-Optimize-appending-new-part-.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.2.22/debian/patches/CVE-2020-12100/0004-lib-mail-message-parser-Optimize-appending-new-part-.patch 2020-08-06 15:26:13.000000000 +0000 @@ -0,0 +1,82 @@ +From 99ee7596712cf0ea0a288b712bc898ecb2b35f9b Mon Sep 17 00:00:00 2001 +From: Timo Sirainen +Date: Thu, 23 Apr 2020 12:00:38 +0300 +Subject: [PATCH 04/13] lib-mail: message-parser - Optimize appending new part + to linked list + +--- + src/lib-mail/message-parser.c | 28 ++++++++++++++++++++++------ + 1 file changed, 22 insertions(+), 6 deletions(-) + +--- a/src/lib-mail/message-parser.c ++++ b/src/lib-mail/message-parser.c +@@ -1,7 +1,7 @@ + /* Copyright (c) 2002-2016 Dovecot authors, see the included COPYING file */ + + #include "lib.h" +-#include "buffer.h" ++#include "array.h" + #include "str.h" + #include "istream.h" + #include "rfc822-parser.h" +@@ -34,6 +34,9 @@ struct message_parser_ctx { + const char *last_boundary; + struct message_boundary *boundaries; + ++ struct message_part **next_part; ++ ARRAY(struct message_part **) next_part_stack; ++ + size_t skip; + char last_chr; + unsigned int want_count; +@@ -171,7 +174,7 @@ static void + message_part_append(struct message_parser_ctx *ctx) + { + struct message_part *parent = ctx->part; +- struct message_part *part, **list; ++ struct message_part *part; + + i_assert(parent != NULL); + +@@ -184,16 +187,27 @@ message_part_append(struct message_parse + parent->body_size.physical_size + + parent->header_size.physical_size; + +- list = &part->parent->children; +- while (*list != NULL) +- list = &(*list)->next; ++ /* add to parent's linked list */ ++ *ctx->next_part = part; ++ /* update the parent's end-of-linked-list pointer */ ++ struct message_part **next_part = &part->next; ++ array_append(&ctx->next_part_stack, &next_part, 1); ++ /* This part is now the new parent for the next message_part_append() ++ call. Its linked list begins with the children pointer. */ ++ ctx->next_part = &part->children; + +- *list = part; + ctx->part = part; + } + + static void message_part_finish(struct message_parser_ctx *ctx) + { ++ struct message_part **const *parent_next_partp; ++ unsigned int count = array_count(&ctx->next_part_stack); ++ ++ parent_next_partp = array_idx(&ctx->next_part_stack, count-1); ++ array_delete(&ctx->next_part_stack, count-1, 1); ++ ctx->next_part = *parent_next_partp; ++ + message_size_add(&ctx->part->parent->body_size, &ctx->part->body_size); + message_size_add(&ctx->part->parent->body_size, &ctx->part->header_size); + ctx->part->parent->children_count += 1 + ctx->part->children_count; +@@ -1050,7 +1064,9 @@ message_parser_init(pool_t part_pool, st + ctx = message_parser_init_int(input, hdr_flags, flags); + ctx->part_pool = part_pool; + 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; ++ p_array_init(&ctx->next_part_stack, ctx->parser_pool, 4); + return ctx; + } + diff -Nru dovecot-2.2.22/debian/patches/CVE-2020-12100/0005-lib-mail-message-parser-Minor-code-cleanup-to-findin.patch dovecot-2.2.22/debian/patches/CVE-2020-12100/0005-lib-mail-message-parser-Minor-code-cleanup-to-findin.patch --- dovecot-2.2.22/debian/patches/CVE-2020-12100/0005-lib-mail-message-parser-Minor-code-cleanup-to-findin.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.2.22/debian/patches/CVE-2020-12100/0005-lib-mail-message-parser-Minor-code-cleanup-to-findin.patch 2020-08-06 15:26:16.000000000 +0000 @@ -0,0 +1,34 @@ +From e39c95b248917eb2b596ca55a957f3cbc7fd406f Mon Sep 17 00:00:00 2001 +From: Timo Sirainen +Date: Thu, 23 Apr 2020 12:10:07 +0300 +Subject: [PATCH 05/13] lib-mail: message-parser - Minor code cleanup to + finding the end of boundary line + +--- + src/lib-mail/message-parser.c | 9 ++++----- + 1 file changed, 4 insertions(+), 5 deletions(-) + +--- a/src/lib-mail/message-parser.c ++++ b/src/lib-mail/message-parser.c +@@ -258,17 +258,16 @@ boundary_line_find(struct message_parser + } + + /* need to find the end of line */ +- if (memchr(data + 2, '\n', size - 2) == NULL && +- size < BOUNDARY_END_MAX_LEN && ++ data += 2; ++ size -= 2; ++ if (memchr(data, '\n', size) == NULL && ++ size+2 < BOUNDARY_END_MAX_LEN && + !ctx->input->eof && !full) { + /* no LF found */ + ctx->want_count = BOUNDARY_END_MAX_LEN; + return 0; + } + +- data += 2; +- size -= 2; +- + *boundary_r = boundary_find(ctx->boundaries, data, size); + if (*boundary_r == NULL) + return -1; diff -Nru dovecot-2.2.22/debian/patches/CVE-2020-12100/0006-lib-mail-message-parser-Truncate-excessively-long-MI.patch dovecot-2.2.22/debian/patches/CVE-2020-12100/0006-lib-mail-message-parser-Truncate-excessively-long-MI.patch --- dovecot-2.2.22/debian/patches/CVE-2020-12100/0006-lib-mail-message-parser-Truncate-excessively-long-MI.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.2.22/debian/patches/CVE-2020-12100/0006-lib-mail-message-parser-Truncate-excessively-long-MI.patch 2020-08-06 15:39:37.000000000 +0000 @@ -0,0 +1,152 @@ +Backport of: + +From aed125484a346b4893c1a169088c39fe7ced01f3 Mon Sep 17 00:00:00 2001 +From: Timo Sirainen +Date: Thu, 23 Apr 2020 12:53:12 +0300 +Subject: [PATCH 06/13] lib-mail: message-parser - Truncate excessively long + MIME boundaries + +RFC 2046 requires that the boundaries are a maximum of 70 characters +(excluding the "--" prefix and suffix). We allow 80 characters for a bit of +extra safety. Anything longer than that is truncated and treated the same +as if it was just 80 characters. +--- + src/lib-mail/message-parser.c | 7 ++- + src/lib-mail/test-message-parser.c | 95 ++++++++++++++++++++++++++++++++++++++ + 2 files changed, 100 insertions(+), 2 deletions(-) + +--- a/src/lib-mail/message-parser.c ++++ b/src/lib-mail/message-parser.c +@@ -10,7 +10,8 @@ + + /* RFC-2046 requires boundaries are max. 70 chars + "--" prefix + "--" suffix. + We'll add a bit more just in case. */ +-#define BOUNDARY_END_MAX_LEN (70 + 2 + 2 + 10) ++#define BOUNDARY_STRING_MAX_LEN (70 + 10) ++#define BOUNDARY_END_MAX_LEN (BOUNDARY_STRING_MAX_LEN + 2 + 2) + + struct message_boundary { + struct message_boundary *next; +@@ -522,8 +523,10 @@ static void parse_content_type(struct me + rfc2231_parse(&parser, &results); + for (; *results != NULL; results += 2) { + if (strcasecmp(results[0], "boundary") == 0) { ++ /* truncate excessively long boundaries */ + ctx->last_boundary = +- p_strdup(ctx->parser_pool, results[1]); ++ p_strndup(ctx->parser_pool, results[1], ++ BOUNDARY_STRING_MAX_LEN); + break; + } + } +--- a/src/lib-mail/test-message-parser.c ++++ b/src/lib-mail/test-message-parser.c +@@ -470,6 +470,100 @@ static void test_message_parser_no_eoh(v + test_end(); + } + ++static void test_message_parser_long_mime_boundary(void) ++{ ++ /* Close the boundaries in wrong reverse order. But because all ++ boundaries are actually truncated to the same size (..890) it ++ works the same as if all of them were duplicate boundaries. */ ++static const char input_msg[] = ++"Content-Type: multipart/mixed; boundary=\"1234567890123456789012345678901234567890123456789012345678901234567890123456789012\"\n" ++"\n" ++"--1234567890123456789012345678901234567890123456789012345678901234567890123456789012\n" ++"Content-Type: multipart/mixed; boundary=\"123456789012345678901234567890123456789012345678901234567890123456789012345678901\"\n" ++"\n" ++"--123456789012345678901234567890123456789012345678901234567890123456789012345678901\n" ++"Content-Type: multipart/mixed; boundary=\"12345678901234567890123456789012345678901234567890123456789012345678901234567890\"\n" ++"\n" ++"--12345678901234567890123456789012345678901234567890123456789012345678901234567890\n" ++"Content-Type: text/plain\n" ++"\n" ++"1\n" ++"--1234567890123456789012345678901234567890123456789012345678901234567890123456789012\n" ++"Content-Type: text/plain\n" ++"\n" ++"22\n" ++"--123456789012345678901234567890123456789012345678901234567890123456789012345678901\n" ++"Content-Type: text/plain\n" ++"\n" ++"333\n" ++"--12345678901234567890123456789012345678901234567890123456789012345678901234567890\n" ++"Content-Type: text/plain\n" ++"\n" ++"4444\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 long 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->children_count == 6); ++ 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 == 126); ++ test_assert(part->header_size.virtual_size == 126+2); ++ test_assert(part->body_size.lines == 22); ++ test_assert(part->body_size.physical_size == 871); ++ test_assert(part->body_size.virtual_size == 871+22); ++ ++ part = parts->children; ++ test_assert(part->children_count == 5); ++ 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 == 125); ++ test_assert(part->header_size.virtual_size == 125+2); ++ test_assert(part->body_size.lines == 19); ++ test_assert(part->body_size.physical_size == 661); ++ test_assert(part->body_size.virtual_size == 661+19); ++ ++ part = parts->children->children; ++ test_assert(part->children_count == 4); ++ 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 == 124); ++ test_assert(part->header_size.virtual_size == 124+2); ++ test_assert(part->body_size.lines == 16); ++ test_assert(part->body_size.physical_size == 453); ++ test_assert(part->body_size.virtual_size == 453+16); ++ ++ part = parts->children->children->children; ++ for (unsigned int i = 1; i <= 3; i++, part = part->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 == 0); ++ test_assert(part->body_size.physical_size == i); ++ test_assert(part->body_size.virtual_size == i); ++ } ++ ++ test_parsed_parts(input, parts); ++ i_stream_unref(&input); ++ pool_unref(&pool); ++ test_end(); ++} ++ + int main(void) + { + static void (*test_functions[])(void) = { +@@ -479,6 +573,7 @@ int main(void) + test_message_parser_truncated_mime_headers3, + test_message_parser_duplicate_mime_boundary, + test_message_parser_continuing_mime_boundary, ++ test_message_parser_long_mime_boundary, + test_message_parser_no_eoh, + NULL + }; diff -Nru dovecot-2.2.22/debian/patches/CVE-2020-12100/0007-lib-mail-message-parser-Optimize-boundary-lookups-wh.patch dovecot-2.2.22/debian/patches/CVE-2020-12100/0007-lib-mail-message-parser-Optimize-boundary-lookups-wh.patch --- dovecot-2.2.22/debian/patches/CVE-2020-12100/0007-lib-mail-message-parser-Optimize-boundary-lookups-wh.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.2.22/debian/patches/CVE-2020-12100/0007-lib-mail-message-parser-Optimize-boundary-lookups-wh.patch 2020-08-06 15:27:08.000000000 +0000 @@ -0,0 +1,60 @@ +From 5f8de52fec3191a1aa68a399ee2068485737dc4f Mon Sep 17 00:00:00 2001 +From: Timo Sirainen +Date: Thu, 23 Apr 2020 13:06:02 +0300 +Subject: [PATCH 07/13] lib-mail: message-parser - Optimize boundary lookups + when exact boundary is found + +When an exact boundary is found, there's no need to continue looking for +more boundaries. +--- + src/lib-mail/message-parser.c | 26 ++++++++++++++++++++++---- + 1 file changed, 22 insertions(+), 4 deletions(-) + +--- a/src/lib-mail/message-parser.c ++++ b/src/lib-mail/message-parser.c +@@ -80,8 +80,14 @@ boundary_find(struct message_boundary *b + while (boundaries != NULL) { + if (boundaries->len <= len && + memcmp(boundaries->boundary, data, boundaries->len) == 0 && +- (best == NULL || best->len < boundaries->len)) ++ (best == NULL || best->len < boundaries->len)) { + best = boundaries; ++ if (best->len == len) { ++ /* This is exactly the wanted boundary. There ++ can't be a better one. */ ++ break; ++ } ++ } + + boundaries = boundaries->next; + } +@@ -261,15 +267,27 @@ boundary_line_find(struct message_parser + /* need to find the end of line */ + data += 2; + size -= 2; +- if (memchr(data, '\n', size) == NULL && ++ const unsigned char *lf_pos = memchr(data, '\n', size); ++ if (lf_pos == NULL && + size+2 < BOUNDARY_END_MAX_LEN && + !ctx->input->eof && !full) { + /* no LF found */ + ctx->want_count = BOUNDARY_END_MAX_LEN; + return 0; + } ++ size_t find_size = size; ++ ++ if (lf_pos != NULL) { ++ find_size = lf_pos - data; ++ if (find_size > 0 && data[find_size-1] == '\r') ++ find_size--; ++ if (find_size > 2 && data[find_size-1] == '-' && ++ data[find_size-2] == '-') ++ find_size -= 2; ++ } else if (find_size > BOUNDARY_END_MAX_LEN) ++ find_size = BOUNDARY_END_MAX_LEN; + +- *boundary_r = boundary_find(ctx->boundaries, data, size); ++ *boundary_r = boundary_find(ctx->boundaries, data, find_size); + if (*boundary_r == NULL) + return -1; + diff -Nru dovecot-2.2.22/debian/patches/CVE-2020-12100/0008-lib-mail-message-parser-Add-boundary_remove_until-he.patch dovecot-2.2.22/debian/patches/CVE-2020-12100/0008-lib-mail-message-parser-Add-boundary_remove_until-he.patch --- dovecot-2.2.22/debian/patches/CVE-2020-12100/0008-lib-mail-message-parser-Add-boundary_remove_until-he.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.2.22/debian/patches/CVE-2020-12100/0008-lib-mail-message-parser-Add-boundary_remove_until-he.patch 2020-08-06 15:27:10.000000000 +0000 @@ -0,0 +1,39 @@ +From 929396767d831bedbdec6392aaa835b045332fd3 Mon Sep 17 00:00:00 2001 +From: Timo Sirainen +Date: Thu, 23 Apr 2020 14:53:27 +0300 +Subject: [PATCH 08/13] lib-mail: message-parser - Add boundary_remove_until() + helper function + +--- + src/lib-mail/message-parser.c | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +--- a/src/lib-mail/message-parser.c ++++ b/src/lib-mail/message-parser.c +@@ -221,6 +221,13 @@ static void message_part_finish(struct m + ctx->part = ctx->part->parent; + } + ++static void ++boundary_remove_until(struct message_parser_ctx *ctx, ++ struct message_boundary *boundary) ++{ ++ ctx->boundaries = boundary; ++} ++ + static void parse_next_body_multipart_init(struct message_parser_ctx *ctx) + { + struct message_boundary *b; +@@ -360,10 +367,10 @@ static int parse_part_finish(struct mess + + if (boundary->epilogue_found) { + /* this boundary isn't needed anymore */ +- ctx->boundaries = boundary->next; ++ boundary_remove_until(ctx, boundary->next); + } else { + /* forget about the boundaries we possibly skipped */ +- ctx->boundaries = boundary; ++ boundary_remove_until(ctx, boundary); + } + + /* the boundary itself should already be in buffer. add that. */ diff -Nru dovecot-2.2.22/debian/patches/CVE-2020-12100/0009-lib-mail-message-parser-Don-t-use-memory-pool-for-pa.patch dovecot-2.2.22/debian/patches/CVE-2020-12100/0009-lib-mail-message-parser-Don-t-use-memory-pool-for-pa.patch --- dovecot-2.2.22/debian/patches/CVE-2020-12100/0009-lib-mail-message-parser-Don-t-use-memory-pool-for-pa.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.2.22/debian/patches/CVE-2020-12100/0009-lib-mail-message-parser-Don-t-use-memory-pool-for-pa.patch 2020-08-06 15:27:55.000000000 +0000 @@ -0,0 +1,161 @@ +Backport of: + +From d53d83214b1d635446a8cf8ff9438cc530133d62 Mon Sep 17 00:00:00 2001 +From: Timo Sirainen +Date: Thu, 23 Apr 2020 15:00:57 +0300 +Subject: [PATCH 09/13] lib-mail: message-parser - Don't use memory pool for + parser + +This reduces memory usage when parsing many MIME parts where boundaries are +being added and removed constantly. +--- + src/lib-mail/message-parser.c | 48 ++++++++++++++++++++++++++++--------------- + 1 file changed, 32 insertions(+), 16 deletions(-) + +--- a/src/lib-mail/message-parser.c ++++ b/src/lib-mail/message-parser.c +@@ -17,14 +17,14 @@ struct message_boundary { + struct message_boundary *next; + + struct message_part *part; +- const char *boundary; ++ char *boundary; + size_t len; + + unsigned int epilogue_found:1; + }; + + struct message_parser_ctx { +- pool_t parser_pool, part_pool; ++ pool_t part_pool; + struct istream *input; + struct message_part *parts, *part; + const char *broken_reason; +@@ -32,7 +32,7 @@ struct message_parser_ctx { + enum message_header_parser_flags hdr_flags; + enum message_parser_flags flags; + +- const char *last_boundary; ++ char *last_boundary; + struct message_boundary *boundaries; + + struct message_part **next_part; +@@ -221,10 +221,24 @@ static void message_part_finish(struct m + ctx->part = ctx->part->parent; + } + ++static void message_boundary_free(struct message_boundary *b) ++{ ++ i_free(b->boundary); ++ i_free(b); ++} ++ + static void + boundary_remove_until(struct message_parser_ctx *ctx, + struct message_boundary *boundary) + { ++ while (ctx->boundaries != boundary) { ++ struct message_boundary *cur = ctx->boundaries; ++ ++ i_assert(cur != NULL); ++ ctx->boundaries = cur->next; ++ message_boundary_free(cur); ++ ++ } + ctx->boundaries = boundary; + } + +@@ -232,15 +246,14 @@ static void parse_next_body_multipart_in + { + struct message_boundary *b; + +- b = p_new(ctx->parser_pool, struct message_boundary, 1); ++ b = i_new(struct message_boundary, 1); + b->part = ctx->part; + b->boundary = ctx->last_boundary; ++ ctx->last_boundary = NULL; + b->len = strlen(b->boundary); + + b->next = ctx->boundaries; + ctx->boundaries = b; +- +- ctx->last_boundary = NULL; + } + + static int parse_next_body_message_rfc822_init(struct message_parser_ctx *ctx, +@@ -357,6 +370,8 @@ static int parse_part_finish(struct mess + struct message_block *block_r, bool first_line) + { + size_t line_size; ++ size_t boundary_len = boundary->len; ++ bool boundary_epilogue_found = boundary->epilogue_found; + + /* get back to parent MIME part, summing the child MIME part sizes + into parent's body sizes */ +@@ -387,7 +402,7 @@ static int parse_part_finish(struct mess + i_assert(block_r->data[0] == '\n'); + line_size = 1; + } +- line_size += 2 + boundary->len + (boundary->epilogue_found ? 2 : 0); ++ line_size += 2 + boundary_len + (boundary_epilogue_found ? 2 : 0); + i_assert(block_r->size >= ctx->skip + line_size); + block_r->size = line_size; + parse_body_add_block(ctx, block_r); +@@ -549,9 +564,9 @@ static void parse_content_type(struct me + for (; *results != NULL; results += 2) { + if (strcasecmp(results[0], "boundary") == 0) { + /* truncate excessively long boundaries */ ++ i_free(ctx->last_boundary); + ctx->last_boundary = +- p_strndup(ctx->parser_pool, results[1], +- BOUNDARY_STRING_MAX_LEN); ++ i_strndup(results[1], BOUNDARY_STRING_MAX_LEN); + break; + } + } +@@ -673,8 +688,8 @@ static int parse_next_header(struct mess + Content-Type. */ + i_assert(!ctx->multipart); + part->flags = 0; +- ctx->last_boundary = NULL; + } ++ i_free(ctx->last_boundary); + + if (!ctx->part_seen_content_type || + (part->flags & MESSAGE_PART_FLAG_IS_MIME) == 0) { +@@ -1069,11 +1084,8 @@ message_parser_init_int(struct istream * + enum message_parser_flags flags) + { + struct message_parser_ctx *ctx; +- pool_t pool; + +- pool = pool_alloconly_create("Message Parser", 1024); +- ctx = p_new(pool, struct message_parser_ctx, 1); +- ctx->parser_pool = pool; ++ ctx = i_new(struct message_parser_ctx, 1); + ctx->hdr_flags = hdr_flags; + ctx->flags = flags; + ctx->input = input; +@@ -1093,7 +1105,7 @@ message_parser_init(pool_t part_pool, st + 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; +- p_array_init(&ctx->next_part_stack, ctx->parser_pool, 4); ++ i_array_init(&ctx->next_part_stack, 4); + return ctx; + } + +@@ -1134,8 +1146,12 @@ int message_parser_deinit_from_parts(str + + if (ctx->hdr_parser_ctx != NULL) + message_parse_header_deinit(&ctx->hdr_parser_ctx); ++ boundary_remove_until(ctx, NULL); + i_stream_unref(&ctx->input); +- pool_unref(&ctx->parser_pool); ++ if (array_is_created(&ctx->next_part_stack)) ++ array_free(&ctx->next_part_stack); ++ i_free(ctx->last_boundary); ++ i_free(ctx); + i_assert(ret < 0 || *parts_r != NULL); + return ret; + } diff -Nru dovecot-2.2.22/debian/patches/CVE-2020-12100/0010-lib-mail-message-parser-Support-limiting-max-number-.patch dovecot-2.2.22/debian/patches/CVE-2020-12100/0010-lib-mail-message-parser-Support-limiting-max-number-.patch --- dovecot-2.2.22/debian/patches/CVE-2020-12100/0010-lib-mail-message-parser-Support-limiting-max-number-.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.2.22/debian/patches/CVE-2020-12100/0010-lib-mail-message-parser-Support-limiting-max-number-.patch 2020-08-06 15:39:41.000000000 +0000 @@ -0,0 +1,175 @@ +From df9e0d358ef86e3342525dcdefcf79dc2d749a30 Mon Sep 17 00:00:00 2001 +From: Timo Sirainen +Date: Thu, 23 Apr 2020 16:59:40 +0300 +Subject: [PATCH 10/13] lib-mail: message-parser - Support limiting max number + of nested MIME parts + +The default is to allow 100 nested MIME parts. When the limit is reached, +the innermost MIME part's body contains all the rest of the inner bodies +until a parent MIME part is reached. +--- + src/lib-mail/message-parser.c | 43 +++++++++++++++++++++++++++++++------- + src/lib-mail/test-message-parser.c | 31 +++++++++++++++++++++++++++ + 2 files changed, 67 insertions(+), 7 deletions(-) + +--- a/src/lib-mail/message-parser.c ++++ b/src/lib-mail/message-parser.c +@@ -13,6 +13,8 @@ + #define BOUNDARY_STRING_MAX_LEN (70 + 10) + #define BOUNDARY_END_MAX_LEN (BOUNDARY_STRING_MAX_LEN + 2 + 2) + ++#define MESSAGE_PARSER_DEFAULT_MAX_NESTED_MIME_PARTS 100 ++ + struct message_boundary { + struct message_boundary *next; + +@@ -28,9 +30,11 @@ struct message_parser_ctx { + struct istream *input; + struct message_part *parts, *part; + const char *broken_reason; ++ unsigned int nested_parts_count; + + enum message_header_parser_flags hdr_flags; + enum message_parser_flags flags; ++ unsigned int max_nested_mime_parts; + + char *last_boundary; + struct message_boundary *boundaries; +@@ -204,6 +208,8 @@ message_part_append(struct message_parse + ctx->next_part = &part->children; + + ctx->part = part; ++ ctx->nested_parts_count++; ++ i_assert(ctx->nested_parts_count < ctx->max_nested_mime_parts); + } + + static void message_part_finish(struct message_parser_ctx *ctx) +@@ -211,8 +217,12 @@ static void message_part_finish(struct m + struct message_part **const *parent_next_partp; + unsigned int count = array_count(&ctx->next_part_stack); + ++ i_assert(ctx->nested_parts_count > 0); ++ ctx->nested_parts_count--; ++ + parent_next_partp = array_idx(&ctx->next_part_stack, count-1); + array_delete(&ctx->next_part_stack, count-1, 1); ++ + ctx->next_part = *parent_next_partp; + + message_size_add(&ctx->part->parent->body_size, &ctx->part->body_size); +@@ -588,6 +598,11 @@ static bool block_is_at_eoh(const struct + return FALSE; + } + ++static bool parse_too_many_nested_mime_parts(struct message_parser_ctx *ctx) ++{ ++ return ctx->nested_parts_count > ctx->max_nested_mime_parts; ++} ++ + #define MUTEX_FLAGS \ + (MESSAGE_PART_FLAG_MESSAGE_RFC822 | MESSAGE_PART_FLAG_MULTIPART) + +@@ -612,8 +627,12 @@ static int parse_next_header(struct mess + "\n--boundary" belongs to us or to a previous boundary. + this is a problem if the boundary prefixes are identical, + because MIME requires only the prefix to match. */ +- parse_next_body_multipart_init(ctx); +- ctx->multipart = TRUE; ++ if (!parse_too_many_nested_mime_parts(ctx)) { ++ parse_next_body_multipart_init(ctx); ++ ctx->multipart = TRUE; ++ } else { ++ part->flags &= ~MESSAGE_PART_FLAG_MULTIPART; ++ } + } + + /* before parsing the header see if we can find a --boundary from here. +@@ -717,12 +736,16 @@ static int parse_next_header(struct mess + i_assert(ctx->last_boundary == NULL); + ctx->multipart = FALSE; + ctx->parse_next_block = parse_next_body_to_boundary; +- } else if (part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) ++ } else if ((part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) != 0 && ++ !parse_too_many_nested_mime_parts(ctx)) { + ctx->parse_next_block = parse_next_body_message_rfc822_init; +- else if (ctx->boundaries != NULL) +- ctx->parse_next_block = parse_next_body_to_boundary; +- else +- ctx->parse_next_block = parse_next_body_to_eof; ++ } else { ++ part->flags &= ~MESSAGE_PART_FLAG_MESSAGE_RFC822; ++ if (ctx->boundaries != NULL) ++ ctx->parse_next_block = parse_next_body_to_boundary; ++ else ++ ctx->parse_next_block = parse_next_body_to_eof; ++ } + + ctx->want_count = 1; + +@@ -1088,6 +1111,8 @@ message_parser_init_int(struct istream * + ctx = i_new(struct message_parser_ctx, 1); + ctx->hdr_flags = hdr_flags; + ctx->flags = flags; ++ ctx->max_nested_mime_parts = ++ MESSAGE_PARSER_DEFAULT_MAX_NESTED_MIME_PARTS; + ctx->input = input; + i_stream_ref(input); + return ctx; +@@ -1147,6 +1172,10 @@ int message_parser_deinit_from_parts(str + if (ctx->hdr_parser_ctx != NULL) + message_parse_header_deinit(&ctx->hdr_parser_ctx); + boundary_remove_until(ctx, NULL); ++ /* caller might have stopped the parsing early */ ++ i_assert(ctx->nested_parts_count == 0 || ++ i_stream_have_bytes_left(ctx->input)); ++ + i_stream_unref(&ctx->input); + if (array_is_created(&ctx->next_part_stack)) + array_free(&ctx->next_part_stack); +--- a/src/lib-mail/test-message-parser.c ++++ b/src/lib-mail/test-message-parser.c +@@ -166,6 +166,36 @@ static void test_message_parser_small_bl + test_end(); + } + ++static void test_message_parser_stop_early(void) ++{ ++ struct message_parser_ctx *parser; ++ struct istream *input; ++ struct message_part *parts; ++ struct message_block block; ++ unsigned int i; ++ pool_t pool; ++ int ret; ++ ++ test_begin("message parser stop early"); ++ pool = pool_alloconly_create("message parser", 10240); ++ input = test_istream_create(test_msg); ++ ++ test_istream_set_allow_eof(input, FALSE); ++ for (i = 1; i <= TEST_MSG_LEN+1; i++) { ++ i_stream_seek(input, 0); ++ test_istream_set_size(input, i); ++ 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); ++ } ++ ++ i_stream_unref(&input); ++ pool_unref(&pool); ++ test_end(); ++} ++ + static void test_message_parser_truncated_mime_headers(void) + { + static const char input_msg[] = +@@ -568,6 +598,7 @@ int main(void) + { + static void (*test_functions[])(void) = { + test_message_parser_small_blocks, ++ test_message_parser_stop_early, + test_message_parser_truncated_mime_headers, + test_message_parser_truncated_mime_headers2, + test_message_parser_truncated_mime_headers3, diff -Nru dovecot-2.2.22/debian/patches/CVE-2020-12100/0011-lib-mail-message-parser-Support-limiting-max-number-.patch dovecot-2.2.22/debian/patches/CVE-2020-12100/0011-lib-mail-message-parser-Support-limiting-max-number-.patch --- dovecot-2.2.22/debian/patches/CVE-2020-12100/0011-lib-mail-message-parser-Support-limiting-max-number-.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.2.22/debian/patches/CVE-2020-12100/0011-lib-mail-message-parser-Support-limiting-max-number-.patch 2020-08-06 15:28:03.000000000 +0000 @@ -0,0 +1,76 @@ +From d7bba401dd234802bcdb55ff27dfb99bffdab804 Mon Sep 17 00:00:00 2001 +From: Timo Sirainen +Date: Thu, 23 Apr 2020 17:09:33 +0300 +Subject: [PATCH 11/13] 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. +--- + src/lib-mail/message-parser.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +--- a/src/lib-mail/message-parser.c ++++ b/src/lib-mail/message-parser.c +@@ -14,6 +14,7 @@ + #define BOUNDARY_END_MAX_LEN (BOUNDARY_STRING_MAX_LEN + 2 + 2) + + #define MESSAGE_PARSER_DEFAULT_MAX_NESTED_MIME_PARTS 100 ++#define MESSAGE_PARSER_DEFAULT_MAX_TOTAL_MIME_PARTS 10000 + + struct message_boundary { + struct message_boundary *next; +@@ -31,10 +32,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; +@@ -209,7 +212,9 @@ message_part_append(struct message_parse + + 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) +@@ -294,6 +299,12 @@ boundary_line_find(struct message_parser + 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; +@@ -1113,6 +1124,8 @@ message_parser_init_int(struct istream * + ctx->flags = flags; + ctx->max_nested_mime_parts = + MESSAGE_PARSER_DEFAULT_MAX_NESTED_MIME_PARTS; ++ ctx->max_total_mime_parts = ++ MESSAGE_PARSER_DEFAULT_MAX_TOTAL_MIME_PARTS; + ctx->input = input; + i_stream_ref(input); + return ctx; +@@ -1130,6 +1143,7 @@ message_parser_init(pool_t part_pool, st + 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 -Nru dovecot-2.2.22/debian/patches/CVE-2020-12100/0012-lib-mail-Fix-handling-trailing-in-MIME-boundaries.patch dovecot-2.2.22/debian/patches/CVE-2020-12100/0012-lib-mail-Fix-handling-trailing-in-MIME-boundaries.patch --- dovecot-2.2.22/debian/patches/CVE-2020-12100/0012-lib-mail-Fix-handling-trailing-in-MIME-boundaries.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.2.22/debian/patches/CVE-2020-12100/0012-lib-mail-Fix-handling-trailing-in-MIME-boundaries.patch 2020-08-06 15:39:44.000000000 +0000 @@ -0,0 +1,122 @@ +Backport of: + +From 0c9d56b41b992a868f299e05677a67c4d0495523 Mon Sep 17 00:00:00 2001 +From: Timo Sirainen +Date: Thu, 2 Jul 2020 17:31:19 +0300 +Subject: [PATCH 12/13] lib-mail: Fix handling trailing "--" in MIME boundaries + +Broken by 5b8ec27fae941d06516c30476dcf4820c6d200ab +--- + src/lib-mail/message-parser.c | 14 ++++++++---- + src/lib-mail/test-message-parser.c | 46 ++++++++++++++++++++++++++++++++++++++ + 2 files changed, 56 insertions(+), 4 deletions(-) + +--- a/src/lib-mail/message-parser.c ++++ b/src/lib-mail/message-parser.c +@@ -75,7 +75,7 @@ static int preparsed_parse_next_header_i + + static struct message_boundary * + boundary_find(struct message_boundary *boundaries, +- const unsigned char *data, size_t len) ++ const unsigned char *data, size_t len, bool trailing_dashes) + { + struct message_boundary *best = NULL; + +@@ -89,7 +89,11 @@ boundary_find(struct message_boundary *b + memcmp(boundaries->boundary, data, boundaries->len) == 0 && + (best == NULL || best->len < boundaries->len)) { + best = boundaries; +- if (best->len == len) { ++ /* If we see "foo--", it could either mean that there ++ is a boundary named "foo" that ends now or there's ++ a boundary "foo--" which continues. */ ++ if (best->len == len || ++ (best->len == len-2 && trailing_dashes)) { + /* This is exactly the wanted boundary. There + can't be a better one. */ + break; +@@ -317,6 +321,7 @@ boundary_line_find(struct message_parser + return 0; + } + size_t find_size = size; ++ bool trailing_dashes = FALSE; + + if (lf_pos != NULL) { + find_size = lf_pos - data; +@@ -324,11 +329,12 @@ boundary_line_find(struct message_parser + find_size--; + if (find_size > 2 && data[find_size-1] == '-' && + data[find_size-2] == '-') +- find_size -= 2; ++ trailing_dashes = TRUE; + } else if (find_size > BOUNDARY_END_MAX_LEN) + find_size = BOUNDARY_END_MAX_LEN; + +- *boundary_r = boundary_find(ctx->boundaries, data, find_size); ++ *boundary_r = boundary_find(ctx->boundaries, data, find_size, ++ trailing_dashes); + if (*boundary_r == NULL) + return -1; + +--- a/src/lib-mail/test-message-parser.c ++++ b/src/lib-mail/test-message-parser.c +@@ -414,6 +414,51 @@ static const char input_msg[] = + test_end(); + } + ++static void test_message_parser_trailing_dashes(void) ++{ ++static const char input_msg[] = ++"Content-Type: multipart/mixed; boundary=\"a--\"\n" ++"\n" ++"--a--\n" ++"Content-Type: multipart/mixed; boundary=\"a----\"\n" ++"\n" ++"--a----\n" ++"Content-Type: text/plain\n" ++"\n" ++"body\n" ++"--a------\n" ++"Content-Type: text/html\n" ++"\n" ++"body2\n" ++"--a----"; ++ struct message_parser_ctx *parser; ++ struct istream *input; ++ struct message_part *parts; ++ struct message_block block; ++ pool_t pool; ++ int ret; ++ ++ test_begin("message parser trailing dashes"); ++ 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); ++ ++ test_assert(parts->children_count == 2); ++ test_assert(parts->children->next == NULL); ++ test_assert(parts->children->children_count == 1); ++ test_assert(parts->children->children->next == NULL); ++ test_assert(parts->children->children->children_count == 0); ++ ++ test_parsed_parts(input, parts); ++ i_stream_unref(&input); ++ pool_unref(&pool); ++ test_end(); ++} ++ + static void test_message_parser_continuing_mime_boundary(void) + { + static const char input_msg[] = +@@ -603,6 +648,7 @@ int main(void) + test_message_parser_truncated_mime_headers2, + test_message_parser_truncated_mime_headers3, + test_message_parser_duplicate_mime_boundary, ++ test_message_parser_trailing_dashes, + test_message_parser_continuing_mime_boundary, + test_message_parser_long_mime_boundary, + test_message_parser_no_eoh, diff -Nru dovecot-2.2.22/debian/patches/CVE-2020-12100/0013-lib-mail-Fix-parse_too_many_nested_mime_parts.patch dovecot-2.2.22/debian/patches/CVE-2020-12100/0013-lib-mail-Fix-parse_too_many_nested_mime_parts.patch --- dovecot-2.2.22/debian/patches/CVE-2020-12100/0013-lib-mail-Fix-parse_too_many_nested_mime_parts.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.2.22/debian/patches/CVE-2020-12100/0013-lib-mail-Fix-parse_too_many_nested_mime_parts.patch 2020-08-06 15:29:35.000000000 +0000 @@ -0,0 +1,21 @@ +From f77a2b6c3ffe2ea96f4a4b05ec38dc9d53266ecb Mon Sep 17 00:00:00 2001 +From: Timo Sirainen +Date: Wed, 27 May 2020 11:35:55 +0300 +Subject: [PATCH 13/13] lib-mail: Fix parse_too_many_nested_mime_parts() + +This was originally correct, until it was "optimized" wrong and got merged. +--- + src/lib-mail/message-parser.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/src/lib-mail/message-parser.c ++++ b/src/lib-mail/message-parser.c +@@ -617,7 +617,7 @@ static bool block_is_at_eoh(const struct + + static bool parse_too_many_nested_mime_parts(struct message_parser_ctx *ctx) + { +- return ctx->nested_parts_count > ctx->max_nested_mime_parts; ++ return ctx->nested_parts_count+1 >= ctx->max_nested_mime_parts; + } + + #define MUTEX_FLAGS \ diff -Nru dovecot-2.2.22/debian/patches/CVE-2020-12100/backport1.patch dovecot-2.2.22/debian/patches/CVE-2020-12100/backport1.patch --- dovecot-2.2.22/debian/patches/CVE-2020-12100/backport1.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.2.22/debian/patches/CVE-2020-12100/backport1.patch 2020-08-06 13:26:06.000000000 +0000 @@ -0,0 +1,242 @@ +From 7a12331c6360968b141a0888e0bf04dd24145f23 Mon Sep 17 00:00:00 2001 +From: Timo Sirainen +Date: Fri, 22 Apr 2016 18:15:44 +0300 +Subject: [PATCH] lib-mail: Fixed handling duplicate boundary prefixes. + +If inner MIME part had the same --boundary prefix as its parent(s) and +the MIME part body started with the inner --boundary prefix, we didn't yet +have it in the list of valid boundaries, so we thought that the outer +boundary was found and the MIME headers were truncated. But due to an extra +bug we still treated it as if it were the inner boundary, except the MIME +part sizes/offsets were set wrong. + +This for example fixes a situation where FETCH [1.2.MIME] returns an extra +newline before the actual headers. +--- + src/lib-mail/message-parser.c | 45 +++++++++-- + src/lib-mail/test-message-parser.c | 116 +++++++++++++++++++++++++++++ + 2 files changed, 154 insertions(+), 7 deletions(-) + +--- a/src/lib-mail/message-parser.c ++++ b/src/lib-mail/message-parser.c +@@ -45,6 +45,7 @@ struct message_parser_ctx { + struct message_block *block_r); + + unsigned int part_seen_content_type:1; ++ unsigned int multipart:1; + unsigned int eof:1; + }; + +@@ -508,6 +509,21 @@ static void parse_content_type(struct me + rfc822_parser_deinit(&parser); + } + ++static bool block_is_at_eoh(const struct message_block *block) ++{ ++ if (block->size < 1) ++ return FALSE; ++ if (block->data[0] == '\n') ++ return TRUE; ++ if (block->data[0] == '\r') { ++ if (block->size < 2) ++ return FALSE; ++ if (block->data[1] == '\n') ++ return TRUE; ++ } ++ return FALSE; ++} ++ + #define MUTEX_FLAGS \ + (MESSAGE_PART_FLAG_MESSAGE_RFC822 | MESSAGE_PART_FLAG_MULTIPART) + +@@ -523,12 +539,30 @@ static int parse_next_header(struct mess + if ((ret = message_parser_read_more(ctx, block_r, &full)) == 0) + return ret; + ++ if (ret > 0 && block_is_at_eoh(block_r) && ++ ctx->last_boundary != NULL && ++ (part->flags & MESSAGE_PART_FLAG_IS_MIME) != 0) { ++ /* we are at the end of headers and we've determined that we're ++ going to start a multipart. add the boundary already here ++ at this point so we can reliably determine whether the ++ "\n--boundary" belongs to us or to a previous boundary. ++ this is a problem if the boundary prefixes are identical, ++ because MIME requires only the prefix to match. */ ++ parse_next_body_multipart_init(ctx); ++ ctx->multipart = TRUE; ++ } ++ + /* before parsing the header see if we can find a --boundary from here. + we're guaranteed to be at the beginning of the line here. */ + if (ret > 0) { + ret = ctx->boundaries == NULL ? -1 : + boundary_line_find(ctx, block_r->data, + block_r->size, full, &boundary); ++ if (ret > 0 && boundary->part == ctx->part) { ++ /* our own body begins with our own --boundary. ++ we don't want to handle that yet. */ ++ ret = -1; ++ } + } + if (ret < 0) { + /* no boundary */ +@@ -585,14 +619,10 @@ static int parse_next_header(struct mess + } + + /* end of headers */ +- if ((part->flags & MESSAGE_PART_FLAG_MULTIPART) != 0 && +- ctx->last_boundary == NULL) { +- /* multipart type but no message boundary */ +- part->flags = 0; +- } + if ((part->flags & MESSAGE_PART_FLAG_IS_MIME) == 0) { + /* It's not MIME. Reset everything we found from + Content-Type. */ ++ i_assert(!ctx->multipart); + part->flags = 0; + ctx->last_boundary = NULL; + } +@@ -619,8 +649,9 @@ static int parse_next_header(struct mess + i_assert((part->flags & MUTEX_FLAGS) != MUTEX_FLAGS); + + ctx->last_chr = '\n'; +- if (ctx->last_boundary != NULL) { +- parse_next_body_multipart_init(ctx); ++ if (ctx->multipart) { ++ i_assert(ctx->last_boundary == NULL); ++ ctx->multipart = FALSE; + ctx->parse_next_block = parse_next_body_to_boundary; + } else if (part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) + ctx->parse_next_block = parse_next_body_message_rfc822_init; +--- a/src/lib-mail/test-message-parser.c ++++ b/src/lib-mail/test-message-parser.c +@@ -196,6 +196,120 @@ static const char input_msg[] = + test_end(); + } + ++static void test_message_parser_duplicate_mime_boundary(void) ++{ ++static const char input_msg[] = ++"Content-Type: multipart/mixed; boundary=\"a\"\n" ++"\n" ++"--a\n" ++"Content-Type: multipart/mixed; boundary=\"a\"\n" ++"\n" ++"--a\n" ++"Content-Type: text/plain\n" ++"\n" ++"body\n"; ++ struct message_parser_ctx *parser; ++ struct istream *input; ++ struct message_part *parts; ++ struct message_block block; ++ pool_t pool; ++ int ret; ++ ++ test_begin("message parser duplicate 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); ++ test_assert(message_parser_deinit(&parser, &parts) == 0); ++ ++ test_assert(parts->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME)); ++ test_assert(parts->header_size.lines == 2); ++ test_assert(parts->header_size.physical_size == 45); ++ test_assert(parts->header_size.virtual_size == 45+2); ++ test_assert(parts->body_size.lines == 7); ++ test_assert(parts->body_size.physical_size == 84); ++ test_assert(parts->body_size.virtual_size == 84+7); ++ test_assert(parts->children->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME)); ++ test_assert(parts->children->physical_pos == 49); ++ test_assert(parts->children->header_size.lines == 2); ++ test_assert(parts->children->header_size.physical_size == 45); ++ test_assert(parts->children->header_size.virtual_size == 45+2); ++ test_assert(parts->children->body_size.lines == 4); ++ test_assert(parts->children->body_size.physical_size == 35); ++ test_assert(parts->children->body_size.virtual_size == 35+4); ++ test_assert(parts->children->children->flags == (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_IS_MIME)); ++ test_assert(parts->children->children->physical_pos == 98); ++ test_assert(parts->children->children->header_size.lines == 2); ++ test_assert(parts->children->children->header_size.physical_size == 26); ++ test_assert(parts->children->children->header_size.virtual_size == 26+2); ++ test_assert(parts->children->children->body_size.lines == 1); ++ test_assert(parts->children->children->body_size.physical_size == 5); ++ test_assert(parts->children->children->body_size.virtual_size == 5+1); ++ ++ i_stream_unref(&input); ++ pool_unref(&pool); ++ test_end(); ++} ++ ++static void test_message_parser_continuing_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" ++"\n" ++"--ab\n" ++"Content-Type: text/plain\n" ++"\n" ++"body\n"; ++ struct message_parser_ctx *parser; ++ struct istream *input; ++ struct message_part *parts; ++ struct message_block block; ++ pool_t pool; ++ int ret; ++ ++ test_begin("message parser continuing 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); ++ test_assert(message_parser_deinit(&parser, &parts) == 0); ++ ++ test_assert(parts->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME)); ++ test_assert(parts->header_size.lines == 2); ++ test_assert(parts->header_size.physical_size == 45); ++ test_assert(parts->header_size.virtual_size == 45+2); ++ test_assert(parts->body_size.lines == 7); ++ test_assert(parts->body_size.physical_size == 86); ++ test_assert(parts->body_size.virtual_size == 86+7); ++ test_assert(parts->children->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME)); ++ test_assert(parts->children->physical_pos == 49); ++ test_assert(parts->children->header_size.lines == 2); ++ test_assert(parts->children->header_size.physical_size == 46); ++ test_assert(parts->children->header_size.virtual_size == 46+2); ++ test_assert(parts->children->body_size.lines == 4); ++ test_assert(parts->children->body_size.physical_size == 36); ++ test_assert(parts->children->body_size.virtual_size == 36+4); ++ test_assert(parts->children->children->flags == (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_IS_MIME)); ++ test_assert(parts->children->children->physical_pos == 100); ++ test_assert(parts->children->children->header_size.lines == 2); ++ test_assert(parts->children->children->header_size.physical_size == 26); ++ test_assert(parts->children->children->header_size.virtual_size == 26+2); ++ test_assert(parts->children->children->body_size.lines == 1); ++ test_assert(parts->children->children->body_size.physical_size == 5); ++ test_assert(parts->children->children->body_size.virtual_size == 5+1); ++ ++ 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"; +@@ -228,6 +342,8 @@ int main(void) + static void (*test_functions[])(void) = { + test_message_parser_small_blocks, + test_message_parser_truncated_mime_headers, ++ test_message_parser_duplicate_mime_boundary, ++ test_message_parser_continuing_mime_boundary, + test_message_parser_no_eoh, + NULL + }; diff -Nru dovecot-2.2.22/debian/patches/CVE-2020-12100/backport2.patch dovecot-2.2.22/debian/patches/CVE-2020-12100/backport2.patch --- dovecot-2.2.22/debian/patches/CVE-2020-12100/backport2.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.2.22/debian/patches/CVE-2020-12100/backport2.patch 2020-08-06 13:25:27.000000000 +0000 @@ -0,0 +1,157 @@ +From 6df4a174b4cbdc439ed435a0cdc71d4a0126d54a Mon Sep 17 00:00:00 2001 +From: Timo Sirainen +Date: Fri, 22 Apr 2016 18:28:20 +0300 +Subject: [PATCH] lib-mail: Fixed inner MIME part boundary being a prefix of + outer boundary. + +--- + src/lib-mail/message-parser.c | 12 ++-- + src/lib-mail/test-message-parser.c | 96 ++++++++++++++++++++++++++++++ + 2 files changed, 104 insertions(+), 4 deletions(-) + +diff --git a/src/lib-mail/message-parser.c b/src/lib-mail/message-parser.c +index e936005d56..e3e712a298 100644 +--- a/src/lib-mail/message-parser.c ++++ b/src/lib-mail/message-parser.c +@@ -66,19 +66,23 @@ static struct message_boundary * + boundary_find(struct message_boundary *boundaries, + const unsigned char *data, size_t len) + { ++ struct message_boundary *best = NULL; ++ + /* As MIME spec says: search from latest one to oldest one so that we + don't break if the same boundary is used in nested parts. Also the + full message line doesn't have to match the boundary, only the +- beginning. */ ++ beginning. However, if there are multiple prefixes whose beginning ++ matches, use the longest matching one. */ + while (boundaries != NULL) { + if (boundaries->len <= len && +- memcmp(boundaries->boundary, data, boundaries->len) == 0) +- return boundaries; ++ memcmp(boundaries->boundary, data, boundaries->len) == 0 && ++ (best == NULL || best->len < boundaries->len)) ++ best = boundaries; + + boundaries = boundaries->next; + } + +- return NULL; ++ return best; + } + + static void parse_body_add_block(struct message_parser_ctx *ctx, +diff --git a/src/lib-mail/test-message-parser.c b/src/lib-mail/test-message-parser.c +index 7f7e7b060f..c613bb5306 100644 +--- a/src/lib-mail/test-message-parser.c ++++ b/src/lib-mail/test-message-parser.c +@@ -196,6 +196,100 @@ static const char input_msg[] = + test_end(); + } + ++static void test_message_parser_truncated_mime_headers2(void) ++{ ++static const char input_msg[] = ++"Content-Type: multipart/mixed; boundary=\"ab\"\n" ++"\n" ++"--ab\n" ++"Content-Type: multipart/mixed; boundary=\"a\"\n" ++"\n" ++"--ab\n" ++"Content-Type: text/plain\n" ++"\n" ++"--a\n\n"; ++ struct message_parser_ctx *parser; ++ struct istream *input; ++ struct message_part *parts; ++ struct message_block block; ++ pool_t pool; ++ int ret; ++ ++ test_begin("message parser truncated mime headers 2"); ++ 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); ++ test_assert(message_parser_deinit(&parser, &parts) == 0); ++ ++ test_assert(parts->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME)); ++ test_assert(parts->header_size.lines == 2); ++ test_assert(parts->header_size.physical_size == 46); ++ test_assert(parts->header_size.virtual_size == 46+2); ++ test_assert(parts->body_size.lines == 8); ++ test_assert(parts->body_size.physical_size == 86); ++ test_assert(parts->body_size.virtual_size == 86+8); ++ ++ test_assert(parts->children->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME)); ++ test_assert(parts->children->physical_pos == 51); ++ test_assert(parts->children->header_size.lines == 1); ++ test_assert(parts->children->header_size.physical_size == 44); ++ test_assert(parts->children->header_size.virtual_size == 44+1); ++ test_assert(parts->children->body_size.lines == 0); ++ test_assert(parts->children->body_size.physical_size == 0); ++ test_assert(parts->children->children == NULL); ++ ++ test_assert(parts->children->next->flags == (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_IS_MIME)); ++ test_assert(parts->children->next->physical_pos == 101); ++ test_assert(parts->children->next->header_size.lines == 2); ++ test_assert(parts->children->next->header_size.physical_size == 26); ++ test_assert(parts->children->next->header_size.virtual_size == 26+2); ++ test_assert(parts->children->next->body_size.lines == 2); ++ test_assert(parts->children->next->body_size.physical_size == 5); ++ test_assert(parts->children->next->body_size.virtual_size == 5+2); ++ test_assert(parts->children->next->children == NULL); ++ ++ i_stream_unref(&input); ++ pool_unref(&pool); ++ test_end(); ++} ++ ++static void test_message_parser_truncated_mime_headers3(void) ++{ ++static const char input_msg[] = ++"Content-Type: multipart/mixed; boundary=\"ab\"\n"; ++ struct message_parser_ctx *parser; ++ struct istream *input; ++ struct message_part *parts; ++ struct message_block block; ++ pool_t pool; ++ int ret; ++ ++ test_begin("message parser truncated mime headers 3"); ++ 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); ++ test_assert(message_parser_deinit(&parser, &parts) == 0); ++ ++ test_assert(parts->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME)); ++ test_assert(parts->header_size.lines == 1); ++ test_assert(parts->header_size.physical_size == 45); ++ test_assert(parts->header_size.virtual_size == 45+1); ++ test_assert(parts->body_size.lines == 0); ++ test_assert(parts->body_size.physical_size == 0); ++ ++ test_assert(parts->children == NULL); ++ ++ i_stream_unref(&input); ++ pool_unref(&pool); ++ test_end(); ++} ++ + static void test_message_parser_duplicate_mime_boundary(void) + { + static const char input_msg[] = +@@ -342,6 +436,8 @@ int main(void) + static void (*test_functions[])(void) = { + test_message_parser_small_blocks, + test_message_parser_truncated_mime_headers, ++ test_message_parser_truncated_mime_headers2, ++ test_message_parser_truncated_mime_headers3, + test_message_parser_duplicate_mime_boundary, + test_message_parser_continuing_mime_boundary, + test_message_parser_no_eoh, diff -Nru dovecot-2.2.22/debian/patches/CVE-2020-12100/backport3.patch dovecot-2.2.22/debian/patches/CVE-2020-12100/backport3.patch --- dovecot-2.2.22/debian/patches/CVE-2020-12100/backport3.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.2.22/debian/patches/CVE-2020-12100/backport3.patch 2020-08-06 15:39:24.000000000 +0000 @@ -0,0 +1,129 @@ +Backport of: + +From aeaf818613081b9a9ff8c9fd4306e8dbfab4fd86 Mon Sep 17 00:00:00 2001 +From: Timo Sirainen +Date: Sat, 11 Jun 2016 21:23:57 +0300 +Subject: [PATCH] lib-mail: Improved message-parser unit tests. + +--- + src/lib-mail/test-message-parser.c | 44 +++++++++++++++++++++++++++++- + 1 file changed, 43 insertions(+), 1 deletion(-) + +--- a/src/lib-mail/test-message-parser.c ++++ b/src/lib-mail/test-message-parser.c +@@ -68,6 +68,32 @@ static bool msg_parts_cmp(struct message + return TRUE; + } + ++static void test_parsed_parts(struct istream *input, struct message_part *parts) ++{ ++ struct message_parser_ctx *parser; ++ struct message_block block; ++ struct message_part *parts2; ++ uoff_t i, input_size; ++ const char *error; ++ int ret; ++ ++ i_stream_seek(input, 0); ++ if (i_stream_get_size(input, TRUE, &input_size) < 0) ++ i_unreached(); ++ ++ parser = message_parser_init_from_parts(parts, input, 0, ++ MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK); ++ for (i = 1; i <= input_size*2+1; i++) { ++ test_istream_set_size(input, i/2); ++ if (i > TEST_MSG_LEN*2) ++ test_istream_set_allow_eof(input, TRUE); ++ while ((ret = message_parser_parse_next_block(parser, ++ &block)) > 0) ; ++ } ++ test_assert(message_parser_deinit_from_parts(&parser, &parts2, &error) == 0); ++ test_assert(msg_parts_cmp(parts, parts2)); ++} ++ + static void test_message_parser_small_blocks(void) + { + struct message_parser_ctx *parser; +@@ -170,18 +196,25 @@ static const char input_msg[] = + test_assert(message_parser_deinit(&parser, &parts) == 0); + + test_assert((parts->flags & MESSAGE_PART_FLAG_MULTIPART) != 0); ++ test_assert(parts->header_size.lines == 2); ++ test_assert(parts->header_size.physical_size == 48); ++ test_assert(parts->header_size.virtual_size == 48+2); + test_assert(parts->body_size.lines == 8); + test_assert(parts->body_size.physical_size == 112); + test_assert(parts->body_size.virtual_size == 112+7); ++ test_assert(parts->children->physical_pos == 55); + test_assert(parts->children->header_size.physical_size == 0); + test_assert(parts->children->body_size.physical_size == 0); + test_assert(parts->children->body_size.lines == 0); ++ test_assert(parts->children->next->physical_pos == 62); + test_assert(parts->children->next->header_size.physical_size == 24); + test_assert(parts->children->next->header_size.virtual_size == 24); + test_assert(parts->children->next->header_size.lines == 0); ++ test_assert(parts->children->next->next->physical_pos == 94); + test_assert(parts->children->next->next->header_size.physical_size == 24); + test_assert(parts->children->next->next->header_size.virtual_size == 24); + test_assert(parts->children->next->next->header_size.lines == 0); ++ test_assert(parts->children->next->next->next->physical_pos == 127); + test_assert(parts->children->next->next->next->header_size.physical_size == 23); + test_assert(parts->children->next->next->next->header_size.virtual_size == 23); + test_assert(parts->children->next->next->next->header_size.lines == 0); +@@ -191,6 +224,7 @@ static const char input_msg[] = + } + test_assert(parts->children->next->next->next->next == NULL); + ++ test_parsed_parts(input, parts); + i_stream_unref(&input); + pool_unref(&pool); + test_end(); +@@ -251,6 +285,7 @@ static const char input_msg[] = + test_assert(parts->children->next->body_size.virtual_size == 5+2); + test_assert(parts->children->next->children == NULL); + ++ test_parsed_parts(input, parts); + i_stream_unref(&input); + pool_unref(&pool); + test_end(); +@@ -267,7 +302,7 @@ static const char input_msg[] = + pool_t pool; + int ret; + +- test_begin("message parser truncated mime headers 3"); ++ test_begin("message parser empty multipart"); + pool = pool_alloconly_create("message parser", 10240); + input = test_istream_create(input_msg); + +@@ -285,6 +320,7 @@ static const char input_msg[] = + + test_assert(parts->children == NULL); + ++ test_parsed_parts(input, parts); + i_stream_unref(&input); + pool_unref(&pool); + test_end(); +@@ -342,6 +378,7 @@ static const char input_msg[] = + test_assert(parts->children->children->body_size.physical_size == 5); + test_assert(parts->children->children->body_size.virtual_size == 5+1); + ++ test_parsed_parts(input, parts); + i_stream_unref(&input); + pool_unref(&pool); + test_end(); +@@ -399,6 +436,7 @@ static const char input_msg[] = + test_assert(parts->children->children->body_size.physical_size == 5); + test_assert(parts->children->children->body_size.virtual_size == 5+1); + ++ test_parsed_parts(input, parts); + i_stream_unref(&input); + pool_unref(&pool); + test_end(); +@@ -426,6 +464,7 @@ static void test_message_parser_no_eoh(v + test_assert(message_parser_parse_next_block(parser, &block) < 0); + test_assert(message_parser_deinit(&parser, &parts) == 0); + ++ test_parsed_parts(input, parts); + i_stream_unref(&input); + pool_unref(&pool); + test_end(); diff -Nru dovecot-2.2.22/debian/patches/CVE-2020-12100/backport4.patch dovecot-2.2.22/debian/patches/CVE-2020-12100/backport4.patch --- dovecot-2.2.22/debian/patches/CVE-2020-12100/backport4.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.2.22/debian/patches/CVE-2020-12100/backport4.patch 2020-08-07 11:14:29.000000000 +0000 @@ -0,0 +1,29 @@ +From 20faa69d801460e89aa0b1214f3db4b026999b1e Mon Sep 17 00:00:00 2001 +From: Timo Sirainen +Date: Sat, 11 Jun 2016 21:20:37 +0300 +Subject: [PATCH] lib-mail: message-header-parser now keeps istream referenced. + +--- + src/lib-mail/message-header-parser.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/lib-mail/message-header-parser.c b/src/lib-mail/message-header-parser.c +index 6f327044e6..096586d009 100644 +--- a/src/lib-mail/message-header-parser.c ++++ b/src/lib-mail/message-header-parser.c +@@ -33,6 +33,7 @@ message_parse_header_init(struct istream *input, struct message_size *hdr_size, + ctx->name = str_new(default_pool, 128); + ctx->flags = flags; + ctx->value_buf = buffer_create_dynamic(default_pool, 4096); ++ i_stream_ref(input); + + if (hdr_size != NULL) + memset(hdr_size, 0, sizeof(*hdr_size)); +@@ -43,6 +44,7 @@ void message_parse_header_deinit(struct message_header_parser_ctx **_ctx) + { + struct message_header_parser_ctx *ctx = *_ctx; + ++ i_stream_unref(&ctx->input); + buffer_free(&ctx->value_buf); + str_free(&ctx->name); + i_free(ctx); diff -Nru dovecot-2.2.22/debian/patches/CVE-2020-12100/backport5.patch dovecot-2.2.22/debian/patches/CVE-2020-12100/backport5.patch --- dovecot-2.2.22/debian/patches/CVE-2020-12100/backport5.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.2.22/debian/patches/CVE-2020-12100/backport5.patch 2020-08-07 11:14:32.000000000 +0000 @@ -0,0 +1,38 @@ +From 1bc6f1c54b4d77830288b8cf19060bd8a6db7b27 Mon Sep 17 00:00:00 2001 +From: Timo Sirainen +Date: Sat, 11 Jun 2016 21:23:07 +0300 +Subject: [PATCH] lib-mail: Fixed message_parser_init_from_parts() with + truncated MIME headers + +--- + src/lib-mail/message-parser.c | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +diff --git a/src/lib-mail/message-parser.c b/src/lib-mail/message-parser.c +index b426b079b2..5c99d6e002 100644 +--- a/src/lib-mail/message-parser.c ++++ b/src/lib-mail/message-parser.c +@@ -1006,14 +1006,22 @@ static int preparsed_parse_next_header(struct message_parser_ctx *ctx, + static int preparsed_parse_next_header_init(struct message_parser_ctx *ctx, + struct message_block *block_r) + { ++ struct istream *hdr_input; ++ + i_assert(ctx->hdr_parser_ctx == NULL); + + i_assert(ctx->part->physical_pos >= ctx->input->v_offset); + i_stream_skip(ctx->input, ctx->part->physical_pos - + ctx->input->v_offset); + ++ /* the header may become truncated by --boundaries. limit the header ++ stream's size to what it's supposed to be to avoid duplicating (and ++ keeping in sync!) all the same complicated logic as in ++ parse_next_header(). */ ++ hdr_input = i_stream_create_limit(ctx->input, ctx->part->header_size.physical_size); + ctx->hdr_parser_ctx = +- message_parse_header_init(ctx->input, NULL, ctx->hdr_flags); ++ message_parse_header_init(hdr_input, NULL, ctx->hdr_flags); ++ i_stream_unref(&hdr_input); + + ctx->parse_next_block = preparsed_parse_next_header; + return preparsed_parse_next_header(ctx, block_r); diff -Nru dovecot-2.2.22/debian/patches/CVE-2020-12100/buffer_free_fix.patch dovecot-2.2.22/debian/patches/CVE-2020-12100/buffer_free_fix.patch --- dovecot-2.2.22/debian/patches/CVE-2020-12100/buffer_free_fix.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.2.22/debian/patches/CVE-2020-12100/buffer_free_fix.patch 2020-08-06 13:39:42.000000000 +0000 @@ -0,0 +1,21 @@ +From 1a6ff0beebf0ab0c71081eaff1d5d7fd26015a94 Mon Sep 17 00:00:00 2001 +From: Josef 'Jeff' Sipek +Date: Tue, 19 Sep 2017 13:26:57 +0300 +Subject: [PATCH] lib: buffer_free(NULL) should be a no-op + +--- + src/lib/buffer.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/src/lib/buffer.c ++++ b/src/lib/buffer.c +@@ -142,6 +142,9 @@ void buffer_free(buffer_t **_buf) + { + struct real_buffer *buf = (struct real_buffer *)*_buf; + ++ if (buf == NULL) ++ return; ++ + *_buf = NULL; + if (buf->alloced) + p_free(buf->pool, buf->w_buffer); diff -Nru dovecot-2.2.22/debian/patches/CVE-2020-12673/0002-lib-ntlm-Check-buffer-length-on-responses.patch dovecot-2.2.22/debian/patches/CVE-2020-12673/0002-lib-ntlm-Check-buffer-length-on-responses.patch --- dovecot-2.2.22/debian/patches/CVE-2020-12673/0002-lib-ntlm-Check-buffer-length-on-responses.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.2.22/debian/patches/CVE-2020-12673/0002-lib-ntlm-Check-buffer-length-on-responses.patch 2020-08-05 14:49:05.000000000 +0000 @@ -0,0 +1,31 @@ +Backport of: + +From 1c6405d3026e5ceae3d214d63945bba85251af4c Mon Sep 17 00:00:00 2001 +From: Aki Tuomi +Date: Mon, 18 May 2020 12:33:39 +0300 +Subject: [PATCH 2/3] lib-ntlm: Check buffer length on responses + +Add missing check for buffer length. + +If this is not checked, it is possible to send message which +causes read past buffer bug. + +Broken in c7480644202e5451fbed448508ea29a25cffc99c +--- + src/lib-ntlm/ntlm-message.c | 5 +++++ + 1 file changed, 5 insertions(+) + +--- a/src/lib-ntlm/ntlm-message.c ++++ b/src/lib-ntlm/ntlm-message.c +@@ -184,6 +184,11 @@ static int ntlmssp_check_buffer(const st + if (length == 0 && space == 0) + return 1; + ++ if (length > data_size) { ++ *error = "buffer length out of bounds"; ++ return 0; ++ } ++ + if (offset >= data_size) { + *error = "buffer offset out of bounds"; + return 0; diff -Nru dovecot-2.2.22/debian/patches/CVE-2020-12674/0001-auth-mech-rpa-Fail-on-zero-len-buffer.patch dovecot-2.2.22/debian/patches/CVE-2020-12674/0001-auth-mech-rpa-Fail-on-zero-len-buffer.patch --- dovecot-2.2.22/debian/patches/CVE-2020-12674/0001-auth-mech-rpa-Fail-on-zero-len-buffer.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.2.22/debian/patches/CVE-2020-12674/0001-auth-mech-rpa-Fail-on-zero-len-buffer.patch 2020-08-05 14:24:44.000000000 +0000 @@ -0,0 +1,25 @@ +From bd9d2fe7da833f0e4705a8280efc56930371806b Mon Sep 17 00:00:00 2001 +From: Aki Tuomi +Date: Wed, 6 May 2020 13:40:36 +0300 +Subject: [PATCH 1/3] auth: mech-rpa - Fail on zero len buffer + +--- + src/auth/mech-rpa.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/auth/mech-rpa.c b/src/auth/mech-rpa.c +index 08298ebdd6..2de8705b4f 100644 +--- a/src/auth/mech-rpa.c ++++ b/src/auth/mech-rpa.c +@@ -224,7 +224,7 @@ rpa_read_buffer(pool_t pool, const unsigned char **data, + return 0; + + len = *p++; +- if (p + len > end) ++ if (p + len > end || len == 0) + return 0; + + *buffer = p_malloc(pool, len); +-- +2.11.0 + diff -Nru dovecot-2.2.22/debian/patches/series dovecot-2.2.22/debian/patches/series --- dovecot-2.2.22/debian/patches/series 2019-08-14 16:19:44.000000000 +0000 +++ dovecot-2.2.22/debian/patches/series 2020-08-07 11:33:43.000000000 +0000 @@ -31,3 +31,24 @@ CVE-2019-11500-2.patch CVE-2019-11500-3.patch CVE-2019-11500-4.patch +CVE-2020-12100/backport1.patch +CVE-2020-12100/backport2.patch +CVE-2020-12100/backport3.patch +CVE-2020-12100/backport4.patch +CVE-2020-12100/backport5.patch +CVE-2020-12100/0001-lib-mail-message-parser-Add-a-message_part_finish-he.patch +CVE-2020-12100/0002-lib-mail-message-parser-Change-message_part_append-t.patch +CVE-2020-12100/0003-lib-mail-message-parser-Optimize-updating-children_c.patch +CVE-2020-12100/0004-lib-mail-message-parser-Optimize-appending-new-part-.patch +CVE-2020-12100/0005-lib-mail-message-parser-Minor-code-cleanup-to-findin.patch +CVE-2020-12100/0006-lib-mail-message-parser-Truncate-excessively-long-MI.patch +CVE-2020-12100/0007-lib-mail-message-parser-Optimize-boundary-lookups-wh.patch +CVE-2020-12100/0008-lib-mail-message-parser-Add-boundary_remove_until-he.patch +CVE-2020-12100/0009-lib-mail-message-parser-Don-t-use-memory-pool-for-pa.patch +CVE-2020-12100/0010-lib-mail-message-parser-Support-limiting-max-number-.patch +CVE-2020-12100/0011-lib-mail-message-parser-Support-limiting-max-number-.patch +CVE-2020-12100/0012-lib-mail-Fix-handling-trailing-in-MIME-boundaries.patch +CVE-2020-12100/0013-lib-mail-Fix-parse_too_many_nested_mime_parts.patch +CVE-2020-12100/buffer_free_fix.patch +CVE-2020-12673/0002-lib-ntlm-Check-buffer-length-on-responses.patch +CVE-2020-12674/0001-auth-mech-rpa-Fail-on-zero-len-buffer.patch