diff options
author | Vsevolod Stakhov <vsevolod@highsecure.ru> | 2015-05-11 12:54:07 +0100 |
---|---|---|
committer | Vsevolod Stakhov <vsevolod@highsecure.ru> | 2015-05-11 12:54:07 +0100 |
commit | 271c03bba943c27214db950ff5fa981397483932 (patch) | |
tree | cd053c9dceeec8e95b4fc5d5bc55c751de820a41 /src | |
parent | 3342ea09af60005184fc03fcec5fdff0e78b01d3 (diff) | |
download | rspamd-271c03bba943c27214db950ff5fa981397483932.tar.gz rspamd-271c03bba943c27214db950ff5fa981397483932.zip |
Deal with multiple signatures in DKIM.
Issue: #254
Diffstat (limited to 'src')
-rw-r--r-- | src/plugins/dkim_check.c | 259 |
1 files changed, 161 insertions, 98 deletions
diff --git a/src/plugins/dkim_check.c b/src/plugins/dkim_check.c index 073bb347f..c5e73fbdc 100644 --- a/src/plugins/dkim_check.c +++ b/src/plugins/dkim_check.c @@ -43,6 +43,7 @@ #include "libutil/hash.h" #include "libutil/map.h" #include "main.h" +#include "utlist.h" #define DEFAULT_SYMBOL_REJECT "R_DKIM_REJECT" #define DEFAULT_SYMBOL_TEMPFAIL "R_DKIM_TEMPFAIL" @@ -67,6 +68,15 @@ struct dkim_ctx { gboolean skip_multi; }; +struct dkim_check_result { + rspamd_dkim_context_t *ctx; + rspamd_dkim_key_t *key; + struct rspamd_task *task; + gint res; + gint mult_allow, mult_deny; + struct dkim_check_result *next, *prev, *first; +}; + static struct dkim_ctx *dkim_module_ctx = NULL; static void dkim_symbol_callback (struct rspamd_task *task, void *unused); @@ -280,53 +290,85 @@ dkim_module_parse_strict (const gchar *value, gint *allow, gint *deny) } static void -dkim_module_check (struct rspamd_task *task, - rspamd_dkim_context_t *ctx, - rspamd_dkim_key_t *key) +dkim_module_check (struct dkim_check_result *res) { - gint res, score_allow = 1, score_deny = 1; + gboolean all_done = TRUE, got_allow = FALSE; const gchar *strict_value; + struct dkim_check_result *first, *cur, *sel = NULL; - msg_debug ("check dkim signature for %s domain from %s", - ctx->domain, - ctx->dns_key); - res = rspamd_dkim_check (ctx, key, task); - - if (dkim_module_ctx->dkim_domains != NULL) { - /* Perform strict check */ - if ((strict_value = - g_hash_table_lookup (dkim_module_ctx->dkim_domains, - ctx->domain)) != NULL) { - if (!dkim_module_parse_strict (strict_value, &score_allow, - &score_deny)) { - score_allow = dkim_module_ctx->strict_multiplier; - score_deny = dkim_module_ctx->strict_multiplier; - msg_debug ( - "no specific score found for %s domain, using %d for it", - ctx->domain, - score_deny); - } - else { - msg_debug ( - "specific score found for %s domain: using %d for deny and %d for allow", - ctx->dns_key, - score_deny, - score_allow); + first = res->first; + + DL_FOREACH (first, cur) { + if (cur->ctx == NULL) { + continue; + } + + if (cur->key != NULL && cur->res == -1) { + msg_debug ("check dkim signature for %s domain from %s", + cur->ctx->domain, + cur->ctx->dns_key); + cur->res = rspamd_dkim_check (cur->ctx, cur->key, cur->task); + + if (dkim_module_ctx->dkim_domains != NULL) { + /* Perform strict check */ + if ((strict_value = + g_hash_table_lookup (dkim_module_ctx->dkim_domains, + cur->ctx->domain)) != NULL) { + if (!dkim_module_parse_strict (strict_value, &cur->mult_allow, + &cur->mult_deny)) { + cur->mult_allow = dkim_module_ctx->strict_multiplier; + cur->mult_deny = dkim_module_ctx->strict_multiplier; + } + } } } - } - if (res == DKIM_REJECT) { - rspamd_task_insert_result (task, dkim_module_ctx->symbol_reject, score_deny, - g_list_prepend (NULL, rspamd_mempool_strdup (task->task_pool, ctx->domain))); + if (cur->res == -1) { + /* Still need a key */ + all_done = FALSE; + } } - else if (res == DKIM_TRYAGAIN) { - rspamd_task_insert_result (task, dkim_module_ctx->symbol_tempfail, 1, - g_list_prepend (NULL, rspamd_mempool_strdup (task->task_pool, ctx->domain))); + + if (all_done) { + DL_FOREACH (first, cur) { + if (cur->res == DKIM_CONTINUE) { + rspamd_task_insert_result (cur->task, + dkim_module_ctx->symbol_allow, + cur->mult_allow * 1.0, + g_list_prepend (NULL, + rspamd_mempool_strdup (cur->task->task_pool, + cur->ctx->domain))); + got_allow = TRUE; + sel = NULL; + } + else if (!got_allow) { + if (sel == NULL) { + sel = cur; + } + else if (sel->res == DKIM_TRYAGAIN && cur->res != DKIM_TRYAGAIN) { + sel = cur; + } + } + } } - else if (res == DKIM_CONTINUE) { - rspamd_task_insert_result (task, dkim_module_ctx->symbol_allow, score_allow, - g_list_prepend (NULL, rspamd_mempool_strdup (task->task_pool, ctx->domain))); + + if (sel != NULL) { + if (sel->res == DKIM_REJECT) { + rspamd_task_insert_result (sel->task, + dkim_module_ctx->symbol_reject, + sel->mult_deny * 1.0, + g_list_prepend (NULL, + rspamd_mempool_strdup (sel->task->task_pool, + sel->ctx->domain))); + } + else { + rspamd_task_insert_result (sel->task, + dkim_module_ctx->symbol_tempfail, + 1.0, + g_list_prepend (NULL, + rspamd_mempool_strdup (sel->task->task_pool, + sel->ctx->domain))); + } } } @@ -337,33 +379,28 @@ dkim_module_key_handler (rspamd_dkim_key_t *key, gpointer ud, GError *err) { - struct rspamd_task *task = ud; - + struct dkim_check_result *res = ud; if (key != NULL) { /* Add new key to the lru cache */ rspamd_lru_hash_insert (dkim_module_ctx->dkim_hash, g_strdup (ctx->dns_key), - key, task->tv.tv_sec, key->ttl); - dkim_module_check (task, ctx, key); + key, res->task->time_real, key->ttl); + res->key = key; } else { /* Insert tempfail symbol */ msg_info ("cannot get key for domain %s", ctx->dns_key); if (err != NULL) { - rspamd_task_insert_result (task, dkim_module_ctx->symbol_tempfail, 1, - g_list_prepend (NULL, - rspamd_mempool_strdup (task->task_pool, err->message))); - - } - else { - rspamd_task_insert_result (task, dkim_module_ctx->symbol_tempfail, 1, NULL); + res->res = DKIM_TRYAGAIN; } } if (err) { g_error_free (err); } + + dkim_module_check (res); } static void @@ -374,6 +411,7 @@ dkim_symbol_callback (struct rspamd_task *task, void *unused) rspamd_dkim_key_t *key; GError *err = NULL; struct raw_header *rh; + struct dkim_check_result *res = NULL, *cur; /* First check if a message has its signature */ hlist = message_get_header (task, @@ -386,61 +424,86 @@ dkim_symbol_callback (struct rspamd_task *task, void *unused) task->from_addr) == RADIX_NO_VALUE) { /* Parse signature */ msg_debug ("create dkim signature"); - /* - * Check only last signature as there is no way to check embeded signatures after - * resend or something like this - */ - if (dkim_module_ctx->skip_multi) { - if (hlist->next != NULL) { - msg_info ( - "<%s> skip dkim check as it has several dkim signatures", - task->message_id); - return; - } - } - hlist = g_list_last (hlist); - rh = (struct raw_header *)hlist->data; - ctx = rspamd_create_dkim_context (rh->decoded, - task->task_pool, - dkim_module_ctx->time_jitter, - &err); - if (ctx == NULL) { - if (err != NULL) { - msg_info ("<%s> cannot parse DKIM context: %s", - task->message_id, err->message); - g_error_free (err); + + while (hlist != NULL) { + rh = (struct raw_header *)hlist->data; + + if (res == NULL) { + res = rspamd_mempool_alloc0 (task->task_pool, sizeof (*res)); + res->prev = res; + cur = res; } else { - msg_info ("<%s> cannot parse DKIM context: unknown error", - task->message_id); - } - } - else { - /* Get key */ - if (dkim_module_ctx->trusted_only && - (dkim_module_ctx->dkim_domains == NULL || - g_hash_table_lookup (dkim_module_ctx->dkim_domains, - ctx->domain) == NULL)) { - msg_debug ("skip dkim check for %s domain", ctx->domain); - return; + cur = rspamd_mempool_alloc0 (task->task_pool, sizeof (*res)); } - 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); + + cur->first = res; + cur->res = -1; + cur->task = task; + cur->mult_allow = 1.0; + cur->mult_deny = 1.0; + + ctx = rspamd_create_dkim_context (rh->decoded, + task->task_pool, + dkim_module_ctx->time_jitter, + &err); + if (ctx == NULL) { + if (err != NULL) { + msg_info ("<%s> cannot parse DKIM context: %s", + task->message_id, err->message); + g_error_free (err); + } + else { + msg_info ("<%s> cannot parse DKIM context: unknown error", + task->message_id); + } + + hlist = g_list_next (hlist); + continue; } 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); + /* Get key */ + + cur->ctx = ctx; + + if (dkim_module_ctx->trusted_only && + (dkim_module_ctx->dkim_domains == NULL || + g_hash_table_lookup (dkim_module_ctx->dkim_domains, + ctx->domain) == NULL)) { + msg_debug ("skip dkim check for %s domain", ctx->domain); + hlist = g_list_next (hlist); + + continue; + } + + 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); + cur->key = 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, + cur); + } } + + if (res != cur) { + DL_APPEND (res, cur); + } + + hlist = g_list_next (hlist); } } } + + if (res != NULL) { + dkim_module_check (res); + } } |