From f58e36df4d8e262134dc145bb08757928fa774a9 Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Sat, 30 Jul 2016 11:44:53 +0100 Subject: [PATCH] [Feature] Allow limiting of the inbound message size - Set default limit to 50MB - Reply even in case of HTTP errors --- src/libserver/cfg_file.h | 1 + src/libserver/cfg_rcl.c | 6 ++++++ src/libserver/cfg_utils.c | 2 ++ src/libutil/http.c | 39 +++++++++++++++++++++++++++++++++++---- src/libutil/http.h | 6 ++++++ src/libutil/util.c | 2 ++ src/worker.c | 39 +++++++++++++++++++++++++++++++++++++-- 7 files changed, 89 insertions(+), 6 deletions(-) diff --git a/src/libserver/cfg_file.h b/src/libserver/cfg_file.h index a44c2fc4a..005c3c984 100644 --- a/src/libserver/cfg_file.h +++ b/src/libserver/cfg_file.h @@ -299,6 +299,7 @@ struct rspamd_config { gsize max_cores_size; /**< maximum size occupied by rspamd core files */ gsize max_cores_count; /**< maximum number of core files */ gchar *cores_dir; /**< directory for core files */ + gsize max_message; /**< maximum size for messages */ enum rspamd_log_type log_type; /**< log type */ gint log_facility; /**< log facility in case of syslog */ diff --git a/src/libserver/cfg_rcl.c b/src/libserver/cfg_rcl.c index 64a96bf0f..ad0629f25 100644 --- a/src/libserver/cfg_rcl.c +++ b/src/libserver/cfg_rcl.c @@ -1962,6 +1962,12 @@ rspamd_rcl_config_init (struct rspamd_config *cfg) G_STRUCT_OFFSET (struct rspamd_config, magic_file), 0, "Path to a custom libmagic file"); + rspamd_rcl_add_default_handler (sub, + "max_message", + rspamd_rcl_parse_struct_integer, + G_STRUCT_OFFSET (struct rspamd_config, max_message), + RSPAMD_CL_FLAG_INT_SIZE, + "Maximum size of the message to be scanned"); /* New DNS configuration */ ssub = rspamd_rcl_add_section_doc (&sub->subsections, "dns", NULL, NULL, UCL_OBJECT, FALSE, TRUE, diff --git a/src/libserver/cfg_utils.c b/src/libserver/cfg_utils.c index 81cf06adf..d3bcfc03a 100644 --- a/src/libserver/cfg_utils.c +++ b/src/libserver/cfg_utils.c @@ -37,6 +37,7 @@ #define DEFAULT_MIN_WORD 4 #define DEFAULT_MAX_WORD 40 #define DEFAULT_WORDS_DECAY 200 +#define DEFAULT_MAX_MESSAGE (50 * 1024 * 1024) struct rspamd_ucl_map_cbdata { struct rspamd_config *cfg; @@ -162,6 +163,7 @@ rspamd_config_new (void) cfg->enable_shutdown_workaround = TRUE; cfg->ssl_ciphers = "HIGH:!aNULL:!kRSA:!PSK:!SRP:!MD5:!RC4"; + cfg->max_message = DEFAULT_MAX_MESSAGE; REF_INIT_RETAIN (cfg, rspamd_config_free); diff --git a/src/libutil/http.c b/src/libutil/http.c index dd3302ee3..03d28db77 100644 --- a/src/libutil/http.c +++ b/src/libutil/http.c @@ -39,7 +39,8 @@ struct _rspamd_http_privbuf { enum rspamd_http_priv_flags { RSPAMD_HTTP_CONN_FLAG_ENCRYPTED = 1 << 0, RSPAMD_HTTP_CONN_FLAG_NEW_HEADER = 1 << 1, - RSPAMD_HTTP_CONN_FLAG_RESETED = 1 << 2 + RSPAMD_HTTP_CONN_FLAG_RESETED = 1 << 2, + RSPAMD_HTTP_CONN_FLAG_TOO_LARGE = 1 << 3, }; #define IS_CONN_ENCRYPTED(c) ((c)->flags & RSPAMD_HTTP_CONN_FLAG_ENCRYPTED) @@ -101,6 +102,7 @@ static const rspamd_ftok_t last_modified_header = { .begin = "Last-Modified", .len = 13 }; +static gsize rspamd_http_global_max_size = 0; static void rspamd_http_message_storage_cleanup (struct rspamd_http_message *msg); static gboolean rspamd_http_message_grow_body (struct rspamd_http_message *msg, @@ -615,6 +617,14 @@ rspamd_http_on_headers_complete (http_parser * parser) priv->flags &= ~RSPAMD_HTTP_CONN_FLAG_NEW_HEADER; } + if (conn->type == RSPAMD_HTTP_SERVER && + rspamd_http_global_max_size > 0 && + parser->content_length > rspamd_http_global_max_size) { + /* Too large message */ + priv->flags |= RSPAMD_HTTP_CONN_FLAG_TOO_LARGE; + return -1; + } + if (!rspamd_http_message_set_body (msg, NULL, parser->content_length)) { return -1; } @@ -653,6 +663,14 @@ rspamd_http_on_body (http_parser * parser, const gchar *at, size_t length) pbuf = priv->buf; p = at; + if (conn->type == RSPAMD_HTTP_SERVER && + rspamd_http_global_max_size > 0 && + msg->body_buf.len + length > rspamd_http_global_max_size) { + /* Body length overflow */ + priv->flags |= RSPAMD_HTTP_CONN_FLAG_TOO_LARGE; + return -1; + } + if (!pbuf->zc_buf) { if (!rspamd_http_message_append_body (msg, at, length)) { return -1; @@ -1077,9 +1095,16 @@ rspamd_http_event_handler (int fd, short what, gpointer ud) if (r > 0) { if (http_parser_execute (&priv->parser, &priv->parser_cb, d, r) != (size_t)r || priv->parser.http_errno != 0) { - err = g_error_new (HTTP_ERROR, priv->parser.http_errno, - "HTTP parser error: %s", - http_errno_description (priv->parser.http_errno)); + if (priv->flags & RSPAMD_HTTP_CONN_FLAG_TOO_LARGE) { + err = g_error_new (HTTP_ERROR, 413, + "Request entity too large"); + } + else { + err = g_error_new (HTTP_ERROR, priv->parser.http_errno, + "HTTP parser error: %s", + http_errno_description (priv->parser.http_errno)); + } + conn->error_handler (conn, err); g_error_free (err); @@ -2510,6 +2535,12 @@ rspamd_http_message_storage_cleanup (struct rspamd_http_message *msg) msg->body_buf.len = 0; } +void +rspamd_http_message_set_max_size (gsize sz) +{ + rspamd_http_global_max_size = sz; +} + void rspamd_http_message_free (struct rspamd_http_message *msg) { diff --git a/src/libutil/http.h b/src/libutil/http.h index 2755638db..8223feb17 100644 --- a/src/libutil/http.h +++ b/src/libutil/http.h @@ -385,6 +385,12 @@ gboolean rspamd_http_message_remove_header (struct rspamd_http_message *msg, */ void rspamd_http_message_free (struct rspamd_http_message *msg); +/** + * Sets global maximum size for HTTP message being processed + * @param sz + */ +void rspamd_http_message_set_max_size (gsize sz); + /** * Increase refcount for shared file (if any) to prevent early memory unlinking * @param msg diff --git a/src/libutil/util.c b/src/libutil/util.c index 762c8d557..ca18a7fcc 100644 --- a/src/libutil/util.c +++ b/src/libutil/util.c @@ -2118,6 +2118,8 @@ rspamd_config_libs (struct rspamd_external_libs_ctx *ctx, magic_load (ctx->libmagic, cfg->magic_file); } } + + rspamd_http_message_set_max_size (cfg->max_message); } void diff --git a/src/worker.c b/src/worker.c index a099e8177..98473b003 100644 --- a/src/worker.c +++ b/src/worker.c @@ -34,6 +34,7 @@ #include "libserver/rspamd_control.h" #include "worker_private.h" #include "utlist.h" +#include "libutil/http_private.h" #include "lua/lua_common.h" @@ -218,11 +219,45 @@ static void rspamd_worker_error_handler (struct rspamd_http_connection *conn, GError *err) { struct rspamd_task *task = (struct rspamd_task *) conn->ud; + struct rspamd_http_message *msg; + rspamd_fstring_t *reply; msg_info_task ("abnormally closing connection from: %s, error: %e", rspamd_inet_address_to_string (task->client_addr), err); - /* Terminate session immediately */ - rspamd_session_destroy (task->s); + if (task->processed_stages & RSPAMD_TASK_STAGE_REPLIED) { + /* Terminate session immediately */ + rspamd_session_destroy (task->s); + } + else { + task->processed_stages |= RSPAMD_TASK_STAGE_REPLIED; + msg = rspamd_http_new_message (HTTP_RESPONSE); + + if (err) { + msg->status = rspamd_fstring_new_init (err->message, + strlen (err->message)); + msg->code = err->code; + } + else { + msg->status = rspamd_fstring_new_init ("Internal error", + strlen ("Internal error")); + msg->code = 500; + } + + msg->date = time (NULL); + + reply = rspamd_fstring_sized_new (msg->status->len + 16); + rspamd_printf_fstring (&reply, "{\"error\":\"%V\"}", msg->status); + rspamd_http_message_set_body_from_fstring_steal (msg, reply); + rspamd_http_connection_reset (task->http_conn); + rspamd_http_connection_write_message (task->http_conn, + msg, + NULL, + "application/json", + task, + task->http_conn->fd, + &task->tv, + task->ev_base); + } } static gint -- 2.39.5