Blame SOURCES/dovecot-2.3.10-CVE_2020_10957,10958,10967.patch

db3cd8
diff -up dovecot-2.3.8/src/lib-smtp/Makefile.am.CVE_2020_10957,10958,10967 dovecot-2.3.8/src/lib-smtp/Makefile.am
db3cd8
--- dovecot-2.3.8/src/lib-smtp/Makefile.am.CVE_2020_10957,10958,10967	2019-10-08 10:46:18.000000000 +0200
db3cd8
+++ dovecot-2.3.8/src/lib-smtp/Makefile.am	2020-05-29 19:11:53.357162340 +0200
db3cd8
@@ -72,6 +72,7 @@ pkginc_libdir=$(pkgincludedir)
db3cd8
 pkginc_lib_HEADERS = $(headers)
db3cd8
 
db3cd8
 test_programs = \
db3cd8
+	test-smtp-syntax \
db3cd8
 	test-smtp-address \
db3cd8
 	test-smtp-params \
db3cd8
 	test-smtp-reply \
db3cd8
@@ -121,6 +122,10 @@ if BUILD_OPENSSL
db3cd8
 test_libs_ssl += ../lib-ssl-iostream/libssl_iostream_openssl.la
db3cd8
 endif
db3cd8
 
db3cd8
+test_smtp_syntax_SOURCES = test-smtp-syntax.c
db3cd8
+test_smtp_syntax_LDADD = $(test_libs)
db3cd8
+test_smtp_syntax_DEPENDENCIES = $(test_deps)
db3cd8
+
db3cd8
 test_smtp_address_SOURCES = test-smtp-address.c
db3cd8
 test_smtp_address_LDFLAGS = -export-dynamic
db3cd8
 test_smtp_address_LDADD = $(test_libs)
db3cd8
diff -up dovecot-2.3.8/src/lib-smtp/smtp-address.c.CVE_2020_10957,10958,10967 dovecot-2.3.8/src/lib-smtp/smtp-address.c
db3cd8
--- dovecot-2.3.8/src/lib-smtp/smtp-address.c.CVE_2020_10957,10958,10967	2020-05-29 19:11:53.356162354 +0200
db3cd8
+++ dovecot-2.3.8/src/lib-smtp/smtp-address.c	2020-05-29 19:13:33.310813425 +0200
db3cd8
@@ -625,13 +625,13 @@ smtp_address_clone(pool_t pool, const st
db3cd8
 	size_t size, lpsize = 0, dsize = 0;
db3cd8
 	char *data, *localpart = NULL, *domain = NULL;
db3cd8
 
db3cd8
-	if (smtp_address_isnull(src))
db3cd8
+	if (src == NULL)
db3cd8
 		return NULL;
db3cd8
 
db3cd8
 	/* @UNSAFE */
db3cd8
 
db3cd8
 	size = sizeof(struct smtp_address);
db3cd8
-	if (src->localpart != NULL && *src->localpart != '\0') {
db3cd8
+	if (!smtp_address_isnull(src)) {
db3cd8
 		lpsize = strlen(src->localpart) + 1;
db3cd8
 		size = MALLOC_ADD(size, lpsize);
db3cd8
 	}
db3cd8
@@ -686,7 +686,7 @@ smtp_address_clone_temp(const struct smt
db3cd8
 {
db3cd8
 	struct smtp_address *new;
db3cd8
 
db3cd8
-	if (smtp_address_isnull(src))
db3cd8
+	if (src == NULL)
db3cd8
 		return NULL;
db3cd8
 
db3cd8
 	new = t_new(struct smtp_address, 1);
db3cd8
diff -up dovecot-2.3.8/src/lib-smtp/smtp-address.h.CVE_2020_10957,10958,10967 dovecot-2.3.8/src/lib-smtp/smtp-address.h
db3cd8
--- dovecot-2.3.8/src/lib-smtp/smtp-address.h.CVE_2020_10957,10958,10967	2019-10-08 10:46:18.000000000 +0200
db3cd8
+++ dovecot-2.3.8/src/lib-smtp/smtp-address.h	2020-05-29 19:11:53.358162327 +0200
db3cd8
@@ -122,8 +122,14 @@ smtp_address_equals(const struct smtp_ad
db3cd8
 static inline bool ATTR_NULL(1) ATTR_PURE
db3cd8
 smtp_address_isnull(const struct smtp_address *address)
db3cd8
 {
db3cd8
-	return (address == NULL || address->localpart == NULL ||
db3cd8
-		*address->localpart == '\0');
db3cd8
+	return (address == NULL || address->localpart == NULL);
db3cd8
 }
db3cd8
 
db3cd8
+static inline bool ATTR_NULL(1) ATTR_PURE
db3cd8
+smtp_address_is_broken(const struct smtp_address *address)
db3cd8
+{
db3cd8
+	return (address != NULL &&
db3cd8
+		smtp_address_isnull(address) /*&&
db3cd8
+		(address->raw != NULL && *address->raw != '\0')*/);
db3cd8
+}
db3cd8
 #endif
db3cd8
diff -up dovecot-2.3.8/src/lib-smtp/smtp-server-cmd-noop.c.CVE_2020_10957,10958,10967 dovecot-2.3.8/src/lib-smtp/smtp-server-cmd-noop.c
db3cd8
--- dovecot-2.3.8/src/lib-smtp/smtp-server-cmd-noop.c.CVE_2020_10957,10958,10967	2019-10-08 10:46:18.000000000 +0200
db3cd8
+++ dovecot-2.3.8/src/lib-smtp/smtp-server-cmd-noop.c	2020-05-29 19:11:53.358162327 +0200
db3cd8
@@ -13,11 +13,15 @@ void smtp_server_cmd_noop(struct smtp_se
db3cd8
 	struct smtp_server_connection *conn = cmd->conn;
db3cd8
 	struct smtp_server_command *command = cmd->cmd;
db3cd8
 	const struct smtp_server_callbacks *callbacks = conn->callbacks;
db3cd8
+	const char *param, *error;
db3cd8
 	int ret;
db3cd8
 
db3cd8
 	/* "NOOP" [ SP String ] CRLF */
db3cd8
-	if (*params != '\0' && smtp_string_parse(params, NULL, NULL) < 0) {
db3cd8
-		smtp_server_reply(cmd, 501, "5.5.4", "Invalid parameters");
db3cd8
+	ret = smtp_string_parse(params, &param, &error);
db3cd8
+	if (ret < 0) {
db3cd8
+		smtp_server_reply(cmd, 501, "5.5.4",
db3cd8
+				  "Invalid string parameter: %s",
db3cd8
+				  error);
db3cd8
 		return;
db3cd8
 	}
db3cd8
 
db3cd8
diff -up dovecot-2.3.8/src/lib-smtp/smtp-server-cmd-vrfy.c.CVE_2020_10957,10958,10967 dovecot-2.3.8/src/lib-smtp/smtp-server-cmd-vrfy.c
db3cd8
--- dovecot-2.3.8/src/lib-smtp/smtp-server-cmd-vrfy.c.CVE_2020_10957,10958,10967	2019-10-08 10:46:18.000000000 +0200
db3cd8
+++ dovecot-2.3.8/src/lib-smtp/smtp-server-cmd-vrfy.c	2020-05-29 19:11:53.358162327 +0200
db3cd8
@@ -17,15 +17,13 @@ void smtp_server_cmd_vrfy(struct smtp_se
db3cd8
 	int ret;
db3cd8
 
db3cd8
 	/* vrfy = "VRFY" SP String CRLF */
db3cd8
-	if ((ret=smtp_string_parse(params, &param, &error)) <= 0) {
db3cd8
-		if (ret < 0) {
db3cd8
-			smtp_server_reply(cmd,
db3cd8
-				501, "5.5.4",
db3cd8
-				"Invalid string parameter: %s", error);
db3cd8
-		} else {
db3cd8
-			smtp_server_reply(cmd,
db3cd8
-				501, "5.5.4", "Invalid parameters");
db3cd8
-		}
db3cd8
+	ret = smtp_string_parse(params, &param, &error);
db3cd8
+	if (ret < 0) {
db3cd8
+		smtp_server_reply(cmd, 501, "5.5.4",
db3cd8
+				  "Invalid string parameter: %s", error);
db3cd8
+		return;
db3cd8
+	} else if (ret == 0) {
db3cd8
+		smtp_server_reply(cmd, 501, "5.5.4", "Invalid parameters");
db3cd8
 		return;
db3cd8
 	}
db3cd8
 
db3cd8
diff -up dovecot-2.3.8/src/lib-smtp/smtp-server-command.c.CVE_2020_10957,10958,10967 dovecot-2.3.8/src/lib-smtp/smtp-server-command.c
db3cd8
--- dovecot-2.3.8/src/lib-smtp/smtp-server-command.c.CVE_2020_10957,10958,10967	2019-10-08 10:46:18.000000000 +0200
db3cd8
+++ dovecot-2.3.8/src/lib-smtp/smtp-server-command.c	2020-05-29 19:11:53.358162327 +0200
db3cd8
@@ -179,14 +179,15 @@ smtp_server_command_new_invalid(struct s
db3cd8
 
db3cd8
 struct smtp_server_command *
db3cd8
 smtp_server_command_new(struct smtp_server_connection *conn,
db3cd8
-			const char *name, const char *params)
db3cd8
+			const char *name)
db3cd8
 {
db3cd8
 	struct smtp_server *server = conn->server;
db3cd8
-	const struct smtp_server_command_reg *cmd_reg;
db3cd8
 	struct smtp_server_command *cmd;
db3cd8
 
db3cd8
 	cmd = smtp_server_command_alloc(conn);
db3cd8
 	cmd->context.name = p_strdup(cmd->context.pool, name);
db3cd8
+	cmd->reg = smtp_server_command_find(server, name);
db3cd8
+
db3cd8
 	smtp_server_command_update_event(cmd);
db3cd8
 
db3cd8
 	struct event_passthrough *e =
db3cd8
@@ -194,7 +195,15 @@ smtp_server_command_new(struct smtp_serv
db3cd8
 		set_name("smtp_server_command_started");
db3cd8
 	e_debug(e->event(), "New command");
db3cd8
 
db3cd8
-	if ((cmd_reg=smtp_server_command_find(server, name)) == NULL) {
db3cd8
+	return cmd;
db3cd8
+}
db3cd8
+
db3cd8
+void smtp_server_command_execute(struct smtp_server_command *cmd,
db3cd8
+				 const char *params)
db3cd8
+{
db3cd8
+	struct smtp_server_connection *conn = cmd->context.conn;
db3cd8
+
db3cd8
+	if (cmd->reg == NULL) {
db3cd8
 		/* RFC 5321, Section 4.2.4: Reply Code 502
db3cd8
 
db3cd8
 		   Questions have been raised as to when reply code 502 (Command
db3cd8
@@ -207,7 +216,7 @@ smtp_server_command_new(struct smtp_serv
db3cd8
 			500, "5.5.1", "Unknown command");
db3cd8
 
db3cd8
 	} else if (!conn->ssl_secured && conn->set.tls_required &&
db3cd8
-		(cmd_reg->flags & SMTP_SERVER_CMD_FLAG_PRETLS) == 0) {
db3cd8
+		   (cmd->reg->flags & SMTP_SERVER_CMD_FLAG_PRETLS) == 0) {
db3cd8
 		/* RFC 3207, Section 4:
db3cd8
 
db3cd8
 		   A SMTP server that is not publicly referenced may choose to
db3cd8
@@ -226,7 +235,7 @@ smtp_server_command_new(struct smtp_serv
db3cd8
 			530, "5.7.0", "TLS required.");
db3cd8
 
db3cd8
 	} else if (!conn->authenticated && !conn->set.auth_optional &&
db3cd8
-		(cmd_reg->flags & SMTP_SERVER_CMD_FLAG_PREAUTH) == 0) {
db3cd8
+		   (cmd->reg->flags & SMTP_SERVER_CMD_FLAG_PREAUTH) == 0) {
db3cd8
 		/* RFC 4954, Section 6: Status Codes
db3cd8
 
db3cd8
 		   530 5.7.0  Authentication required
db3cd8
@@ -242,16 +251,14 @@ smtp_server_command_new(struct smtp_serv
db3cd8
 	} else {
db3cd8
 		struct smtp_server_command *tmp_cmd = cmd;
db3cd8
 
db3cd8
-		i_assert(cmd_reg->func != NULL);
db3cd8
+		i_assert(cmd->reg->func != NULL);
db3cd8
 		smtp_server_command_ref(tmp_cmd);
db3cd8
-		tmp_cmd->reg = cmd_reg;
db3cd8
-		cmd_reg->func(&tmp_cmd->context, params);
db3cd8
+		cmd->reg->func(&tmp_cmd->context, params);
db3cd8
 		if (tmp_cmd->state == SMTP_SERVER_COMMAND_STATE_NEW)
db3cd8
 			tmp_cmd->state = SMTP_SERVER_COMMAND_STATE_PROCESSING;
db3cd8
 		if (!smtp_server_command_unref(&tmp_cmd))
db3cd8
 			cmd = NULL;
db3cd8
 	}
db3cd8
-	return cmd;
db3cd8
 }
db3cd8
 
db3cd8
 void smtp_server_command_ref(struct smtp_server_command *cmd)
db3cd8
@@ -389,8 +396,11 @@ bool smtp_server_command_call_hooks(stru
db3cd8
 	struct smtp_server_command *cmd = *_cmd;
db3cd8
 	struct smtp_server_command_hook *hook;
db3cd8
 
db3cd8
-	if (type != SMTP_SERVER_COMMAND_HOOK_DESTROY)
db3cd8
+	if (type != SMTP_SERVER_COMMAND_HOOK_DESTROY) {
db3cd8
+		if (cmd->state >= SMTP_SERVER_COMMAND_STATE_FINISHED)
db3cd8
+			return FALSE;
db3cd8
 		smtp_server_command_ref(cmd);
db3cd8
+	}
db3cd8
 
db3cd8
 	hook = cmd->hooks_head;
db3cd8
 	while (hook != NULL) {
db3cd8
diff -up dovecot-2.3.8/src/lib-smtp/smtp-server-connection.c.CVE_2020_10957,10958,10967 dovecot-2.3.8/src/lib-smtp/smtp-server-connection.c
db3cd8
--- dovecot-2.3.8/src/lib-smtp/smtp-server-connection.c.CVE_2020_10957,10958,10967	2019-10-08 10:46:18.000000000 +0200
db3cd8
+++ dovecot-2.3.8/src/lib-smtp/smtp-server-connection.c	2020-05-29 19:11:53.358162327 +0200
db3cd8
@@ -293,20 +293,28 @@ smtp_server_connection_handle_command(st
db3cd8
 {
db3cd8
 	struct smtp_server_connection *tmp_conn = conn;
db3cd8
 	struct smtp_server_command *cmd;
db3cd8
+	bool finished;
db3cd8
+
db3cd8
+	cmd = smtp_server_command_new(tmp_conn, cmd_name);
db3cd8
+
db3cd8
+	smtp_server_command_ref(cmd);
db3cd8
 
db3cd8
 	smtp_server_connection_ref(tmp_conn);
db3cd8
-	cmd = smtp_server_command_new(tmp_conn, cmd_name, cmd_params);
db3cd8
+	smtp_server_command_execute(cmd, cmd_params);
db3cd8
 	if (!smtp_server_connection_unref(&tmp_conn)) {
db3cd8
 		/* the command start callback managed to get this connection
db3cd8
 		   destroyed */
db3cd8
+		smtp_server_command_unref(&cmd);
db3cd8
 		return FALSE;
db3cd8
 	}
db3cd8
 
db3cd8
-	if (cmd != NULL && conn->command_queue_head == cmd)
db3cd8
+	if (conn->command_queue_head == cmd)
db3cd8
 		(void)smtp_server_command_next_to_reply(&cmd);
db3cd8
 
db3cd8
 	smtp_server_connection_timeout_update(conn);
db3cd8
-	return (cmd == NULL || !cmd->input_locked);
db3cd8
+
db3cd8
+	finished = !cmd->input_locked;
db3cd8
+	return (!smtp_server_command_unref(&cmd) || finished);
db3cd8
 }
db3cd8
 
db3cd8
 static int
db3cd8
diff -up dovecot-2.3.8/src/lib-smtp/smtp-server-private.h.CVE_2020_10957,10958,10967 dovecot-2.3.8/src/lib-smtp/smtp-server-private.h
db3cd8
--- dovecot-2.3.8/src/lib-smtp/smtp-server-private.h.CVE_2020_10957,10958,10967	2019-10-08 10:46:18.000000000 +0200
db3cd8
+++ dovecot-2.3.8/src/lib-smtp/smtp-server-private.h	2020-05-29 19:11:53.358162327 +0200
db3cd8
@@ -241,8 +241,11 @@ void smtp_server_command_debug(struct sm
db3cd8
 struct smtp_server_command *
db3cd8
 smtp_server_command_new_invalid(struct smtp_server_connection *conn);
db3cd8
 struct smtp_server_command *
db3cd8
-smtp_server_command_new(struct smtp_server_connection *conn,
db3cd8
-	const char *name, const char *params);
db3cd8
+smtp_server_command_new(struct smtp_server_connection *conn, const char *name);
db3cd8
+
db3cd8
+void smtp_server_command_execute(struct smtp_server_command *cmd,
db3cd8
+				 const char *params);
db3cd8
+
db3cd8
 void smtp_server_command_ref(struct smtp_server_command *cmd);
db3cd8
 bool smtp_server_command_unref(struct smtp_server_command **_cmd);
db3cd8
 void smtp_server_command_abort(struct smtp_server_command **_cmd);
db3cd8
diff -up dovecot-2.3.8/src/lib-smtp/smtp-syntax.c.CVE_2020_10957,10958,10967 dovecot-2.3.8/src/lib-smtp/smtp-syntax.c
db3cd8
--- dovecot-2.3.8/src/lib-smtp/smtp-syntax.c.CVE_2020_10957,10958,10967	2019-10-08 10:46:18.000000000 +0200
db3cd8
+++ dovecot-2.3.8/src/lib-smtp/smtp-syntax.c	2020-05-29 19:11:53.358162327 +0200
db3cd8
@@ -17,18 +17,19 @@ int smtp_string_parse(const char *string
db3cd8
 	const char **value_r, const char **error_r)
db3cd8
 {
db3cd8
 	struct smtp_parser parser;
db3cd8
-	int ret;
db3cd8
+
db3cd8
+	*value_r = NULL;
db3cd8
+	*error_r = NULL;
db3cd8
 
db3cd8
 	if (string == NULL || *string == '\0') {
db3cd8
 		*value_r = "";
db3cd8
-		return 1;
db3cd8
+		return 0;
db3cd8
 	}
db3cd8
 
db3cd8
 	smtp_parser_init(&parser, pool_datastack_create(), string);
db3cd8
 
db3cd8
-	if ((ret=smtp_parser_parse_string(&parser, value_r)) < 0) {
db3cd8
-		if (error_r != NULL)
db3cd8
-			*error_r = parser.error;
db3cd8
+	if (smtp_parser_parse_string(&parser, value_r) < 0) {
db3cd8
+		*error_r = parser.error;
db3cd8
 		return -1;
db3cd8
 	}
db3cd8
 	if (parser.cur < parser.end) {
db3cd8
@@ -85,20 +86,20 @@ int smtp_xtext_parse(const char *xtext,
db3cd8
 {
db3cd8
 	struct smtp_parser parser;
db3cd8
 	string_t *value = NULL;
db3cd8
-	int ret;
db3cd8
+
db3cd8
+	*value_r = NULL;
db3cd8
+	*error_r = NULL;
db3cd8
 
db3cd8
 	if (xtext == NULL || *xtext == '\0') {
db3cd8
 		*value_r = "";
db3cd8
 		return 1;
db3cd8
 	}
db3cd8
 
db3cd8
-	if (value_r != NULL)
db3cd8
-		value = t_str_new(256);
db3cd8
+	value = t_str_new(256);
db3cd8
 	smtp_parser_init(&parser, pool_datastack_create(), xtext);
db3cd8
 
db3cd8
-	if ((ret=smtp_parser_parse_xtext(&parser, value)) < 0) {
db3cd8
-		if (error_r != NULL)
db3cd8
-			*error_r = parser.error;
db3cd8
+	if (smtp_parser_parse_xtext(&parser, value) < 0) {
db3cd8
+		*error_r = parser.error;
db3cd8
 		return -1;
db3cd8
 	}
db3cd8
 	if (parser.cur < parser.end) {
db3cd8
@@ -109,8 +110,7 @@ int smtp_xtext_parse(const char *xtext,
db3cd8
 	if (value_r != NULL) {
db3cd8
 		*value_r = str_c(value);
db3cd8
 		if (strlen(*value_r) != str_len(value)) {
db3cd8
-			if (*error_r != NULL)
db3cd8
-				*error_r = "Encountered NUL character in xtext";
db3cd8
+			*error_r = "Encountered NUL character in xtext";
db3cd8
 			return -1;
db3cd8
 		}
db3cd8
 	}
db3cd8
@@ -249,12 +249,10 @@ static int smtp_parse_ehlo_line(struct s
db3cd8
 		(i_isalnum(*parser->cur) || *parser->cur == '-'))
db3cd8
 		parser->cur++;
db3cd8
 
db3cd8
-	if (key_r != NULL)
db3cd8
-		*key_r = p_strdup_until(parser->pool, pbegin, parser->cur);
db3cd8
+	*key_r = p_strdup_until(parser->pool, pbegin, parser->cur);
db3cd8
 
db3cd8
 	if (parser->cur >= parser->end) {
db3cd8
-		if (params_r != NULL)
db3cd8
-			*params_r = p_new(parser->pool, const char *, 1);
db3cd8
+		*params_r = p_new(parser->pool, const char *, 1);
db3cd8
 		return 1;
db3cd8
 	}
db3cd8
 	if (*parser->cur != ' ') {
db3cd8
@@ -264,18 +262,16 @@ static int smtp_parse_ehlo_line(struct s
db3cd8
 	parser->cur++;
db3cd8
 
db3cd8
 	pbegin = parser->cur;
db3cd8
-	if (params_r != NULL)
db3cd8
-		p_array_init(&params, parser->pool, 32);
db3cd8
+	p_array_init(&params, parser->pool, 32);
db3cd8
 	while (parser->cur < parser->end) {
db3cd8
 		if (*parser->cur == ' ') {
db3cd8
 			if (parser->cur+1 >= parser->end || *(parser->cur+1) == ' ') {
db3cd8
 				parser->error = "Missing EHLO parameter after ' '";
db3cd8
 				return -1;
db3cd8
 			}
db3cd8
-			if (params_r != NULL) {
db3cd8
-				param = p_strdup_until(parser->pool, pbegin, parser->cur);
db3cd8
-				array_push_back(&params, ¶m;;
db3cd8
-			}
db3cd8
+			param = p_strdup_until(parser->pool, pbegin,
db3cd8
+					       parser->cur);
db3cd8
+			array_push_back(&params, ¶m;;
db3cd8
 			pbegin = parser->cur + 1;
db3cd8
 		} else if (!smtp_char_is_ehlo_param(*parser->cur)) {
db3cd8
 			parser->error = "Unexpected character in EHLO parameter";
db3cd8
@@ -284,12 +280,10 @@ static int smtp_parse_ehlo_line(struct s
db3cd8
 		parser->cur++;
db3cd8
 	}
db3cd8
 
db3cd8
-	if (params_r != NULL) {
db3cd8
-		param = p_strdup_until(parser->pool, pbegin, parser->cur);
db3cd8
-		array_push_back(&params, ¶m;;
db3cd8
-		array_append_zero(&params);
db3cd8
-		*params_r = array_front(&params);
db3cd8
-	}
db3cd8
+	param = p_strdup_until(parser->pool, pbegin, parser->cur);
db3cd8
+	array_push_back(&params, ¶m;;
db3cd8
+	array_append_zero(&params);
db3cd8
+	*params_r = array_front(&params);
db3cd8
 	return 1;
db3cd8
 }
db3cd8
 
db3cd8
@@ -297,19 +291,20 @@ int smtp_ehlo_line_parse(const char *ehl
db3cd8
 	const char *const **params_r, const char **error_r)
db3cd8
 {
db3cd8
 	struct smtp_parser parser;
db3cd8
-	int ret;
db3cd8
+
db3cd8
+	*key_r = NULL;
db3cd8
+	*params_r = NULL;
db3cd8
+	*error_r = NULL;
db3cd8
 
db3cd8
 	if (ehlo_line == NULL || *ehlo_line == '\0') {
db3cd8
-		if (error_r != NULL)
db3cd8
-			*error_r = "Parameter is empty";
db3cd8
+		*error_r = "Parameter is empty";
db3cd8
 		return -1;
db3cd8
 	}
db3cd8
 
db3cd8
 	smtp_parser_init(&parser, pool_datastack_create(), ehlo_line);
db3cd8
 
db3cd8
-	if ((ret=smtp_parse_ehlo_line(&parser, key_r, params_r)) <= 0) {
db3cd8
-		if (error_r != NULL)
db3cd8
-			*error_r = parser.error;
db3cd8
+	if (smtp_parse_ehlo_line(&parser, key_r, params_r) <= 0) {
db3cd8
+		*error_r = parser.error;
db3cd8
 		return -1;
db3cd8
 	}
db3cd8
 	return 1;
db3cd8
diff -up dovecot-2.3.8/src/lib-smtp/test-smtp-server-errors.c.CVE_2020_10957,10958,10967 dovecot-2.3.8/src/lib-smtp/test-smtp-server-errors.c
db3cd8
--- dovecot-2.3.8/src/lib-smtp/test-smtp-server-errors.c.CVE_2020_10957,10958,10967	2019-10-08 10:46:18.000000000 +0200
db3cd8
+++ dovecot-2.3.8/src/lib-smtp/test-smtp-server-errors.c	2020-05-29 19:11:53.359162313 +0200
db3cd8
@@ -574,6 +574,174 @@ static void test_bad_command(void)
db3cd8
 }
db3cd8
 
db3cd8
 /*
db3cd8
+ * Many bad commands
db3cd8
+ */
db3cd8
+
db3cd8
+/* client */
db3cd8
+
db3cd8
+struct _many_bad_commands_client {
db3cd8
+	struct smtp_reply_parser *parser;
db3cd8
+	unsigned int reply;
db3cd8
+	bool replied:1;
db3cd8
+};
db3cd8
+
db3cd8
+static void
db3cd8
+test_many_bad_commands_client_input(struct client_connection *conn)
db3cd8
+{
db3cd8
+	struct _many_bad_commands_client *ctx = conn->context;
db3cd8
+	struct smtp_reply *reply;
db3cd8
+	const char *error;
db3cd8
+	int ret;
db3cd8
+
db3cd8
+	while ((ret=smtp_reply_parse_next(ctx->parser, FALSE,
db3cd8
+					  &reply, &error)) > 0) {
db3cd8
+		if (debug)
db3cd8
+			i_debug("REPLY: %s", smtp_reply_log(reply));
db3cd8
+
db3cd8
+		switch (ctx->reply++) {
db3cd8
+		/* greeting */
db3cd8
+		case 0:
db3cd8
+			i_assert(reply->status == 220);
db3cd8
+			break;
db3cd8
+		/* bad command reply */
db3cd8
+		case 1: case 2: case 3: case 4: case 5:
db3cd8
+		case 6: case 7: case 8: case 9: case 10:
db3cd8
+			i_assert(reply->status == 500);
db3cd8
+			break;
db3cd8
+		case 11:
db3cd8
+			i_assert(reply->status == 421);
db3cd8
+			ctx->replied = TRUE;
db3cd8
+			io_loop_stop(ioloop);
db3cd8
+			connection_disconnect(&conn->conn);
db3cd8
+			return;
db3cd8
+		default:
db3cd8
+			i_unreached();
db3cd8
+		}
db3cd8
+	}
db3cd8
+
db3cd8
+	i_assert(ret >= 0);
db3cd8
+}
db3cd8
+
db3cd8
+static void
db3cd8
+test_many_bad_commands_client_connected(struct client_connection *conn)
db3cd8
+{
db3cd8
+	struct _many_bad_commands_client *ctx;
db3cd8
+
db3cd8
+	ctx = p_new(conn->pool, struct _many_bad_commands_client, 1);
db3cd8
+	ctx->parser = smtp_reply_parser_init(conn->conn.input, (size_t)-1);
db3cd8
+	conn->context = ctx;
db3cd8
+
db3cd8
+	switch (client_index) {
db3cd8
+	case 0:
db3cd8
+		o_stream_nsend_str(conn->conn.output,
db3cd8
+				   "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
db3cd8
+		break;
db3cd8
+	case 1:
db3cd8
+		o_stream_nsend_str(conn->conn.output,
db3cd8
+				   "a\r\nb\r\nc\r\nd\r\ne\r\nf\r\ng\r\nh\r\n"
db3cd8
+				   "i\r\nj\r\nk\r\nl\r\nm\r\nn\r\no\r\np\r\n");
db3cd8
+		break;
db3cd8
+	default:
db3cd8
+		i_unreached();
db3cd8
+	}
db3cd8
+}
db3cd8
+
db3cd8
+static void
db3cd8
+test_many_bad_commands_client_deinit(struct client_connection *conn)
db3cd8
+{
db3cd8
+	struct _many_bad_commands_client *ctx = conn->context;
db3cd8
+
db3cd8
+	i_assert(ctx->replied);
db3cd8
+	smtp_reply_parser_deinit(&ctx->parser);
db3cd8
+}
db3cd8
+
db3cd8
+static void test_client_many_bad_commands(unsigned int index)
db3cd8
+{
db3cd8
+	test_client_input = test_many_bad_commands_client_input;
db3cd8
+	test_client_connected = test_many_bad_commands_client_connected;
db3cd8
+	test_client_deinit = test_many_bad_commands_client_deinit;
db3cd8
+	test_client_run(index);
db3cd8
+}
db3cd8
+
db3cd8
+/* server */
db3cd8
+
db3cd8
+struct _many_bad_commands {
db3cd8
+	struct istream *payload_input;
db3cd8
+	struct io *io;
db3cd8
+
db3cd8
+	bool serviced:1;
db3cd8
+};
db3cd8
+
db3cd8
+static void
db3cd8
+test_server_many_bad_commands_disconnect(void *context ATTR_UNUSED,
db3cd8
+					   const char *reason)
db3cd8
+{
db3cd8
+	if (debug)
db3cd8
+		i_debug("Disconnect: %s", reason);
db3cd8
+	io_loop_stop(ioloop);
db3cd8
+}
db3cd8
+
db3cd8
+static int
db3cd8
+test_server_many_bad_commands_helo(
db3cd8
+	void *conn_ctx ATTR_UNUSED, struct smtp_server_cmd_ctx *cmd ATTR_UNUSED,
db3cd8
+	struct smtp_server_cmd_helo *data ATTR_UNUSED)
db3cd8
+{
db3cd8
+	test_assert(FALSE);
db3cd8
+	return 1;
db3cd8
+}
db3cd8
+
db3cd8
+static int
db3cd8
+test_server_many_bad_commands_rcpt(
db3cd8
+	void *conn_ctx ATTR_UNUSED, struct smtp_server_cmd_ctx *cmd ATTR_UNUSED,
db3cd8
+	struct smtp_server_recipient *rcpt ATTR_UNUSED)
db3cd8
+{
db3cd8
+	test_assert(FALSE);
db3cd8
+	return 1;
db3cd8
+}
db3cd8
+
db3cd8
+static int
db3cd8
+test_server_many_bad_commands_data_begin(
db3cd8
+	void *conn_ctx ATTR_UNUSED, struct smtp_server_cmd_ctx *cmd ATTR_UNUSED,
db3cd8
+	struct smtp_server_transaction *trans ATTR_UNUSED,
db3cd8
+	struct istream *data_input ATTR_UNUSED)
db3cd8
+{
db3cd8
+	test_assert(FALSE);
db3cd8
+	return 1;
db3cd8
+}
db3cd8
+
db3cd8
+static void test_server_many_bad_commands
db3cd8
+(const struct smtp_server_settings *server_set)
db3cd8
+{
db3cd8
+	server_callbacks.conn_disconnect =
db3cd8
+		test_server_many_bad_commands_disconnect;
db3cd8
+
db3cd8
+	server_callbacks.conn_cmd_helo =
db3cd8
+		test_server_many_bad_commands_helo;
db3cd8
+	server_callbacks.conn_cmd_rcpt =
db3cd8
+		test_server_many_bad_commands_rcpt;
db3cd8
+	server_callbacks.conn_cmd_data_begin =
db3cd8
+		test_server_many_bad_commands_data_begin;
db3cd8
+	test_server_run(server_set);
db3cd8
+}
db3cd8
+
db3cd8
+/* test */
db3cd8
+
db3cd8
+static void test_many_bad_commands(void)
db3cd8
+{
db3cd8
+	struct smtp_server_settings smtp_server_set;
db3cd8
+
db3cd8
+	test_server_defaults(&smtp_server_set);
db3cd8
+	smtp_server_set.max_client_idle_time_msecs = 1000;
db3cd8
+	smtp_server_set.max_bad_commands = 10;
db3cd8
+
db3cd8
+	test_begin("many bad commands");
db3cd8
+	test_run_client_server(&smtp_server_set,
db3cd8
+		test_server_many_bad_commands,
db3cd8
+		test_client_many_bad_commands, 2);
db3cd8
+	test_end();
db3cd8
+}
db3cd8
+
db3cd8
+/*
db3cd8
  * Long command
db3cd8
  */
db3cd8
 
db3cd8
@@ -1274,6 +1442,316 @@ static void test_bad_rcpt(void)
db3cd8
 }
db3cd8
 
db3cd8
 /*
db3cd8
+ * Bad VRFY
db3cd8
+ */
db3cd8
+
db3cd8
+/* client */
db3cd8
+
db3cd8
+struct _bad_vrfy_client {
db3cd8
+	struct smtp_reply_parser *parser;
db3cd8
+	unsigned int reply;
db3cd8
+
db3cd8
+	bool replied:1;
db3cd8
+};
db3cd8
+
db3cd8
+static void
db3cd8
+test_bad_vrfy_client_input(struct client_connection *conn)
db3cd8
+{
db3cd8
+	struct _bad_vrfy_client *ctx = conn->context;
db3cd8
+	struct smtp_reply *reply;
db3cd8
+	const char *error;
db3cd8
+	int ret;
db3cd8
+
db3cd8
+	while ((ret = smtp_reply_parse_next(ctx->parser, FALSE,
db3cd8
+					    &reply, &error)) > 0) {
db3cd8
+		if (debug)
db3cd8
+			i_debug("REPLY: %s", smtp_reply_log(reply));
db3cd8
+
db3cd8
+		switch (ctx->reply++) {
db3cd8
+		case 0: /* greeting */
db3cd8
+			i_assert(reply->status == 220);
db3cd8
+			break;
db3cd8
+		case 1: /* bad command reply */
db3cd8
+			switch (client_index) {
db3cd8
+			case 0: case 1: case 2:
db3cd8
+				i_assert(reply->status == 501);
db3cd8
+				break;
db3cd8
+			case 3:
db3cd8
+				i_assert(smtp_reply_is_success(reply));
db3cd8
+				break;
db3cd8
+			default:
db3cd8
+				i_unreached();
db3cd8
+			}
db3cd8
+			ctx->replied = TRUE;
db3cd8
+			io_loop_stop(ioloop);
db3cd8
+			connection_disconnect(&conn->conn);
db3cd8
+			return;
db3cd8
+		default:
db3cd8
+			i_unreached();
db3cd8
+		}
db3cd8
+	}
db3cd8
+
db3cd8
+	i_assert(ret == 0);
db3cd8
+}
db3cd8
+
db3cd8
+static void
db3cd8
+test_bad_vrfy_client_connected(struct client_connection *conn)
db3cd8
+{
db3cd8
+	struct _bad_vrfy_client *ctx;
db3cd8
+
db3cd8
+	ctx = p_new(conn->pool, struct _bad_vrfy_client, 1);
db3cd8
+	ctx->parser = smtp_reply_parser_init(conn->conn.input, (size_t)-1);
db3cd8
+	conn->context = ctx;
db3cd8
+
db3cd8
+	switch (client_index) {
db3cd8
+	case 0:
db3cd8
+		o_stream_nsend_str(conn->conn.output,
db3cd8
+				   "VRFY\r\n");
db3cd8
+		break;
db3cd8
+	case 1:
db3cd8
+		o_stream_nsend_str(conn->conn.output,
db3cd8
+				   "VRFY \"hendrik\r\n");
db3cd8
+		break;
db3cd8
+	case 2:
db3cd8
+		o_stream_nsend_str(conn->conn.output,
db3cd8
+				   "VRFY hen\"drik\r\n");
db3cd8
+		break;
db3cd8
+	case 3:
db3cd8
+		o_stream_nsend_str(conn->conn.output,
db3cd8
+				   "VRFY \"hendrik\"\r\n");
db3cd8
+		break;
db3cd8
+	default:
db3cd8
+		i_unreached();
db3cd8
+	}
db3cd8
+}
db3cd8
+
db3cd8
+static void
db3cd8
+test_bad_vrfy_client_deinit(struct client_connection *conn)
db3cd8
+{
db3cd8
+	struct _bad_vrfy_client *ctx = conn->context;
db3cd8
+
db3cd8
+	i_assert(ctx->replied);
db3cd8
+	smtp_reply_parser_deinit(&ctx->parser);
db3cd8
+}
db3cd8
+
db3cd8
+static void test_client_bad_vrfy(unsigned int index)
db3cd8
+{
db3cd8
+	test_client_input = test_bad_vrfy_client_input;
db3cd8
+	test_client_connected = test_bad_vrfy_client_connected;
db3cd8
+	test_client_deinit = test_bad_vrfy_client_deinit;
db3cd8
+	test_client_run(index);
db3cd8
+}
db3cd8
+
db3cd8
+/* server */
db3cd8
+
db3cd8
+static void
db3cd8
+test_server_bad_vrfy_disconnect(void *context ATTR_UNUSED, const char *reason)
db3cd8
+{
db3cd8
+	if (debug)
db3cd8
+		i_debug("Disconnect: %s", reason);
db3cd8
+}
db3cd8
+
db3cd8
+static int
db3cd8
+test_server_bad_vrfy_rcpt(void *conn_ctx ATTR_UNUSED,
db3cd8
+			  struct smtp_server_cmd_ctx *cmd ATTR_UNUSED,
db3cd8
+			  struct smtp_server_recipient *rcpt ATTR_UNUSED)
db3cd8
+{
db3cd8
+	test_assert(FALSE);
db3cd8
+	return 1;
db3cd8
+}
db3cd8
+
db3cd8
+static int
db3cd8
+test_server_bad_vrfy_data_begin(
db3cd8
+	void *conn_ctx ATTR_UNUSED, struct smtp_server_cmd_ctx *cmd ATTR_UNUSED,
db3cd8
+	struct smtp_server_transaction *trans ATTR_UNUSED,
db3cd8
+	struct istream *data_input ATTR_UNUSED)
db3cd8
+{
db3cd8
+	test_assert(FALSE);
db3cd8
+	return 1;
db3cd8
+}
db3cd8
+
db3cd8
+static void
db3cd8
+test_server_bad_vrfy(const struct smtp_server_settings *server_set)
db3cd8
+{
db3cd8
+	server_callbacks.conn_disconnect = test_server_bad_vrfy_disconnect;
db3cd8
+
db3cd8
+	server_callbacks.conn_cmd_rcpt = test_server_bad_vrfy_rcpt;
db3cd8
+	server_callbacks.conn_cmd_data_begin = test_server_bad_vrfy_data_begin;
db3cd8
+	test_server_run(server_set);
db3cd8
+}
db3cd8
+
db3cd8
+/* test */
db3cd8
+
db3cd8
+static void test_bad_vrfy(void)
db3cd8
+{
db3cd8
+	struct smtp_server_settings smtp_server_set;
db3cd8
+
db3cd8
+	test_server_defaults(&smtp_server_set);
db3cd8
+	smtp_server_set.max_client_idle_time_msecs = 1000;
db3cd8
+
db3cd8
+	test_begin("bad VRFY");
db3cd8
+	test_run_client_server(&smtp_server_set,
db3cd8
+			       test_server_bad_vrfy,
db3cd8
+			       test_client_bad_vrfy, 4);
db3cd8
+	test_end();
db3cd8
+}
db3cd8
+
db3cd8
+/*
db3cd8
+ * Bad NOOP
db3cd8
+ */
db3cd8
+
db3cd8
+/* client */
db3cd8
+
db3cd8
+struct _bad_noop_client {
db3cd8
+	struct smtp_reply_parser *parser;
db3cd8
+	unsigned int reply;
db3cd8
+
db3cd8
+	bool replied:1;
db3cd8
+};
db3cd8
+
db3cd8
+static void
db3cd8
+test_bad_noop_client_input(struct client_connection *conn)
db3cd8
+{
db3cd8
+	struct _bad_noop_client *ctx = conn->context;
db3cd8
+	struct smtp_reply *reply;
db3cd8
+	const char *error;
db3cd8
+	int ret;
db3cd8
+
db3cd8
+	while ((ret = smtp_reply_parse_next(ctx->parser, FALSE,
db3cd8
+					    &reply, &error)) > 0) {
db3cd8
+		if (debug)
db3cd8
+			i_debug("REPLY: %s", smtp_reply_log(reply));
db3cd8
+
db3cd8
+		switch (ctx->reply++) {
db3cd8
+		case 0: /* greeting */
db3cd8
+			i_assert(reply->status == 220);
db3cd8
+			break;
db3cd8
+		case 1: /* bad command reply */
db3cd8
+			switch (client_index) {
db3cd8
+			case 1: case 2:
db3cd8
+				i_assert(reply->status == 501);
db3cd8
+				break;
db3cd8
+			case 0: case 3:
db3cd8
+				i_assert(smtp_reply_is_success(reply));
db3cd8
+				break;
db3cd8
+			default:
db3cd8
+				i_unreached();
db3cd8
+			}
db3cd8
+			ctx->replied = TRUE;
db3cd8
+			io_loop_stop(ioloop);
db3cd8
+			connection_disconnect(&conn->conn);
db3cd8
+			return;
db3cd8
+		default:
db3cd8
+			i_unreached();
db3cd8
+		}
db3cd8
+	}
db3cd8
+
db3cd8
+	i_assert(ret == 0);
db3cd8
+}
db3cd8
+
db3cd8
+static void
db3cd8
+test_bad_noop_client_connected(struct client_connection *conn)
db3cd8
+{
db3cd8
+	struct _bad_noop_client *ctx;
db3cd8
+
db3cd8
+	ctx = p_new(conn->pool, struct _bad_noop_client, 1);
db3cd8
+	ctx->parser = smtp_reply_parser_init(conn->conn.input, (size_t)-1);
db3cd8
+	conn->context = ctx;
db3cd8
+
db3cd8
+	switch (client_index) {
db3cd8
+	case 0:
db3cd8
+		o_stream_nsend_str(conn->conn.output,
db3cd8
+				   "NOOP\r\n");
db3cd8
+		break;
db3cd8
+	case 1:
db3cd8
+		o_stream_nsend_str(conn->conn.output,
db3cd8
+				   "NOOP \"frop\r\n");
db3cd8
+		break;
db3cd8
+	case 2:
db3cd8
+		o_stream_nsend_str(conn->conn.output,
db3cd8
+				   "NOOP fr\"op\r\n");
db3cd8
+		break;
db3cd8
+	case 3:
db3cd8
+		o_stream_nsend_str(conn->conn.output,
db3cd8
+				   "NOOP \"frop\"\r\n");
db3cd8
+		break;
db3cd8
+	default:
db3cd8
+		i_unreached();
db3cd8
+	}
db3cd8
+}
db3cd8
+
db3cd8
+static void
db3cd8
+test_bad_noop_client_deinit(struct client_connection *conn)
db3cd8
+{
db3cd8
+	struct _bad_noop_client *ctx = conn->context;
db3cd8
+
db3cd8
+	i_assert(ctx->replied);
db3cd8
+	smtp_reply_parser_deinit(&ctx->parser);
db3cd8
+}
db3cd8
+
db3cd8
+static void test_client_bad_noop(unsigned int index)
db3cd8
+{
db3cd8
+	test_client_input = test_bad_noop_client_input;
db3cd8
+	test_client_connected = test_bad_noop_client_connected;
db3cd8
+	test_client_deinit = test_bad_noop_client_deinit;
db3cd8
+	test_client_run(index);
db3cd8
+}
db3cd8
+
db3cd8
+/* server */
db3cd8
+
db3cd8
+static void
db3cd8
+test_server_bad_noop_disconnect(void *context ATTR_UNUSED, const char *reason)
db3cd8
+{
db3cd8
+	if (debug)
db3cd8
+		i_debug("Disconnect: %s", reason);
db3cd8
+}
db3cd8
+
db3cd8
+static int
db3cd8
+test_server_bad_noop_rcpt(void *conn_ctx ATTR_UNUSED,
db3cd8
+			  struct smtp_server_cmd_ctx *cmd ATTR_UNUSED,
db3cd8
+			  struct smtp_server_recipient *rcpt ATTR_UNUSED)
db3cd8
+{
db3cd8
+	test_assert(FALSE);
db3cd8
+	return 1;
db3cd8
+}
db3cd8
+
db3cd8
+static int
db3cd8
+test_server_bad_noop_data_begin(
db3cd8
+	void *conn_ctx ATTR_UNUSED, struct smtp_server_cmd_ctx *cmd ATTR_UNUSED,
db3cd8
+	struct smtp_server_transaction *trans ATTR_UNUSED,
db3cd8
+	struct istream *data_input ATTR_UNUSED)
db3cd8
+{
db3cd8
+	test_assert(FALSE);
db3cd8
+	return 1;
db3cd8
+}
db3cd8
+
db3cd8
+static void
db3cd8
+test_server_bad_noop(const struct smtp_server_settings *server_set)
db3cd8
+{
db3cd8
+	server_callbacks.conn_disconnect = test_server_bad_noop_disconnect;
db3cd8
+
db3cd8
+	server_callbacks.conn_cmd_rcpt = test_server_bad_noop_rcpt;
db3cd8
+	server_callbacks.conn_cmd_data_begin = test_server_bad_noop_data_begin;
db3cd8
+	test_server_run(server_set);
db3cd8
+}
db3cd8
+
db3cd8
+/* test */
db3cd8
+
db3cd8
+static void test_bad_noop(void)
db3cd8
+{
db3cd8
+	struct smtp_server_settings smtp_server_set;
db3cd8
+
db3cd8
+	test_server_defaults(&smtp_server_set);
db3cd8
+	smtp_server_set.max_client_idle_time_msecs = 1000;
db3cd8
+
db3cd8
+	test_begin("bad NOOP");
db3cd8
+	test_run_client_server(&smtp_server_set,
db3cd8
+			       test_server_bad_noop,
db3cd8
+			       test_client_bad_noop, 4);
db3cd8
+	test_end();
db3cd8
+}
db3cd8
+
db3cd8
+/*
db3cd8
  * MAIL workarounds
db3cd8
  */
db3cd8
 
db3cd8
@@ -2023,11 +2501,14 @@ static void (*const test_functions[])(vo
db3cd8
 	test_slow_client,
db3cd8
 	test_hanging_command_payload,
db3cd8
 	test_bad_command,
db3cd8
+	test_many_bad_commands,
db3cd8
 	test_long_command,
db3cd8
 	test_big_data,
db3cd8
 	test_bad_ehlo,
db3cd8
 	test_bad_mail,
db3cd8
 	test_bad_rcpt,
db3cd8
+	test_bad_vrfy,
db3cd8
+	test_bad_noop,
db3cd8
 	test_mail_workarounds,
db3cd8
 	test_rcpt_workarounds,
db3cd8
 	test_too_many_recipients,
db3cd8
diff -up dovecot-2.3.8/src/lib-smtp/test-smtp-syntax.c.CVE_2020_10957,10958,10967 dovecot-2.3.8/src/lib-smtp/test-smtp-syntax.c
db3cd8
--- dovecot-2.3.8/src/lib-smtp/test-smtp-syntax.c.CVE_2020_10957,10958,10967	2020-05-29 19:11:53.359162313 +0200
db3cd8
+++ dovecot-2.3.8/src/lib-smtp/test-smtp-syntax.c	2020-05-29 19:11:53.359162313 +0200
db3cd8
@@ -0,0 +1,150 @@
db3cd8
+/* Copyright (c) 2020 Dovecot authors, see the included COPYING file */
db3cd8
+
db3cd8
+#include "lib.h"
db3cd8
+#include "str.h"
db3cd8
+#include "str-sanitize.h"
db3cd8
+#include "test-common.h"
db3cd8
+#include "smtp-syntax.h"
db3cd8
+
db3cd8
+/*
db3cd8
+ * Valid string parse tests
db3cd8
+ */
db3cd8
+
db3cd8
+struct valid_string_parse_test {
db3cd8
+	const char *input, *parsed, *output;
db3cd8
+};
db3cd8
+
db3cd8
+static const struct valid_string_parse_test
db3cd8
+valid_string_parse_tests[] = {
db3cd8
+	{
db3cd8
+		.input = "",
db3cd8
+		.parsed = "",
db3cd8
+	},
db3cd8
+	{
db3cd8
+		.input = "atom",
db3cd8
+		.parsed = "atom",
db3cd8
+	},
db3cd8
+	{
db3cd8
+		.input = "abcdefghijklmnopqrstuvwxyz"
db3cd8
+			 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
db3cd8
+			 "0123456789!#$%&'*+-/=?^_`{|}~",
db3cd8
+		.parsed = "abcdefghijklmnopqrstuvwxyz"
db3cd8
+			  "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
db3cd8
+			  "0123456789!#$%&'*+-/=?^_`{|}~",
db3cd8
+	},
db3cd8
+	{
db3cd8
+		.input = "\"quoted-string\"",
db3cd8
+		.parsed = "quoted-string",
db3cd8
+		.output = "quoted-string",
db3cd8
+	},
db3cd8
+	{
db3cd8
+		.input = "\"quoted \\\"string\\\"\"",
db3cd8
+		.parsed = "quoted \"string\"",
db3cd8
+	},
db3cd8
+	{
db3cd8
+		.input = "\"quoted \\\\string\\\\\"",
db3cd8
+		.parsed = "quoted \\string\\",
db3cd8
+	},
db3cd8
+};
db3cd8
+
db3cd8
+static const unsigned int valid_string_parse_test_count =
db3cd8
+	N_ELEMENTS(valid_string_parse_tests);
db3cd8
+
db3cd8
+static void test_smtp_string_parse_valid(void)
db3cd8
+{
db3cd8
+	unsigned int i;
db3cd8
+
db3cd8
+	for (i = 0; i < valid_string_parse_test_count; i++) T_BEGIN {
db3cd8
+		const struct valid_string_parse_test *test =
db3cd8
+			&valid_string_parse_tests[i];
db3cd8
+		const char *parsed, *error = NULL;
db3cd8
+		int ret;
db3cd8
+
db3cd8
+		ret = smtp_string_parse(test->input, &parsed, &error);
db3cd8
+
db3cd8
+		test_begin(t_strdup_printf("smtp string valid [%d]", i));
db3cd8
+		test_out_reason(t_strdup_printf("parse(\"%s\")", test->input),
db3cd8
+				ret >= 0, error);
db3cd8
+		test_assert(ret != 0 || *test->input == '\0');
db3cd8
+
db3cd8
+		if (!test_has_failed()) {
db3cd8
+			string_t *encoded;
db3cd8
+			const char *output;
db3cd8
+
db3cd8
+			test_out(t_strdup_printf("parsed = \"%s\"", parsed),
db3cd8
+				 null_strcmp(parsed, test->parsed) == 0);
db3cd8
+
db3cd8
+			encoded = t_str_new(255);
db3cd8
+			smtp_string_write(encoded, parsed);
db3cd8
+			output = (test->output == NULL ?
db3cd8
+				  test->input : test->output);
db3cd8
+			test_out(t_strdup_printf("write() = \"%s\"",
db3cd8
+						 str_c(encoded)),
db3cd8
+				 strcmp(str_c(encoded), output) == 0);
db3cd8
+		}
db3cd8
+		test_end();
db3cd8
+	} T_END;
db3cd8
+}
db3cd8
+
db3cd8
+/*
db3cd8
+ * Invalid string parse tests
db3cd8
+ */
db3cd8
+
db3cd8
+struct invalid_string_parse_test {
db3cd8
+	const char *input;
db3cd8
+};
db3cd8
+
db3cd8
+static const struct invalid_string_parse_test
db3cd8
+invalid_string_parse_tests[] = {
db3cd8
+	{
db3cd8
+		.input = " ",
db3cd8
+	},
db3cd8
+	{
db3cd8
+		.input = "\\",
db3cd8
+	},
db3cd8
+	{
db3cd8
+		.input = "\"",
db3cd8
+	},
db3cd8
+	{
db3cd8
+		.input = "\"aa",
db3cd8
+	},
db3cd8
+	{
db3cd8
+		.input = "aa\"",
db3cd8
+	},
db3cd8
+};
db3cd8
+
db3cd8
+static const unsigned int invalid_string_parse_test_count =
db3cd8
+	N_ELEMENTS(invalid_string_parse_tests);
db3cd8
+
db3cd8
+static void test_smtp_string_parse_invalid(void)
db3cd8
+{
db3cd8
+	unsigned int i;
db3cd8
+
db3cd8
+	for (i = 0; i < invalid_string_parse_test_count; i++) T_BEGIN {
db3cd8
+		const struct invalid_string_parse_test *test =
db3cd8
+			&invalid_string_parse_tests[i];
db3cd8
+		const char *parsed, *error;
db3cd8
+		int ret;
db3cd8
+
db3cd8
+		ret = smtp_string_parse(test->input, &parsed, &error);
db3cd8
+
db3cd8
+		test_begin(t_strdup_printf("smtp string invalid [%d]", i));
db3cd8
+		test_out_reason(t_strdup_printf("parse(\"%s\")", test->input),
db3cd8
+				ret < 0, error);
db3cd8
+		test_end();
db3cd8
+	} T_END;
db3cd8
+}
db3cd8
+
db3cd8
+/*
db3cd8
+ * Tests
db3cd8
+ */
db3cd8
+
db3cd8
+int main(void)
db3cd8
+{
db3cd8
+	static void (*test_functions[])(void) = {
db3cd8
+		test_smtp_string_parse_valid,
db3cd8
+		test_smtp_string_parse_invalid,
db3cd8
+		NULL
db3cd8
+	};
db3cd8
+	return test_run(test_functions);
db3cd8
+}
db3cd8
diff -up dovecot-2.3.8/src/lmtp/lmtp-commands.c.CVE_2020_10957,10958,10967 dovecot-2.3.8/src/lmtp/lmtp-commands.c
db3cd8
--- dovecot-2.3.8/src/lmtp/lmtp-commands.c.CVE_2020_10957,10958,10967	2019-10-08 10:46:18.000000000 +0200
db3cd8
+++ dovecot-2.3.8/src/lmtp/lmtp-commands.c	2020-05-29 19:11:53.359162313 +0200
db3cd8
@@ -66,10 +66,18 @@ int client_default_cmd_rcpt(struct clien
db3cd8
 	char delim = '\0';
db3cd8
 	int ret;
db3cd8
 
db3cd8
+	i_assert(!smtp_address_isnull(rcpt->path));
db3cd8
+	if (*rcpt->path->localpart == '\0' && rcpt->path->domain == NULL) {
db3cd8
+		smtp_server_recipient_reply(
db3cd8
+			rcpt, 550, "5.1.1",
db3cd8
+			"Unacceptable TO: Empty path not allowed");
db3cd8
+		return -1;
db3cd8
+	}
db3cd8
 
db3cd8
 	smtp_address_detail_parse_temp(
db3cd8
 		client->unexpanded_lda_set->recipient_delimiter,
db3cd8
 		rcpt->path, &username, &delim, &detail);
db3cd8
+	i_assert(*username != '\0');
db3cd8
 
db3cd8
 	/* Make user name and detail available in the recipient event. The
db3cd8
 	   mail_user event (for local delivery) also adds the user field, but