From 61e347959b684ac1682f09940080355586c106f5 Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Mon, 12 Mar 2018 12:16:04 +0000 Subject: [Fix] Deal with deeply nested messages more aggressively --- src/libmime/message.c | 18 +++++++++- src/libmime/mime_parser.c | 86 ++++++++++++++++++++++++++++------------------- src/libmime/mime_parser.h | 9 ++++- 3 files changed, 76 insertions(+), 37 deletions(-) diff --git a/src/libmime/message.c b/src/libmime/message.c index c92a1d26e..5e1118fc2 100644 --- a/src/libmime/message.c +++ b/src/libmime/message.c @@ -945,10 +945,14 @@ rspamd_message_parse (struct rspamd_task *task) rspamd_cryptobox_hash_init (&st, NULL, 0); if (task->flags & RSPAMD_TASK_FLAG_MIME) { + enum rspamd_mime_parse_error ret; debug_task ("construct mime parser from string length %d", (gint) task->msg.len); - if (!rspamd_mime_parse_task (task, &err)) { + ret = rspamd_mime_parse_task (task, &err); + + switch (ret) { + case RSPAMD_MIME_PARSE_FATAL: msg_err_task ("cannot construct mime from stream: %e", err); if (task->cfg && (!task->cfg->allow_raw_input)) { @@ -963,6 +967,18 @@ rspamd_message_parse (struct rspamd_task *task) task->flags &= ~RSPAMD_TASK_FLAG_MIME; rspamd_message_from_data (task, p, len); } + break; + case RSPAMD_MIME_PARSE_NESTING: + msg_warn_task ("cannot construct full mime from stream: %e", err); + task->flags |= RSPAMD_TASK_FLAG_BROKEN_HEADERS; + break; + case RSPAMD_MIME_PARSE_OK: + default: + break; + } + + if (err) { + g_error_free (err); } } else { diff --git a/src/libmime/mime_parser.c b/src/libmime/mime_parser.c index 8bcee0c95..d60a3fea2 100644 --- a/src/libmime/mime_parser.c +++ b/src/libmime/mime_parser.c @@ -26,9 +26,11 @@ struct rspamd_mime_parser_lib_ctx { struct rspamd_multipattern *mp_boundary; guchar hkey[rspamd_cryptobox_SIPKEYBYTES]; /* Key for hashing */ guint key_usages; -} *lib_ctx = NULL; +}; + +struct rspamd_mime_parser_lib_ctx *lib_ctx = NULL; -static const guint max_nested = 32; +static const guint max_nested = 64; static const guint max_key_usages = 10000; #define msg_debug_mime(...) rspamd_conditional_debug_fast (NULL, task->from_addr, \ @@ -56,19 +58,20 @@ struct rspamd_mime_parser_ctx { const gchar *pos; const gchar *end; struct rspamd_task *task; + guint nesting; }; -static gboolean +static enum rspamd_mime_parse_error rspamd_mime_parse_multipart_part (struct rspamd_task *task, struct rspamd_mime_part *part, struct rspamd_mime_parser_ctx *st, GError **err); -static gboolean +static enum rspamd_mime_parse_error rspamd_mime_parse_message (struct rspamd_task *task, struct rspamd_mime_part *part, struct rspamd_mime_parser_ctx *st, GError **err); -static gboolean +static enum rspamd_mime_parse_error rspamd_mime_parse_normal_part (struct rspamd_task *task, struct rspamd_mime_part *part, struct rspamd_mime_parser_ctx *st, @@ -424,7 +427,7 @@ rspamd_mime_parser_calc_digest (struct rspamd_mime_part *part) } } -static gboolean +static enum rspamd_mime_parse_error rspamd_mime_parse_normal_part (struct rspamd_task *task, struct rspamd_mime_part *part, struct rspamd_mime_parser_ctx *st, @@ -513,7 +516,7 @@ rspamd_mime_parse_normal_part (struct rspamd_task *task, part->raw_data.len, rspamd_cte_to_string (part->cte)); rspamd_mime_parser_calc_digest (part); - return TRUE; + return RSPAMD_MIME_PARSE_OK; } struct rspamd_mime_multipart_cbdata { @@ -526,7 +529,7 @@ struct rspamd_mime_multipart_cbdata { GError **err; }; -static gboolean +static enum rspamd_mime_parse_error rspamd_mime_process_multipart_node (struct rspamd_task *task, struct rspamd_mime_parser_ctx *st, struct rspamd_mime_part *multipart, @@ -540,7 +543,7 @@ rspamd_mime_process_multipart_node (struct rspamd_task *task, GString str; goffset hdr_pos, body_pos; guint i; - gboolean ret = FALSE; + enum rspamd_mime_parse_error ret = RSPAMD_MIME_PARSE_FATAL; str.str = (gchar *)start; @@ -626,13 +629,16 @@ rspamd_mime_process_multipart_node (struct rspamd_task *task, npart->ct = sel; if (sel->flags & RSPAMD_CONTENT_TYPE_MULTIPART) { + st->nesting ++; g_ptr_array_add (st->stack, npart); ret = rspamd_mime_parse_multipart_part (task, npart, st, err); } else if (sel->flags & RSPAMD_CONTENT_TYPE_MESSAGE) { + st->nesting ++; g_ptr_array_add (st->stack, npart); - if ((ret = rspamd_mime_parse_normal_part (task, npart, st, err))) { + if ((ret = rspamd_mime_parse_normal_part (task, npart, st, err)) + == RSPAMD_MIME_PARSE_OK) { ret = rspamd_mime_parse_message (task, npart, st, err); } } @@ -643,7 +649,7 @@ rspamd_mime_process_multipart_node (struct rspamd_task *task, return ret; } -static gboolean +static enum rspamd_mime_parse_error rspamd_mime_parse_multipart_cb (struct rspamd_task *task, struct rspamd_mime_part *multipart, struct rspamd_mime_parser_ctx *st, @@ -651,6 +657,7 @@ rspamd_mime_parse_multipart_cb (struct rspamd_task *task, struct rspamd_mime_boundary *b) { const gchar *pos = st->start + b->boundary; + enum rspamd_mime_parse_error ret; task = cb->task; @@ -666,9 +673,10 @@ rspamd_mime_parse_multipart_cb (struct rspamd_task *task, g_assert (cb->cur_boundary != NULL); - if (!rspamd_mime_process_multipart_node (task, cb->st, - cb->multipart, cb->part_start, pos, cb->err)) { - return FALSE; + if ((ret = rspamd_mime_process_multipart_node (task, cb->st, + cb->multipart, cb->part_start, pos, cb->err)) + != RSPAMD_MIME_PARSE_OK) { + return ret; } /* Go towards the next part */ @@ -680,10 +688,10 @@ rspamd_mime_parse_multipart_cb (struct rspamd_task *task, } } - return TRUE; + return RSPAMD_MIME_PARSE_OK; } -static gint +static enum rspamd_mime_parse_error rspamd_multipart_boundaries_filter (struct rspamd_task *task, struct rspamd_mime_part *multipart, struct rspamd_mime_parser_ctx *st, @@ -692,6 +700,7 @@ rspamd_multipart_boundaries_filter (struct rspamd_task *task, struct rspamd_mime_boundary *cur; goffset last_offset; guint i, sel = 0; + enum rspamd_mime_parse_error ret; last_offset = (multipart->raw_data.begin - st->start) + multipart->raw_data.len; @@ -740,9 +749,9 @@ rspamd_multipart_boundaries_filter (struct rspamd_task *task, } if (cur->hash == cb->bhash || cur->closed_hash == cb->bhash) { - if (!rspamd_mime_parse_multipart_cb (task, multipart, st, - cb, cur)) { - return FALSE; + if ((ret = rspamd_mime_parse_multipart_cb (task, multipart, st, + cb, cur)) != RSPAMD_MIME_PARSE_OK) { + return ret; } if (cur->closed_hash == cb->bhash) { @@ -779,31 +788,32 @@ rspamd_multipart_boundaries_filter (struct rspamd_task *task, fb.boundary = last_offset; - if (!rspamd_mime_parse_multipart_cb (task, multipart, st, - cb, &fb)) { - return FALSE; + if ((ret = rspamd_mime_parse_multipart_cb (task, multipart, st, + cb, &fb)) != RSPAMD_MIME_PARSE_OK) { + return ret; } } - return TRUE; + return RSPAMD_MIME_PARSE_OK; } -static gboolean +static enum rspamd_mime_parse_error rspamd_mime_parse_multipart_part (struct rspamd_task *task, struct rspamd_mime_part *part, struct rspamd_mime_parser_ctx *st, GError **err) { struct rspamd_mime_multipart_cbdata cbdata; - gboolean ret; + enum rspamd_mime_parse_error ret; - if (st->stack->len > max_nested) { + if (st->nesting > max_nested) { g_set_error (err, RSPAMD_MIME_QUARK, E2BIG, "Nesting level is too high: %d", - st->stack->len); - return FALSE; + st->nesting); + return RSPAMD_MIME_PARSE_NESTING; } g_ptr_array_add (task->parts, part); + st->nesting ++; rspamd_mime_part_get_cte (task, part->raw_headers, part, FALSE); st->pos = part->raw_data.begin; @@ -829,6 +839,7 @@ rspamd_mime_parse_multipart_part (struct rspamd_task *task, ret = rspamd_multipart_boundaries_filter (task, part, st, &cbdata); /* Cleanup stack */ + st->nesting --; g_ptr_array_remove_index_fast (st->stack, st->stack->len - 1); return ret; @@ -1025,7 +1036,7 @@ rspamd_mime_parse_stack_free (struct rspamd_mime_parser_ctx *st) } } -static gboolean +static enum rspamd_mime_parse_error rspamd_mime_parse_message (struct rspamd_task *task, struct rspamd_mime_part *part, struct rspamd_mime_parser_ctx *st, @@ -1039,14 +1050,14 @@ rspamd_mime_parse_message (struct rspamd_task *task, struct rspamd_mime_part *npart; goffset hdr_pos, body_pos; guint i; - gboolean ret = FALSE; + enum rspamd_mime_parse_error ret = RSPAMD_MIME_PARSE_OK; GString str; struct rspamd_mime_parser_ctx *nst = st; - if (st->stack->len > max_nested) { + if (st->nesting > max_nested) { g_set_error (err, RSPAMD_MIME_QUARK, E2BIG, "Nesting level is too high: %d", - st->stack->len); - return FALSE; + st->nesting); + return RSPAMD_MIME_PARSE_NESTING; } /* Allocate real part */ @@ -1160,6 +1171,8 @@ rspamd_mime_parse_message (struct rspamd_task *task, nst->end = nst->start + part->parsed_data.len; nst->pos = nst->start; nst->task = st->task; + nst->nesting = st->nesting; + st->nesting ++; str.str = (gchar *)part->parsed_data.begin; str.len = part->parsed_data.len; @@ -1235,10 +1248,12 @@ rspamd_mime_parse_message (struct rspamd_task *task, if (sel->flags & RSPAMD_CONTENT_TYPE_MULTIPART) { g_ptr_array_add (nst->stack, npart); + nst->nesting ++; ret = rspamd_mime_parse_multipart_part (task, npart, nst, err); } else if (sel->flags & RSPAMD_CONTENT_TYPE_MESSAGE) { g_ptr_array_add (nst->stack, npart); + nst->nesting ++; ret = rspamd_mime_parse_message (task, npart, nst, err); } else { @@ -1248,6 +1263,7 @@ rspamd_mime_parse_message (struct rspamd_task *task, if (part) { /* Remove message part from the parent stack */ g_ptr_array_remove_index_fast (st->stack, st->stack->len - 1); + st->nesting --; } if (nst != st) { @@ -1257,11 +1273,11 @@ rspamd_mime_parse_message (struct rspamd_task *task, return ret; } -gboolean +enum rspamd_mime_parse_error rspamd_mime_parse_task (struct rspamd_task *task, GError **err) { struct rspamd_mime_parser_ctx *st; - gboolean ret; + enum rspamd_mime_parse_error ret = RSPAMD_MIME_PARSE_OK; if (lib_ctx == NULL) { rspamd_mime_parser_init_lib (); diff --git a/src/libmime/mime_parser.h b/src/libmime/mime_parser.h index f1355d72e..c0b7dec7e 100644 --- a/src/libmime/mime_parser.h +++ b/src/libmime/mime_parser.h @@ -20,6 +20,13 @@ struct rspamd_task; -gboolean rspamd_mime_parse_task (struct rspamd_task *task, GError **err); +enum rspamd_mime_parse_error { + RSPAMD_MIME_PARSE_OK = 0, + RSPAMD_MIME_PARSE_FATAL, + RSPAMD_MIME_PARSE_NESTING, +}; + +enum rspamd_mime_parse_error rspamd_mime_parse_task (struct rspamd_task *task, + GError **err); #endif /* SRC_LIBMIME_MIME_PARSER_H_ */ -- cgit v1.2.3