]> source.dussan.org Git - rspamd.git/commitdiff
[Feature] Add pubkey checks for dkim_signing
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Tue, 12 Jun 2018 15:42:52 +0000 (16:42 +0100)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Tue, 12 Jun 2018 15:42:52 +0000 (16:42 +0100)
Issue: #2277

src/plugins/dkim_check.c
src/plugins/lua/dkim_signing.lua

index 773f9c56a984fa7b34f26dffe793a6c4c24e29fc..74bc4ea546fac1014799c61f1cde5a7141b2fd7f 100644 (file)
@@ -623,7 +623,7 @@ lua_dkim_sign_handler (lua_State *L)
        rspamd_dkim_sign_context_t *ctx;
        rspamd_dkim_sign_key_t *dkim_key;
        gsize rawlen = 0, keylen = 0;
-       gboolean no_cache = FALSE;
+       gboolean no_cache = FALSE, strict_pubkey_check = FALSE;
 
        luaL_argcheck (L, lua_type (L, 2) == LUA_TTABLE, 2, "'table' expected");
        /*
@@ -634,10 +634,12 @@ lua_dkim_sign_handler (lua_State *L)
         */
        if (!rspamd_lua_parse_table_arguments (L, 2, &err,
                        "key=V;rawkey=V;*domain=S;*selector=S;no_cache=B;headers=S;"
-                                       "sign_type=S;arc_idx=I;arc_cv=S;expire=I;pubkey=S",
+                       "sign_type=S;arc_idx=I;arc_cv=S;expire=I;pubkey=S;"
+                       "strict_pubkey_check=B",
                        &keylen, &key, &rawlen, &rawkey, &domain,
                        &selector, &no_cache, &headers,
-                       &sign_type_str, &arc_idx, &arc_cv, &expire, &pubkey)) {
+                       &sign_type_str, &arc_idx, &arc_cv, &expire, &pubkey,
+                       &strict_pubkey_check)) {
                msg_err_task ("cannot parse table arguments: %e",
                                err);
                g_error_free (err);
@@ -775,20 +777,42 @@ lua_dkim_sign_handler (lua_State *L)
                pk = rspamd_dkim_parse_key (pubkey, &keylen, NULL);
 
                if (pk == NULL) {
-                       msg_warn_task ("cannot parse pubkey from string: %s",
-                                       pubkey);
+                       if (strict_pubkey_check) {
+                               msg_err_task ("cannot parse pubkey from string: %s, skip signing",
+                                               pubkey);
+                               lua_pushboolean (L, FALSE);
+
+                               return 1;
+                       }
+                       else {
+                               msg_warn_task ("cannot parse pubkey from string: %s",
+                                               pubkey);
+                       }
                }
                else {
                        GError *te = NULL;
 
                        /* We have parsed the key, so try to check keys */
                        if (!rspamd_dkim_match_keys (pk, dkim_key, &te)) {
-                               msg_warn_task ("public key for %s/%s does not match private key: %e",
-                                               domain, selector, te);
-                               g_error_free (te);
+                               if (strict_pubkey_check) {
+                                       msg_err_task ("public key for %s/%s does not match private "
+                                                                 "key: %e, skip signing",
+                                                       domain, selector, te);
+                                       g_error_free (te);
+                                       lua_pushboolean (L, FALSE);
+                                       rspamd_dkim_key_unref (pk);
 
-                               /* TODO: add fatal failure possibility */
+                                       return 1;
+                               }
+                               else {
+                                       msg_warn_task ("public key for %s/%s does not match private "
+                                                                  "key: %e",
+                                                       domain, selector, te);
+                                       g_error_free (te);
+                               }
                        }
+
+                       rspamd_dkim_key_unref (pk);
                }
        }
 
index 5e057cf9ea9830200782c7895ae1569e82e47ffb..62370554919fc19e56aa8fbf49a211173c1e18de 100644 (file)
@@ -32,7 +32,9 @@ local settings = {
   allow_hdrfrom_mismatch_sign_networks = false,
   allow_hdrfrom_multiple = false,
   allow_username_mismatch = false,
+  allow_pubkey_mismatch = true,
   auth_only = true,
+  check_pubkey = false,
   domain = {},
   path = string.format('%s/%s/%s', rspamd_paths['DBDIR'], 'dkim', '$domain.$selector.key'),
   sign_local = true,
@@ -56,6 +58,41 @@ local function dkim_signing_cb(task)
     return
   end
 
+  local function do_sign()
+    if settings.check_pubkey then
+      local resolve_name = p.selector .. "._domainkey." .. p.domain
+      task:get_resolver():resolve_txt({
+        task = task,
+        name = resolve_name,
+        callback = function(_, _, results, err)
+          task:inc_dns_req()
+          if not err and results and results[1] then
+            p.pubkey = results[1]
+            p.strict_pubkey_check = not settings.allow_pubkey_mismatch
+          elseif not settings.allow_pubkey_mismatch then
+            rspamd_logger.errx('public key for domain %s/%s is not found: %s, skip signing',
+                p.domain, p.selector, err)
+            return
+          else
+            rspamd_logger.infox('public key for domain %s/%s is not found: %s',
+                p.domain, p.selector, err)
+          end
+
+          local sret, _ = sign_func(task, p)
+          if sret then
+            task:insert_result(settings.symbol, 1.0)
+          end
+        end,
+        forced = true
+      })
+    else
+      local sret, _ = sign_func(task, p)
+      if sret then
+        task:insert_result(settings.symbol, 1.0)
+      end
+    end
+  end
+
   if settings.use_redis then
     local function try_redis_key(selector)
       p.key = nil
@@ -68,11 +105,8 @@ local function dkim_signing_cb(task)
         elseif type(data) ~= 'string' then
           rspamd_logger.debugm(N, task, "missing DKIM key for %s", rk)
         else
-        p.rawkey = data
-        local sret, _ = sign_func(task, p)
-        if sret then
-          task:insert_result(settings.symbol, 1.0)
-        end
+          p.rawkey = data
+          do_sign()
         end
       end
       local rret = rspamd_redis_make_request(task,
@@ -127,8 +161,7 @@ local function dkim_signing_cb(task)
         return false
       end
 
-      local sret, _ = sign_func(task, p)
-      return sret
+      do_sign()
     else
       rspamd_logger.infox(task, 'key path or dkim selector unconfigured; no signing')
       return false