* 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-or-later */
/* Code for DKIM support. Other DKIM relevant code is in
receive.c, transport.c and transports/smtp.c */
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 */
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++];
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 */
+ gstring_reset(g); /* overwrite previous record */
}
bad:
store_reset(reset_point);
+store_free_dns_answer(dnsa);
return NULL; /*XXX better error detail? logging? */
}
{
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 */
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)
{
"(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]");
}
break;
}
-log_write(0, LOG_MAIN, "%s", string_from_gstring(logmsg));
+log_write(0, LOG_MAIN, "%Y", logmsg);
return;
}
gstring * g = NULL;
const uschar * errstr = NULL;
-store_pool = POOL_PERM;
+store_pool = POOL_MESSAGE;
/* Delete eventual previous signature chain */
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";
}
{
int start = 0; /* compiler quietening */
-DEBUG(D_acl) start = g->ptr;
+DEBUG(D_acl) start = gstring_length(g);
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;
}
DEBUG(D_acl)
- if (g->ptr == start)
- debug_printf("DKIM: no authres\n");
+ if (gstring_length(g) == start)
+ debug_printf("DKIM:\tno authres\n");
else
- debug_printf("DKIM: authres '%.*s'\n", g->ptr - start - 3, g->s + start + 3);
+ debug_printf("DKIM:\tauthres '%.*s'\n", g->ptr - start - 3, g->s + start + 3);
return g;
}