fix CVE-2024-23184: using a large number of address headers may trigger a denial of service (RHEL-55212)
fix CVE-2024-23185: very large headers can cause resource exhaustion when parsing message (RHEL-55224) Resolves: RHEL-55224
This commit is contained in:
		
							parent
							
								
									4ac09ac3cb
								
							
						
					
					
						commit
						74f35bf31a
					
				
							
								
								
									
										976
									
								
								dovecot-2.3.21.1-CVE-2024-23184.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										976
									
								
								dovecot-2.3.21.1-CVE-2024-23184.patch
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,976 @@ | ||||
| From 8e4c42dbb3c770fcdbc396f2abcf1bc228ec548d Mon Sep 17 00:00:00 2001 | ||||
| From: Timo Sirainen <timo.sirainen@open-xchange.com> | ||||
| Date: Fri, 9 Feb 2024 00:32:39 +0200 | ||||
| Subject: [PATCH 1/6] lib: test-llist - Fix dllist2 test name | ||||
| 
 | ||||
| ---
 | ||||
|  src/lib/test-llist.c | 2 +- | ||||
|  1 file changed, 1 insertion(+), 1 deletion(-) | ||||
| 
 | ||||
| diff --git a/src/lib/test-llist.c b/src/lib/test-llist.c
 | ||||
| index d57006ce2aa..ed584318fa3 100644
 | ||||
| --- a/src/lib/test-llist.c
 | ||||
| +++ b/src/lib/test-llist.c
 | ||||
| @@ -71,7 +71,7 @@ static void test_dllist2(void)
 | ||||
|  	l2 = t_new(struct dllist, 1); | ||||
|  	l1 = t_new(struct dllist, 1); | ||||
|   | ||||
| -	test_begin("dllist");
 | ||||
| +	test_begin("dllist2");
 | ||||
|  	/* prepend to empty */ | ||||
|  	DLLIST2_PREPEND(&head, &tail, l3); | ||||
|  	test_assert(head == l3 && tail == l3); | ||||
| 
 | ||||
| From cee08202c759a3bdf185d998dcf888ebd1bc6e36 Mon Sep 17 00:00:00 2001 | ||||
| From: Timo Sirainen <timo.sirainen@open-xchange.com> | ||||
| Date: Fri, 9 Feb 2024 00:33:00 +0200 | ||||
| Subject: [PATCH 2/6] lib: Add DLLIST2_JOIN() | ||||
| 
 | ||||
| ---
 | ||||
|  src/lib/llist.h      | 14 ++++++++++++++ | ||||
|  src/lib/test-llist.c | 39 +++++++++++++++++++++++++++++++++++++++ | ||||
|  2 files changed, 53 insertions(+) | ||||
| 
 | ||||
| diff --git a/src/lib/llist.h b/src/lib/llist.h
 | ||||
| index 8a52e873352..5ad5d75c0df 100644
 | ||||
| --- a/src/lib/llist.h
 | ||||
| +++ b/src/lib/llist.h
 | ||||
| @@ -78,4 +78,18 @@
 | ||||
|  #define DLLIST2_REMOVE(head, tail, item) \ | ||||
|  	DLLIST2_REMOVE_FULL(head, tail, item, prev, next) | ||||
|   | ||||
| +#define DLLIST2_JOIN_FULL(head1, tail1, head2, tail2, prev, next) STMT_START { \
 | ||||
| +	if (*(head1) == NULL) { \
 | ||||
| +		*(head1) = *(head2); \
 | ||||
| +		*(tail1) = *(tail2); \
 | ||||
| +	} else if (*(head2) != NULL) { \
 | ||||
| +		(*(tail1))->next = *(head2); \
 | ||||
| +		(*(head2))->prev = *(tail1); \
 | ||||
| +		(*tail1) = (*tail2); \
 | ||||
| +	} \
 | ||||
| +	} STMT_END
 | ||||
| +
 | ||||
| +#define DLLIST2_JOIN(head1, tail1, head2, tail2) \
 | ||||
| +	DLLIST2_JOIN_FULL(head1, tail1, head2, tail2, prev, next)
 | ||||
| +
 | ||||
|  #endif | ||||
| diff --git a/src/lib/test-llist.c b/src/lib/test-llist.c
 | ||||
| index ed584318fa3..e293eb6a603 100644
 | ||||
| --- a/src/lib/test-llist.c
 | ||||
| +++ b/src/lib/test-llist.c
 | ||||
| @@ -131,8 +131,47 @@ static void test_dllist2(void)
 | ||||
|  	test_end(); | ||||
|  } | ||||
|   | ||||
| +static void test_dllist2_join(void)
 | ||||
| +{
 | ||||
| +	struct dllist *head, *tail, *elem[4];
 | ||||
| +	struct dllist *head2, *tail2, *elem2[N_ELEMENTS(elem)];
 | ||||
| +
 | ||||
| +	test_begin("dllist2 join");
 | ||||
| +	for (unsigned int i = 0; i < N_ELEMENTS(elem); i++) {
 | ||||
| +		elem[i] = t_new(struct dllist, 1);
 | ||||
| +		elem2[i] = t_new(struct dllist, 1);
 | ||||
| +	}
 | ||||
| +	for (unsigned int i = 0; i < N_ELEMENTS(elem); i++) {
 | ||||
| +		for (unsigned int j = 0; j < N_ELEMENTS(elem2); j++) {
 | ||||
| +			head = tail = head2 = tail2 = NULL;
 | ||||
| +			for (unsigned int n = 0; n < i; n++)
 | ||||
| +				DLLIST2_APPEND(&head, &tail, elem[n]);
 | ||||
| +			for (unsigned int n = 0; n < j; n++)
 | ||||
| +				DLLIST2_APPEND(&head2, &tail2, elem2[n]);
 | ||||
| +			DLLIST2_JOIN(&head, &tail, &head2, &tail2);
 | ||||
| +
 | ||||
| +			/* verify */
 | ||||
| +			struct dllist *tmp = head, *last = NULL;
 | ||||
| +			for (unsigned int n = 0; n < i; n++) {
 | ||||
| +				test_assert(tmp == elem[n]);
 | ||||
| +				last = tmp;
 | ||||
| +				tmp = tmp->next;
 | ||||
| +			}
 | ||||
| +			for (unsigned int n = 0; n < j; n++) {
 | ||||
| +				test_assert(tmp == elem2[n]);
 | ||||
| +				last = tmp;
 | ||||
| +				tmp = tmp->next;
 | ||||
| +			}
 | ||||
| +			test_assert(tmp == NULL);
 | ||||
| +			test_assert(tail == last);
 | ||||
| +		}
 | ||||
| +	}
 | ||||
| +	test_end();
 | ||||
| +}
 | ||||
| +
 | ||||
|  void test_llist(void) | ||||
|  { | ||||
|  	test_dllist(); | ||||
|  	test_dllist2(); | ||||
| +	test_dllist2_join();
 | ||||
|  } | ||||
| 
 | ||||
| From 0bae091859c905dc335f21eed01347e6b8338672 Mon Sep 17 00:00:00 2001 | ||||
| From: Timo Sirainen <timo.sirainen@open-xchange.com> | ||||
| Date: Tue, 30 Jan 2024 22:42:50 +0200 | ||||
| Subject: [PATCH 3/6] lib-mail: test-imap-envelope - Use test_assert_idx() | ||||
|  where possible | ||||
| 
 | ||||
| ---
 | ||||
|  src/lib-imap/test-imap-envelope.c | 6 +++--- | ||||
|  1 file changed, 3 insertions(+), 3 deletions(-) | ||||
| 
 | ||||
| diff --git a/src/lib-imap/test-imap-envelope.c b/src/lib-imap/test-imap-envelope.c
 | ||||
| index 1f295e58bab..c9b92b4be2b 100644
 | ||||
| --- a/src/lib-imap/test-imap-envelope.c
 | ||||
| +++ b/src/lib-imap/test-imap-envelope.c
 | ||||
| @@ -157,7 +157,7 @@ static void test_imap_envelope_write(void)
 | ||||
|  		envlp = msg_parse(pool, test->message); | ||||
|   | ||||
|  		imap_envelope_write(envlp, str); | ||||
| -		test_assert(strcmp(str_c(str), test->envelope) == 0);
 | ||||
| +		test_assert_idx(strcmp(str_c(str), test->envelope) == 0, i);
 | ||||
|   | ||||
|  		pool_unref(&pool); | ||||
|  		test_end(); | ||||
| @@ -179,12 +179,12 @@ static void test_imap_envelope_parse(void)
 | ||||
|  		test_begin(t_strdup_printf("imap envelope parser [%u]", i)); | ||||
|   | ||||
|  		ret = imap_envelope_parse(test->envelope, pool, &envlp, &error); | ||||
| -		test_assert(ret);
 | ||||
| +		test_assert_idx(ret, i);
 | ||||
|   | ||||
|  		if (ret) { | ||||
|  			str_truncate(str, 0); | ||||
|  			imap_envelope_write(envlp, str); | ||||
| -			test_assert(strcmp(str_c(str), test->envelope) == 0);
 | ||||
| +			test_assert_idx(strcmp(str_c(str), test->envelope) == 0, i);
 | ||||
|  		} else { | ||||
|  			i_error("Invalid envelope: %s", error); | ||||
|  		} | ||||
| 
 | ||||
| From a1c9b0409454e45937bf7e9c3685f5e91d6a5a43 Mon Sep 17 00:00:00 2001 | ||||
| From: Timo Sirainen <timo.sirainen@open-xchange.com> | ||||
| Date: Sun, 4 Feb 2024 00:26:57 +0200 | ||||
| Subject: [PATCH 4/6] lib-mail: Change message_address to be doubly linked list | ||||
| 
 | ||||
| ---
 | ||||
|  src/lib-imap/imap-envelope.c        |  11 +- | ||||
|  src/lib-mail/message-address.c      |   8 +- | ||||
|  src/lib-mail/message-address.h      |   2 +- | ||||
|  src/lib-mail/test-message-address.c | 226 ++++++++++++++-------------- | ||||
|  4 files changed, 121 insertions(+), 126 deletions(-) | ||||
| 
 | ||||
| diff --git a/src/lib-imap/imap-envelope.c b/src/lib-imap/imap-envelope.c
 | ||||
| index 87297f4f691..1312eae2ff3 100644
 | ||||
| --- a/src/lib-imap/imap-envelope.c
 | ||||
| +++ b/src/lib-imap/imap-envelope.c
 | ||||
| @@ -1,6 +1,7 @@
 | ||||
|  /* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ | ||||
|   | ||||
|  #include "lib.h" | ||||
| +#include "llist.h"
 | ||||
|  #include "istream.h" | ||||
|  #include "str.h" | ||||
|  #include "message-address.h" | ||||
| @@ -127,7 +128,7 @@ static bool
 | ||||
|  imap_envelope_parse_addresses(const struct imap_arg *arg, | ||||
|  	pool_t pool, struct message_address **addrs_r) | ||||
|  { | ||||
| -	struct message_address *first, *addr, *prev;
 | ||||
| +	struct message_address *first, *last, *addr;
 | ||||
|  	const struct imap_arg *list_args; | ||||
|   | ||||
|  	if (arg->type == IMAP_ARG_NIL) { | ||||
| @@ -138,16 +139,12 @@ imap_envelope_parse_addresses(const struct imap_arg *arg,
 | ||||
|  	if (!imap_arg_get_list(arg, &list_args)) | ||||
|  		return FALSE; | ||||
|   | ||||
| -	first = addr = prev = NULL;
 | ||||
| +	first = last = addr = NULL;
 | ||||
|  	for (; !IMAP_ARG_IS_EOL(list_args); list_args++) { | ||||
|  		if (!imap_envelope_parse_address | ||||
|  			(list_args, pool, &addr)) | ||||
|  			return FALSE; | ||||
| -		if (first == NULL)
 | ||||
| -			first = addr;
 | ||||
| -		if (prev != NULL)
 | ||||
| -			prev->next = addr;
 | ||||
| -		prev = addr;
 | ||||
| +		DLLIST2_APPEND(&first, &last, addr);
 | ||||
|  	} | ||||
|   | ||||
|  	*addrs_r = first; | ||||
| diff --git a/src/lib-mail/message-address.c b/src/lib-mail/message-address.c
 | ||||
| index fb06afae7b7..9d192799468 100644
 | ||||
| --- a/src/lib-mail/message-address.c
 | ||||
| +++ b/src/lib-mail/message-address.c
 | ||||
| @@ -1,6 +1,7 @@
 | ||||
|  /* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ | ||||
|   | ||||
|  #include "lib.h" | ||||
| +#include "llist.h"
 | ||||
|  #include "str.h" | ||||
|  #include "strescape.h" | ||||
|  #include "smtp-address.h" | ||||
| @@ -27,11 +28,7 @@ static void add_address(struct message_address_parser_context *ctx)
 | ||||
|  	memcpy(addr, &ctx->addr, sizeof(ctx->addr)); | ||||
|  	i_zero(&ctx->addr); | ||||
|   | ||||
| -	if (ctx->first_addr == NULL)
 | ||||
| -		ctx->first_addr = addr;
 | ||||
| -	else
 | ||||
| -		ctx->last_addr->next = addr;
 | ||||
| -	ctx->last_addr = addr;
 | ||||
| +	DLLIST2_APPEND(&ctx->first_addr, &ctx->last_addr, addr);
 | ||||
|  } | ||||
|   | ||||
|  /* quote with "" and escape all '\', '"' and "'" characters if need */ | ||||
| @@ -631,6 +628,7 @@ const char *message_address_first_to_string(const struct message_address *addr)
 | ||||
|  	struct message_address first_addr; | ||||
|   | ||||
|  	first_addr = *addr; | ||||
| +	first_addr.prev = NULL;
 | ||||
|  	first_addr.next = NULL; | ||||
|  	first_addr.route = NULL; | ||||
|  	return message_address_to_string(&first_addr); | ||||
| diff --git a/src/lib-mail/message-address.h b/src/lib-mail/message-address.h
 | ||||
| index 8370397741c..85cff3dcc6f 100644
 | ||||
| --- a/src/lib-mail/message-address.h
 | ||||
| +++ b/src/lib-mail/message-address.h
 | ||||
| @@ -18,7 +18,7 @@ enum message_address_parse_flags {
 | ||||
|     {name = NULL, NULL, "group", NULL}, ..., {NULL, NULL, NULL, NULL} | ||||
|  */ | ||||
|  struct message_address { | ||||
| -	struct message_address *next;
 | ||||
| +	struct message_address *prev, *next;
 | ||||
|   | ||||
|  	/* display-name */ | ||||
|  	const char *name; | ||||
| diff --git a/src/lib-mail/test-message-address.c b/src/lib-mail/test-message-address.c
 | ||||
| index e6204bb0588..261cbfba70a 100644
 | ||||
| --- a/src/lib-mail/test-message-address.c
 | ||||
| +++ b/src/lib-mail/test-message-address.c
 | ||||
| @@ -47,174 +47,174 @@ static void test_message_address(void)
 | ||||
|  	} tests[] = { | ||||
|  		/* user@domain -> <user@domain> */ | ||||
|  		{ "user@domain", "<user@domain>", NULL, | ||||
| -		  { NULL, NULL, NULL, "user", "domain", FALSE },
 | ||||
| -		  { NULL, NULL, NULL, "user", "domain", FALSE }, 0 },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "user", "domain", FALSE },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "user", "domain", FALSE }, 0 },
 | ||||
|  		{ "\"user\"@domain", "<user@domain>", NULL, | ||||
| -		  { NULL, NULL, NULL, "user", "domain", FALSE },
 | ||||
| -		  { NULL, NULL, NULL, "user", "domain", FALSE }, 0 },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "user", "domain", FALSE },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "user", "domain", FALSE }, 0 },
 | ||||
|  		{ "\"user name\"@domain", "<\"user name\"@domain>", NULL, | ||||
| -		  { NULL, NULL, NULL, "user name", "domain", FALSE },
 | ||||
| -		  { NULL, NULL, NULL, "user name", "domain", FALSE }, 0 },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "user name", "domain", FALSE },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "user name", "domain", FALSE }, 0 },
 | ||||
|  		{ "\"user@na\\\\me\"@domain", "<\"user@na\\\\me\"@domain>", NULL, | ||||
| -		  { NULL, NULL, NULL, "user@na\\me", "domain", FALSE },
 | ||||
| -		  { NULL, NULL, NULL, "user@na\\me", "domain", FALSE }, 0 },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "user@na\\me", "domain", FALSE },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "user@na\\me", "domain", FALSE }, 0 },
 | ||||
|  		{ "\"user\\\"name\"@domain", "<\"user\\\"name\"@domain>", NULL, | ||||
| -		  { NULL, NULL, NULL, "user\"name", "domain", FALSE },
 | ||||
| -		  { NULL, NULL, NULL, "user\"name", "domain", FALSE }, 0 },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "user\"name", "domain", FALSE },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "user\"name", "domain", FALSE }, 0 },
 | ||||
|  		{ "\"\"@domain", "<\"\"@domain>", NULL, | ||||
| -		  { NULL, NULL, NULL, "", "domain", FALSE },
 | ||||
| -		  { NULL, NULL, NULL, "", "domain", FALSE }, 0 },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "", "domain", FALSE },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "", "domain", FALSE }, 0 },
 | ||||
|  		{ "user", "<user>", "<user@MISSING_DOMAIN>", | ||||
| -		  { NULL, NULL, NULL, "user", "", TRUE },
 | ||||
| -		  { NULL, NULL, NULL, "user", "MISSING_DOMAIN", TRUE }, 0 },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "user", "", TRUE },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "user", "MISSING_DOMAIN", TRUE }, 0 },
 | ||||
|  		{ "@domain", "<\"\"@domain>", "<MISSING_MAILBOX@domain>", | ||||
| -		  { NULL, NULL, NULL, "", "domain", TRUE },
 | ||||
| -		  { NULL, NULL, NULL, "MISSING_MAILBOX", "domain", TRUE }, 0 },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "", "domain", TRUE },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "MISSING_MAILBOX", "domain", TRUE }, 0 },
 | ||||
|   | ||||
|  		/* Display Name -> Display Name */ | ||||
|  		{ "Display Name", "\"Display Name\"", "\"Display Name\" <MISSING_MAILBOX@MISSING_DOMAIN>", | ||||
| -		  { NULL, "Display Name", NULL, "", "", TRUE },
 | ||||
| -		  { NULL, "Display Name", NULL, "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE }, 0 },
 | ||||
| +		  { NULL, NULL, "Display Name", NULL, "", "", TRUE },
 | ||||
| +		  { NULL, NULL, "Display Name", NULL, "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE }, 0 },
 | ||||
|  		{ "\"Display Name\"", "\"Display Name\"", "\"Display Name\" <MISSING_MAILBOX@MISSING_DOMAIN>", | ||||
| -		  { NULL, "Display Name", NULL, "", "", TRUE },
 | ||||
| -		  { NULL, "Display Name", NULL, "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE }, 0 },
 | ||||
| +		  { NULL, NULL, "Display Name", NULL, "", "", TRUE },
 | ||||
| +		  { NULL, NULL, "Display Name", NULL, "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE }, 0 },
 | ||||
|  		{ "Display \"Name\"", "\"Display Name\"", "\"Display Name\" <MISSING_MAILBOX@MISSING_DOMAIN>", | ||||
| -		  { NULL, "Display Name", NULL, "", "", TRUE },
 | ||||
| -		  { NULL, "Display Name", NULL, "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE }, 0 },
 | ||||
| +		  { NULL, NULL, "Display Name", NULL, "", "", TRUE },
 | ||||
| +		  { NULL, NULL, "Display Name", NULL, "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE }, 0 },
 | ||||
|  		{ "\"Display\" \"Name\"", "\"Display Name\"", "\"Display Name\" <MISSING_MAILBOX@MISSING_DOMAIN>", | ||||
| -		  { NULL, "Display Name", NULL, "", "", TRUE },
 | ||||
| -		  { NULL, "Display Name", NULL, "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE }, 0 },
 | ||||
| +		  { NULL, NULL, "Display Name", NULL, "", "", TRUE },
 | ||||
| +		  { NULL, NULL, "Display Name", NULL, "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE }, 0 },
 | ||||
|  		{ "\"\"", "", "<MISSING_MAILBOX@MISSING_DOMAIN>", | ||||
| -		  { NULL, "", NULL, "", "", TRUE },
 | ||||
| -		  { NULL, "", NULL, "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE }, 0 },
 | ||||
| +		  { NULL, NULL, "", NULL, "", "", TRUE },
 | ||||
| +		  { NULL, NULL, "", NULL, "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE }, 0 },
 | ||||
|   | ||||
|  		/* <user@domain> -> <user@domain> */ | ||||
|  		{ "<user@domain>", NULL, NULL, | ||||
| -		  { NULL, NULL, NULL, "user", "domain", FALSE },
 | ||||
| -		  { NULL, NULL, NULL, "user", "domain", FALSE }, 0 },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "user", "domain", FALSE },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "user", "domain", FALSE }, 0 },
 | ||||
|  		{ "<\"user\"@domain>", "<user@domain>", NULL, | ||||
| -		  { NULL, NULL, NULL, "user", "domain", FALSE },
 | ||||
| -		  { NULL, NULL, NULL, "user", "domain", FALSE }, 0 },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "user", "domain", FALSE },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "user", "domain", FALSE }, 0 },
 | ||||
|  		{ "<\"user name\"@domain>", NULL, NULL, | ||||
| -		  { NULL, NULL, NULL, "user name", "domain", FALSE },
 | ||||
| -		  { NULL, NULL, NULL, "user name", "domain", FALSE }, 0 },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "user name", "domain", FALSE },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "user name", "domain", FALSE }, 0 },
 | ||||
|  		{ "<\"user@na\\\\me\"@domain>", NULL, NULL, | ||||
| -		  { NULL, NULL, NULL, "user@na\\me", "domain", FALSE },
 | ||||
| -		  { NULL, NULL, NULL, "user@na\\me", "domain", FALSE }, 0 },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "user@na\\me", "domain", FALSE },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "user@na\\me", "domain", FALSE }, 0 },
 | ||||
|  		{ "<\"user\\\"name\"@domain>", NULL, NULL, | ||||
| -		  { NULL, NULL, NULL, "user\"name", "domain", FALSE },
 | ||||
| -		  { NULL, NULL, NULL, "user\"name", "domain", FALSE }, 0 },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "user\"name", "domain", FALSE },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "user\"name", "domain", FALSE }, 0 },
 | ||||
|  		{ "<\"\"@domain>", NULL, NULL, | ||||
| -		  { NULL, NULL, NULL, "", "domain", FALSE },
 | ||||
| -		  { NULL, NULL, NULL, "", "domain", FALSE }, 0 },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "", "domain", FALSE },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "", "domain", FALSE }, 0 },
 | ||||
|  		{ "<user>", NULL, "<user@MISSING_DOMAIN>", | ||||
| -		  { NULL, NULL, NULL, "user", "", TRUE },
 | ||||
| -		  { NULL, NULL, NULL, "user", "MISSING_DOMAIN", TRUE }, 0 },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "user", "", TRUE },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "user", "MISSING_DOMAIN", TRUE }, 0 },
 | ||||
|  		{ "<@route>", "<@route:\"\">", "<INVALID_ROUTE:MISSING_MAILBOX@MISSING_DOMAIN>", | ||||
| -		  { NULL, NULL, "@route", "", "", TRUE },
 | ||||
| -		  { NULL, NULL, "INVALID_ROUTE", "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE }, 0 },
 | ||||
| +		  { NULL, NULL, NULL, "@route", "", "", TRUE },
 | ||||
| +		  { NULL, NULL, NULL, "INVALID_ROUTE", "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE }, 0 },
 | ||||
|   | ||||
|  		/* user@domain (Display Name) -> "Display Name" <user@domain> */ | ||||
|  		{ "user@domain (DisplayName)", "DisplayName <user@domain>", NULL, | ||||
| -		  { NULL, "DisplayName", NULL, "user", "domain", FALSE },
 | ||||
| -		  { NULL, "DisplayName", NULL, "user", "domain", FALSE }, 0 },
 | ||||
| +		  { NULL, NULL, "DisplayName", NULL, "user", "domain", FALSE },
 | ||||
| +		  { NULL, NULL, "DisplayName", NULL, "user", "domain", FALSE }, 0 },
 | ||||
|  		{ "user@domain (Display Name)", "\"Display Name\" <user@domain>", NULL, | ||||
| -		  { NULL, "Display Name", NULL, "user", "domain", FALSE },
 | ||||
| -		  { NULL, "Display Name", NULL, "user", "domain", FALSE }, 0 },
 | ||||
| +		  { NULL, NULL, "Display Name", NULL, "user", "domain", FALSE },
 | ||||
| +		  { NULL, NULL, "Display Name", NULL, "user", "domain", FALSE }, 0 },
 | ||||
|  		{ "user@domain (Display\"Name)", "\"Display\\\"Name\" <user@domain>", NULL, | ||||
| -		  { NULL, "Display\"Name", NULL, "user", "domain", FALSE },
 | ||||
| -		  { NULL, "Display\"Name", NULL, "user", "domain", FALSE }, 0 },
 | ||||
| +		  { NULL, NULL, "Display\"Name", NULL, "user", "domain", FALSE },
 | ||||
| +		  { NULL, NULL, "Display\"Name", NULL, "user", "domain", FALSE }, 0 },
 | ||||
|  		{ "user (Display Name)", "\"Display Name\" <user>", "\"Display Name\" <user@MISSING_DOMAIN>", | ||||
| -		  { NULL, "Display Name", NULL, "user", "", TRUE },
 | ||||
| -		  { NULL, "Display Name", NULL, "user", "MISSING_DOMAIN", TRUE }, 0 },
 | ||||
| +		  { NULL, NULL, "Display Name", NULL, "user", "", TRUE },
 | ||||
| +		  { NULL, NULL, "Display Name", NULL, "user", "MISSING_DOMAIN", TRUE }, 0 },
 | ||||
|  		{ "@domain (Display Name)", "\"Display Name\" <\"\"@domain>", "\"Display Name\" <MISSING_MAILBOX@domain>", | ||||
| -		  { NULL, "Display Name", NULL, "", "domain", TRUE },
 | ||||
| -		  { NULL, "Display Name", NULL, "MISSING_MAILBOX", "domain", TRUE }, 0 },
 | ||||
| +		  { NULL, NULL, "Display Name", NULL, "", "domain", TRUE },
 | ||||
| +		  { NULL, NULL, "Display Name", NULL, "MISSING_MAILBOX", "domain", TRUE }, 0 },
 | ||||
|  		{ "user@domain ()", "<user@domain>", NULL, | ||||
| -		  { NULL, NULL, NULL, "user", "domain", FALSE },
 | ||||
| -		  { NULL, NULL, NULL, "user", "domain", FALSE }, 0 },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "user", "domain", FALSE },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "user", "domain", FALSE }, 0 },
 | ||||
|   | ||||
|  		/* Display Name <user@domain> -> "Display Name" <user@domain> */ | ||||
|  		{ "DisplayName <user@domain>", NULL, NULL, | ||||
| -		  { NULL, "DisplayName", NULL, "user", "domain", FALSE },
 | ||||
| -		  { NULL, "DisplayName", NULL, "user", "domain", FALSE }, 0 },
 | ||||
| +		  { NULL, NULL, "DisplayName", NULL, "user", "domain", FALSE },
 | ||||
| +		  { NULL, NULL, "DisplayName", NULL, "user", "domain", FALSE }, 0 },
 | ||||
|  		{ "Display Name <user@domain>", "\"Display Name\" <user@domain>", NULL, | ||||
| -		  { NULL, "Display Name", NULL, "user", "domain", FALSE },
 | ||||
| -		  { NULL, "Display Name", NULL, "user", "domain", FALSE }, 0 },
 | ||||
| +		  { NULL, NULL, "Display Name", NULL, "user", "domain", FALSE },
 | ||||
| +		  { NULL, NULL, "Display Name", NULL, "user", "domain", FALSE }, 0 },
 | ||||
|  		{ "\"Display Name\" <user@domain>", NULL, NULL, | ||||
| -		  { NULL, "Display Name", NULL, "user", "domain", FALSE },
 | ||||
| -		  { NULL, "Display Name", NULL, "user", "domain", FALSE }, 0 },
 | ||||
| +		  { NULL, NULL, "Display Name", NULL, "user", "domain", FALSE },
 | ||||
| +		  { NULL, NULL, "Display Name", NULL, "user", "domain", FALSE }, 0 },
 | ||||
|  		{ "\"Display\\\"Name\" <user@domain>", NULL, NULL, | ||||
| -		  { NULL, "Display\"Name", NULL, "user", "domain", FALSE },
 | ||||
| -		  { NULL, "Display\"Name", NULL, "user", "domain", FALSE }, 0 },
 | ||||
| +		  { NULL, NULL, "Display\"Name", NULL, "user", "domain", FALSE },
 | ||||
| +		  { NULL, NULL, "Display\"Name", NULL, "user", "domain", FALSE }, 0 },
 | ||||
|  		{ "Display Name <user>", "\"Display Name\" <user>", "\"Display Name\" <user@MISSING_DOMAIN>", | ||||
| -		  { NULL, "Display Name", NULL, "user", "", TRUE },
 | ||||
| -		  { NULL, "Display Name", NULL, "user", "MISSING_DOMAIN", TRUE }, 0 },
 | ||||
| +		  { NULL, NULL, "Display Name", NULL, "user", "", TRUE },
 | ||||
| +		  { NULL, NULL, "Display Name", NULL, "user", "MISSING_DOMAIN", TRUE }, 0 },
 | ||||
|  		{ "\"\" <user@domain>", "<user@domain>", NULL, | ||||
| -		  { NULL, NULL, NULL, "user", "domain", FALSE },
 | ||||
| -		  { NULL, NULL, NULL, "user", "domain", FALSE }, 0 },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "user", "domain", FALSE },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "user", "domain", FALSE }, 0 },
 | ||||
|   | ||||
|  		/* <@route:user@domain> -> <@route:user@domain> */ | ||||
|  		{ "<@route:user@domain>", NULL, NULL, | ||||
| -		  { NULL, NULL, "@route", "user", "domain", FALSE },
 | ||||
| -		  { NULL, NULL, "@route", "user", "domain", FALSE }, 0 },
 | ||||
| +		  { NULL, NULL, NULL, "@route", "user", "domain", FALSE },
 | ||||
| +		  { NULL, NULL, NULL, "@route", "user", "domain", FALSE }, 0 },
 | ||||
|  		{ "<@route,@route2:user@domain>", NULL, NULL, | ||||
| -		  { NULL, NULL, "@route,@route2", "user", "domain", FALSE },
 | ||||
| -		  { NULL, NULL, "@route,@route2", "user", "domain", FALSE }, 0 },
 | ||||
| +		  { NULL, NULL, NULL, "@route,@route2", "user", "domain", FALSE },
 | ||||
| +		  { NULL, NULL, NULL, "@route,@route2", "user", "domain", FALSE }, 0 },
 | ||||
|  		{ "<@route@route2:user@domain>", "<@route,@route2:user@domain>", NULL, | ||||
| -		  { NULL, NULL, "@route,@route2", "user", "domain", FALSE },
 | ||||
| -		  { NULL, NULL, "@route,@route2", "user", "domain", FALSE }, 0 },
 | ||||
| +		  { NULL, NULL, NULL, "@route,@route2", "user", "domain", FALSE },
 | ||||
| +		  { NULL, NULL, NULL, "@route,@route2", "user", "domain", FALSE }, 0 },
 | ||||
|  		{ "<@route@route2:user>", "<@route,@route2:user>", "<@route,@route2:user@MISSING_DOMAIN>", | ||||
| -		  { NULL, NULL, "@route,@route2", "user", "", TRUE },
 | ||||
| -		  { NULL, NULL, "@route,@route2", "user", "MISSING_DOMAIN", TRUE }, 0 },
 | ||||
| +		  { NULL, NULL, NULL, "@route,@route2", "user", "", TRUE },
 | ||||
| +		  { NULL, NULL, NULL, "@route,@route2", "user", "MISSING_DOMAIN", TRUE }, 0 },
 | ||||
|  		{ "<@route@route2:\"\"@domain>", "<@route,@route2:\"\"@domain>", NULL, | ||||
| -		  { NULL, NULL, "@route,@route2", "", "domain", FALSE },
 | ||||
| -		  { NULL, NULL, "@route,@route2", "", "domain", FALSE }, 0 },
 | ||||
| +		  { NULL, NULL, NULL, "@route,@route2", "", "domain", FALSE },
 | ||||
| +		  { NULL, NULL, NULL, "@route,@route2", "", "domain", FALSE }, 0 },
 | ||||
|   | ||||
|  		/* Display Name <@route:user@domain> -> | ||||
|  		   "Display Name" <@route:user@domain> */ | ||||
|  		{ "Display Name <@route:user@domain>", "\"Display Name\" <@route:user@domain>", NULL, | ||||
| -		  { NULL, "Display Name", "@route", "user", "domain", FALSE },
 | ||||
| -		  { NULL, "Display Name", "@route", "user", "domain", FALSE }, 0 },
 | ||||
| +		  { NULL, NULL, "Display Name", "@route", "user", "domain", FALSE },
 | ||||
| +		  { NULL, NULL, "Display Name", "@route", "user", "domain", FALSE }, 0 },
 | ||||
|  		{ "Display Name <@route,@route2:user@domain>", "\"Display Name\" <@route,@route2:user@domain>", NULL, | ||||
| -		  { NULL, "Display Name", "@route,@route2", "user", "domain", FALSE },
 | ||||
| -		  { NULL, "Display Name", "@route,@route2", "user", "domain", FALSE }, 0 },
 | ||||
| +		  { NULL, NULL, "Display Name", "@route,@route2", "user", "domain", FALSE },
 | ||||
| +		  { NULL, NULL, "Display Name", "@route,@route2", "user", "domain", FALSE }, 0 },
 | ||||
|  		{ "Display Name <@route@route2:user@domain>", "\"Display Name\" <@route,@route2:user@domain>", NULL, | ||||
| -		  { NULL, "Display Name", "@route,@route2", "user", "domain", FALSE },
 | ||||
| -		  { NULL, "Display Name", "@route,@route2", "user", "domain", FALSE }, 0 },
 | ||||
| +		  { NULL, NULL, "Display Name", "@route,@route2", "user", "domain", FALSE },
 | ||||
| +		  { NULL, NULL, "Display Name", "@route,@route2", "user", "domain", FALSE }, 0 },
 | ||||
|  		{ "Display Name <@route@route2:user>", "\"Display Name\" <@route,@route2:user>", "\"Display Name\" <@route,@route2:user@MISSING_DOMAIN>", | ||||
| -		  { NULL, "Display Name", "@route,@route2", "user", "", TRUE },
 | ||||
| -		  { NULL, "Display Name", "@route,@route2", "user", "MISSING_DOMAIN", TRUE }, 0 },
 | ||||
| +		  { NULL, NULL, "Display Name", "@route,@route2", "user", "", TRUE },
 | ||||
| +		  { NULL, NULL, "Display Name", "@route,@route2", "user", "MISSING_DOMAIN", TRUE }, 0 },
 | ||||
|  		{ "Display Name <@route@route2:\"\"@domain>", "\"Display Name\" <@route,@route2:\"\"@domain>", NULL, | ||||
| -		  { NULL, "Display Name", "@route,@route2", "", "domain", FALSE },
 | ||||
| -		  { NULL, "Display Name", "@route,@route2", "", "domain", FALSE }, 0 },
 | ||||
| +		  { NULL, NULL, "Display Name", "@route,@route2", "", "domain", FALSE },
 | ||||
| +		  { NULL, NULL, "Display Name", "@route,@route2", "", "domain", FALSE }, 0 },
 | ||||
|   | ||||
|  		/* other tests: */ | ||||
|  		{ "\"foo: <a@b>;,\" <user@domain>", NULL, NULL, | ||||
| -		  { NULL, "foo: <a@b>;,", NULL, "user", "domain", FALSE },
 | ||||
| -		  { NULL, "foo: <a@b>;,", NULL, "user", "domain", FALSE }, 0 },
 | ||||
| +		  { NULL, NULL, "foo: <a@b>;,", NULL, "user", "domain", FALSE },
 | ||||
| +		  { NULL, NULL, "foo: <a@b>;,", NULL, "user", "domain", FALSE }, 0 },
 | ||||
|  		{ "<>", "", "<MISSING_MAILBOX@MISSING_DOMAIN>", | ||||
| -		  { NULL, NULL, NULL, "", "", TRUE },
 | ||||
| -		  { NULL, NULL, NULL, "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE }, 0 },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "", "", TRUE },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE }, 0 },
 | ||||
|  		{ "<@>", "", "<INVALID_ROUTE:MISSING_MAILBOX@MISSING_DOMAIN>", | ||||
| -		  { NULL, NULL, NULL, "", "", TRUE },
 | ||||
| -		  { NULL, NULL, "INVALID_ROUTE", "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE }, 0 },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "", "", TRUE },
 | ||||
| +		  { NULL, NULL, NULL, "INVALID_ROUTE", "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE }, 0 },
 | ||||
|   | ||||
|  		/* Test against a out-of-bounds read bug - keep these two tests | ||||
|  		   together in this same order: */ | ||||
|  		{ "aaaa@", "<aaaa>", "<aaaa@MISSING_DOMAIN>", | ||||
| -		  { NULL, NULL, NULL, "aaaa", "", TRUE },
 | ||||
| -		  { NULL, NULL, NULL, "aaaa", "MISSING_DOMAIN", TRUE }, 0 },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "aaaa", "", TRUE },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "aaaa", "MISSING_DOMAIN", TRUE }, 0 },
 | ||||
|  		{ "a(aa", "", "<MISSING_MAILBOX@MISSING_DOMAIN>", | ||||
| -		  { NULL, NULL, NULL, "", "", TRUE },
 | ||||
| -		  { NULL, NULL, NULL, "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "", "", TRUE },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE },
 | ||||
|  		  TEST_MESSAGE_ADDRESS_FLAG_SKIP_LIST }, | ||||
|  	}; | ||||
|  	static struct message_address group_prefix = { | ||||
| -		NULL, NULL, NULL, "group", NULL, FALSE
 | ||||
| +		NULL, NULL, NULL, NULL, "group", NULL, FALSE
 | ||||
|  	}; | ||||
|  	static struct message_address group_suffix = { | ||||
| -		NULL, NULL, NULL, NULL, NULL, FALSE
 | ||||
| +		NULL, NULL, NULL, NULL, NULL, NULL, FALSE
 | ||||
|  	}; | ||||
|  	const struct message_address *addr; | ||||
|  	string_t *str, *group; | ||||
| @@ -327,7 +327,7 @@ static void test_message_address_nuls(void)
 | ||||
|  	const unsigned char input[] = | ||||
|  		"\"user\0nuls\\\0-esc\"@[domain\0nuls\\\0-esc] (comment\0nuls\\\0-esc)"; | ||||
|  	const struct message_address output = { | ||||
| -		NULL, "comment\xEF\xBF\xBDnuls\\\xEF\xBF\xBD-esc", NULL,
 | ||||
| +		NULL, NULL, "comment\xEF\xBF\xBDnuls\\\xEF\xBF\xBD-esc", NULL,
 | ||||
|  		"user\xEF\xBF\xBDnuls\\\xEF\xBF\xBD-esc", | ||||
|  		"[domain\xEF\xBF\xBDnuls\\\xEF\xBF\xBD-esc]", FALSE | ||||
|  	}; | ||||
| @@ -345,7 +345,7 @@ static void test_message_address_nuls_display_name(void)
 | ||||
|  	const unsigned char input[] = | ||||
|  		"\"displayname\0nuls\\\0-esc\" <\"user\0nuls\\\0-esc\"@[domain\0nuls\\\0-esc]>"; | ||||
|  	const struct message_address output = { | ||||
| -		NULL, "displayname\xEF\xBF\xBDnuls\\\xEF\xBF\xBD-esc", NULL,
 | ||||
| +		NULL, NULL, "displayname\xEF\xBF\xBDnuls\\\xEF\xBF\xBD-esc", NULL,
 | ||||
|  		"user\xEF\xBF\xBDnuls\\\xEF\xBF\xBD-esc", | ||||
|  		"[domain\xEF\xBF\xBDnuls\\\xEF\xBF\xBD-esc]", FALSE | ||||
|  	}; | ||||
| @@ -369,7 +369,7 @@ static void test_message_address_non_strict_dots(void)
 | ||||
|  	}; | ||||
|  	const struct message_address *addr; | ||||
|  	struct message_address output = { | ||||
| -		NULL, NULL, NULL, "local-part",
 | ||||
| +		NULL, NULL, NULL, NULL, "local-part",
 | ||||
|  		"example.com", FALSE | ||||
|  	}; | ||||
|   | ||||
| @@ -421,29 +421,29 @@ static void test_message_address_path(void)
 | ||||
|  		struct message_address addr; | ||||
|  	} tests[] = { | ||||
|  		{ "<>", NULL, | ||||
| -		  { NULL, NULL, NULL, NULL, NULL, FALSE } },
 | ||||
| +		  { NULL, NULL, NULL, NULL, NULL, NULL, FALSE } },
 | ||||
|  		{ " < > ", "<>", | ||||
| -		  { NULL, NULL, NULL, NULL, NULL, FALSE } },
 | ||||
| +		  { NULL, NULL, NULL, NULL, NULL, NULL, FALSE } },
 | ||||
|  		{ "<user@domain>", NULL, | ||||
| -		  { NULL, NULL, NULL, "user", "domain", FALSE } },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "user", "domain", FALSE } },
 | ||||
|  		{ "  <user@domain>  ", "<user@domain>", | ||||
| -		  { NULL, NULL, NULL, "user", "domain", FALSE } },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "user", "domain", FALSE } },
 | ||||
|  		{ "user@domain", "<user@domain>", | ||||
| -		  { NULL, NULL, NULL, "user", "domain", FALSE } },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "user", "domain", FALSE } },
 | ||||
|  		{ "  user@domain  ", "<user@domain>", | ||||
| -		  { NULL, NULL, NULL, "user", "domain", FALSE } },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "user", "domain", FALSE } },
 | ||||
|  		{ "<\"user\"@domain>", "<user@domain>", | ||||
| -		  { NULL, NULL, NULL, "user", "domain", FALSE } },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "user", "domain", FALSE } },
 | ||||
|  		{ "<\"user name\"@domain>", NULL, | ||||
| -		  { NULL, NULL, NULL, "user name", "domain", FALSE } },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "user name", "domain", FALSE } },
 | ||||
|  		{ "<\"user@na\\\\me\"@domain>", NULL, | ||||
| -		  { NULL, NULL, NULL, "user@na\\me", "domain", FALSE } },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "user@na\\me", "domain", FALSE } },
 | ||||
|  		{ "<\"user\\\"name\"@domain>", NULL, | ||||
| -		  { NULL, NULL, NULL, "user\"name", "domain", FALSE } },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "user\"name", "domain", FALSE } },
 | ||||
|  		{ "<\"\"@domain>", NULL, | ||||
| -		  { NULL, NULL, NULL, "", "domain", FALSE } },
 | ||||
| +		  { NULL, NULL, NULL, NULL, "", "domain", FALSE } },
 | ||||
|  		{ "<@source", "<>", | ||||
| -		  { NULL, NULL, NULL, NULL, NULL, TRUE } },
 | ||||
| +		  { NULL, NULL, NULL, NULL, NULL, NULL, TRUE } },
 | ||||
|  	}; | ||||
|  	const struct message_address *addr; | ||||
|  	string_t *str; | ||||
| 
 | ||||
| From da61d20311da34f22944c6111a0b97ea2a1f8a47 Mon Sep 17 00:00:00 2001 | ||||
| From: Timo Sirainen <timo.sirainen@open-xchange.com> | ||||
| Date: Tue, 30 Jan 2024 22:17:38 +0200 | ||||
| Subject: [PATCH 5/6] lib-mail: Add message_address_parse_full() and struct | ||||
|  message_address_list | ||||
| 
 | ||||
| ---
 | ||||
|  src/lib-mail/message-address.c      | 37 +++++++++++++--------- | ||||
|  src/lib-mail/message-address.h      | 10 ++++++ | ||||
|  src/lib-mail/test-message-address.c | 48 +++++++++++++++++++++++++---- | ||||
|  3 files changed, 75 insertions(+), 20 deletions(-) | ||||
| 
 | ||||
| diff --git a/src/lib-mail/message-address.c b/src/lib-mail/message-address.c
 | ||||
| index 9d192799468..ae37014079a 100644
 | ||||
| --- a/src/lib-mail/message-address.c
 | ||||
| +++ b/src/lib-mail/message-address.c
 | ||||
| @@ -13,7 +13,8 @@ struct message_address_parser_context {
 | ||||
|  	pool_t pool; | ||||
|  	struct rfc822_parser_context parser; | ||||
|   | ||||
| -	struct message_address *first_addr, *last_addr, addr;
 | ||||
| +	struct message_address addr;
 | ||||
| +	struct message_address_list addr_list;
 | ||||
|  	string_t *str; | ||||
|   | ||||
|  	bool fill_missing, non_strict_dots; | ||||
| @@ -28,7 +29,7 @@ static void add_address(struct message_address_parser_context *ctx)
 | ||||
|  	memcpy(addr, &ctx->addr, sizeof(ctx->addr)); | ||||
|  	i_zero(&ctx->addr); | ||||
|   | ||||
| -	DLLIST2_APPEND(&ctx->first_addr, &ctx->last_addr, addr);
 | ||||
| +	DLLIST2_APPEND(&ctx->addr_list.head, &ctx->addr_list.tail, addr);
 | ||||
|  } | ||||
|   | ||||
|  /* quote with "" and escape all '\', '"' and "'" characters if need */ | ||||
| @@ -439,10 +440,11 @@ static int parse_path(struct message_address_parser_context *ctx)
 | ||||
|  	return ret; | ||||
|  } | ||||
|   | ||||
| -static struct message_address *
 | ||||
| +static void
 | ||||
|  message_address_parse_real(pool_t pool, const unsigned char *data, size_t size, | ||||
|  			   unsigned int max_addresses, | ||||
| -			   enum message_address_parse_flags flags)
 | ||||
| +			   enum message_address_parse_flags flags,
 | ||||
| +			   struct message_address_list *list_r)
 | ||||
|  { | ||||
|  	struct message_address_parser_context ctx; | ||||
|   | ||||
| @@ -461,7 +463,7 @@ message_address_parse_real(pool_t pool, const unsigned char *data, size_t size,
 | ||||
|  		(void)parse_address_list(&ctx, max_addresses); | ||||
|  	} | ||||
|  	rfc822_parser_deinit(&ctx.parser); | ||||
| -	return ctx.first_addr;
 | ||||
| +	*list_r = ctx.addr_list;
 | ||||
|  } | ||||
|   | ||||
|  static int | ||||
| @@ -481,7 +483,7 @@ message_address_parse_path_real(pool_t pool, const unsigned char *data,
 | ||||
|  	ret = parse_path(&ctx); | ||||
|   | ||||
|  	rfc822_parser_deinit(&ctx.parser); | ||||
| -	*addr_r = ctx.first_addr;
 | ||||
| +	*addr_r = ctx.addr_list.head;
 | ||||
|  	return (ret < 0 ? -1 : 0); | ||||
|  } | ||||
|   | ||||
| @@ -490,17 +492,24 @@ message_address_parse(pool_t pool, const unsigned char *data, size_t size,
 | ||||
|  		      unsigned int max_addresses, | ||||
|  		      enum message_address_parse_flags flags) | ||||
|  { | ||||
| -	struct message_address *addr;
 | ||||
| +	struct message_address_list list;
 | ||||
| +	message_address_parse_full(pool, data, size, max_addresses, flags,
 | ||||
| +				   &list);
 | ||||
| +	return list.head;
 | ||||
| +}
 | ||||
|   | ||||
| +void message_address_parse_full(pool_t pool, const unsigned char *data,
 | ||||
| +				size_t size, unsigned int max_addresses,
 | ||||
| +				enum message_address_parse_flags flags,
 | ||||
| +				struct message_address_list *list_r)
 | ||||
| +{
 | ||||
|  	if (pool->datastack_pool) { | ||||
| -		return message_address_parse_real(pool, data, size,
 | ||||
| -						  max_addresses, flags);
 | ||||
| -	}
 | ||||
| -	T_BEGIN {
 | ||||
| -		addr = message_address_parse_real(pool, data, size,
 | ||||
| -						  max_addresses, flags);
 | ||||
| +		message_address_parse_real(pool, data, size,
 | ||||
| +					   max_addresses, flags, list_r);
 | ||||
| +	} else T_BEGIN {
 | ||||
| +		message_address_parse_real(pool, data, size,
 | ||||
| +					   max_addresses, flags, list_r);
 | ||||
|  	} T_END; | ||||
| -	return addr;
 | ||||
|  } | ||||
|   | ||||
|  int message_address_parse_path(pool_t pool, const unsigned char *data, | ||||
| diff --git a/src/lib-mail/message-address.h b/src/lib-mail/message-address.h
 | ||||
| index 85cff3dcc6f..224f7a75605 100644
 | ||||
| --- a/src/lib-mail/message-address.h
 | ||||
| +++ b/src/lib-mail/message-address.h
 | ||||
| @@ -31,12 +31,22 @@ struct message_address {
 | ||||
|  	bool invalid_syntax; | ||||
|  }; | ||||
|   | ||||
| +struct message_address_list {
 | ||||
| +	struct message_address *head, *tail;
 | ||||
| +};
 | ||||
| +
 | ||||
|  /* Parse message addresses from given data. Note that giving an empty string | ||||
|     will return NULL since there are no addresses. */ | ||||
|  struct message_address * | ||||
|  message_address_parse(pool_t pool, const unsigned char *data, size_t size, | ||||
|  		      unsigned int max_addresses, | ||||
|  		      enum message_address_parse_flags flags); | ||||
| +/* Same as message_address_parse(), but return message_address_list containing
 | ||||
| +   both the first and the last address in the linked list. */
 | ||||
| +void message_address_parse_full(pool_t pool, const unsigned char *data,
 | ||||
| +				size_t size, unsigned int max_addresses,
 | ||||
| +				enum message_address_parse_flags flags,
 | ||||
| +				struct message_address_list *list_r);
 | ||||
|   | ||||
|  /* Parse RFC 5322 "path" (Return-Path header) from given data. Returns -1 if | ||||
|     the path is invalid and 0 otherwise. | ||||
| diff --git a/src/lib-mail/test-message-address.c b/src/lib-mail/test-message-address.c
 | ||||
| index 261cbfba70a..54aa9a83101 100644
 | ||||
| --- a/src/lib-mail/test-message-address.c
 | ||||
| +++ b/src/lib-mail/test-message-address.c
 | ||||
| @@ -19,8 +19,9 @@ static bool cmp_addr(const struct message_address *a1,
 | ||||
|  		a1->invalid_syntax == a2->invalid_syntax; | ||||
|  } | ||||
|   | ||||
| -static const struct message_address *
 | ||||
| -test_parse_address(const char *input, bool fill_missing)
 | ||||
| +static void
 | ||||
| +test_parse_address_full(const char *input, bool fill_missing,
 | ||||
| +			struct message_address_list *list_r)
 | ||||
|  { | ||||
|  	const enum message_address_parse_flags flags = | ||||
|  		fill_missing ? MESSAGE_ADDRESS_PARSE_FLAG_FILL_MISSING : 0; | ||||
| @@ -28,11 +29,18 @@ test_parse_address(const char *input, bool fill_missing)
 | ||||
|  	   if there's any out-of-bounds access */ | ||||
|  	size_t input_len = strlen(input); | ||||
|  	unsigned char *input_dup = i_memdup(input, input_len); | ||||
| -	const struct message_address *addr =
 | ||||
| -		message_address_parse(pool_datastack_create(),
 | ||||
| -				      input_dup, input_len, UINT_MAX, flags);
 | ||||
| +	message_address_parse_full(pool_datastack_create(),
 | ||||
| +				   input_dup, input_len, UINT_MAX, flags,
 | ||||
| +				   list_r);
 | ||||
|  	i_free(input_dup); | ||||
| -	return addr;
 | ||||
| +}
 | ||||
| +
 | ||||
| +static const struct message_address *
 | ||||
| +test_parse_address(const char *input, bool fill_missing)
 | ||||
| +{
 | ||||
| +	struct message_address_list list;
 | ||||
| +	test_parse_address_full(input, fill_missing, &list);
 | ||||
| +	return list.head;
 | ||||
|  } | ||||
|   | ||||
|  static void test_message_address(void) | ||||
| @@ -322,6 +330,33 @@ static void test_message_address(void)
 | ||||
|  	test_end(); | ||||
|  } | ||||
|   | ||||
| +static void test_message_address_list(void)
 | ||||
| +{
 | ||||
| +	test_begin("message address list");
 | ||||
| +
 | ||||
| +	const char *test_input =
 | ||||
| +		"user1@example1.com, user2@example2.com, user3@example3.com";
 | ||||
| +	const struct message_address wanted_addrs[] = {
 | ||||
| +		{ NULL, NULL, NULL, NULL, "user1", "example1.com", FALSE },
 | ||||
| +		{ NULL, NULL, NULL, NULL, "user2", "example2.com", FALSE },
 | ||||
| +		{ NULL, NULL, NULL, NULL, "user3", "example3.com", FALSE },
 | ||||
| +	};
 | ||||
| +
 | ||||
| +	struct message_address_list list;
 | ||||
| +	struct message_address *addr, *scanned_last_addr;
 | ||||
| +	test_parse_address_full(test_input, FALSE, &list);
 | ||||
| +	addr = list.head;
 | ||||
| +	for (unsigned int i = 0; i < N_ELEMENTS(wanted_addrs); i++) {
 | ||||
| +		test_assert_idx(cmp_addr(addr, &wanted_addrs[i]), i);
 | ||||
| +		scanned_last_addr = addr;
 | ||||
| +		addr = addr->next;
 | ||||
| +	}
 | ||||
| +	test_assert(list.tail == scanned_last_addr);
 | ||||
| +	test_assert(addr == NULL);
 | ||||
| +
 | ||||
| +	test_end();
 | ||||
| +}
 | ||||
| +
 | ||||
|  static void test_message_address_nuls(void) | ||||
|  { | ||||
|  	const unsigned char input[] = | ||||
| @@ -521,6 +556,7 @@ int main(void)
 | ||||
|  { | ||||
|  	static void (*const test_functions[])(void) = { | ||||
|  		test_message_address, | ||||
| +		test_message_address_list,
 | ||||
|  		test_message_address_nuls, | ||||
|  		test_message_address_nuls_display_name, | ||||
|  		test_message_address_non_strict_dots, | ||||
| 
 | ||||
| From 1481c04f02df7647f520df65d63df7626bf0ee32 Mon Sep 17 00:00:00 2001 | ||||
| From: Timo Sirainen <timo.sirainen@open-xchange.com> | ||||
| Date: Fri, 9 Feb 2024 00:57:12 +0200 | ||||
| Subject: [PATCH 6/6] lib-mail, lib-imap: Optimize parsing large number of | ||||
|  address headers | ||||
| 
 | ||||
| Every header was appended to a linked list by walking through the whole | ||||
| list, causing excessive CPU usage when the list became large enough. | ||||
| Fixed by changing struct message_part_envelope to use struct | ||||
| message_address_list, which stores also linked list tail pointers. This | ||||
| allows quickly appending to the end of the linked list. | ||||
| ---
 | ||||
|  src/lib-imap/imap-envelope.c              | 27 ++++++++++------------- | ||||
|  src/lib-mail/message-part-data.c          | 17 +++++++------- | ||||
|  src/lib-mail/message-part-data.h          |  6 +++-- | ||||
|  src/lib-storage/index/index-search-mime.c |  4 ++-- | ||||
|  4 files changed, 27 insertions(+), 27 deletions(-) | ||||
| 
 | ||||
| diff --git a/src/lib-imap/imap-envelope.c b/src/lib-imap/imap-envelope.c
 | ||||
| index 1312eae2ff3..da3177025a5 100644
 | ||||
| --- a/src/lib-imap/imap-envelope.c
 | ||||
| +++ b/src/lib-imap/imap-envelope.c
 | ||||
| @@ -67,17 +67,17 @@ void imap_envelope_write(struct message_part_envelope *data,
 | ||||
|  	} | ||||
|   | ||||
|  	str_append_c(str, ' '); | ||||
| -	imap_write_address(str, data->from);
 | ||||
| +	imap_write_address(str, data->from.head);
 | ||||
|  	str_append_c(str, ' '); | ||||
| -	imap_write_address(str, NVL(data->sender, data->from));
 | ||||
| +	imap_write_address(str, NVL(data->sender.head, data->from.head));
 | ||||
|  	str_append_c(str, ' '); | ||||
| -	imap_write_address(str, NVL(data->reply_to, data->from));
 | ||||
| +	imap_write_address(str, NVL(data->reply_to.head, data->from.head));
 | ||||
|  	str_append_c(str, ' '); | ||||
| -	imap_write_address(str, data->to);
 | ||||
| +	imap_write_address(str, data->to.head);
 | ||||
|  	str_append_c(str, ' '); | ||||
| -	imap_write_address(str, data->cc);
 | ||||
| +	imap_write_address(str, data->cc.head);
 | ||||
|  	str_append_c(str, ' '); | ||||
| -	imap_write_address(str, data->bcc);
 | ||||
| +	imap_write_address(str, data->bcc.head);
 | ||||
|   | ||||
|  	str_append_c(str, ' '); | ||||
|  	imap_append_nstring_nolf(str, data->in_reply_to); | ||||
| @@ -126,28 +126,25 @@ imap_envelope_parse_address(const struct imap_arg *arg,
 | ||||
|   | ||||
|  static bool | ||||
|  imap_envelope_parse_addresses(const struct imap_arg *arg, | ||||
| -	pool_t pool, struct message_address **addrs_r)
 | ||||
| +	pool_t pool, struct message_address_list *addrs_r)
 | ||||
|  { | ||||
| -	struct message_address *first, *last, *addr;
 | ||||
| +	struct message_address *addr;
 | ||||
|  	const struct imap_arg *list_args; | ||||
|   | ||||
| -	if (arg->type == IMAP_ARG_NIL) {
 | ||||
| -		*addrs_r = NULL;
 | ||||
| +	i_zero(addrs_r);
 | ||||
| +	if (arg->type == IMAP_ARG_NIL)
 | ||||
|  		return TRUE; | ||||
| -	}
 | ||||
|   | ||||
|  	if (!imap_arg_get_list(arg, &list_args)) | ||||
|  		return FALSE; | ||||
|   | ||||
| -	first = last = addr = NULL;
 | ||||
| +	addr = NULL;
 | ||||
|  	for (; !IMAP_ARG_IS_EOL(list_args); list_args++) { | ||||
|  		if (!imap_envelope_parse_address | ||||
|  			(list_args, pool, &addr)) | ||||
|  			return FALSE; | ||||
| -		DLLIST2_APPEND(&first, &last, addr);
 | ||||
| +		DLLIST2_APPEND(&addrs_r->head, &addrs_r->tail, addr);
 | ||||
|  	} | ||||
| -
 | ||||
| -	*addrs_r = first;
 | ||||
|  	return TRUE; | ||||
|  } | ||||
|   | ||||
| diff --git a/src/lib-mail/message-part-data.c b/src/lib-mail/message-part-data.c
 | ||||
| index a5771f87e2e..25019ab432d 100644
 | ||||
| --- a/src/lib-mail/message-part-data.c
 | ||||
| +++ b/src/lib-mail/message-part-data.c
 | ||||
| @@ -4,6 +4,7 @@
 | ||||
|  #include "str.h" | ||||
|  #include "wildcard-match.h" | ||||
|  #include "array.h" | ||||
| +#include "llist.h"
 | ||||
|  #include "rfc822-parser.h" | ||||
|  #include "rfc2231-parser.h" | ||||
|  #include "message-address.h" | ||||
| @@ -176,7 +177,7 @@ void message_part_envelope_parse_from_header(pool_t pool,
 | ||||
|  { | ||||
|  	struct message_part_envelope *d; | ||||
|  	enum envelope_field field; | ||||
| -	struct message_address **addr_p, *addr;
 | ||||
| +	struct message_address_list *addr_p, new_addr;
 | ||||
|  	const char **str_p; | ||||
|   | ||||
|  	if (*data == NULL) { | ||||
| @@ -234,18 +235,18 @@ void message_part_envelope_parse_from_header(pool_t pool,
 | ||||
|  	} | ||||
|   | ||||
|  	if (addr_p != NULL) { | ||||
| -		addr = message_address_parse(pool, hdr->full_value,
 | ||||
| -					     hdr->full_value_len,
 | ||||
| -					     UINT_MAX,
 | ||||
| -					     MESSAGE_ADDRESS_PARSE_FLAG_FILL_MISSING);
 | ||||
| +		message_address_parse_full(pool, hdr->full_value,
 | ||||
| +					   hdr->full_value_len,
 | ||||
| +					   UINT_MAX,
 | ||||
| +					   MESSAGE_ADDRESS_PARSE_FLAG_FILL_MISSING,
 | ||||
| +					   &new_addr);
 | ||||
|  		/* Merge multiple headers the same as if they were comma | ||||
|  		   separated in a single line. This is better from security | ||||
|  		   point of view, because attacker could intentionally write | ||||
|  		   addresses in a way that e.g. the first From header is | ||||
|  		   validated while MUA only shows the second From header. */ | ||||
| -		while (*addr_p != NULL)
 | ||||
| -			addr_p = &(*addr_p)->next;
 | ||||
| -		*addr_p = addr;
 | ||||
| +		DLLIST2_JOIN(&addr_p->head, &addr_p->tail,
 | ||||
| +			     &new_addr.head, &new_addr.tail);
 | ||||
|  	} else if (str_p != NULL) { | ||||
|  		*str_p = message_header_strdup(pool, hdr->full_value, | ||||
|  					       hdr->full_value_len); | ||||
| diff --git a/src/lib-mail/message-part-data.h b/src/lib-mail/message-part-data.h
 | ||||
| index 5ff9ffe1bc6..7ec878de68e 100644
 | ||||
| --- a/src/lib-mail/message-part-data.h
 | ||||
| +++ b/src/lib-mail/message-part-data.h
 | ||||
| @@ -2,6 +2,7 @@
 | ||||
|  #define MESSAGE_PART_DATA_H | ||||
|   | ||||
|  #include "message-part.h" | ||||
| +#include "message-address.h"
 | ||||
|   | ||||
|  #define MESSAGE_PART_DEFAULT_CHARSET "us-ascii" | ||||
|   | ||||
| @@ -14,8 +15,9 @@ struct message_part_param {
 | ||||
|   | ||||
|  struct message_part_envelope { | ||||
|  	const char *date, *subject; | ||||
| -	struct message_address *from, *sender, *reply_to;
 | ||||
| -	struct message_address *to, *cc, *bcc;
 | ||||
| +
 | ||||
| +	struct message_address_list from, sender, reply_to;
 | ||||
| +	struct message_address_list to, cc, bcc;
 | ||||
|   | ||||
|  	const char *in_reply_to, *message_id; | ||||
|  }; | ||||
| diff --git a/src/lib-storage/index/index-search-mime.c b/src/lib-storage/index/index-search-mime.c
 | ||||
| index da7e5e17092..3328ce98af1 100644
 | ||||
| --- a/src/lib-storage/index/index-search-mime.c
 | ||||
| +++ b/src/lib-storage/index/index-search-mime.c
 | ||||
| @@ -205,7 +205,7 @@ seach_arg_mime_envelope_address_match(
 | ||||
|  	enum mail_search_mime_arg_type type, const char *key, | ||||
|  	const struct message_part_envelope *envelope) | ||||
|  { | ||||
| -	const struct message_address *addrs;
 | ||||
| +	struct message_address_list addrs;
 | ||||
|  	string_t *addrs_enc; | ||||
|   | ||||
|  	if (envelope == NULL) | ||||
| @@ -239,7 +239,7 @@ seach_arg_mime_envelope_address_match(
 | ||||
|  	   probably be normalized directly in the struct message_address. */ | ||||
|   | ||||
|  	addrs_enc = t_str_new(128); | ||||
| -	message_address_write(addrs_enc, addrs);
 | ||||
| +	message_address_write(addrs_enc, addrs.head);
 | ||||
|  	return (strstr(str_c(addrs_enc), key) != NULL ? 1 : 0); | ||||
|  } | ||||
|   | ||||
							
								
								
									
										459
									
								
								dovecot-2.3.21.1-CVE-2024-23185.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										459
									
								
								dovecot-2.3.21.1-CVE-2024-23185.patch
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,459 @@ | ||||
| diff -up dovecot-2.3.16/src/lib-mail/message-header-parser.c.CVE-2024-23185 dovecot-2.3.16/src/lib-mail/message-header-parser.c
 | ||||
| --- dovecot-2.3.16/src/lib-mail/message-header-parser.c.CVE-2024-23185	2021-08-06 11:25:51.000000000 +0200
 | ||||
| +++ dovecot-2.3.16/src/lib-mail/message-header-parser.c	2024-08-20 23:29:25.214183880 +0200
 | ||||
| @@ -17,6 +17,9 @@ struct message_header_parser_ctx {
 | ||||
|  	string_t *name; | ||||
|  	buffer_t *value_buf; | ||||
|   | ||||
| +	size_t header_block_max_size;
 | ||||
| +	size_t header_block_total_size;
 | ||||
| +
 | ||||
|  	enum message_header_parser_flags flags; | ||||
|  	bool skip_line:1; | ||||
|  	bool has_nuls:1; | ||||
| @@ -34,6 +37,7 @@ message_parse_header_init(struct istream
 | ||||
|  	ctx->name = str_new(default_pool, 128); | ||||
|  	ctx->flags = flags; | ||||
|  	ctx->value_buf = buffer_create_dynamic(default_pool, 4096); | ||||
| +	ctx->header_block_max_size = MESSAGE_HEADER_BLOCK_DEFAULT_MAX_SIZE;
 | ||||
|  	i_stream_ref(input); | ||||
|   | ||||
|  	if (hdr_size != NULL) | ||||
| @@ -41,6 +45,21 @@ message_parse_header_init(struct istream
 | ||||
|  	return ctx; | ||||
|  } | ||||
|   | ||||
| +void
 | ||||
| +message_parse_header_set_limit(struct message_header_parser_ctx *parser,
 | ||||
| +			       size_t header_block_max_size)
 | ||||
| +{
 | ||||
| +	parser->header_block_max_size = header_block_max_size;
 | ||||
| +}
 | ||||
| +
 | ||||
| +void
 | ||||
| +message_parse_header_lower_limit(struct message_header_parser_ctx *parser,
 | ||||
| +				 size_t header_block_max_size)
 | ||||
| +{
 | ||||
| +	if (header_block_max_size < parser->header_block_max_size)
 | ||||
| +		message_parse_header_set_limit(parser, header_block_max_size);
 | ||||
| +}
 | ||||
| +
 | ||||
|  void message_parse_header_deinit(struct message_header_parser_ctx **_ctx) | ||||
|  { | ||||
|  	struct message_header_parser_ctx *ctx = *_ctx; | ||||
| @@ -73,6 +92,7 @@ int message_parse_header_next(struct mes
 | ||||
|  		/* new header line */ | ||||
|  		line->name_offset = ctx->input->v_offset; | ||||
|  		colon_pos = UINT_MAX; | ||||
| +		ctx->header_block_total_size += ctx->value_buf->used;
 | ||||
|  		buffer_set_used_size(ctx->value_buf, 0); | ||||
|  	} | ||||
|   | ||||
| @@ -326,33 +346,39 @@ int message_parse_header_next(struct mes
 | ||||
|  		line->middle = str_data(ctx->name) + line->name_len + 1; | ||||
|  	} | ||||
|   | ||||
| +	line->value_len = I_MIN(line->value_len, ctx->header_block_max_size);
 | ||||
| +	size_t line_value_size = line->value_len;
 | ||||
| +	size_t header_total_used = ctx->header_block_total_size + ctx->value_buf->used;
 | ||||
| +	size_t line_available = ctx->header_block_max_size <= header_total_used ? 0 :
 | ||||
| +				ctx->header_block_max_size - header_total_used;
 | ||||
| +	line_value_size = I_MIN(line_value_size, line_available);
 | ||||
| +
 | ||||
|  	if (!line->continued) { | ||||
|  		/* first header line. make a copy of the line since we can't | ||||
|  		   really trust input stream not to lose it. */ | ||||
| -		buffer_append(ctx->value_buf, line->value, line->value_len);
 | ||||
| +		buffer_append(ctx->value_buf, line->value, line_value_size);
 | ||||
|  		line->value = line->full_value = ctx->value_buf->data; | ||||
| -		line->full_value_len = line->value_len;
 | ||||
| +		line->full_value_len = line->value_len = line_value_size;
 | ||||
|  	} else if (line->use_full_value) { | ||||
|  		/* continue saving the full value. */ | ||||
|  		if (last_no_newline) { | ||||
|  			/* line is longer than fit into our buffer, so we | ||||
|  			   were forced to break it into multiple | ||||
|  			   message_header_lines */ | ||||
| -		} else {
 | ||||
| -			if (last_crlf)
 | ||||
| +		} else if (line_value_size > 1) {
 | ||||
| +			if (last_crlf && line_value_size > 2)
 | ||||
|  				buffer_append_c(ctx->value_buf, '\r'); | ||||
|  			buffer_append_c(ctx->value_buf, '\n'); | ||||
|  		} | ||||
|  		if ((ctx->flags & MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE) != 0 && | ||||
|  		    line->value_len > 0 && line->value[0] != ' ' && | ||||
| -		    IS_LWSP(line->value[0])) {
 | ||||
| +		    IS_LWSP(line->value[0]) &&
 | ||||
| +		    line_value_size > 0) {
 | ||||
|  			buffer_append_c(ctx->value_buf, ' '); | ||||
| -			buffer_append(ctx->value_buf,
 | ||||
| -				      line->value + 1, line->value_len - 1);
 | ||||
| -		} else {
 | ||||
| -			buffer_append(ctx->value_buf,
 | ||||
| -				      line->value, line->value_len);
 | ||||
| -		}
 | ||||
| +			buffer_append(ctx->value_buf, line->value + 1, line_value_size - 1);
 | ||||
| +		} else
 | ||||
| +			buffer_append(ctx->value_buf, line->value, line_value_size);
 | ||||
| +
 | ||||
|  		line->full_value = ctx->value_buf->data; | ||||
|  		line->full_value_len = ctx->value_buf->used; | ||||
|  	} else { | ||||
| diff -up dovecot-2.3.16/src/lib-mail/message-header-parser.h.CVE-2024-23185 dovecot-2.3.16/src/lib-mail/message-header-parser.h
 | ||||
| --- dovecot-2.3.16/src/lib-mail/message-header-parser.h.CVE-2024-23185	2021-08-06 11:25:51.000000000 +0200
 | ||||
| +++ dovecot-2.3.16/src/lib-mail/message-header-parser.h	2024-08-20 22:55:36.530652449 +0200
 | ||||
| @@ -1,6 +1,9 @@
 | ||||
|  #ifndef MESSAGE_HEADER_PARSER_H | ||||
|  #define MESSAGE_HEADER_PARSER_H | ||||
|   | ||||
| +/* This can be overridden by message_parse_header_set_limit() */
 | ||||
| +#define MESSAGE_HEADER_BLOCK_DEFAULT_MAX_SIZE ((size_t) 10 * 1024*1024)
 | ||||
| +
 | ||||
|  #define IS_LWSP(c) \ | ||||
|  	((c) == ' ' || (c) == '\t') | ||||
|   | ||||
| @@ -48,6 +51,13 @@ message_parse_header_init(struct istream
 | ||||
|  			  enum message_header_parser_flags flags) ATTR_NULL(2); | ||||
|  void message_parse_header_deinit(struct message_header_parser_ctx **ctx); | ||||
|   | ||||
| +void
 | ||||
| +message_parse_header_set_limit(struct message_header_parser_ctx *parser,
 | ||||
| +			       size_t header_block_max_size);
 | ||||
| +void
 | ||||
| +message_parse_header_lower_limit(struct message_header_parser_ctx *parser,
 | ||||
| +				 size_t header_block_max_size);
 | ||||
| +
 | ||||
|  /* Read and return next header line. Returns 1 if header is returned, 0 if | ||||
|     input stream is non-blocking and more data needs to be read, -1 when all is | ||||
|     done or error occurred (see stream's error status). */ | ||||
| diff -up dovecot-2.3.16/src/lib-mail/message-parser.c.CVE-2024-23185 dovecot-2.3.16/src/lib-mail/message-parser.c
 | ||||
| --- dovecot-2.3.16/src/lib-mail/message-parser.c.CVE-2024-23185	2021-08-06 11:25:51.000000000 +0200
 | ||||
| +++ dovecot-2.3.16/src/lib-mail/message-parser.c	2024-08-20 22:55:36.531652458 +0200
 | ||||
| @@ -617,7 +617,18 @@ static int parse_next_header(struct mess
 | ||||
|  	} | ||||
|  	if (ret < 0) { | ||||
|  		/* no boundary */ | ||||
| +		size_t headers_available =
 | ||||
| +			ctx->all_headers_max_size > ctx->all_headers_total_size ?
 | ||||
| +			ctx->all_headers_max_size - ctx->all_headers_total_size : 0;
 | ||||
| +		message_parse_header_lower_limit(ctx->hdr_parser_ctx, headers_available);
 | ||||
|  		ret = message_parse_header_next(ctx->hdr_parser_ctx, &hdr); | ||||
| +		if (ret > 0) {
 | ||||
| +			if (!hdr->continues) {
 | ||||
| +				ctx->all_headers_total_size += hdr->name_len;
 | ||||
| +				ctx->all_headers_total_size += hdr->middle_len;
 | ||||
| +			}
 | ||||
| +			ctx->all_headers_total_size += hdr->value_len;
 | ||||
| +		}
 | ||||
|  		if (ret == 0 || (ret < 0 && ctx->input->stream_errno != 0)) { | ||||
|  			ctx->want_count = i_stream_get_data_size(ctx->input) + 1; | ||||
|  			return ret; | ||||
| @@ -762,6 +773,9 @@ message_parser_init_int(struct istream *
 | ||||
|  	ctx->max_total_mime_parts = set->max_total_mime_parts != 0 ? | ||||
|  		set->max_total_mime_parts : | ||||
|  		MESSAGE_PARSER_DEFAULT_MAX_TOTAL_MIME_PARTS; | ||||
| +	ctx->all_headers_max_size = set->all_headers_max_size != 0 ?
 | ||||
| +		set->all_headers_max_size :
 | ||||
| +		MESSAGE_PARSER_DEFAULT_ALL_HEADERS_MAX_SIZE;
 | ||||
|  	ctx->input = input; | ||||
|  	i_stream_ref(input); | ||||
|  	return ctx; | ||||
| @@ -779,6 +793,7 @@ message_parser_init(pool_t part_pool, st
 | ||||
|  	ctx->next_part = &ctx->part->children; | ||||
|  	ctx->parse_next_block = parse_next_header_init; | ||||
|  	ctx->total_parts_count = 1; | ||||
| +	ctx->all_headers_total_size = 0;
 | ||||
|  	i_array_init(&ctx->next_part_stack, 4); | ||||
|  	return ctx; | ||||
|  } | ||||
| diff -up dovecot-2.3.16/src/lib-mail/message-parser.h.CVE-2024-23185 dovecot-2.3.16/src/lib-mail/message-parser.h
 | ||||
| --- dovecot-2.3.16/src/lib-mail/message-parser.h.CVE-2024-23185	2021-08-06 11:25:51.000000000 +0200
 | ||||
| +++ dovecot-2.3.16/src/lib-mail/message-parser.h	2024-08-20 22:55:36.531652458 +0200
 | ||||
| @@ -19,6 +19,7 @@ enum message_parser_flags {
 | ||||
|   | ||||
|  #define MESSAGE_PARSER_DEFAULT_MAX_NESTED_MIME_PARTS 100 | ||||
|  #define MESSAGE_PARSER_DEFAULT_MAX_TOTAL_MIME_PARTS 10000 | ||||
| +#define MESSAGE_PARSER_DEFAULT_ALL_HEADERS_MAX_SIZE ((size_t) 50 * 1024*1024)
 | ||||
|   | ||||
|  struct message_parser_settings { | ||||
|  	enum message_header_parser_flags hdr_flags; | ||||
| @@ -30,6 +31,11 @@ struct message_parser_settings {
 | ||||
|  	/* Maximum MIME parts in total. | ||||
|  	   0 = MESSAGE_PARSER_DEFAULT_MAX_TOTAL_MIME_PARTS. */ | ||||
|  	unsigned int max_total_mime_parts; | ||||
| +
 | ||||
| +	/* Maximum bytes fore headers in top header plus all
 | ||||
| +	   MIME sections headers
 | ||||
| +	   0 = MESSAGE_PARSER_DEFAULT_ALL_HEADERS_MAX_SIZE */
 | ||||
| +	size_t all_headers_max_size;
 | ||||
|  }; | ||||
|   | ||||
|  struct message_parser_ctx; | ||||
| diff -up dovecot-2.3.16/src/lib-mail/message-parser-private.h.CVE-2024-23185 dovecot-2.3.16/src/lib-mail/message-parser-private.h
 | ||||
| --- dovecot-2.3.16/src/lib-mail/message-parser-private.h.CVE-2024-23185	2021-08-06 11:25:51.000000000 +0200
 | ||||
| +++ dovecot-2.3.16/src/lib-mail/message-parser-private.h	2024-08-20 22:55:36.531652458 +0200
 | ||||
| @@ -30,6 +30,8 @@ struct message_parser_ctx {
 | ||||
|  	enum message_parser_flags flags; | ||||
|  	unsigned int max_nested_mime_parts; | ||||
|  	unsigned int max_total_mime_parts; | ||||
| +	size_t all_headers_max_size;
 | ||||
| +	size_t all_headers_total_size;
 | ||||
|   | ||||
|  	char *last_boundary; | ||||
|  	struct message_boundary *boundaries; | ||||
| diff -up dovecot-2.3.16/src/lib-mail/test-message-header-parser.c.CVE-2024-23185 dovecot-2.3.16/src/lib-mail/test-message-header-parser.c
 | ||||
| --- dovecot-2.3.16/src/lib-mail/test-message-header-parser.c.CVE-2024-23185	2021-08-06 11:25:51.000000000 +0200
 | ||||
| +++ dovecot-2.3.16/src/lib-mail/test-message-header-parser.c	2024-08-20 23:23:18.169196280 +0200
 | ||||
| @@ -332,6 +332,71 @@ static void test_message_header_parser_n
 | ||||
|  	test_end(); | ||||
|  } | ||||
|   | ||||
| +#define assert_parsed_field(line, expected, actual, len) STMT_START {		\
 | ||||
| +	test_assert_idx(memcmp(expected, actual, strlen(expected)) == 0, line); \
 | ||||
| +	test_assert_cmp_idx(strlen(expected), ==, len, line);			\
 | ||||
| +} STMT_END
 | ||||
| +
 | ||||
| +/* NOTE: implicit variables: (parser, hdr) */
 | ||||
| +#define assert_parse_line(line, exp_name, exp_value, exp_full) STMT_START {		\
 | ||||
| +	test_assert_idx(message_parse_header_next(parser, &hdr) > 0, line); 		\
 | ||||
| +	assert_parsed_field(line, exp_name,   hdr->name,       hdr->name_len);		\
 | ||||
| +	assert_parsed_field(line, exp_value,  hdr->value,      hdr->value_len);		\
 | ||||
| +	assert_parsed_field(line, exp_full,   hdr->full_value, hdr->full_value_len);	\
 | ||||
| +	if (hdr->continues) hdr->use_full_value = TRUE;					\
 | ||||
| +} STMT_END
 | ||||
| +
 | ||||
| +static const unsigned char test_message_header_truncation_input[] =
 | ||||
| +	/*01*/	"header1: this is short\n"
 | ||||
| +	/*02*/	"header2: this is multiline\n"
 | ||||
| +	/*03*/	" and long 343638404244464850525456586062\n"
 | ||||
| +	/*04*/	" 64666870727476788082848688909294969800\n"
 | ||||
| +	/*05*/	" 02040608101214161820222426283032343638\n"
 | ||||
| +	/*06*/	"header3: I should not appear at all\n"
 | ||||
| +	/*07*/	"\n";
 | ||||
| +
 | ||||
| +static void test_message_header_truncation_clean_oneline(void)
 | ||||
| +{
 | ||||
| +	test_begin("message header parser truncate + CLEAN_ONELINE flag");
 | ||||
| +	struct message_header_line *hdr = NULL;
 | ||||
| +	struct istream *input = test_istream_create_data(test_message_header_truncation_input, sizeof(test_message_header_truncation_input));
 | ||||
| +	struct message_header_parser_ctx *parser = message_parse_header_init(input, NULL, MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE);
 | ||||
| +	message_parse_header_set_limit(parser, 96);
 | ||||
| +
 | ||||
| +	assert_parse_line( 1, "header1", "this is short",	                     "this is short");
 | ||||
| +	assert_parse_line( 2, "header2", "this is multiline", 	                     "this is multiline");
 | ||||
| +	assert_parse_line( 3, "header2", " and long 343638404244464850525456586062", "this is multiline and long 343638404244464850525456586062");
 | ||||
| +	assert_parse_line( 4, "header2", " 64666870727476788082848688909294969800",  "this is multiline and long 343638404244464850525456586062 6466687072747678808284868");
 | ||||
| +	assert_parse_line( 5, "header2", " 02040608101214161820222426283032343638",  "this is multiline and long 343638404244464850525456586062 6466687072747678808284868");
 | ||||
| +	assert_parse_line( 6, "header3", "", "");
 | ||||
| +	test_assert(message_parse_header_next(parser, &hdr) > 0 && hdr->eoh);
 | ||||
| +
 | ||||
| +	message_parse_header_deinit(&parser);
 | ||||
| +	i_stream_unref(&input);
 | ||||
| +	test_end();
 | ||||
| +}
 | ||||
| +
 | ||||
| +static void test_message_header_truncation_flag0(void)
 | ||||
| +{
 | ||||
| +	test_begin("message header parser truncate + NO flags");
 | ||||
| +	struct message_header_line *hdr = NULL;
 | ||||
| +	struct istream *input = test_istream_create_data(test_message_header_truncation_input, sizeof(test_message_header_truncation_input));
 | ||||
| +	struct message_header_parser_ctx *parser = message_parse_header_init(input, NULL, 0);
 | ||||
| +	message_parse_header_set_limit(parser, 96);
 | ||||
| +
 | ||||
| +	assert_parse_line( 1, "header1", "this is short",	                     "this is short");
 | ||||
| +	assert_parse_line( 2, "header2", "this is multiline", 	                     "this is multiline");
 | ||||
| +	assert_parse_line( 3, "header2", " and long 343638404244464850525456586062", "this is multiline\n and long 343638404244464850525456586062");
 | ||||
| +	assert_parse_line( 4, "header2", " 64666870727476788082848688909294969800",  "this is multiline\n and long 343638404244464850525456586062\n 646668707274767880828486");
 | ||||
| +	assert_parse_line( 5, "header2", " 02040608101214161820222426283032343638",  "this is multiline\n and long 343638404244464850525456586062\n 646668707274767880828486");
 | ||||
| +	assert_parse_line( 6, "header3", "", "");
 | ||||
| +	test_assert(message_parse_header_next(parser, &hdr) > 0 && hdr->eoh);
 | ||||
| +
 | ||||
| +	message_parse_header_deinit(&parser);
 | ||||
| +	i_stream_unref(&input);
 | ||||
| +	test_end();
 | ||||
| +}
 | ||||
| +
 | ||||
|  int main(void) | ||||
|  { | ||||
|  	static void (*const test_functions[])(void) = { | ||||
| @@ -341,6 +406,8 @@ int main(void)
 | ||||
|  		test_message_header_parser_extra_cr_in_eoh, | ||||
|  		test_message_header_parser_no_eoh, | ||||
|  		test_message_header_parser_nul, | ||||
| +		test_message_header_truncation_flag0,
 | ||||
| +		test_message_header_truncation_clean_oneline,
 | ||||
|  		NULL | ||||
|  	}; | ||||
|  	return test_run(test_functions); | ||||
| diff -up dovecot-2.3.16/src/lib-mail/test-message-parser.c.CVE-2024-23185 dovecot-2.3.16/src/lib-mail/test-message-parser.c
 | ||||
| --- dovecot-2.3.16/src/lib-mail/test-message-parser.c.CVE-2024-23185	2021-08-06 11:25:51.000000000 +0200
 | ||||
| +++ dovecot-2.3.16/src/lib-mail/test-message-parser.c	2024-08-20 22:55:36.531652458 +0200
 | ||||
| @@ -1369,6 +1369,158 @@ static const char input_msg[] =
 | ||||
|  	test_end(); | ||||
|  } | ||||
|   | ||||
| +#define test_assert_virtual_size(part) \
 | ||||
| +	test_assert((part).virtual_size == (part).lines + (part).physical_size)
 | ||||
| +
 | ||||
| +#define test_assert_part(part, flags_, children, h_lines, h_size, b_lines, b_size ) \
 | ||||
| +STMT_START { 								\
 | ||||
| +	test_assert((part)->flags == (flags_));				\
 | ||||
| +	test_assert((part)->children_count == children);		\
 | ||||
| +	test_assert((part)->header_size.lines == h_lines);		\
 | ||||
| +	test_assert((part)->header_size.physical_size == h_size);	\
 | ||||
| +	test_assert((part)->body_size.lines == b_lines);		\
 | ||||
| +	test_assert((part)->body_size.physical_size == b_size);		\
 | ||||
| +	test_assert_virtual_size((part)->header_size);			\
 | ||||
| +	test_assert_virtual_size((part)->body_size);			\
 | ||||
| +} STMT_END
 | ||||
| +
 | ||||
| +static const enum message_part_flags FLAGS_MULTIPART =
 | ||||
| +	MESSAGE_PART_FLAG_IS_MIME | MESSAGE_PART_FLAG_MULTIPART;
 | ||||
| +static const enum message_part_flags FLAGS_RFC822 =
 | ||||
| +	MESSAGE_PART_FLAG_IS_MIME | MESSAGE_PART_FLAG_MESSAGE_RFC822;
 | ||||
| +static const enum message_part_flags FLAGS_TEXT =
 | ||||
| +	MESSAGE_PART_FLAG_IS_MIME | MESSAGE_PART_FLAG_TEXT;
 | ||||
| +
 | ||||
| +static const char too_many_header_bytes_input_msg[] =
 | ||||
| +	"Content-Type: multipart/mixed; boundary=\"1\"\n\n"
 | ||||
| +		"--1\n"
 | ||||
| +		"Content-Type: multipart/mixed; boundary=\"2\"\n\n"
 | ||||
| +			"--2\n"
 | ||||
| +			"Content-Type: message/rfc822\n\n"
 | ||||
| +				"Content-Type: text/plain\n\n1-rfc822\n"
 | ||||
| +			"--2\n"
 | ||||
| +			"Content-Type: message/rfc822\n\n"
 | ||||
| +				"Content-Type: text/plain\n\n2-rfc822\n"
 | ||||
| +		"--1\n"
 | ||||
| +			"Content-Type: message/rfc822\n\n"
 | ||||
| +				"Content-Type: text/plain\n\n3-rfc822\n";
 | ||||
| +
 | ||||
| +static void test_message_parser_too_many_header_bytes_run(
 | ||||
| +	const struct message_parser_settings *parser_set,
 | ||||
| +	pool_t *pool_r, struct istream **input_r,
 | ||||
| +	struct message_part **parts_r)
 | ||||
| +{
 | ||||
| +	*pool_r = pool_alloconly_create("message parser", 10240);
 | ||||
| +	*input_r = test_istream_create(too_many_header_bytes_input_msg);
 | ||||
| +	struct message_parser_ctx *parser = message_parser_init(*pool_r, *input_r, parser_set);
 | ||||
| +
 | ||||
| +	int ret;
 | ||||
| +	struct message_block block ATTR_UNUSED;
 | ||||
| +	while ((ret = message_parser_parse_next_block(parser, &block)) > 0);
 | ||||
| +	test_assert(ret < 0);
 | ||||
| +
 | ||||
| +	message_parser_deinit(&parser, parts_r);
 | ||||
| +}
 | ||||
| +
 | ||||
| +static void test_message_parser_too_many_header_bytes_default(void)
 | ||||
| +{
 | ||||
| +	test_begin("message parser too many header bytes default");
 | ||||
| +
 | ||||
| +	pool_t pool;
 | ||||
| +	struct istream *input;
 | ||||
| +	struct message_part *part_root;
 | ||||
| +	const struct message_parser_settings parser_set = { .all_headers_max_size = 0 };
 | ||||
| +
 | ||||
| +	test_message_parser_too_many_header_bytes_run(&parser_set, &pool, &input, &part_root);
 | ||||
| +
 | ||||
| +	// test_assert_part(part, flags_, children, h_lines, h_size, b_lines, b_size )
 | ||||
| +
 | ||||
| +	test_assert_part(part_root, FLAGS_MULTIPART, 7, 2, 45, 21, 256);
 | ||||
| +	test_assert(part_root->parent == NULL);
 | ||||
| +
 | ||||
| +		struct message_part *part_1 = part_root->children;
 | ||||
| +		test_assert_part(part_1, FLAGS_MULTIPART, 4, 2, 45, 11, 137);
 | ||||
| +
 | ||||
| +			struct message_part *part_1_1 = part_1->children;
 | ||||
| +			test_assert_part(part_1_1, FLAGS_RFC822, 1, 2, 30, 2, 34);
 | ||||
| +
 | ||||
| +				struct message_part *part_1_1_1 = part_1_1->children;
 | ||||
| +				test_assert_part(part_1_1_1, FLAGS_TEXT, 0, 2, 26, 0, 8);
 | ||||
| +
 | ||||
| +				test_assert(part_1_1_1->next == NULL);
 | ||||
| +
 | ||||
| +			struct message_part *part_1_2 = part_1_1->next;
 | ||||
| +			test_assert_part(part_1_2, FLAGS_RFC822, 1, 2, 30, 2, 34);
 | ||||
| +
 | ||||
| +				struct message_part *part_1_2_1 = part_1_2->children;
 | ||||
| +				test_assert_part(part_1_2_1, FLAGS_TEXT, 0, 2, 26, 0, 8);
 | ||||
| +
 | ||||
| +				test_assert(part_1_2_1->next == NULL);
 | ||||
| +
 | ||||
| +			test_assert(part_1_2->next == NULL);
 | ||||
| +
 | ||||
| +		struct message_part *part_2 = part_1->next;
 | ||||
| +		test_assert_part(part_2, FLAGS_RFC822, 1, 2, 30, 3, 35);
 | ||||
| +
 | ||||
| +			struct message_part *part_2_1 = part_2->children;
 | ||||
| +			test_assert_part(part_2_1, FLAGS_TEXT, 0, 2, 26, 1, 9);
 | ||||
| +			test_assert(part_2_1->next == NULL);
 | ||||
| +
 | ||||
| +		test_assert(part_2->next == NULL);
 | ||||
| +
 | ||||
| +	test_assert(part_root->next == NULL);
 | ||||
| +
 | ||||
| +	test_parsed_parts(input, part_root);
 | ||||
| +	i_stream_unref(&input);
 | ||||
| +	pool_unref(&pool);
 | ||||
| +	test_end();
 | ||||
| +}
 | ||||
| +
 | ||||
| +static void test_message_parser_too_many_header_bytes_100(void)
 | ||||
| +{
 | ||||
| +	test_begin("message parser too many header bytes 100");
 | ||||
| +
 | ||||
| +	pool_t pool;
 | ||||
| +	struct istream *input;
 | ||||
| +	struct message_part *part_root;
 | ||||
| +	const struct message_parser_settings parser_set = { .all_headers_max_size = 100 };
 | ||||
| +
 | ||||
| +	test_message_parser_too_many_header_bytes_run(&parser_set, &pool, &input, &part_root);
 | ||||
| +
 | ||||
| +	// test_assert_part(part, flags_, children, h_lines, h_size, b_lines, b_size )
 | ||||
| +
 | ||||
| +	test_assert_part(part_root, FLAGS_MULTIPART, 5, 2, 45, 21, 256);
 | ||||
| +	test_assert(part_root->parent == NULL);
 | ||||
| +
 | ||||
| +		struct message_part *part_1 = part_root->children;
 | ||||
| +		test_assert_part(part_1, FLAGS_MULTIPART, 3, 2, 45, 11, 137);
 | ||||
| +
 | ||||
| +			struct message_part *part_1_1 = part_1->children;
 | ||||
| +			test_assert_part(part_1_1, FLAGS_RFC822, 1, 2, 30, 2, 34);
 | ||||
| +
 | ||||
| +				struct message_part *part_1_1_1 = part_1_1->children;
 | ||||
| +				test_assert_part(part_1_1_1, MESSAGE_PART_FLAG_IS_MIME, 0, 2, 26, 0, 8);
 | ||||
| +
 | ||||
| +				test_assert(part_1_1_1->next == NULL);
 | ||||
| +
 | ||||
| +			struct message_part *part_1_2 = part_1_1->next;
 | ||||
| +			test_assert_part(part_1_2, MESSAGE_PART_FLAG_IS_MIME, 0, 2, 30, 2, 34);
 | ||||
| +
 | ||||
| +			test_assert(part_1_2->next == NULL);
 | ||||
| +
 | ||||
| +		struct message_part *part_2 = part_1->next;
 | ||||
| +		test_assert_part(part_2, MESSAGE_PART_FLAG_IS_MIME, 0, 2, 30, 3, 35);
 | ||||
| +
 | ||||
| +		test_assert(part_2->next == NULL);
 | ||||
| +
 | ||||
| +	test_assert(part_root->next == NULL);
 | ||||
| +
 | ||||
| +	test_parsed_parts(input, part_root);
 | ||||
| +	i_stream_unref(&input);
 | ||||
| +	pool_unref(&pool);
 | ||||
| +	test_end();
 | ||||
| +}
 | ||||
| +
 | ||||
|  int main(void) | ||||
|  { | ||||
|  	static void (*const test_functions[])(void) = { | ||||
| @@ -1392,6 +1544,8 @@ int main(void)
 | ||||
|  		test_message_parser_mime_part_limit_rfc822, | ||||
|  		test_message_parser_mime_version, | ||||
|  		test_message_parser_mime_version_missing, | ||||
| +		test_message_parser_too_many_header_bytes_default,
 | ||||
| +		test_message_parser_too_many_header_bytes_100,
 | ||||
|  		NULL | ||||
|  	}; | ||||
|  	return test_run(test_functions); | ||||
							
								
								
									
										16
									
								
								dovecot.spec
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								dovecot.spec
									
									
									
									
									
								
							| @ -6,7 +6,7 @@ Name: dovecot | ||||
| Epoch: 1 | ||||
| Version: 2.3.16 | ||||
| %global prever %{nil} | ||||
| Release: 13%{?dist} | ||||
| Release: 14%{?dist} | ||||
| #dovecot itself is MIT, a few sources are PD, pigeonhole is LGPLv2 | ||||
| License: MIT and LGPLv2 | ||||
| 
 | ||||
| @ -65,6 +65,14 @@ Patch26: dovecot-2.3-ph_scriptcmp.patch | ||||
| # fix test failing due to too long path with all the mock path prefixes | ||||
| Patch27: dovecot-2.3.21-test-socket-path.patch | ||||
| 
 | ||||
| # from upstream for < 2.3.21.1, RHEL-55212 | ||||
| # https://github.com/dovecot/core/compare/8e4c42d%5E...1481c04.patch | ||||
| Patch28: dovecot-2.3.21.1-CVE-2024-23184.patch | ||||
| 
 | ||||
| # from upstream for < 2.3.21.1, RHEL-55224 | ||||
| # https://github.com/dovecot/core/compare/f020e13%5E...ce88c33.patch | ||||
| Patch29: dovecot-2.3.21.1-CVE-2024-23185.patch | ||||
| 
 | ||||
| BuildRequires: gcc, gcc-c++, openssl-devel, pam-devel, zlib-devel, bzip2-devel, libcap-devel | ||||
| BuildRequires: libtool, autoconf, automake, pkgconfig | ||||
| BuildRequires: sqlite-devel | ||||
| @ -170,6 +178,8 @@ mv dovecot-2.3-pigeonhole-%{pigeonholever} dovecot-pigeonhole | ||||
| %patch -P 25 -p1 -b .ph_optglob | ||||
| %patch -P 26 -p1 -b .ph_scriptcmp | ||||
| %patch -P 27 -p1 -b .test-socket-path | ||||
| %patch -P 28 -p1 -b .CVE-2024-23184 | ||||
| %patch -P 29 -p1 -b .CVE-2024-23185 | ||||
| cp run-test-valgrind.supp dovecot-pigeonhole/ | ||||
| # valgrind would fail with shell wrapper | ||||
| echo "testsuite" >dovecot-pigeonhole/run-test-valgrind.exclude | ||||
| @ -514,6 +524,10 @@ make check | ||||
| %{_libdir}/%{name}/dict/libdriver_pgsql.so | ||||
| 
 | ||||
| %changelog | ||||
| * Tue Aug 20 2024 Michal Hlavinka <mhlavink@redhat.com> - 1:2.3.16-14 | ||||
| - fix CVE-2024-23184: using a large number of address headers may trigger a denial of service (RHEL-55212) | ||||
| - fix CVE-2024-23185: very large headers can cause resource exhaustion when parsing message (RHEL-55224) | ||||
| 
 | ||||
| * Mon Aug 05 2024 Michal Hlavinka <mhlavink@redhat.com> - 1:2.3.16-13 | ||||
| - rebuild with updated tests | ||||
| 
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user