diff --git a/src/lib-smtp/smtp-server-cmd-data.c b/src/lib-smtp/smtp-server-cmd-data.c index 674416609c..122aa5a2d9 100644 --- a/src/lib-smtp/smtp-server-cmd-data.c +++ b/src/lib-smtp/smtp-server-cmd-data.c @@ -21,6 +21,33 @@ struct cmd_data_context { bool chunk_last:1; }; +static void +smtp_server_cmd_data_size_limit_exceeded(struct smtp_server_cmd_ctx *cmd) +{ + struct smtp_server_command *command = cmd->cmd; + + smtp_server_command_fail(command, 552, "5.2.3", + "Message size exceeds administrative limit"); +} + +bool smtp_server_cmd_data_check_size(struct smtp_server_cmd_ctx *cmd) +{ + struct smtp_server_connection *conn = cmd->conn; + const struct smtp_server_settings *set = &conn->set; + + i_assert(conn->state.state == SMTP_SERVER_STATE_DATA); + + if (conn->state.data_input == NULL) + return TRUE; + if (set->max_message_size == 0) + return TRUE; + if (conn->state.data_input->v_offset <= set->max_message_size) + return TRUE; + + smtp_server_cmd_data_size_limit_exceeded(cmd); + return FALSE; +} + bool smtp_server_connection_data_check_state(struct smtp_server_cmd_ctx *cmd) { struct smtp_server_connection *conn = cmd->conn; @@ -213,6 +240,9 @@ static int cmd_data_handle_input(struct smtp_server_cmd_ctx *cmd) i_assert(data_cmd != NULL); + if (!smtp_server_cmd_data_check_size(cmd)) + return -1; + /* continue reading from client */ smtp_server_command_ref(command); i_assert(callbacks != NULL && @@ -220,7 +250,10 @@ static int cmd_data_handle_input(struct smtp_server_cmd_ctx *cmd) ret = callbacks->conn_cmd_data_continue(conn->context, cmd, conn->state.trans); if (ret >= 0) { - if (!i_stream_have_bytes_left(conn->state.data_input)) { + if (!smtp_server_cmd_data_check_size(cmd)) { + smtp_server_command_unref(&command); + return -1; + } else if (!i_stream_have_bytes_left(conn->state.data_input)) { smtp_server_command_debug(cmd, "End of data"); smtp_server_command_input_lock(cmd); @@ -451,16 +484,27 @@ int smtp_server_connection_data_chunk_add(struct smtp_server_cmd_ctx *cmd, bool client_input) { struct smtp_server_connection *conn = cmd->conn; + const struct smtp_server_settings *set = &conn->set; struct smtp_server_command *command = cmd->cmd; struct cmd_data_context *data_cmd = (struct cmd_data_context *)command->data; struct istream *input; + uoff_t new_size; i_assert(data_cmd != NULL); if (!smtp_server_connection_data_check_state(cmd)) return -1; + /* check message size increase early */ + new_size = conn->state.data_size + chunk_size; + if (new_size < conn->state.data_size || + (set->max_message_size > 0 && new_size > set->max_message_size)) { + smtp_server_cmd_data_size_limit_exceeded(cmd); + return -1; + } + conn->state.data_size = new_size; + command->hook_replied = (chunk_last ? cmd_data_replied : cmd_data_chunk_replied); diff --git a/src/lib-smtp/smtp-server-cmd-helo.c b/src/lib-smtp/smtp-server-cmd-helo.c index 2aed40e100..6657dfb047 100644 --- a/src/lib-smtp/smtp-server-cmd-helo.c +++ b/src/lib-smtp/smtp-server-cmd-helo.c @@ -122,6 +122,15 @@ smtp_server_cmd_helo_run(struct smtp_server_cmd_ctx *cmd, const char *params, "ENHANCEDSTATUSCODES"); } smtp_server_reply_ehlo_add(reply, "PIPELINING"); + if ((caps & SMTP_CAPABILITY_SIZE) != 0) { + uoff_t cap_size = conn->set.max_message_size; + if (cap_size > 0 && cap_size != (uoff_t)-1) { + smtp_server_reply_ehlo_add_param(reply, + "SIZE", "%"PRIuUOFF_T, cap_size); + } else { + smtp_server_reply_ehlo_add(reply, "SIZE"); + } + } if ((caps & SMTP_CAPABILITY_STARTTLS) != 0) smtp_server_reply_ehlo_add(reply, "STARTTLS"); smtp_server_reply_ehlo_add(reply, "VRFY"); diff --git a/src/lib-smtp/smtp-server-cmd-mail.c b/src/lib-smtp/smtp-server-cmd-mail.c index a270f5b5df..5c76709fc3 100644 --- a/src/lib-smtp/smtp-server-cmd-mail.c +++ b/src/lib-smtp/smtp-server-cmd-mail.c @@ -151,6 +151,13 @@ void smtp_server_cmd_mail(struct smtp_server_cmd_ctx *cmd, return; } + if ((caps & SMTP_CAPABILITY_SIZE) != 0 && set->max_message_size > 0 && + mail_data->params.size > set->max_message_size) { + smtp_server_reply(cmd, 552, "5.2.3", + "Message size exceeds administrative limit"); + return; + } + mail_data->path = smtp_address_clone(cmd->pool, path); mail_data->timestamp = ioloop_timeval; diff --git a/src/lib-smtp/smtp-server-connection.c b/src/lib-smtp/smtp-server-connection.c index 1c0fdb33d4..e667bf305a 100644 --- a/src/lib-smtp/smtp-server-connection.c +++ b/src/lib-smtp/smtp-server-connection.c @@ -812,6 +812,24 @@ smtp_server_connection_alloc(struct smtp_server *server, smtp_command_limits_merge(&conn->set.command_limits, &set->command_limits); + conn->set.max_message_size = set->max_message_size; + if (set->max_message_size == 0 || + set->max_message_size == (uoff_t)-1) { + conn->set.command_limits.max_data_size = UOFF_T_MAX; + } else if (conn->set.command_limits.max_data_size != 0) { + /* explicit limit given */ + } else if (set->max_message_size > + (UOFF_T_MAX - SMTP_SERVER_DEFAULT_MAX_SIZE_EXCESS_LIMIT)) { + /* very high limit */ + conn->set.command_limits.max_data_size = UOFF_T_MAX; + } else { + /* absolute maximum before connection is closed in DATA + command */ + conn->set.command_limits.max_data_size = + set->max_message_size + + SMTP_SERVER_DEFAULT_MAX_SIZE_EXCESS_LIMIT; + } + if (set->xclient_extensions != NULL) { server->set.xclient_extensions = p_strarray_dup(pool, set->xclient_extensions); diff --git a/src/lib-smtp/smtp-server-private.h b/src/lib-smtp/smtp-server-private.h index dc843aaf41..58972646ca 100644 --- a/src/lib-smtp/smtp-server-private.h +++ b/src/lib-smtp/smtp-server-private.h @@ -5,10 +5,11 @@ #include "smtp-server.h" -#define SMTP_SERVER_COMMAND_POOL_MAX (8 * 1024) +#define SMTP_SERVER_COMMAND_POOL_MAX (8 * 1024) -#define SMTP_SERVER_DEFAULT_MAX_COMMAND_LINE (4 * 1024) -#define SMTP_SERVER_DEFAULT_MAX_BAD_COMMANDS 10 +#define SMTP_SERVER_DEFAULT_MAX_COMMAND_LINE (4 * 1024) +#define SMTP_SERVER_DEFAULT_MAX_BAD_COMMANDS 10 +#define SMTP_SERVER_DEFAULT_MAX_SIZE_EXCESS_LIMIT (1024*1024) #define SMTP_SERVER_DEFAULT_CAPABILITIES \ (SMTP_CAPABILITY_SIZE | SMTP_CAPABILITY_ENHANCEDSTATUSCODES | \ @@ -110,6 +111,7 @@ struct smtp_server_state_data { struct istream *data_input, *data_chain_input; struct istream_chain *data_chain; unsigned int data_chunks; + uoff_t data_size; bool data_failed:1; }; diff --git a/src/lib-smtp/smtp-server.c b/src/lib-smtp/smtp-server.c index 54ada3700f..4cc0fabef8 100644 --- a/src/lib-smtp/smtp-server.c +++ b/src/lib-smtp/smtp-server.c @@ -51,6 +51,7 @@ struct smtp_server *smtp_server_init(const struct smtp_server_settings *set) set->max_bad_commands : SMTP_SERVER_DEFAULT_MAX_BAD_COMMANDS); server->set.max_recipients = set->max_recipients; server->set.command_limits = set->command_limits; + server->set.max_message_size = set->max_message_size; if (set->xclient_extensions != NULL) { server->set.xclient_extensions = diff --git a/src/lib-smtp/smtp-server.h b/src/lib-smtp/smtp-server.h index bc749b06f2..972d5219c5 100644 --- a/src/lib-smtp/smtp-server.h +++ b/src/lib-smtp/smtp-server.h @@ -258,6 +258,9 @@ struct smtp_server_settings { /* command limits */ struct smtp_command_limits command_limits; + /* message size limit */ + uoff_t max_message_size; + /* accept these additional custom XCLIENT fields */ const char *const *xclient_extensions; @@ -454,6 +457,10 @@ void smtp_server_cmd_auth_success(struct smtp_server_cmd_ctx *cmd, const char *username, const char *success_msg) ATTR_NULL(3); +/* DATA */ + +bool smtp_server_cmd_data_check_size(struct smtp_server_cmd_ctx *cmd); + /* * Reply */