]> source.dussan.org Git - rspamd.git/commitdiff
[Feature] Allow to match private and public keys for DKIM signatures
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Mon, 11 Jun 2018 18:26:49 +0000 (19:26 +0100)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Mon, 11 Jun 2018 18:26:49 +0000 (19:26 +0100)
src/libserver/dkim.c
src/libserver/dkim.h
src/plugins/dkim_check.c

index 4dc16fd1ecfde77dc46c60213b11d24ac6ff1c8c..3104428a96a7225c3762255230763312b283ca86 100644 (file)
@@ -2997,3 +2997,39 @@ rspamd_dkim_sign (struct rspamd_task *task, const gchar *selector,
 
        return hdr;
 }
+
+gboolean
+rspamd_dkim_match_keys (rspamd_dkim_key_t *pk,
+                                                                rspamd_dkim_sign_key_t *sk,
+                                                                GError **err)
+{
+       const BIGNUM *n1, *n2;
+
+       if (pk == NULL || sk == NULL) {
+               g_set_error (err, dkim_error_quark (), DKIM_SIGERROR_KEYFAIL,
+                               "missing public or private key");
+               return FALSE;
+       }
+
+       if (pk->type != RSPAMD_DKIM_KEY_RSA) {
+               g_set_error (err, dkim_error_quark (), DKIM_SIGERROR_KEYFAIL,
+                               "pubkey is not RSA key");
+               return FALSE;
+       }
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+       RSA_get0_key (pk->key.key_rsa, &n1, NULL, NULL);
+       n2 = RSA_get0_key (sk->key_rsa, &n2, NULL, NULL);
+#else
+       n1 = pk->key.key_rsa->n;
+       n2 = sk->key_rsa->n;
+#endif
+
+       if (BN_cmp (n1, n2) != 0) {
+               g_set_error (err, dkim_error_quark (), DKIM_SIGERROR_KEYHASHMISMATCH,
+                               "pubkey does not match private key");
+               return FALSE;
+       }
+
+       return TRUE;
+}
\ No newline at end of file
index 5081c0f52d1e0f248827dd48bb90d465ac568b2a..6ce09946423b7e07c876d1bb3e29ab04daa3b8be 100644 (file)
@@ -260,6 +260,17 @@ goffset rspamd_dkim_canonize_header_relaxed_str (const gchar *hname,
                gchar *out,
                gsize outlen);
 
+/**
+ * Checks public and private keys for match
+ * @param pk
+ * @param sk
+ * @param err
+ * @return
+ */
+gboolean rspamd_dkim_match_keys (rspamd_dkim_key_t *pk,
+                                                                rspamd_dkim_sign_key_t *sk,
+                                                                GError **err);
+
 /**
  * Free DKIM key
  * @param key
index 8f63a167d599137927ff93daa21a1991f89edc4b..773f9c56a984fa7b34f26dffe793a6c4c24e29fc 100644 (file)
@@ -618,7 +618,8 @@ lua_dkim_sign_handler (lua_State *L)
        GError *err = NULL;
        GString *hdr;
        const gchar *selector = NULL, *domain = NULL, *key = NULL, *rawkey = NULL,
-                       *headers = NULL, *sign_type_str = NULL, *arc_cv = NULL;
+                       *headers = NULL, *sign_type_str = NULL, *arc_cv = NULL,
+                       *pubkey = NULL;
        rspamd_dkim_sign_context_t *ctx;
        rspamd_dkim_sign_key_t *dkim_key;
        gsize rawlen = 0, keylen = 0;
@@ -633,11 +634,11 @@ 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",
+                                       "sign_type=S;arc_idx=I;arc_cv=S;expire=I;pubkey=S",
                        &keylen, &key, &rawlen, &rawkey, &domain,
                        &selector, &no_cache, &headers,
-                       &sign_type_str, &arc_idx, &arc_cv, &expire)) {
-               msg_err_task ("invalid return value from sign condition: %e",
+                       &sign_type_str, &arc_idx, &arc_cv, &expire, &pubkey)) {
+               msg_err_task ("cannot parse table arguments: %e",
                                err);
                g_error_free (err);
 
@@ -766,6 +767,31 @@ lua_dkim_sign_handler (lua_State *L)
                }
        }
 
+       if (pubkey != NULL) {
+               /* Also check if private and public keys match */
+               rspamd_dkim_key_t *pk;
+               gsize keylen = strlen (pubkey);
+
+               pk = rspamd_dkim_parse_key (pubkey, &keylen, NULL);
+
+               if (pk == NULL) {
+                       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);
+
+                               /* TODO: add fatal failure possibility */
+                       }
+               }
+       }
+
        ctx = rspamd_create_dkim_sign_context (task, dkim_key,
                        DKIM_CANON_RELAXED, DKIM_CANON_RELAXED,
                        headers, sign_type, &err);