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