]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-smtp: server: Implement enforcement of maximum message size.
authorStephan Bosch <stephan.bosch@dovecot.fi>
Fri, 6 Apr 2018 23:05:15 +0000 (01:05 +0200)
committerVille Savolainen <ville.savolainen@dovecot.fi>
Mon, 28 May 2018 06:16:13 +0000 (09:16 +0300)
src/lib-smtp/smtp-server-cmd-data.c
src/lib-smtp/smtp-server-cmd-helo.c
src/lib-smtp/smtp-server-cmd-mail.c
src/lib-smtp/smtp-server-connection.c
src/lib-smtp/smtp-server-private.h
src/lib-smtp/smtp-server.c
src/lib-smtp/smtp-server.h

index 674416609cffa286b798478df8592d553085d605..122aa5a2d939be71324ea5029f28de007084a5ab 100644 (file)
@@ -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);
 
index 2aed40e100bd795150098bf7c11639e6bf73832e..6657dfb047e6629622d7e1711c162fefbb8ee395 100644 (file)
@@ -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");
index a270f5b5dfe1d34ae33adb2f077ac2046c617581..5c76709fc35cfaa71038a7d33a44e283c586adf0 100644 (file)
@@ -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;
 
index 1c0fdb33d4c497c1cbf3e7a1d1fffdce51b0f6e2..e667bf305a6fd71b19e0ccf44d1f32962b94ec00 100644 (file)
@@ -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);
index dc843aaf417df92de5c67cab7cb05c239e0379f2..58972646ca57b257d7a3f80d6357a3428d30ddab 100644 (file)
@@ -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;
 };
index 54ada3700f1364f0f84c8aeec5b92a47dc570cee..4cc0fabef83b2656fdaba22f156acc13101af787 100644 (file)
@@ -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 =
index bc749b06f2176e954633d1e85f9ee424e15cbbab..972d5219c59955b01bd953991fbd0d59e08da9ff 100644 (file)
@@ -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
  */