From 9d6477913e50e2c73efbede6f01a37b615cae335 Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Mon, 11 Jun 2018 19:26:49 +0100 Subject: [PATCH] [Feature] Allow to match private and public keys for DKIM signatures --- src/libserver/dkim.c | 36 ++++++++++++++++++++++++++++++++++++ src/libserver/dkim.h | 11 +++++++++++ src/plugins/dkim_check.c | 34 ++++++++++++++++++++++++++++++---- 3 files changed, 77 insertions(+), 4 deletions(-) diff --git a/src/libserver/dkim.c b/src/libserver/dkim.c index 4dc16fd1e..3104428a9 100644 --- a/src/libserver/dkim.c +++ b/src/libserver/dkim.c @@ -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 diff --git a/src/libserver/dkim.h b/src/libserver/dkim.h index 5081c0f52..6ce099464 100644 --- a/src/libserver/dkim.h +++ b/src/libserver/dkim.h @@ -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 diff --git a/src/plugins/dkim_check.c b/src/plugins/dkim_check.c index 8f63a167d..773f9c56a 100644 --- a/src/plugins/dkim_check.c +++ b/src/plugins/dkim_check.c @@ -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); -- 2.39.5