diff -up dovecot-2.3.8/src/lib-mail/message-decoder.c.CVE_2020_12100prereq dovecot-2.3.8/src/lib-mail/message-decoder.c --- dovecot-2.3.8/src/lib-mail/message-decoder.c.CVE_2020_12100prereq 2019-10-08 10:46:18.000000000 +0200 +++ dovecot-2.3.8/src/lib-mail/message-decoder.c 2020-08-07 17:48:58.320126698 +0200 @@ -13,9 +13,6 @@ #include "message-header-decode.h" #include "message-decoder.h" -/* base64 takes max 4 bytes per character, q-p takes max 3. */ -#define MAX_ENCODING_BUF_SIZE 3 - struct message_decoder_context { enum message_decoder_flags flags; normalizer_func_t *normalizer; @@ -30,7 +27,7 @@ struct message_decoder_context { size_t translation_size; struct qp_decoder *qp; - buffer_t *encoding_buf; + struct base64_decoder base64_decoder; char *content_type, *content_charset; enum message_cte message_cte; @@ -53,7 +50,7 @@ message_decoder_init(normalizer_func_t * ctx->normalizer = normalizer; ctx->buf = buffer_create_dynamic(default_pool, 8192); ctx->buf2 = buffer_create_dynamic(default_pool, 8192); - ctx->encoding_buf = buffer_create_dynamic(default_pool, 128); + base64_decode_init(&ctx->base64_decoder, &base64_scheme, 0); return ctx; } @@ -68,7 +65,6 @@ void message_decoder_deinit(struct messa if (ctx->qp != NULL) qp_decoder_deinit(&ctx->qp); - buffer_free(&ctx->encoding_buf); buffer_free(&ctx->buf); buffer_free(&ctx->buf2); i_free(ctx->charset_trans_charset); @@ -273,14 +269,9 @@ static bool message_decode_body(struct m struct message_block *input, struct message_block *output) { - struct base64_decoder b64dec; const unsigned char *data = NULL; - size_t pos = 0, size = 0; + size_t pos, size = 0; const char *error; - int ret; - - if (ctx->encoding_buf->used != 0) - buffer_append(ctx->encoding_buf, input->data, input->size); switch (ctx->message_cte) { case MESSAGE_CTE_UNKNOWN: @@ -289,12 +280,10 @@ static bool message_decode_body(struct m case MESSAGE_CTE_78BIT: case MESSAGE_CTE_BINARY: - i_assert(ctx->encoding_buf->used == 0); data = input->data; - size = pos = input->size; + size = input->size; break; case MESSAGE_CTE_QP: { - i_assert(ctx->encoding_buf->used == 0); buffer_set_used_size(ctx->buf, 0); if (ctx->qp == NULL) ctx->qp = qp_decoder_init(ctx->buf); @@ -302,45 +291,24 @@ static bool message_decode_body(struct m &pos, &error); data = ctx->buf->data; size = ctx->buf->used; - /* eat away all input. qp-decoder buffers it internally. */ - pos = input->size; break; } case MESSAGE_CTE_BASE64: buffer_set_used_size(ctx->buf, 0); - base64_decode_init(&b64dec, &base64_scheme, 0); - if (ctx->encoding_buf->used != 0) { - ret = base64_decode_more(&b64dec, - ctx->encoding_buf->data, - ctx->encoding_buf->used, - &pos, ctx->buf); - } else { - ret = base64_decode_more(&b64dec, - input->data, input->size, - &pos, ctx->buf); - } - if (ret < 0 || base64_decode_finish(&b64dec) < 0) { - /* corrupted base64 data, don't bother with - the rest of it */ - return FALSE; - } - if (ret == 0) { - /* end of base64 input */ - pos = input->size; - buffer_set_used_size(ctx->encoding_buf, 0); + if (!base64_decode_is_finished(&ctx->base64_decoder)) { + if (base64_decode_more(&ctx->base64_decoder, + input->data, input->size, + &pos, ctx->buf) <= 0) { + /* ignore the rest of the input in this + MIME part */ + (void)base64_decode_finish(&ctx->base64_decoder); + } } data = ctx->buf->data; size = ctx->buf->used; break; } - if (ctx->encoding_buf->used != 0) - buffer_delete(ctx->encoding_buf, 0, pos); - else if (pos != input->size) { - buffer_append(ctx->encoding_buf, - input->data + pos, input->size - pos); - } - if (ctx->binary_input) { output->data = data; output->size = size; @@ -402,10 +370,11 @@ void message_decoder_decode_reset(struct { const char *error; + base64_decode_reset(&ctx->base64_decoder); + if (ctx->qp != NULL) (void)qp_decoder_finish(ctx->qp, &error); i_free_and_null(ctx->content_type); i_free_and_null(ctx->content_charset); ctx->message_cte = MESSAGE_CTE_78BIT; - buffer_set_used_size(ctx->encoding_buf, 0); } diff -up dovecot-2.3.8/src/lib-mail/test-message-decoder.c.CVE_2020_12100prereq dovecot-2.3.8/src/lib-mail/test-message-decoder.c --- dovecot-2.3.8/src/lib-mail/test-message-decoder.c.CVE_2020_12100prereq 2019-10-08 10:46:18.000000000 +0200 +++ dovecot-2.3.8/src/lib-mail/test-message-decoder.c 2020-08-07 17:50:04.612248484 +0200 @@ -1,7 +1,8 @@ /* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */ #include "lib.h" -#include "buffer.h" +#include "str.h" +#include "istream.h" #include "charset-utf8.h" #include "message-parser.h" #include "message-header-decode.h" @@ -82,6 +83,66 @@ static void test_message_decoder(void) test_end(); } +static void test_message_decoder_multipart(void) +{ + static const char test_message_input[] = + "Content-Type: multipart/mixed; boundary=foo\n" + "\n" + "--foo\n" + "Content-Transfer-Encoding: quoted-printable\n" + "Content-Type: text/plain; charset=utf-8\n" + "\n" + "p=C3=A4iv=C3=A4=C3=A4\n" + "\n" + "--foo\n" + "Content-Transfer-Encoding: base64\n" + "Content-Type: text/plain; charset=utf-8\n" + "\n" + "ecO2dMOkIHZhYW4uCg== ignored\n" + "--foo\n" + "Content-Transfer-Encoding: base64\n" + "Content-Type: text/plain; charset=utf-8\n" + "\n" + "?garbage\n" + "--foo--\n"; + struct message_parser_ctx *parser; + struct message_decoder_context *decoder; + struct message_part *parts; + struct message_block input, output; + struct istream *istream; + string_t *str_out = t_str_new(20); + int ret; + + test_begin("message decoder multipart"); + + istream = test_istream_create(test_message_input); + parser = message_parser_init(pool_datastack_create(), istream, 0, 0); + decoder = message_decoder_init(NULL, 0); + + test_istream_set_allow_eof(istream, FALSE); + for (size_t i = 0; i < sizeof(test_message_input); i++) { + if (i == sizeof(test_message_input)-1) + test_istream_set_allow_eof(istream, TRUE); + test_istream_set_size(istream, i); + while ((ret = message_parser_parse_next_block(parser, &input)) > 0) { + if (message_decoder_decode_next_block(decoder, &input, &output) && + output.hdr == NULL && output.size > 0) + str_append_data(str_out, output.data, output.size); + } + if (i == sizeof(test_message_input)-1) + test_assert(ret == -1); + else + test_assert(ret == 0); + } + /* NOTE: qp-decoder decoder changes \n into \r\n */ + test_assert_strcmp(str_c(str_out), "p\xC3\xA4iv\xC3\xA4\xC3\xA4\r\ny\xC3\xB6t\xC3\xA4 vaan.\n"); + + message_decoder_deinit(&decoder); + message_parser_deinit(&parser, &parts); + i_stream_unref(&istream); + test_end(); +} + static void test_message_decoder_current_content_type(void) { struct message_decoder_context *ctx; @@ -149,6 +210,7 @@ int main(void) { static void (*const test_functions[])(void) = { test_message_decoder, + test_message_decoder_multipart, test_message_decoder_current_content_type, NULL };