diff -up dovecot-2.3.8/src/doveadm/doveadm-mail-fetch.c.CVE_2020_12100 dovecot-2.3.8/src/doveadm/doveadm-mail-fetch.c
--- dovecot-2.3.8/src/doveadm/doveadm-mail-fetch.c.CVE_2020_12100 2019-10-08 10:46:18.000000000 +0200
+++ dovecot-2.3.8/src/doveadm/doveadm-mail-fetch.c 2020-08-07 12:29:39.975827486 +0200
@@ -265,6 +265,9 @@ static int fetch_text(struct fetch_cmd_c
static int fetch_text_utf8(struct fetch_cmd_context *ctx)
{
+ const struct message_parser_settings parser_set = {
+ .hdr_flags = MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE,
+ };
struct istream *input;
struct message_parser_ctx *parser;
struct message_decoder_context *decoder;
@@ -275,9 +278,7 @@ static int fetch_text_utf8(struct fetch_
if (mail_get_stream(ctx->mail, NULL, NULL, &input) < 0)
return -1;
- parser = message_parser_init(pool_datastack_create(), input,
- MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE,
- 0);
+ parser = message_parser_init(pool_datastack_create(), input, &parser_set);
decoder = message_decoder_init(NULL, 0);
while ((ret = message_parser_parse_next_block(parser, &raw_block)) > 0) {
diff -up dovecot-2.3.8/src/lib-imap/test-imap-bodystructure.c.CVE_2020_12100 dovecot-2.3.8/src/lib-imap/test-imap-bodystructure.c
--- dovecot-2.3.8/src/lib-imap/test-imap-bodystructure.c.CVE_2020_12100 2019-10-08 10:46:18.000000000 +0200
+++ dovecot-2.3.8/src/lib-imap/test-imap-bodystructure.c 2020-08-07 12:29:39.975827486 +0200
@@ -381,6 +381,11 @@ static const unsigned int normalize_test
static struct message_part *
msg_parse(pool_t pool, const char *message, bool parse_bodystructure)
{
+ const struct message_parser_settings parser_set = {
+ .hdr_flags = MESSAGE_HEADER_PARSER_FLAG_SKIP_INITIAL_LWSP |
+ MESSAGE_HEADER_PARSER_FLAG_DROP_CR,
+ .flags = MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK,
+ };
struct message_parser_ctx *parser;
struct istream *input;
struct message_block block;
@@ -388,10 +393,7 @@ msg_parse(pool_t pool, const char *messa
int ret;
input = i_stream_create_from_data(message, strlen(message));
- parser = message_parser_init(pool, input,
- MESSAGE_HEADER_PARSER_FLAG_SKIP_INITIAL_LWSP |
- MESSAGE_HEADER_PARSER_FLAG_DROP_CR,
- MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK);
+ parser = message_parser_init(pool, input, &parser_set);
while ((ret = message_parser_parse_next_block(parser, &block)) > 0) {
if (parse_bodystructure) {
message_part_data_parse_from_header(pool, block.part,
diff -up dovecot-2.3.8/src/lib-imap/test-imap-envelope.c.CVE_2020_12100 dovecot-2.3.8/src/lib-imap/test-imap-envelope.c
--- dovecot-2.3.8/src/lib-imap/test-imap-envelope.c.CVE_2020_12100 2019-10-08 10:46:18.000000000 +0200
+++ dovecot-2.3.8/src/lib-imap/test-imap-envelope.c 2020-08-07 12:29:39.975827486 +0200
@@ -118,6 +118,11 @@ static const unsigned int parse_tests_co
static struct message_part_envelope *
msg_parse(pool_t pool, const char *message)
{
+ const struct message_parser_settings parser_set = {
+ .hdr_flags = MESSAGE_HEADER_PARSER_FLAG_SKIP_INITIAL_LWSP |
+ MESSAGE_HEADER_PARSER_FLAG_DROP_CR,
+ .flags = MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK,
+ };
struct message_parser_ctx *parser;
struct message_part_envelope *envlp = NULL;
struct istream *input;
@@ -126,10 +131,7 @@ msg_parse(pool_t pool, const char *messa
int ret;
input = i_stream_create_from_data(message, strlen(message));
- parser = message_parser_init(pool, input,
- MESSAGE_HEADER_PARSER_FLAG_SKIP_INITIAL_LWSP |
- MESSAGE_HEADER_PARSER_FLAG_DROP_CR,
- MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK);
+ parser = message_parser_init(pool, input, &parser_set);
while ((ret = message_parser_parse_next_block(parser, &block)) > 0) {
i_assert(block.part->parent == NULL);
message_part_envelope_parse_from_header(pool, &envlp, block.hdr);
diff -up dovecot-2.3.8/src/lib-mail/istream-attachment-extractor.c.CVE_2020_12100 dovecot-2.3.8/src/lib-mail/istream-attachment-extractor.c
--- dovecot-2.3.8/src/lib-mail/istream-attachment-extractor.c.CVE_2020_12100 2019-10-08 10:46:18.000000000 +0200
+++ dovecot-2.3.8/src/lib-mail/istream-attachment-extractor.c 2020-08-07 12:29:39.975827486 +0200
@@ -696,6 +696,10 @@ i_stream_create_attachment_extractor(str
struct istream_attachment_settings *set,
void *context)
{
+ const struct message_parser_settings parser_set = {
+ .flags = MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS |
+ MESSAGE_PARSER_FLAG_INCLUDE_BOUNDARIES,
+ };
struct attachment_istream *astream;
i_assert(set->min_size > 0);
@@ -722,9 +726,7 @@ i_stream_create_attachment_extractor(str
astream->istream.istream.seekable = FALSE;
astream->pool = pool_alloconly_create("istream attachment", 1024);
- astream->parser = message_parser_init(astream->pool, input, 0,
- MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS |
- MESSAGE_PARSER_FLAG_INCLUDE_BOUNDARIES);
+ astream->parser = message_parser_init(astream->pool, input, &parser_set);
return i_stream_create(&astream->istream, input,
i_stream_get_fd(input), 0);
}
diff -up dovecot-2.3.8/src/lib-mail/istream-binary-converter.c.CVE_2020_12100 dovecot-2.3.8/src/lib-mail/istream-binary-converter.c
--- dovecot-2.3.8/src/lib-mail/istream-binary-converter.c.CVE_2020_12100 2019-10-08 10:46:18.000000000 +0200
+++ dovecot-2.3.8/src/lib-mail/istream-binary-converter.c 2020-08-07 12:29:39.975827486 +0200
@@ -286,6 +286,10 @@ static void i_stream_binary_converter_cl
struct istream *i_stream_create_binary_converter(struct istream *input)
{
+ const struct message_parser_settings parser_set = {
+ .flags = MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS |
+ MESSAGE_PARSER_FLAG_INCLUDE_BOUNDARIES,
+ };
struct binary_converter_istream *bstream;
bstream = i_new(struct binary_converter_istream, 1);
@@ -299,9 +303,7 @@ struct istream *i_stream_create_binary_c
bstream->istream.istream.seekable = FALSE;
bstream->pool = pool_alloconly_create("istream binary converter", 128);
- bstream->parser = message_parser_init(bstream->pool, input, 0,
- MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS |
- MESSAGE_PARSER_FLAG_INCLUDE_BOUNDARIES);
+ bstream->parser = message_parser_init(bstream->pool, input, &parser_set);
return i_stream_create(&bstream->istream, input,
i_stream_get_fd(input), 0);
}
diff -up dovecot-2.3.8/src/lib-mail/Makefile.am.CVE_2020_12100 dovecot-2.3.8/src/lib-mail/Makefile.am
--- dovecot-2.3.8/src/lib-mail/Makefile.am.CVE_2020_12100 2019-10-08 10:46:18.000000000 +0200
+++ dovecot-2.3.8/src/lib-mail/Makefile.am 2020-08-07 12:29:39.972827528 +0200
@@ -28,6 +28,7 @@ libmail_la_SOURCES = \
message-header-parser.c \
message-id.c \
message-parser.c \
+ message-parser-from-parts.c \
message-part.c \
message-part-data.c \
message-part-serialize.c \
@@ -42,7 +43,8 @@ libmail_la_SOURCES = \
rfc822-parser.c
noinst_HEADERS = \
- html-entities.h
+ html-entities.h \
+ message-parser-private.h
headers = \
istream-attachment-connector.h \
diff -up dovecot-2.3.8/src/lib-mail/message-parser.c.CVE_2020_12100 dovecot-2.3.8/src/lib-mail/message-parser.c
--- dovecot-2.3.8/src/lib-mail/message-parser.c.CVE_2020_12100 2019-10-08 10:46:18.000000000 +0200
+++ dovecot-2.3.8/src/lib-mail/message-parser.c 2020-08-07 12:29:39.978827444 +0200
@@ -1,54 +1,12 @@
/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
#include "lib.h"
-#include "buffer.h"
+#include "array.h"
#include "str.h"
#include "istream.h"
#include "rfc822-parser.h"
#include "rfc2231-parser.h"
-#include "message-parser.h"
-
-/* RFC-2046 requires boundaries are max. 70 chars + "--" prefix + "--" suffix.
- We'll add a bit more just in case. */
-#define BOUNDARY_END_MAX_LEN (70 + 2 + 2 + 10)
-
-struct message_boundary {
- struct message_boundary *next;
-
- struct message_part *part;
- const char *boundary;
- size_t len;
-
- bool epilogue_found:1;
-};
-
-struct message_parser_ctx {
- pool_t parser_pool, part_pool;
- struct istream *input;
- struct message_part *parts, *part;
- const char *broken_reason;
-
- enum message_header_parser_flags hdr_flags;
- enum message_parser_flags flags;
-
- const char *last_boundary;
- struct message_boundary *boundaries;
-
- size_t skip;
- char last_chr;
- unsigned int want_count;
-
- struct message_header_parser_ctx *hdr_parser_ctx;
- unsigned int prev_hdr_newline_size;
-
- int (*parse_next_block)(struct message_parser_ctx *ctx,
- struct message_block *block_r);
-
- bool part_seen_content_type:1;
- bool multipart:1;
- bool preparsed:1;
- bool eof:1;
-};
+#include "message-parser-private.h"
message_part_header_callback_t *null_message_part_header_callback = NULL;
@@ -58,14 +16,10 @@ static int parse_next_body_to_boundary(s
struct message_block *block_r);
static int parse_next_body_to_eof(struct message_parser_ctx *ctx,
struct message_block *block_r);
-static int preparsed_parse_epilogue_init(struct message_parser_ctx *ctx,
- struct message_block *block_r);
-static int preparsed_parse_next_header_init(struct message_parser_ctx *ctx,
- struct message_block *block_r);
static struct message_boundary *
boundary_find(struct message_boundary *boundaries,
- const unsigned char *data, size_t len)
+ const unsigned char *data, size_t len, bool trailing_dashes)
{
struct message_boundary *best = NULL;
@@ -77,8 +31,18 @@ boundary_find(struct message_boundary *b
while (boundaries != NULL) {
if (boundaries->len <= len &&
memcmp(boundaries->boundary, data, boundaries->len) == 0 &&
- (best == NULL || best->len < boundaries->len))
+ (best == NULL || best->len < boundaries->len)) {
best = boundaries;
+ /* If we see "foo--", it could either mean that there
+ is a boundary named "foo" that ends now or there's
+ a boundary "foo--" which continues. */
+ if (best->len == len ||
+ (best->len == len-2 && trailing_dashes)) {
+ /* This is exactly the wanted boundary. There
+ can't be a better one. */
+ break;
+ }
+ }
boundaries = boundaries->next;
}
@@ -122,8 +86,8 @@ static void parse_body_add_block(struct
ctx->part->body_size.virtual_size += block->size + missing_cr_count;
}
-static int message_parser_read_more(struct message_parser_ctx *ctx,
- struct message_block *block_r, bool *full_r)
+int message_parser_read_more(struct message_parser_ctx *ctx,
+ struct message_block *block_r, bool *full_r)
{
int ret;
@@ -168,19 +132,18 @@ static int message_parser_read_more(stru
return 1;
}
-static struct message_part *
-message_part_append(pool_t pool, struct message_part *parent)
+static void
+message_part_append(struct message_parser_ctx *ctx)
{
- struct message_part *p, *part, **list;
+ struct message_part *parent = ctx->part;
+ struct message_part *part;
i_assert(parent != NULL);
i_assert((parent->flags & (MESSAGE_PART_FLAG_MULTIPART |
MESSAGE_PART_FLAG_MESSAGE_RFC822)) != 0);
- part = p_new(pool, struct message_part, 1);
+ part = p_new(ctx->part_pool, struct message_part, 1);
part->parent = parent;
- for (p = parent; p != NULL; p = p->parent)
- p->children_count++;
/* set child position */
part->physical_pos =
@@ -188,33 +151,78 @@ message_part_append(pool_t pool, struct
parent->body_size.physical_size +
parent->header_size.physical_size;
- list = &part->parent->children;
- while (*list != NULL)
- list = &(*list)->next;
+ /* add to parent's linked list */
+ *ctx->next_part = part;
+ /* update the parent's end-of-linked-list pointer */
+ struct message_part **next_part = &part->next;
+ array_push_back(&ctx->next_part_stack, &next_part);
+ /* This part is now the new parent for the next message_part_append()
+ call. Its linked list begins with the children pointer. */
+ ctx->next_part = &part->children;
+
+ ctx->part = part;
+ ctx->nested_parts_count++;
+ ctx->total_parts_count++;
+ i_assert(ctx->nested_parts_count < ctx->max_nested_mime_parts);
+ i_assert(ctx->total_parts_count <= ctx->max_total_mime_parts);
+}
+
+static void message_part_finish(struct message_parser_ctx *ctx)
+{
+ struct message_part **const *parent_next_partp;
+
+ i_assert(ctx->nested_parts_count > 0);
+ ctx->nested_parts_count--;
+
+ parent_next_partp = array_back(&ctx->next_part_stack);
+ array_pop_back(&ctx->next_part_stack);
+ ctx->next_part = *parent_next_partp;
+
+ message_size_add(&ctx->part->parent->body_size, &ctx->part->body_size);
+ message_size_add(&ctx->part->parent->body_size, &ctx->part->header_size);
+ ctx->part->parent->children_count += 1 + ctx->part->children_count;
+ ctx->part = ctx->part->parent;
+}
+
+static void message_boundary_free(struct message_boundary *b)
+{
+ i_free(b->boundary);
+ i_free(b);
+}
+
+static void
+boundary_remove_until(struct message_parser_ctx *ctx,
+ struct message_boundary *boundary)
+{
+ while (ctx->boundaries != boundary) {
+ struct message_boundary *cur = ctx->boundaries;
- *list = part;
- return part;
+ i_assert(cur != NULL);
+ ctx->boundaries = cur->next;
+ message_boundary_free(cur);
+
+ }
+ ctx->boundaries = boundary;
}
static void parse_next_body_multipart_init(struct message_parser_ctx *ctx)
{
struct message_boundary *b;
- b = p_new(ctx->parser_pool, struct message_boundary, 1);
+ b = i_new(struct message_boundary, 1);
b->part = ctx->part;
b->boundary = ctx->last_boundary;
+ ctx->last_boundary = NULL;
b->len = strlen(b->boundary);
b->next = ctx->boundaries;
ctx->boundaries = b;
-
- ctx->last_boundary = NULL;
}
static int parse_next_body_message_rfc822_init(struct message_parser_ctx *ctx,
struct message_block *block_r)
{
- ctx->part = message_part_append(ctx->part_pool, ctx->part);
+ message_part_append(ctx);
return parse_next_header_init(ctx, block_r);
}
@@ -239,19 +247,38 @@ boundary_line_find(struct message_parser
return -1;
}
+ if (ctx->total_parts_count >= ctx->max_total_mime_parts) {
+ /* can't add any more MIME parts. just stop trying to find
+ more boundaries. */
+ return -1;
+ }
+
/* need to find the end of line */
- if (memchr(data + 2, '\n', size - 2) == NULL &&
- size < BOUNDARY_END_MAX_LEN &&
+ data += 2;
+ size -= 2;
+ const unsigned char *lf_pos = memchr(data, '\n', size);
+ if (lf_pos == NULL &&
+ size+2 < BOUNDARY_END_MAX_LEN &&
!ctx->input->eof && !full) {
/* no LF found */
ctx->want_count = BOUNDARY_END_MAX_LEN;
return 0;
}
+ size_t find_size = size;
+ bool trailing_dashes = FALSE;
- data += 2;
- size -= 2;
+ if (lf_pos != NULL) {
+ find_size = lf_pos - data;
+ if (find_size > 0 && data[find_size-1] == '\r')
+ find_size--;
+ if (find_size > 2 && data[find_size-1] == '-' &&
+ data[find_size-2] == '-')
+ trailing_dashes = TRUE;
+ } else if (find_size > BOUNDARY_END_MAX_LEN)
+ find_size = BOUNDARY_END_MAX_LEN;
- *boundary_r = boundary_find(ctx->boundaries, data, size);
+ *boundary_r = boundary_find(ctx->boundaries, data, find_size,
+ trailing_dashes);
if (*boundary_r == NULL)
return -1;
@@ -264,7 +291,7 @@ boundary_line_find(struct message_parser
static int parse_next_mime_header_init(struct message_parser_ctx *ctx,
struct message_block *block_r)
{
- ctx->part = message_part_append(ctx->part_pool, ctx->part);
+ message_part_append(ctx);
ctx->part->flags |= MESSAGE_PART_FLAG_IS_MIME;
return parse_next_header_init(ctx, block_r);
@@ -313,26 +340,25 @@ static int parse_part_finish(struct mess
struct message_boundary *boundary,
struct message_block *block_r, bool first_line)
{
- struct message_part *part;
size_t line_size;
+ size_t boundary_len = boundary->len;
+ bool boundary_epilogue_found = boundary->epilogue_found;
i_assert(ctx->last_boundary == NULL);
/* get back to parent MIME part, summing the child MIME part sizes
into parent's body sizes */
- for (part = ctx->part; part != boundary->part; part = part->parent) {
- message_size_add(&part->parent->body_size, &part->body_size);
- message_size_add(&part->parent->body_size, &part->header_size);
+ while (ctx->part != boundary->part) {
+ message_part_finish(ctx);
+ i_assert(ctx->part != NULL);
}
- i_assert(part != NULL);
- ctx->part = part;
if (boundary->epilogue_found) {
/* this boundary isn't needed anymore */
- ctx->boundaries = boundary->next;
+ boundary_remove_until(ctx, boundary->next);
} else {
/* forget about the boundaries we possibly skipped */
- ctx->boundaries = boundary;
+ boundary_remove_until(ctx, boundary);
}
/* the boundary itself should already be in buffer. add that. */
@@ -349,7 +375,7 @@ static int parse_part_finish(struct mess
i_assert(block_r->data[0] == '\n');
line_size = 1;
}
- line_size += 2 + boundary->len + (boundary->epilogue_found ? 2 : 0);
+ line_size += 2 + boundary_len + (boundary_epilogue_found ? 2 : 0);
i_assert(block_r->size >= ctx->skip + line_size);
block_r->size = line_size;
parse_body_add_block(ctx, block_r);
@@ -510,8 +536,10 @@ static void parse_content_type(struct me
rfc2231_parse(&parser, &results);
for (; *results != NULL; results += 2) {
if (strcasecmp(results[0], "boundary") == 0) {
+ /* truncate excessively long boundaries */
+ i_free(ctx->last_boundary);
ctx->last_boundary =
- p_strdup(ctx->parser_pool, results[1]);
+ i_strndup(results[1], BOUNDARY_STRING_MAX_LEN);
break;
}
}
@@ -533,6 +561,11 @@ static bool block_is_at_eoh(const struct
return FALSE;
}
+static bool parse_too_many_nested_mime_parts(struct message_parser_ctx *ctx)
+{
+ return ctx->nested_parts_count+1 >= ctx->max_nested_mime_parts;
+}
+
#define MUTEX_FLAGS \
(MESSAGE_PART_FLAG_MESSAGE_RFC822 | MESSAGE_PART_FLAG_MULTIPART)
@@ -557,8 +590,12 @@ static int parse_next_header(struct mess
"\n--boundary" belongs to us or to a previous boundary.
this is a problem if the boundary prefixes are identical,
because MIME requires only the prefix to match. */
- parse_next_body_multipart_init(ctx);
- ctx->multipart = TRUE;
+ if (!parse_too_many_nested_mime_parts(ctx)) {
+ parse_next_body_multipart_init(ctx);
+ ctx->multipart = TRUE;
+ } else {
+ part->flags &= ~MESSAGE_PART_FLAG_MULTIPART;
+ }
}
/* before parsing the header see if we can find a --boundary from here.
@@ -634,7 +671,7 @@ static int parse_next_header(struct mess
i_assert(!ctx->multipart);
part->flags = 0;
}
- ctx->last_boundary = NULL;
+ i_free(ctx->last_boundary);
if (!ctx->part_seen_content_type ||
(part->flags & MESSAGE_PART_FLAG_IS_MIME) == 0) {
@@ -662,12 +699,16 @@ static int parse_next_header(struct mess
i_assert(ctx->last_boundary == NULL);
ctx->multipart = FALSE;
ctx->parse_next_block = parse_next_body_to_boundary;
- } else if ((part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) != 0)
+ } else if ((part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) != 0 &&
+ !parse_too_many_nested_mime_parts(ctx)) {
ctx->parse_next_block = parse_next_body_message_rfc822_init;
- else if (ctx->boundaries != NULL)
- ctx->parse_next_block = parse_next_body_to_boundary;
- else
- ctx->parse_next_block = parse_next_body_to_eof;
+ } else {
+ part->flags &= ~MESSAGE_PART_FLAG_MESSAGE_RFC822;
+ if (ctx->boundaries != NULL)
+ ctx->parse_next_block = parse_next_body_to_boundary;
+ else
+ ctx->parse_next_block = parse_next_body_to_eof;
+ }
ctx->want_count = 1;
@@ -692,358 +733,21 @@ static int parse_next_header_init(struct
return parse_next_header(ctx, block_r);
}
-static int preparsed_parse_eof(struct message_parser_ctx *ctx ATTR_UNUSED,
- struct message_block *block_r ATTR_UNUSED)
-{
- return -1;
-}
-
-static void preparsed_skip_to_next(struct message_parser_ctx *ctx)
-{
- ctx->parse_next_block = preparsed_parse_next_header_init;
- while (ctx->part != NULL) {
- if (ctx->part->next != NULL) {
- ctx->part = ctx->part->next;
- break;
- }
-
- /* parse epilogue of multipart parent if requested */
- if (ctx->part->parent != NULL &&
- (ctx->part->parent->flags & MESSAGE_PART_FLAG_MULTIPART) != 0 &&
- (ctx->flags & MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS) != 0) {
- /* check for presence of epilogue */
- uoff_t part_end = ctx->part->physical_pos +
- ctx->part->header_size.physical_size +
- ctx->part->body_size.physical_size;
- uoff_t parent_end = ctx->part->parent->physical_pos +
- ctx->part->parent->header_size.physical_size +
- ctx->part->parent->body_size.physical_size;
-
- if (parent_end > part_end) {
- ctx->parse_next_block = preparsed_parse_epilogue_init;
- break;
- }
- }
- ctx->part = ctx->part->parent;
- }
- if (ctx->part == NULL)
- ctx->parse_next_block = preparsed_parse_eof;
-}
-
-static int preparsed_parse_body_finish(struct message_parser_ctx *ctx,
- struct message_block *block_r)
-{
- i_stream_skip(ctx->input, ctx->skip);
- ctx->skip = 0;
-
- preparsed_skip_to_next(ctx);
- return ctx->parse_next_block(ctx, block_r);
-}
-
-static int preparsed_parse_prologue_finish(struct message_parser_ctx *ctx,
- struct message_block *block_r)
-{
- i_stream_skip(ctx->input, ctx->skip);
- ctx->skip = 0;
-
- ctx->parse_next_block = preparsed_parse_next_header_init;
- ctx->part = ctx->part->children;
- return ctx->parse_next_block(ctx, block_r);
-}
-
-static int preparsed_parse_body_more(struct message_parser_ctx *ctx,
- struct message_block *block_r)
-{
- uoff_t end_offset = ctx->part->physical_pos +
- ctx->part->header_size.physical_size +
- ctx->part->body_size.physical_size;
- bool full;
- int ret;
-
- if ((ret = message_parser_read_more(ctx, block_r, &full)) <= 0)
- return ret;
-
- if (ctx->input->v_offset + block_r->size >= end_offset) {
- block_r->size = end_offset - ctx->input->v_offset;
- ctx->parse_next_block = preparsed_parse_body_finish;
- }
- ctx->skip = block_r->size;
- return 1;
-}
-
-static int preparsed_parse_prologue_more(struct message_parser_ctx *ctx,
- struct message_block *block_r)
-{
- uoff_t boundary_min_start, end_offset;
- const unsigned char *cur;
- bool full;
- int ret;
-
- i_assert(ctx->part->children != NULL);
- end_offset = ctx->part->children->physical_pos;
-
- if ((ret = message_parser_read_more(ctx, block_r, &full)) <= 0)
- return ret;
-
- if (ctx->input->v_offset + block_r->size >= end_offset) {
- /* we've got the full prologue: clip off the initial boundary */
- block_r->size = end_offset - ctx->input->v_offset;
- cur = block_r->data + block_r->size - 1;
-
- /* [\r]\n--boundary[\r]\n */
- if (block_r->size < 5 || *cur != '\n') {
- ctx->broken_reason = "Prologue boundary end not at expected position";
- return -1;
- }
-
- cur--;
- if (*cur == '\r') cur--;
-
- /* find newline just before boundary */
- for (; cur >= block_r->data; cur--) {
- if (*cur == '\n') break;
- }
-
- if (cur[0] != '\n' || cur[1] != '-' || cur[2] != '-') {
- ctx->broken_reason = "Prologue boundary beginning not at expected position";
- return -1;
- }
-
- if (cur != block_r->data && cur[-1] == '\r') cur--;
-
- /* clip boundary */
- block_r->size = cur - block_r->data;
-
- ctx->parse_next_block = preparsed_parse_prologue_finish;
- ctx->skip = block_r->size;
- return 1;
- }
-
- /* retain enough data in the stream buffer to contain initial boundary */
- if (end_offset > BOUNDARY_END_MAX_LEN)
- boundary_min_start = end_offset - BOUNDARY_END_MAX_LEN;
- else
- boundary_min_start = 0;
-
- if (ctx->input->v_offset + block_r->size >= boundary_min_start) {
- if (boundary_min_start <= ctx->input->v_offset)
- return 0;
- block_r->size = boundary_min_start - ctx->input->v_offset;
- }
- ctx->skip = block_r->size;
- return 1;
-}
-
-static int preparsed_parse_epilogue_more(struct message_parser_ctx *ctx,
- struct message_block *block_r)
-{
- uoff_t end_offset = ctx->part->physical_pos +
- ctx->part->header_size.physical_size +
- ctx->part->body_size.physical_size;
- bool full;
- int ret;
-
- if ((ret = message_parser_read_more(ctx, block_r, &full)) <= 0)
- return ret;
-
- if (ctx->input->v_offset + block_r->size >= end_offset) {
- block_r->size = end_offset - ctx->input->v_offset;
- ctx->parse_next_block = preparsed_parse_body_finish;
- }
- ctx->skip = block_r->size;
- return 1;
-}
-
-static int preparsed_parse_epilogue_boundary(struct message_parser_ctx *ctx,
- struct message_block *block_r)
-{
- uoff_t end_offset = ctx->part->physical_pos +
- ctx->part->header_size.physical_size +
- ctx->part->body_size.physical_size;
- const unsigned char *data, *cur;
- size_t size;
- bool full;
- int ret;
-
- if (end_offset - ctx->input->v_offset < 7) {
- ctx->broken_reason = "Epilogue position is wrong";
- return -1;
- }
-
- if ((ret = message_parser_read_more(ctx, block_r, &full)) <= 0)
- return ret;
-
- /* [\r]\n--boundary--[\r]\n */
- if (block_r->size < 7) {
- ctx->want_count = 7;
- return 0;
- }
-
- data = block_r->data;
- size = block_r->size;
- cur = data;
-
- if (*cur == '\r') cur++;
-
- if (cur[0] != '\n' || cur[1] != '-' || data[2] != '-') {
- ctx->broken_reason = "Epilogue boundary start not at expected position";
- return -1;
- }
-
- /* find the end of the line */
- cur += 3;
- if ((cur = memchr(cur, '\n', size - (cur-data))) == NULL) {
- if (end_offset < ctx->input->v_offset + size) {
- ctx->broken_reason = "Epilogue boundary end not at expected position";
- return -1;
- } else if (ctx->input->v_offset + size < end_offset &&
- size < BOUNDARY_END_MAX_LEN &&
- !ctx->input->eof && !full) {
- ctx->want_count = BOUNDARY_END_MAX_LEN;
- return 0;
- }
- }
-
- block_r->size = 0;
- ctx->parse_next_block = preparsed_parse_epilogue_more;
- ctx->skip = cur - data + 1;
- return 0;
-}
-
-static int preparsed_parse_body_init(struct message_parser_ctx *ctx,
- struct message_block *block_r)
-{
- uoff_t offset = ctx->part->physical_pos +
- ctx->part->header_size.physical_size;
-
- if (offset < ctx->input->v_offset) {
- /* header was actually larger than the cached size suggested */
- ctx->broken_reason = "Header larger than its cached size";
- return -1;
- }
- i_stream_skip(ctx->input, offset - ctx->input->v_offset);
-
- /* multipart messages may begin with --boundary--, which makes them
- not have any children. */
- if ((ctx->part->flags & MESSAGE_PART_FLAG_MULTIPART) == 0 ||
- ctx->part->children == NULL)
- ctx->parse_next_block = preparsed_parse_body_more;
- else
- ctx->parse_next_block = preparsed_parse_prologue_more;
- return ctx->parse_next_block(ctx, block_r);
-}
-
-static int preparsed_parse_epilogue_init(struct message_parser_ctx *ctx,
- struct message_block *block_r)
-{
- uoff_t offset = ctx->part->physical_pos +
- ctx->part->header_size.physical_size +
- ctx->part->body_size.physical_size;
-
- ctx->part = ctx->part->parent;
-
- if (offset < ctx->input->v_offset) {
- /* last child was actually larger than the cached size
- suggested */
- ctx->broken_reason = "Part larger than its cached size";
- return -1;
- }
- i_stream_skip(ctx->input, offset - ctx->input->v_offset);
-
- ctx->parse_next_block = preparsed_parse_epilogue_boundary;
- return ctx->parse_next_block(ctx, block_r);
-}
-
-static int preparsed_parse_finish_header(struct message_parser_ctx *ctx,
- struct message_block *block_r)
-{
- if (ctx->part->children != NULL) {
- if ((ctx->part->flags & MESSAGE_PART_FLAG_MULTIPART) != 0 &&
- (ctx->flags & MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS) != 0)
- ctx->parse_next_block = preparsed_parse_body_init;
- else {
- ctx->parse_next_block = preparsed_parse_next_header_init;
- ctx->part = ctx->part->children;
- }
- } else if ((ctx->flags & MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK) == 0) {
- ctx->parse_next_block = preparsed_parse_body_init;
- } else {
- preparsed_skip_to_next(ctx);
- }
- return ctx->parse_next_block(ctx, block_r);
-}
-
-static int preparsed_parse_next_header(struct message_parser_ctx *ctx,
- struct message_block *block_r)
-{
- struct message_header_line *hdr;
- int ret;
-
- ret = message_parse_header_next(ctx->hdr_parser_ctx, &hdr);
- if (ret == 0 || (ret < 0 && ctx->input->stream_errno != 0)) {
- ctx->want_count = i_stream_get_data_size(ctx->input) + 1;
- return ret;
- }
-
- if (hdr != NULL) {
- block_r->hdr = hdr;
- block_r->size = 0;
- return 1;
- }
- message_parse_header_deinit(&ctx->hdr_parser_ctx);
-
- ctx->parse_next_block = preparsed_parse_finish_header;
-
- /* return empty block as end of headers */
- block_r->hdr = NULL;
- block_r->size = 0;
-
- i_assert(ctx->skip == 0);
- if (ctx->input->v_offset != ctx->part->physical_pos +
- ctx->part->header_size.physical_size) {
- ctx->broken_reason = "Cached header size mismatch";
- return -1;
- }
- return 1;
-}
-
-static int preparsed_parse_next_header_init(struct message_parser_ctx *ctx,
- struct message_block *block_r)
-{
- struct istream *hdr_input;
-
- i_assert(ctx->hdr_parser_ctx == NULL);
-
- i_assert(ctx->part->physical_pos >= ctx->input->v_offset);
- i_stream_skip(ctx->input, ctx->part->physical_pos -
- ctx->input->v_offset);
-
- /* the header may become truncated by --boundaries. limit the header
- stream's size to what it's supposed to be to avoid duplicating (and
- keeping in sync!) all the same complicated logic as in
- parse_next_header(). */
- hdr_input = i_stream_create_limit(ctx->input, ctx->part->header_size.physical_size);
- ctx->hdr_parser_ctx =
- message_parse_header_init(hdr_input, NULL, ctx->hdr_flags);
- i_stream_unref(&hdr_input);
-
- ctx->parse_next_block = preparsed_parse_next_header;
- return preparsed_parse_next_header(ctx, block_r);
-}
-
-static struct message_parser_ctx *
+struct message_parser_ctx *
message_parser_init_int(struct istream *input,
- enum message_header_parser_flags hdr_flags,
- enum message_parser_flags flags)
+ const struct message_parser_settings *set)
{
struct message_parser_ctx *ctx;
- pool_t pool;
- pool = pool_alloconly_create("Message Parser", 1024);
- ctx = p_new(pool, struct message_parser_ctx, 1);
- ctx->parser_pool = pool;
- ctx->hdr_flags = hdr_flags;
- ctx->flags = flags;
+ ctx = i_new(struct message_parser_ctx, 1);
+ ctx->hdr_flags = set->hdr_flags;
+ ctx->flags = set->flags;
+ ctx->max_nested_mime_parts = set->max_nested_mime_parts != 0 ?
+ set->max_nested_mime_parts :
+ MESSAGE_PARSER_DEFAULT_MAX_NESTED_MIME_PARTS;
+ ctx->max_total_mime_parts = set->max_total_mime_parts != 0 ?
+ set->max_total_mime_parts :
+ MESSAGE_PARSER_DEFAULT_MAX_TOTAL_MIME_PARTS;
ctx->input = input;
i_stream_ref(input);
return ctx;
@@ -1051,32 +755,17 @@ message_parser_init_int(struct istream *
struct message_parser_ctx *
message_parser_init(pool_t part_pool, struct istream *input,
- enum message_header_parser_flags hdr_flags,
- enum message_parser_flags flags)
+ const struct message_parser_settings *set)
{
struct message_parser_ctx *ctx;
- ctx = message_parser_init_int(input, hdr_flags, flags);
+ ctx = message_parser_init_int(input, set);
ctx->part_pool = part_pool;
ctx->parts = ctx->part = p_new(part_pool, struct message_part, 1);
+ ctx->next_part = &ctx->part->children;
ctx->parse_next_block = parse_next_header_init;
- return ctx;
-}
-
-struct message_parser_ctx *
-message_parser_init_from_parts(struct message_part *parts,
- struct istream *input,
- enum message_header_parser_flags hdr_flags,
- enum message_parser_flags flags)
-{
- struct message_parser_ctx *ctx;
-
- i_assert(parts != NULL);
-
- ctx = message_parser_init_int(input, hdr_flags, flags);
- ctx->preparsed = TRUE;
- ctx->parts = ctx->part = parts;
- ctx->parse_next_block = preparsed_parse_next_header_init;
+ ctx->total_parts_count = 1;
+ i_array_init(&ctx->next_part_stack, 4);
return ctx;
}
@@ -1103,8 +792,15 @@ int message_parser_deinit_from_parts(str
if (ctx->hdr_parser_ctx != NULL)
message_parse_header_deinit(&ctx->hdr_parser_ctx);
+ boundary_remove_until(ctx, NULL);
+ /* caller might have stopped the parsing early */
+ i_assert(ctx->nested_parts_count == 0 ||
+ i_stream_have_bytes_left(ctx->input));
+
i_stream_unref(&ctx->input);
- pool_unref(&ctx->parser_pool);
+ array_free(&ctx->next_part_stack);
+ i_free(ctx->last_boundary);
+ i_free(ctx);
i_assert(ret < 0 || *parts_r != NULL);
return ret;
}
@@ -1136,13 +832,8 @@ int message_parser_parse_next_block(stru
i_assert(ctx->input->eof || ctx->input->closed ||
ctx->input->stream_errno != 0 ||
ctx->broken_reason != NULL);
- while (ctx->part->parent != NULL) {
- message_size_add(&ctx->part->parent->body_size,
- &ctx->part->body_size);
- message_size_add(&ctx->part->parent->body_size,
- &ctx->part->header_size);
- ctx->part = ctx->part->parent;
- }
+ while (ctx->part->parent != NULL)
+ message_part_finish(ctx);
}
if (block_r->size == 0) {
diff -up dovecot-2.3.8/src/lib-mail/message-parser-from-parts.c.CVE_2020_12100 dovecot-2.3.8/src/lib-mail/message-parser-from-parts.c
--- dovecot-2.3.8/src/lib-mail/message-parser-from-parts.c.CVE_2020_12100 2020-08-07 12:29:39.972827528 +0200
+++ dovecot-2.3.8/src/lib-mail/message-parser-from-parts.c 2020-08-07 12:29:39.975827486 +0200
@@ -0,0 +1,365 @@
+/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "istream.h"
+#include "message-parser-private.h"
+
+static int preparsed_parse_epilogue_init(struct message_parser_ctx *ctx,
+ struct message_block *block_r);
+static int preparsed_parse_next_header_init(struct message_parser_ctx *ctx,
+ struct message_block *block_r);
+
+static int preparsed_parse_eof(struct message_parser_ctx *ctx ATTR_UNUSED,
+ struct message_block *block_r ATTR_UNUSED)
+{
+ return -1;
+}
+
+static void preparsed_skip_to_next(struct message_parser_ctx *ctx)
+{
+ ctx->parse_next_block = preparsed_parse_next_header_init;
+ while (ctx->part != NULL) {
+ if (ctx->part->next != NULL) {
+ ctx->part = ctx->part->next;
+ break;
+ }
+
+ /* parse epilogue of multipart parent if requested */
+ if (ctx->part->parent != NULL &&
+ (ctx->part->parent->flags & MESSAGE_PART_FLAG_MULTIPART) != 0 &&
+ (ctx->flags & MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS) != 0) {
+ /* check for presence of epilogue */
+ uoff_t part_end = ctx->part->physical_pos +
+ ctx->part->header_size.physical_size +
+ ctx->part->body_size.physical_size;
+ uoff_t parent_end = ctx->part->parent->physical_pos +
+ ctx->part->parent->header_size.physical_size +
+ ctx->part->parent->body_size.physical_size;
+
+ if (parent_end > part_end) {
+ ctx->parse_next_block = preparsed_parse_epilogue_init;
+ break;
+ }
+ }
+ ctx->part = ctx->part->parent;
+ }
+ if (ctx->part == NULL)
+ ctx->parse_next_block = preparsed_parse_eof;
+}
+
+static int preparsed_parse_body_finish(struct message_parser_ctx *ctx,
+ struct message_block *block_r)
+{
+ i_stream_skip(ctx->input, ctx->skip);
+ ctx->skip = 0;
+
+ preparsed_skip_to_next(ctx);
+ return ctx->parse_next_block(ctx, block_r);
+}
+
+static int preparsed_parse_prologue_finish(struct message_parser_ctx *ctx,
+ struct message_block *block_r)
+{
+ i_stream_skip(ctx->input, ctx->skip);
+ ctx->skip = 0;
+
+ ctx->parse_next_block = preparsed_parse_next_header_init;
+ ctx->part = ctx->part->children;
+ return ctx->parse_next_block(ctx, block_r);
+}
+
+static int preparsed_parse_body_more(struct message_parser_ctx *ctx,
+ struct message_block *block_r)
+{
+ uoff_t end_offset = ctx->part->physical_pos +
+ ctx->part->header_size.physical_size +
+ ctx->part->body_size.physical_size;
+ bool full;
+ int ret;
+
+ if ((ret = message_parser_read_more(ctx, block_r, &full)) <= 0)
+ return ret;
+
+ if (ctx->input->v_offset + block_r->size >= end_offset) {
+ block_r->size = end_offset - ctx->input->v_offset;
+ ctx->parse_next_block = preparsed_parse_body_finish;
+ }
+ ctx->skip = block_r->size;
+ return 1;
+}
+
+static int preparsed_parse_prologue_more(struct message_parser_ctx *ctx,
+ struct message_block *block_r)
+{
+ uoff_t boundary_min_start, end_offset;
+ const unsigned char *cur;
+ bool full;
+ int ret;
+
+ i_assert(ctx->part->children != NULL);
+ end_offset = ctx->part->children->physical_pos;
+
+ if ((ret = message_parser_read_more(ctx, block_r, &full)) <= 0)
+ return ret;
+
+ if (ctx->input->v_offset + block_r->size >= end_offset) {
+ /* we've got the full prologue: clip off the initial boundary */
+ block_r->size = end_offset - ctx->input->v_offset;
+ cur = block_r->data + block_r->size - 1;
+
+ /* [\r]\n--boundary[\r]\n */
+ if (block_r->size < 5 || *cur != '\n') {
+ ctx->broken_reason = "Prologue boundary end not at expected position";
+ return -1;
+ }
+
+ cur--;
+ if (*cur == '\r') cur--;
+
+ /* find newline just before boundary */
+ for (; cur >= block_r->data; cur--) {
+ if (*cur == '\n') break;
+ }
+
+ if (cur[0] != '\n' || cur[1] != '-' || cur[2] != '-') {
+ ctx->broken_reason = "Prologue boundary beginning not at expected position";
+ return -1;
+ }
+
+ if (cur != block_r->data && cur[-1] == '\r') cur--;
+
+ /* clip boundary */
+ block_r->size = cur - block_r->data;
+
+ ctx->parse_next_block = preparsed_parse_prologue_finish;
+ ctx->skip = block_r->size;
+ return 1;
+ }
+
+ /* retain enough data in the stream buffer to contain initial boundary */
+ if (end_offset > BOUNDARY_END_MAX_LEN)
+ boundary_min_start = end_offset - BOUNDARY_END_MAX_LEN;
+ else
+ boundary_min_start = 0;
+
+ if (ctx->input->v_offset + block_r->size >= boundary_min_start) {
+ if (boundary_min_start <= ctx->input->v_offset)
+ return 0;
+ block_r->size = boundary_min_start - ctx->input->v_offset;
+ }
+ ctx->skip = block_r->size;
+ return 1;
+}
+
+static int preparsed_parse_epilogue_more(struct message_parser_ctx *ctx,
+ struct message_block *block_r)
+{
+ uoff_t end_offset = ctx->part->physical_pos +
+ ctx->part->header_size.physical_size +
+ ctx->part->body_size.physical_size;
+ bool full;
+ int ret;
+
+ if ((ret = message_parser_read_more(ctx, block_r, &full)) <= 0)
+ return ret;
+
+ if (ctx->input->v_offset + block_r->size >= end_offset) {
+ block_r->size = end_offset - ctx->input->v_offset;
+ ctx->parse_next_block = preparsed_parse_body_finish;
+ }
+ ctx->skip = block_r->size;
+ return 1;
+}
+
+static int preparsed_parse_epilogue_boundary(struct message_parser_ctx *ctx,
+ struct message_block *block_r)
+{
+ uoff_t end_offset = ctx->part->physical_pos +
+ ctx->part->header_size.physical_size +
+ ctx->part->body_size.physical_size;
+ const unsigned char *data, *cur;
+ size_t size;
+ bool full;
+ int ret;
+
+ if (end_offset - ctx->input->v_offset < 7) {
+ ctx->broken_reason = "Epilogue position is wrong";
+ return -1;
+ }
+
+ if ((ret = message_parser_read_more(ctx, block_r, &full)) <= 0)
+ return ret;
+
+ /* [\r]\n--boundary--[\r]\n */
+ if (block_r->size < 7) {
+ ctx->want_count = 7;
+ return 0;
+ }
+
+ data = block_r->data;
+ size = block_r->size;
+ cur = data;
+
+ if (*cur == '\r') cur++;
+
+ if (cur[0] != '\n' || cur[1] != '-' || data[2] != '-') {
+ ctx->broken_reason = "Epilogue boundary start not at expected position";
+ return -1;
+ }
+
+ /* find the end of the line */
+ cur += 3;
+ if ((cur = memchr(cur, '\n', size - (cur-data))) == NULL) {
+ if (end_offset < ctx->input->v_offset + size) {
+ ctx->broken_reason = "Epilogue boundary end not at expected position";
+ return -1;
+ } else if (ctx->input->v_offset + size < end_offset &&
+ size < BOUNDARY_END_MAX_LEN &&
+ !ctx->input->eof && !full) {
+ ctx->want_count = BOUNDARY_END_MAX_LEN;
+ return 0;
+ }
+ }
+
+ block_r->size = 0;
+ ctx->parse_next_block = preparsed_parse_epilogue_more;
+ ctx->skip = cur - data + 1;
+ return 0;
+}
+
+static int preparsed_parse_body_init(struct message_parser_ctx *ctx,
+ struct message_block *block_r)
+{
+ uoff_t offset = ctx->part->physical_pos +
+ ctx->part->header_size.physical_size;
+
+ if (offset < ctx->input->v_offset) {
+ /* header was actually larger than the cached size suggested */
+ ctx->broken_reason = "Header larger than its cached size";
+ return -1;
+ }
+ i_stream_skip(ctx->input, offset - ctx->input->v_offset);
+
+ /* multipart messages may begin with --boundary--, which makes them
+ not have any children. */
+ if ((ctx->part->flags & MESSAGE_PART_FLAG_MULTIPART) == 0 ||
+ ctx->part->children == NULL)
+ ctx->parse_next_block = preparsed_parse_body_more;
+ else
+ ctx->parse_next_block = preparsed_parse_prologue_more;
+ return ctx->parse_next_block(ctx, block_r);
+}
+
+static int preparsed_parse_epilogue_init(struct message_parser_ctx *ctx,
+ struct message_block *block_r)
+{
+ uoff_t offset = ctx->part->physical_pos +
+ ctx->part->header_size.physical_size +
+ ctx->part->body_size.physical_size;
+
+ ctx->part = ctx->part->parent;
+
+ if (offset < ctx->input->v_offset) {
+ /* last child was actually larger than the cached size
+ suggested */
+ ctx->broken_reason = "Part larger than its cached size";
+ return -1;
+ }
+ i_stream_skip(ctx->input, offset - ctx->input->v_offset);
+
+ ctx->parse_next_block = preparsed_parse_epilogue_boundary;
+ return ctx->parse_next_block(ctx, block_r);
+}
+
+static int preparsed_parse_finish_header(struct message_parser_ctx *ctx,
+ struct message_block *block_r)
+{
+ if (ctx->part->children != NULL) {
+ if ((ctx->part->flags & MESSAGE_PART_FLAG_MULTIPART) != 0 &&
+ (ctx->flags & MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS) != 0)
+ ctx->parse_next_block = preparsed_parse_body_init;
+ else {
+ ctx->parse_next_block = preparsed_parse_next_header_init;
+ ctx->part = ctx->part->children;
+ }
+ } else if ((ctx->flags & MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK) == 0) {
+ ctx->parse_next_block = preparsed_parse_body_init;
+ } else {
+ preparsed_skip_to_next(ctx);
+ }
+ return ctx->parse_next_block(ctx, block_r);
+}
+
+static int preparsed_parse_next_header(struct message_parser_ctx *ctx,
+ struct message_block *block_r)
+{
+ struct message_header_line *hdr;
+ int ret;
+
+ ret = message_parse_header_next(ctx->hdr_parser_ctx, &hdr);
+ if (ret == 0 || (ret < 0 && ctx->input->stream_errno != 0)) {
+ ctx->want_count = i_stream_get_data_size(ctx->input) + 1;
+ return ret;
+ }
+
+ if (hdr != NULL) {
+ block_r->hdr = hdr;
+ block_r->size = 0;
+ return 1;
+ }
+ message_parse_header_deinit(&ctx->hdr_parser_ctx);
+
+ ctx->parse_next_block = preparsed_parse_finish_header;
+
+ /* return empty block as end of headers */
+ block_r->hdr = NULL;
+ block_r->size = 0;
+
+ i_assert(ctx->skip == 0);
+ if (ctx->input->v_offset != ctx->part->physical_pos +
+ ctx->part->header_size.physical_size) {
+ ctx->broken_reason = "Cached header size mismatch";
+ return -1;
+ }
+ return 1;
+}
+
+static int preparsed_parse_next_header_init(struct message_parser_ctx *ctx,
+ struct message_block *block_r)
+{
+ struct istream *hdr_input;
+
+ i_assert(ctx->hdr_parser_ctx == NULL);
+
+ i_assert(ctx->part->physical_pos >= ctx->input->v_offset);
+ i_stream_skip(ctx->input, ctx->part->physical_pos -
+ ctx->input->v_offset);
+
+ /* the header may become truncated by --boundaries. limit the header
+ stream's size to what it's supposed to be to avoid duplicating (and
+ keeping in sync!) all the same complicated logic as in
+ parse_next_header(). */
+ hdr_input = i_stream_create_limit(ctx->input, ctx->part->header_size.physical_size);
+ ctx->hdr_parser_ctx =
+ message_parse_header_init(hdr_input, NULL, ctx->hdr_flags);
+ i_stream_unref(&hdr_input);
+
+ ctx->parse_next_block = preparsed_parse_next_header;
+ return preparsed_parse_next_header(ctx, block_r);
+}
+
+struct message_parser_ctx *
+message_parser_init_from_parts(struct message_part *parts,
+ struct istream *input,
+ const struct message_parser_settings *set)
+{
+ struct message_parser_ctx *ctx;
+
+ i_assert(parts != NULL);
+
+ ctx = message_parser_init_int(input, set);
+ ctx->preparsed = TRUE;
+ ctx->parts = ctx->part = parts;
+ ctx->parse_next_block = preparsed_parse_next_header_init;
+ return ctx;
+}
diff -up dovecot-2.3.8/src/lib-mail/message-parser.h.CVE_2020_12100 dovecot-2.3.8/src/lib-mail/message-parser.h
--- dovecot-2.3.8/src/lib-mail/message-parser.h.CVE_2020_12100 2019-10-08 10:46:18.000000000 +0200
+++ dovecot-2.3.8/src/lib-mail/message-parser.h 2020-08-07 12:29:39.978827444 +0200
@@ -17,6 +17,21 @@ enum message_parser_flags {
MESSAGE_PARSER_FLAG_INCLUDE_BOUNDARIES = 0x08
};
+#define MESSAGE_PARSER_DEFAULT_MAX_NESTED_MIME_PARTS 100
+#define MESSAGE_PARSER_DEFAULT_MAX_TOTAL_MIME_PARTS 10000
+
+struct message_parser_settings {
+ enum message_header_parser_flags hdr_flags;
+ enum message_parser_flags flags;
+
+ /* Maximum nested MIME parts.
+ 0 = MESSAGE_PARSER_DEFAULT_MAX_NESTED_MIME_PARTS. */
+ unsigned int max_nested_mime_parts;
+ /* Maximum MIME parts in total.
+ 0 = MESSAGE_PARSER_DEFAULT_MAX_TOTAL_MIME_PARTS. */
+ unsigned int max_total_mime_parts;
+};
+
struct message_parser_ctx;
struct message_block {
@@ -45,8 +60,7 @@ extern message_part_header_callback_t *n
are allocated from. */
struct message_parser_ctx *
message_parser_init(pool_t part_pool, struct istream *input,
- enum message_header_parser_flags hdr_flags,
- enum message_parser_flags flags);
+ const struct message_parser_settings *set);
/* Deinitialize message parser. The ctx must NOT have been created by
message_parser_init_from_parts(). */
void message_parser_deinit(struct message_parser_ctx **ctx,
@@ -55,8 +69,7 @@ void message_parser_deinit(struct messag
struct message_parser_ctx *
message_parser_init_from_parts(struct message_part *parts,
struct istream *input,
- enum message_header_parser_flags hdr_flags,
- enum message_parser_flags flags);
+ const struct message_parser_settings *set);
/* Same as message_parser_deinit(), but return an error message describing
why the preparsed parts didn't match the message. This can also safely be
called even when preparsed parts weren't used - it'll always just return
diff -up dovecot-2.3.8/src/lib-mail/message-parser-private.h.CVE_2020_12100 dovecot-2.3.8/src/lib-mail/message-parser-private.h
--- dovecot-2.3.8/src/lib-mail/message-parser-private.h.CVE_2020_12100 2020-08-07 12:29:39.972827528 +0200
+++ dovecot-2.3.8/src/lib-mail/message-parser-private.h 2020-08-07 12:29:39.978827444 +0200
@@ -0,0 +1,62 @@
+#ifndef MESSAGE_PARSER_PRIVATE_H
+#define MESSAGE_PARSER_PRIVATE_H
+
+#include "message-parser.h"
+
+/* RFC-2046 requires boundaries are max. 70 chars + "--" prefix + "--" suffix.
+ We'll add a bit more just in case. */
+#define BOUNDARY_STRING_MAX_LEN (70 + 10)
+#define BOUNDARY_END_MAX_LEN (BOUNDARY_STRING_MAX_LEN + 2 + 2)
+
+struct message_boundary {
+ struct message_boundary *next;
+
+ struct message_part *part;
+ char *boundary;
+ size_t len;
+
+ bool epilogue_found:1;
+};
+
+struct message_parser_ctx {
+ pool_t part_pool;
+ struct istream *input;
+ struct message_part *parts, *part;
+ const char *broken_reason;
+ unsigned int nested_parts_count;
+ unsigned int total_parts_count;
+
+ enum message_header_parser_flags hdr_flags;
+ enum message_parser_flags flags;
+ unsigned int max_nested_mime_parts;
+ unsigned int max_total_mime_parts;
+
+ char *last_boundary;
+ struct message_boundary *boundaries;
+
+ struct message_part **next_part;
+ ARRAY(struct message_part **) next_part_stack;
+
+ size_t skip;
+ char last_chr;
+ unsigned int want_count;
+
+ struct message_header_parser_ctx *hdr_parser_ctx;
+ unsigned int prev_hdr_newline_size;
+
+ int (*parse_next_block)(struct message_parser_ctx *ctx,
+ struct message_block *block_r);
+
+ bool part_seen_content_type:1;
+ bool multipart:1;
+ bool preparsed:1;
+ bool eof:1;
+};
+
+struct message_parser_ctx *
+message_parser_init_int(struct istream *input,
+ const struct message_parser_settings *set);
+int message_parser_read_more(struct message_parser_ctx *ctx,
+ struct message_block *block_r, bool *full_r);
+
+#endif
diff -up dovecot-2.3.8/src/lib-mail/message-search.c.CVE_2020_12100 dovecot-2.3.8/src/lib-mail/message-search.c
--- dovecot-2.3.8/src/lib-mail/message-search.c.CVE_2020_12100 2019-10-08 10:46:18.000000000 +0200
+++ dovecot-2.3.8/src/lib-mail/message-search.c 2020-08-07 12:29:39.976827472 +0200
@@ -196,8 +196,9 @@ message_search_msg_real(struct message_s
struct istream *input, struct message_part *parts,
const char **error_r)
{
- const enum message_header_parser_flags hdr_parser_flags =
- MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE;
+ const struct message_parser_settings parser_set = {
+ .hdr_flags = MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE,
+ };
struct message_parser_ctx *parser_ctx;
struct message_block raw_block;
struct message_part *new_parts;
@@ -207,10 +208,10 @@ message_search_msg_real(struct message_s
if (parts != NULL) {
parser_ctx = message_parser_init_from_parts(parts,
- input, hdr_parser_flags, 0);
+ input, &parser_set);
} else {
parser_ctx = message_parser_init(pool_datastack_create(),
- input, hdr_parser_flags, 0);
+ input, &parser_set);
}
while ((ret = message_parser_parse_next_block(parser_ctx,
diff -up dovecot-2.3.8/src/lib-mail/message-snippet.c.CVE_2020_12100 dovecot-2.3.8/src/lib-mail/message-snippet.c
--- dovecot-2.3.8/src/lib-mail/message-snippet.c.CVE_2020_12100 2019-10-08 10:46:18.000000000 +0200
+++ dovecot-2.3.8/src/lib-mail/message-snippet.c 2020-08-07 12:30:01.710525318 +0200
@@ -99,6 +99,7 @@ int message_snippet_generate(struct istr
unsigned int max_snippet_chars,
string_t *snippet)
{
+ const struct message_parser_settings parser_set = { .flags = 0 };
struct message_parser_ctx *parser;
struct message_part *parts;
struct message_decoder_context *decoder;
@@ -112,7 +113,7 @@ int message_snippet_generate(struct istr
ctx.snippet = snippet;
ctx.chars_left = max_snippet_chars;
- parser = message_parser_init(pool_datastack_create(), input, 0, 0);
+ parser = message_parser_init(pool_datastack_create(), input, &parser_set);
decoder = message_decoder_init(NULL, 0);
while ((ret = message_parser_parse_next_block(parser, &raw_block)) > 0) {
if (!message_decoder_decode_next_block(decoder, &raw_block, &block))
diff -up dovecot-2.3.8/src/lib-mail/test-message-decoder.c.CVE_2020_12100 dovecot-2.3.8/src/lib-mail/test-message-decoder.c
--- dovecot-2.3.8/src/lib-mail/test-message-decoder.c.CVE_2020_12100 2020-08-07 12:29:39.970827555 +0200
+++ dovecot-2.3.8/src/lib-mail/test-message-decoder.c 2020-08-07 12:29:39.976827472 +0200
@@ -105,6 +105,7 @@ static void test_message_decoder_multipa
"\n"
"?garbage\n"
"--foo--\n";
+ const struct message_parser_settings parser_set = { .flags = 0, };
struct message_parser_ctx *parser;
struct message_decoder_context *decoder;
struct message_part *parts;
@@ -116,7 +117,8 @@ static void test_message_decoder_multipa
test_begin("message decoder multipart");
istream = test_istream_create(test_message_input);
- parser = message_parser_init(pool_datastack_create(), istream, 0, 0);
+ parser = message_parser_init(pool_datastack_create(), istream,
+ &parser_set);
decoder = message_decoder_init(NULL, 0);
test_istream_set_allow_eof(istream, FALSE);
diff -up dovecot-2.3.8/src/lib-mail/test-message-parser.c.CVE_2020_12100 dovecot-2.3.8/src/lib-mail/test-message-parser.c
--- dovecot-2.3.8/src/lib-mail/test-message-parser.c.CVE_2020_12100 2019-10-08 10:46:18.000000000 +0200
+++ dovecot-2.3.8/src/lib-mail/test-message-parser.c 2020-08-07 12:29:39.979827431 +0200
@@ -39,6 +39,8 @@ static const char test_msg[] =
"\n";
#define TEST_MSG_LEN (sizeof(test_msg)-1)
+static const struct message_parser_settings set_empty = { .flags = 0 };
+
static bool msg_parts_cmp(struct message_part *p1, struct message_part *p2)
{
while (p1 != NULL || p2 != NULL) {
@@ -59,6 +61,7 @@ static bool msg_parts_cmp(struct message
p1->body_size.physical_size != p2->body_size.physical_size ||
p1->body_size.virtual_size != p2->body_size.virtual_size ||
p1->body_size.lines != p2->body_size.lines ||
+ p1->children_count != p2->children_count ||
p1->flags != p2->flags)
return FALSE;
@@ -70,6 +73,9 @@ static bool msg_parts_cmp(struct message
static void test_parsed_parts(struct istream *input, struct message_part *parts)
{
+ const struct message_parser_settings parser_set = {
+ .flags = MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK,
+ };
struct message_parser_ctx *parser;
struct message_block block;
struct message_part *parts2;
@@ -81,8 +87,7 @@ static void test_parsed_parts(struct ist
if (i_stream_get_size(input, TRUE, &input_size) < 0)
i_unreached();
- parser = message_parser_init_from_parts(parts, input, 0,
- MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK);
+ parser = message_parser_init_from_parts(parts, input, &parser_set);
for (i = 1; i <= input_size*2+1; i++) {
test_istream_set_size(input, i/2);
if (i > TEST_MSG_LEN*2)
@@ -112,9 +117,11 @@ static void test_message_parser_small_bl
output = t_str_new(128);
/* full parsing */
- parser = message_parser_init(pool, input, 0,
- MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS |
- MESSAGE_PARSER_FLAG_INCLUDE_BOUNDARIES);
+ const struct message_parser_settings full_parser_set = {
+ .flags = MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS |
+ MESSAGE_PARSER_FLAG_INCLUDE_BOUNDARIES,
+ };
+ parser = message_parser_init(pool, input, &full_parser_set);
while ((ret = message_parser_parse_next_block(parser, &block)) > 0) {
if (block.hdr != NULL)
message_header_line_write(output, block.hdr);
@@ -130,7 +137,7 @@ static void test_message_parser_small_bl
i_stream_seek(input, 0);
test_istream_set_allow_eof(input, FALSE);
- parser = message_parser_init(pool, input, 0, 0);
+ parser = message_parser_init(pool, input, &set_empty);
for (i = 1; i <= TEST_MSG_LEN*2+1; i++) {
test_istream_set_size(input, i/2);
if (i > TEST_MSG_LEN*2)
@@ -148,8 +155,11 @@ static void test_message_parser_small_bl
test_istream_set_allow_eof(input, FALSE);
end_of_headers_idx = (strstr(test_msg, "\n-----") - test_msg);
- parser = message_parser_init_from_parts(parts, input, 0,
- MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK);
+ const struct message_parser_settings preparsed_parser_set = {
+ .flags = MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK,
+ };
+ parser = message_parser_init_from_parts(parts, input,
+ &preparsed_parser_set);
for (i = 1; i <= TEST_MSG_LEN*2+1; i++) {
test_istream_set_size(input, i/2);
if (i > TEST_MSG_LEN*2)
@@ -167,6 +177,36 @@ static void test_message_parser_small_bl
test_end();
}
+static void test_message_parser_stop_early(void)
+{
+ struct message_parser_ctx *parser;
+ struct istream *input;
+ struct message_part *parts;
+ struct message_block block;
+ unsigned int i;
+ pool_t pool;
+ int ret;
+
+ test_begin("message parser in stop early");
+ pool = pool_alloconly_create("message parser", 10240);
+ input = test_istream_create(test_msg);
+
+ test_istream_set_allow_eof(input, FALSE);
+ for (i = 1; i <= TEST_MSG_LEN+1; i++) {
+ i_stream_seek(input, 0);
+ test_istream_set_size(input, i);
+ parser = message_parser_init(pool, input, &set_empty);
+ while ((ret = message_parser_parse_next_block(parser,
+ &block)) > 0) ;
+ test_assert(ret == 0);
+ message_parser_deinit(&parser, &parts);
+ }
+
+ i_stream_unref(&input);
+ pool_unref(&pool);
+ test_end();
+}
+
static void test_message_parser_truncated_mime_headers(void)
{
static const char input_msg[] =
@@ -191,12 +231,13 @@ static const char input_msg[] =
pool = pool_alloconly_create("message parser", 10240);
input = test_istream_create(input_msg);
- parser = message_parser_init(pool, input, 0, 0);
+ parser = message_parser_init(pool, input, &set_empty);
while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ;
test_assert(ret < 0);
message_parser_deinit(&parser, &parts);
test_assert((parts->flags & MESSAGE_PART_FLAG_MULTIPART) != 0);
+ test_assert(parts->children_count == 4);
test_assert(parts->header_size.lines == 2);
test_assert(parts->header_size.physical_size == 48);
test_assert(parts->header_size.virtual_size == 48+2);
@@ -220,6 +261,7 @@ static const char input_msg[] =
test_assert(parts->children->next->next->next->header_size.virtual_size == 23);
test_assert(parts->children->next->next->next->header_size.lines == 0);
for (part = parts->children; part != NULL; part = part->next) {
+ test_assert(part->children_count == 0);
test_assert(part->body_size.physical_size == 0);
test_assert(part->body_size.virtual_size == 0);
}
@@ -254,12 +296,13 @@ static const char input_msg[] =
pool = pool_alloconly_create("message parser", 10240);
input = test_istream_create(input_msg);
- parser = message_parser_init(pool, input, 0, 0);
+ parser = message_parser_init(pool, input, &set_empty);
while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ;
test_assert(ret < 0);
message_parser_deinit(&parser, &parts);
test_assert(parts->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
+ test_assert(parts->children_count == 2);
test_assert(parts->header_size.lines == 2);
test_assert(parts->header_size.physical_size == 46);
test_assert(parts->header_size.virtual_size == 46+2);
@@ -267,6 +310,7 @@ static const char input_msg[] =
test_assert(parts->body_size.physical_size == 86);
test_assert(parts->body_size.virtual_size == 86+8);
+ test_assert(parts->children->children_count == 0);
test_assert(parts->children->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
test_assert(parts->children->physical_pos == 51);
test_assert(parts->children->header_size.lines == 1);
@@ -276,6 +320,7 @@ static const char input_msg[] =
test_assert(parts->children->body_size.physical_size == 0);
test_assert(parts->children->children == NULL);
+ test_assert(parts->children->next->children_count == 0);
test_assert(parts->children->next->flags == (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_IS_MIME));
test_assert(parts->children->next->physical_pos == 101);
test_assert(parts->children->next->header_size.lines == 2);
@@ -307,11 +352,12 @@ static const char input_msg[] =
pool = pool_alloconly_create("message parser", 10240);
input = test_istream_create(input_msg);
- parser = message_parser_init(pool, input, 0, 0);
+ parser = message_parser_init(pool, input, &set_empty);
while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ;
test_assert(ret < 0);
message_parser_deinit(&parser, &parts);
+ test_assert(parts->children_count == 0);
test_assert(parts->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
test_assert(parts->header_size.lines == 1);
test_assert(parts->header_size.physical_size == 45);
@@ -344,11 +390,12 @@ static const char input_msg[] =
pool = pool_alloconly_create("message parser", 10240);
input = test_istream_create(input_msg);
- parser = message_parser_init(pool, input, 0, 0);
+ parser = message_parser_init(pool, input, &set_empty);
while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ;
test_assert(ret < 0);
message_parser_deinit(&parser, &parts);
+ test_assert(parts->children_count == 0);
test_assert(parts->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
test_assert(parts->header_size.lines == 2);
test_assert(parts->header_size.physical_size == 46);
@@ -388,11 +435,12 @@ static const char input_msg[] =
pool = pool_alloconly_create("message parser", 10240);
input = test_istream_create(input_msg);
- parser = message_parser_init(pool, input, 0, 0);
+ parser = message_parser_init(pool, input, &set_empty);
while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ;
test_assert(ret < 0);
message_parser_deinit(&parser, &parts);
+ test_assert(parts->children_count == 2);
test_assert(parts->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
test_assert(parts->header_size.lines == 2);
test_assert(parts->header_size.physical_size == 45);
@@ -400,6 +448,7 @@ static const char input_msg[] =
test_assert(parts->body_size.lines == 7);
test_assert(parts->body_size.physical_size == 84);
test_assert(parts->body_size.virtual_size == 84+7);
+ test_assert(parts->children->children_count == 1);
test_assert(parts->children->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
test_assert(parts->children->physical_pos == 49);
test_assert(parts->children->header_size.lines == 2);
@@ -408,6 +457,7 @@ static const char input_msg[] =
test_assert(parts->children->body_size.lines == 4);
test_assert(parts->children->body_size.physical_size == 35);
test_assert(parts->children->body_size.virtual_size == 35+4);
+ test_assert(parts->children->children->children_count == 0);
test_assert(parts->children->children->flags == (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_IS_MIME));
test_assert(parts->children->children->physical_pos == 98);
test_assert(parts->children->children->header_size.lines == 2);
@@ -446,11 +496,12 @@ static const char input_msg[] =
pool = pool_alloconly_create("message parser", 10240);
input = test_istream_create(input_msg);
- parser = message_parser_init(pool, input, 0, 0);
+ parser = message_parser_init(pool, input, &set_empty);
while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ;
test_assert(ret < 0);
message_parser_deinit(&parser, &parts);
+ test_assert(parts->children_count == 2);
test_assert(parts->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
test_assert(parts->header_size.lines == 2);
test_assert(parts->header_size.physical_size == 45);
@@ -458,6 +509,7 @@ static const char input_msg[] =
test_assert(parts->body_size.lines == 7);
test_assert(parts->body_size.physical_size == 86);
test_assert(parts->body_size.virtual_size == 86+7);
+ test_assert(parts->children->children_count == 1);
test_assert(parts->children->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
test_assert(parts->children->physical_pos == 50);
test_assert(parts->children->header_size.lines == 2);
@@ -466,6 +518,7 @@ static const char input_msg[] =
test_assert(parts->children->body_size.lines == 4);
test_assert(parts->children->body_size.physical_size == 36);
test_assert(parts->children->body_size.virtual_size == 36+4);
+ test_assert(parts->children->children->children_count == 0);
test_assert(parts->children->children->flags == (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_IS_MIME));
test_assert(parts->children->children->physical_pos == 100);
test_assert(parts->children->children->header_size.lines == 2);
@@ -481,6 +534,51 @@ static const char input_msg[] =
test_end();
}
+static void test_message_parser_trailing_dashes(void)
+{
+static const char input_msg[] =
+"Content-Type: multipart/mixed; boundary=\"a--\"\n"
+"\n"
+"--a--\n"
+"Content-Type: multipart/mixed; boundary=\"a----\"\n"
+"\n"
+"--a----\n"
+"Content-Type: text/plain\n"
+"\n"
+"body\n"
+"--a------\n"
+"Content-Type: text/html\n"
+"\n"
+"body2\n"
+"--a----";
+ struct message_parser_ctx *parser;
+ struct istream *input;
+ struct message_part *parts;
+ struct message_block block;
+ pool_t pool;
+ int ret;
+
+ test_begin("message parser trailing dashes");
+ pool = pool_alloconly_create("message parser", 10240);
+ input = test_istream_create(input_msg);
+
+ parser = message_parser_init(pool, input, &set_empty);
+ while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ;
+ test_assert(ret < 0);
+ message_parser_deinit(&parser, &parts);
+
+ test_assert(parts->children_count == 2);
+ test_assert(parts->children->next == NULL);
+ test_assert(parts->children->children_count == 1);
+ test_assert(parts->children->children->next == NULL);
+ test_assert(parts->children->children->children_count == 0);
+
+ test_parsed_parts(input, parts);
+ i_stream_unref(&input);
+ pool_unref(&pool);
+ test_end();
+}
+
static void test_message_parser_continuing_mime_boundary(void)
{
static const char input_msg[] =
@@ -504,11 +602,12 @@ static const char input_msg[] =
pool = pool_alloconly_create("message parser", 10240);
input = test_istream_create(input_msg);
- parser = message_parser_init(pool, input, 0, 0);
+ parser = message_parser_init(pool, input, &set_empty);
while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ;
test_assert(ret < 0);
message_parser_deinit(&parser, &parts);
+ test_assert(parts->children_count == 2);
test_assert(parts->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
test_assert(parts->header_size.lines == 2);
test_assert(parts->header_size.physical_size == 45);
@@ -516,6 +615,7 @@ static const char input_msg[] =
test_assert(parts->body_size.lines == 7);
test_assert(parts->body_size.physical_size == 86);
test_assert(parts->body_size.virtual_size == 86+7);
+ test_assert(parts->children->children_count == 1);
test_assert(parts->children->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
test_assert(parts->children->physical_pos == 49);
test_assert(parts->children->header_size.lines == 2);
@@ -524,6 +624,7 @@ static const char input_msg[] =
test_assert(parts->children->body_size.lines == 4);
test_assert(parts->children->body_size.physical_size == 36);
test_assert(parts->children->body_size.virtual_size == 36+4);
+ test_assert(parts->children->children->children_count == 0);
test_assert(parts->children->children->flags == (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_IS_MIME));
test_assert(parts->children->children->physical_pos == 100);
test_assert(parts->children->children->header_size.lines == 2);
@@ -563,12 +664,13 @@ static const char input_msg[] =
pool = pool_alloconly_create("message parser", 10240);
input = test_istream_create(input_msg);
- parser = message_parser_init(pool, input, 0, 0);
+ parser = message_parser_init(pool, input, &set_empty);
while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ;
test_assert(ret < 0);
message_parser_deinit(&parser, &parts);
part = parts;
+ test_assert(part->children_count == 3);
test_assert(part->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
test_assert(part->header_size.lines == 2);
test_assert(part->header_size.physical_size == 45);
@@ -578,6 +680,7 @@ static const char input_msg[] =
test_assert(part->body_size.virtual_size == 112+9);
part = parts->children;
+ test_assert(part->children_count == 0);
test_assert(part->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
test_assert(part->physical_pos == 49);
test_assert(part->header_size.lines == 1);
@@ -591,6 +694,7 @@ static const char input_msg[] =
we could make it, but it would complicate the message-parser even
more. */
part = parts->children->next;
+ test_assert(part->children_count == 0);
test_assert(part->flags == (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_IS_MIME));
test_assert(part->physical_pos == 117);
test_assert(part->header_size.lines == 1);
@@ -601,6 +705,7 @@ static const char input_msg[] =
test_assert(part->children == NULL);
part = parts->children->next->next;
+ test_assert(part->children_count == 0);
test_assert(part->flags == (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_IS_MIME));
test_assert(part->header_size.lines == 0);
test_assert(part->header_size.physical_size == 0);
@@ -615,6 +720,80 @@ static const char input_msg[] =
test_end();
}
+static void test_message_parser_continuing_mime_boundary_reverse(void)
+{
+static const char input_msg[] =
+"Content-Type: multipart/mixed; boundary=\"ab\"\n"
+"\n"
+"--ab\n"
+"Content-Type: multipart/mixed; boundary=\"a\"\n"
+"\n"
+"--a\n"
+"Content-Type: text/plain\n"
+"\n"
+"body\n"
+"--ab\n"
+"Content-Type: text/html\n"
+"\n"
+"body2\n";
+ struct message_parser_ctx *parser;
+ struct istream *input;
+ struct message_part *parts;
+ struct message_block block;
+ pool_t pool;
+ int ret;
+
+ test_begin("message parser continuing mime boundary reverse");
+ pool = pool_alloconly_create("message parser", 10240);
+ input = test_istream_create(input_msg);
+
+ parser = message_parser_init(pool, input, &set_empty);
+ while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ;
+ test_assert(ret < 0);
+ message_parser_deinit(&parser, &parts);
+
+ test_assert(parts->children_count == 3);
+ test_assert(parts->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
+ test_assert(parts->header_size.lines == 2);
+ test_assert(parts->header_size.physical_size == 46);
+ test_assert(parts->header_size.virtual_size == 46+2);
+ test_assert(parts->body_size.lines == 11);
+ test_assert(parts->body_size.physical_size == 121);
+ test_assert(parts->body_size.virtual_size == 121+11);
+ test_assert(parts->children->children_count == 1);
+ test_assert(parts->children->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
+ test_assert(parts->children->physical_pos == 51);
+ test_assert(parts->children->header_size.lines == 2);
+ test_assert(parts->children->header_size.physical_size == 45);
+ test_assert(parts->children->header_size.virtual_size == 45+2);
+ test_assert(parts->children->body_size.lines == 3);
+ test_assert(parts->children->body_size.physical_size == 34);
+ test_assert(parts->children->body_size.virtual_size == 34+3);
+ test_assert(parts->children->children->children_count == 0);
+ test_assert(parts->children->children->flags == (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_IS_MIME));
+ test_assert(parts->children->children->physical_pos == 100);
+ test_assert(parts->children->children->header_size.lines == 2);
+ test_assert(parts->children->children->header_size.physical_size == 26);
+ test_assert(parts->children->children->header_size.virtual_size == 26+2);
+ test_assert(parts->children->children->body_size.lines == 0);
+ test_assert(parts->children->children->body_size.physical_size == 4);
+ test_assert(parts->children->children->body_size.virtual_size == 4);
+ test_assert(parts->children->next->children_count == 0);
+ test_assert(parts->children->next->flags == (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_IS_MIME));
+ test_assert(parts->children->next->physical_pos == 136);
+ test_assert(parts->children->next->header_size.lines == 2);
+ test_assert(parts->children->next->header_size.physical_size == 25);
+ test_assert(parts->children->next->header_size.virtual_size == 25+2);
+ test_assert(parts->children->next->body_size.lines == 1);
+ test_assert(parts->children->next->body_size.physical_size == 6);
+ test_assert(parts->children->next->body_size.virtual_size == 6+1);
+
+ test_parsed_parts(input, parts);
+ i_stream_unref(&input);
+ pool_unref(&pool);
+ test_end();
+}
+
static void test_message_parser_no_eoh(void)
{
static const char input_msg[] = "a:b\n";
@@ -628,7 +807,7 @@ static void test_message_parser_no_eoh(v
pool = pool_alloconly_create("message parser", 10240);
input = test_istream_create(input_msg);
- parser = message_parser_init(pool, input, 0, 0);
+ parser = message_parser_init(pool, input, &set_empty);
test_assert(message_parser_parse_next_block(parser, &block) > 0 &&
block.hdr != NULL && strcmp(block.hdr->name, "a") == 0 &&
block.hdr->value_len == 1 && block.hdr->value[0] == 'b');
@@ -643,19 +822,335 @@ static void test_message_parser_no_eoh(v
test_end();
}
+static void test_message_parser_long_mime_boundary(void)
+{
+ /* Close the boundaries in wrong reverse order. But because all
+ boundaries are actually truncated to the same size (..890) it
+ works the same as if all of them were duplicate boundaries. */
+static const char input_msg[] =
+"Content-Type: multipart/mixed; boundary=\"1234567890123456789012345678901234567890123456789012345678901234567890123456789012\"\n"
+"\n"
+"--1234567890123456789012345678901234567890123456789012345678901234567890123456789012\n"
+"Content-Type: multipart/mixed; boundary=\"123456789012345678901234567890123456789012345678901234567890123456789012345678901\"\n"
+"\n"
+"--123456789012345678901234567890123456789012345678901234567890123456789012345678901\n"
+"Content-Type: multipart/mixed; boundary=\"12345678901234567890123456789012345678901234567890123456789012345678901234567890\"\n"
+"\n"
+"--12345678901234567890123456789012345678901234567890123456789012345678901234567890\n"
+"Content-Type: text/plain\n"
+"\n"
+"1\n"
+"--1234567890123456789012345678901234567890123456789012345678901234567890123456789012\n"
+"Content-Type: text/plain\n"
+"\n"
+"22\n"
+"--123456789012345678901234567890123456789012345678901234567890123456789012345678901\n"
+"Content-Type: text/plain\n"
+"\n"
+"333\n"
+"--12345678901234567890123456789012345678901234567890123456789012345678901234567890\n"
+"Content-Type: text/plain\n"
+"\n"
+"4444\n";
+ struct message_parser_ctx *parser;
+ struct istream *input;
+ struct message_part *parts, *part;
+ struct message_block block;
+ pool_t pool;
+ int ret;
+
+ test_begin("message parser long mime boundary");
+ pool = pool_alloconly_create("message parser", 10240);
+ input = test_istream_create(input_msg);
+
+ parser = message_parser_init(pool, input, &set_empty);
+ while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ;
+ test_assert(ret < 0);
+ message_parser_deinit(&parser, &parts);
+
+ part = parts;
+ test_assert(part->children_count == 6);
+ test_assert(part->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
+ test_assert(part->header_size.lines == 2);
+ test_assert(part->header_size.physical_size == 126);
+ test_assert(part->header_size.virtual_size == 126+2);
+ test_assert(part->body_size.lines == 22);
+ test_assert(part->body_size.physical_size == 871);
+ test_assert(part->body_size.virtual_size == 871+22);
+
+ part = parts->children;
+ test_assert(part->children_count == 5);
+ test_assert(part->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
+ test_assert(part->header_size.lines == 2);
+ test_assert(part->header_size.physical_size == 125);
+ test_assert(part->header_size.virtual_size == 125+2);
+ test_assert(part->body_size.lines == 19);
+ test_assert(part->body_size.physical_size == 661);
+ test_assert(part->body_size.virtual_size == 661+19);
+
+ part = parts->children->children;
+ test_assert(part->children_count == 4);
+ test_assert(part->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
+ test_assert(part->header_size.lines == 2);
+ test_assert(part->header_size.physical_size == 124);
+ test_assert(part->header_size.virtual_size == 124+2);
+ test_assert(part->body_size.lines == 16);
+ test_assert(part->body_size.physical_size == 453);
+ test_assert(part->body_size.virtual_size == 453+16);
+
+ part = parts->children->children->children;
+ for (unsigned int i = 1; i <= 3; i++, part = part->next) {
+ test_assert(part->children_count == 0);
+ test_assert(part->flags == (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_IS_MIME));
+ test_assert(part->header_size.lines == 2);
+ test_assert(part->header_size.physical_size == 26);
+ test_assert(part->header_size.virtual_size == 26+2);
+ test_assert(part->body_size.lines == 0);
+ test_assert(part->body_size.physical_size == i);
+ test_assert(part->body_size.virtual_size == i);
+ }
+
+ test_parsed_parts(input, parts);
+ i_stream_unref(&input);
+ pool_unref(&pool);
+ test_end();
+}
+
+static void test_message_parser_mime_part_nested_limit(void)
+{
+static const char input_msg[] =
+"Content-Type: multipart/mixed; boundary=\"1\"\n"
+"\n"
+"--1\n"
+"Content-Type: multipart/mixed; boundary=\"2\"\n"
+"\n"
+"--2\n"
+"Content-Type: text/plain\n"
+"\n"
+"1\n"
+"--2\n"
+"Content-Type: text/plain\n"
+"\n"
+"22\n"
+"--1\n"
+"Content-Type: text/plain\n"
+"\n"
+"333\n";
+ const struct message_parser_settings parser_set = {
+ .max_nested_mime_parts = 2,
+ };
+ struct message_parser_ctx *parser;
+ struct istream *input;
+ struct message_part *parts, *part;
+ struct message_block block;
+ pool_t pool;
+ int ret;
+
+ test_begin("message parser mime part nested limit");
+ pool = pool_alloconly_create("message parser", 10240);
+ input = test_istream_create(input_msg);
+
+ parser = message_parser_init(pool, input, &parser_set);
+ while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ;
+ test_assert(ret < 0);
+ message_parser_deinit(&parser, &parts);
+
+ part = parts;
+ test_assert(part->children_count == 2);
+ test_assert(part->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
+ test_assert(part->header_size.lines == 2);
+ test_assert(part->header_size.physical_size == 45);
+ test_assert(part->header_size.virtual_size == 45+2);
+ test_assert(part->body_size.lines == 15);
+ test_assert(part->body_size.physical_size == 148);
+ test_assert(part->body_size.virtual_size == 148+15);
+
+ part = parts->children;
+ test_assert(part->children_count == 0);
+ test_assert(part->flags == MESSAGE_PART_FLAG_IS_MIME);
+ test_assert(part->header_size.lines == 2);
+ test_assert(part->header_size.physical_size == 45);
+ test_assert(part->header_size.virtual_size == 45+2);
+ test_assert(part->body_size.lines == 7);
+ test_assert(part->body_size.physical_size == 64);
+ test_assert(part->body_size.virtual_size == 64+7);
+
+ part = parts->children->next;
+ test_assert(part->children_count == 0);
+ test_assert(part->flags == (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_IS_MIME));
+ test_assert(part->header_size.lines == 2);
+ test_assert(part->header_size.physical_size == 26);
+ test_assert(part->header_size.virtual_size == 26+2);
+ test_assert(part->body_size.lines == 1);
+ test_assert(part->body_size.physical_size == 4);
+ test_assert(part->body_size.virtual_size == 4+1);
+
+ test_parsed_parts(input, parts);
+ i_stream_unref(&input);
+ pool_unref(&pool);
+ test_end();
+}
+
+static void test_message_parser_mime_part_nested_limit_rfc822(void)
+{
+static const char input_msg[] =
+"Content-Type: message/rfc822\n"
+"\n"
+"Content-Type: message/rfc822\n"
+"\n"
+"Content-Type: text/plain\n"
+"\n"
+"1\n";
+ const struct message_parser_settings parser_set = {
+ .max_nested_mime_parts = 2,
+ };
+ struct message_parser_ctx *parser;
+ struct istream *input;
+ struct message_part *parts, *part;
+ struct message_block block;
+ pool_t pool;
+ int ret;
+
+ test_begin("message parser mime part nested limit rfc822");
+ pool = pool_alloconly_create("message parser", 10240);
+ input = test_istream_create(input_msg);
+
+ parser = message_parser_init(pool, input, &parser_set);
+ while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ;
+ test_assert(ret < 0);
+ message_parser_deinit(&parser, &parts);
+
+ part = parts;
+ test_assert(part->children_count == 1);
+ test_assert(part->flags == (MESSAGE_PART_FLAG_MESSAGE_RFC822 | MESSAGE_PART_FLAG_IS_MIME));
+ test_assert(part->header_size.lines == 2);
+ test_assert(part->header_size.physical_size == 30);
+ test_assert(part->header_size.virtual_size == 30+2);
+ test_assert(part->body_size.lines == 5);
+ test_assert(part->body_size.physical_size == 58);
+ test_assert(part->body_size.virtual_size == 58+5);
+
+ part = parts->children;
+ test_assert(part->children_count == 0);
+ test_assert(part->flags == MESSAGE_PART_FLAG_IS_MIME);
+ test_assert(part->header_size.lines == 2);
+ test_assert(part->header_size.physical_size == 30);
+ test_assert(part->header_size.virtual_size == 30+2);
+ test_assert(part->body_size.lines == 3);
+ test_assert(part->body_size.physical_size == 28);
+ test_assert(part->body_size.virtual_size == 28+3);
+
+ test_parsed_parts(input, parts);
+ i_stream_unref(&input);
+ pool_unref(&pool);
+ test_end();
+}
+
+static void test_message_parser_mime_part_limit(void)
+{
+static const char input_msg[] =
+"Content-Type: multipart/mixed; boundary=\"1\"\n"
+"\n"
+"--1\n"
+"Content-Type: multipart/mixed; boundary=\"2\"\n"
+"\n"
+"--2\n"
+"Content-Type: text/plain\n"
+"\n"
+"1\n"
+"--2\n"
+"Content-Type: text/plain\n"
+"\n"
+"22\n"
+"--1\n"
+"Content-Type: text/plain\n"
+"\n"
+"333\n";
+ const struct message_parser_settings parser_set = {
+ .max_total_mime_parts = 4,
+ };
+ struct message_parser_ctx *parser;
+ struct istream *input;
+ struct message_part *parts, *part;
+ struct message_block block;
+ pool_t pool;
+ int ret;
+
+ test_begin("message parser mime part limit");
+ pool = pool_alloconly_create("message parser", 10240);
+ input = test_istream_create(input_msg);
+
+ parser = message_parser_init(pool, input, &parser_set);
+ while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ;
+ test_assert(ret < 0);
+ message_parser_deinit(&parser, &parts);
+
+ part = parts;
+ test_assert(part->children_count == 3);
+ test_assert(part->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
+ test_assert(part->header_size.lines == 2);
+ test_assert(part->header_size.physical_size == 45);
+ test_assert(part->header_size.virtual_size == 45+2);
+ test_assert(part->body_size.lines == 15);
+ test_assert(part->body_size.physical_size == 148);
+ test_assert(part->body_size.virtual_size == 148+15);
+
+ part = parts->children;
+ test_assert(part->children_count == 2);
+ test_assert(part->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
+ test_assert(part->header_size.lines == 2);
+ test_assert(part->header_size.physical_size == 45);
+ test_assert(part->header_size.virtual_size == 45+2);
+ test_assert(part->body_size.lines == 12);
+ test_assert(part->body_size.physical_size == 99);
+ test_assert(part->body_size.virtual_size == 99+12);
+
+ part = parts->children->children;
+ test_assert(part->children_count == 0);
+ test_assert(part->flags == (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_IS_MIME));
+ test_assert(part->header_size.lines == 2);
+ test_assert(part->header_size.physical_size == 26);
+ test_assert(part->header_size.virtual_size == 26+2);
+ test_assert(part->body_size.lines == 0);
+ test_assert(part->body_size.physical_size == 1);
+ test_assert(part->body_size.virtual_size == 1);
+
+ part = parts->children->children->next;
+ test_assert(part->children_count == 0);
+ test_assert(part->flags == (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_IS_MIME));
+ test_assert(part->header_size.lines == 2);
+ test_assert(part->header_size.physical_size == 26);
+ test_assert(part->header_size.virtual_size == 26+2);
+ test_assert(part->body_size.lines == 5);
+ test_assert(part->body_size.physical_size == 37);
+ test_assert(part->body_size.virtual_size == 37+5);
+
+ test_parsed_parts(input, parts);
+ i_stream_unref(&input);
+ pool_unref(&pool);
+ test_end();
+}
+
int main(void)
{
static void (*const test_functions[])(void) = {
test_message_parser_small_blocks,
+ test_message_parser_stop_early,
test_message_parser_truncated_mime_headers,
test_message_parser_truncated_mime_headers2,
test_message_parser_truncated_mime_headers3,
test_message_parser_empty_multipart,
test_message_parser_duplicate_mime_boundary,
test_message_parser_garbage_suffix_mime_boundary,
+ test_message_parser_trailing_dashes,
test_message_parser_continuing_mime_boundary,
test_message_parser_continuing_truncated_mime_boundary,
+ test_message_parser_continuing_mime_boundary_reverse,
+ test_message_parser_long_mime_boundary,
test_message_parser_no_eoh,
+ test_message_parser_mime_part_nested_limit,
+ test_message_parser_mime_part_nested_limit_rfc822,
+ test_message_parser_mime_part_limit,
NULL
};
return test_run(test_functions);
diff -up dovecot-2.3.8/src/lib-mail/test-message-part.c.CVE_2020_12100 dovecot-2.3.8/src/lib-mail/test-message-part.c
--- dovecot-2.3.8/src/lib-mail/test-message-part.c.CVE_2020_12100 2019-10-08 10:46:18.000000000 +0200
+++ dovecot-2.3.8/src/lib-mail/test-message-part.c 2020-08-07 12:29:39.976827472 +0200
@@ -65,6 +65,7 @@ static const char test_msg[] =
static void test_message_part_idx(void)
{
+ const struct message_parser_settings set = { .flags = 0 };
struct message_parser_ctx *parser;
struct istream *input;
struct message_part *parts, *part, *prev_part;
@@ -77,7 +78,7 @@ static void test_message_part_idx(void)
pool = pool_alloconly_create("message parser", 10240);
input = i_stream_create_from_data(test_msg, TEST_MSG_LEN);
- parser = message_parser_init(pool, input, 0, 0);
+ parser = message_parser_init(pool, input, &set);
while ((ret = message_parser_parse_next_block(parser, &block)) > 0) {
part_idx = message_part_to_idx(block.part);
test_assert(part_idx >= prev_idx);
diff -up dovecot-2.3.8/src/lib-storage/index/index-mail-headers.c.CVE_2020_12100 dovecot-2.3.8/src/lib-storage/index/index-mail-headers.c
--- dovecot-2.3.8/src/lib-storage/index/index-mail-headers.c.CVE_2020_12100 2019-10-08 10:46:18.000000000 +0200
+++ dovecot-2.3.8/src/lib-storage/index/index-mail-headers.c 2020-08-07 12:31:09.948576696 +0200
@@ -16,11 +16,11 @@
#include "index-storage.h"
#include "index-mail.h"
-static const enum message_header_parser_flags hdr_parser_flags =
- MESSAGE_HEADER_PARSER_FLAG_SKIP_INITIAL_LWSP |
- MESSAGE_HEADER_PARSER_FLAG_DROP_CR;
-static const enum message_parser_flags msg_parser_flags =
- MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK;
+static const struct message_parser_settings msg_parser_set = {
+ .hdr_flags = MESSAGE_HEADER_PARSER_FLAG_SKIP_INITIAL_LWSP |
+ MESSAGE_HEADER_PARSER_FLAG_DROP_CR,
+ .flags = MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK,
+};
static int header_line_cmp(const struct index_mail_line *l1,
const struct index_mail_line *l2)
@@ -397,7 +397,7 @@ index_mail_cache_parse_init(struct mail
mail->data.parser_input = input;
mail->data.parser_ctx =
message_parser_init(mail->mail.data_pool, input,
- hdr_parser_flags, msg_parser_flags);
+ &msg_parser_set);
i_stream_unref(&input);
return input2;
}
@@ -426,14 +426,12 @@ static void index_mail_init_parser(struc
data->parser_input = data->stream;
data->parser_ctx = message_parser_init(mail->mail.data_pool,
data->stream,
- hdr_parser_flags,
- msg_parser_flags);
+ &msg_parser_set);
} else {
data->parser_ctx =
message_parser_init_from_parts(data->parts,
data->stream,
- hdr_parser_flags,
- msg_parser_flags);
+ &msg_parser_set);
}
}
@@ -466,7 +464,7 @@ int index_mail_parse_headers(struct inde
i_assert(!data->save_bodystructure_body ||
data->parser_ctx != NULL);
message_parse_header(data->stream, &data->hdr_size,
- hdr_parser_flags,
+ msg_parser_set.hdr_flags,
index_mail_parse_header_cb, mail);
}
if (index_mail_stream_check_failure(mail) < 0)
@@ -521,7 +519,7 @@ int index_mail_headers_get_envelope(stru
if (mail->data.envelope == NULL && stream != NULL) {
/* we got the headers from cache - parse them to get the
envelope */
- message_parse_header(stream, NULL, hdr_parser_flags,
+ message_parse_header(stream, NULL, msg_parser_set.hdr_flags,
imap_envelope_parse_callback, mail);
if (stream->stream_errno != 0) {
index_mail_stream_log_failure_for(mail, stream);
diff -up dovecot-2.3.8/src/plugins/fts/fts-build-mail.c.CVE_2020_12100 dovecot-2.3.8/src/plugins/fts/fts-build-mail.c
--- dovecot-2.3.8/src/plugins/fts/fts-build-mail.c.CVE_2020_12100 2019-10-08 10:46:18.000000000 +0200
+++ dovecot-2.3.8/src/plugins/fts/fts-build-mail.c 2020-08-07 12:29:39.977827458 +0200
@@ -475,6 +475,9 @@ fts_build_mail_real(struct fts_backend_u
const char **retriable_err_msg_r,
bool *may_need_retry_r)
{
+ const struct message_parser_settings parser_set = {
+ .hdr_flags = MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE,
+ };
struct fts_mail_build_context ctx;
struct istream *input;
struct message_parser_ctx *parser;
@@ -503,9 +506,7 @@ fts_build_mail_real(struct fts_backend_u
ctx.pending_input = buffer_create_dynamic(default_pool, 128);
prev_part = NULL;
- parser = message_parser_init(pool_datastack_create(), input,
- MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE,
- 0);
+ parser = message_parser_init(pool_datastack_create(), input, &parser_set);
decoder = message_decoder_init(update_ctx->normalizer, 0);
for (;;) {