1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) University of Cambridge, 1995 - 2018 */
6 /* See the file NOTICE for conditions of use and distribution. */
8 /* Code for DKIM support. Other DKIM relevant code is in
9 receive.c, transport.c and transports/smtp.c */
15 # include "pdkim/pdkim.h"
18 # include "macro_predef.h"
23 builtin_macro_create_var(US"_DKIM_SIGN_HEADERS", US PDKIM_DEFAULT_SIGN_HEADERS);
24 builtin_macro_create_var(US"_DKIM_OVERSIGN_HEADERS", US PDKIM_OVERSIGN_HEADERS);
26 # else /*!MACRO_PREDEF*/
30 pdkim_ctx dkim_sign_ctx;
32 int dkim_verify_oldpool;
33 pdkim_ctx *dkim_verify_ctx = NULL;
34 pdkim_signature *dkim_cur_sig = NULL;
35 static const uschar * dkim_collect_error = NULL;
37 #define DKIM_MAX_SIGNATURES 20
41 /* Look up the DKIM record in DNS for the given hostname.
42 Will use the first found if there are multiple.
43 The return string is tainted, having come from off-site.
47 dkim_exim_query_dns_txt(const uschar * name)
49 dns_answer * dnsa = store_get_dns_answer();
51 rmark reset_point = store_mark();
54 lookup_dnssec_authenticated = NULL;
55 if (dns_lookup(dnsa, name, T_TXT, NULL) != DNS_SUCCEED)
56 return NULL; /*XXX better error detail? logging? */
58 /* Search for TXT record */
60 for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS);
62 rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
63 if (rr->type == T_TXT)
67 /* Copy record content to the answer buffer */
69 while (rr_offset < rr->size)
71 uschar len = rr->data[rr_offset++];
73 g = string_catn(g, US(rr->data + rr_offset), len);
74 if (g->ptr >= PDKIM_DNS_TXT_MAX_RECLEN)
80 /* check if this looks like a DKIM record */
81 if (Ustrncmp(g->s, "v=", 2) != 0 || strncasecmp(CS g->s, "v=dkim", 6) == 0)
83 gstring_release_unused(g);
84 return string_from_gstring(g);
87 if (g) g->ptr = 0; /* overwrite previous record */
91 store_reset(reset_point);
92 return NULL; /*XXX better error detail? logging? */
99 if (f.dkim_init_done) return;
100 f.dkim_init_done = TRUE;
107 dkim_exim_verify_init(BOOL dot_stuffing)
111 /* There is a store-reset between header & body reception
112 so cannot use the main pool. Any allocs done by Exim
113 memory-handling must use the perm pool. */
115 dkim_verify_oldpool = store_pool;
116 store_pool = POOL_PERM;
118 /* Free previous context if there is one */
121 pdkim_free_ctx(dkim_verify_ctx);
123 /* Create new context */
125 dkim_verify_ctx = pdkim_init_verify(&dkim_exim_query_dns_txt, dot_stuffing);
126 dkim_collect_input = dkim_verify_ctx ? DKIM_MAX_SIGNATURES : 0;
127 dkim_collect_error = NULL;
129 /* Start feed up with any cached data */
132 store_pool = dkim_verify_oldpool;
137 dkim_exim_verify_feed(uschar * data, int len)
141 store_pool = POOL_PERM;
142 if ( dkim_collect_input
143 && (rc = pdkim_feed(dkim_verify_ctx, data, len)) != PDKIM_OK)
145 dkim_collect_error = pdkim_errstr(rc);
146 log_write(0, LOG_MAIN,
147 "DKIM: validation error: %.100s", dkim_collect_error);
148 dkim_collect_input = 0;
150 store_pool = dkim_verify_oldpool;
154 /* Log the result for the given signature */
156 dkim_exim_verify_log_sig(pdkim_signature * sig)
163 /* Remember the domain for the first pass result */
165 if ( !dkim_verify_overall
166 && dkim_verify_status
167 ? Ustrcmp(dkim_verify_status, US"pass") == 0
168 : sig->verify_status == PDKIM_VERIFY_PASS
170 dkim_verify_overall = string_copy(sig->domain);
172 /* Rewrite the sig result if the ACL overrode it. This is only
173 needed because the DMARC code (sigh) peeks at the dkim sigs.
174 Mark the sig for this having been done. */
176 if ( dkim_verify_status
177 && ( dkim_verify_status != dkim_exim_expand_query(DKIM_VERIFY_STATUS)
178 || dkim_verify_reason != dkim_exim_expand_query(DKIM_VERIFY_REASON)
180 { /* overridden by ACL */
181 sig->verify_ext_status = -1;
182 if (Ustrcmp(dkim_verify_status, US"fail") == 0)
183 sig->verify_status = PDKIM_VERIFY_POLICY | PDKIM_VERIFY_FAIL;
184 else if (Ustrcmp(dkim_verify_status, US"invalid") == 0)
185 sig->verify_status = PDKIM_VERIFY_POLICY | PDKIM_VERIFY_INVALID;
186 else if (Ustrcmp(dkim_verify_status, US"none") == 0)
187 sig->verify_status = PDKIM_VERIFY_POLICY | PDKIM_VERIFY_NONE;
188 else if (Ustrcmp(dkim_verify_status, US"pass") == 0)
189 sig->verify_status = PDKIM_VERIFY_POLICY | PDKIM_VERIFY_PASS;
191 sig->verify_status = -1;
194 if (!LOGGING(dkim_verbose)) return;
197 logmsg = string_catn(NULL, US"DKIM: ", 6);
198 if (!(s = sig->domain)) s = US"<UNSET>";
199 logmsg = string_append(logmsg, 2, "d=", s);
200 if (!(s = sig->selector)) s = US"<UNSET>";
201 logmsg = string_append(logmsg, 2, " s=", s);
202 logmsg = string_fmt_append(logmsg, " c=%s/%s a=%s b=" SIZE_T_FMT,
203 sig->canon_headers == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
204 sig->canon_body == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
205 dkim_sig_to_a_tag(sig),
206 (int)sig->sighash.len > -1 ? sig->sighash.len * 8 : (size_t)0);
207 if ((s= sig->identity)) logmsg = string_append(logmsg, 2, " i=", s);
208 if (sig->created > 0) logmsg = string_fmt_append(logmsg, " t=%lu",
210 if (sig->expires > 0) logmsg = string_fmt_append(logmsg, " x=%lu",
212 if (sig->bodylength > -1) logmsg = string_fmt_append(logmsg, " l=%lu",
215 if (sig->verify_status & PDKIM_VERIFY_POLICY)
216 logmsg = string_append(logmsg, 5,
217 US" [", dkim_verify_status, US" - ", dkim_verify_reason, US"]");
219 switch (sig->verify_status)
221 case PDKIM_VERIFY_NONE:
222 logmsg = string_cat(logmsg, US" [not verified]");
225 case PDKIM_VERIFY_INVALID:
226 logmsg = string_cat(logmsg, US" [invalid - ");
227 switch (sig->verify_ext_status)
229 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
230 logmsg = string_cat(logmsg,
231 US"public key record (currently?) unavailable]");
234 case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
235 logmsg = string_cat(logmsg, US"overlong public key record]");
238 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:
239 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT:
240 logmsg = string_cat(logmsg, US"syntax error in public key record]");
243 case PDKIM_VERIFY_INVALID_SIGNATURE_ERROR:
244 logmsg = string_cat(logmsg, US"signature tag missing or invalid]");
247 case PDKIM_VERIFY_INVALID_DKIM_VERSION:
248 logmsg = string_cat(logmsg, US"unsupported DKIM version]");
252 logmsg = string_cat(logmsg, US"unspecified problem]");
256 case PDKIM_VERIFY_FAIL:
257 logmsg = string_cat(logmsg, US" [verification failed - ");
258 switch (sig->verify_ext_status)
260 case PDKIM_VERIFY_FAIL_BODY:
261 logmsg = string_cat(logmsg,
262 US"body hash mismatch (body probably modified in transit)]");
265 case PDKIM_VERIFY_FAIL_MESSAGE:
266 logmsg = string_cat(logmsg,
267 US"signature did not verify "
268 "(headers probably modified in transit)]");
272 logmsg = string_cat(logmsg, US"unspecified reason]");
276 case PDKIM_VERIFY_PASS:
277 logmsg = string_cat(logmsg, US" [verification succeeded]");
281 log_write(0, LOG_MAIN, "%s", string_from_gstring(logmsg));
286 /* Log a line for each signature */
288 dkim_exim_verify_log_all(void)
290 for (pdkim_signature * sig = dkim_signatures; sig; sig = sig->next)
291 dkim_exim_verify_log_sig(sig);
296 dkim_exim_verify_finish(void)
300 const uschar * errstr = NULL;
302 store_pool = POOL_PERM;
304 /* Delete eventual previous signature chain */
307 dkim_signatures = NULL;
309 if (dkim_collect_error)
311 log_write(0, LOG_MAIN,
312 "DKIM: Error during validation, disabling signature verification: %.100s",
314 f.dkim_disable_verify = TRUE;
318 dkim_collect_input = 0;
320 /* Finish DKIM operation and fetch link to signatures chain */
322 rc = pdkim_feed_finish(dkim_verify_ctx, (pdkim_signature **)&dkim_signatures,
324 if (rc != PDKIM_OK && errstr)
325 log_write(0, LOG_MAIN, "DKIM: validation error: %s", errstr);
327 /* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */
329 for (pdkim_signature * sig = dkim_signatures; sig; sig = sig->next)
331 if (sig->domain) g = string_append_listele(g, ':', sig->domain);
332 if (sig->identity) g = string_append_listele(g, ':', sig->identity);
335 if (g) dkim_signers = g->s;
338 store_pool = dkim_verify_oldpool;
343 /* Args as per dkim_exim_acl_run() below */
345 dkim_acl_call(uschar * id, gstring ** res_ptr,
346 uschar ** user_msgptr, uschar ** log_msgptr)
350 debug_printf("calling acl_smtp_dkim for dkim_cur_signer='%s'\n", id);
352 rc = acl_check(ACL_WHERE_DKIM, NULL, acl_smtp_dkim, user_msgptr, log_msgptr);
353 dkim_exim_verify_log_sig(dkim_cur_sig);
354 *res_ptr = string_append_listele(*res_ptr, ':', dkim_verify_status);
360 /* For the given identity, run the DKIM ACL once for each matching signature.
363 id Identity to look for in dkim signatures
364 res_ptr ptr to growable string-list of status results,
365 appended to per ACL run
366 user_msgptr where to put a user error (for SMTP response)
367 log_msgptr where to put a logging message (not for SMTP response)
369 Returns: OK access is granted by an ACCEPT verb
370 DISCARD access is granted by a DISCARD verb
371 FAIL access is denied
372 FAIL_DROP access is denied; drop the connection
373 DEFER can't tell at the moment
378 dkim_exim_acl_run(uschar * id, gstring ** res_ptr,
379 uschar ** user_msgptr, uschar ** log_msgptr)
384 dkim_verify_status = US"none";
385 dkim_verify_reason = US"";
386 dkim_cur_signer = id;
388 if (f.dkim_disable_verify || !id || !dkim_verify_ctx)
391 /* Find signatures to run ACL on */
393 for (pdkim_signature * sig = dkim_signatures; sig; sig = sig->next)
394 if ( (cmp_val = Ustrchr(id, '@') != NULL ? US sig->identity : US sig->domain)
395 && strcmpic(cmp_val, id) == 0
398 /* The "dkim_domain" and "dkim_selector" expansion variables have
399 related globals, since they are used in the signing code too.
400 Instead of inventing separate names for verification, we set
401 them here. This is easy since a domain and selector is guaranteed
402 to be in a signature. The other dkim_* expansion items are
403 dynamically fetched from dkim_cur_sig at expansion time (see
404 dkim_exim_expand_query() below). */
407 dkim_signing_domain = US sig->domain;
408 dkim_signing_selector = US sig->selector;
409 dkim_key_length = sig->keybits;
411 /* These two return static strings, so we can compare the addr
412 later to see if the ACL overwrote them. Check that when logging */
414 dkim_verify_status = dkim_exim_expand_query(DKIM_VERIFY_STATUS);
415 dkim_verify_reason = dkim_exim_expand_query(DKIM_VERIFY_REASON);
417 if ((rc = dkim_acl_call(id, res_ptr, user_msgptr, log_msgptr)) != OK)
424 /* No matching sig found. Call ACL once anyway. */
427 return dkim_acl_call(id, res_ptr, user_msgptr, log_msgptr);
432 dkim_exim_expand_defaults(int what)
436 case DKIM_ALGO: return US"";
437 case DKIM_BODYLENGTH: return US"9999999999999";
438 case DKIM_CANON_BODY: return US"";
439 case DKIM_CANON_HEADERS: return US"";
440 case DKIM_COPIEDHEADERS: return US"";
441 case DKIM_CREATED: return US"0";
442 case DKIM_EXPIRES: return US"9999999999999";
443 case DKIM_HEADERNAMES: return US"";
444 case DKIM_IDENTITY: return US"";
445 case DKIM_KEY_GRANULARITY: return US"*";
446 case DKIM_KEY_SRVTYPE: return US"*";
447 case DKIM_KEY_NOTES: return US"";
448 case DKIM_KEY_TESTING: return US"0";
449 case DKIM_NOSUBDOMAINS: return US"0";
450 case DKIM_VERIFY_STATUS: return US"none";
451 case DKIM_VERIFY_REASON: return US"";
452 default: return US"";
458 dkim_exim_expand_query(int what)
460 if (!dkim_verify_ctx || f.dkim_disable_verify || !dkim_cur_sig)
461 return dkim_exim_expand_defaults(what);
466 return dkim_sig_to_a_tag(dkim_cur_sig);
468 case DKIM_BODYLENGTH:
469 return dkim_cur_sig->bodylength >= 0
470 ? string_sprintf("%ld", dkim_cur_sig->bodylength)
471 : dkim_exim_expand_defaults(what);
473 case DKIM_CANON_BODY:
474 switch (dkim_cur_sig->canon_body)
476 case PDKIM_CANON_RELAXED: return US"relaxed";
477 case PDKIM_CANON_SIMPLE:
478 default: return US"simple";
481 case DKIM_CANON_HEADERS:
482 switch (dkim_cur_sig->canon_headers)
484 case PDKIM_CANON_RELAXED: return US"relaxed";
485 case PDKIM_CANON_SIMPLE:
486 default: return US"simple";
489 case DKIM_COPIEDHEADERS:
490 return dkim_cur_sig->copiedheaders
491 ? US dkim_cur_sig->copiedheaders : dkim_exim_expand_defaults(what);
494 return dkim_cur_sig->created > 0
495 ? string_sprintf("%lu", dkim_cur_sig->created)
496 : dkim_exim_expand_defaults(what);
499 return dkim_cur_sig->expires > 0
500 ? string_sprintf("%lu", dkim_cur_sig->expires)
501 : dkim_exim_expand_defaults(what);
503 case DKIM_HEADERNAMES:
504 return dkim_cur_sig->headernames
505 ? dkim_cur_sig->headernames : dkim_exim_expand_defaults(what);
508 return dkim_cur_sig->identity
509 ? US dkim_cur_sig->identity : dkim_exim_expand_defaults(what);
511 case DKIM_KEY_GRANULARITY:
512 return dkim_cur_sig->pubkey
513 ? dkim_cur_sig->pubkey->granularity
514 ? US dkim_cur_sig->pubkey->granularity
515 : dkim_exim_expand_defaults(what)
516 : dkim_exim_expand_defaults(what);
518 case DKIM_KEY_SRVTYPE:
519 return dkim_cur_sig->pubkey
520 ? dkim_cur_sig->pubkey->srvtype
521 ? US dkim_cur_sig->pubkey->srvtype
522 : dkim_exim_expand_defaults(what)
523 : dkim_exim_expand_defaults(what);
526 return dkim_cur_sig->pubkey
527 ? dkim_cur_sig->pubkey->notes
528 ? US dkim_cur_sig->pubkey->notes
529 : dkim_exim_expand_defaults(what)
530 : dkim_exim_expand_defaults(what);
532 case DKIM_KEY_TESTING:
533 return dkim_cur_sig->pubkey
534 ? dkim_cur_sig->pubkey->testing
536 : dkim_exim_expand_defaults(what)
537 : dkim_exim_expand_defaults(what);
539 case DKIM_NOSUBDOMAINS:
540 return dkim_cur_sig->pubkey
541 ? dkim_cur_sig->pubkey->no_subdomaining
543 : dkim_exim_expand_defaults(what)
544 : dkim_exim_expand_defaults(what);
546 case DKIM_VERIFY_STATUS:
547 switch (dkim_cur_sig->verify_status)
549 case PDKIM_VERIFY_INVALID: return US"invalid";
550 case PDKIM_VERIFY_FAIL: return US"fail";
551 case PDKIM_VERIFY_PASS: return US"pass";
552 case PDKIM_VERIFY_NONE:
553 default: return US"none";
556 case DKIM_VERIFY_REASON:
557 switch (dkim_cur_sig->verify_ext_status)
559 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
560 return US"pubkey_unavailable";
561 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:return US"pubkey_dns_syntax";
562 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT: return US"pubkey_der_syntax";
563 case PDKIM_VERIFY_FAIL_BODY: return US"bodyhash_mismatch";
564 case PDKIM_VERIFY_FAIL_MESSAGE: return US"signature_incorrect";
574 dkim_exim_sign_init(void)
576 int old_pool = store_pool;
579 store_pool = POOL_MAIN;
580 pdkim_init_context(&dkim_sign_ctx, FALSE, &dkim_exim_query_dns_txt);
581 store_pool = old_pool;
585 /* Generate signatures for the given file.
586 If a prefix is given, prepend it to the file for the calculations.
589 NULL: error; error string written
590 string: signature header(s), or a zero-length string (not an error)
594 dkim_exim_sign(int fd, off_t off, uschar * prefix,
595 struct ob_dkim * dkim, const uschar ** errstr)
597 const uschar * dkim_domain = NULL;
599 gstring * seen_doms = NULL;
600 pdkim_signature * sig;
606 int old_pool = store_pool;
610 if (dkim->dot_stuffed)
611 dkim_sign_ctx.flags |= PDKIM_DOT_TERM;
613 store_pool = POOL_MAIN;
615 if ((s = dkim->dkim_domain) && !(dkim_domain = expand_cstring(s)))
616 /* expansion error, do not send message. */
617 { errwhen = US"dkim_domain"; goto expand_bad; }
619 /* Set $dkim_domain expansion variable to each unique domain in list. */
622 while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep, NULL, 0)))
624 const uschar * dkim_sel;
627 if (dkim_signing_domain[0] == '\0')
630 /* Only sign once for each domain, no matter how often it
631 appears in the expanded list. */
633 dkim_signing_domain = string_copylc(dkim_signing_domain);
634 if (match_isinlist(dkim_signing_domain, CUSS &seen_doms,
635 0, NULL, NULL, MCL_STRING, TRUE, NULL) == OK)
638 seen_doms = string_append_listele(seen_doms, ':', dkim_signing_domain);
640 /* Set $dkim_selector expansion variable to each selector in list,
643 if (!(dkim_sel = expand_string(dkim->dkim_selector)))
644 { errwhen = US"dkim_selector"; goto expand_bad; }
646 while ((dkim_signing_selector = string_nextinlist(&dkim_sel, &sel_sep,
649 uschar * dkim_canon_expanded;
651 uschar * dkim_sign_headers_expanded = NULL;
652 uschar * dkim_private_key_expanded;
653 uschar * dkim_hash_expanded;
654 uschar * dkim_identity_expanded = NULL;
655 uschar * dkim_timestamps_expanded = NULL;
656 unsigned long tval = 0, xval = 0;
658 /* Get canonicalization to use */
660 dkim_canon_expanded = dkim->dkim_canon
661 ? expand_string(dkim->dkim_canon) : US"relaxed";
662 if (!dkim_canon_expanded) /* expansion error, do not send message. */
663 { errwhen = US"dkim_canon"; goto expand_bad; }
665 if (Ustrcmp(dkim_canon_expanded, "relaxed") == 0)
666 pdkim_canon = PDKIM_CANON_RELAXED;
667 else if (Ustrcmp(dkim_canon_expanded, "simple") == 0)
668 pdkim_canon = PDKIM_CANON_SIMPLE;
671 log_write(0, LOG_MAIN,
672 "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",
673 dkim_canon_expanded);
674 pdkim_canon = PDKIM_CANON_RELAXED;
677 if ( dkim->dkim_sign_headers
678 && !(dkim_sign_headers_expanded = expand_string(dkim->dkim_sign_headers)))
679 { errwhen = US"dkim_sign_header"; goto expand_bad; }
680 /* else pass NULL, which means default header list */
682 /* Get private key to use. */
684 if (!(dkim_private_key_expanded = expand_string(dkim->dkim_private_key)))
685 { errwhen = US"dkim_private_key"; goto expand_bad; }
687 if ( Ustrlen(dkim_private_key_expanded) == 0
688 || Ustrcmp(dkim_private_key_expanded, "0") == 0
689 || Ustrcmp(dkim_private_key_expanded, "false") == 0
691 continue; /* don't sign, but no error */
693 if ( dkim_private_key_expanded[0] == '/'
694 && !(dkim_private_key_expanded =
695 expand_file_big_buffer(dkim_private_key_expanded)))
698 if (!(dkim_hash_expanded = expand_string(dkim->dkim_hash)))
699 { errwhen = US"dkim_hash"; goto expand_bad; }
701 if (dkim->dkim_identity)
702 if (!(dkim_identity_expanded = expand_string(dkim->dkim_identity)))
703 { errwhen = US"dkim_identity"; goto expand_bad; }
704 else if (!*dkim_identity_expanded)
705 dkim_identity_expanded = NULL;
707 if (dkim->dkim_timestamps)
708 if (!(dkim_timestamps_expanded = expand_string(dkim->dkim_timestamps)))
709 { errwhen = US"dkim_timestamps"; goto expand_bad; }
711 xval = (tval = (unsigned long) time(NULL))
712 + strtoul(CCS dkim_timestamps_expanded, NULL, 10);
714 if (!(sig = pdkim_init_sign(&dkim_sign_ctx, dkim_signing_domain,
715 dkim_signing_selector,
716 dkim_private_key_expanded,
721 dkim_private_key_expanded[0] = '\0';
723 pdkim_set_optional(sig,
724 CS dkim_sign_headers_expanded,
725 CS dkim_identity_expanded,
727 pdkim_canon, -1, tval, xval);
729 if (!pdkim_set_sig_bodyhash(&dkim_sign_ctx, sig))
732 if (!dkim_sign_ctx.sig) /* link sig to context chain */
733 dkim_sign_ctx.sig = sig;
736 pdkim_signature * n = dkim_sign_ctx.sig;
737 while (n->next) n = n->next;
743 /* We may need to carry on with the data-feed even if there are no DKIM sigs to
744 produce, if some other package (eg. ARC) is signing. */
746 if (!dkim_sign_ctx.sig && !dkim->force_bodyhash)
748 DEBUG(D_transport) debug_printf("DKIM: no viable signatures to use\n");
749 sigbuf = string_get(1); /* return a zero-len string */
753 if (prefix && (pdkim_rc = pdkim_feed(&dkim_sign_ctx, prefix, Ustrlen(prefix))) != PDKIM_OK)
756 if (lseek(fd, off, SEEK_SET) < 0)
759 while ((sread = read(fd, &buf, sizeof(buf))) > 0)
760 if ((pdkim_rc = pdkim_feed(&dkim_sign_ctx, buf, sread)) != PDKIM_OK)
763 /* Handle failed read above. */
766 debug_printf("DKIM: Error reading -K file.\n");
771 /* Build string of headers, one per signature */
773 if ((pdkim_rc = pdkim_feed_finish(&dkim_sign_ctx, &sig, errstr)) != PDKIM_OK)
778 DEBUG(D_transport) debug_printf("DKIM: no signatures to use\n");
779 sigbuf = string_get(1); /* return a zero-len string */
781 else for (sigbuf = NULL; sig; sig = sig->next)
782 sigbuf = string_append(sigbuf, 2, US sig->signature_header, US"\r\n");
786 (void) string_from_gstring(sigbuf);
787 store_pool = old_pool;
792 log_write(0, LOG_MAIN|LOG_PANIC,
793 "DKIM: signing failed: %.100s", pdkim_errstr(pdkim_rc));
799 *errstr = string_sprintf("failed to expand %s: %s",
800 errwhen, expand_string_message);
801 log_write(0, LOG_MAIN | LOG_PANIC, "%s", *errstr);
809 authres_dkim(gstring * g)
811 int start = 0; /* compiler quietening */
813 DEBUG(D_acl) start = g->ptr;
815 for (pdkim_signature * sig = dkim_signatures; sig; sig = sig->next)
817 g = string_catn(g, US";\n\tdkim=", 8);
819 if (sig->verify_status & PDKIM_VERIFY_POLICY)
820 g = string_append(g, 5,
821 US"policy (", dkim_verify_status, US" - ", dkim_verify_reason, US")");
822 else switch(sig->verify_status)
824 case PDKIM_VERIFY_NONE: g = string_cat(g, US"none"); break;
825 case PDKIM_VERIFY_INVALID:
826 switch (sig->verify_ext_status)
828 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
829 g = string_cat(g, US"tmperror (pubkey unavailable)\n\t\t"); break;
830 case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
831 g = string_cat(g, US"permerror (overlong public key record)\n\t\t"); break;
832 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:
833 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT:
834 g = string_cat(g, US"neutral (public key record import problem)\n\t\t");
836 case PDKIM_VERIFY_INVALID_SIGNATURE_ERROR:
837 g = string_cat(g, US"neutral (signature tag missing or invalid)\n\t\t");
839 case PDKIM_VERIFY_INVALID_DKIM_VERSION:
840 g = string_cat(g, US"neutral (unsupported DKIM version)\n\t\t");
843 g = string_cat(g, US"permerror (unspecified problem)\n\t\t"); break;
846 case PDKIM_VERIFY_FAIL:
847 switch (sig->verify_ext_status)
849 case PDKIM_VERIFY_FAIL_BODY:
851 US"fail (body hash mismatch; body probably modified in transit)\n\t\t");
853 case PDKIM_VERIFY_FAIL_MESSAGE:
855 US"fail (signature did not verify; headers probably modified in transit)\n\t\t");
858 g = string_cat(g, US"fail (unspecified reason)\n\t\t");
862 case PDKIM_VERIFY_PASS: g = string_cat(g, US"pass"); break;
863 default: g = string_cat(g, US"permerror"); break;
865 if (sig->domain) g = string_append(g, 2, US" header.d=", sig->domain);
866 if (sig->identity) g = string_append(g, 2, US" header.i=", sig->identity);
867 if (sig->selector) g = string_append(g, 2, US" header.s=", sig->selector);
868 g = string_append(g, 2, US" header.a=", dkim_sig_to_a_tag(sig));
873 debug_printf("DKIM: no authres\n");
875 debug_printf("DKIM: authres '%.*s'\n", g->ptr - start - 3, g->s + start + 3);
880 # endif /*!MACRO_PREDEF*/
881 #endif /*!DISABLE_DKIM*/