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

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