From da1b50cb159a91092a6e6b689e4ea34197ce72f2 Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Tue, 29 May 2012 22:08:54 +0400 Subject: [PATCH] * Add support of strict_domains. Several fixes in dkim code. Make initial support of body relaxed canonization. --- src/dkim.c | 98 +++++++++++++++++++++++++++++++++------- src/html.c | 2 +- src/plugins/dkim_check.c | 35 +++++++++++--- 3 files changed, 111 insertions(+), 24 deletions(-) diff --git a/src/dkim.c b/src/dkim.c index 20a52b0c6..87959e6f4 100644 --- a/src/dkim.c +++ b/src/dkim.c @@ -648,7 +648,7 @@ rspamd_dkim_parse_key (const gchar *txt, gsize *keylen, GError **err) case 1: /* State when we got p= and looking for some public key */ if ((*p == ';' || p == end) && p > c) { - len = (p == end) ? p - c : p - c - 1; + len = p - c; return rspamd_dkim_make_key (c, len, err); } else { @@ -692,6 +692,7 @@ rspamd_dkim_dns_cb (struct rspamd_dns_reply *reply, gpointer arg) if (key) { break; } + cur = g_list_next (cur); } if (key != NULL && err != NULL) { /* Free error as it is insignificant */ @@ -726,31 +727,89 @@ rspamd_get_dkim_key (rspamd_dkim_context_t *ctx, struct rspamd_dns_resolver *res return make_dns_request (resolver, s, ctx->pool, rspamd_dkim_dns_cb, cbdata, DNS_REQUEST_TXT, ctx->dns_key); } +static gboolean +rspamd_dkim_relaxed_body_step (GChecksum *ck, const gchar **start, guint remain) +{ + const gchar *h; + static gchar buf[BUFSIZ]; + gchar *t; + guint len, inlen; + gboolean got_sp, finished = FALSE; + + if (remain > sizeof (buf)) { + len = sizeof (buf); + } + else { + len = remain; + finished = TRUE; + } + len = MIN (sizeof (buf), remain); + inlen = len; + h = *start; + t = &buf[0]; + got_sp = FALSE; + + while (len && inlen) { + if ((*h == '\r' || *h == '\n') && got_sp) { + /* Ignore spaces at the end of line */ + got_sp = FALSE; + t --; + len --; + } + else if (g_ascii_isspace (*h)) { + if (got_sp) { + /* Ignore multiply spaces */ + h ++; + inlen --; + continue; + } + else { + got_sp = TRUE; + } + } + else { + got_sp = FALSE; + } + *t++ = *h++; + inlen --; + len --; + } + + *start = h; + + g_checksum_update (ck, buf, t - buf); + + return finished; +} + static gboolean rspamd_dkim_canonize_body (rspamd_dkim_context_t *ctx, const gchar *start, const gchar *end) { - if (ctx->body_canon_type == DKIM_CANON_SIMPLE) { - /* Perform simple canonization */ - if (start == NULL) { + if (start == NULL) { + /* Empty body */ + g_checksum_update (ctx->body_hash, CRLF, sizeof (CRLF) - 2); + } + else { + end --; + while (end > start + 2) { + if (*end == '\n' && *(end - 1) == '\r' && *(end - 2) == '\n') { + end -= 2; + } + else { + break; + } + } + if (end == start || end == start + 2) { /* Empty body */ g_checksum_update (ctx->body_hash, CRLF, sizeof (CRLF) - 2); } else { - end --; - while (end > start + 2) { - if (*end == '\n' && *(end - 1) == '\r' && *(end - 2) == '\n') { - end -= 2; - } - else { - break; - } - } - if (end == start || end == start + 2) { - /* Empty body */ - g_checksum_update (ctx->body_hash, CRLF, sizeof (CRLF) - 2); + if (ctx->body_canon_type == DKIM_CANON_SIMPLE) { + /* Simple canonization */ + g_checksum_update (ctx->body_hash, start, end - start + 1); } else { - g_checksum_update (ctx->body_hash, start, end - start + 1); + while (rspamd_dkim_relaxed_body_step (ctx->body_hash, &start, end - start + 1)); } } return TRUE; @@ -826,6 +885,10 @@ rspamd_dkim_canonize_header_simple (rspamd_dkim_context_t *ctx, const gchar *hea /* Get value */ state = 2; } + else { + /* Skip the whole header */ + state = 1; + } } else { /* Skip the whole header */ @@ -981,6 +1044,7 @@ rspamd_dkim_check (rspamd_dkim_context_t *ctx, rspamd_dkim_key_t *key, struct wo cur = ctx->hlist; while (cur) { if (!rspamd_dkim_canonize_header (ctx, task, cur->data, FALSE)) { + msg_debug ("cannot find header %s for canonization", cur->data); return DKIM_RECORD_ERROR; } cur = g_list_next (cur); diff --git a/src/html.c b/src/html.c index dac09bf0e..41dd1df7d 100644 --- a/src/html.c +++ b/src/html.c @@ -900,7 +900,7 @@ add_html_node (struct worker_task *task, memory_pool_t * pool, struct mime_text_ else { new = construct_html_node (pool, tag_text, tag_len); if (new == NULL) { - debug_task ("cannot construct HTML node for text '%s'", tag_text); + debug_task ("cannot construct HTML node for text '%*s'", tag_len, tag_text); return FALSE; } data = new->data; diff --git a/src/plugins/dkim_check.c b/src/plugins/dkim_check.c index db93b3da1..338c81aae 100644 --- a/src/plugins/dkim_check.c +++ b/src/plugins/dkim_check.c @@ -22,14 +22,17 @@ */ -/***MODULE:spf - * rspamd module that checks spf records of incoming email +/***MODULE:dkim + * rspamd module that checks dkim records of incoming email * * Allowed options: - * - symbol_allow (string): symbol to insert (default: 'R_SPF_ALLOW') - * - symbol_fail (string): symbol to insert (default: 'R_SPF_FAIL') - * - symbol_softfail (string): symbol to insert (default: 'R_SPF_SOFTFAIL') + * - symbol_allow (string): symbol to insert in case of allow (default: 'R_DKIM_ALLOW') + * - symbol_reject (string): symbol to insert (default: 'R_DKIM_REJECT') + * - symbol_rempfail (string): symbol to insert in case of temporary fail (default: 'R_DKIM_TEMPFAIL') * - whitelist (map): map of whitelisted networks + * - domains (map): map of domains to check (if absent all domains are checked) + * - strict_domains (map): map of domains that requires strict score for dkim + * - strict_multiplier (number): multiplier for strict domains */ #include "config.h" @@ -59,6 +62,8 @@ struct dkim_ctx { memory_pool_t *dkim_pool; radix_tree_t *whitelist_ip; GHashTable *dkim_domains; + GHashTable *strict_domains; + guint strict_multiplier; rspamd_lru_hash_t *dkim_hash; }; @@ -93,6 +98,8 @@ dkim_module_init (struct config_file *cfg, struct module_ctx **ctx) register_module_opt ("dkim", "dkim_cache_expire", MODULE_OPT_TYPE_TIME); register_module_opt ("dkim", "whitelist", MODULE_OPT_TYPE_MAP); register_module_opt ("dkim", "domains", MODULE_OPT_TYPE_MAP); + register_module_opt ("dkim", "strict_domains", MODULE_OPT_TYPE_MAP); + register_module_opt ("dkim", "strict_multiplier", MODULE_OPT_TYPE_UINT); return 0; } @@ -146,6 +153,11 @@ dkim_module_config (struct config_file *cfg) msg_warn ("cannot load domains list from %s", value); } } + if ((value = get_module_opt (cfg, "dkim", "strict_domains")) != NULL) { + if (! add_map (value, read_host_list, fin_host_list, (void **)&dkim_module_ctx->strict_domains)) { + msg_warn ("cannot load strict domains list from %s", value); + } + } register_symbol (&cfg->cache, dkim_module_ctx->symbol_reject, 1, dkim_symbol_callback, NULL); register_virtual_symbol (&cfg->cache, dkim_module_ctx->symbol_tempfail, 1); @@ -173,11 +185,18 @@ dkim_module_reconfig (struct config_file *cfg) static void dkim_module_check (struct worker_task *task, rspamd_dkim_context_t *ctx, rspamd_dkim_key_t *key) { - gint res; + gint res, score = 1; msg_debug ("check dkim signature for %s domain", ctx->dns_key); res = rspamd_dkim_check (ctx, key, task); + if (dkim_module_ctx->strict_domains != NULL && dkim_module_ctx->strict_multiplier > 0) { + /* Perform strict check */ + if (g_hash_table_lookup (dkim_module_ctx->strict_domains, ctx->dns_key) != NULL) { + score *= dkim_module_ctx->strict_multiplier; + } + } + if (res == DKIM_REJECT) { insert_result (task, dkim_module_ctx->symbol_reject, 1, NULL); } @@ -202,6 +221,7 @@ dkim_module_key_handler (rspamd_dkim_key_t *key, gsize keylen, rspamd_dkim_conte } else { /* Insert tempfail symbol */ + msg_info ("cannot get key for domain %s", ctx->dns_key); insert_result (task, dkim_module_ctx->symbol_tempfail, 1, NULL); } } @@ -236,9 +256,12 @@ dkim_symbol_callback (struct worker_task *task, void *unused) /* Get key */ key = rspamd_lru_hash_lookup (dkim_module_ctx->dkim_hash, ctx->dns_key, task->tv.tv_sec); if (key != NULL) { + debug_task ("found key for %s in cache", ctx->dns_key); dkim_module_check (task, ctx, key); } else { + debug_task ("request key for %s from DNS", ctx->dns_key); + task->dns_requests ++; rspamd_get_dkim_key (ctx, task->resolver, task->s, dkim_module_key_handler, task); } } -- 2.39.5