aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVsevolod Stakhov <vsevolod@highsecure.ru>2016-07-30 11:44:53 +0100
committerVsevolod Stakhov <vsevolod@highsecure.ru>2016-07-30 11:44:53 +0100
commitf58e36df4d8e262134dc145bb08757928fa774a9 (patch)
tree09457a0819aaa5072e7d4ca50fe9783a3600e999
parent7d8e0702811eeaf4ca86c17b3c378be37c43b7eb (diff)
downloadrspamd-f58e36df4d8e262134dc145bb08757928fa774a9.tar.gz
rspamd-f58e36df4d8e262134dc145bb08757928fa774a9.zip
[Feature] Allow limiting of the inbound message size
- Set default limit to 50MB - Reply even in case of HTTP errors
-rw-r--r--src/libserver/cfg_file.h1
-rw-r--r--src/libserver/cfg_rcl.c6
-rw-r--r--src/libserver/cfg_utils.c2
-rw-r--r--src/libutil/http.c39
-rw-r--r--src/libutil/http.h6
-rw-r--r--src/libutil/util.c2
-rw-r--r--src/worker.c39
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);
@@ -2511,6 +2536,12 @@ rspamd_http_message_storage_cleanup (struct rspamd_http_message *msg)
}
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)
{
struct rspamd_http_header *hdr, *htmp;
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
@@ -386,6 +386,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