Blame SOURCES/dovecot-2.3.8-CVE_2020_12100.patch

5be1f1
diff -up dovecot-2.2.36/src/doveadm/doveadm-mail-fetch.c.CVE_2020_12100 dovecot-2.2.36/src/doveadm/doveadm-mail-fetch.c
5be1f1
--- dovecot-2.2.36/src/doveadm/doveadm-mail-fetch.c.CVE_2020_12100	2018-04-30 15:52:05.000000000 +0200
5be1f1
+++ dovecot-2.2.36/src/doveadm/doveadm-mail-fetch.c	2020-08-08 13:57:49.295662113 +0200
5be1f1
@@ -265,6 +265,9 @@ static int fetch_text(struct fetch_cmd_c
5be1f1
 
5be1f1
 static int fetch_text_utf8(struct fetch_cmd_context *ctx)
5be1f1
 {
5be1f1
+	const struct message_parser_settings parser_set = {
5be1f1
+		.hdr_flags = MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE,
5be1f1
+	};
5be1f1
 	struct istream *input;
5be1f1
 	struct message_parser_ctx *parser;
5be1f1
 	struct message_decoder_context *decoder;
5be1f1
@@ -275,9 +278,7 @@ static int fetch_text_utf8(struct fetch_
5be1f1
 	if (mail_get_stream(ctx->mail, NULL, NULL, &input) < 0)
5be1f1
 		return -1;
5be1f1
 
5be1f1
-	parser = message_parser_init(pool_datastack_create(), input,
5be1f1
-				     MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE,
5be1f1
-				     0);
5be1f1
+	parser = message_parser_init(pool_datastack_create(), input, &parser_set);
5be1f1
 	decoder = message_decoder_init(NULL, 0);
5be1f1
 
5be1f1
 	while ((ret = message_parser_parse_next_block(parser, &raw_block)) > 0) {
5be1f1
diff -up dovecot-2.2.36/src/lib-imap/test-imap-bodystructure.c.CVE_2020_12100 dovecot-2.2.36/src/lib-imap/test-imap-bodystructure.c
5be1f1
--- dovecot-2.2.36/src/lib-imap/test-imap-bodystructure.c.CVE_2020_12100	2018-04-30 15:52:05.000000000 +0200
5be1f1
+++ dovecot-2.2.36/src/lib-imap/test-imap-bodystructure.c	2020-08-08 13:57:49.295662113 +0200
5be1f1
@@ -381,6 +381,11 @@ static const unsigned int normalize_test
5be1f1
 static struct message_part *
5be1f1
 msg_parse(pool_t pool, const char *message, bool parse_bodystructure)
5be1f1
 {
5be1f1
+	const struct message_parser_settings parser_set = {
5be1f1
+		.hdr_flags = MESSAGE_HEADER_PARSER_FLAG_SKIP_INITIAL_LWSP |
5be1f1
+			MESSAGE_HEADER_PARSER_FLAG_DROP_CR,
5be1f1
+		.flags = MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK,
5be1f1
+	};
5be1f1
 	struct message_parser_ctx *parser;
5be1f1
 	struct istream *input;
5be1f1
 	struct message_block block;
5be1f1
@@ -388,10 +393,7 @@ msg_parse(pool_t pool, const char *messa
5be1f1
 	int ret;
5be1f1
 
5be1f1
 	input = i_stream_create_from_data(message, strlen(message));
5be1f1
-	parser = message_parser_init(pool, input,
5be1f1
-			MESSAGE_HEADER_PARSER_FLAG_SKIP_INITIAL_LWSP |
5be1f1
-			MESSAGE_HEADER_PARSER_FLAG_DROP_CR,
5be1f1
-			MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK);
5be1f1
+	parser = message_parser_init(pool, input, &parser_set);
5be1f1
 	while ((ret = message_parser_parse_next_block(parser, &block)) > 0) {
5be1f1
 		if (parse_bodystructure) {
5be1f1
 			message_part_data_parse_from_header(pool, block.part,
5be1f1
diff -up dovecot-2.2.36/src/lib-imap/test-imap-envelope.c.CVE_2020_12100 dovecot-2.2.36/src/lib-imap/test-imap-envelope.c
5be1f1
--- dovecot-2.2.36/src/lib-imap/test-imap-envelope.c.CVE_2020_12100	2018-04-30 15:52:05.000000000 +0200
5be1f1
+++ dovecot-2.2.36/src/lib-imap/test-imap-envelope.c	2020-08-08 13:57:49.295662113 +0200
5be1f1
@@ -118,6 +118,11 @@ static const unsigned int parse_tests_co
5be1f1
 static struct message_part_envelope *
5be1f1
 msg_parse(pool_t pool, const char *message)
5be1f1
 {
5be1f1
+	const struct message_parser_settings parser_set = {
5be1f1
+		.hdr_flags = MESSAGE_HEADER_PARSER_FLAG_SKIP_INITIAL_LWSP |
5be1f1
+			MESSAGE_HEADER_PARSER_FLAG_DROP_CR,
5be1f1
+		.flags = MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK,
5be1f1
+	};
5be1f1
 	struct message_parser_ctx *parser;
5be1f1
 	struct message_part_envelope *envlp = NULL;
5be1f1
 	struct istream *input;
5be1f1
@@ -126,10 +131,7 @@ msg_parse(pool_t pool, const char *messa
5be1f1
 	int ret;
5be1f1
 
5be1f1
 	input = i_stream_create_from_data(message, strlen(message));
5be1f1
-	parser = message_parser_init(pool, input,
5be1f1
-			MESSAGE_HEADER_PARSER_FLAG_SKIP_INITIAL_LWSP |
5be1f1
-			MESSAGE_HEADER_PARSER_FLAG_DROP_CR,
5be1f1
-			MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK);
5be1f1
+	parser = message_parser_init(pool, input, &parser_set);
5be1f1
 	while ((ret = message_parser_parse_next_block(parser, &block)) > 0) {
5be1f1
 		i_assert(block.part->parent == NULL);
5be1f1
 		message_part_envelope_parse_from_header(pool, &envlp, block.hdr);
5be1f1
diff -up dovecot-2.2.36/src/lib-mail/istream-attachment-extractor.c.CVE_2020_12100 dovecot-2.2.36/src/lib-mail/istream-attachment-extractor.c
5be1f1
--- dovecot-2.2.36/src/lib-mail/istream-attachment-extractor.c.CVE_2020_12100	2018-04-30 15:52:05.000000000 +0200
5be1f1
+++ dovecot-2.2.36/src/lib-mail/istream-attachment-extractor.c	2020-08-08 13:59:10.665535546 +0200
5be1f1
@@ -691,6 +691,10 @@ i_stream_create_attachment_extractor(str
5be1f1
 				     struct istream_attachment_settings *set,
5be1f1
 				     void *context)
5be1f1
 {
5be1f1
+	const struct message_parser_settings parser_set = {
5be1f1
+		.flags = MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS |
5be1f1
+			MESSAGE_PARSER_FLAG_INCLUDE_BOUNDARIES,
5be1f1
+	};
5be1f1
 	struct attachment_istream *astream;
5be1f1
 
5be1f1
 	i_assert(set->min_size > 0);
5be1f1
@@ -717,9 +721,7 @@ i_stream_create_attachment_extractor(str
5be1f1
 	astream->istream.istream.seekable = FALSE;
5be1f1
 
5be1f1
 	astream->pool = pool_alloconly_create("istream attachment", 1024);
5be1f1
-	astream->parser = message_parser_init(astream->pool, input, 0,
5be1f1
-				MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS |
5be1f1
-				MESSAGE_PARSER_FLAG_INCLUDE_BOUNDARIES);
5be1f1
+	astream->parser = message_parser_init(astream->pool, input, &parser_set);
5be1f1
 	return i_stream_create(&astream->istream, input,
5be1f1
 			       i_stream_get_fd(input));
5be1f1
 }
5be1f1
diff -up dovecot-2.2.36/src/lib-mail/istream-binary-converter.c.CVE_2020_12100 dovecot-2.2.36/src/lib-mail/istream-binary-converter.c
5be1f1
--- dovecot-2.2.36/src/lib-mail/istream-binary-converter.c.CVE_2020_12100	2018-04-30 15:52:05.000000000 +0200
5be1f1
+++ dovecot-2.2.36/src/lib-mail/istream-binary-converter.c	2020-08-08 14:00:06.560761661 +0200
5be1f1
@@ -286,6 +286,10 @@ static void i_stream_binary_converter_cl
5be1f1
 
5be1f1
 struct istream *i_stream_create_binary_converter(struct istream *input)
5be1f1
 {
5be1f1
+	const struct message_parser_settings parser_set = {
5be1f1
+		.flags = MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS |
5be1f1
+			MESSAGE_PARSER_FLAG_INCLUDE_BOUNDARIES,
5be1f1
+	};
5be1f1
 	struct binary_converter_istream *bstream;
5be1f1
 
5be1f1
 	bstream = i_new(struct binary_converter_istream, 1);
5be1f1
@@ -299,9 +303,7 @@ struct istream *i_stream_create_binary_c
5be1f1
 	bstream->istream.istream.seekable = FALSE;
5be1f1
 
5be1f1
 	bstream->pool = pool_alloconly_create("istream binary converter", 128);
5be1f1
-	bstream->parser = message_parser_init(bstream->pool, input, 0,
5be1f1
-				MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS |
5be1f1
-				MESSAGE_PARSER_FLAG_INCLUDE_BOUNDARIES);
5be1f1
+	bstream->parser = message_parser_init(bstream->pool, input, &parser_set);
5be1f1
 	return i_stream_create(&bstream->istream, input,
5be1f1
 			       i_stream_get_fd(input));
5be1f1
 }
5be1f1
diff -up dovecot-2.2.36/src/lib-mail/Makefile.am.CVE_2020_12100 dovecot-2.2.36/src/lib-mail/Makefile.am
5be1f1
--- dovecot-2.2.36/src/lib-mail/Makefile.am.CVE_2020_12100	2018-04-30 15:52:05.000000000 +0200
5be1f1
+++ dovecot-2.2.36/src/lib-mail/Makefile.am	2020-08-08 13:57:49.296662100 +0200
5be1f1
@@ -27,6 +27,7 @@ libmail_la_SOURCES = \
5be1f1
 	message-header-parser.c \
5be1f1
 	message-id.c \
5be1f1
 	message-parser.c \
5be1f1
+	message-parser-from-parts.c \
5be1f1
 	message-part.c \
5be1f1
 	message-part-data.c \
5be1f1
 	message-part-serialize.c \
5be1f1
@@ -41,7 +42,8 @@ libmail_la_SOURCES = \
5be1f1
 	rfc822-parser.c
5be1f1
 
5be1f1
 noinst_HEADERS = \
5be1f1
-	html-entities.h
5be1f1
+	html-entities.h \
5be1f1
+	message-parser-private.h
5be1f1
 
5be1f1
 headers = \
5be1f1
 	istream-attachment-connector.h \
5be1f1
diff -up dovecot-2.2.36/src/lib-mail/message-parser.c.CVE_2020_12100 dovecot-2.2.36/src/lib-mail/message-parser.c
5be1f1
--- dovecot-2.2.36/src/lib-mail/message-parser.c.CVE_2020_12100	2018-04-30 15:52:05.000000000 +0200
5be1f1
+++ dovecot-2.2.36/src/lib-mail/message-parser.c	2020-08-08 14:06:41.696290855 +0200
5be1f1
@@ -1,53 +1,12 @@
5be1f1
 /* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
5be1f1
 
5be1f1
 #include "lib.h"
5be1f1
-#include "buffer.h"
5be1f1
+#include "array.h"
5be1f1
 #include "str.h"
5be1f1
 #include "istream.h"
5be1f1
 #include "rfc822-parser.h"
5be1f1
 #include "rfc2231-parser.h"
5be1f1
-#include "message-parser.h"
5be1f1
-
5be1f1
-/* RFC-2046 requires boundaries are max. 70 chars + "--" prefix + "--" suffix.
5be1f1
-   We'll add a bit more just in case. */
5be1f1
-#define BOUNDARY_END_MAX_LEN (70 + 2 + 2 + 10)
5be1f1
-
5be1f1
-struct message_boundary {
5be1f1
-	struct message_boundary *next;
5be1f1
-
5be1f1
-	struct message_part *part;
5be1f1
-	const char *boundary;
5be1f1
-	size_t len;
5be1f1
-
5be1f1
-	unsigned int epilogue_found:1;
5be1f1
-};
5be1f1
-
5be1f1
-struct message_parser_ctx {
5be1f1
-	pool_t parser_pool, part_pool;
5be1f1
-	struct istream *input;
5be1f1
-	struct message_part *parts, *part;
5be1f1
-	const char *broken_reason;
5be1f1
-
5be1f1
-	enum message_header_parser_flags hdr_flags;
5be1f1
-	enum message_parser_flags flags;
5be1f1
-
5be1f1
-	const char *last_boundary;
5be1f1
-	struct message_boundary *boundaries;
5be1f1
-
5be1f1
-	size_t skip;
5be1f1
-	char last_chr;
5be1f1
-	unsigned int want_count;
5be1f1
-
5be1f1
-	struct message_header_parser_ctx *hdr_parser_ctx;
5be1f1
-	unsigned int prev_hdr_newline_size;
5be1f1
-
5be1f1
-	int (*parse_next_block)(struct message_parser_ctx *ctx,
5be1f1
-				struct message_block *block_r);
5be1f1
-
5be1f1
-	unsigned int part_seen_content_type:1;
5be1f1
-	unsigned int multipart:1;
5be1f1
-	unsigned int eof:1;
5be1f1
-};
5be1f1
+#include "message-parser-private.h"
5be1f1
 
5be1f1
 message_part_header_callback_t *null_message_part_header_callback = NULL;
5be1f1
 
5be1f1
@@ -57,14 +16,10 @@ static int parse_next_body_to_boundary(s
5be1f1
 				       struct message_block *block_r);
5be1f1
 static int parse_next_body_to_eof(struct message_parser_ctx *ctx,
5be1f1
 				  struct message_block *block_r);
5be1f1
-static int preparsed_parse_epilogue_init(struct message_parser_ctx *ctx,
5be1f1
-					 struct message_block *block_r);
5be1f1
-static int preparsed_parse_next_header_init(struct message_parser_ctx *ctx,
5be1f1
-					    struct message_block *block_r);
5be1f1
 
5be1f1
 static struct message_boundary *
5be1f1
 boundary_find(struct message_boundary *boundaries,
5be1f1
-	      const unsigned char *data, size_t len)
5be1f1
+	      const unsigned char *data, size_t len, bool trailing_dashes)
5be1f1
 {
5be1f1
 	struct message_boundary *best = NULL;
5be1f1
 
5be1f1
@@ -76,8 +31,18 @@ boundary_find(struct message_boundary *b
5be1f1
 	while (boundaries != NULL) {
5be1f1
 		if (boundaries->len <= len &&
5be1f1
 		    memcmp(boundaries->boundary, data, boundaries->len) == 0 &&
5be1f1
-		    (best == NULL || best->len < boundaries->len))
5be1f1
+		    (best == NULL || best->len < boundaries->len)) {
5be1f1
 			best = boundaries;
5be1f1
+			/* If we see "foo--", it could either mean that there
5be1f1
+			   is a boundary named "foo" that ends now or there's
5be1f1
+			   a boundary "foo--" which continues. */
5be1f1
+			if (best->len == len ||
5be1f1
+			    (best->len == len-2 && trailing_dashes)) {
5be1f1
+				/* This is exactly the wanted boundary. There
5be1f1
+				   can't be a better one. */
5be1f1
+				break;
5be1f1
+			}
5be1f1
+		}
5be1f1
 
5be1f1
 		boundaries = boundaries->next;
5be1f1
 	}
5be1f1
@@ -121,8 +86,8 @@ static void parse_body_add_block(struct
5be1f1
 	ctx->part->body_size.virtual_size += block->size + missing_cr_count;
5be1f1
 }
5be1f1
 
5be1f1
-static int message_parser_read_more(struct message_parser_ctx *ctx,
5be1f1
-				    struct message_block *block_r, bool *full_r)
5be1f1
+int message_parser_read_more(struct message_parser_ctx *ctx,
5be1f1
+			     struct message_block *block_r, bool *full_r)
5be1f1
 {
5be1f1
 	int ret;
5be1f1
 
5be1f1
@@ -167,19 +132,18 @@ static int message_parser_read_more(stru
5be1f1
 	return 1;
5be1f1
 }
5be1f1
 
5be1f1
-static struct message_part *
5be1f1
-message_part_append(pool_t pool, struct message_part *parent)
5be1f1
+static void
5be1f1
+message_part_append(struct message_parser_ctx *ctx)
5be1f1
 {
5be1f1
-	struct message_part *p, *part, **list;
5be1f1
+	struct message_part *parent = ctx->part;
5be1f1
+	struct message_part *part;
5be1f1
 
5be1f1
 	i_assert(parent != NULL);
5be1f1
 	i_assert((parent->flags & (MESSAGE_PART_FLAG_MULTIPART |
5be1f1
 				   MESSAGE_PART_FLAG_MESSAGE_RFC822)) != 0);
5be1f1
 
5be1f1
-	part = p_new(pool, struct message_part, 1);
5be1f1
+	part = p_new(ctx->part_pool, struct message_part, 1);
5be1f1
 	part->parent = parent;
5be1f1
-	for (p = parent; p != NULL; p = p->parent)
5be1f1
-		p->children_count++;
5be1f1
 
5be1f1
 	/* set child position */
5be1f1
 	part->physical_pos =
5be1f1
@@ -187,33 +151,78 @@ message_part_append(pool_t pool, struct
5be1f1
 		parent->body_size.physical_size +
5be1f1
 		parent->header_size.physical_size;
5be1f1
 
5be1f1
-	list = &part->parent->children;
5be1f1
-	while (*list != NULL)
5be1f1
-		list = &(*list)->next;
5be1f1
+	/* add to parent's linked list */
5be1f1
+	*ctx->next_part = part;
5be1f1
+	/* update the parent's end-of-linked-list pointer */
5be1f1
+	struct message_part **next_part = &part->next;
5be1f1
+	array_push_back(&ctx->next_part_stack, &next_part);
5be1f1
+	/* This part is now the new parent for the next message_part_append()
5be1f1
+	   call. Its linked list begins with the children pointer. */
5be1f1
+	ctx->next_part = &part->children;
5be1f1
+
5be1f1
+	ctx->part = part;
5be1f1
+	ctx->nested_parts_count++;
5be1f1
+	ctx->total_parts_count++;
5be1f1
+	i_assert(ctx->nested_parts_count < ctx->max_nested_mime_parts);
5be1f1
+	i_assert(ctx->total_parts_count <= ctx->max_total_mime_parts);
5be1f1
+}
5be1f1
+
5be1f1
+static void message_part_finish(struct message_parser_ctx *ctx)
5be1f1
+{
5be1f1
+	struct message_part **const *parent_next_partp;
5be1f1
+
5be1f1
+	i_assert(ctx->nested_parts_count > 0);
5be1f1
+	ctx->nested_parts_count--;
5be1f1
+
5be1f1
+	parent_next_partp = array_back(&ctx->next_part_stack);
5be1f1
+	array_pop_back(&ctx->next_part_stack);
5be1f1
+	ctx->next_part = *parent_next_partp;
5be1f1
+
5be1f1
+	message_size_add(&ctx->part->parent->body_size, &ctx->part->body_size);
5be1f1
+	message_size_add(&ctx->part->parent->body_size, &ctx->part->header_size);
5be1f1
+	ctx->part->parent->children_count += 1 + ctx->part->children_count;
5be1f1
+	ctx->part = ctx->part->parent;
5be1f1
+}
5be1f1
+
5be1f1
+static void message_boundary_free(struct message_boundary *b)
5be1f1
+{
5be1f1
+	i_free(b->boundary);
5be1f1
+	i_free(b);
5be1f1
+}
5be1f1
+
5be1f1
+static void
5be1f1
+boundary_remove_until(struct message_parser_ctx *ctx,
5be1f1
+		      struct message_boundary *boundary)
5be1f1
+{
5be1f1
+	while (ctx->boundaries != boundary) {
5be1f1
+		struct message_boundary *cur = ctx->boundaries;
5be1f1
 
5be1f1
-	*list = part;
5be1f1
-	return part;
5be1f1
+		i_assert(cur != NULL);
5be1f1
+		ctx->boundaries = cur->next;
5be1f1
+		message_boundary_free(cur);
5be1f1
+
5be1f1
+	}
5be1f1
+	ctx->boundaries = boundary;
5be1f1
 }
5be1f1
 
5be1f1
 static void parse_next_body_multipart_init(struct message_parser_ctx *ctx)
5be1f1
 {
5be1f1
 	struct message_boundary *b;
5be1f1
 
5be1f1
-	b = p_new(ctx->parser_pool, struct message_boundary, 1);
5be1f1
+	b = i_new(struct message_boundary, 1);
5be1f1
 	b->part = ctx->part;
5be1f1
 	b->boundary = ctx->last_boundary;
5be1f1
+	ctx->last_boundary = NULL;
5be1f1
 	b->len = strlen(b->boundary);
5be1f1
 
5be1f1
 	b->next = ctx->boundaries;
5be1f1
 	ctx->boundaries = b;
5be1f1
-
5be1f1
-	ctx->last_boundary = NULL;
5be1f1
 }
5be1f1
 
5be1f1
 static int parse_next_body_message_rfc822_init(struct message_parser_ctx *ctx,
5be1f1
 					       struct message_block *block_r)
5be1f1
 {
5be1f1
-	ctx->part = message_part_append(ctx->part_pool, ctx->part);
5be1f1
+	message_part_append(ctx);
5be1f1
 	return parse_next_header_init(ctx, block_r);
5be1f1
 }
5be1f1
 
5be1f1
@@ -238,19 +247,38 @@ boundary_line_find(struct message_parser
5be1f1
 		return -1;
5be1f1
 	}
5be1f1
 
5be1f1
+	if (ctx->total_parts_count >= ctx->max_total_mime_parts) {
5be1f1
+		/* can't add any more MIME parts. just stop trying to find
5be1f1
+		   more boundaries. */
5be1f1
+		return -1;
5be1f1
+	}
5be1f1
+
5be1f1
 	/* need to find the end of line */
5be1f1
-	if (memchr(data + 2, '\n', size - 2) == NULL &&
5be1f1
-	    size < BOUNDARY_END_MAX_LEN &&
5be1f1
+	data += 2;
5be1f1
+	size -= 2;
5be1f1
+	const unsigned char *lf_pos = memchr(data, '\n', size);
5be1f1
+	if (lf_pos == NULL &&
5be1f1
+	    size+2 < BOUNDARY_END_MAX_LEN &&
5be1f1
 	    !ctx->input->eof && !full) {
5be1f1
 		/* no LF found */
5be1f1
 		ctx->want_count = BOUNDARY_END_MAX_LEN;
5be1f1
 		return 0;
5be1f1
 	}
5be1f1
+	size_t find_size = size;
5be1f1
+	bool trailing_dashes = FALSE;
5be1f1
 
5be1f1
-	data += 2;
5be1f1
-	size -= 2;
5be1f1
+	if (lf_pos != NULL) {
5be1f1
+		find_size = lf_pos - data;
5be1f1
+		if (find_size > 0 && data[find_size-1] == '\r')
5be1f1
+			find_size--;
5be1f1
+		if (find_size > 2 && data[find_size-1] == '-' &&
5be1f1
+		    data[find_size-2] == '-')
5be1f1
+			trailing_dashes = TRUE;
5be1f1
+	} else if (find_size > BOUNDARY_END_MAX_LEN)
5be1f1
+		find_size = BOUNDARY_END_MAX_LEN;
5be1f1
 
5be1f1
-	*boundary_r = boundary_find(ctx->boundaries, data, size);
5be1f1
+	*boundary_r = boundary_find(ctx->boundaries, data, find_size,
5be1f1
+				    trailing_dashes);
5be1f1
 	if (*boundary_r == NULL)
5be1f1
 		return -1;
5be1f1
 
5be1f1
@@ -263,7 +291,7 @@ boundary_line_find(struct message_parser
5be1f1
 static int parse_next_mime_header_init(struct message_parser_ctx *ctx,
5be1f1
 				       struct message_block *block_r)
5be1f1
 {
5be1f1
-	ctx->part = message_part_append(ctx->part_pool, ctx->part);
5be1f1
+	message_part_append(ctx);
5be1f1
 	ctx->part->flags |= MESSAGE_PART_FLAG_IS_MIME;
5be1f1
 
5be1f1
 	return parse_next_header_init(ctx, block_r);
5be1f1
@@ -312,26 +340,25 @@ static int parse_part_finish(struct mess
5be1f1
 			     struct message_boundary *boundary,
5be1f1
 			     struct message_block *block_r, bool first_line)
5be1f1
 {
5be1f1
-	struct message_part *part;
5be1f1
 	size_t line_size;
5be1f1
+	size_t boundary_len = boundary->len;
5be1f1
+	bool boundary_epilogue_found = boundary->epilogue_found;
5be1f1
 
5be1f1
 	i_assert(ctx->last_boundary == NULL);
5be1f1
 
5be1f1
 	/* get back to parent MIME part, summing the child MIME part sizes
5be1f1
 	   into parent's body sizes */
5be1f1
-	for (part = ctx->part; part != boundary->part; part = part->parent) {
5be1f1
-		message_size_add(&part->parent->body_size, &part->body_size);
5be1f1
-		message_size_add(&part->parent->body_size, &part->header_size);
5be1f1
+	while (ctx->part != boundary->part) {
5be1f1
+		message_part_finish(ctx);
5be1f1
+		i_assert(ctx->part != NULL);
5be1f1
 	}
5be1f1
-	i_assert(part != NULL);
5be1f1
-	ctx->part = part;
5be1f1
 
5be1f1
 	if (boundary->epilogue_found) {
5be1f1
 		/* this boundary isn't needed anymore */
5be1f1
-		ctx->boundaries = boundary->next;
5be1f1
+		boundary_remove_until(ctx, boundary->next);
5be1f1
 	} else {
5be1f1
 		/* forget about the boundaries we possibly skipped */
5be1f1
-		ctx->boundaries = boundary;
5be1f1
+		boundary_remove_until(ctx, boundary);
5be1f1
 	}
5be1f1
 
5be1f1
 	/* the boundary itself should already be in buffer. add that. */
5be1f1
@@ -348,7 +375,7 @@ static int parse_part_finish(struct mess
5be1f1
 		i_assert(block_r->data[0] == '\n');
5be1f1
 		line_size = 1;
5be1f1
 	}
5be1f1
-	line_size += 2 + boundary->len + (boundary->epilogue_found ? 2 : 0);
5be1f1
+	line_size += 2 + boundary_len + (boundary_epilogue_found ? 2 : 0);
5be1f1
 	i_assert(block_r->size >= ctx->skip + line_size);
5be1f1
 	block_r->size = line_size;
5be1f1
 	parse_body_add_block(ctx, block_r);
5be1f1
@@ -509,8 +536,10 @@ static void parse_content_type(struct me
5be1f1
 	rfc2231_parse(&parser, &results);
5be1f1
 	for (; *results != NULL; results += 2) {
5be1f1
 		if (strcasecmp(results[0], "boundary") == 0) {
5be1f1
+			/* truncate excessively long boundaries */
5be1f1
+			i_free(ctx->last_boundary);
5be1f1
 			ctx->last_boundary =
5be1f1
-				p_strdup(ctx->parser_pool, results[1]);
5be1f1
+				i_strndup(results[1], BOUNDARY_STRING_MAX_LEN);
5be1f1
 			break;
5be1f1
 		}
5be1f1
 	}
5be1f1
@@ -532,6 +561,11 @@ static bool block_is_at_eoh(const struct
5be1f1
 	return FALSE;
5be1f1
 }
5be1f1
 
5be1f1
+static bool parse_too_many_nested_mime_parts(struct message_parser_ctx *ctx)
5be1f1
+{
5be1f1
+	return ctx->nested_parts_count+1 >= ctx->max_nested_mime_parts;
5be1f1
+}
5be1f1
+
5be1f1
 #define MUTEX_FLAGS \
5be1f1
 	(MESSAGE_PART_FLAG_MESSAGE_RFC822 | MESSAGE_PART_FLAG_MULTIPART)
5be1f1
 
5be1f1
@@ -556,8 +590,12 @@ static int parse_next_header(struct mess
5be1f1
 		   "\n--boundary" belongs to us or to a previous boundary.
5be1f1
 		   this is a problem if the boundary prefixes are identical,
5be1f1
 		   because MIME requires only the prefix to match. */
5be1f1
-		parse_next_body_multipart_init(ctx);
5be1f1
-		ctx->multipart = TRUE;
5be1f1
+		if (!parse_too_many_nested_mime_parts(ctx)) {
5be1f1
+			parse_next_body_multipart_init(ctx);
5be1f1
+			ctx->multipart = TRUE;
5be1f1
+		} else {
5be1f1
+			part->flags &= ~MESSAGE_PART_FLAG_MULTIPART;
5be1f1
+		}
5be1f1
 	}
5be1f1
 
5be1f1
 	/* before parsing the header see if we can find a --boundary from here.
5be1f1
@@ -633,7 +671,7 @@ static int parse_next_header(struct mess
5be1f1
 		i_assert(!ctx->multipart);
5be1f1
 		part->flags = 0;
5be1f1
 	}
5be1f1
-	ctx->last_boundary = NULL;
5be1f1
+	i_free(ctx->last_boundary);
5be1f1
 
5be1f1
 	if (!ctx->part_seen_content_type ||
5be1f1
 	    (part->flags & MESSAGE_PART_FLAG_IS_MIME) == 0) {
5be1f1
@@ -661,12 +699,16 @@ static int parse_next_header(struct mess
5be1f1
 		i_assert(ctx->last_boundary == NULL);
5be1f1
 		ctx->multipart = FALSE;
5be1f1
 		ctx->parse_next_block = parse_next_body_to_boundary;
5be1f1
-	} else if (part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822)
5be1f1
+	} else if ((part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) != 0 &&
5be1f1
+		   !parse_too_many_nested_mime_parts(ctx)) {
5be1f1
 		ctx->parse_next_block = parse_next_body_message_rfc822_init;
5be1f1
-	else if (ctx->boundaries != NULL)
5be1f1
-		ctx->parse_next_block = parse_next_body_to_boundary;
5be1f1
-	else
5be1f1
-		ctx->parse_next_block = parse_next_body_to_eof;
5be1f1
+	} else {
5be1f1
+		part->flags &= ~MESSAGE_PART_FLAG_MESSAGE_RFC822;
5be1f1
+		if (ctx->boundaries != NULL)
5be1f1
+			ctx->parse_next_block = parse_next_body_to_boundary;
5be1f1
+		else
5be1f1
+			ctx->parse_next_block = parse_next_body_to_eof;
5be1f1
+	}
5be1f1
 
5be1f1
 	ctx->want_count = 1;
5be1f1
 
5be1f1
@@ -691,358 +733,21 @@ static int parse_next_header_init(struct
5be1f1
 	return parse_next_header(ctx, block_r);
5be1f1
 }
5be1f1
 
5be1f1
-static int preparsed_parse_eof(struct message_parser_ctx *ctx ATTR_UNUSED,
5be1f1
-			       struct message_block *block_r ATTR_UNUSED)
5be1f1
-{
5be1f1
-	return -1;
5be1f1
-}
5be1f1
-
5be1f1
-static void preparsed_skip_to_next(struct message_parser_ctx *ctx)
5be1f1
-{
5be1f1
-	ctx->parse_next_block = preparsed_parse_next_header_init;
5be1f1
-	while (ctx->part != NULL) {
5be1f1
-		if (ctx->part->next != NULL) {
5be1f1
-			ctx->part = ctx->part->next;
5be1f1
-			break;
5be1f1
-		}
5be1f1
-
5be1f1
-		/* parse epilogue of multipart parent if requested */
5be1f1
-		if (ctx->part->parent != NULL &&
5be1f1
-		    (ctx->part->parent->flags & MESSAGE_PART_FLAG_MULTIPART) != 0 &&
5be1f1
-		    (ctx->flags & MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS) != 0) {
5be1f1
-			/* check for presence of epilogue */
5be1f1
-			uoff_t part_end = ctx->part->physical_pos +
5be1f1
-				ctx->part->header_size.physical_size +
5be1f1
-				ctx->part->body_size.physical_size;
5be1f1
-			uoff_t parent_end = ctx->part->parent->physical_pos +
5be1f1
-				ctx->part->parent->header_size.physical_size +
5be1f1
-				ctx->part->parent->body_size.physical_size;
5be1f1
-
5be1f1
-			if (parent_end > part_end) {
5be1f1
-				ctx->parse_next_block = preparsed_parse_epilogue_init;
5be1f1
-				break;
5be1f1
-			}
5be1f1
-		}
5be1f1
-		ctx->part = ctx->part->parent;
5be1f1
-	}
5be1f1
-	if (ctx->part == NULL)
5be1f1
-		ctx->parse_next_block = preparsed_parse_eof;
5be1f1
-}
5be1f1
-
5be1f1
-static int preparsed_parse_body_finish(struct message_parser_ctx *ctx,
5be1f1
-				       struct message_block *block_r)
5be1f1
-{
5be1f1
-	i_stream_skip(ctx->input, ctx->skip);
5be1f1
-	ctx->skip = 0;
5be1f1
-
5be1f1
-	preparsed_skip_to_next(ctx);
5be1f1
-	return ctx->parse_next_block(ctx, block_r);
5be1f1
-}
5be1f1
-
5be1f1
-static int preparsed_parse_prologue_finish(struct message_parser_ctx *ctx,
5be1f1
-					   struct message_block *block_r)
5be1f1
-{
5be1f1
-	i_stream_skip(ctx->input, ctx->skip);
5be1f1
-	ctx->skip = 0;
5be1f1
-
5be1f1
-	ctx->parse_next_block = preparsed_parse_next_header_init;
5be1f1
-	ctx->part = ctx->part->children;
5be1f1
-	return ctx->parse_next_block(ctx, block_r);
5be1f1
-}
5be1f1
-
5be1f1
-static int preparsed_parse_body_more(struct message_parser_ctx *ctx,
5be1f1
-				     struct message_block *block_r)
5be1f1
-{
5be1f1
-	uoff_t end_offset = ctx->part->physical_pos +
5be1f1
-		ctx->part->header_size.physical_size +
5be1f1
-		ctx->part->body_size.physical_size;
5be1f1
-	bool full;
5be1f1
-	int ret;
5be1f1
-
5be1f1
-	if ((ret = message_parser_read_more(ctx, block_r, &full)) <= 0)
5be1f1
-		return ret;
5be1f1
-
5be1f1
-	if (ctx->input->v_offset + block_r->size >= end_offset) {
5be1f1
-		block_r->size = end_offset - ctx->input->v_offset;
5be1f1
-		ctx->parse_next_block = preparsed_parse_body_finish;
5be1f1
-	}
5be1f1
-	ctx->skip = block_r->size;
5be1f1
-	return 1;
5be1f1
-}
5be1f1
-
5be1f1
-static int preparsed_parse_prologue_more(struct message_parser_ctx *ctx,
5be1f1
-					 struct message_block *block_r)
5be1f1
-{
5be1f1
-	uoff_t boundary_min_start, end_offset;
5be1f1
-	const unsigned char *cur;
5be1f1
-	bool full;
5be1f1
-	int ret;
5be1f1
-
5be1f1
-	i_assert(ctx->part->children != NULL);
5be1f1
-	end_offset = ctx->part->children->physical_pos;
5be1f1
-
5be1f1
-	if ((ret = message_parser_read_more(ctx, block_r, &full)) <= 0)
5be1f1
-		return ret;
5be1f1
-
5be1f1
-	if (ctx->input->v_offset + block_r->size >= end_offset) {
5be1f1
-		/* we've got the full prologue: clip off the initial boundary */
5be1f1
-		block_r->size = end_offset - ctx->input->v_offset;
5be1f1
-		cur = block_r->data + block_r->size - 1;
5be1f1
-
5be1f1
-		/* [\r]\n--boundary[\r]\n */ 
5be1f1
-		if (block_r->size < 5 || *cur != '\n') {
5be1f1
-			ctx->broken_reason = "Prologue boundary end not at expected position";
5be1f1
-			return -1;
5be1f1
-		}
5be1f1
-		
5be1f1
-		cur--;
5be1f1
-		if (*cur == '\r') cur--;
5be1f1
-
5be1f1
-		/* find newline just before boundary */
5be1f1
-		for (; cur >= block_r->data; cur--) {
5be1f1
-			if (*cur == '\n') break;
5be1f1
-		}
5be1f1
-
5be1f1
-		if (cur[0] != '\n' || cur[1] != '-' || cur[2] != '-') {
5be1f1
-			ctx->broken_reason = "Prologue boundary beginning not at expected position";
5be1f1
-			return -1;
5be1f1
-		}
5be1f1
-
5be1f1
-		if (cur != block_r->data && cur[-1] == '\r') cur--;
5be1f1
-
5be1f1
-		/* clip boundary */
5be1f1
-		block_r->size = cur - block_r->data;			
5be1f1
-
5be1f1
-		ctx->parse_next_block = preparsed_parse_prologue_finish;
5be1f1
-		ctx->skip = block_r->size;
5be1f1
-		return 1;
5be1f1
-	}
5be1f1
-		
5be1f1
-	/* retain enough data in the stream buffer to contain initial boundary */
5be1f1
-	if (end_offset > BOUNDARY_END_MAX_LEN)
5be1f1
-		boundary_min_start = end_offset - BOUNDARY_END_MAX_LEN;
5be1f1
-	else
5be1f1
-		boundary_min_start = 0;
5be1f1
-
5be1f1
-	if (ctx->input->v_offset + block_r->size >= boundary_min_start) {
5be1f1
-		if (boundary_min_start <= ctx->input->v_offset)
5be1f1
-			return 0;
5be1f1
-		block_r->size = boundary_min_start - ctx->input->v_offset;
5be1f1
-	}
5be1f1
-	ctx->skip = block_r->size;
5be1f1
-	return 1;
5be1f1
-}
5be1f1
-
5be1f1
-static int preparsed_parse_epilogue_more(struct message_parser_ctx *ctx,
5be1f1
-					 struct message_block *block_r)
5be1f1
-{
5be1f1
-	uoff_t end_offset = ctx->part->physical_pos +
5be1f1
-		ctx->part->header_size.physical_size +
5be1f1
-		ctx->part->body_size.physical_size;
5be1f1
-	bool full;
5be1f1
-	int ret;
5be1f1
-
5be1f1
-	if ((ret = message_parser_read_more(ctx, block_r, &full)) <= 0)
5be1f1
-		return ret;
5be1f1
-
5be1f1
-	if (ctx->input->v_offset + block_r->size >= end_offset) {
5be1f1
-		block_r->size = end_offset - ctx->input->v_offset;
5be1f1
-		ctx->parse_next_block = preparsed_parse_body_finish;
5be1f1
-	}
5be1f1
-	ctx->skip = block_r->size;
5be1f1
-	return 1;
5be1f1
-}
5be1f1
-
5be1f1
-static int preparsed_parse_epilogue_boundary(struct message_parser_ctx *ctx,
5be1f1
-					     struct message_block *block_r)
5be1f1
-{
5be1f1
-	uoff_t end_offset = ctx->part->physical_pos +
5be1f1
-		ctx->part->header_size.physical_size +
5be1f1
-		ctx->part->body_size.physical_size;
5be1f1
-	const unsigned char *data, *cur;
5be1f1
-	size_t size;
5be1f1
-	bool full;
5be1f1
-	int ret;
5be1f1
-
5be1f1
-	if (end_offset - ctx->input->v_offset < 7) {
5be1f1
-		ctx->broken_reason = "Epilogue position is wrong";
5be1f1
-		return -1;
5be1f1
-	}
5be1f1
-
5be1f1
-	if ((ret = message_parser_read_more(ctx, block_r, &full)) <= 0)
5be1f1
-		return ret;
5be1f1
-
5be1f1
-	/* [\r]\n--boundary--[\r]\n */
5be1f1
-	if (block_r->size < 7) {
5be1f1
-		ctx->want_count = 7;
5be1f1
-		return 0;
5be1f1
-	}
5be1f1
-
5be1f1
-	data = block_r->data;
5be1f1
-	size = block_r->size;
5be1f1
-	cur = data;
5be1f1
-
5be1f1
-	if (*cur == '\r') cur++;
5be1f1
-
5be1f1
-	if (cur[0] != '\n' || cur[1] != '-' || data[2] != '-') {
5be1f1
-		ctx->broken_reason = "Epilogue boundary start not at expected position";
5be1f1
-		return -1;
5be1f1
-	}
5be1f1
-
5be1f1
-	/* find the end of the line */
5be1f1
-	cur += 3;
5be1f1
-	if ((cur = memchr(cur, '\n', size - (cur-data))) == NULL) {
5be1f1
-		if (end_offset < ctx->input->v_offset + size) {
5be1f1
-			ctx->broken_reason = "Epilogue boundary end not at expected position";
5be1f1
-			return -1;
5be1f1
-		} else if (ctx->input->v_offset + size < end_offset &&
5be1f1
-			   size < BOUNDARY_END_MAX_LEN &&
5be1f1
-			   !ctx->input->eof && !full) {
5be1f1
-			ctx->want_count = BOUNDARY_END_MAX_LEN;
5be1f1
-			return 0;
5be1f1
-		}
5be1f1
-	}
5be1f1
-
5be1f1
-	block_r->size = 0;
5be1f1
-	ctx->parse_next_block = preparsed_parse_epilogue_more;
5be1f1
-	ctx->skip = cur - data + 1;
5be1f1
-	return 0;
5be1f1
-}
5be1f1
-
5be1f1
-static int preparsed_parse_body_init(struct message_parser_ctx *ctx,
5be1f1
-				     struct message_block *block_r)
5be1f1
-{
5be1f1
-	uoff_t offset = ctx->part->physical_pos +
5be1f1
-		ctx->part->header_size.physical_size;
5be1f1
-
5be1f1
-	if (offset < ctx->input->v_offset) {
5be1f1
-		/* header was actually larger than the cached size suggested */
5be1f1
-		ctx->broken_reason = "Header larger than its cached size";
5be1f1
-		return -1;
5be1f1
-	}
5be1f1
-	i_stream_skip(ctx->input, offset - ctx->input->v_offset);
5be1f1
-
5be1f1
-	/* multipart messages may begin with --boundary--, which makes them
5be1f1
-	   not have any children. */
5be1f1
-	if ((ctx->part->flags & MESSAGE_PART_FLAG_MULTIPART) == 0 ||
5be1f1
-	    ctx->part->children == NULL)
5be1f1
-		ctx->parse_next_block = preparsed_parse_body_more;
5be1f1
-	else
5be1f1
-		ctx->parse_next_block = preparsed_parse_prologue_more;
5be1f1
-	return ctx->parse_next_block(ctx, block_r);
5be1f1
-}
5be1f1
-
5be1f1
-static int preparsed_parse_epilogue_init(struct message_parser_ctx *ctx,
5be1f1
-					 struct message_block *block_r)
5be1f1
-{
5be1f1
-	uoff_t offset = ctx->part->physical_pos +
5be1f1
-		ctx->part->header_size.physical_size +
5be1f1
-		ctx->part->body_size.physical_size;
5be1f1
-
5be1f1
-	ctx->part = ctx->part->parent;
5be1f1
-
5be1f1
-	if (offset < ctx->input->v_offset) {
5be1f1
-		/* last child was actually larger than the cached size
5be1f1
-		   suggested */
5be1f1
-		ctx->broken_reason = "Part larger than its cached size";
5be1f1
-		return -1;
5be1f1
-	}
5be1f1
-	i_stream_skip(ctx->input, offset - ctx->input->v_offset);
5be1f1
-
5be1f1
-	ctx->parse_next_block = preparsed_parse_epilogue_boundary;
5be1f1
-	return ctx->parse_next_block(ctx, block_r);
5be1f1
-}
5be1f1
-
5be1f1
-static int preparsed_parse_finish_header(struct message_parser_ctx *ctx,
5be1f1
-					 struct message_block *block_r)
5be1f1
-{
5be1f1
-	if (ctx->part->children != NULL) {
5be1f1
-		if ((ctx->part->flags & MESSAGE_PART_FLAG_MULTIPART) != 0 &&
5be1f1
-		    (ctx->flags & MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS) != 0)
5be1f1
-			ctx->parse_next_block = preparsed_parse_body_init;
5be1f1
-		else {
5be1f1
-			ctx->parse_next_block = preparsed_parse_next_header_init;
5be1f1
-			ctx->part = ctx->part->children;
5be1f1
-		}
5be1f1
-	} else if ((ctx->flags & MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK) == 0) {
5be1f1
-		ctx->parse_next_block = preparsed_parse_body_init;
5be1f1
-	} else {
5be1f1
-		preparsed_skip_to_next(ctx);
5be1f1
-	}
5be1f1
-	return ctx->parse_next_block(ctx, block_r);
5be1f1
-}
5be1f1
-
5be1f1
-static int preparsed_parse_next_header(struct message_parser_ctx *ctx,
5be1f1
-				       struct message_block *block_r)
5be1f1
-{
5be1f1
-	struct message_header_line *hdr;
5be1f1
-	int ret;
5be1f1
-
5be1f1
-	ret = message_parse_header_next(ctx->hdr_parser_ctx, &hdr);
5be1f1
-	if (ret == 0 || (ret < 0 && ctx->input->stream_errno != 0)) {
5be1f1
-		ctx->want_count = i_stream_get_data_size(ctx->input) + 1;
5be1f1
-		return ret;
5be1f1
-	}
5be1f1
-
5be1f1
-	if (hdr != NULL) {
5be1f1
-		block_r->hdr = hdr;
5be1f1
-		block_r->size = 0;
5be1f1
-		return 1;
5be1f1
-	}
5be1f1
-	message_parse_header_deinit(&ctx->hdr_parser_ctx);
5be1f1
-
5be1f1
-	ctx->parse_next_block = preparsed_parse_finish_header;
5be1f1
-
5be1f1
-	/* return empty block as end of headers */
5be1f1
-	block_r->hdr = NULL;
5be1f1
-	block_r->size = 0;
5be1f1
-
5be1f1
-	i_assert(ctx->skip == 0);
5be1f1
-	if (ctx->input->v_offset != ctx->part->physical_pos +
5be1f1
-	    ctx->part->header_size.physical_size) {
5be1f1
-		ctx->broken_reason = "Cached header size mismatch";
5be1f1
-		return -1;
5be1f1
-	}
5be1f1
-	return 1;
5be1f1
-}
5be1f1
-
5be1f1
-static int preparsed_parse_next_header_init(struct message_parser_ctx *ctx,
5be1f1
-					    struct message_block *block_r)
5be1f1
-{
5be1f1
-	struct istream *hdr_input;
5be1f1
-
5be1f1
-	i_assert(ctx->hdr_parser_ctx == NULL);
5be1f1
-
5be1f1
-	i_assert(ctx->part->physical_pos >= ctx->input->v_offset);
5be1f1
-	i_stream_skip(ctx->input, ctx->part->physical_pos -
5be1f1
-		      ctx->input->v_offset);
5be1f1
-
5be1f1
-	/* the header may become truncated by --boundaries. limit the header
5be1f1
-	   stream's size to what it's supposed to be to avoid duplicating (and
5be1f1
-	   keeping in sync!) all the same complicated logic as in
5be1f1
-	   parse_next_header(). */
5be1f1
-	hdr_input = i_stream_create_limit(ctx->input, ctx->part->header_size.physical_size);
5be1f1
-	ctx->hdr_parser_ctx =
5be1f1
-		message_parse_header_init(hdr_input, NULL, ctx->hdr_flags);
5be1f1
-	i_stream_unref(&hdr_input);
5be1f1
-
5be1f1
-	ctx->parse_next_block = preparsed_parse_next_header;
5be1f1
-	return preparsed_parse_next_header(ctx, block_r);
5be1f1
-}
5be1f1
-
5be1f1
-static struct message_parser_ctx *
5be1f1
+struct message_parser_ctx *
5be1f1
 message_parser_init_int(struct istream *input,
5be1f1
-			enum message_header_parser_flags hdr_flags,
5be1f1
-			enum message_parser_flags flags)
5be1f1
+			const struct message_parser_settings *set)
5be1f1
 {
5be1f1
 	struct message_parser_ctx *ctx;
5be1f1
-	pool_t pool;
5be1f1
 
5be1f1
-	pool = pool_alloconly_create("Message Parser", 1024);
5be1f1
-	ctx = p_new(pool, struct message_parser_ctx, 1);
5be1f1
-	ctx->parser_pool = pool;
5be1f1
-	ctx->hdr_flags = hdr_flags;
5be1f1
-	ctx->flags = flags;
5be1f1
+	ctx = i_new(struct message_parser_ctx, 1);
5be1f1
+	ctx->hdr_flags = set->hdr_flags;
5be1f1
+	ctx->flags = set->flags;
5be1f1
+	ctx->max_nested_mime_parts = set->max_nested_mime_parts != 0 ?
5be1f1
+		set->max_nested_mime_parts :
5be1f1
+		MESSAGE_PARSER_DEFAULT_MAX_NESTED_MIME_PARTS;
5be1f1
+	ctx->max_total_mime_parts = set->max_total_mime_parts != 0 ?
5be1f1
+		set->max_total_mime_parts :
5be1f1
+		MESSAGE_PARSER_DEFAULT_MAX_TOTAL_MIME_PARTS;
5be1f1
 	ctx->input = input;
5be1f1
 	i_stream_ref(input);
5be1f1
 	return ctx;
5be1f1
@@ -1050,31 +755,17 @@ message_parser_init_int(struct istream *
5be1f1
 
5be1f1
 struct message_parser_ctx *
5be1f1
 message_parser_init(pool_t part_pool, struct istream *input,
5be1f1
-		    enum message_header_parser_flags hdr_flags,
5be1f1
-		    enum message_parser_flags flags)
5be1f1
+		    const struct message_parser_settings *set)
5be1f1
 {
5be1f1
 	struct message_parser_ctx *ctx;
5be1f1
 
5be1f1
-	ctx = message_parser_init_int(input, hdr_flags, flags);
5be1f1
+	ctx = message_parser_init_int(input, set);
5be1f1
 	ctx->part_pool = part_pool;
5be1f1
 	ctx->parts = ctx->part = p_new(part_pool, struct message_part, 1);
5be1f1
+	ctx->next_part = &ctx->part->children;
5be1f1
 	ctx->parse_next_block = parse_next_header_init;
5be1f1
-	return ctx;
5be1f1
-}
5be1f1
-
5be1f1
-struct message_parser_ctx *
5be1f1
-message_parser_init_from_parts(struct message_part *parts,
5be1f1
-			       struct istream *input,
5be1f1
-			       enum message_header_parser_flags hdr_flags,
5be1f1
-			       enum message_parser_flags flags)
5be1f1
-{
5be1f1
-	struct message_parser_ctx *ctx;
5be1f1
-
5be1f1
-	i_assert(parts != NULL);
5be1f1
-
5be1f1
-	ctx = message_parser_init_int(input, hdr_flags, flags);
5be1f1
-	ctx->parts = ctx->part = parts;
5be1f1
-	ctx->parse_next_block = preparsed_parse_next_header_init;
5be1f1
+	ctx->total_parts_count = 1;
5be1f1
+	i_array_init(&ctx->next_part_stack, 4);
5be1f1
 	return ctx;
5be1f1
 }
5be1f1
 
5be1f1
@@ -1099,8 +790,15 @@ int message_parser_deinit_from_parts(str
5be1f1
 
5be1f1
 	if (ctx->hdr_parser_ctx != NULL)
5be1f1
 		message_parse_header_deinit(&ctx->hdr_parser_ctx);
5be1f1
+	boundary_remove_until(ctx, NULL);
5be1f1
+	/* caller might have stopped the parsing early */
5be1f1
+	i_assert(ctx->nested_parts_count == 0 ||
5be1f1
+		 i_stream_have_bytes_left(ctx->input));
5be1f1
+
5be1f1
 	i_stream_unref(&ctx->input);
5be1f1
-	pool_unref(&ctx->parser_pool);
5be1f1
+	array_free(&ctx->next_part_stack);
5be1f1
+	i_free(ctx->last_boundary);
5be1f1
+	i_free(ctx);
5be1f1
 	i_assert(ret < 0 || *parts_r != NULL);
5be1f1
 	return ret;
5be1f1
 }
5be1f1
@@ -1132,13 +830,8 @@ int message_parser_parse_next_block(stru
5be1f1
 		i_assert(ctx->input->eof || ctx->input->closed ||
5be1f1
 			 ctx->input->stream_errno != 0 ||
5be1f1
 			 ctx->broken_reason != NULL);
5be1f1
-		while (ctx->part->parent != NULL) {
5be1f1
-			message_size_add(&ctx->part->parent->body_size,
5be1f1
-					 &ctx->part->body_size);
5be1f1
-			message_size_add(&ctx->part->parent->body_size,
5be1f1
-					 &ctx->part->header_size);
5be1f1
-			ctx->part = ctx->part->parent;
5be1f1
-		}
5be1f1
+		while (ctx->part->parent != NULL)
5be1f1
+			message_part_finish(ctx);
5be1f1
 	}
5be1f1
 
5be1f1
 	if (block_r->size == 0) {
5be1f1
diff -up dovecot-2.2.36/src/lib-mail/message-parser-from-parts.c.CVE_2020_12100 dovecot-2.2.36/src/lib-mail/message-parser-from-parts.c
5be1f1
--- dovecot-2.2.36/src/lib-mail/message-parser-from-parts.c.CVE_2020_12100	2020-08-08 13:57:49.296662100 +0200
5be1f1
+++ dovecot-2.2.36/src/lib-mail/message-parser-from-parts.c	2020-08-08 13:57:49.296662100 +0200
5be1f1
@@ -0,0 +1,365 @@
5be1f1
+/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
5be1f1
+
5be1f1
+#include "lib.h"
5be1f1
+#include "istream.h"
5be1f1
+#include "message-parser-private.h"
5be1f1
+
5be1f1
+static int preparsed_parse_epilogue_init(struct message_parser_ctx *ctx,
5be1f1
+					 struct message_block *block_r);
5be1f1
+static int preparsed_parse_next_header_init(struct message_parser_ctx *ctx,
5be1f1
+					    struct message_block *block_r);
5be1f1
+
5be1f1
+static int preparsed_parse_eof(struct message_parser_ctx *ctx ATTR_UNUSED,
5be1f1
+			       struct message_block *block_r ATTR_UNUSED)
5be1f1
+{
5be1f1
+	return -1;
5be1f1
+}
5be1f1
+
5be1f1
+static void preparsed_skip_to_next(struct message_parser_ctx *ctx)
5be1f1
+{
5be1f1
+	ctx->parse_next_block = preparsed_parse_next_header_init;
5be1f1
+	while (ctx->part != NULL) {
5be1f1
+		if (ctx->part->next != NULL) {
5be1f1
+			ctx->part = ctx->part->next;
5be1f1
+			break;
5be1f1
+		}
5be1f1
+
5be1f1
+		/* parse epilogue of multipart parent if requested */
5be1f1
+		if (ctx->part->parent != NULL &&
5be1f1
+		    (ctx->part->parent->flags & MESSAGE_PART_FLAG_MULTIPART) != 0 &&
5be1f1
+		    (ctx->flags & MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS) != 0) {
5be1f1
+			/* check for presence of epilogue */
5be1f1
+			uoff_t part_end = ctx->part->physical_pos +
5be1f1
+				ctx->part->header_size.physical_size +
5be1f1
+				ctx->part->body_size.physical_size;
5be1f1
+			uoff_t parent_end = ctx->part->parent->physical_pos +
5be1f1
+				ctx->part->parent->header_size.physical_size +
5be1f1
+				ctx->part->parent->body_size.physical_size;
5be1f1
+
5be1f1
+			if (parent_end > part_end) {
5be1f1
+				ctx->parse_next_block = preparsed_parse_epilogue_init;
5be1f1
+				break;
5be1f1
+			}
5be1f1
+		}
5be1f1
+		ctx->part = ctx->part->parent;
5be1f1
+	}
5be1f1
+	if (ctx->part == NULL)
5be1f1
+		ctx->parse_next_block = preparsed_parse_eof;
5be1f1
+}
5be1f1
+
5be1f1
+static int preparsed_parse_body_finish(struct message_parser_ctx *ctx,
5be1f1
+				       struct message_block *block_r)
5be1f1
+{
5be1f1
+	i_stream_skip(ctx->input, ctx->skip);
5be1f1
+	ctx->skip = 0;
5be1f1
+
5be1f1
+	preparsed_skip_to_next(ctx);
5be1f1
+	return ctx->parse_next_block(ctx, block_r);
5be1f1
+}
5be1f1
+
5be1f1
+static int preparsed_parse_prologue_finish(struct message_parser_ctx *ctx,
5be1f1
+					   struct message_block *block_r)
5be1f1
+{
5be1f1
+	i_stream_skip(ctx->input, ctx->skip);
5be1f1
+	ctx->skip = 0;
5be1f1
+
5be1f1
+	ctx->parse_next_block = preparsed_parse_next_header_init;
5be1f1
+	ctx->part = ctx->part->children;
5be1f1
+	return ctx->parse_next_block(ctx, block_r);
5be1f1
+}
5be1f1
+
5be1f1
+static int preparsed_parse_body_more(struct message_parser_ctx *ctx,
5be1f1
+				     struct message_block *block_r)
5be1f1
+{
5be1f1
+	uoff_t end_offset = ctx->part->physical_pos +
5be1f1
+		ctx->part->header_size.physical_size +
5be1f1
+		ctx->part->body_size.physical_size;
5be1f1
+	bool full;
5be1f1
+	int ret;
5be1f1
+
5be1f1
+	if ((ret = message_parser_read_more(ctx, block_r, &full)) <= 0)
5be1f1
+		return ret;
5be1f1
+
5be1f1
+	if (ctx->input->v_offset + block_r->size >= end_offset) {
5be1f1
+		block_r->size = end_offset - ctx->input->v_offset;
5be1f1
+		ctx->parse_next_block = preparsed_parse_body_finish;
5be1f1
+	}
5be1f1
+	ctx->skip = block_r->size;
5be1f1
+	return 1;
5be1f1
+}
5be1f1
+
5be1f1
+static int preparsed_parse_prologue_more(struct message_parser_ctx *ctx,
5be1f1
+					 struct message_block *block_r)
5be1f1
+{
5be1f1
+	uoff_t boundary_min_start, end_offset;
5be1f1
+	const unsigned char *cur;
5be1f1
+	bool full;
5be1f1
+	int ret;
5be1f1
+
5be1f1
+	i_assert(ctx->part->children != NULL);
5be1f1
+	end_offset = ctx->part->children->physical_pos;
5be1f1
+
5be1f1
+	if ((ret = message_parser_read_more(ctx, block_r, &full)) <= 0)
5be1f1
+		return ret;
5be1f1
+
5be1f1
+	if (ctx->input->v_offset + block_r->size >= end_offset) {
5be1f1
+		/* we've got the full prologue: clip off the initial boundary */
5be1f1
+		block_r->size = end_offset - ctx->input->v_offset;
5be1f1
+		cur = block_r->data + block_r->size - 1;
5be1f1
+
5be1f1
+		/* [\r]\n--boundary[\r]\n */ 
5be1f1
+		if (block_r->size < 5 || *cur != '\n') {
5be1f1
+			ctx->broken_reason = "Prologue boundary end not at expected position";
5be1f1
+			return -1;
5be1f1
+		}
5be1f1
+		
5be1f1
+		cur--;
5be1f1
+		if (*cur == '\r') cur--;
5be1f1
+
5be1f1
+		/* find newline just before boundary */
5be1f1
+		for (; cur >= block_r->data; cur--) {
5be1f1
+			if (*cur == '\n') break;
5be1f1
+		}
5be1f1
+
5be1f1
+		if (cur[0] != '\n' || cur[1] != '-' || cur[2] != '-') {
5be1f1
+			ctx->broken_reason = "Prologue boundary beginning not at expected position";
5be1f1
+			return -1;
5be1f1
+		}
5be1f1
+
5be1f1
+		if (cur != block_r->data && cur[-1] == '\r') cur--;
5be1f1
+
5be1f1
+		/* clip boundary */
5be1f1
+		block_r->size = cur - block_r->data;			
5be1f1
+
5be1f1
+		ctx->parse_next_block = preparsed_parse_prologue_finish;
5be1f1
+		ctx->skip = block_r->size;
5be1f1
+		return 1;
5be1f1
+	}
5be1f1
+		
5be1f1
+	/* retain enough data in the stream buffer to contain initial boundary */
5be1f1
+	if (end_offset > BOUNDARY_END_MAX_LEN)
5be1f1
+		boundary_min_start = end_offset - BOUNDARY_END_MAX_LEN;
5be1f1
+	else
5be1f1
+		boundary_min_start = 0;
5be1f1
+
5be1f1
+	if (ctx->input->v_offset + block_r->size >= boundary_min_start) {
5be1f1
+		if (boundary_min_start <= ctx->input->v_offset)
5be1f1
+			return 0;
5be1f1
+		block_r->size = boundary_min_start - ctx->input->v_offset;
5be1f1
+	}
5be1f1
+	ctx->skip = block_r->size;
5be1f1
+	return 1;
5be1f1
+}
5be1f1
+
5be1f1
+static int preparsed_parse_epilogue_more(struct message_parser_ctx *ctx,
5be1f1
+					 struct message_block *block_r)
5be1f1
+{
5be1f1
+	uoff_t end_offset = ctx->part->physical_pos +
5be1f1
+		ctx->part->header_size.physical_size +
5be1f1
+		ctx->part->body_size.physical_size;
5be1f1
+	bool full;
5be1f1
+	int ret;
5be1f1
+
5be1f1
+	if ((ret = message_parser_read_more(ctx, block_r, &full)) <= 0)
5be1f1
+		return ret;
5be1f1
+
5be1f1
+	if (ctx->input->v_offset + block_r->size >= end_offset) {
5be1f1
+		block_r->size = end_offset - ctx->input->v_offset;
5be1f1
+		ctx->parse_next_block = preparsed_parse_body_finish;
5be1f1
+	}
5be1f1
+	ctx->skip = block_r->size;
5be1f1
+	return 1;
5be1f1
+}
5be1f1
+
5be1f1
+static int preparsed_parse_epilogue_boundary(struct message_parser_ctx *ctx,
5be1f1
+					     struct message_block *block_r)
5be1f1
+{
5be1f1
+	uoff_t end_offset = ctx->part->physical_pos +
5be1f1
+		ctx->part->header_size.physical_size +
5be1f1
+		ctx->part->body_size.physical_size;
5be1f1
+	const unsigned char *data, *cur;
5be1f1
+	size_t size;
5be1f1
+	bool full;
5be1f1
+	int ret;
5be1f1
+
5be1f1
+	if (end_offset - ctx->input->v_offset < 7) {
5be1f1
+		ctx->broken_reason = "Epilogue position is wrong";
5be1f1
+		return -1;
5be1f1
+	}
5be1f1
+
5be1f1
+	if ((ret = message_parser_read_more(ctx, block_r, &full)) <= 0)
5be1f1
+		return ret;
5be1f1
+
5be1f1
+	/* [\r]\n--boundary--[\r]\n */
5be1f1
+	if (block_r->size < 7) {
5be1f1
+		ctx->want_count = 7;
5be1f1
+		return 0;
5be1f1
+	}
5be1f1
+
5be1f1
+	data = block_r->data;
5be1f1
+	size = block_r->size;
5be1f1
+	cur = data;
5be1f1
+
5be1f1
+	if (*cur == '\r') cur++;
5be1f1
+
5be1f1
+	if (cur[0] != '\n' || cur[1] != '-' || data[2] != '-') {
5be1f1
+		ctx->broken_reason = "Epilogue boundary start not at expected position";
5be1f1
+		return -1;
5be1f1
+	}
5be1f1
+
5be1f1
+	/* find the end of the line */
5be1f1
+	cur += 3;
5be1f1
+	if ((cur = memchr(cur, '\n', size - (cur-data))) == NULL) {
5be1f1
+		if (end_offset < ctx->input->v_offset + size) {
5be1f1
+			ctx->broken_reason = "Epilogue boundary end not at expected position";
5be1f1
+			return -1;
5be1f1
+		} else if (ctx->input->v_offset + size < end_offset &&
5be1f1
+			   size < BOUNDARY_END_MAX_LEN &&
5be1f1
+			   !ctx->input->eof && !full) {
5be1f1
+			ctx->want_count = BOUNDARY_END_MAX_LEN;
5be1f1
+			return 0;
5be1f1
+		}
5be1f1
+	}
5be1f1
+
5be1f1
+	block_r->size = 0;
5be1f1
+	ctx->parse_next_block = preparsed_parse_epilogue_more;
5be1f1
+	ctx->skip = cur - data + 1;
5be1f1
+	return 0;
5be1f1
+}
5be1f1
+
5be1f1
+static int preparsed_parse_body_init(struct message_parser_ctx *ctx,
5be1f1
+				     struct message_block *block_r)
5be1f1
+{
5be1f1
+	uoff_t offset = ctx->part->physical_pos +
5be1f1
+		ctx->part->header_size.physical_size;
5be1f1
+
5be1f1
+	if (offset < ctx->input->v_offset) {
5be1f1
+		/* header was actually larger than the cached size suggested */
5be1f1
+		ctx->broken_reason = "Header larger than its cached size";
5be1f1
+		return -1;
5be1f1
+	}
5be1f1
+	i_stream_skip(ctx->input, offset - ctx->input->v_offset);
5be1f1
+
5be1f1
+	/* multipart messages may begin with --boundary--, which makes them
5be1f1
+	   not have any children. */
5be1f1
+	if ((ctx->part->flags & MESSAGE_PART_FLAG_MULTIPART) == 0 ||
5be1f1
+	    ctx->part->children == NULL)
5be1f1
+		ctx->parse_next_block = preparsed_parse_body_more;
5be1f1
+	else
5be1f1
+		ctx->parse_next_block = preparsed_parse_prologue_more;
5be1f1
+	return ctx->parse_next_block(ctx, block_r);
5be1f1
+}
5be1f1
+
5be1f1
+static int preparsed_parse_epilogue_init(struct message_parser_ctx *ctx,
5be1f1
+					 struct message_block *block_r)
5be1f1
+{
5be1f1
+	uoff_t offset = ctx->part->physical_pos +
5be1f1
+		ctx->part->header_size.physical_size +
5be1f1
+		ctx->part->body_size.physical_size;
5be1f1
+
5be1f1
+	ctx->part = ctx->part->parent;
5be1f1
+
5be1f1
+	if (offset < ctx->input->v_offset) {
5be1f1
+		/* last child was actually larger than the cached size
5be1f1
+		   suggested */
5be1f1
+		ctx->broken_reason = "Part larger than its cached size";
5be1f1
+		return -1;
5be1f1
+	}
5be1f1
+	i_stream_skip(ctx->input, offset - ctx->input->v_offset);
5be1f1
+
5be1f1
+	ctx->parse_next_block = preparsed_parse_epilogue_boundary;
5be1f1
+	return ctx->parse_next_block(ctx, block_r);
5be1f1
+}
5be1f1
+
5be1f1
+static int preparsed_parse_finish_header(struct message_parser_ctx *ctx,
5be1f1
+					 struct message_block *block_r)
5be1f1
+{
5be1f1
+	if (ctx->part->children != NULL) {
5be1f1
+		if ((ctx->part->flags & MESSAGE_PART_FLAG_MULTIPART) != 0 &&
5be1f1
+		    (ctx->flags & MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS) != 0)
5be1f1
+			ctx->parse_next_block = preparsed_parse_body_init;
5be1f1
+		else {
5be1f1
+			ctx->parse_next_block = preparsed_parse_next_header_init;
5be1f1
+			ctx->part = ctx->part->children;
5be1f1
+		}
5be1f1
+	} else if ((ctx->flags & MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK) == 0) {
5be1f1
+		ctx->parse_next_block = preparsed_parse_body_init;
5be1f1
+	} else {
5be1f1
+		preparsed_skip_to_next(ctx);
5be1f1
+	}
5be1f1
+	return ctx->parse_next_block(ctx, block_r);
5be1f1
+}
5be1f1
+
5be1f1
+static int preparsed_parse_next_header(struct message_parser_ctx *ctx,
5be1f1
+				       struct message_block *block_r)
5be1f1
+{
5be1f1
+	struct message_header_line *hdr;
5be1f1
+	int ret;
5be1f1
+
5be1f1
+	ret = message_parse_header_next(ctx->hdr_parser_ctx, &hdr);
5be1f1
+	if (ret == 0 || (ret < 0 && ctx->input->stream_errno != 0)) {
5be1f1
+		ctx->want_count = i_stream_get_data_size(ctx->input) + 1;
5be1f1
+		return ret;
5be1f1
+	}
5be1f1
+
5be1f1
+	if (hdr != NULL) {
5be1f1
+		block_r->hdr = hdr;
5be1f1
+		block_r->size = 0;
5be1f1
+		return 1;
5be1f1
+	}
5be1f1
+	message_parse_header_deinit(&ctx->hdr_parser_ctx);
5be1f1
+
5be1f1
+	ctx->parse_next_block = preparsed_parse_finish_header;
5be1f1
+
5be1f1
+	/* return empty block as end of headers */
5be1f1
+	block_r->hdr = NULL;
5be1f1
+	block_r->size = 0;
5be1f1
+
5be1f1
+	i_assert(ctx->skip == 0);
5be1f1
+	if (ctx->input->v_offset != ctx->part->physical_pos +
5be1f1
+	    ctx->part->header_size.physical_size) {
5be1f1
+		ctx->broken_reason = "Cached header size mismatch";
5be1f1
+		return -1;
5be1f1
+	}
5be1f1
+	return 1;
5be1f1
+}
5be1f1
+
5be1f1
+static int preparsed_parse_next_header_init(struct message_parser_ctx *ctx,
5be1f1
+					    struct message_block *block_r)
5be1f1
+{
5be1f1
+	struct istream *hdr_input;
5be1f1
+
5be1f1
+	i_assert(ctx->hdr_parser_ctx == NULL);
5be1f1
+
5be1f1
+	i_assert(ctx->part->physical_pos >= ctx->input->v_offset);
5be1f1
+	i_stream_skip(ctx->input, ctx->part->physical_pos -
5be1f1
+		      ctx->input->v_offset);
5be1f1
+
5be1f1
+	/* the header may become truncated by --boundaries. limit the header
5be1f1
+	   stream's size to what it's supposed to be to avoid duplicating (and
5be1f1
+	   keeping in sync!) all the same complicated logic as in
5be1f1
+	   parse_next_header(). */
5be1f1
+	hdr_input = i_stream_create_limit(ctx->input, ctx->part->header_size.physical_size);
5be1f1
+	ctx->hdr_parser_ctx =
5be1f1
+		message_parse_header_init(hdr_input, NULL, ctx->hdr_flags);
5be1f1
+	i_stream_unref(&hdr_input);
5be1f1
+
5be1f1
+	ctx->parse_next_block = preparsed_parse_next_header;
5be1f1
+	return preparsed_parse_next_header(ctx, block_r);
5be1f1
+}
5be1f1
+
5be1f1
+struct message_parser_ctx *
5be1f1
+message_parser_init_from_parts(struct message_part *parts,
5be1f1
+			       struct istream *input,
5be1f1
+			       const struct message_parser_settings *set)
5be1f1
+{
5be1f1
+	struct message_parser_ctx *ctx;
5be1f1
+
5be1f1
+	i_assert(parts != NULL);
5be1f1
+
5be1f1
+	ctx = message_parser_init_int(input, set);
5be1f1
+	ctx->preparsed = TRUE;
5be1f1
+	ctx->parts = ctx->part = parts;
5be1f1
+	ctx->parse_next_block = preparsed_parse_next_header_init;
5be1f1
+	return ctx;
5be1f1
+}
5be1f1
diff -up dovecot-2.2.36/src/lib-mail/message-parser.h.CVE_2020_12100 dovecot-2.2.36/src/lib-mail/message-parser.h
5be1f1
--- dovecot-2.2.36/src/lib-mail/message-parser.h.CVE_2020_12100	2018-04-30 15:52:05.000000000 +0200
5be1f1
+++ dovecot-2.2.36/src/lib-mail/message-parser.h	2020-08-08 14:07:36.351534219 +0200
5be1f1
@@ -17,6 +17,21 @@ enum message_parser_flags {
5be1f1
 	MESSAGE_PARSER_FLAG_INCLUDE_BOUNDARIES		= 0x08
5be1f1
 };
5be1f1
 
5be1f1
+#define MESSAGE_PARSER_DEFAULT_MAX_NESTED_MIME_PARTS 100
5be1f1
+#define MESSAGE_PARSER_DEFAULT_MAX_TOTAL_MIME_PARTS 10000
5be1f1
+
5be1f1
+struct message_parser_settings {
5be1f1
+	enum message_header_parser_flags hdr_flags;
5be1f1
+	enum message_parser_flags flags;
5be1f1
+
5be1f1
+	/* Maximum nested MIME parts.
5be1f1
+	   0 = MESSAGE_PARSER_DEFAULT_MAX_NESTED_MIME_PARTS. */
5be1f1
+	unsigned int max_nested_mime_parts;
5be1f1
+	/* Maximum MIME parts in total.
5be1f1
+	   0 = MESSAGE_PARSER_DEFAULT_MAX_TOTAL_MIME_PARTS. */
5be1f1
+	unsigned int max_total_mime_parts;
5be1f1
+};
5be1f1
+
5be1f1
 struct message_parser_ctx;
5be1f1
 
5be1f1
 struct message_block {
5be1f1
@@ -45,14 +60,12 @@ extern message_part_header_callback_t *n
5be1f1
    are allocated from. */
5be1f1
 struct message_parser_ctx *
5be1f1
 message_parser_init(pool_t part_pool, struct istream *input,
5be1f1
-		    enum message_header_parser_flags hdr_flags,
5be1f1
-		    enum message_parser_flags flags);
5be1f1
+		    const struct message_parser_settings *set);
5be1f1
 /* Use preparsed parts to speed up parsing. */
5be1f1
 struct message_parser_ctx *
5be1f1
 message_parser_init_from_parts(struct message_part *parts,
5be1f1
 			       struct istream *input,
5be1f1
-			       enum message_header_parser_flags hdr_flags,
5be1f1
-			       enum message_parser_flags flags);
5be1f1
+			       const struct message_parser_settings *set);
5be1f1
 /* Returns 0 if parts were returned, -1 we used preparsed parts and they
5be1f1
    didn't match the current message */
5be1f1
 int message_parser_deinit(struct message_parser_ctx **ctx,
5be1f1
diff -up dovecot-2.2.36/src/lib-mail/message-parser-private.h.CVE_2020_12100 dovecot-2.2.36/src/lib-mail/message-parser-private.h
5be1f1
--- dovecot-2.2.36/src/lib-mail/message-parser-private.h.CVE_2020_12100	2020-08-08 13:57:49.297662085 +0200
5be1f1
+++ dovecot-2.2.36/src/lib-mail/message-parser-private.h	2020-08-08 13:57:49.297662085 +0200
5be1f1
@@ -0,0 +1,62 @@
5be1f1
+#ifndef MESSAGE_PARSER_PRIVATE_H
5be1f1
+#define MESSAGE_PARSER_PRIVATE_H
5be1f1
+
5be1f1
+#include "message-parser.h"
5be1f1
+
5be1f1
+/* RFC-2046 requires boundaries are max. 70 chars + "--" prefix + "--" suffix.
5be1f1
+   We'll add a bit more just in case. */
5be1f1
+#define BOUNDARY_STRING_MAX_LEN (70 + 10)
5be1f1
+#define BOUNDARY_END_MAX_LEN (BOUNDARY_STRING_MAX_LEN + 2 + 2)
5be1f1
+
5be1f1
+struct message_boundary {
5be1f1
+	struct message_boundary *next;
5be1f1
+
5be1f1
+	struct message_part *part;
5be1f1
+	char *boundary;
5be1f1
+	size_t len;
5be1f1
+
5be1f1
+	bool epilogue_found:1;
5be1f1
+};
5be1f1
+
5be1f1
+struct message_parser_ctx {
5be1f1
+	pool_t part_pool;
5be1f1
+	struct istream *input;
5be1f1
+	struct message_part *parts, *part;
5be1f1
+	const char *broken_reason;
5be1f1
+	unsigned int nested_parts_count;
5be1f1
+	unsigned int total_parts_count;
5be1f1
+
5be1f1
+	enum message_header_parser_flags hdr_flags;
5be1f1
+	enum message_parser_flags flags;
5be1f1
+	unsigned int max_nested_mime_parts;
5be1f1
+	unsigned int max_total_mime_parts;
5be1f1
+
5be1f1
+	char *last_boundary;
5be1f1
+	struct message_boundary *boundaries;
5be1f1
+
5be1f1
+	struct message_part **next_part;
5be1f1
+	ARRAY(struct message_part **) next_part_stack;
5be1f1
+
5be1f1
+	size_t skip;
5be1f1
+	char last_chr;
5be1f1
+	unsigned int want_count;
5be1f1
+
5be1f1
+	struct message_header_parser_ctx *hdr_parser_ctx;
5be1f1
+	unsigned int prev_hdr_newline_size;
5be1f1
+
5be1f1
+	int (*parse_next_block)(struct message_parser_ctx *ctx,
5be1f1
+				struct message_block *block_r);
5be1f1
+
5be1f1
+	bool part_seen_content_type:1;
5be1f1
+	bool multipart:1;
5be1f1
+	bool preparsed:1;
5be1f1
+	bool eof:1;
5be1f1
+};
5be1f1
+
5be1f1
+struct message_parser_ctx *
5be1f1
+message_parser_init_int(struct istream *input,
5be1f1
+			const struct message_parser_settings *set);
5be1f1
+int message_parser_read_more(struct message_parser_ctx *ctx,
5be1f1
+			     struct message_block *block_r, bool *full_r);
5be1f1
+
5be1f1
+#endif
5be1f1
diff -up dovecot-2.2.36/src/lib-mail/message-search.c.CVE_2020_12100 dovecot-2.2.36/src/lib-mail/message-search.c
5be1f1
--- dovecot-2.2.36/src/lib-mail/message-search.c.CVE_2020_12100	2018-04-30 15:52:05.000000000 +0200
5be1f1
+++ dovecot-2.2.36/src/lib-mail/message-search.c	2020-08-08 13:57:49.297662085 +0200
5be1f1
@@ -196,8 +196,9 @@ message_search_msg_real(struct message_s
5be1f1
 			struct istream *input, struct message_part *parts,
5be1f1
 			const char **error_r)
5be1f1
 {
5be1f1
-	const enum message_header_parser_flags hdr_parser_flags =
5be1f1
-		MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE;
5be1f1
+	const struct message_parser_settings parser_set = {
5be1f1
+		.hdr_flags = MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE,
5be1f1
+	};
5be1f1
 	struct message_parser_ctx *parser_ctx;
5be1f1
 	struct message_block raw_block;
5be1f1
 	struct message_part *new_parts;
5be1f1
@@ -207,10 +208,10 @@ message_search_msg_real(struct message_s
5be1f1
 
5be1f1
 	if (parts != NULL) {
5be1f1
 		parser_ctx = message_parser_init_from_parts(parts,
5be1f1
-						input, hdr_parser_flags, 0);
5be1f1
+						input, &parser_set);
5be1f1
 	} else {
5be1f1
 		parser_ctx = message_parser_init(pool_datastack_create(),
5be1f1
-						 input, hdr_parser_flags, 0);
5be1f1
+						 input, &parser_set);
5be1f1
 	}
5be1f1
 
5be1f1
 	while ((ret = message_parser_parse_next_block(parser_ctx,
5be1f1
diff -up dovecot-2.2.36/src/lib-mail/message-snippet.c.CVE_2020_12100 dovecot-2.2.36/src/lib-mail/message-snippet.c
5be1f1
--- dovecot-2.2.36/src/lib-mail/message-snippet.c.CVE_2020_12100	2018-04-30 15:52:05.000000000 +0200
5be1f1
+++ dovecot-2.2.36/src/lib-mail/message-snippet.c	2020-08-08 13:57:49.297662085 +0200
5be1f1
@@ -95,6 +95,7 @@ int message_snippet_generate(struct istr
5be1f1
 			     unsigned int max_snippet_chars,
5be1f1
 			     string_t *snippet)
5be1f1
 {
5be1f1
+	const struct message_parser_settings parser_set = { .flags = 0 };
5be1f1
 	struct message_parser_ctx *parser;
5be1f1
 	struct message_part *parts;
5be1f1
 	struct message_decoder_context *decoder;
5be1f1
@@ -108,7 +109,7 @@ int message_snippet_generate(struct istr
5be1f1
 	ctx.snippet = snippet;
5be1f1
 	ctx.chars_left = max_snippet_chars;
5be1f1
 
5be1f1
-	parser = message_parser_init(pool_datastack_create(), input, 0, 0);
5be1f1
+	parser = message_parser_init(pool_datastack_create(), input, &parser_set);
5be1f1
 	decoder = message_decoder_init(NULL, 0);
5be1f1
 	while ((ret = message_parser_parse_next_block(parser, &raw_block)) > 0) {
5be1f1
 		if (!message_decoder_decode_next_block(decoder, &raw_block, &block))
5be1f1
diff -up dovecot-2.2.36/src/lib-mail/test-message-decoder.c.CVE_2020_12100 dovecot-2.2.36/src/lib-mail/test-message-decoder.c
5be1f1
--- dovecot-2.2.36/src/lib-mail/test-message-decoder.c.CVE_2020_12100	2020-08-08 13:57:49.294662127 +0200
5be1f1
+++ dovecot-2.2.36/src/lib-mail/test-message-decoder.c	2020-08-08 13:57:49.297662085 +0200
5be1f1
@@ -105,6 +105,7 @@ static void test_message_decoder_multipa
5be1f1
 		"\n"
5be1f1
 		"?garbage\n"
5be1f1
 		"--foo--\n";
5be1f1
+	const struct message_parser_settings parser_set = { .flags = 0, };
5be1f1
 	struct message_parser_ctx *parser;
5be1f1
 	struct message_decoder_context *decoder;
5be1f1
 	struct message_part *parts;
5be1f1
@@ -116,7 +117,8 @@ static void test_message_decoder_multipa
5be1f1
 	test_begin("message decoder multipart");
5be1f1
 
5be1f1
 	istream = test_istream_create(test_message_input);
5be1f1
-	parser = message_parser_init(pool_datastack_create(), istream, 0, 0);
5be1f1
+	parser = message_parser_init(pool_datastack_create(), istream,
5be1f1
+				     &parser_set);
5be1f1
 	decoder = message_decoder_init(NULL, 0);
5be1f1
 
5be1f1
 	test_istream_set_allow_eof(istream, FALSE);
5be1f1
diff -up dovecot-2.2.36/src/lib-mail/test-message-parser.c.CVE_2020_12100 dovecot-2.2.36/src/lib-mail/test-message-parser.c
5be1f1
--- dovecot-2.2.36/src/lib-mail/test-message-parser.c.CVE_2020_12100	2018-04-30 15:52:05.000000000 +0200
5be1f1
+++ dovecot-2.2.36/src/lib-mail/test-message-parser.c	2020-08-08 14:17:25.070385831 +0200
5be1f1
@@ -39,6 +39,8 @@ static const char test_msg[] =
5be1f1
 "\n";
5be1f1
 #define TEST_MSG_LEN (sizeof(test_msg)-1)
5be1f1
 
5be1f1
+static const struct message_parser_settings set_empty = { .flags = 0 };
5be1f1
+
5be1f1
 static bool msg_parts_cmp(struct message_part *p1, struct message_part *p2)
5be1f1
 {
5be1f1
 	while (p1 != NULL || p2 != NULL) {
5be1f1
@@ -59,6 +61,7 @@ static bool msg_parts_cmp(struct message
5be1f1
 		    p1->body_size.physical_size != p2->body_size.physical_size ||
5be1f1
 		    p1->body_size.virtual_size != p2->body_size.virtual_size ||
5be1f1
 		    p1->body_size.lines != p2->body_size.lines ||
5be1f1
+		    p1->children_count != p2->children_count ||
5be1f1
 		    p1->flags != p2->flags)
5be1f1
 			return FALSE;
5be1f1
 
5be1f1
@@ -70,6 +73,9 @@ static bool msg_parts_cmp(struct message
5be1f1
 
5be1f1
 static void test_parsed_parts(struct istream *input, struct message_part *parts)
5be1f1
 {
5be1f1
+	const struct message_parser_settings parser_set = {
5be1f1
+		.flags = MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK,
5be1f1
+	};
5be1f1
 	struct message_parser_ctx *parser;
5be1f1
 	struct message_block block;
5be1f1
 	struct message_part *parts2;
5be1f1
@@ -81,8 +87,7 @@ static void test_parsed_parts(struct ist
5be1f1
 	if (i_stream_get_size(input, TRUE, &input_size) < 0)
5be1f1
 		i_unreached();
5be1f1
 
5be1f1
-	parser = message_parser_init_from_parts(parts, input, 0,
5be1f1
-					MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK);
5be1f1
+	parser = message_parser_init_from_parts(parts, input, &parser_set);
5be1f1
 	for (i = 1; i <= input_size*2+1; i++) {
5be1f1
 		test_istream_set_size(input, i/2);
5be1f1
 		if (i > TEST_MSG_LEN*2)
5be1f1
@@ -111,9 +116,11 @@ static void test_message_parser_small_bl
5be1f1
 	output = t_str_new(128);
5be1f1
 
5be1f1
 	/* full parsing */
5be1f1
-	parser = message_parser_init(pool, input, 0,
5be1f1
-		MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS |
5be1f1
-		MESSAGE_PARSER_FLAG_INCLUDE_BOUNDARIES);
5be1f1
+	const struct message_parser_settings full_parser_set = {
5be1f1
+		.flags = MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS |
5be1f1
+			MESSAGE_PARSER_FLAG_INCLUDE_BOUNDARIES,
5be1f1
+	};
5be1f1
+	parser = message_parser_init(pool, input, &full_parser_set);
5be1f1
 	while ((ret = message_parser_parse_next_block(parser, &block)) > 0) {
5be1f1
 		if (block.hdr != NULL)
5be1f1
 			message_header_line_write(output, block.hdr);
5be1f1
@@ -129,7 +136,7 @@ static void test_message_parser_small_bl
5be1f1
 	i_stream_seek(input, 0);
5be1f1
 	test_istream_set_allow_eof(input, FALSE);
5be1f1
 
5be1f1
-	parser = message_parser_init(pool, input, 0, 0);
5be1f1
+	parser = message_parser_init(pool, input, &set_empty);
5be1f1
 	for (i = 1; i <= TEST_MSG_LEN*2+1; i++) {
5be1f1
 		test_istream_set_size(input, i/2);
5be1f1
 		if (i > TEST_MSG_LEN*2)
5be1f1
@@ -147,8 +154,11 @@ static void test_message_parser_small_bl
5be1f1
 	test_istream_set_allow_eof(input, FALSE);
5be1f1
 
5be1f1
 	end_of_headers_idx = (strstr(test_msg, "\n-----") - test_msg);
5be1f1
-	parser = message_parser_init_from_parts(parts, input, 0,
5be1f1
-					MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK);
5be1f1
+	const struct message_parser_settings preparsed_parser_set = {
5be1f1
+		.flags = MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK,
5be1f1
+	};
5be1f1
+	parser = message_parser_init_from_parts(parts, input,
5be1f1
+						&preparsed_parser_set);
5be1f1
 	for (i = 1; i <= TEST_MSG_LEN*2+1; i++) {
5be1f1
 		test_istream_set_size(input, i/2);
5be1f1
 		if (i > TEST_MSG_LEN*2)
5be1f1
@@ -166,6 +176,36 @@ static void test_message_parser_small_bl
5be1f1
 	test_end();
5be1f1
 }
5be1f1
 
5be1f1
+static void test_message_parser_stop_early(void)
5be1f1
+{
5be1f1
+	struct message_parser_ctx *parser;
5be1f1
+	struct istream *input;
5be1f1
+	struct message_part *parts;
5be1f1
+	struct message_block block;
5be1f1
+	unsigned int i;
5be1f1
+	pool_t pool;
5be1f1
+	int ret;
5be1f1
+
5be1f1
+	test_begin("message parser in stop early");
5be1f1
+	pool = pool_alloconly_create("message parser", 10240);
5be1f1
+	input = test_istream_create(test_msg);
5be1f1
+
5be1f1
+	test_istream_set_allow_eof(input, FALSE);
5be1f1
+	for (i = 1; i <= TEST_MSG_LEN+1; i++) {
5be1f1
+		i_stream_seek(input, 0);
5be1f1
+		test_istream_set_size(input, i);
5be1f1
+		parser = message_parser_init(pool, input, &set_empty);
5be1f1
+		while ((ret = message_parser_parse_next_block(parser,
5be1f1
+							      &block)) > 0) ;
5be1f1
+		test_assert(ret == 0);
5be1f1
+		message_parser_deinit(&parser, &parts;;
5be1f1
+	}
5be1f1
+
5be1f1
+	i_stream_unref(&input);
5be1f1
+	pool_unref(&pool);
5be1f1
+	test_end();
5be1f1
+}
5be1f1
+
5be1f1
 static void test_message_parser_truncated_mime_headers(void)
5be1f1
 {
5be1f1
 static const char input_msg[] =
5be1f1
@@ -190,12 +230,13 @@ static const char input_msg[] =
5be1f1
 	pool = pool_alloconly_create("message parser", 10240);
5be1f1
 	input = test_istream_create(input_msg);
5be1f1
 
5be1f1
-	parser = message_parser_init(pool, input, 0, 0);
5be1f1
+	parser = message_parser_init(pool, input, &set_empty);
5be1f1
 	while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ;
5be1f1
 	test_assert(ret < 0);
5be1f1
 	test_assert(message_parser_deinit(&parser, &parts) == 0);
5be1f1
 
5be1f1
 	test_assert((parts->flags & MESSAGE_PART_FLAG_MULTIPART) != 0);
5be1f1
+	test_assert(parts->children_count == 4);
5be1f1
 	test_assert(parts->header_size.lines == 2);
5be1f1
 	test_assert(parts->header_size.physical_size == 48);
5be1f1
 	test_assert(parts->header_size.virtual_size == 48+2);
5be1f1
@@ -219,6 +260,7 @@ static const char input_msg[] =
5be1f1
 	test_assert(parts->children->next->next->next->header_size.virtual_size == 23);
5be1f1
 	test_assert(parts->children->next->next->next->header_size.lines == 0);
5be1f1
 	for (part = parts->children; part != NULL; part = part->next) {
5be1f1
+		test_assert(part->children_count == 0);
5be1f1
 		test_assert(part->body_size.physical_size == 0);
5be1f1
 		test_assert(part->body_size.virtual_size == 0);
5be1f1
 	}
5be1f1
@@ -253,12 +295,13 @@ static const char input_msg[] =
5be1f1
 	pool = pool_alloconly_create("message parser", 10240);
5be1f1
 	input = test_istream_create(input_msg);
5be1f1
 
5be1f1
-	parser = message_parser_init(pool, input, 0, 0);
5be1f1
+	parser = message_parser_init(pool, input, &set_empty);
5be1f1
 	while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ;
5be1f1
 	test_assert(ret < 0);
5be1f1
 	test_assert(message_parser_deinit(&parser, &parts) == 0);
5be1f1
 
5be1f1
 	test_assert(parts->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
5be1f1
+	test_assert(parts->children_count == 2);
5be1f1
 	test_assert(parts->header_size.lines == 2);
5be1f1
 	test_assert(parts->header_size.physical_size == 46);
5be1f1
 	test_assert(parts->header_size.virtual_size == 46+2);
5be1f1
@@ -266,6 +309,7 @@ static const char input_msg[] =
5be1f1
 	test_assert(parts->body_size.physical_size == 86);
5be1f1
 	test_assert(parts->body_size.virtual_size == 86+8);
5be1f1
 
5be1f1
+	test_assert(parts->children->children_count == 0);
5be1f1
 	test_assert(parts->children->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
5be1f1
 	test_assert(parts->children->physical_pos == 51);
5be1f1
 	test_assert(parts->children->header_size.lines == 1);
5be1f1
@@ -275,6 +319,7 @@ static const char input_msg[] =
5be1f1
 	test_assert(parts->children->body_size.physical_size == 0);
5be1f1
 	test_assert(parts->children->children == NULL);
5be1f1
 
5be1f1
+	test_assert(parts->children->next->children_count == 0);
5be1f1
 	test_assert(parts->children->next->flags == (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_IS_MIME));
5be1f1
 	test_assert(parts->children->next->physical_pos == 101);
5be1f1
 	test_assert(parts->children->next->header_size.lines == 2);
5be1f1
@@ -306,11 +351,12 @@ static const char input_msg[] =
5be1f1
 	pool = pool_alloconly_create("message parser", 10240);
5be1f1
 	input = test_istream_create(input_msg);
5be1f1
 
5be1f1
-	parser = message_parser_init(pool, input, 0, 0);
5be1f1
+	parser = message_parser_init(pool, input, &set_empty);
5be1f1
 	while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ;
5be1f1
 	test_assert(ret < 0);
5be1f1
 	test_assert(message_parser_deinit(&parser, &parts) == 0);
5be1f1
 
5be1f1
+	test_assert(parts->children_count == 0);
5be1f1
 	test_assert(parts->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
5be1f1
 	test_assert(parts->header_size.lines == 1);
5be1f1
 	test_assert(parts->header_size.physical_size == 45);
5be1f1
@@ -343,11 +389,12 @@ static const char input_msg[] =
5be1f1
 	pool = pool_alloconly_create("message parser", 10240);
5be1f1
 	input = test_istream_create(input_msg);
5be1f1
 
5be1f1
-	parser = message_parser_init(pool, input, 0, 0);
5be1f1
+	parser = message_parser_init(pool, input, &set_empty);
5be1f1
 	while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ;
5be1f1
 	test_assert(ret < 0);
5be1f1
 	test_assert(message_parser_deinit(&parser, &parts) == 0);
5be1f1
 
5be1f1
+	test_assert(parts->children_count == 0);
5be1f1
 	test_assert(parts->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
5be1f1
 	test_assert(parts->header_size.lines == 2);
5be1f1
 	test_assert(parts->header_size.physical_size == 46);
5be1f1
@@ -387,11 +434,12 @@ static const char input_msg[] =
5be1f1
 	pool = pool_alloconly_create("message parser", 10240);
5be1f1
 	input = test_istream_create(input_msg);
5be1f1
 
5be1f1
-	parser = message_parser_init(pool, input, 0, 0);
5be1f1
+	parser = message_parser_init(pool, input, &set_empty);
5be1f1
 	while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ;
5be1f1
 	test_assert(ret < 0);
5be1f1
 	test_assert(message_parser_deinit(&parser, &parts) == 0);
5be1f1
 
5be1f1
+	test_assert(parts->children_count == 2);
5be1f1
 	test_assert(parts->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
5be1f1
 	test_assert(parts->header_size.lines == 2);
5be1f1
 	test_assert(parts->header_size.physical_size == 45);
5be1f1
@@ -399,6 +447,7 @@ static const char input_msg[] =
5be1f1
 	test_assert(parts->body_size.lines == 7);
5be1f1
 	test_assert(parts->body_size.physical_size == 84);
5be1f1
 	test_assert(parts->body_size.virtual_size == 84+7);
5be1f1
+	test_assert(parts->children->children_count == 1);
5be1f1
 	test_assert(parts->children->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
5be1f1
 	test_assert(parts->children->physical_pos == 49);
5be1f1
 	test_assert(parts->children->header_size.lines == 2);
5be1f1
@@ -407,6 +456,7 @@ static const char input_msg[] =
5be1f1
 	test_assert(parts->children->body_size.lines == 4);
5be1f1
 	test_assert(parts->children->body_size.physical_size == 35);
5be1f1
 	test_assert(parts->children->body_size.virtual_size == 35+4);
5be1f1
+	test_assert(parts->children->children->children_count == 0);
5be1f1
 	test_assert(parts->children->children->flags == (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_IS_MIME));
5be1f1
 	test_assert(parts->children->children->physical_pos == 98);
5be1f1
 	test_assert(parts->children->children->header_size.lines == 2);
5be1f1
@@ -445,11 +495,12 @@ static const char input_msg[] =
5be1f1
 	pool = pool_alloconly_create("message parser", 10240);
5be1f1
 	input = test_istream_create(input_msg);
5be1f1
 
5be1f1
-	parser = message_parser_init(pool, input, 0, 0);
5be1f1
+	parser = message_parser_init(pool, input, &set_empty);
5be1f1
 	while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ;
5be1f1
 	test_assert(ret < 0);
5be1f1
 	test_assert(message_parser_deinit(&parser, &parts) == 0);
5be1f1
 
5be1f1
+	test_assert(parts->children_count == 2);
5be1f1
 	test_assert(parts->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
5be1f1
 	test_assert(parts->header_size.lines == 2);
5be1f1
 	test_assert(parts->header_size.physical_size == 45);
5be1f1
@@ -457,6 +508,7 @@ static const char input_msg[] =
5be1f1
 	test_assert(parts->body_size.lines == 7);
5be1f1
 	test_assert(parts->body_size.physical_size == 86);
5be1f1
 	test_assert(parts->body_size.virtual_size == 86+7);
5be1f1
+	test_assert(parts->children->children_count == 1);
5be1f1
 	test_assert(parts->children->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
5be1f1
 	test_assert(parts->children->physical_pos == 50);
5be1f1
 	test_assert(parts->children->header_size.lines == 2);
5be1f1
@@ -465,6 +517,7 @@ static const char input_msg[] =
5be1f1
 	test_assert(parts->children->body_size.lines == 4);
5be1f1
 	test_assert(parts->children->body_size.physical_size == 36);
5be1f1
 	test_assert(parts->children->body_size.virtual_size == 36+4);
5be1f1
+	test_assert(parts->children->children->children_count == 0);
5be1f1
 	test_assert(parts->children->children->flags == (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_IS_MIME));
5be1f1
 	test_assert(parts->children->children->physical_pos == 100);
5be1f1
 	test_assert(parts->children->children->header_size.lines == 2);
5be1f1
@@ -480,6 +533,51 @@ static const char input_msg[] =
5be1f1
 	test_end();
5be1f1
 }
5be1f1
 
5be1f1
+static void test_message_parser_trailing_dashes(void)
5be1f1
+{
5be1f1
+static const char input_msg[] =
5be1f1
+"Content-Type: multipart/mixed; boundary=\"a--\"\n"
5be1f1
+"\n"
5be1f1
+"--a--\n"
5be1f1
+"Content-Type: multipart/mixed; boundary=\"a----\"\n"
5be1f1
+"\n"
5be1f1
+"--a----\n"
5be1f1
+"Content-Type: text/plain\n"
5be1f1
+"\n"
5be1f1
+"body\n"
5be1f1
+"--a------\n"
5be1f1
+"Content-Type: text/html\n"
5be1f1
+"\n"
5be1f1
+"body2\n"
5be1f1
+"--a----";
5be1f1
+	struct message_parser_ctx *parser;
5be1f1
+	struct istream *input;
5be1f1
+	struct message_part *parts;
5be1f1
+	struct message_block block;
5be1f1
+	pool_t pool;
5be1f1
+	int ret;
5be1f1
+
5be1f1
+	test_begin("message parser trailing dashes");
5be1f1
+	pool = pool_alloconly_create("message parser", 10240);
5be1f1
+	input = test_istream_create(input_msg);
5be1f1
+
5be1f1
+	parser = message_parser_init(pool, input, &set_empty);
5be1f1
+	while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ;
5be1f1
+	test_assert(ret < 0);
5be1f1
+	message_parser_deinit(&parser, &parts;;
5be1f1
+
5be1f1
+	test_assert(parts->children_count == 2);
5be1f1
+	test_assert(parts->children->next == NULL);
5be1f1
+	test_assert(parts->children->children_count == 1);
5be1f1
+	test_assert(parts->children->children->next == NULL);
5be1f1
+	test_assert(parts->children->children->children_count == 0);
5be1f1
+
5be1f1
+	test_parsed_parts(input, parts);
5be1f1
+	i_stream_unref(&input);
5be1f1
+	pool_unref(&pool);
5be1f1
+	test_end();
5be1f1
+}
5be1f1
+
5be1f1
 static void test_message_parser_continuing_mime_boundary(void)
5be1f1
 {
5be1f1
 static const char input_msg[] =
5be1f1
@@ -503,11 +601,12 @@ static const char input_msg[] =
5be1f1
 	pool = pool_alloconly_create("message parser", 10240);
5be1f1
 	input = test_istream_create(input_msg);
5be1f1
 
5be1f1
-	parser = message_parser_init(pool, input, 0, 0);
5be1f1
+	parser = message_parser_init(pool, input, &set_empty);
5be1f1
 	while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ;
5be1f1
 	test_assert(ret < 0);
5be1f1
 	test_assert(message_parser_deinit(&parser, &parts) == 0);
5be1f1
 
5be1f1
+	test_assert(parts->children_count == 2);
5be1f1
 	test_assert(parts->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
5be1f1
 	test_assert(parts->header_size.lines == 2);
5be1f1
 	test_assert(parts->header_size.physical_size == 45);
5be1f1
@@ -515,6 +614,7 @@ static const char input_msg[] =
5be1f1
 	test_assert(parts->body_size.lines == 7);
5be1f1
 	test_assert(parts->body_size.physical_size == 86);
5be1f1
 	test_assert(parts->body_size.virtual_size == 86+7);
5be1f1
+	test_assert(parts->children->children_count == 1);
5be1f1
 	test_assert(parts->children->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
5be1f1
 	test_assert(parts->children->physical_pos == 49);
5be1f1
 	test_assert(parts->children->header_size.lines == 2);
5be1f1
@@ -523,6 +623,7 @@ static const char input_msg[] =
5be1f1
 	test_assert(parts->children->body_size.lines == 4);
5be1f1
 	test_assert(parts->children->body_size.physical_size == 36);
5be1f1
 	test_assert(parts->children->body_size.virtual_size == 36+4);
5be1f1
+	test_assert(parts->children->children->children_count == 0);
5be1f1
 	test_assert(parts->children->children->flags == (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_IS_MIME));
5be1f1
 	test_assert(parts->children->children->physical_pos == 100);
5be1f1
 	test_assert(parts->children->children->header_size.lines == 2);
5be1f1
@@ -562,12 +663,13 @@ static const char input_msg[] =
5be1f1
 	pool = pool_alloconly_create("message parser", 10240);
5be1f1
 	input = test_istream_create(input_msg);
5be1f1
 
5be1f1
-	parser = message_parser_init(pool, input, 0, 0);
5be1f1
+	parser = message_parser_init(pool, input, &set_empty);
5be1f1
 	while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ;
5be1f1
 	test_assert(ret < 0);
5be1f1
 	message_parser_deinit(&parser, &parts;;
5be1f1
 
5be1f1
 	part = parts;
5be1f1
+	test_assert(part->children_count == 3);
5be1f1
 	test_assert(part->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
5be1f1
 	test_assert(part->header_size.lines == 2);
5be1f1
 	test_assert(part->header_size.physical_size == 45);
5be1f1
@@ -577,6 +679,7 @@ static const char input_msg[] =
5be1f1
 	test_assert(part->body_size.virtual_size == 112+9);
5be1f1
 
5be1f1
 	part = parts->children;
5be1f1
+	test_assert(part->children_count == 0);
5be1f1
 	test_assert(part->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
5be1f1
 	test_assert(part->physical_pos == 49);
5be1f1
 	test_assert(part->header_size.lines == 1);
5be1f1
@@ -590,6 +693,7 @@ static const char input_msg[] =
5be1f1
 	   we could make it, but it would complicate the message-parser even
5be1f1
 	   more. */
5be1f1
 	part = parts->children->next;
5be1f1
+	test_assert(part->children_count == 0);
5be1f1
 	test_assert(part->flags == (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_IS_MIME));
5be1f1
 	test_assert(part->physical_pos == 117);
5be1f1
 	test_assert(part->header_size.lines == 1);
5be1f1
@@ -600,6 +704,7 @@ static const char input_msg[] =
5be1f1
 	test_assert(part->children == NULL);
5be1f1
 
5be1f1
 	part = parts->children->next->next;
5be1f1
+	test_assert(part->children_count == 0);
5be1f1
 	test_assert(part->flags == (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_IS_MIME));
5be1f1
 	test_assert(part->header_size.lines == 0);
5be1f1
 	test_assert(part->header_size.physical_size == 0);
5be1f1
@@ -614,6 +719,80 @@ static const char input_msg[] =
5be1f1
 	test_end();
5be1f1
 }
5be1f1
 
5be1f1
+static void test_message_parser_continuing_mime_boundary_reverse(void)
5be1f1
+{
5be1f1
+static const char input_msg[] =
5be1f1
+"Content-Type: multipart/mixed; boundary=\"ab\"\n"
5be1f1
+"\n"
5be1f1
+"--ab\n"
5be1f1
+"Content-Type: multipart/mixed; boundary=\"a\"\n"
5be1f1
+"\n"
5be1f1
+"--a\n"
5be1f1
+"Content-Type: text/plain\n"
5be1f1
+"\n"
5be1f1
+"body\n"
5be1f1
+"--ab\n"
5be1f1
+"Content-Type: text/html\n"
5be1f1
+"\n"
5be1f1
+"body2\n";
5be1f1
+	struct message_parser_ctx *parser;
5be1f1
+	struct istream *input;
5be1f1
+	struct message_part *parts;
5be1f1
+	struct message_block block;
5be1f1
+	pool_t pool;
5be1f1
+	int ret;
5be1f1
+
5be1f1
+	test_begin("message parser continuing mime boundary reverse");
5be1f1
+	pool = pool_alloconly_create("message parser", 10240);
5be1f1
+	input = test_istream_create(input_msg);
5be1f1
+
5be1f1
+	parser = message_parser_init(pool, input, &set_empty);
5be1f1
+	while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ;
5be1f1
+	test_assert(ret < 0);
5be1f1
+	message_parser_deinit(&parser, &parts;;
5be1f1
+
5be1f1
+	test_assert(parts->children_count == 3);
5be1f1
+	test_assert(parts->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
5be1f1
+	test_assert(parts->header_size.lines == 2);
5be1f1
+	test_assert(parts->header_size.physical_size == 46);
5be1f1
+	test_assert(parts->header_size.virtual_size == 46+2);
5be1f1
+	test_assert(parts->body_size.lines == 11);
5be1f1
+	test_assert(parts->body_size.physical_size == 121);
5be1f1
+	test_assert(parts->body_size.virtual_size == 121+11);
5be1f1
+	test_assert(parts->children->children_count == 1);
5be1f1
+	test_assert(parts->children->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
5be1f1
+	test_assert(parts->children->physical_pos == 51);
5be1f1
+	test_assert(parts->children->header_size.lines == 2);
5be1f1
+	test_assert(parts->children->header_size.physical_size == 45);
5be1f1
+	test_assert(parts->children->header_size.virtual_size == 45+2);
5be1f1
+	test_assert(parts->children->body_size.lines == 3);
5be1f1
+	test_assert(parts->children->body_size.physical_size == 34);
5be1f1
+	test_assert(parts->children->body_size.virtual_size == 34+3);
5be1f1
+	test_assert(parts->children->children->children_count == 0);
5be1f1
+	test_assert(parts->children->children->flags == (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_IS_MIME));
5be1f1
+	test_assert(parts->children->children->physical_pos == 100);
5be1f1
+	test_assert(parts->children->children->header_size.lines == 2);
5be1f1
+	test_assert(parts->children->children->header_size.physical_size == 26);
5be1f1
+	test_assert(parts->children->children->header_size.virtual_size == 26+2);
5be1f1
+	test_assert(parts->children->children->body_size.lines == 0);
5be1f1
+	test_assert(parts->children->children->body_size.physical_size == 4);
5be1f1
+	test_assert(parts->children->children->body_size.virtual_size == 4);
5be1f1
+	test_assert(parts->children->next->children_count == 0);
5be1f1
+	test_assert(parts->children->next->flags == (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_IS_MIME));
5be1f1
+	test_assert(parts->children->next->physical_pos == 136);
5be1f1
+	test_assert(parts->children->next->header_size.lines == 2);
5be1f1
+	test_assert(parts->children->next->header_size.physical_size == 25);
5be1f1
+	test_assert(parts->children->next->header_size.virtual_size == 25+2);
5be1f1
+	test_assert(parts->children->next->body_size.lines == 1);
5be1f1
+	test_assert(parts->children->next->body_size.physical_size == 6);
5be1f1
+	test_assert(parts->children->next->body_size.virtual_size == 6+1);
5be1f1
+
5be1f1
+	test_parsed_parts(input, parts);
5be1f1
+	i_stream_unref(&input);
5be1f1
+	pool_unref(&pool);
5be1f1
+	test_end();
5be1f1
+}
5be1f1
+
5be1f1
 static void test_message_parser_no_eoh(void)
5be1f1
 {
5be1f1
 	static const char input_msg[] = "a:b\n";
5be1f1
@@ -627,7 +806,7 @@ static void test_message_parser_no_eoh(v
5be1f1
 	pool = pool_alloconly_create("message parser", 10240);
5be1f1
 	input = test_istream_create(input_msg);
5be1f1
 
5be1f1
-	parser = message_parser_init(pool, input, 0, 0);
5be1f1
+	parser = message_parser_init(pool, input, &set_empty);
5be1f1
 	test_assert(message_parser_parse_next_block(parser, &block) > 0 &&
5be1f1
 		    block.hdr != NULL && strcmp(block.hdr->name, "a") == 0 &&
5be1f1
 		    block.hdr->value_len == 1 && block.hdr->value[0] == 'b');
5be1f1
@@ -642,19 +821,335 @@ static void test_message_parser_no_eoh(v
5be1f1
 	test_end();
5be1f1
 }
5be1f1
 
5be1f1
+static void test_message_parser_long_mime_boundary(void)
5be1f1
+{
5be1f1
+	/* Close the boundaries in wrong reverse order. But because all
5be1f1
+	   boundaries are actually truncated to the same size (..890) it
5be1f1
+	   works the same as if all of them were duplicate boundaries. */
5be1f1
+static const char input_msg[] =
5be1f1
+"Content-Type: multipart/mixed; boundary=\"1234567890123456789012345678901234567890123456789012345678901234567890123456789012\"\n"
5be1f1
+"\n"
5be1f1
+"--1234567890123456789012345678901234567890123456789012345678901234567890123456789012\n"
5be1f1
+"Content-Type: multipart/mixed; boundary=\"123456789012345678901234567890123456789012345678901234567890123456789012345678901\"\n"
5be1f1
+"\n"
5be1f1
+"--123456789012345678901234567890123456789012345678901234567890123456789012345678901\n"
5be1f1
+"Content-Type: multipart/mixed; boundary=\"12345678901234567890123456789012345678901234567890123456789012345678901234567890\"\n"
5be1f1
+"\n"
5be1f1
+"--12345678901234567890123456789012345678901234567890123456789012345678901234567890\n"
5be1f1
+"Content-Type: text/plain\n"
5be1f1
+"\n"
5be1f1
+"1\n"
5be1f1
+"--1234567890123456789012345678901234567890123456789012345678901234567890123456789012\n"
5be1f1
+"Content-Type: text/plain\n"
5be1f1
+"\n"
5be1f1
+"22\n"
5be1f1
+"--123456789012345678901234567890123456789012345678901234567890123456789012345678901\n"
5be1f1
+"Content-Type: text/plain\n"
5be1f1
+"\n"
5be1f1
+"333\n"
5be1f1
+"--12345678901234567890123456789012345678901234567890123456789012345678901234567890\n"
5be1f1
+"Content-Type: text/plain\n"
5be1f1
+"\n"
5be1f1
+"4444\n";
5be1f1
+	struct message_parser_ctx *parser;
5be1f1
+	struct istream *input;
5be1f1
+	struct message_part *parts, *part;
5be1f1
+	struct message_block block;
5be1f1
+	pool_t pool;
5be1f1
+	int ret;
5be1f1
+
5be1f1
+	test_begin("message parser long mime boundary");
5be1f1
+	pool = pool_alloconly_create("message parser", 10240);
5be1f1
+	input = test_istream_create(input_msg);
5be1f1
+
5be1f1
+	parser = message_parser_init(pool, input, &set_empty);
5be1f1
+	while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ;
5be1f1
+	test_assert(ret < 0);
5be1f1
+	message_parser_deinit(&parser, &parts;;
5be1f1
+
5be1f1
+	part = parts;
5be1f1
+	test_assert(part->children_count == 6);
5be1f1
+	test_assert(part->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
5be1f1
+	test_assert(part->header_size.lines == 2);
5be1f1
+	test_assert(part->header_size.physical_size == 126);
5be1f1
+	test_assert(part->header_size.virtual_size == 126+2);
5be1f1
+	test_assert(part->body_size.lines == 22);
5be1f1
+	test_assert(part->body_size.physical_size == 871);
5be1f1
+	test_assert(part->body_size.virtual_size == 871+22);
5be1f1
+
5be1f1
+	part = parts->children;
5be1f1
+	test_assert(part->children_count == 5);
5be1f1
+	test_assert(part->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
5be1f1
+	test_assert(part->header_size.lines == 2);
5be1f1
+	test_assert(part->header_size.physical_size == 125);
5be1f1
+	test_assert(part->header_size.virtual_size == 125+2);
5be1f1
+	test_assert(part->body_size.lines == 19);
5be1f1
+	test_assert(part->body_size.physical_size == 661);
5be1f1
+	test_assert(part->body_size.virtual_size == 661+19);
5be1f1
+
5be1f1
+	part = parts->children->children;
5be1f1
+	test_assert(part->children_count == 4);
5be1f1
+	test_assert(part->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
5be1f1
+	test_assert(part->header_size.lines == 2);
5be1f1
+	test_assert(part->header_size.physical_size == 124);
5be1f1
+	test_assert(part->header_size.virtual_size == 124+2);
5be1f1
+	test_assert(part->body_size.lines == 16);
5be1f1
+	test_assert(part->body_size.physical_size == 453);
5be1f1
+	test_assert(part->body_size.virtual_size == 453+16);
5be1f1
+
5be1f1
+	part = parts->children->children->children;
5be1f1
+	for (unsigned int i = 1; i <= 3; i++, part = part->next) {
5be1f1
+		test_assert(part->children_count == 0);
5be1f1
+		test_assert(part->flags == (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_IS_MIME));
5be1f1
+		test_assert(part->header_size.lines == 2);
5be1f1
+		test_assert(part->header_size.physical_size == 26);
5be1f1
+		test_assert(part->header_size.virtual_size == 26+2);
5be1f1
+		test_assert(part->body_size.lines == 0);
5be1f1
+		test_assert(part->body_size.physical_size == i);
5be1f1
+		test_assert(part->body_size.virtual_size == i);
5be1f1
+	}
5be1f1
+
5be1f1
+	test_parsed_parts(input, parts);
5be1f1
+	i_stream_unref(&input);
5be1f1
+	pool_unref(&pool);
5be1f1
+	test_end();
5be1f1
+}
5be1f1
+
5be1f1
+static void test_message_parser_mime_part_nested_limit(void)
5be1f1
+{
5be1f1
+static const char input_msg[] =
5be1f1
+"Content-Type: multipart/mixed; boundary=\"1\"\n"
5be1f1
+"\n"
5be1f1
+"--1\n"
5be1f1
+"Content-Type: multipart/mixed; boundary=\"2\"\n"
5be1f1
+"\n"
5be1f1
+"--2\n"
5be1f1
+"Content-Type: text/plain\n"
5be1f1
+"\n"
5be1f1
+"1\n"
5be1f1
+"--2\n"
5be1f1
+"Content-Type: text/plain\n"
5be1f1
+"\n"
5be1f1
+"22\n"
5be1f1
+"--1\n"
5be1f1
+"Content-Type: text/plain\n"
5be1f1
+"\n"
5be1f1
+"333\n";
5be1f1
+	const struct message_parser_settings parser_set = {
5be1f1
+		.max_nested_mime_parts = 2,
5be1f1
+	};
5be1f1
+	struct message_parser_ctx *parser;
5be1f1
+	struct istream *input;
5be1f1
+	struct message_part *parts, *part;
5be1f1
+	struct message_block block;
5be1f1
+	pool_t pool;
5be1f1
+	int ret;
5be1f1
+
5be1f1
+	test_begin("message parser mime part nested limit");
5be1f1
+	pool = pool_alloconly_create("message parser", 10240);
5be1f1
+	input = test_istream_create(input_msg);
5be1f1
+
5be1f1
+	parser = message_parser_init(pool, input, &parser_set);
5be1f1
+	while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ;
5be1f1
+	test_assert(ret < 0);
5be1f1
+	message_parser_deinit(&parser, &parts;;
5be1f1
+
5be1f1
+	part = parts;
5be1f1
+	test_assert(part->children_count == 2);
5be1f1
+	test_assert(part->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
5be1f1
+	test_assert(part->header_size.lines == 2);
5be1f1
+	test_assert(part->header_size.physical_size == 45);
5be1f1
+	test_assert(part->header_size.virtual_size == 45+2);
5be1f1
+	test_assert(part->body_size.lines == 15);
5be1f1
+	test_assert(part->body_size.physical_size == 148);
5be1f1
+	test_assert(part->body_size.virtual_size == 148+15);
5be1f1
+
5be1f1
+	part = parts->children;
5be1f1
+	test_assert(part->children_count == 0);
5be1f1
+	test_assert(part->flags == MESSAGE_PART_FLAG_IS_MIME);
5be1f1
+	test_assert(part->header_size.lines == 2);
5be1f1
+	test_assert(part->header_size.physical_size == 45);
5be1f1
+	test_assert(part->header_size.virtual_size == 45+2);
5be1f1
+	test_assert(part->body_size.lines == 7);
5be1f1
+	test_assert(part->body_size.physical_size == 64);
5be1f1
+	test_assert(part->body_size.virtual_size == 64+7);
5be1f1
+
5be1f1
+	part = parts->children->next;
5be1f1
+	test_assert(part->children_count == 0);
5be1f1
+	test_assert(part->flags == (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_IS_MIME));
5be1f1
+	test_assert(part->header_size.lines == 2);
5be1f1
+	test_assert(part->header_size.physical_size == 26);
5be1f1
+	test_assert(part->header_size.virtual_size == 26+2);
5be1f1
+	test_assert(part->body_size.lines == 1);
5be1f1
+	test_assert(part->body_size.physical_size == 4);
5be1f1
+	test_assert(part->body_size.virtual_size == 4+1);
5be1f1
+
5be1f1
+	test_parsed_parts(input, parts);
5be1f1
+	i_stream_unref(&input);
5be1f1
+	pool_unref(&pool);
5be1f1
+	test_end();
5be1f1
+}
5be1f1
+
5be1f1
+static void test_message_parser_mime_part_nested_limit_rfc822(void)
5be1f1
+{
5be1f1
+static const char input_msg[] =
5be1f1
+"Content-Type: message/rfc822\n"
5be1f1
+"\n"
5be1f1
+"Content-Type: message/rfc822\n"
5be1f1
+"\n"
5be1f1
+"Content-Type: text/plain\n"
5be1f1
+"\n"
5be1f1
+"1\n";
5be1f1
+	const struct message_parser_settings parser_set = {
5be1f1
+		.max_nested_mime_parts = 2,
5be1f1
+	};
5be1f1
+	struct message_parser_ctx *parser;
5be1f1
+	struct istream *input;
5be1f1
+	struct message_part *parts, *part;
5be1f1
+	struct message_block block;
5be1f1
+	pool_t pool;
5be1f1
+	int ret;
5be1f1
+
5be1f1
+	test_begin("message parser mime part nested limit rfc822");
5be1f1
+	pool = pool_alloconly_create("message parser", 10240);
5be1f1
+	input = test_istream_create(input_msg);
5be1f1
+
5be1f1
+	parser = message_parser_init(pool, input, &parser_set);
5be1f1
+	while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ;
5be1f1
+	test_assert(ret < 0);
5be1f1
+	message_parser_deinit(&parser, &parts;;
5be1f1
+
5be1f1
+	part = parts;
5be1f1
+	test_assert(part->children_count == 1);
5be1f1
+	test_assert(part->flags == (MESSAGE_PART_FLAG_MESSAGE_RFC822 | MESSAGE_PART_FLAG_IS_MIME));
5be1f1
+	test_assert(part->header_size.lines == 2);
5be1f1
+	test_assert(part->header_size.physical_size == 30);
5be1f1
+	test_assert(part->header_size.virtual_size == 30+2);
5be1f1
+	test_assert(part->body_size.lines == 5);
5be1f1
+	test_assert(part->body_size.physical_size == 58);
5be1f1
+	test_assert(part->body_size.virtual_size == 58+5);
5be1f1
+
5be1f1
+	part = parts->children;
5be1f1
+	test_assert(part->children_count == 0);
5be1f1
+	test_assert(part->flags == MESSAGE_PART_FLAG_IS_MIME);
5be1f1
+	test_assert(part->header_size.lines == 2);
5be1f1
+	test_assert(part->header_size.physical_size == 30);
5be1f1
+	test_assert(part->header_size.virtual_size == 30+2);
5be1f1
+	test_assert(part->body_size.lines == 3);
5be1f1
+	test_assert(part->body_size.physical_size == 28);
5be1f1
+	test_assert(part->body_size.virtual_size == 28+3);
5be1f1
+
5be1f1
+	test_parsed_parts(input, parts);
5be1f1
+	i_stream_unref(&input);
5be1f1
+	pool_unref(&pool);
5be1f1
+	test_end();
5be1f1
+}
5be1f1
+
5be1f1
+static void test_message_parser_mime_part_limit(void)
5be1f1
+{
5be1f1
+static const char input_msg[] =
5be1f1
+"Content-Type: multipart/mixed; boundary=\"1\"\n"
5be1f1
+"\n"
5be1f1
+"--1\n"
5be1f1
+"Content-Type: multipart/mixed; boundary=\"2\"\n"
5be1f1
+"\n"
5be1f1
+"--2\n"
5be1f1
+"Content-Type: text/plain\n"
5be1f1
+"\n"
5be1f1
+"1\n"
5be1f1
+"--2\n"
5be1f1
+"Content-Type: text/plain\n"
5be1f1
+"\n"
5be1f1
+"22\n"
5be1f1
+"--1\n"
5be1f1
+"Content-Type: text/plain\n"
5be1f1
+"\n"
5be1f1
+"333\n";
5be1f1
+	const struct message_parser_settings parser_set = {
5be1f1
+		.max_total_mime_parts = 4,
5be1f1
+	};
5be1f1
+	struct message_parser_ctx *parser;
5be1f1
+	struct istream *input;
5be1f1
+	struct message_part *parts, *part;
5be1f1
+	struct message_block block;
5be1f1
+	pool_t pool;
5be1f1
+	int ret;
5be1f1
+
5be1f1
+	test_begin("message parser mime part limit");
5be1f1
+	pool = pool_alloconly_create("message parser", 10240);
5be1f1
+	input = test_istream_create(input_msg);
5be1f1
+
5be1f1
+	parser = message_parser_init(pool, input, &parser_set);
5be1f1
+	while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ;
5be1f1
+	test_assert(ret < 0);
5be1f1
+	message_parser_deinit(&parser, &parts;;
5be1f1
+
5be1f1
+	part = parts;
5be1f1
+	test_assert(part->children_count == 3);
5be1f1
+	test_assert(part->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
5be1f1
+	test_assert(part->header_size.lines == 2);
5be1f1
+	test_assert(part->header_size.physical_size == 45);
5be1f1
+	test_assert(part->header_size.virtual_size == 45+2);
5be1f1
+	test_assert(part->body_size.lines == 15);
5be1f1
+	test_assert(part->body_size.physical_size == 148);
5be1f1
+	test_assert(part->body_size.virtual_size == 148+15);
5be1f1
+
5be1f1
+	part = parts->children;
5be1f1
+	test_assert(part->children_count == 2);
5be1f1
+	test_assert(part->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
5be1f1
+	test_assert(part->header_size.lines == 2);
5be1f1
+	test_assert(part->header_size.physical_size == 45);
5be1f1
+	test_assert(part->header_size.virtual_size == 45+2);
5be1f1
+	test_assert(part->body_size.lines == 12);
5be1f1
+	test_assert(part->body_size.physical_size == 99);
5be1f1
+	test_assert(part->body_size.virtual_size == 99+12);
5be1f1
+
5be1f1
+	part = parts->children->children;
5be1f1
+	test_assert(part->children_count == 0);
5be1f1
+	test_assert(part->flags == (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_IS_MIME));
5be1f1
+	test_assert(part->header_size.lines == 2);
5be1f1
+	test_assert(part->header_size.physical_size == 26);
5be1f1
+	test_assert(part->header_size.virtual_size == 26+2);
5be1f1
+	test_assert(part->body_size.lines == 0);
5be1f1
+	test_assert(part->body_size.physical_size == 1);
5be1f1
+	test_assert(part->body_size.virtual_size == 1);
5be1f1
+
5be1f1
+	part = parts->children->children->next;
5be1f1
+	test_assert(part->children_count == 0);
5be1f1
+	test_assert(part->flags == (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_IS_MIME));
5be1f1
+	test_assert(part->header_size.lines == 2);
5be1f1
+	test_assert(part->header_size.physical_size == 26);
5be1f1
+	test_assert(part->header_size.virtual_size == 26+2);
5be1f1
+	test_assert(part->body_size.lines == 5);
5be1f1
+	test_assert(part->body_size.physical_size == 37);
5be1f1
+	test_assert(part->body_size.virtual_size == 37+5);
5be1f1
+
5be1f1
+	test_parsed_parts(input, parts);
5be1f1
+	i_stream_unref(&input);
5be1f1
+	pool_unref(&pool);
5be1f1
+	test_end();
5be1f1
+}
5be1f1
+
5be1f1
 int main(void)
5be1f1
 {
5be1f1
 	static void (*test_functions[])(void) = {
5be1f1
 		test_message_parser_small_blocks,
5be1f1
+		test_message_parser_stop_early,
5be1f1
 		test_message_parser_truncated_mime_headers,
5be1f1
 		test_message_parser_truncated_mime_headers2,
5be1f1
 		test_message_parser_truncated_mime_headers3,
5be1f1
 		test_message_parser_empty_multipart,
5be1f1
 		test_message_parser_duplicate_mime_boundary,
5be1f1
 		test_message_parser_garbage_suffix_mime_boundary,
5be1f1
+		test_message_parser_trailing_dashes,
5be1f1
 		test_message_parser_continuing_mime_boundary,
5be1f1
 		test_message_parser_continuing_truncated_mime_boundary,
5be1f1
+		test_message_parser_continuing_mime_boundary_reverse,
5be1f1
+		test_message_parser_long_mime_boundary,
5be1f1
 		test_message_parser_no_eoh,
5be1f1
+		test_message_parser_mime_part_nested_limit,
5be1f1
+		test_message_parser_mime_part_nested_limit_rfc822,
5be1f1
+		test_message_parser_mime_part_limit,
5be1f1
 		NULL
5be1f1
 	};
5be1f1
 	return test_run(test_functions);
5be1f1
diff -up dovecot-2.2.36/src/lib-mail/test-message-part.c.CVE_2020_12100 dovecot-2.2.36/src/lib-mail/test-message-part.c
5be1f1
--- dovecot-2.2.36/src/lib-mail/test-message-part.c.CVE_2020_12100	2018-04-30 15:52:05.000000000 +0200
5be1f1
+++ dovecot-2.2.36/src/lib-mail/test-message-part.c	2020-08-08 13:57:49.297662085 +0200
5be1f1
@@ -65,6 +65,7 @@ static const char test_msg[] =
5be1f1
 
5be1f1
 static void test_message_part_idx(void)
5be1f1
 {
5be1f1
+	const struct message_parser_settings set = { .flags = 0 };
5be1f1
 	struct message_parser_ctx *parser;
5be1f1
 	struct istream *input;
5be1f1
 	struct message_part *parts, *part, *prev_part;
5be1f1
@@ -77,7 +78,7 @@ static void test_message_part_idx(void)
5be1f1
 	pool = pool_alloconly_create("message parser", 10240);
5be1f1
 	input = i_stream_create_from_data(test_msg, TEST_MSG_LEN);
5be1f1
 
5be1f1
-	parser = message_parser_init(pool, input, 0, 0);
5be1f1
+	parser = message_parser_init(pool, input, &set);
5be1f1
 	while ((ret = message_parser_parse_next_block(parser, &block)) > 0) {
5be1f1
 		part_idx = message_part_to_idx(block.part);
5be1f1
 		test_assert(part_idx >= prev_idx);
5be1f1
diff -up dovecot-2.2.36/src/lib-storage/index/index-mail-headers.c.CVE_2020_12100 dovecot-2.2.36/src/lib-storage/index/index-mail-headers.c
5be1f1
--- dovecot-2.2.36/src/lib-storage/index/index-mail-headers.c.CVE_2020_12100	2018-04-30 15:52:05.000000000 +0200
5be1f1
+++ dovecot-2.2.36/src/lib-storage/index/index-mail-headers.c	2020-08-08 13:57:49.298662071 +0200
5be1f1
@@ -16,11 +16,11 @@
5be1f1
 #include "index-storage.h"
5be1f1
 #include "index-mail.h"
5be1f1
 
5be1f1
-static const enum message_header_parser_flags hdr_parser_flags =
5be1f1
-	MESSAGE_HEADER_PARSER_FLAG_SKIP_INITIAL_LWSP |
5be1f1
-	MESSAGE_HEADER_PARSER_FLAG_DROP_CR;
5be1f1
-static const enum message_parser_flags msg_parser_flags =
5be1f1
-	MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK;
5be1f1
+static const struct message_parser_settings msg_parser_set = {
5be1f1
+	.hdr_flags = MESSAGE_HEADER_PARSER_FLAG_SKIP_INITIAL_LWSP |
5be1f1
+		MESSAGE_HEADER_PARSER_FLAG_DROP_CR,
5be1f1
+	.flags = MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK,
5be1f1
+};
5be1f1
 
5be1f1
 static int header_line_cmp(const struct index_mail_line *l1,
5be1f1
 			   const struct index_mail_line *l2)
5be1f1
@@ -399,7 +399,7 @@ index_mail_cache_parse_init(struct mail
5be1f1
 	mail->data.parser_input = input;
5be1f1
 	mail->data.parser_ctx =
5be1f1
 		message_parser_init(mail->mail.data_pool, input,
5be1f1
-				    hdr_parser_flags, msg_parser_flags);
5be1f1
+				    &msg_parser_set);
5be1f1
 	i_stream_unref(&input);
5be1f1
 	return input2;
5be1f1
 }
5be1f1
@@ -422,14 +422,12 @@ static void index_mail_init_parser(struc
5be1f1
 		data->parser_input = data->stream;
5be1f1
 		data->parser_ctx = message_parser_init(mail->mail.data_pool,
5be1f1
 						       data->stream,
5be1f1
-						       hdr_parser_flags,
5be1f1
-						       msg_parser_flags);
5be1f1
+						       &msg_parser_set);
5be1f1
 	} else {
5be1f1
 		data->parser_ctx =
5be1f1
 			message_parser_init_from_parts(data->parts,
5be1f1
 						       data->stream,
5be1f1
-						       hdr_parser_flags,
5be1f1
-						       msg_parser_flags);
5be1f1
+						       &msg_parser_set);
5be1f1
 	}
5be1f1
 }
5be1f1
 
5be1f1
@@ -462,7 +460,7 @@ int index_mail_parse_headers(struct inde
5be1f1
 		i_assert(!data->save_bodystructure_body ||
5be1f1
 			 data->parser_ctx != NULL);
5be1f1
 		message_parse_header(data->stream, &data->hdr_size,
5be1f1
-				     hdr_parser_flags,
5be1f1
+				     msg_parser_set.hdr_flags,
5be1f1
 				     index_mail_parse_header_cb, mail);
5be1f1
 	}
5be1f1
 	if (index_mail_stream_check_failure(mail) < 0)
5be1f1
@@ -517,7 +515,7 @@ int index_mail_headers_get_envelope(stru
5be1f1
 	if (mail->data.envelope == NULL && stream != NULL) {
5be1f1
 		/* we got the headers from cache - parse them to get the
5be1f1
 		   envelope */
5be1f1
-		message_parse_header(stream, NULL, hdr_parser_flags,
5be1f1
+		message_parse_header(stream, NULL, msg_parser_set.hdr_flags,
5be1f1
 				     imap_envelope_parse_callback, mail);
5be1f1
 		if (stream->stream_errno != 0) {
5be1f1
 			index_mail_stream_log_failure_for(mail, stream);
5be1f1
diff -up dovecot-2.2.36/src/plugins/fts/fts-build-mail.c.CVE_2020_12100 dovecot-2.2.36/src/plugins/fts/fts-build-mail.c
5be1f1
--- dovecot-2.2.36/src/plugins/fts/fts-build-mail.c.CVE_2020_12100	2018-04-30 15:52:05.000000000 +0200
5be1f1
+++ dovecot-2.2.36/src/plugins/fts/fts-build-mail.c	2020-08-08 14:18:31.289469311 +0200
5be1f1
@@ -458,6 +458,9 @@ static int
5be1f1
 fts_build_mail_real(struct fts_backend_update_context *update_ctx,
5be1f1
 		    struct mail *mail)
5be1f1
 {
5be1f1
+	const struct message_parser_settings parser_set = {
5be1f1
+		.hdr_flags = MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE,
5be1f1
+	};
5be1f1
 	struct fts_mail_build_context ctx;
5be1f1
 	struct istream *input;
5be1f1
 	struct message_parser_ctx *parser;
5be1f1
@@ -485,9 +488,7 @@ fts_build_mail_real(struct fts_backend_u
5be1f1
 		ctx.pending_input = buffer_create_dynamic(default_pool, 128);
5be1f1
 
5be1f1
 	prev_part = NULL;
5be1f1
-	parser = message_parser_init(pool_datastack_create(), input,
5be1f1
-				     MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE,
5be1f1
-				     0);
5be1f1
+	parser = message_parser_init(pool_datastack_create(), input, &parser_set);
5be1f1
 
5be1f1
 	decoder = message_decoder_init(update_ctx->normalizer, 0);
5be1f1
 	for (;;) {