X-Git-Url: https://git.exim.org/users/jgh/exim.git/blobdiff_plain/01603eec64d42431f182b33008206facfc7f800e..042e558f346b01902dd414206a047fa47b686f0b:/src/src/pdkim/pdkim.c diff --git a/src/src/pdkim/pdkim.c b/src/src/pdkim/pdkim.c index 9ebcfc1b6..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) { @@ -238,7 +246,7 @@ debug_printf("\n"); static pdkim_stringlist * pdkim_prepend_stringlist(pdkim_stringlist * base, const uschar * str) { -pdkim_stringlist * new_entry = store_get(sizeof(pdkim_stringlist)); +pdkim_stringlist * new_entry = store_get(sizeof(pdkim_stringlist), FALSE); memset(new_entry, 0, sizeof(pdkim_stringlist)); new_entry->value = string_copy(str); @@ -328,7 +336,7 @@ pdkim_relax_header_n(const uschar * header, int len, BOOL append_crlf) { BOOL past_field_name = FALSE; BOOL seen_wsp = FALSE; -uschar * relaxed = store_get(len+3); +uschar * relaxed = store_get(len+3, TRUE); /* tainted */ uschar * q = relaxed; for (const uschar * p = header; p - header < len; p++) @@ -408,7 +416,7 @@ pdkim_decode_qp(const uschar * str) int nchar = 0; uschar * q; const uschar * p = str; -uschar * n = store_get(Ustrlen(str)+1); +uschar * n = store_get(Ustrlen(str)+1, TRUE); *n = '\0'; q = n; @@ -465,7 +473,7 @@ BOOL past_hname = FALSE; BOOL in_b_val = FALSE; int where = PDKIM_HDR_LIMBO; -sig = store_get(sizeof(pdkim_signature)); +sig = store_get(sizeof(pdkim_signature), FALSE); memset(sig, 0, sizeof(pdkim_signature)); sig->bodylength = -1; @@ -474,7 +482,7 @@ sig->version = 0; sig->keytype = -1; sig->hashtype = -1; -q = sig->rawsig_no_b_val = store_get(Ustrlen(raw_hdr)+1); +q = sig->rawsig_no_b_val = store_get(Ustrlen(raw_hdr)+1, TRUE); /* tainted */ for (uschar * p = raw_hdr; ; p++) { @@ -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) @@ -656,7 +662,7 @@ const uschar * ele; int sep = ';'; pdkim_pubkey * pub; -pub = store_get(sizeof(pdkim_pubkey)); +pub = store_get(sizeof(pdkim_pubkey), TRUE); /* tainted */ memset(pub, 0, sizeof(pdkim_pubkey)); while ((ele = string_nextinlist(&raw_record, &sep, NULL, 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: @@ -1861,15 +1924,16 @@ return ctx->flags & PDKIM_MODE_SIGN || verify_pass /* -------------------------------------------------------------------------- */ DLLEXPORT pdkim_ctx * -pdkim_init_verify(uschar * (*dns_txt_callback)(uschar *), BOOL dot_stuffing) +pdkim_init_verify(uschar * (*dns_txt_callback)(const uschar *), BOOL dot_stuffing) { pdkim_ctx * ctx; -ctx = store_get(sizeof(pdkim_ctx)); +ctx = store_get(sizeof(pdkim_ctx), FALSE); memset(ctx, 0, sizeof(pdkim_ctx)); if (dot_stuffing) ctx->flags = PDKIM_DOT_TERM; -ctx->linebuf = store_get(PDKIM_MAX_BODY_LINE_LEN); +/* The line-buffer is for message data, hence tainted */ +ctx->linebuf = store_get(PDKIM_MAX_BODY_LINE_LEN, TRUE); ctx->dns_txt_callback = dns_txt_callback; return ctx; @@ -1891,7 +1955,7 @@ if (!domain || !selector || !privkey) /* Allocate & init one signature struct */ -sig = store_get(sizeof(pdkim_signature)); +sig = store_get(sizeof(pdkim_signature), FALSE); memset(sig, 0, sizeof(pdkim_signature)); sig->bodylength = -1; @@ -1977,7 +2041,7 @@ for (b = ctx->bodyhash; b; b = b->next) DEBUG(D_receive) debug_printf("PDKIM: new bodyhash %d/%d/%ld\n", hashtype, canon_method, bodylength); -b = store_get(sizeof(pdkim_bodyhash)); +b = store_get(sizeof(pdkim_bodyhash), FALSE); b->next = ctx->bodyhash; b->hashtype = hashtype; b->canon_method = canon_method; @@ -2017,11 +2081,12 @@ return b; void pdkim_init_context(pdkim_ctx * ctx, BOOL dot_stuffed, - uschar * (*dns_txt_callback)(uschar *)) + uschar * (*dns_txt_callback)(const uschar *)) { memset(ctx, 0, sizeof(pdkim_ctx)); ctx->flags = dot_stuffed ? PDKIM_MODE_SIGN | PDKIM_DOT_TERM : PDKIM_MODE_SIGN; -ctx->linebuf = store_get(PDKIM_MAX_BODY_LINE_LEN); +/* The line buffer is for message data, hence tainted */ +ctx->linebuf = store_get(PDKIM_MAX_BODY_LINE_LEN, TRUE); DEBUG(D_acl) ctx->dns_txt_callback = dns_txt_callback; }