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);
25 # else /*!MACRO_PREDEF*/
29 pdkim_ctx dkim_sign_ctx;
31 int dkim_verify_oldpool;
32 pdkim_ctx *dkim_verify_ctx = NULL;
33 pdkim_signature *dkim_signatures = NULL;
34 pdkim_signature *dkim_cur_sig = NULL;
35 static const uschar * dkim_collect_error = NULL;
39 /*XXX the caller only uses the first record if we return multiple.
43 dkim_exim_query_dns_txt(uschar * name)
50 lookup_dnssec_authenticated = NULL;
51 if (dns_lookup(&dnsa, name, T_TXT, NULL) != DNS_SUCCEED)
52 return NULL; /*XXX better error detail? logging? */
54 /* Search for TXT record */
56 for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
58 rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
59 if (rr->type == T_TXT)
63 /* Copy record content to the answer buffer */
65 while (rr_offset < rr->size)
67 uschar len = rr->data[rr_offset++];
69 g = string_catn(g, US(rr->data + rr_offset), len);
70 if (g->ptr >= PDKIM_DNS_TXT_MAX_RECLEN)
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)
79 gstring_reset_unused(g);
80 return string_from_gstring(g);
83 if (g) g->ptr = 0; /* overwrite previous record */
87 if (g) store_reset(g);
88 return NULL; /*XXX better error detail? logging? */
101 dkim_exim_verify_init(BOOL dot_stuffing)
103 /* There is a store-reset between header & body reception
104 so cannot use the main pool. Any allocs done by Exim
105 memory-handling must use the perm pool. */
107 dkim_verify_oldpool = store_pool;
108 store_pool = POOL_PERM;
110 /* Free previous context if there is one */
113 pdkim_free_ctx(dkim_verify_ctx);
115 /* Create new context */
117 dkim_verify_ctx = pdkim_init_verify(&dkim_exim_query_dns_txt, dot_stuffing);
118 dkim_collect_input = !!dkim_verify_ctx;
119 dkim_collect_error = NULL;
121 /* Start feed up with any cached data */
124 store_pool = dkim_verify_oldpool;
129 dkim_exim_verify_feed(uschar * data, int len)
133 store_pool = POOL_PERM;
134 if ( dkim_collect_input
135 && (rc = pdkim_feed(dkim_verify_ctx, data, len)) != PDKIM_OK)
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;
142 store_pool = dkim_verify_oldpool;
146 /* Log the result for the given signature */
148 dkim_exim_verify_log_sig(pdkim_signature * sig)
155 /* Remember the domain for the first pass result */
157 if ( !dkim_verify_overall
158 && dkim_verify_status
159 ? Ustrcmp(dkim_verify_status, US"pass") == 0
160 : sig->verify_status == PDKIM_VERIFY_PASS
162 dkim_verify_overall = string_copy(sig->domain);
164 /* Rewrite the sig result if the ACL overrode it. This is only
165 needed because the DMARC code (sigh) peeks at the dkim sigs.
166 Mark the sig for this having been done. */
168 if ( 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)
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;
183 sig->verify_status = -1;
186 if (!LOGGING(dkim_verbose)) return;
189 logmsg = string_catn(NULL, US"DKIM: ", 6);
190 if (!(s = sig->domain)) s = US"<UNSET>";
191 logmsg = string_append(logmsg, 2, "d=", s);
192 if (!(s = sig->selector)) s = US"<UNSET>";
193 logmsg = string_append(logmsg, 2, " s=", s);
194 logmsg = 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),
198 string_sprintf(" b=" SIZE_T_FMT,
199 (int)sig->sighash.len > -1 ? sig->sighash.len * 8 : 0));
200 if ((s= sig->identity)) logmsg = string_append(logmsg, 2, " i=", s);
201 if (sig->created > 0) logmsg = string_cat(logmsg,
202 string_sprintf(" t=%lu", sig->created));
203 if (sig->expires > 0) logmsg = string_cat(logmsg,
204 string_sprintf(" x=%lu", sig->expires));
205 if (sig->bodylength > -1) logmsg = string_cat(logmsg,
206 string_sprintf(" l=%lu", sig->bodylength));
208 if (sig->verify_status & PDKIM_VERIFY_POLICY)
209 logmsg = string_append(logmsg, 5,
210 US" [", dkim_verify_status, US" - ", dkim_verify_reason, US"]");
212 switch (sig->verify_status)
214 case PDKIM_VERIFY_NONE:
215 logmsg = string_cat(logmsg, US" [not verified]");
218 case PDKIM_VERIFY_INVALID:
219 logmsg = string_cat(logmsg, US" [invalid - ");
220 switch (sig->verify_ext_status)
222 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
223 logmsg = string_cat(logmsg,
224 US"public key record (currently?) unavailable]");
227 case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
228 logmsg = string_cat(logmsg, US"overlong public key record]");
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]");
236 case PDKIM_VERIFY_INVALID_SIGNATURE_ERROR:
237 logmsg = string_cat(logmsg, US"signature tag missing or invalid]");
240 case PDKIM_VERIFY_INVALID_DKIM_VERSION:
241 logmsg = string_cat(logmsg, US"unsupported DKIM version]");
245 logmsg = string_cat(logmsg, US"unspecified problem]");
249 case PDKIM_VERIFY_FAIL:
250 logmsg = string_cat(logmsg, US" [verification failed - ");
251 switch (sig->verify_ext_status)
253 case PDKIM_VERIFY_FAIL_BODY:
254 logmsg = string_cat(logmsg,
255 US"body hash mismatch (body probably modified in transit)]");
258 case PDKIM_VERIFY_FAIL_MESSAGE:
259 logmsg = string_cat(logmsg,
260 US"signature did not verify "
261 "(headers probably modified in transit)]");
265 logmsg = string_cat(logmsg, US"unspecified reason]");
269 case PDKIM_VERIFY_PASS:
270 logmsg = string_cat(logmsg, US" [verification succeeded]");
274 log_write(0, LOG_MAIN, "%s", string_from_gstring(logmsg));
279 /* Log a line for each signature */
281 dkim_exim_verify_log_all(void)
283 pdkim_signature * sig;
284 for (sig = dkim_signatures; sig; sig = sig->next) dkim_exim_verify_log_sig(sig);
289 dkim_exim_verify_finish(void)
291 pdkim_signature * sig;
294 const uschar * errstr = NULL;
296 store_pool = POOL_PERM;
298 /* Delete eventual previous signature chain */
301 dkim_signatures = NULL;
303 if (dkim_collect_error)
305 log_write(0, LOG_MAIN,
306 "DKIM: Error during validation, disabling signature verification: %.100s",
308 dkim_disable_verify = TRUE;
312 dkim_collect_input = FALSE;
314 /* Finish DKIM operation and fetch link to signatures chain */
316 rc = pdkim_feed_finish(dkim_verify_ctx, &dkim_signatures, &errstr);
317 if (rc != PDKIM_OK && errstr)
318 log_write(0, LOG_MAIN, "DKIM: validation error: %s", errstr);
320 /* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */
322 for (sig = dkim_signatures; sig; sig = sig->next)
324 if (sig->domain) g = string_append_listele(g, ':', sig->domain);
325 if (sig->identity) g = string_append_listele(g, ':', sig->identity);
328 if (g) dkim_signers = g->s;
331 store_pool = dkim_verify_oldpool;
336 /* Args as per dkim_exim_acl_run() below */
338 dkim_acl_call(uschar * id, gstring ** res_ptr,
339 uschar ** user_msgptr, uschar ** log_msgptr)
343 debug_printf("calling acl_smtp_dkim for dkim_cur_signer='%s'\n", id);
345 rc = acl_check(ACL_WHERE_DKIM, NULL, acl_smtp_dkim, user_msgptr, log_msgptr);
346 dkim_exim_verify_log_sig(dkim_cur_sig);
347 *res_ptr = string_append_listele(*res_ptr, ':', dkim_verify_status);
353 /* For the given identity, run the DKIM ACL once for each matching signature.
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)
362 Returns: 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
371 dkim_exim_acl_run(uschar * id, gstring ** res_ptr,
372 uschar ** user_msgptr, uschar ** log_msgptr)
374 pdkim_signature * sig;
378 dkim_verify_status = US"none";
379 dkim_verify_reason = US"";
380 dkim_cur_signer = id;
382 if (dkim_disable_verify || !id || !dkim_verify_ctx)
385 /* Find signatures to run ACL on */
387 for (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
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
401 dkim_signing_domain = US sig->domain;
402 dkim_signing_selector = US sig->selector;
403 dkim_key_length = sig->sighash.len * 8;
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 */
408 dkim_verify_status = dkim_exim_expand_query(DKIM_VERIFY_STATUS);
409 dkim_verify_reason = dkim_exim_expand_query(DKIM_VERIFY_REASON);
411 if ((rc = dkim_acl_call(id, res_ptr, user_msgptr, log_msgptr)) != OK)
418 /* No matching sig found. Call ACL once anyway. */
421 return dkim_acl_call(id, res_ptr, user_msgptr, log_msgptr);
426 dkim_exim_expand_defaults(int what)
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"";
452 dkim_exim_expand_query(int what)
454 if (!dkim_verify_ctx || dkim_disable_verify || !dkim_cur_sig)
455 return dkim_exim_expand_defaults(what);
460 return dkim_sig_to_a_tag(dkim_cur_sig);
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);
467 case DKIM_CANON_BODY:
468 switch (dkim_cur_sig->canon_body)
470 case PDKIM_CANON_RELAXED: return US"relaxed";
471 case PDKIM_CANON_SIMPLE:
472 default: return US"simple";
475 case DKIM_CANON_HEADERS:
476 switch (dkim_cur_sig->canon_headers)
478 case PDKIM_CANON_RELAXED: return US"relaxed";
479 case PDKIM_CANON_SIMPLE:
480 default: return US"simple";
483 case DKIM_COPIEDHEADERS:
484 return dkim_cur_sig->copiedheaders
485 ? US dkim_cur_sig->copiedheaders : dkim_exim_expand_defaults(what);
488 return dkim_cur_sig->created > 0
489 ? string_sprintf("%lu", dkim_cur_sig->created)
490 : dkim_exim_expand_defaults(what);
493 return dkim_cur_sig->expires > 0
494 ? string_sprintf("%lu", dkim_cur_sig->expires)
495 : dkim_exim_expand_defaults(what);
497 case DKIM_HEADERNAMES:
498 return dkim_cur_sig->headernames
499 ? dkim_cur_sig->headernames : dkim_exim_expand_defaults(what);
502 return dkim_cur_sig->identity
503 ? US dkim_cur_sig->identity : dkim_exim_expand_defaults(what);
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);
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);
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);
526 case DKIM_KEY_TESTING:
527 return dkim_cur_sig->pubkey
528 ? dkim_cur_sig->pubkey->testing
530 : dkim_exim_expand_defaults(what)
531 : dkim_exim_expand_defaults(what);
533 case DKIM_NOSUBDOMAINS:
534 return dkim_cur_sig->pubkey
535 ? dkim_cur_sig->pubkey->no_subdomaining
537 : dkim_exim_expand_defaults(what)
538 : dkim_exim_expand_defaults(what);
540 case DKIM_VERIFY_STATUS:
541 switch (dkim_cur_sig->verify_status)
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";
550 case DKIM_VERIFY_REASON:
551 switch (dkim_cur_sig->verify_ext_status)
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";
568 dkim_exim_sign_init(void)
570 int old_pool = store_pool;
571 store_pool = POOL_MAIN;
572 pdkim_init_context(&dkim_sign_ctx, FALSE, &dkim_exim_query_dns_txt);
573 store_pool = old_pool;
577 /* Generate signatures for the given file.
578 If a prefix is given, prepend it to the file for the calculations.
581 NULL: error; error string written
582 string: signature header(s), or a zero-length string (not an error)
586 dkim_exim_sign(int fd, off_t off, uschar * prefix,
587 struct ob_dkim * dkim, const uschar ** errstr)
589 const uschar * dkim_domain = NULL;
591 gstring * seen_doms = NULL;
592 pdkim_signature * sig;
598 int old_pool = store_pool;
602 if (dkim->dot_stuffed)
603 dkim_sign_ctx.flags |= PDKIM_DOT_TERM;
605 store_pool = POOL_MAIN;
607 if ((s = dkim->dkim_domain) && !(dkim_domain = expand_cstring(s)))
608 /* expansion error, do not send message. */
609 { errwhen = US"dkim_domain"; goto expand_bad; }
611 /* Set $dkim_domain expansion variable to each unique domain in list. */
614 while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep, NULL, 0)))
616 const uschar * dkim_sel;
619 if (dkim_signing_domain[0] == '\0')
622 /* Only sign once for each domain, no matter how often it
623 appears in the expanded list. */
625 if (match_isinlist(dkim_signing_domain, CUSS &seen_doms,
626 0, NULL, NULL, MCL_STRING, TRUE, NULL) == OK)
629 seen_doms = string_append_listele(seen_doms, ':', dkim_signing_domain);
631 /* Set $dkim_selector expansion variable to each selector in list,
634 if (!(dkim_sel = expand_string(dkim->dkim_selector)))
635 { errwhen = US"dkim_selector"; goto expand_bad; }
637 while ((dkim_signing_selector = string_nextinlist(&dkim_sel, &sel_sep,
640 uschar * dkim_canon_expanded;
642 uschar * dkim_sign_headers_expanded = NULL;
643 uschar * dkim_private_key_expanded;
644 uschar * dkim_hash_expanded;
645 uschar * dkim_identity_expanded = NULL;
647 /* Get canonicalization to use */
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; }
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;
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;
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 */
671 /* Get private key to use. */
673 if (!(dkim_private_key_expanded = expand_string(dkim->dkim_private_key)))
674 { errwhen = US"dkim_private_key"; goto expand_bad; }
676 if ( Ustrlen(dkim_private_key_expanded) == 0
677 || Ustrcmp(dkim_private_key_expanded, "0") == 0
678 || Ustrcmp(dkim_private_key_expanded, "false") == 0
680 continue; /* don't sign, but no error */
682 if ( dkim_private_key_expanded[0] == '/'
683 && !(dkim_private_key_expanded =
684 expand_file_big_buffer(dkim_private_key_expanded)))
687 if (!(dkim_hash_expanded = expand_string(dkim->dkim_hash)))
688 { errwhen = US"dkim_hash"; goto expand_bad; }
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;
696 if (!(sig = pdkim_init_sign(&dkim_sign_ctx, dkim_signing_domain,
697 dkim_signing_selector,
698 dkim_private_key_expanded,
703 dkim_private_key_expanded[0] = '\0';
705 pdkim_set_optional(sig,
706 CS dkim_sign_headers_expanded,
707 CS dkim_identity_expanded,
709 pdkim_canon, -1, 0, 0);
711 if (!pdkim_set_sig_bodyhash(&dkim_sign_ctx, sig))
714 if (!dkim_sign_ctx.sig) /* link sig to context chain */
715 dkim_sign_ctx.sig = sig;
718 pdkim_signature * n = dkim_sign_ctx.sig;
719 while (n->next) n = n->next;
725 /* We may need to carry on with the data-feed even if there are no DKIM sigs to
726 produce, if some other package (eg. ARC) is signing. */
728 if (!dkim_sign_ctx.sig && !dkim->force_bodyhash)
730 DEBUG(D_transport) debug_printf("DKIM: no viable signatures to use\n");
731 sigbuf = string_get(1); /* return a zero-len string */
735 if (prefix && (pdkim_rc = pdkim_feed(&dkim_sign_ctx, prefix, Ustrlen(prefix))) != PDKIM_OK)
738 if (lseek(fd, off, SEEK_SET) < 0)
741 while ((sread = read(fd, &buf, sizeof(buf))) > 0)
742 if ((pdkim_rc = pdkim_feed(&dkim_sign_ctx, buf, sread)) != PDKIM_OK)
745 /* Handle failed read above. */
748 debug_printf("DKIM: Error reading -K file.\n");
753 /* Build string of headers, one per signature */
755 if ((pdkim_rc = pdkim_feed_finish(&dkim_sign_ctx, &sig, errstr)) != PDKIM_OK)
760 DEBUG(D_transport) debug_printf("DKIM: no signatures to use\n");
761 sigbuf = string_get(1); /* return a zero-len string */
763 else for (sigbuf = NULL; sig; sig = sig->next)
764 sigbuf = string_append(sigbuf, 2, US sig->signature_header, US"\r\n");
768 (void) string_from_gstring(sigbuf);
769 store_pool = old_pool;
774 log_write(0, LOG_MAIN|LOG_PANIC,
775 "DKIM: signing failed: %.100s", pdkim_errstr(pdkim_rc));
781 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand %s: %s",
782 errwhen, expand_string_message);
790 authres_dkim(gstring * g)
792 pdkim_signature * sig;
793 int start = 0; /* compiler quietening */
795 DEBUG(D_acl) start = g->ptr;
797 for (sig = dkim_signatures; sig; sig = sig->next)
799 g = string_catn(g, US";\n\tdkim=", 8);
801 if (sig->verify_status & PDKIM_VERIFY_POLICY)
802 g = string_append(g, 5,
803 US"policy (", dkim_verify_status, US" - ", dkim_verify_reason, US")");
804 else switch(sig->verify_status)
806 case PDKIM_VERIFY_NONE: g = string_cat(g, US"none"); break;
807 case PDKIM_VERIFY_INVALID:
808 switch (sig->verify_ext_status)
810 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
811 g = string_cat(g, US"tmperror (pubkey unavailable)\n\t\t"); break;
812 case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
813 g = string_cat(g, US"permerror (overlong public key record)\n\t\t"); break;
814 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:
815 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT:
816 g = string_cat(g, US"neutral (syntax error in public key record)\n\t\t");
818 case PDKIM_VERIFY_INVALID_SIGNATURE_ERROR:
819 g = string_cat(g, US"neutral (signature tag missing or invalid)\n\t\t");
821 case PDKIM_VERIFY_INVALID_DKIM_VERSION:
822 g = string_cat(g, US"neutral (unsupported DKIM version)\n\t\t");
825 g = string_cat(g, US"permerror (unspecified problem)\n\t\t"); break;
828 case PDKIM_VERIFY_FAIL:
829 switch (sig->verify_ext_status)
831 case PDKIM_VERIFY_FAIL_BODY:
833 US"fail (body hash mismatch; body probably modified in transit)\n\t\t");
835 case PDKIM_VERIFY_FAIL_MESSAGE:
837 US"fail (signature did not verify; headers probably modified in transit)\n\t\t");
840 g = string_cat(g, US"fail (unspecified reason)\n\t\t");
844 case PDKIM_VERIFY_PASS: g = string_cat(g, US"pass"); break;
845 default: g = string_cat(g, US"permerror"); break;
847 if (sig->domain) g = string_append(g, 2, US" header.d=", sig->domain);
848 if (sig->identity) g = string_append(g, 2, US" header.i=", sig->identity);
849 if (sig->selector) g = string_append(g, 2, US" header.s=", sig->selector);
850 g = string_append(g, 2, US" header.a=", dkim_sig_to_a_tag(sig));
855 debug_printf("DKIM: no authres\n");
857 debug_printf("DKIM: authres '%.*s'\n", g->ptr - start - 3, g->s + start + 3);
862 # endif /*!MACRO_PREDEF*/
863 #endif /*!DISABLE_DKIM*/