From: Stephan Bosch Date: Sat, 14 Sep 2019 15:59:08 +0000 (+0200) Subject: lib-smtp: smtp-server - Support accepting broken path parameter in MAIL command. X-Git-Tag: 2.3.9~119 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4622e7f5097193fb269306d31b92c4d47c9e4e5a;p=thirdparty%2Fdovecot%2Fcore.git lib-smtp: smtp-server - Support accepting broken path parameter in MAIL command. --- diff --git a/src/lda/main.c b/src/lda/main.c index 7076460db4..4d0755886c 100644 --- a/src/lda/main.c +++ b/src/lda/main.c @@ -556,6 +556,11 @@ int main(int argc, char *argv[]) "userdb lookup skipped, username taken from %s", user_source); } + if (mail_from_error != NULL) { + e_debug(event, "Broken -f parameter: %s " + "(proceeding with <> as sender)", + mail_from_error); + } ret = lda_deliver(&dinput, service_user, user, path, rcpt_to, rcpt_to_source, stderr_rejection); diff --git a/src/lib-smtp/smtp-server-cmd-mail.c b/src/lib-smtp/smtp-server-cmd-mail.c index 57a7c74e37..7fbcaa9348 100644 --- a/src/lib-smtp/smtp-server-cmd-mail.c +++ b/src/lib-smtp/smtp-server-cmd-mail.c @@ -72,7 +72,7 @@ void smtp_server_cmd_mail(struct smtp_server_cmd_ctx *cmd, struct smtp_server_cmd_mail *mail_data; enum smtp_address_parse_flags path_parse_flags; const char *const *param_extensions = NULL; - struct smtp_address *path; + struct smtp_address *path = NULL; enum smtp_param_parse_error pperror; const char *error; int ret; @@ -103,23 +103,38 @@ void smtp_server_cmd_mail(struct smtp_server_cmd_ctx *cmd, "Unexpected whitespace before path"); return; } - path_parse_flags = SMTP_ADDRESS_PARSE_FLAG_ALLOW_EMPTY; + path_parse_flags = + SMTP_ADDRESS_PARSE_FLAG_ALLOW_EMPTY | + SMTP_ADDRESS_PARSE_FLAG_PRESERVE_RAW; if (*params != '\0' && - (set->workarounds & SMTP_SERVER_WORKAROUND_MAILBOX_FOR_PATH) != 0) + (set->mail_path_allow_broken || + (set->workarounds & SMTP_SERVER_WORKAROUND_MAILBOX_FOR_PATH) != 0)) path_parse_flags |= SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL; - if (smtp_address_parse_path_full(pool_datastack_create(), params, - path_parse_flags, &path, &error, - ¶ms) < 0) { - smtp_server_reply(cmd, 501, "5.5.4", "Invalid FROM: %s", error); + if (set->mail_path_allow_broken) { + path_parse_flags |= + SMTP_ADDRESS_PARSE_FLAG_ALLOW_BAD_LOCALPART | + SMTP_ADDRESS_PARSE_FLAG_IGNORE_BROKEN; + } + ret = smtp_address_parse_path_full(pool_datastack_create(), params, + path_parse_flags, &path, &error, + ¶ms); + if (ret < 0 && !smtp_address_is_broken(path)) { + smtp_server_reply(cmd, 501, "5.5.4", + "Invalid FROM: %s", error); return; } if (*params == ' ') params++; else if (*params != '\0') { - smtp_server_reply(cmd, 501, "5.5.4", + smtp_server_reply( + cmd, 501, "5.5.4", "Invalid FROM: Invalid character in path"); return; } + if (ret < 0) { + e_debug(conn->event, "Invalid FROM: %s " + "(proceeding with <> as sender)", error); + } mail_data = p_new(cmd->pool, struct smtp_server_cmd_mail, 1); diff --git a/src/lib-smtp/smtp-server-connection.c b/src/lib-smtp/smtp-server-connection.c index 8c4f2e36b1..5e0a148548 100644 --- a/src/lib-smtp/smtp-server-connection.c +++ b/src/lib-smtp/smtp-server-connection.c @@ -881,6 +881,9 @@ smtp_server_connection_alloc(struct smtp_server *server, conn->set.tls_required || set->tls_required; conn->set.auth_optional = conn->set.auth_optional || set->auth_optional; + conn->set.mail_path_allow_broken = + conn->set.mail_path_allow_broken || + set->mail_path_allow_broken; conn->set.rcpt_domain_optional = conn->set.rcpt_domain_optional || set->rcpt_domain_optional; diff --git a/src/lib-smtp/smtp-server-transaction.c b/src/lib-smtp/smtp-server-transaction.c index 1fe6648475..a8481abbc9 100644 --- a/src/lib-smtp/smtp-server-transaction.c +++ b/src/lib-smtp/smtp-server-transaction.c @@ -20,6 +20,8 @@ smtp_server_transaction_update_event(struct smtp_server_transaction *trans) event_add_str(event, "transaction_id", trans->id); event_add_str(event, "mail_from", smtp_address_encode(trans->mail_from)); + event_add_str(event, "mail_from_raw", + smtp_address_encode_raw(trans->mail_from)); smtp_params_mail_add_to_event(&trans->params, event); event_set_append_log_prefix(event, t_strdup_printf("trans %s: ", trans->id)); diff --git a/src/lib-smtp/smtp-server.c b/src/lib-smtp/smtp-server.c index 3058a6817f..cf5ea4374d 100644 --- a/src/lib-smtp/smtp-server.c +++ b/src/lib-smtp/smtp-server.c @@ -82,6 +82,7 @@ struct smtp_server *smtp_server_init(const struct smtp_server_settings *set) server->set.tls_required = set->tls_required; server->set.auth_optional = set->auth_optional; server->set.rcpt_domain_optional = set->rcpt_domain_optional; + server->set.mail_path_allow_broken = set->mail_path_allow_broken; server->set.debug = set->debug; /* There is no event log prefix added here, since the server itself does diff --git a/src/lib-smtp/smtp-server.h b/src/lib-smtp/smtp-server.h index 633282761e..c25b97a090 100644 --- a/src/lib-smtp/smtp-server.h +++ b/src/lib-smtp/smtp-server.h @@ -364,6 +364,15 @@ struct smtp_server_settings { bool auth_optional:1; /* TLS security is required for this service */ bool tls_required:1; + /* The path provided to the MAIL command does not need to be valid. A + completely invalid path will parse as <>. Paths that can still be + fixed by splitting it on the last `@' yielding a usable localpart and + domain, will be parsed as such. There are limits though; when the + path is badly delimited or contains control characters, the MAIL + command will still fail. The unparsed broken address will be + available in the `raw' field of struct smtp_address for logging etc. + */ + bool mail_path_allow_broken:1; /* The path provided to the RCPT command does not need to have the domain part. */ bool rcpt_domain_optional:1; diff --git a/src/lib-smtp/test-smtp-server-errors.c b/src/lib-smtp/test-smtp-server-errors.c index d13df451e5..1a96fa89ba 100644 --- a/src/lib-smtp/test-smtp-server-errors.c +++ b/src/lib-smtp/test-smtp-server-errors.c @@ -2014,6 +2014,225 @@ static void test_data_binarymime(void) test_end(); } +/* + * MAIL broken path + */ + +/* client */ + +struct _mail_broken_path_client { + struct smtp_reply_parser *parser; + unsigned int reply; + + bool replied:1; +}; + +static void +test_mail_broken_path_client_input(struct client_connection *conn) +{ + struct _mail_broken_path_client *ctx = conn->context; + struct smtp_reply *reply; + const char *error; + int ret; + + while ((ret=smtp_reply_parse_next(ctx->parser, FALSE, + &reply, &error)) > 0) { + if (debug) + i_debug("REPLY: %s", smtp_reply_log(reply)); + + switch (ctx->reply++) { + case 0: /* greeting */ + i_assert(reply->status == 220); + break; + case 1: /* bad command reply */ + switch (client_index) { + case 0: case 1: case 2: case 4: case 5: + case 6: case 7: case 8: + i_assert(reply->status == 501); + break; + case 3: case 9: case 10: case 11: case 12: case 13: + case 14: case 15: case 16: case 17: + i_assert(reply->status == 250); + break; + default: + i_info("STATUS: %u", reply->status); + i_unreached(); + } + ctx->replied = TRUE; + io_loop_stop(ioloop); + connection_disconnect(&conn->conn); + return; + default: + i_unreached(); + } + } + + i_assert(ret >= 0); +} + +static void +test_mail_broken_path_client_connected(struct client_connection *conn) +{ + struct _mail_broken_path_client *ctx; + + ctx = p_new(conn->pool, struct _mail_broken_path_client, 1); + ctx->parser = smtp_reply_parser_init(conn->conn.input, (size_t)-1); + conn->context = ctx; + + switch (client_index) { + case 0: + o_stream_nsend_str(conn->conn.output, + "MAIL FROM: \r\n"); + break; + case 1: + o_stream_nsend_str(conn->conn.output, + "MAIL FROM:\t\r\n"); + break; + case 2: + o_stream_nsend_str(conn->conn.output, + "MAIL FROM:\t \r\n"); + break; + case 3: + o_stream_nsend_str(conn->conn.output, + "MAIL FROM:hendrik@example.com\r\n"); + break; + case 4: + o_stream_nsend_str(conn->conn.output, + "MAIL FROM: hendrik@example.com\r\n"); + break; + case 5: + o_stream_nsend_str(conn->conn.output, + "MAIL FROM:\r\n"); + break; + case 6: + o_stream_nsend_str(conn->conn.output, + "MAIL FROM: \r\n"); + break; + case 7: + o_stream_nsend_str(conn->conn.output, + "MAIL FROM: BODY=7BIT\r\n"); + break; + case 8: + o_stream_nsend_str(conn->conn.output, + "MAIL FROM: <>\r\n"); + break; + case 9: + o_stream_nsend_str(conn->conn.output, + "MAIL FROM:\r\n"); + break; + case 10: + o_stream_nsend_str(conn->conn.output, + "MAIL FROM:<>\r\n"); + break; + case 11: + o_stream_nsend_str(conn->conn.output, + "MAIL FROM:bla$die%bla@die&bla\r\n"); + break; + case 12: + o_stream_nsend_str(conn->conn.output, + "MAIL FROM:\r\n"); + break; + case 13: + o_stream_nsend_str(conn->conn.output, + "MAIL FROM:\r\n"); + break; + case 14: + o_stream_nsend_str(conn->conn.output, + "MAIL FROM:/@)$@)BLAARGH!@#$$\r\n"); + break; + case 15: + o_stream_nsend_str(conn->conn.output, + "MAIL FROM:\r\n"); + break; + case 16: + o_stream_nsend_str(conn->conn.output, + "MAIL FROM:f\xc3\xb6\xc3\xa4@\xc3\xb6\xc3\xa4\r\n"); + break; + case 17: + o_stream_nsend_str(conn->conn.output, + "MAIL FROM:\r\n"); + break; + default: + i_unreached(); + } +} + +static void +test_mail_broken_path_client_deinit(struct client_connection *conn) +{ + struct _mail_broken_path_client *ctx = conn->context; + + i_assert(ctx->replied); + smtp_reply_parser_deinit(&ctx->parser); +} + +static void test_client_mail_broken_path(unsigned int index) +{ + test_client_input = test_mail_broken_path_client_input; + test_client_connected = test_mail_broken_path_client_connected; + test_client_deinit = test_mail_broken_path_client_deinit; + test_client_run(index); +} + +/* server */ + +static void +test_server_mail_broken_path_disconnect(void *context ATTR_UNUSED, + const char *reason) +{ + if (debug) + i_debug("Disconnect: %s", reason); +} + +static int +test_server_mail_broken_path_rcpt(void *conn_ctx ATTR_UNUSED, + struct smtp_server_cmd_ctx *cmd ATTR_UNUSED, + struct smtp_server_recipient *rcpt ATTR_UNUSED) +{ + test_assert(FALSE); + return 1; +} + +static int +test_server_mail_broken_path_data_begin(void *conn_ctx ATTR_UNUSED, + struct smtp_server_cmd_ctx *cmd ATTR_UNUSED, + struct smtp_server_transaction *trans ATTR_UNUSED, + struct istream *data_input ATTR_UNUSED) +{ + test_assert(FALSE); + return 1; +} + +static void test_server_mail_broken_path +(const struct smtp_server_settings *server_set) +{ + server_callbacks.conn_disconnect = + test_server_mail_broken_path_disconnect; + + server_callbacks.conn_cmd_rcpt = + test_server_mail_broken_path_rcpt; + server_callbacks.conn_cmd_data_begin = + test_server_mail_broken_path_data_begin; + test_server_run(server_set); +} + +/* test */ + +static void test_mail_broken_path(void) +{ + struct smtp_server_settings smtp_server_set; + + test_server_defaults(&smtp_server_set); + smtp_server_set.mail_path_allow_broken = TRUE; + smtp_server_set.max_client_idle_time_msecs = 1000; + + test_begin("MAIL workarounds"); + test_run_client_server(&smtp_server_set, + test_server_mail_broken_path, + test_client_mail_broken_path, 16); + test_end(); +} + /* * All tests */ @@ -2034,6 +2253,7 @@ static void (*const test_functions[])(void) = { test_data_no_mail, test_data_no_rcpt, test_data_binarymime, + test_mail_broken_path, NULL };