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