Blame SOURCES/dovecot-2.3.8-CVE_2020_12100prereq.patch

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