Blame SOURCES/dovecot-2.3.13-CVE_2020_25275-part1.patch

b62b43
From b9a2f18466a0d3377bab3e7a57691bdd75d8507c Mon Sep 17 00:00:00 2001
b62b43
From: Timo Sirainen <timo.sirainen@open-xchange.com>
b62b43
Date: Mon, 17 Aug 2020 17:32:11 +0300
b62b43
Subject: [PATCH] lib-imap: Add imap_parser_read_tag() and _read_command_name()
b62b43
b62b43
---
b62b43
 src/lib-imap/imap-parser.c      | 67 +++++++++++++++++++++++++++++++++
b62b43
 src/lib-imap/imap-parser.h      |  7 ++++
b62b43
 src/lib-imap/test-imap-parser.c | 67 +++++++++++++++++++++++++++++++++
b62b43
 3 files changed, 141 insertions(+)
b62b43
b62b43
diff --git a/src/lib-imap/imap-parser.c b/src/lib-imap/imap-parser.c
b62b43
index b6c6e63fb1..52d79282fa 100644
b62b43
--- a/src/lib-imap/imap-parser.c
b62b43
+++ b/src/lib-imap/imap-parser.c
b62b43
@@ -947,3 +947,70 @@ const char *imap_parser_read_word(struct imap_parser *parser)
b62b43
 		return NULL;
b62b43
 	}
b62b43
 }
b62b43
+
b62b43
+static int
b62b43
+imap_parser_read_next_atom(struct imap_parser *parser, bool parsing_tag,
b62b43
+			   const char **atom_r)
b62b43
+{
b62b43
+	const unsigned char *data;
b62b43
+	size_t i, data_size;
b62b43
+
b62b43
+	data = i_stream_get_data(parser->input, &data_size);
b62b43
+
b62b43
+	/*
b62b43
+	   tag            = 1*<any ASTRING-CHAR except "+">
b62b43
+	   ASTRING-CHAR   = ATOM-CHAR / resp-specials
b62b43
+	   ATOM-CHAR      = <any CHAR except atom-specials>
b62b43
+
b62b43
+	   x-command      = "X" atom <experimental command arguments>
b62b43
+	   atom           = 1*ATOM-CHAR
b62b43
+	*/
b62b43
+	for (i = 0; i < data_size; i++) {
b62b43
+		/* explicitly check for atom-specials, because
b62b43
+		   IS_ATOM_PARSER_INPUT() allows some atom-specials */
b62b43
+		switch (data[i]) {
b62b43
+		case ' ':
b62b43
+		case '\r':
b62b43
+		case '\n':
b62b43
+			data_size = i + (data[i] == ' ' ? 1 : 0);
b62b43
+			parser->line_size += data_size;
b62b43
+			i_stream_skip(parser->input, data_size);
b62b43
+			*atom_r = p_strndup(parser->pool, data, i);
b62b43
+			/* don't allow empty string */
b62b43
+			return i == 0 ? -1 : 1;
b62b43
+		/* atom-specials: */
b62b43
+		case '(':
b62b43
+		case ')':
b62b43
+		case '{':
b62b43
+		/* list-wildcards: */
b62b43
+		case '%':
b62b43
+		case '*':
b62b43
+		/* quoted-specials: */
b62b43
+		case '"':
b62b43
+		case '\\':
b62b43
+		/* resp-specials: */
b62b43
+		case ']':
b62b43
+			return -1;
b62b43
+		case '+':
b62b43
+			if (parsing_tag)
b62b43
+				return -1;
b62b43
+			break;
b62b43
+		default:
b62b43
+			if ((unsigned char)data[i] < ' ' ||
b62b43
+			    (unsigned char)data[i] >= 0x80)
b62b43
+				return -1;
b62b43
+		}
b62b43
+	}
b62b43
+	return 0;
b62b43
+}
b62b43
+
b62b43
+int imap_parser_read_tag(struct imap_parser *parser, const char **tag_r)
b62b43
+{
b62b43
+	return imap_parser_read_next_atom(parser, TRUE, tag_r);
b62b43
+}
b62b43
+
b62b43
+int imap_parser_read_command_name(struct imap_parser *parser,
b62b43
+				  const char **name_r)
b62b43
+{
b62b43
+	return imap_parser_read_next_atom(parser, FALSE, name_r);
b62b43
+}
b62b43
diff --git a/src/lib-imap/imap-parser.h b/src/lib-imap/imap-parser.h
b62b43
index e5d01c17f2..5e09d61d2b 100644
b62b43
--- a/src/lib-imap/imap-parser.h
b62b43
+++ b/src/lib-imap/imap-parser.h
b62b43
@@ -101,5 +101,12 @@ int imap_parser_finish_line(struct imap_parser *parser, unsigned int count,
b62b43
 /* Read one word - used for reading tag and command name.
b62b43
    Returns NULL if more data is needed. */
b62b43
 const char *imap_parser_read_word(struct imap_parser *parser);
b62b43
+/* Read command tag. Returns 1 if tag was returned, 0 if more data is needed,
b62b43
+   -1 if input isn't a valid tag. */
b62b43
+int imap_parser_read_tag(struct imap_parser *parser, const char **tag_r);
b62b43
+/* Read command name. Returns 1 if command name was returned, 0 if more data is
b62b43
+   needed, -1 if input isn't a valid command name string. */
b62b43
+int imap_parser_read_command_name(struct imap_parser *parser,
b62b43
+				  const char **name_r);
b62b43
 
b62b43
 #endif
b62b43
diff --git a/src/lib-imap/test-imap-parser.c b/src/lib-imap/test-imap-parser.c
b62b43
index 93ef8fd59b..3ca4e34858 100644
b62b43
--- a/src/lib-imap/test-imap-parser.c
b62b43
+++ b/src/lib-imap/test-imap-parser.c
b62b43
@@ -79,10 +79,77 @@ static void test_imap_parser_partial_list(void)
b62b43
 	test_end();
b62b43
 }
b62b43
 
b62b43
+static void test_imap_parser_read_tag_cmd(void)
b62b43
+{
b62b43
+	enum read_type {
b62b43
+		BOTH,
b62b43
+		TAG,
b62b43
+		COMMAND
b62b43
+	};
b62b43
+	struct {
b62b43
+		const char *input;
b62b43
+		const char *tag;
b62b43
+		int ret;
b62b43
+		enum read_type type;
b62b43
+	} tests[] = {
b62b43
+		{ "tag foo", "tag", 1, BOTH },
b62b43
+		{ "tag\r", "tag", 1, BOTH },
b62b43
+		{ "tag\rfoo", "tag", 1, BOTH },
b62b43
+		{ "tag\nfoo", "tag", 1, BOTH },
b62b43
+		{ "tag\r\nfoo", "tag", 1, BOTH },
b62b43
+		{ "\n", NULL, -1, BOTH },
b62b43
+		{ "tag", NULL, 0, BOTH },
b62b43
+		{ "tag\t", NULL, -1, BOTH },
b62b43
+		{ "tag\001", NULL, -1, BOTH },
b62b43
+		{ "tag\x80", NULL, -1, BOTH },
b62b43
+		{ "tag(", NULL, -1, BOTH },
b62b43
+		{ "tag)", NULL, -1, BOTH },
b62b43
+		{ "tag{", NULL, -1, BOTH },
b62b43
+		{ "tag/ ", "tag/", 1, BOTH },
b62b43
+		{ "tag%", NULL, -1, BOTH },
b62b43
+		{ "tag*", NULL, -1, BOTH },
b62b43
+		{ "tag\"", NULL, -1, BOTH },
b62b43
+		{ "tag\\", NULL, -1, BOTH },
b62b43
+		{ "tag+", NULL, -1, TAG },
b62b43
+		{ "tag+ ", "tag+", 1, COMMAND },
b62b43
+	};
b62b43
+	struct istream *input;
b62b43
+	struct imap_parser *parser;
b62b43
+	const char *atom;
b62b43
+	int ret;
b62b43
+
b62b43
+	test_begin("imap_parser_read_tag and imap_parser_read_command_name");
b62b43
+	for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) {
b62b43
+		if (tests[i].type != COMMAND) {
b62b43
+			input = test_istream_create(tests[i].input);
b62b43
+			test_assert(i_stream_read(input) > 0);
b62b43
+			parser = imap_parser_create(input, NULL, 1024);
b62b43
+			ret = imap_parser_read_tag(parser, &atom);
b62b43
+			test_assert_idx(ret == tests[i].ret, i);
b62b43
+			test_assert_idx(ret <= 0 || strcmp(tests[i].tag, atom) == 0, i);
b62b43
+			imap_parser_unref(&parser);
b62b43
+			i_stream_destroy(&input);
b62b43
+		}
b62b43
+
b62b43
+		if (tests[i].type != TAG) {
b62b43
+			input = test_istream_create(tests[i].input);
b62b43
+			test_assert(i_stream_read(input) > 0);
b62b43
+			parser = imap_parser_create(input, NULL, 1024);
b62b43
+			ret = imap_parser_read_command_name(parser, &atom);
b62b43
+			test_assert_idx(ret == tests[i].ret, i);
b62b43
+			test_assert_idx(ret <= 0 || strcmp(tests[i].tag, atom) == 0, i);
b62b43
+			imap_parser_unref(&parser);
b62b43
+			i_stream_destroy(&input);
b62b43
+		}
b62b43
+	}
b62b43
+	test_end();
b62b43
+}
b62b43
+
b62b43
 int main(void)
b62b43
 {
b62b43
 	static void (*const test_functions[])(void) = {
b62b43
 		test_imap_parser_crlf,
b62b43
+		test_imap_parser_read_tag_cmd,
b62b43
 		NULL
b62b43
 	};
b62b43
 	return test_run(test_functions);