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