Blame SOURCES/dovecot-2.3.10-CVE_2020_10957p6.patch

311a5d
From f206dc478d1b22183f645ea8c98ffe3712e106b2 Mon Sep 17 00:00:00 2001
311a5d
From: Stephan Bosch <stephan.bosch@open-xchange.com>
311a5d
Date: Tue, 24 Mar 2020 22:33:45 +0100
311a5d
Subject: [PATCH] lib-smtp: test-smtp-server-errors - Add tests for VRFY and
311a5d
 NOOP commands with invalid parameters.
311a5d
311a5d
---
311a5d
 src/lib-smtp/test-smtp-server-errors.c | 312 +++++++++++++++++++++++++
311a5d
 1 file changed, 312 insertions(+)
311a5d
311a5d
diff --git a/src/lib-smtp/test-smtp-server-errors.c b/src/lib-smtp/test-smtp-server-errors.c
311a5d
index 759c332d88..13d3b97def 100644
311a5d
--- a/src/lib-smtp/test-smtp-server-errors.c
311a5d
+++ b/src/lib-smtp/test-smtp-server-errors.c
311a5d
@@ -1273,6 +1273,316 @@ static void test_bad_rcpt(void)
311a5d
 	test_end();
311a5d
 }
311a5d
 
311a5d
+/*
311a5d
+ * Bad VRFY
311a5d
+ */
311a5d
+
311a5d
+/* client */
311a5d
+
311a5d
+struct _bad_vrfy_client {
311a5d
+	struct smtp_reply_parser *parser;
311a5d
+	unsigned int reply;
311a5d
+
311a5d
+	bool replied:1;
311a5d
+};
311a5d
+
311a5d
+static void
311a5d
+test_bad_vrfy_client_input(struct client_connection *conn)
311a5d
+{
311a5d
+	struct _bad_vrfy_client *ctx = conn->context;
311a5d
+	struct smtp_reply *reply;
311a5d
+	const char *error;
311a5d
+	int ret;
311a5d
+
311a5d
+	while ((ret = smtp_reply_parse_next(ctx->parser, FALSE,
311a5d
+					    &reply, &error)) > 0) {
311a5d
+		if (debug)
311a5d
+			i_debug("REPLY: %s", smtp_reply_log(reply));
311a5d
+
311a5d
+		switch (ctx->reply++) {
311a5d
+		case 0: /* greeting */
311a5d
+			i_assert(reply->status == 220);
311a5d
+			break;
311a5d
+		case 1: /* bad command reply */
311a5d
+			switch (client_index) {
311a5d
+			case 0: case 1: case 2:
311a5d
+				i_assert(reply->status == 501);
311a5d
+				break;
311a5d
+			case 3:
311a5d
+				i_assert(smtp_reply_is_success(reply));
311a5d
+				break;
311a5d
+			default:
311a5d
+				i_unreached();
311a5d
+			}
311a5d
+			ctx->replied = TRUE;
311a5d
+			io_loop_stop(ioloop);
311a5d
+			connection_disconnect(&conn->conn);
311a5d
+			return;
311a5d
+		default:
311a5d
+			i_unreached();
311a5d
+		}
311a5d
+	}
311a5d
+
311a5d
+	i_assert(ret == 0);
311a5d
+}
311a5d
+
311a5d
+static void
311a5d
+test_bad_vrfy_client_connected(struct client_connection *conn)
311a5d
+{
311a5d
+	struct _bad_vrfy_client *ctx;
311a5d
+
311a5d
+	ctx = p_new(conn->pool, struct _bad_vrfy_client, 1);
311a5d
+	ctx->parser = smtp_reply_parser_init(conn->conn.input, (size_t)-1);
311a5d
+	conn->context = ctx;
311a5d
+
311a5d
+	switch (client_index) {
311a5d
+	case 0:
311a5d
+		o_stream_nsend_str(conn->conn.output,
311a5d
+				   "VRFY\r\n");
311a5d
+		break;
311a5d
+	case 1:
311a5d
+		o_stream_nsend_str(conn->conn.output,
311a5d
+				   "VRFY \"hendrik\r\n");
311a5d
+		break;
311a5d
+	case 2:
311a5d
+		o_stream_nsend_str(conn->conn.output,
311a5d
+				   "VRFY hen\"drik\r\n");
311a5d
+		break;
311a5d
+	case 3:
311a5d
+		o_stream_nsend_str(conn->conn.output,
311a5d
+				   "VRFY \"hendrik\"\r\n");
311a5d
+		break;
311a5d
+	default:
311a5d
+		i_unreached();
311a5d
+	}
311a5d
+}
311a5d
+
311a5d
+static void
311a5d
+test_bad_vrfy_client_deinit(struct client_connection *conn)
311a5d
+{
311a5d
+	struct _bad_vrfy_client *ctx = conn->context;
311a5d
+
311a5d
+	i_assert(ctx->replied);
311a5d
+	smtp_reply_parser_deinit(&ctx->parser);
311a5d
+}
311a5d
+
311a5d
+static void test_client_bad_vrfy(unsigned int index)
311a5d
+{
311a5d
+	test_client_input = test_bad_vrfy_client_input;
311a5d
+	test_client_connected = test_bad_vrfy_client_connected;
311a5d
+	test_client_deinit = test_bad_vrfy_client_deinit;
311a5d
+	test_client_run(index);
311a5d
+}
311a5d
+
311a5d
+/* server */
311a5d
+
311a5d
+static void
311a5d
+test_server_bad_vrfy_disconnect(void *context ATTR_UNUSED, const char *reason)
311a5d
+{
311a5d
+	if (debug)
311a5d
+		i_debug("Disconnect: %s", reason);
311a5d
+}
311a5d
+
311a5d
+static int
311a5d
+test_server_bad_vrfy_rcpt(void *conn_ctx ATTR_UNUSED,
311a5d
+			  struct smtp_server_cmd_ctx *cmd ATTR_UNUSED,
311a5d
+			  struct smtp_server_recipient *rcpt ATTR_UNUSED)
311a5d
+{
311a5d
+	test_assert(FALSE);
311a5d
+	return 1;
311a5d
+}
311a5d
+
311a5d
+static int
311a5d
+test_server_bad_vrfy_data_begin(
311a5d
+	void *conn_ctx ATTR_UNUSED, struct smtp_server_cmd_ctx *cmd ATTR_UNUSED,
311a5d
+	struct smtp_server_transaction *trans ATTR_UNUSED,
311a5d
+	struct istream *data_input ATTR_UNUSED)
311a5d
+{
311a5d
+	test_assert(FALSE);
311a5d
+	return 1;
311a5d
+}
311a5d
+
311a5d
+static void
311a5d
+test_server_bad_vrfy(const struct smtp_server_settings *server_set)
311a5d
+{
311a5d
+	server_callbacks.conn_disconnect = test_server_bad_vrfy_disconnect;
311a5d
+
311a5d
+	server_callbacks.conn_cmd_rcpt = test_server_bad_vrfy_rcpt;
311a5d
+	server_callbacks.conn_cmd_data_begin = test_server_bad_vrfy_data_begin;
311a5d
+	test_server_run(server_set);
311a5d
+}
311a5d
+
311a5d
+/* test */
311a5d
+
311a5d
+static void test_bad_vrfy(void)
311a5d
+{
311a5d
+	struct smtp_server_settings smtp_server_set;
311a5d
+
311a5d
+	test_server_defaults(&smtp_server_set);
311a5d
+	smtp_server_set.max_client_idle_time_msecs = 1000;
311a5d
+
311a5d
+	test_begin("bad VRFY");
311a5d
+	test_run_client_server(&smtp_server_set,
311a5d
+			       test_server_bad_vrfy,
311a5d
+			       test_client_bad_vrfy, 4);
311a5d
+	test_end();
311a5d
+}
311a5d
+
311a5d
+/*
311a5d
+ * Bad NOOP
311a5d
+ */
311a5d
+
311a5d
+/* client */
311a5d
+
311a5d
+struct _bad_noop_client {
311a5d
+	struct smtp_reply_parser *parser;
311a5d
+	unsigned int reply;
311a5d
+
311a5d
+	bool replied:1;
311a5d
+};
311a5d
+
311a5d
+static void
311a5d
+test_bad_noop_client_input(struct client_connection *conn)
311a5d
+{
311a5d
+	struct _bad_noop_client *ctx = conn->context;
311a5d
+	struct smtp_reply *reply;
311a5d
+	const char *error;
311a5d
+	int ret;
311a5d
+
311a5d
+	while ((ret = smtp_reply_parse_next(ctx->parser, FALSE,
311a5d
+					    &reply, &error)) > 0) {
311a5d
+		if (debug)
311a5d
+			i_debug("REPLY: %s", smtp_reply_log(reply));
311a5d
+
311a5d
+		switch (ctx->reply++) {
311a5d
+		case 0: /* greeting */
311a5d
+			i_assert(reply->status == 220);
311a5d
+			break;
311a5d
+		case 1: /* bad command reply */
311a5d
+			switch (client_index) {
311a5d
+			case 1: case 2:
311a5d
+				i_assert(reply->status == 501);
311a5d
+				break;
311a5d
+			case 0: case 3:
311a5d
+				i_assert(smtp_reply_is_success(reply));
311a5d
+				break;
311a5d
+			default:
311a5d
+				i_unreached();
311a5d
+			}
311a5d
+			ctx->replied = TRUE;
311a5d
+			io_loop_stop(ioloop);
311a5d
+			connection_disconnect(&conn->conn);
311a5d
+			return;
311a5d
+		default:
311a5d
+			i_unreached();
311a5d
+		}
311a5d
+	}
311a5d
+
311a5d
+	i_assert(ret == 0);
311a5d
+}
311a5d
+
311a5d
+static void
311a5d
+test_bad_noop_client_connected(struct client_connection *conn)
311a5d
+{
311a5d
+	struct _bad_noop_client *ctx;
311a5d
+
311a5d
+	ctx = p_new(conn->pool, struct _bad_noop_client, 1);
311a5d
+	ctx->parser = smtp_reply_parser_init(conn->conn.input, (size_t)-1);
311a5d
+	conn->context = ctx;
311a5d
+
311a5d
+	switch (client_index) {
311a5d
+	case 0:
311a5d
+		o_stream_nsend_str(conn->conn.output,
311a5d
+				   "NOOP\r\n");
311a5d
+		break;
311a5d
+	case 1:
311a5d
+		o_stream_nsend_str(conn->conn.output,
311a5d
+				   "NOOP \"frop\r\n");
311a5d
+		break;
311a5d
+	case 2:
311a5d
+		o_stream_nsend_str(conn->conn.output,
311a5d
+				   "NOOP fr\"op\r\n");
311a5d
+		break;
311a5d
+	case 3:
311a5d
+		o_stream_nsend_str(conn->conn.output,
311a5d
+				   "NOOP \"frop\"\r\n");
311a5d
+		break;
311a5d
+	default:
311a5d
+		i_unreached();
311a5d
+	}
311a5d
+}
311a5d
+
311a5d
+static void
311a5d
+test_bad_noop_client_deinit(struct client_connection *conn)
311a5d
+{
311a5d
+	struct _bad_noop_client *ctx = conn->context;
311a5d
+
311a5d
+	i_assert(ctx->replied);
311a5d
+	smtp_reply_parser_deinit(&ctx->parser);
311a5d
+}
311a5d
+
311a5d
+static void test_client_bad_noop(unsigned int index)
311a5d
+{
311a5d
+	test_client_input = test_bad_noop_client_input;
311a5d
+	test_client_connected = test_bad_noop_client_connected;
311a5d
+	test_client_deinit = test_bad_noop_client_deinit;
311a5d
+	test_client_run(index);
311a5d
+}
311a5d
+
311a5d
+/* server */
311a5d
+
311a5d
+static void
311a5d
+test_server_bad_noop_disconnect(void *context ATTR_UNUSED, const char *reason)
311a5d
+{
311a5d
+	if (debug)
311a5d
+		i_debug("Disconnect: %s", reason);
311a5d
+}
311a5d
+
311a5d
+static int
311a5d
+test_server_bad_noop_rcpt(void *conn_ctx ATTR_UNUSED,
311a5d
+			  struct smtp_server_cmd_ctx *cmd ATTR_UNUSED,
311a5d
+			  struct smtp_server_recipient *rcpt ATTR_UNUSED)
311a5d
+{
311a5d
+	test_assert(FALSE);
311a5d
+	return 1;
311a5d
+}
311a5d
+
311a5d
+static int
311a5d
+test_server_bad_noop_data_begin(
311a5d
+	void *conn_ctx ATTR_UNUSED, struct smtp_server_cmd_ctx *cmd ATTR_UNUSED,
311a5d
+	struct smtp_server_transaction *trans ATTR_UNUSED,
311a5d
+	struct istream *data_input ATTR_UNUSED)
311a5d
+{
311a5d
+	test_assert(FALSE);
311a5d
+	return 1;
311a5d
+}
311a5d
+
311a5d
+static void
311a5d
+test_server_bad_noop(const struct smtp_server_settings *server_set)
311a5d
+{
311a5d
+	server_callbacks.conn_disconnect = test_server_bad_noop_disconnect;
311a5d
+
311a5d
+	server_callbacks.conn_cmd_rcpt = test_server_bad_noop_rcpt;
311a5d
+	server_callbacks.conn_cmd_data_begin = test_server_bad_noop_data_begin;
311a5d
+	test_server_run(server_set);
311a5d
+}
311a5d
+
311a5d
+/* test */
311a5d
+
311a5d
+static void test_bad_noop(void)
311a5d
+{
311a5d
+	struct smtp_server_settings smtp_server_set;
311a5d
+
311a5d
+	test_server_defaults(&smtp_server_set);
311a5d
+	smtp_server_set.max_client_idle_time_msecs = 1000;
311a5d
+
311a5d
+	test_begin("bad NOOP");
311a5d
+	test_run_client_server(&smtp_server_set,
311a5d
+			       test_server_bad_noop,
311a5d
+			       test_client_bad_noop, 4);
311a5d
+	test_end();
311a5d
+}
311a5d
+
311a5d
 /*
311a5d
  * MAIL workarounds
311a5d
  */
311a5d
@@ -2246,6 +2556,8 @@ static void (*const test_functions[])(void) = {
311a5d
 	test_bad_ehlo,
311a5d
 	test_bad_mail,
311a5d
 	test_bad_rcpt,
311a5d
+	test_bad_vrfy,
311a5d
+	test_bad_noop,
311a5d
 	test_mail_workarounds,
311a5d
 	test_rcpt_workarounds,
311a5d
 	test_too_many_recipients,