Copyright updates:
[exim.git] / src / src / dkim.c
CommitLineData
80a47a2c
TK
1/*************************************************
2* Exim - an Internet mail transport agent *
3*************************************************/
4
f9ba5e22 5/* Copyright (c) University of Cambridge, 1995 - 2018 */
1e1ddfac 6/* Copyright (c) The Exim Maintainers 2020 */
80a47a2c
TK
7/* See the file NOTICE for conditions of use and distribution. */
8
9/* Code for DKIM support. Other DKIM relevant code is in
10 receive.c, transport.c and transports/smtp.c */
11
12#include "exim.h"
13
14#ifndef DISABLE_DKIM
15
970424a5
JH
16# include "pdkim/pdkim.h"
17
18# ifdef MACRO_PREDEF
5f69a529 19# include "macro_predef.h"
970424a5
JH
20
21void
a2701501 22params_dkim(void)
970424a5
JH
23{
24builtin_macro_create_var(US"_DKIM_SIGN_HEADERS", US PDKIM_DEFAULT_SIGN_HEADERS);
d34c22b8 25builtin_macro_create_var(US"_DKIM_OVERSIGN_HEADERS", US PDKIM_OVERSIGN_HEADERS);
970424a5
JH
26}
27# else /*!MACRO_PREDEF*/
28
29
30
617d3932 31pdkim_ctx dkim_sign_ctx;
80a47a2c 32
ca9cb170 33int dkim_verify_oldpool;
1cfe5c1c 34pdkim_ctx *dkim_verify_ctx = NULL;
1cfe5c1c 35pdkim_signature *dkim_cur_sig = NULL;
b9df1829 36static const uschar * dkim_collect_error = NULL;
80a47a2c 37
64b67b65
JH
38#define DKIM_MAX_SIGNATURES 20
39
d73e45df
JH
40
41
362e4161
JH
42/* Look up the DKIM record in DNS for the given hostname.
43Will use the first found if there are multiple.
44The return string is tainted, having come from off-site.
d73e45df
JH
45*/
46
617d3932 47uschar *
fc2ba7b9 48dkim_exim_query_dns_txt(const uschar * name)
1cfe5c1c 49{
8743d3ac 50dns_answer * dnsa = store_get_dns_answer();
1cfe5c1c 51dns_scan dnss;
f3ebb786 52rmark reset_point = store_mark();
1eedc10f 53gstring * g = NULL;
80a47a2c 54
1cfe5c1c 55lookup_dnssec_authenticated = NULL;
362e4161 56if (dns_lookup(dnsa, name, T_TXT, NULL) != DNS_SUCCEED)
1eedc10f 57 return NULL; /*XXX better error detail? logging? */
80a47a2c 58
1cfe5c1c 59/* Search for TXT record */
80a47a2c 60
362e4161 61for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS);
1cfe5c1c 62 rr;
362e4161 63 rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
1cfe5c1c
JH
64 if (rr->type == T_TXT)
65 {
80a47a2c 66 int rr_offset = 0;
1cfe5c1c
JH
67
68 /* Copy record content to the answer buffer */
69
70 while (rr_offset < rr->size)
71 {
72 uschar len = rr->data[rr_offset++];
1eedc10f
JH
73
74 g = string_catn(g, US(rr->data + rr_offset), len);
75 if (g->ptr >= PDKIM_DNS_TXT_MAX_RECLEN)
76 goto bad;
77
1cfe5c1c 78 rr_offset += len;
4263f395 79 }
dd33c4e6
HSHR
80
81 /* check if this looks like a DKIM record */
d011368a 82 if (Ustrncmp(g->s, "v=", 2) != 0 || strncasecmp(CS g->s, "v=dkim", 6) == 0)
1eedc10f 83 {
e59797e3 84 gstring_release_unused(g);
1eedc10f
JH
85 return string_from_gstring(g);
86 }
87
88 if (g) g->ptr = 0; /* overwrite previous record */
80a47a2c 89 }
80a47a2c 90
1eedc10f 91bad:
f3ebb786 92store_reset(reset_point);
1eedc10f 93return NULL; /*XXX better error detail? logging? */
80a47a2c
TK
94}
95
96
1cfe5c1c 97void
2592e6c0
JH
98dkim_exim_init(void)
99{
d85cdeb5
JH
100if (f.dkim_init_done) return;
101f.dkim_init_done = TRUE;
2592e6c0
JH
102pdkim_init();
103}
104
105
ca9cb170 106
2592e6c0 107void
e983e85a 108dkim_exim_verify_init(BOOL dot_stuffing)
1cfe5c1c 109{
d85cdeb5
JH
110dkim_exim_init();
111
ca9cb170
JH
112/* There is a store-reset between header & body reception
113so cannot use the main pool. Any allocs done by Exim
114memory-handling must use the perm pool. */
115
116dkim_verify_oldpool = store_pool;
117store_pool = POOL_PERM;
118
1cfe5c1c 119/* Free previous context if there is one */
80a47a2c 120
1cfe5c1c
JH
121if (dkim_verify_ctx)
122 pdkim_free_ctx(dkim_verify_ctx);
80a47a2c 123
1cfe5c1c 124/* Create new context */
80a47a2c 125
e983e85a 126dkim_verify_ctx = pdkim_init_verify(&dkim_exim_query_dns_txt, dot_stuffing);
64b67b65 127dkim_collect_input = dkim_verify_ctx ? DKIM_MAX_SIGNATURES : 0;
b9df1829 128dkim_collect_error = NULL;
ca9cb170 129
584e96c6
JH
130/* Start feed up with any cached data */
131receive_get_cache();
132
ca9cb170 133store_pool = dkim_verify_oldpool;
80a47a2c
TK
134}
135
136
1cfe5c1c
JH
137void
138dkim_exim_verify_feed(uschar * data, int len)
139{
f7302073
JH
140int rc;
141
ca9cb170 142store_pool = POOL_PERM;
1cfe5c1c 143if ( dkim_collect_input
ef698bf6 144 && (rc = pdkim_feed(dkim_verify_ctx, data, len)) != PDKIM_OK)
f7302073 145 {
b9df1829 146 dkim_collect_error = pdkim_errstr(rc);
f7302073 147 log_write(0, LOG_MAIN,
b9df1829 148 "DKIM: validation error: %.100s", dkim_collect_error);
64b67b65 149 dkim_collect_input = 0;
f7302073 150 }
ca9cb170 151store_pool = dkim_verify_oldpool;
80a47a2c
TK
152}
153
154
a79d8834
JH
155/* Log the result for the given signature */
156static void
157dkim_exim_verify_log_sig(pdkim_signature * sig)
1cfe5c1c 158{
cc55f420 159gstring * logmsg;
a79d8834
JH
160uschar * s;
161
cc55f420
JH
162if (!sig) return;
163
6678c382 164/* Remember the domain for the first pass result */
dfbcb5ac 165
2c47372f
JH
166if ( !dkim_verify_overall
167 && dkim_verify_status
168 ? Ustrcmp(dkim_verify_status, US"pass") == 0
169 : sig->verify_status == PDKIM_VERIFY_PASS
170 )
171 dkim_verify_overall = string_copy(sig->domain);
172
6678c382
JH
173/* Rewrite the sig result if the ACL overrode it. This is only
174needed because the DMARC code (sigh) peeks at the dkim sigs.
175Mark the sig for this having been done. */
176
177if ( dkim_verify_status
178 && ( dkim_verify_status != dkim_exim_expand_query(DKIM_VERIFY_STATUS)
179 || dkim_verify_reason != dkim_exim_expand_query(DKIM_VERIFY_REASON)
180 ) )
181 { /* overridden by ACL */
182 sig->verify_ext_status = -1;
183 if (Ustrcmp(dkim_verify_status, US"fail") == 0)
184 sig->verify_status = PDKIM_VERIFY_POLICY | PDKIM_VERIFY_FAIL;
185 else if (Ustrcmp(dkim_verify_status, US"invalid") == 0)
186 sig->verify_status = PDKIM_VERIFY_POLICY | PDKIM_VERIFY_INVALID;
187 else if (Ustrcmp(dkim_verify_status, US"none") == 0)
188 sig->verify_status = PDKIM_VERIFY_POLICY | PDKIM_VERIFY_NONE;
189 else if (Ustrcmp(dkim_verify_status, US"pass") == 0)
190 sig->verify_status = PDKIM_VERIFY_POLICY | PDKIM_VERIFY_PASS;
191 else
192 sig->verify_status = -1;
193 }
194
2c47372f
JH
195if (!LOGGING(dkim_verbose)) return;
196
6678c382 197
aaaa94ea 198logmsg = string_catn(NULL, US"DKIM: ", 6);
a79d8834
JH
199if (!(s = sig->domain)) s = US"<UNSET>";
200logmsg = string_append(logmsg, 2, "d=", s);
201if (!(s = sig->selector)) s = US"<UNSET>";
202logmsg = string_append(logmsg, 2, " s=", s);
52f12a7c
JH
203logmsg = string_fmt_append(logmsg, " c=%s/%s a=%s b=" SIZE_T_FMT,
204 sig->canon_headers == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
205 sig->canon_body == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
206 dkim_sig_to_a_tag(sig),
207 (int)sig->sighash.len > -1 ? sig->sighash.len * 8 : (size_t)0);
a79d8834 208if ((s= sig->identity)) logmsg = string_append(logmsg, 2, " i=", s);
52f12a7c
JH
209if (sig->created > 0) logmsg = string_fmt_append(logmsg, " t=%lu",
210 sig->created);
211if (sig->expires > 0) logmsg = string_fmt_append(logmsg, " x=%lu",
212 sig->expires);
213if (sig->bodylength > -1) logmsg = string_fmt_append(logmsg, " l=%lu",
214 sig->bodylength);
a79d8834 215
dfbcb5ac
JH
216if (sig->verify_status & PDKIM_VERIFY_POLICY)
217 logmsg = string_append(logmsg, 5,
218 US" [", dkim_verify_status, US" - ", dkim_verify_reason, US"]");
219else
1cfe5c1c
JH
220 switch (sig->verify_status)
221 {
222 case PDKIM_VERIFY_NONE:
aaaa94ea 223 logmsg = string_cat(logmsg, US" [not verified]");
80a47a2c 224 break;
1cfe5c1c
JH
225
226 case PDKIM_VERIFY_INVALID:
aaaa94ea 227 logmsg = string_cat(logmsg, US" [invalid - ");
1cfe5c1c
JH
228 switch (sig->verify_ext_status)
229 {
230 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
acec9514 231 logmsg = string_cat(logmsg,
aaaa94ea 232 US"public key record (currently?) unavailable]");
1cfe5c1c
JH
233 break;
234
235 case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
aaaa94ea 236 logmsg = string_cat(logmsg, US"overlong public key record]");
1cfe5c1c
JH
237 break;
238
df3def24
JH
239 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:
240 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT:
aaaa94ea 241 logmsg = string_cat(logmsg, US"syntax error in public key record]");
1cfe5c1c 242 break;
07eeb4df 243
a79d8834 244 case PDKIM_VERIFY_INVALID_SIGNATURE_ERROR:
aaaa94ea 245 logmsg = string_cat(logmsg, US"signature tag missing or invalid]");
a79d8834 246 break;
07eeb4df 247
a79d8834 248 case PDKIM_VERIFY_INVALID_DKIM_VERSION:
aaaa94ea 249 logmsg = string_cat(logmsg, US"unsupported DKIM version]");
a79d8834 250 break;
1cfe5c1c
JH
251
252 default:
aaaa94ea 253 logmsg = string_cat(logmsg, US"unspecified problem]");
1cfe5c1c 254 }
80a47a2c 255 break;
1cfe5c1c
JH
256
257 case PDKIM_VERIFY_FAIL:
aaaa94ea 258 logmsg = string_cat(logmsg, US" [verification failed - ");
1cfe5c1c
JH
259 switch (sig->verify_ext_status)
260 {
261 case PDKIM_VERIFY_FAIL_BODY:
acec9514 262 logmsg = string_cat(logmsg,
aaaa94ea 263 US"body hash mismatch (body probably modified in transit)]");
1cfe5c1c
JH
264 break;
265
266 case PDKIM_VERIFY_FAIL_MESSAGE:
acec9514 267 logmsg = string_cat(logmsg,
aaaa94ea
JH
268 US"signature did not verify "
269 "(headers probably modified in transit)]");
dfbcb5ac 270 break;
1cfe5c1c 271
be24b950
JH
272 case PDKIM_VERIFY_INVALID_PUBKEY_KEYSIZE:
273 logmsg = string_cat(logmsg,
274 US"signature invalid (key too short)]");
275 break;
276
1cfe5c1c 277 default:
aaaa94ea 278 logmsg = string_cat(logmsg, US"unspecified reason]");
1cfe5c1c 279 }
80a47a2c 280 break;
1cfe5c1c
JH
281
282 case PDKIM_VERIFY_PASS:
aaaa94ea 283 logmsg = string_cat(logmsg, US" [verification succeeded]");
80a47a2c
TK
284 break;
285 }
a79d8834 286
aaaa94ea 287log_write(0, LOG_MAIN, "%s", string_from_gstring(logmsg));
a79d8834
JH
288return;
289}
290
80a47a2c 291
a79d8834
JH
292/* Log a line for each signature */
293void
294dkim_exim_verify_log_all(void)
295{
d7978c0f
JH
296for (pdkim_signature * sig = dkim_signatures; sig; sig = sig->next)
297 dkim_exim_verify_log_sig(sig);
a79d8834 298}
1cfe5c1c 299
80a47a2c 300
a79d8834
JH
301void
302dkim_exim_verify_finish(void)
303{
a79d8834
JH
304int rc;
305gstring * g = NULL;
286b9d5f 306const uschar * errstr = NULL;
a79d8834
JH
307
308store_pool = POOL_PERM;
309
310/* Delete eventual previous signature chain */
311
312dkim_signers = NULL;
313dkim_signatures = NULL;
314
315if (dkim_collect_error)
316 {
317 log_write(0, LOG_MAIN,
318 "DKIM: Error during validation, disabling signature verification: %.100s",
319 dkim_collect_error);
8768d548 320 f.dkim_disable_verify = TRUE;
a79d8834
JH
321 goto out;
322 }
323
64b67b65 324dkim_collect_input = 0;
a79d8834
JH
325
326/* Finish DKIM operation and fetch link to signatures chain */
327
b4757e36
JH
328rc = pdkim_feed_finish(dkim_verify_ctx, (pdkim_signature **)&dkim_signatures,
329 &errstr);
286b9d5f
JH
330if (rc != PDKIM_OK && errstr)
331 log_write(0, LOG_MAIN, "DKIM: validation error: %s", errstr);
a79d8834
JH
332
333/* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */
334
d7978c0f 335for (pdkim_signature * sig = dkim_signatures; sig; sig = sig->next)
a79d8834
JH
336 {
337 if (sig->domain) g = string_append_listele(g, ':', sig->domain);
338 if (sig->identity) g = string_append_listele(g, ':', sig->identity);
80a47a2c
TK
339 }
340
acec9514
JH
341if (g) dkim_signers = g->s;
342
ca9cb170
JH
343out:
344store_pool = dkim_verify_oldpool;
80a47a2c
TK
345}
346
347
18067c75
JH
348
349/* Args as per dkim_exim_acl_run() below */
350static int
351dkim_acl_call(uschar * id, gstring ** res_ptr,
352 uschar ** user_msgptr, uschar ** log_msgptr)
353{
354int rc;
355DEBUG(D_receive)
356 debug_printf("calling acl_smtp_dkim for dkim_cur_signer='%s'\n", id);
357
358rc = acl_check(ACL_WHERE_DKIM, NULL, acl_smtp_dkim, user_msgptr, log_msgptr);
359dkim_exim_verify_log_sig(dkim_cur_sig);
360*res_ptr = string_append_listele(*res_ptr, ':', dkim_verify_status);
361return rc;
362}
363
364
365
366/* For the given identity, run the DKIM ACL once for each matching signature.
367
368Arguments
369 id Identity to look for in dkim signatures
370 res_ptr ptr to growable string-list of status results,
371 appended to per ACL run
372 user_msgptr where to put a user error (for SMTP response)
373 log_msgptr where to put a logging message (not for SMTP response)
374
375Returns: OK access is granted by an ACCEPT verb
376 DISCARD access is granted by a DISCARD verb
377 FAIL access is denied
378 FAIL_DROP access is denied; drop the connection
379 DEFER can't tell at the moment
380 ERROR disaster
381*/
382
383int
384dkim_exim_acl_run(uschar * id, gstring ** res_ptr,
385 uschar ** user_msgptr, uschar ** log_msgptr)
1cfe5c1c 386{
1cfe5c1c 387uschar * cmp_val;
18067c75 388int rc = -1;
1cfe5c1c 389
cc55f420
JH
390dkim_verify_status = US"none";
391dkim_verify_reason = US"";
1cfe5c1c
JH
392dkim_cur_signer = id;
393
8768d548 394if (f.dkim_disable_verify || !id || !dkim_verify_ctx)
18067c75 395 return OK;
1cfe5c1c 396
18067c75 397/* Find signatures to run ACL on */
1cfe5c1c 398
d7978c0f 399for (pdkim_signature * sig = dkim_signatures; sig; sig = sig->next)
1cfe5c1c
JH
400 if ( (cmp_val = Ustrchr(id, '@') != NULL ? US sig->identity : US sig->domain)
401 && strcmpic(cmp_val, id) == 0
402 )
403 {
1cfe5c1c 404 /* The "dkim_domain" and "dkim_selector" expansion variables have
18067c75
JH
405 related globals, since they are used in the signing code too.
406 Instead of inventing separate names for verification, we set
407 them here. This is easy since a domain and selector is guaranteed
408 to be in a signature. The other dkim_* expansion items are
409 dynamically fetched from dkim_cur_sig at expansion time (see
b4757e36 410 dkim_exim_expand_query() below). */
1cfe5c1c 411
18067c75 412 dkim_cur_sig = sig;
1cfe5c1c
JH
413 dkim_signing_domain = US sig->domain;
414 dkim_signing_selector = US sig->selector;
a841a6ec 415 dkim_key_length = sig->keybits;
a79d8834
JH
416
417 /* These two return static strings, so we can compare the addr
418 later to see if the ACL overwrote them. Check that when logging */
419
420 dkim_verify_status = dkim_exim_expand_query(DKIM_VERIFY_STATUS);
421 dkim_verify_reason = dkim_exim_expand_query(DKIM_VERIFY_REASON);
dd33c4e6 422
18067c75
JH
423 if ((rc = dkim_acl_call(id, res_ptr, user_msgptr, log_msgptr)) != OK)
424 return rc;
80a47a2c 425 }
18067c75
JH
426
427if (rc != -1)
428 return rc;
429
430/* No matching sig found. Call ACL once anyway. */
431
432dkim_cur_sig = NULL;
433return dkim_acl_call(id, res_ptr, user_msgptr, log_msgptr);
80a47a2c
TK
434}
435
436
1cfe5c1c
JH
437static uschar *
438dkim_exim_expand_defaults(int what)
439{
440switch (what)
441 {
442 case DKIM_ALGO: return US"";
443 case DKIM_BODYLENGTH: return US"9999999999999";
444 case DKIM_CANON_BODY: return US"";
445 case DKIM_CANON_HEADERS: return US"";
446 case DKIM_COPIEDHEADERS: return US"";
447 case DKIM_CREATED: return US"0";
448 case DKIM_EXPIRES: return US"9999999999999";
449 case DKIM_HEADERNAMES: return US"";
450 case DKIM_IDENTITY: return US"";
451 case DKIM_KEY_GRANULARITY: return US"*";
452 case DKIM_KEY_SRVTYPE: return US"*";
453 case DKIM_KEY_NOTES: return US"";
454 case DKIM_KEY_TESTING: return US"0";
455 case DKIM_NOSUBDOMAINS: return US"0";
456 case DKIM_VERIFY_STATUS: return US"none";
457 case DKIM_VERIFY_REASON: return US"";
458 default: return US"";
459 }
460}
80a47a2c 461
80a47a2c 462
1cfe5c1c
JH
463uschar *
464dkim_exim_expand_query(int what)
465{
8768d548 466if (!dkim_verify_ctx || f.dkim_disable_verify || !dkim_cur_sig)
1cfe5c1c
JH
467 return dkim_exim_expand_defaults(what);
468
469switch (what)
470 {
471 case DKIM_ALGO:
d73e45df 472 return dkim_sig_to_a_tag(dkim_cur_sig);
1cfe5c1c
JH
473
474 case DKIM_BODYLENGTH:
475 return dkim_cur_sig->bodylength >= 0
bb07bcd3 476 ? string_sprintf("%ld", dkim_cur_sig->bodylength)
1cfe5c1c
JH
477 : dkim_exim_expand_defaults(what);
478
479 case DKIM_CANON_BODY:
480 switch (dkim_cur_sig->canon_body)
481 {
482 case PDKIM_CANON_RELAXED: return US"relaxed";
483 case PDKIM_CANON_SIMPLE:
484 default: return US"simple";
80a47a2c 485 }
1cfe5c1c
JH
486
487 case DKIM_CANON_HEADERS:
a79d8834
JH
488 switch (dkim_cur_sig->canon_headers)
489 {
490 case PDKIM_CANON_RELAXED: return US"relaxed";
491 case PDKIM_CANON_SIMPLE:
492 default: return US"simple";
493 }
1cfe5c1c
JH
494
495 case DKIM_COPIEDHEADERS:
496 return dkim_cur_sig->copiedheaders
497 ? US dkim_cur_sig->copiedheaders : dkim_exim_expand_defaults(what);
498
499 case DKIM_CREATED:
500 return dkim_cur_sig->created > 0
bb07bcd3 501 ? string_sprintf("%lu", dkim_cur_sig->created)
1cfe5c1c
JH
502 : dkim_exim_expand_defaults(what);
503
504 case DKIM_EXPIRES:
505 return dkim_cur_sig->expires > 0
bb07bcd3 506 ? string_sprintf("%lu", dkim_cur_sig->expires)
1cfe5c1c
JH
507 : dkim_exim_expand_defaults(what);
508
509 case DKIM_HEADERNAMES:
510 return dkim_cur_sig->headernames
2592e6c0 511 ? dkim_cur_sig->headernames : dkim_exim_expand_defaults(what);
1cfe5c1c
JH
512
513 case DKIM_IDENTITY:
514 return dkim_cur_sig->identity
515 ? US dkim_cur_sig->identity : dkim_exim_expand_defaults(what);
516
517 case DKIM_KEY_GRANULARITY:
518 return dkim_cur_sig->pubkey
519 ? dkim_cur_sig->pubkey->granularity
520 ? US dkim_cur_sig->pubkey->granularity
521 : dkim_exim_expand_defaults(what)
522 : dkim_exim_expand_defaults(what);
523
524 case DKIM_KEY_SRVTYPE:
525 return dkim_cur_sig->pubkey
526 ? dkim_cur_sig->pubkey->srvtype
527 ? US dkim_cur_sig->pubkey->srvtype
528 : dkim_exim_expand_defaults(what)
529 : dkim_exim_expand_defaults(what);
530
531 case DKIM_KEY_NOTES:
532 return dkim_cur_sig->pubkey
533 ? dkim_cur_sig->pubkey->notes
534 ? US dkim_cur_sig->pubkey->notes
535 : dkim_exim_expand_defaults(what)
536 : dkim_exim_expand_defaults(what);
537
538 case DKIM_KEY_TESTING:
539 return dkim_cur_sig->pubkey
540 ? dkim_cur_sig->pubkey->testing
541 ? US"1"
542 : dkim_exim_expand_defaults(what)
543 : dkim_exim_expand_defaults(what);
544
545 case DKIM_NOSUBDOMAINS:
546 return dkim_cur_sig->pubkey
547 ? dkim_cur_sig->pubkey->no_subdomaining
548 ? US"1"
549 : dkim_exim_expand_defaults(what)
550 : dkim_exim_expand_defaults(what);
551
552 case DKIM_VERIFY_STATUS:
553 switch (dkim_cur_sig->verify_status)
554 {
555 case PDKIM_VERIFY_INVALID: return US"invalid";
556 case PDKIM_VERIFY_FAIL: return US"fail";
557 case PDKIM_VERIFY_PASS: return US"pass";
558 case PDKIM_VERIFY_NONE:
559 default: return US"none";
80a47a2c 560 }
80a47a2c 561
1cfe5c1c
JH
562 case DKIM_VERIFY_REASON:
563 switch (dkim_cur_sig->verify_ext_status)
564 {
565 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
566 return US"pubkey_unavailable";
df3def24
JH
567 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:return US"pubkey_dns_syntax";
568 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT: return US"pubkey_der_syntax";
be24b950 569 case PDKIM_VERIFY_INVALID_PUBKEY_KEYSIZE: return US"pubkey_too_short";
1cfe5c1c
JH
570 case PDKIM_VERIFY_FAIL_BODY: return US"bodyhash_mismatch";
571 case PDKIM_VERIFY_FAIL_MESSAGE: return US"signature_incorrect";
572 }
80a47a2c 573
1cfe5c1c
JH
574 default:
575 return US"";
80a47a2c
TK
576 }
577}
578
579
617d3932
JH
580void
581dkim_exim_sign_init(void)
582{
583int old_pool = store_pool;
d85cdeb5
JH
584
585dkim_exim_init();
617d3932
JH
586store_pool = POOL_MAIN;
587pdkim_init_context(&dkim_sign_ctx, FALSE, &dkim_exim_query_dns_txt);
588store_pool = old_pool;
589}
590
591
0b8f4f1a 592/* Generate signatures for the given file.
42055a33 593If a prefix is given, prepend it to the file for the calculations.
0b8f4f1a
JH
594
595Return:
596 NULL: error; error string written
597 string: signature header(s), or a zero-length string (not an error)
42055a33
JH
598*/
599
acec9514 600gstring *
42055a33
JH
601dkim_exim_sign(int fd, off_t off, uschar * prefix,
602 struct ob_dkim * dkim, const uschar ** errstr)
55414b25 603{
617d3932 604const uschar * dkim_domain = NULL;
1cfe5c1c 605int sep = 0;
acec9514 606gstring * seen_doms = NULL;
9e70917d 607pdkim_signature * sig;
acec9514 608gstring * sigbuf;
1cfe5c1c
JH
609int pdkim_rc;
610int sread;
ef698bf6 611uschar buf[4096];
1cfe5c1c
JH
612int save_errno = 0;
613int old_pool = store_pool;
7c6ec81b 614uschar * errwhen;
617d3932 615const uschar * s;
1cfe5c1c 616
617d3932
JH
617if (dkim->dot_stuffed)
618 dkim_sign_ctx.flags |= PDKIM_DOT_TERM;
1cfe5c1c 619
617d3932 620store_pool = POOL_MAIN;
9e70917d 621
617d3932 622if ((s = dkim->dkim_domain) && !(dkim_domain = expand_cstring(s)))
1cfe5c1c 623 /* expansion error, do not send message. */
7c6ec81b 624 { errwhen = US"dkim_domain"; goto expand_bad; }
1cfe5c1c
JH
625
626/* Set $dkim_domain expansion variable to each unique domain in list. */
627
617d3932
JH
628if (dkim_domain)
629 while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep, NULL, 0)))
1cfe5c1c 630 {
9e70917d
JH
631 const uschar * dkim_sel;
632 int sel_sep = 0;
633
42055a33 634 if (dkim_signing_domain[0] == '\0')
1cfe5c1c
JH
635 continue;
636
637 /* Only sign once for each domain, no matter how often it
638 appears in the expanded list. */
639
fe12ec88 640 dkim_signing_domain = string_copylc(dkim_signing_domain);
9e70917d
JH
641 if (match_isinlist(dkim_signing_domain, CUSS &seen_doms,
642 0, NULL, NULL, MCL_STRING, TRUE, NULL) == OK)
643 continue;
1cfe5c1c 644
acec9514 645 seen_doms = string_append_listele(seen_doms, ':', dkim_signing_domain);
1cfe5c1c 646
9e70917d
JH
647 /* Set $dkim_selector expansion variable to each selector in list,
648 for this domain. */
1cfe5c1c 649
9e70917d 650 if (!(dkim_sel = expand_string(dkim->dkim_selector)))
7c6ec81b 651 { errwhen = US"dkim_selector"; goto expand_bad; }
1cfe5c1c 652
9e70917d
JH
653 while ((dkim_signing_selector = string_nextinlist(&dkim_sel, &sel_sep,
654 NULL, 0)))
1cfe5c1c 655 {
9e70917d
JH
656 uschar * dkim_canon_expanded;
657 int pdkim_canon;
658 uschar * dkim_sign_headers_expanded = NULL;
659 uschar * dkim_private_key_expanded;
660 uschar * dkim_hash_expanded;
7c6ec81b 661 uschar * dkim_identity_expanded = NULL;
2bc0f45e
JH
662 uschar * dkim_timestamps_expanded = NULL;
663 unsigned long tval = 0, xval = 0;
80a47a2c 664
9e70917d 665 /* Get canonicalization to use */
1cfe5c1c 666
9e70917d
JH
667 dkim_canon_expanded = dkim->dkim_canon
668 ? expand_string(dkim->dkim_canon) : US"relaxed";
7c6ec81b
JH
669 if (!dkim_canon_expanded) /* expansion error, do not send message. */
670 { errwhen = US"dkim_canon"; goto expand_bad; }
1cfe5c1c 671
9e70917d
JH
672 if (Ustrcmp(dkim_canon_expanded, "relaxed") == 0)
673 pdkim_canon = PDKIM_CANON_RELAXED;
674 else if (Ustrcmp(dkim_canon_expanded, "simple") == 0)
675 pdkim_canon = PDKIM_CANON_SIMPLE;
676 else
677 {
678 log_write(0, LOG_MAIN,
679 "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",
680 dkim_canon_expanded);
681 pdkim_canon = PDKIM_CANON_RELAXED;
682 }
1cfe5c1c 683
7c6ec81b
JH
684 if ( dkim->dkim_sign_headers
685 && !(dkim_sign_headers_expanded = expand_string(dkim->dkim_sign_headers)))
686 { errwhen = US"dkim_sign_header"; goto expand_bad; }
687 /* else pass NULL, which means default header list */
1cfe5c1c 688
9e70917d 689 /* Get private key to use. */
e983e85a 690
9e70917d 691 if (!(dkim_private_key_expanded = expand_string(dkim->dkim_private_key)))
7c6ec81b 692 { errwhen = US"dkim_private_key"; goto expand_bad; }
80a47a2c 693
9e70917d
JH
694 if ( Ustrlen(dkim_private_key_expanded) == 0
695 || Ustrcmp(dkim_private_key_expanded, "0") == 0
696 || Ustrcmp(dkim_private_key_expanded, "false") == 0
697 )
698 continue; /* don't sign, but no error */
699
617d3932
JH
700 if ( dkim_private_key_expanded[0] == '/'
701 && !(dkim_private_key_expanded =
702 expand_file_big_buffer(dkim_private_key_expanded)))
703 goto bad;
80a47a2c 704
9e70917d 705 if (!(dkim_hash_expanded = expand_string(dkim->dkim_hash)))
7c6ec81b
JH
706 { errwhen = US"dkim_hash"; goto expand_bad; }
707
708 if (dkim->dkim_identity)
709 if (!(dkim_identity_expanded = expand_string(dkim->dkim_identity)))
710 { errwhen = US"dkim_identity"; goto expand_bad; }
711 else if (!*dkim_identity_expanded)
712 dkim_identity_expanded = NULL;
1cfe5c1c 713
2bc0f45e
JH
714 if (dkim->dkim_timestamps)
715 if (!(dkim_timestamps_expanded = expand_string(dkim->dkim_timestamps)))
716 { errwhen = US"dkim_timestamps"; goto expand_bad; }
717 else
718 xval = (tval = (unsigned long) time(NULL))
48334568 719 + strtoul(CCS dkim_timestamps_expanded, NULL, 10);
2bc0f45e 720
617d3932 721 if (!(sig = pdkim_init_sign(&dkim_sign_ctx, dkim_signing_domain,
9e70917d
JH
722 dkim_signing_selector,
723 dkim_private_key_expanded,
724 dkim_hash_expanded,
725 errstr
726 )))
727 goto bad;
728 dkim_private_key_expanded[0] = '\0';
729
730 pdkim_set_optional(sig,
731 CS dkim_sign_headers_expanded,
27fd1318 732 CS dkim_identity_expanded,
9e70917d 733 pdkim_canon,
2bc0f45e 734 pdkim_canon, -1, tval, xval);
9e70917d 735
617d3932 736 if (!pdkim_set_sig_bodyhash(&dkim_sign_ctx, sig))
cf1cce5e
JH
737 goto bad;
738
617d3932
JH
739 if (!dkim_sign_ctx.sig) /* link sig to context chain */
740 dkim_sign_ctx.sig = sig;
9e70917d
JH
741 else
742 {
617d3932 743 pdkim_signature * n = dkim_sign_ctx.sig;
9e70917d
JH
744 while (n->next) n = n->next;
745 n->next = sig;
746 }
80a47a2c 747 }
9e70917d 748 }
617d3932
JH
749
750/* We may need to carry on with the data-feed even if there are no DKIM sigs to
751produce, if some other package (eg. ARC) is signing. */
752
753if (!dkim_sign_ctx.sig && !dkim->force_bodyhash)
0b8f4f1a
JH
754 {
755 DEBUG(D_transport) debug_printf("DKIM: no viable signatures to use\n");
756 sigbuf = string_get(1); /* return a zero-len string */
0b8f4f1a 757 }
9e70917d 758else
9e70917d 759 {
617d3932
JH
760 if (prefix && (pdkim_rc = pdkim_feed(&dkim_sign_ctx, prefix, Ustrlen(prefix))) != PDKIM_OK)
761 goto pk_bad;
a8e1eeba 762
617d3932
JH
763 if (lseek(fd, off, SEEK_SET) < 0)
764 sread = -1;
765 else
766 while ((sread = read(fd, &buf, sizeof(buf))) > 0)
767 if ((pdkim_rc = pdkim_feed(&dkim_sign_ctx, buf, sread)) != PDKIM_OK)
768 goto pk_bad;
9e70917d 769
617d3932
JH
770 /* Handle failed read above. */
771 if (sread == -1)
772 {
773 debug_printf("DKIM: Error reading -K file.\n");
774 save_errno = errno;
775 goto bad;
776 }
777
778 /* Build string of headers, one per signature */
9e70917d 779
617d3932
JH
780 if ((pdkim_rc = pdkim_feed_finish(&dkim_sign_ctx, &sig, errstr)) != PDKIM_OK)
781 goto pk_bad;
782
783 if (!sig)
784 {
785 DEBUG(D_transport) debug_printf("DKIM: no signatures to use\n");
786 sigbuf = string_get(1); /* return a zero-len string */
787 }
788 else for (sigbuf = NULL; sig; sig = sig->next)
789 sigbuf = string_append(sigbuf, 2, US sig->signature_header, US"\r\n");
790 }
9e70917d 791
1cfe5c1c 792CLEANUP:
0b8f4f1a 793 (void) string_from_gstring(sigbuf);
f7302073
JH
794 store_pool = old_pool;
795 errno = save_errno;
9e70917d 796 return sigbuf;
f7302073
JH
797
798pk_bad:
799 log_write(0, LOG_MAIN|LOG_PANIC,
9fa4d5b4 800 "DKIM: signing failed: %.100s", pdkim_errstr(pdkim_rc));
f7302073 801bad:
9e70917d 802 sigbuf = NULL;
f7302073 803 goto CLEANUP;
7c6ec81b
JH
804
805expand_bad:
9fa4d5b4
RJ
806 *errstr = string_sprintf("failed to expand %s: %s",
807 errwhen, expand_string_message);
808 log_write(0, LOG_MAIN | LOG_PANIC, "%s", *errstr);
7c6ec81b 809 goto bad;
93f2d376 810}
80a47a2c 811
dfbcb5ac
JH
812
813
814
815gstring *
816authres_dkim(gstring * g)
817{
0ae2cff6 818int start = 0; /* compiler quietening */
617d3932
JH
819
820DEBUG(D_acl) start = g->ptr;
dfbcb5ac 821
d7978c0f 822for (pdkim_signature * sig = dkim_signatures; sig; sig = sig->next)
dfbcb5ac 823 {
e34f8ca2 824 g = string_catn(g, US";\n\tdkim=", 8);
dfbcb5ac
JH
825
826 if (sig->verify_status & PDKIM_VERIFY_POLICY)
827 g = string_append(g, 5,
828 US"policy (", dkim_verify_status, US" - ", dkim_verify_reason, US")");
829 else switch(sig->verify_status)
830 {
831 case PDKIM_VERIFY_NONE: g = string_cat(g, US"none"); break;
832 case PDKIM_VERIFY_INVALID:
833 switch (sig->verify_ext_status)
834 {
835 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
617d3932 836 g = string_cat(g, US"tmperror (pubkey unavailable)\n\t\t"); break;
dfbcb5ac 837 case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
617d3932 838 g = string_cat(g, US"permerror (overlong public key record)\n\t\t"); break;
dfbcb5ac
JH
839 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:
840 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT:
0e8aed8a 841 g = string_cat(g, US"neutral (public key record import problem)\n\t\t");
dfbcb5ac
JH
842 break;
843 case PDKIM_VERIFY_INVALID_SIGNATURE_ERROR:
617d3932 844 g = string_cat(g, US"neutral (signature tag missing or invalid)\n\t\t");
dfbcb5ac
JH
845 break;
846 case PDKIM_VERIFY_INVALID_DKIM_VERSION:
617d3932 847 g = string_cat(g, US"neutral (unsupported DKIM version)\n\t\t");
dfbcb5ac
JH
848 break;
849 default:
617d3932 850 g = string_cat(g, US"permerror (unspecified problem)\n\t\t"); break;
dfbcb5ac
JH
851 }
852 break;
853 case PDKIM_VERIFY_FAIL:
854 switch (sig->verify_ext_status)
855 {
856 case PDKIM_VERIFY_FAIL_BODY:
857 g = string_cat(g,
617d3932 858 US"fail (body hash mismatch; body probably modified in transit)\n\t\t");
dfbcb5ac
JH
859 break;
860 case PDKIM_VERIFY_FAIL_MESSAGE:
861 g = string_cat(g,
617d3932 862 US"fail (signature did not verify; headers probably modified in transit)\n\t\t");
dfbcb5ac 863 break;
be24b950
JH
864 case PDKIM_VERIFY_INVALID_PUBKEY_KEYSIZE: /* should this really be "polcy"? */
865 g = string_fmt_append(g, "fail (public key too short: %u bits)\n\t\t", sig->keybits);
866 break;
dfbcb5ac 867 default:
617d3932 868 g = string_cat(g, US"fail (unspecified reason)\n\t\t");
dfbcb5ac
JH
869 break;
870 }
871 break;
872 case PDKIM_VERIFY_PASS: g = string_cat(g, US"pass"); break;
873 default: g = string_cat(g, US"permerror"); break;
874 }
875 if (sig->domain) g = string_append(g, 2, US" header.d=", sig->domain);
876 if (sig->identity) g = string_append(g, 2, US" header.i=", sig->identity);
877 if (sig->selector) g = string_append(g, 2, US" header.s=", sig->selector);
878 g = string_append(g, 2, US" header.a=", dkim_sig_to_a_tag(sig));
879 }
617d3932
JH
880
881DEBUG(D_acl)
882 if (g->ptr == start)
883 debug_printf("DKIM: no authres\n");
884 else
885 debug_printf("DKIM: authres '%.*s'\n", g->ptr - start - 3, g->s + start + 3);
dfbcb5ac
JH
886return g;
887}
888
889
970424a5
JH
890# endif /*!MACRO_PREDEF*/
891#endif /*!DISABLE_DKIM*/