X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/d85cdeb5e554b59bf4c43c54461409c15c6ee9c5..a85c067ba6c6940512cf57ec213277a370d87e70:/src/src/dkim.c diff --git a/src/src/dkim.c b/src/src/dkim.c index 5c9d2279e..9b6e14a3f 100644 --- a/src/src/dkim.c +++ b/src/src/dkim.c @@ -2,8 +2,10 @@ * Exim - an Internet mail transport agent * *************************************************/ +/* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge, 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Code for DKIM support. Other DKIM relevant code is in receive.c, transport.c and transports/smtp.c */ @@ -21,6 +23,7 @@ void params_dkim(void) { builtin_macro_create_var(US"_DKIM_SIGN_HEADERS", US PDKIM_DEFAULT_SIGN_HEADERS); +builtin_macro_create_var(US"_DKIM_OVERSIGN_HEADERS", US PDKIM_OVERSIGN_HEADERS); } # else /*!MACRO_PREDEF*/ @@ -48,11 +51,11 @@ dkim_exim_query_dns_txt(const uschar * name) dns_answer * dnsa = store_get_dns_answer(); dns_scan dnss; rmark reset_point = store_mark(); -gstring * g = NULL; +gstring * g = string_get_tainted(256, GET_TAINTED); lookup_dnssec_authenticated = NULL; if (dns_lookup(dnsa, name, T_TXT, NULL) != DNS_SUCCEED) - return NULL; /*XXX better error detail? logging? */ + goto bad; /* Search for TXT record */ @@ -60,12 +63,8 @@ for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr; rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) if (rr->type == T_TXT) - { - int rr_offset = 0; - - /* Copy record content to the answer buffer */ - - while (rr_offset < rr->size) + { /* Copy record content to the answer buffer */ + for (int rr_offset = 0; rr_offset < rr->size; ) { uschar len = rr->data[rr_offset++]; @@ -76,18 +75,20 @@ for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr_offset += len; } - /* check if this looks like a DKIM record */ + /* Check if this looks like a DKIM record */ if (Ustrncmp(g->s, "v=", 2) != 0 || strncasecmp(CS g->s, "v=dkim", 6) == 0) { + store_free_dns_answer(dnsa); gstring_release_unused(g); return string_from_gstring(g); } - if (g) g->ptr = 0; /* overwrite previous record */ + g->ptr = 0; /* overwrite previous record */ } bad: store_reset(reset_point); +store_free_dns_answer(dnsa); return NULL; /*XXX better error detail? logging? */ } @@ -107,12 +108,15 @@ dkim_exim_verify_init(BOOL dot_stuffing) { dkim_exim_init(); -/* There is a store-reset between header & body reception -so cannot use the main pool. Any allocs done by Exim -memory-handling must use the perm pool. */ +/* There is a store-reset between header & body reception for the main pool +(actually, after every header line) so cannot use that as we need the data we +store per-header, during header processing, at the end of body reception +for evaluating the signature. Any allocs done for dkim verify +memory-handling must use a different pool. We use a separate one that we +can reset per message. */ dkim_verify_oldpool = store_pool; -store_pool = POOL_PERM; +store_pool = POOL_MESSAGE; /* Free previous context if there is one */ @@ -125,19 +129,22 @@ dkim_verify_ctx = pdkim_init_verify(&dkim_exim_query_dns_txt, dot_stuffing); dkim_collect_input = dkim_verify_ctx ? DKIM_MAX_SIGNATURES : 0; dkim_collect_error = NULL; -/* Start feed up with any cached data */ -receive_get_cache(); +/* Start feed up with any cached data, but limited to message data */ +receive_get_cache(chunking_state == CHUNKING_LAST + ? chunking_data_left : GETC_BUFFER_UNLIMITED); store_pool = dkim_verify_oldpool; } +/* Submit a chunk of data for verification input. +Only use the data when the feed is activated. */ void dkim_exim_verify_feed(uschar * data, int len) { int rc; -store_pool = POOL_PERM; +store_pool = POOL_MESSAGE; if ( dkim_collect_input && (rc = pdkim_feed(dkim_verify_ctx, data, len)) != PDKIM_OK) { @@ -267,6 +274,11 @@ else "(headers probably modified in transit)]"); break; + case PDKIM_VERIFY_INVALID_PUBKEY_KEYSIZE: + logmsg = string_cat(logmsg, + US"signature invalid (key too short)]"); + break; + default: logmsg = string_cat(logmsg, US"unspecified reason]"); } @@ -298,7 +310,7 @@ int rc; gstring * g = NULL; const uschar * errstr = NULL; -store_pool = POOL_PERM; +store_pool = POOL_MESSAGE; /* Delete eventual previous signature chain */ @@ -405,7 +417,7 @@ for (pdkim_signature * sig = dkim_signatures; sig; sig = sig->next) dkim_cur_sig = sig; dkim_signing_domain = US sig->domain; dkim_signing_selector = US sig->selector; - dkim_key_length = sig->sighash.len * 8; + dkim_key_length = sig->keybits; /* These two return static strings, so we can compare the addr later to see if the ACL overwrote them. Check that when logging */ @@ -559,6 +571,7 @@ switch (what) return US"pubkey_unavailable"; case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:return US"pubkey_dns_syntax"; case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT: return US"pubkey_der_syntax"; + case PDKIM_VERIFY_INVALID_PUBKEY_KEYSIZE: return US"pubkey_too_short"; case PDKIM_VERIFY_FAIL_BODY: return US"bodyhash_mismatch"; case PDKIM_VERIFY_FAIL_MESSAGE: return US"signature_incorrect"; } @@ -853,6 +866,9 @@ for (pdkim_signature * sig = dkim_signatures; sig; sig = sig->next) g = string_cat(g, US"fail (signature did not verify; headers probably modified in transit)\n\t\t"); break; + case PDKIM_VERIFY_INVALID_PUBKEY_KEYSIZE: /* should this really be "polcy"? */ + g = string_fmt_append(g, "fail (public key too short: %u bits)\n\t\t", sig->keybits); + break; default: g = string_cat(g, US"fail (unspecified reason)\n\t\t"); break;