diff options
-rw-r--r-- | rules/regexp/headers.lua | 44 | ||||
-rw-r--r-- | src/libmime/content_type.c | 6 | ||||
-rw-r--r-- | src/libmime/content_type.h | 3 | ||||
-rw-r--r-- | src/libmime/mime_parser.c | 81 |
4 files changed, 107 insertions, 27 deletions
diff --git a/rules/regexp/headers.lua b/rules/regexp/headers.lua index c47205fea..516fc1232 100644 --- a/rules/regexp/headers.lua +++ b/rules/regexp/headers.lua @@ -153,15 +153,6 @@ reconf['R_MISSING_CHARSET'] = { mime_only = true, } --- Subject seems to be spam -reconf['R_SAJDING'] = { - re = 'Subject=/\\bsajding(?:om|a)?\\b/iH', - score = 8.0, - description = 'Subject seems to be spam', - group = 'headers', - mime_only = true, -} - -- Find forged Outlook MUA -- Yahoo groups messages local yahoo_bulk = 'Received=/from \\[\\S+\\] by \\S+\\.(?:groups|scd|dcn)\\.yahoo\\.com with NNFMP/H' @@ -436,20 +427,9 @@ reconf['FORGED_MUA_KMAIL_MSGID_UNKNOWN'] = { local opera1x_mua = 'User-Agent=/^\\s*Opera Mail\\/1[01]\\.\\d+ /H' -- Opera Mail Message-ID template local opera1x_msgid = 'Message-ID=/^<?op\\.[a-z\\d]{14}\\@\\S+>?$/H' --- Suspicious Opera Mail User-Agent header -local suspicious_opera10w_mua = 'User-Agent=/^\\s*Opera Mail\\/10\\.\\d+ \\(Windows\\)$/H' --- Suspicious Opera Mail Message-ID, apparently from KMail -local suspicious_opera10w_msgid = 'Message-Id=/^<?2009\\d{8}\\.\\d+\\.\\S+\\@\\S+?>$/H' --- Summary rule for forged Opera Mail User-Agent header and Message-ID header from KMail -reconf['SUSPICIOUS_OPERA_10W_MSGID'] = { - re = string.format('(%s) & (%s)', suspicious_opera10w_mua, suspicious_opera10w_msgid), - score = 4.0, - description = 'Message pretends to be send from suspicious Opera Mail/10.x (Windows) but has forged Message-ID, apparently from KMail', - group = 'mua' -} --- Summary rule for forged Opera Mail Message-ID header +-- Rule for forged Opera Mail Message-ID header reconf['FORGED_MUA_OPERA_MSGID'] = { - re = string.format('(%s) & !(%s) & !(%s) & !(%s)', opera1x_mua, opera1x_msgid, reconf['SUSPICIOUS_OPERA_10W_MSGID']['re'], unusable_msgid), + re = string.format('(%s) & !(%s) & !(%s)', opera1x_mua, opera1x_msgid, unusable_msgid), score = 4.0, description = 'Message pretends to be send from Opera Mail but has forged Message-ID', group = 'mua' @@ -993,3 +973,23 @@ reconf['OLD_X_MAILER'] = { score = 2.0, group = 'headers', } + +-- X-Mailer header values which should not occur (in the modern mail) at all +local bad_x_mailers = { + -- header name repeated in the header value + [[X-Mailer: ]], + -- Mozilla Thunderbird uses User-Agnet header, not X-Mailer + -- Early Thunderbird had U-A like: + -- Mozilla Thunderbird 1.0.2 (Windows/20050317) + -- Thunderbird 2.0.0.23 (X11/20090812) + [[(?:Mozilla )?Thunderbird \d]], + -- Was used by Yahoo Groups in 2000s + [[eGroups Message Poster]], +} + +reconf['FORGED_X_MAILER'] = { + description = 'Forged X-Mailer header', + re = string.format('X-Mailer=/^(?:%s)/', table.concat(bad_x_mailers, '|')), + score = 4.0, + group = 'headers', +} diff --git a/src/libmime/content_type.c b/src/libmime/content_type.c index 0c06f51fb..7b9c213ca 100644 --- a/src/libmime/content_type.c +++ b/src/libmime/content_type.c @@ -706,6 +706,12 @@ rspamd_content_type_parse (const gchar *in, res->flags |= RSPAMD_CONTENT_TYPE_BROKEN; RSPAMD_FTOK_ASSIGN (&res->subtype, "alternative"); } + + /* PKCS7 smime */ + RSPAMD_FTOK_ASSIGN (&srch, "x-pkcs7-mime"); + if (rspamd_ftok_casecmp (&res->subtype, &srch) == 0) { + res->flags |= RSPAMD_CONTENT_TYPE_SMIME; + } } RSPAMD_FTOK_ASSIGN (&srch, "multipart"); diff --git a/src/libmime/content_type.h b/src/libmime/content_type.h index 3991e6785..6bc7262e7 100644 --- a/src/libmime/content_type.h +++ b/src/libmime/content_type.h @@ -32,7 +32,8 @@ enum rspamd_content_type_flags { RSPAMD_CONTENT_TYPE_MESSAGE = 1 << 3, RSPAMD_CONTENT_TYPE_DSN = 1 << 4, RSPAMD_CONTENT_TYPE_MISSING = 1 << 5, - RSPAMD_CONTENT_TYPE_ENCRYPTED = 1 << 1, + RSPAMD_CONTENT_TYPE_ENCRYPTED = 1 << 6, + RSPAMD_CONTENT_TYPE_SMIME = 1 << 7, }; enum rspamd_content_param_flags { diff --git a/src/libmime/mime_parser.c b/src/libmime/mime_parser.c index 213eb99d2..207bd4462 100644 --- a/src/libmime/mime_parser.c +++ b/src/libmime/mime_parser.c @@ -23,6 +23,8 @@ #include "multipattern.h" #include "contrib/libottery/ottery.h" #include "contrib/uthash/utlist.h" +#include <openssl/cms.h> +#include <openssl/pkcs7.h> struct rspamd_mime_parser_lib_ctx { struct rspamd_multipattern *mp_boundary; @@ -77,8 +79,17 @@ 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, + struct rspamd_content_type *ct, GError **err); +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, + const gchar *start, const gchar *end, + gboolean is_finished, + GError **err); + #define RSPAMD_MIME_QUARK (rspamd_mime_parser_quark()) static GQuark @@ -575,6 +586,7 @@ 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, + struct rspamd_content_type *ct, GError **err) { rspamd_fstring_t *parsed; @@ -690,6 +702,67 @@ 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); + if (ct && (ct->flags & RSPAMD_CONTENT_TYPE_SMIME)) { + CMS_ContentInfo *cms; + const unsigned char *der_beg = part->parsed_data.begin; + cms = d2i_CMS_ContentInfo (NULL, &der_beg, part->parsed_data.len); + + if (cms) { + const ASN1_OBJECT *asn_ct = CMS_get0_eContentType (cms); + int ct_nid = OBJ_obj2nid (asn_ct); + + if (ct_nid == NID_pkcs7_data) { + BIO *bio = BIO_new_mem_buf (part->parsed_data.begin, + part->parsed_data.len); + + PKCS7 *p7; + p7 = d2i_PKCS7_bio (bio, NULL); + + if (p7) { + ct_nid = OBJ_obj2nid (p7->type); + + if (ct_nid == NID_pkcs7_signed) { + PKCS7 *p7_signed_content = p7->d.sign->contents; + + ct_nid = OBJ_obj2nid (p7_signed_content->type); + + if (ct_nid == NID_pkcs7_data) { + int ret; + + msg_debug_mime ("found an additional part inside of " + "smime structure of type %T/%T; length=%d", + &ct->type, &ct->subtype, p7_signed_content->d.data->length); + /* + * Since ASN.1 structures are freed, we need to copy + * the content + */ + gchar *cpy = rspamd_mempool_alloc (task->task_pool, + p7_signed_content->d.data->length); + memcpy (cpy, p7_signed_content->d.data->data, + p7_signed_content->d.data->length); + ret = rspamd_mime_process_multipart_node (task, + st, NULL, + cpy,cpy + p7_signed_content->d.data->length, + TRUE, err); + + PKCS7_free (p7); + BIO_free (bio); + CMS_ContentInfo_free (cms); + + return ret; + } + } + + PKCS7_free (p7); + } + + BIO_free (bio); + } + + CMS_ContentInfo_free (cms); + } + } + return RSPAMD_MIME_PARSE_OK; } @@ -839,13 +912,13 @@ rspamd_mime_process_multipart_node (struct rspamd_task *task, g_ptr_array_add (st->stack, npart); npart->part_type = RSPAMD_MIME_PART_MESSAGE; - if ((ret = rspamd_mime_parse_normal_part (task, npart, st, err)) + if ((ret = rspamd_mime_parse_normal_part (task, npart, st, sel, err)) == RSPAMD_MIME_PARSE_OK) { ret = rspamd_mime_parse_message (task, npart, st, err); } } else { - ret = rspamd_mime_parse_normal_part (task, npart, st, err); + ret = rspamd_mime_parse_normal_part (task, npart, st, sel, err); } return ret; @@ -1478,14 +1551,14 @@ rspamd_mime_parse_message (struct rspamd_task *task, ret = rspamd_mime_parse_multipart_part (task, npart, nst, err); } else if (sel->flags & RSPAMD_CONTENT_TYPE_MESSAGE) { - if ((ret = rspamd_mime_parse_normal_part (task, npart, nst, err)) + if ((ret = rspamd_mime_parse_normal_part (task, npart, nst, sel, err)) == RSPAMD_MIME_PARSE_OK) { npart->part_type = RSPAMD_MIME_PART_MESSAGE; ret = rspamd_mime_parse_message (task, npart, nst, err); } } else { - ret = rspamd_mime_parse_normal_part (task, npart, nst, err); + ret = rspamd_mime_parse_normal_part (task, npart, nst, sel, err); } if (ret != RSPAMD_MIME_PARSE_OK) { |