From 042e558f346b01902dd414206a047fa47b686f0b Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Sat, 10 Aug 2019 17:58:22 +0100 Subject: DKIM: preferences for verify algorithms --- src/src/globals.c | 3 ++ src/src/globals.h | 3 ++ src/src/pdkim/pdkim.c | 93 ++++++++++++++++++++++++++++++++++++++++++--------- src/src/pdkim/pdkim.h | 5 +-- src/src/readconf.c | 3 ++ 5 files changed, 88 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/src/globals.c b/src/src/globals.c index 15fb0898c..61a9c9796 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -830,6 +830,9 @@ void *dkim_signatures = NULL; uschar *dkim_signers = NULL; uschar *dkim_signing_domain = NULL; uschar *dkim_signing_selector = NULL; +uschar *dkim_verify_hashes = US"sha256:sha512:sha1"; +uschar *dkim_verify_keytypes = US"ed25519:rsa"; +BOOL dkim_verify_minimal = FALSE; uschar *dkim_verify_overall = NULL; uschar *dkim_verify_signers = US"$dkim_signers"; uschar *dkim_verify_status = NULL; diff --git a/src/src/globals.h b/src/src/globals.h index c22600425..4ab43ca65 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -503,6 +503,9 @@ extern void *dkim_signatures; /* Actually a (pdkim_signature *) but mos extern uschar *dkim_signers; /* Expansion variable, holds colon-separated list of domains and identities that have signed a message */ extern uschar *dkim_signing_domain; /* Expansion variable, domain used for signing a message. */ extern uschar *dkim_signing_selector; /* Expansion variable, selector used for signing a message. */ +extern uschar *dkim_verify_hashes; /* Preference order for signatures */ +extern uschar *dkim_verify_keytypes; /* Preference order for signatures */ +extern BOOL dkim_verify_minimal; /* Shortcircuit signture verification */ extern uschar *dkim_verify_overall; /* First successful domain verified, or null */ extern uschar *dkim_verify_signers; /* Colon-separated list of domains for each of which we call the DKIM ACL */ extern uschar *dkim_verify_status; /* result for this signature */ diff --git a/src/src/pdkim/pdkim.c b/src/src/pdkim/pdkim.c index f10f20627..79e7c633d 100644 --- a/src/src/pdkim/pdkim.c +++ b/src/src/pdkim/pdkim.c @@ -121,6 +121,14 @@ return string_sprintf("%s-%s", } +static int +pdkim_keyname_to_keytype(const uschar * s) +{ +for (int i = 0; i < nelem(pdkim_keytypes); i++) + if (Ustrcmp(s, pdkim_keytypes[i]) == 0) return i; +return -1; +} + int pdkim_hashname_to_hashtype(const uschar * s, unsigned len) { @@ -561,9 +569,7 @@ for (uschar * p = raw_hdr; ; p++) uschar * elem; if ((elem = string_nextinlist(&list, &sep, NULL, 0))) - for (int i = 0; i < nelem(pdkim_keytypes); i++) - if (Ustrcmp(elem, pdkim_keytypes[i]) == 0) - { sig->keytype = i; break; } + sig->keytype = pdkim_keyname_to_keytype(elem); if ((elem = string_nextinlist(&list, &sep, NULL, 0))) for (int i = 0; i < nelem(pdkim_hashes); i++) if (Ustrcmp(elem, pdkim_hashes[i].dkim_hashname) == 0) @@ -1411,16 +1417,13 @@ time we do not have a signature so we must interpret the pubkey k= tag instead. Assume writing on the sig is ok in that case. */ if (sig->keytype < 0) - { - for(int i = 0; i < nelem(pdkim_keytypes); i++) - if (Ustrcmp(p->keytype, pdkim_keytypes[i]) == 0) - { sig->keytype = i; goto k_ok; } - DEBUG(D_acl) debug_printf("verify_init: unhandled keytype %s\n", p->keytype); - sig->verify_status = PDKIM_VERIFY_INVALID; - sig->verify_ext_status = PDKIM_VERIFY_INVALID_PUBKEY_IMPORT; - return NULL; - } -k_ok: + if ((sig->keytype = pdkim_keyname_to_keytype(p->keytype)) < 0) + { + DEBUG(D_acl) debug_printf("verify_init: unhandled keytype %s\n", p->keytype); + sig->verify_status = PDKIM_VERIFY_INVALID; + sig->verify_ext_status = PDKIM_VERIFY_INVALID_PUBKEY_IMPORT; + return NULL; + } if (sig->keytype == KEYTYPE_ED25519) check_bare_ed25519_pubkey(p); @@ -1440,6 +1443,61 @@ return p; } +/* -------------------------------------------------------------------------- */ +/* Sort and filter the sigs developed from the message */ + +static pdkim_signature * +sort_sig_methods(pdkim_signature * siglist) +{ +pdkim_signature * yield, ** ss; +const uschar * prefs; +uschar * ele; +int sep; + +if (!siglist) return NULL; + +/* first select in order of hashtypes */ +DEBUG(D_acl) debug_printf("PDKIM: dkim_verify_hashes '%s'\n", dkim_verify_hashes); +for (prefs = dkim_verify_hashes, sep = 0, yield = NULL, ss = &yield; + ele = string_nextinlist(&prefs, &sep, NULL, 0); ) + { + int i = pdkim_hashname_to_hashtype(CUS ele, 0); + for (pdkim_signature * s = siglist, * next, ** prev = &siglist; s; + s = next) + { + next = s->next; + if (s->hashtype == i) + { *prev = next; s->next = NULL; *ss = s; ss = &s->next; } + else + prev = &s->next; + } + } + +/* then in order of keytypes */ +siglist = yield; +DEBUG(D_acl) debug_printf("PDKIM: dkim_verify_keytypes '%s'\n", dkim_verify_keytypes); +for (prefs = dkim_verify_keytypes, sep = 0, yield = NULL, ss = &yield; + ele = string_nextinlist(&prefs, &sep, NULL, 0); ) + { + int i = pdkim_keyname_to_keytype(CUS ele); + for (pdkim_signature * s = siglist, * next, ** prev = &siglist; s; + s = next) + { + next = s->next; + if (s->keytype == i) + { *prev = next; s->next = NULL; *ss = s; ss = &s->next; } + else + prev = &s->next; + } + } + +DEBUG(D_acl) for (pdkim_signature * s = yield; s; s = s->next) + debug_printf(" retain d=%s s=%s a=%s\n", + s->domain, s->selector, dkim_sig_to_a_tag(s)); +return yield; +} + + /* -------------------------------------------------------------------------- */ DLLEXPORT int @@ -1472,6 +1530,11 @@ have a hash to do for ARC. */ pdkim_finish_bodyhash(ctx); +/* Sort and filter the recived signatures */ + +if (!(ctx->flags & PDKIM_MODE_SIGN)) + ctx->sig = sort_sig_methods(ctx->sig); + if (!ctx->sig) { DEBUG(D_acl) debug_printf("PDKIM: no signatures\n"); @@ -1496,7 +1559,7 @@ for (pdkim_signature * sig = ctx->sig; sig; sig = sig->next) } /*XXX The hash of the headers is needed for GCrypt (for which we can do RSA - suging only, as it happens) and for either GnuTLS and OpenSSL when we are + signing only, as it happens) and for either GnuTLS and OpenSSL when we are signing with EC (specifically, Ed25519). The former is because the GCrypt signing operation is pure (does not do its own hash) so we must hash. The latter is because we (stupidly, but this is what the IETF draft is saying) @@ -1550,7 +1613,6 @@ for (pdkim_signature * sig = ctx->sig; sig; sig = sig->next) /* Import private key, including the keytype which we need for building the signature header */ -/*XXX extend for non-RSA algos */ if ((*err = exim_dkim_signing_init(CUS sig->privkey, &sctx))) { log_write(0, LOG_MAIN|LOG_PANIC, "signing_init: %s", *err); @@ -1831,6 +1893,7 @@ for (pdkim_signature * sig = ctx->sig; sig; sig = sig->next) { sig->verify_status = PDKIM_VERIFY_PASS; verify_pass = TRUE; + if (dkim_verify_minimal) break; } NEXT_VERIFY: diff --git a/src/src/pdkim/pdkim.h b/src/src/pdkim/pdkim.h index b2f586ca1..0c9d46d01 100644 --- a/src/src/pdkim/pdkim.h +++ b/src/src/pdkim/pdkim.h @@ -74,9 +74,6 @@ /* Some parameter values */ #define PDKIM_QUERYMETHOD_DNS_TXT 0 -/*#define PDKIM_ALGO_RSA_SHA256 0 */ -/*#define PDKIM_ALGO_RSA_SHA1 1 */ - #define PDKIM_CANON_SIMPLE 0 #define PDKIM_CANON_RELAXED 1 @@ -142,7 +139,7 @@ typedef struct pdkim_signature { /* (v=) The version, as an integer. Currently, always "1" */ int version; - /* (a=) The signature algorithm. Either PDKIM_ALGO_RSA_SHA256 */ + /* (a=) The signature algorithm. */ int keytype; /* pdkim_keytypes index */ int hashtype; /* pdkim_hashes index */ diff --git a/src/src/readconf.c b/src/src/readconf.c index a5482f72d..05d3077e0 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -117,6 +117,9 @@ static optionlist optionlist_config[] = { #endif { "disable_ipv6", opt_bool, &disable_ipv6 }, #ifndef DISABLE_DKIM + { "dkim_verify_hashes", opt_stringptr, &dkim_verify_hashes }, + { "dkim_verify_keytypes", opt_stringptr, &dkim_verify_keytypes }, + { "dkim_verify_minimal", opt_bool, &dkim_verify_minimal }, { "dkim_verify_signers", opt_stringptr, &dkim_verify_signers }, #endif #ifdef EXPERIMENTAL_DMARC -- cgit v1.2.3