1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) University of Cambridge, 1995 - 2017 */
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*/
30 int dkim_verify_oldpool;
31 pdkim_ctx *dkim_verify_ctx = NULL;
32 pdkim_signature *dkim_signatures = NULL;
33 pdkim_signature *dkim_cur_sig = NULL;
34 static const uschar * dkim_collect_error = NULL;
38 /*XXX the caller only uses the first record if we return multiple.
39 Could we hand back an allocated string?
43 dkim_exim_query_dns_txt(char *name, char *answer)
49 lookup_dnssec_authenticated = NULL;
50 if (dns_lookup(&dnsa, US name, T_TXT, NULL) != DNS_SUCCEED)
51 return PDKIM_FAIL; /*XXX better error detail? logging? */
53 /* Search for TXT record */
55 for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
57 rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
58 if (rr->type == T_TXT)
61 int answer_offset = 0;
63 /* Copy record content to the answer buffer */
65 while (rr_offset < rr->size)
67 uschar len = rr->data[rr_offset++];
68 snprintf(answer + answer_offset,
69 PDKIM_DNS_TXT_MAX_RECLEN - answer_offset,
70 "%.*s", (int)len, CS (rr->data + rr_offset));
73 if (answer_offset >= PDKIM_DNS_TXT_MAX_RECLEN)
74 return PDKIM_FAIL; /*XXX better error detail? logging? */
79 return PDKIM_FAIL; /*XXX better error detail? logging? */
92 dkim_exim_verify_init(BOOL dot_stuffing)
94 /* There is a store-reset between header & body reception
95 so cannot use the main pool. Any allocs done by Exim
96 memory-handling must use the perm pool. */
98 dkim_verify_oldpool = store_pool;
99 store_pool = POOL_PERM;
101 /* Free previous context if there is one */
104 pdkim_free_ctx(dkim_verify_ctx);
106 /* Create new context */
108 dkim_verify_ctx = pdkim_init_verify(&dkim_exim_query_dns_txt, dot_stuffing);
109 dkim_collect_input = !!dkim_verify_ctx;
110 dkim_collect_error = NULL;
112 /* Start feed up with any cached data */
115 store_pool = dkim_verify_oldpool;
120 dkim_exim_verify_feed(uschar * data, int len)
124 store_pool = POOL_PERM;
125 if ( dkim_collect_input
126 && (rc = pdkim_feed(dkim_verify_ctx, data, len)) != PDKIM_OK)
128 dkim_collect_error = pdkim_errstr(rc);
129 log_write(0, LOG_MAIN,
130 "DKIM: validation error: %.100s", dkim_collect_error);
131 dkim_collect_input = FALSE;
133 store_pool = dkim_verify_oldpool;
137 /* Log the result for the given signature */
139 dkim_exim_verify_log_sig(pdkim_signature * sig)
146 logmsg = string_catn(NULL, "DKIM: ", 6);
147 if (!(s = sig->domain)) s = US"<UNSET>";
148 logmsg = string_append(logmsg, 2, "d=", s);
149 if (!(s = sig->selector)) s = US"<UNSET>";
150 logmsg = string_append(logmsg, 2, " s=", s);
151 logmsg = string_append(logmsg, 7,
152 " c=", sig->canon_headers == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
153 "/", sig->canon_body == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
154 " a=", dkim_sig_to_a_tag(sig),
155 string_sprintf(" b=" SIZE_T_FMT,
156 (int)sig->sighash.len > -1 ? sig->sighash.len * 8 : 0));
157 if ((s= sig->identity)) logmsg = string_append(logmsg, 2, " i=", s);
158 if (sig->created > 0) logmsg = string_cat(logmsg,
159 string_sprintf(" t=%lu", sig->created));
160 if (sig->expires > 0) logmsg = string_cat(logmsg,
161 string_sprintf(" x=%lu", sig->expires));
162 if (sig->bodylength > -1) logmsg = string_cat(logmsg,
163 string_sprintf(" l=%lu", sig->bodylength));
165 if ( !dkim_verify_status
166 || ( dkim_verify_status == dkim_exim_expand_query(DKIM_VERIFY_STATUS)
167 && dkim_verify_reason == dkim_exim_expand_query(DKIM_VERIFY_REASON)
169 switch (sig->verify_status)
171 case PDKIM_VERIFY_NONE:
172 logmsg = string_cat(logmsg, " [not verified]");
175 case PDKIM_VERIFY_INVALID:
176 logmsg = string_cat(logmsg, " [invalid - ");
177 switch (sig->verify_ext_status)
179 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
180 logmsg = string_cat(logmsg,
181 "public key record (currently?) unavailable]");
184 case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
185 logmsg = string_cat(logmsg, "overlong public key record]");
188 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:
189 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT:
190 logmsg = string_cat(logmsg, "syntax error in public key record]");
193 case PDKIM_VERIFY_INVALID_SIGNATURE_ERROR:
194 logmsg = string_cat(logmsg, "signature tag missing or invalid]");
197 case PDKIM_VERIFY_INVALID_DKIM_VERSION:
198 logmsg = string_cat(logmsg, "unsupported DKIM version]");
202 logmsg = string_cat(logmsg, "unspecified problem]");
206 case PDKIM_VERIFY_FAIL:
207 logmsg = string_cat(logmsg, " [verification failed - ");
208 switch (sig->verify_ext_status)
210 case PDKIM_VERIFY_FAIL_BODY:
211 logmsg = string_cat(logmsg,
212 "body hash mismatch (body probably modified in transit)]");
215 case PDKIM_VERIFY_FAIL_MESSAGE:
216 logmsg = string_cat(logmsg,
217 "signature did not verify (headers probably modified in transit)]");
221 logmsg = string_cat(logmsg, "unspecified reason]");
225 case PDKIM_VERIFY_PASS:
226 logmsg = string_cat(logmsg, " [verification succeeded]");
230 logmsg = string_append(logmsg, 5,
231 US" [", dkim_verify_status, US" - ", dkim_verify_reason, US"]");
233 log_write(0, LOG_MAIN, string_from_gstring(logmsg));
238 /* Log a line for each signature */
240 dkim_exim_verify_log_all(void)
242 pdkim_signature * sig;
243 for (sig = dkim_signatures; sig; sig = sig->next) dkim_exim_verify_log_sig(sig);
248 dkim_exim_verify_finish(void)
250 pdkim_signature * sig;
253 const uschar * errstr;
255 store_pool = POOL_PERM;
257 /* Delete eventual previous signature chain */
260 dkim_signatures = NULL;
262 if (dkim_collect_error)
264 log_write(0, LOG_MAIN,
265 "DKIM: Error during validation, disabling signature verification: %.100s",
267 dkim_disable_verify = TRUE;
271 dkim_collect_input = FALSE;
273 /* Finish DKIM operation and fetch link to signatures chain */
275 rc = pdkim_feed_finish(dkim_verify_ctx, &dkim_signatures, &errstr);
278 log_write(0, LOG_MAIN, "DKIM: validation error: %.100s%s%s", pdkim_errstr(rc),
279 errstr ? ": " : "", errstr ? errstr : US"");
283 /* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */
285 for (sig = dkim_signatures; sig; sig = sig->next)
287 if (sig->domain) g = string_append_listele(g, ':', sig->domain);
288 if (sig->identity) g = string_append_listele(g, ':', sig->identity);
291 if (g) dkim_signers = g->s;
294 store_pool = dkim_verify_oldpool;
299 /* Args as per dkim_exim_acl_run() below */
301 dkim_acl_call(uschar * id, gstring ** res_ptr,
302 uschar ** user_msgptr, uschar ** log_msgptr)
306 debug_printf("calling acl_smtp_dkim for dkim_cur_signer='%s'\n", id);
308 rc = acl_check(ACL_WHERE_DKIM, NULL, acl_smtp_dkim, user_msgptr, log_msgptr);
309 dkim_exim_verify_log_sig(dkim_cur_sig);
310 *res_ptr = string_append_listele(*res_ptr, ':', dkim_verify_status);
316 /* For the given identity, run the DKIM ACL once for each matching signature.
319 id Identity to look for in dkim signatures
320 res_ptr ptr to growable string-list of status results,
321 appended to per ACL run
322 user_msgptr where to put a user error (for SMTP response)
323 log_msgptr where to put a logging message (not for SMTP response)
325 Returns: OK access is granted by an ACCEPT verb
326 DISCARD access is granted by a DISCARD verb
327 FAIL access is denied
328 FAIL_DROP access is denied; drop the connection
329 DEFER can't tell at the moment
334 dkim_exim_acl_run(uschar * id, gstring ** res_ptr,
335 uschar ** user_msgptr, uschar ** log_msgptr)
337 pdkim_signature * sig;
341 dkim_verify_status = US"none";
342 dkim_verify_reason = US"";
343 dkim_cur_signer = id;
345 if (dkim_disable_verify || !id || !dkim_verify_ctx)
348 /* Find signatures to run ACL on */
350 for (sig = dkim_signatures; sig; sig = sig->next)
351 if ( (cmp_val = Ustrchr(id, '@') != NULL ? US sig->identity : US sig->domain)
352 && strcmpic(cmp_val, id) == 0
355 /* The "dkim_domain" and "dkim_selector" expansion variables have
356 related globals, since they are used in the signing code too.
357 Instead of inventing separate names for verification, we set
358 them here. This is easy since a domain and selector is guaranteed
359 to be in a signature. The other dkim_* expansion items are
360 dynamically fetched from dkim_cur_sig at expansion time (see
364 dkim_signing_domain = US sig->domain;
365 dkim_signing_selector = US sig->selector;
366 dkim_key_length = sig->sighash.len * 8;
368 /* These two return static strings, so we can compare the addr
369 later to see if the ACL overwrote them. Check that when logging */
371 dkim_verify_status = dkim_exim_expand_query(DKIM_VERIFY_STATUS);
372 dkim_verify_reason = dkim_exim_expand_query(DKIM_VERIFY_REASON);
374 if ((rc = dkim_acl_call(id, res_ptr, user_msgptr, log_msgptr)) != OK)
381 /* No matching sig found. Call ACL once anyway. */
384 return dkim_acl_call(id, res_ptr, user_msgptr, log_msgptr);
389 dkim_exim_expand_defaults(int what)
393 case DKIM_ALGO: return US"";
394 case DKIM_BODYLENGTH: return US"9999999999999";
395 case DKIM_CANON_BODY: return US"";
396 case DKIM_CANON_HEADERS: return US"";
397 case DKIM_COPIEDHEADERS: return US"";
398 case DKIM_CREATED: return US"0";
399 case DKIM_EXPIRES: return US"9999999999999";
400 case DKIM_HEADERNAMES: return US"";
401 case DKIM_IDENTITY: return US"";
402 case DKIM_KEY_GRANULARITY: return US"*";
403 case DKIM_KEY_SRVTYPE: return US"*";
404 case DKIM_KEY_NOTES: return US"";
405 case DKIM_KEY_TESTING: return US"0";
406 case DKIM_NOSUBDOMAINS: return US"0";
407 case DKIM_VERIFY_STATUS: return US"none";
408 case DKIM_VERIFY_REASON: return US"";
409 default: return US"";
415 dkim_exim_expand_query(int what)
417 if (!dkim_verify_ctx || dkim_disable_verify || !dkim_cur_sig)
418 return dkim_exim_expand_defaults(what);
423 return dkim_sig_to_a_tag(dkim_cur_sig);
425 case DKIM_BODYLENGTH:
426 return dkim_cur_sig->bodylength >= 0
427 ? string_sprintf("%ld", dkim_cur_sig->bodylength)
428 : dkim_exim_expand_defaults(what);
430 case DKIM_CANON_BODY:
431 switch (dkim_cur_sig->canon_body)
433 case PDKIM_CANON_RELAXED: return US"relaxed";
434 case PDKIM_CANON_SIMPLE:
435 default: return US"simple";
438 case DKIM_CANON_HEADERS:
439 switch (dkim_cur_sig->canon_headers)
441 case PDKIM_CANON_RELAXED: return US"relaxed";
442 case PDKIM_CANON_SIMPLE:
443 default: return US"simple";
446 case DKIM_COPIEDHEADERS:
447 return dkim_cur_sig->copiedheaders
448 ? US dkim_cur_sig->copiedheaders : dkim_exim_expand_defaults(what);
451 return dkim_cur_sig->created > 0
452 ? string_sprintf("%lu", dkim_cur_sig->created)
453 : dkim_exim_expand_defaults(what);
456 return dkim_cur_sig->expires > 0
457 ? string_sprintf("%lu", dkim_cur_sig->expires)
458 : dkim_exim_expand_defaults(what);
460 case DKIM_HEADERNAMES:
461 return dkim_cur_sig->headernames
462 ? dkim_cur_sig->headernames : dkim_exim_expand_defaults(what);
465 return dkim_cur_sig->identity
466 ? US dkim_cur_sig->identity : dkim_exim_expand_defaults(what);
468 case DKIM_KEY_GRANULARITY:
469 return dkim_cur_sig->pubkey
470 ? dkim_cur_sig->pubkey->granularity
471 ? US dkim_cur_sig->pubkey->granularity
472 : dkim_exim_expand_defaults(what)
473 : dkim_exim_expand_defaults(what);
475 case DKIM_KEY_SRVTYPE:
476 return dkim_cur_sig->pubkey
477 ? dkim_cur_sig->pubkey->srvtype
478 ? US dkim_cur_sig->pubkey->srvtype
479 : dkim_exim_expand_defaults(what)
480 : dkim_exim_expand_defaults(what);
483 return dkim_cur_sig->pubkey
484 ? dkim_cur_sig->pubkey->notes
485 ? US dkim_cur_sig->pubkey->notes
486 : dkim_exim_expand_defaults(what)
487 : dkim_exim_expand_defaults(what);
489 case DKIM_KEY_TESTING:
490 return dkim_cur_sig->pubkey
491 ? dkim_cur_sig->pubkey->testing
493 : dkim_exim_expand_defaults(what)
494 : dkim_exim_expand_defaults(what);
496 case DKIM_NOSUBDOMAINS:
497 return dkim_cur_sig->pubkey
498 ? dkim_cur_sig->pubkey->no_subdomaining
500 : dkim_exim_expand_defaults(what)
501 : dkim_exim_expand_defaults(what);
503 case DKIM_VERIFY_STATUS:
504 switch (dkim_cur_sig->verify_status)
506 case PDKIM_VERIFY_INVALID: return US"invalid";
507 case PDKIM_VERIFY_FAIL: return US"fail";
508 case PDKIM_VERIFY_PASS: return US"pass";
509 case PDKIM_VERIFY_NONE:
510 default: return US"none";
513 case DKIM_VERIFY_REASON:
514 switch (dkim_cur_sig->verify_ext_status)
516 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
517 return US"pubkey_unavailable";
518 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:return US"pubkey_dns_syntax";
519 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT: return US"pubkey_der_syntax";
520 case PDKIM_VERIFY_FAIL_BODY: return US"bodyhash_mismatch";
521 case PDKIM_VERIFY_FAIL_MESSAGE: return US"signature_incorrect";
530 /* Generate signatures for the given file, returning a string.
531 If a prefix is given, prepend it to the file for the calculations.
535 dkim_exim_sign(int fd, off_t off, uschar * prefix,
536 struct ob_dkim * dkim, const uschar ** errstr)
538 const uschar * dkim_domain;
540 gstring * seen_doms = NULL;
542 pdkim_signature * sig;
548 int old_pool = store_pool;
551 store_pool = POOL_MAIN;
553 pdkim_init_context(&ctx, dkim->dot_stuffed, &dkim_exim_query_dns_txt);
555 if (!(dkim_domain = expand_cstring(dkim->dkim_domain)))
556 /* expansion error, do not send message. */
557 { errwhen = US"dkim_domain"; goto expand_bad; }
559 /* Set $dkim_domain expansion variable to each unique domain in list. */
561 while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep, NULL, 0)))
563 const uschar * dkim_sel;
566 if (dkim_signing_domain[0] == '\0')
569 /* Only sign once for each domain, no matter how often it
570 appears in the expanded list. */
572 if (match_isinlist(dkim_signing_domain, CUSS &seen_doms,
573 0, NULL, NULL, MCL_STRING, TRUE, NULL) == OK)
576 seen_doms = string_append_listele(seen_doms, ':', dkim_signing_domain);
578 /* Set $dkim_selector expansion variable to each selector in list,
581 if (!(dkim_sel = expand_string(dkim->dkim_selector)))
582 if (!(dkim_signing_selector = expand_string(dkim->dkim_selector)))
583 { errwhen = US"dkim_selector"; goto expand_bad; }
585 while ((dkim_signing_selector = string_nextinlist(&dkim_sel, &sel_sep,
588 uschar * dkim_canon_expanded;
590 uschar * dkim_sign_headers_expanded = NULL;
591 uschar * dkim_private_key_expanded;
592 uschar * dkim_hash_expanded;
593 uschar * dkim_identity_expanded = NULL;
595 /* Get canonicalization to use */
597 dkim_canon_expanded = dkim->dkim_canon
598 ? expand_string(dkim->dkim_canon) : US"relaxed";
599 if (!dkim_canon_expanded) /* expansion error, do not send message. */
600 { errwhen = US"dkim_canon"; goto expand_bad; }
602 if (Ustrcmp(dkim_canon_expanded, "relaxed") == 0)
603 pdkim_canon = PDKIM_CANON_RELAXED;
604 else if (Ustrcmp(dkim_canon_expanded, "simple") == 0)
605 pdkim_canon = PDKIM_CANON_SIMPLE;
608 log_write(0, LOG_MAIN,
609 "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",
610 dkim_canon_expanded);
611 pdkim_canon = PDKIM_CANON_RELAXED;
614 if ( dkim->dkim_sign_headers
615 && !(dkim_sign_headers_expanded = expand_string(dkim->dkim_sign_headers)))
616 { errwhen = US"dkim_sign_header"; goto expand_bad; }
617 /* else pass NULL, which means default header list */
619 /* Get private key to use. */
621 if (!(dkim_private_key_expanded = expand_string(dkim->dkim_private_key)))
622 { errwhen = US"dkim_private_key"; goto expand_bad; }
624 if ( Ustrlen(dkim_private_key_expanded) == 0
625 || Ustrcmp(dkim_private_key_expanded, "0") == 0
626 || Ustrcmp(dkim_private_key_expanded, "false") == 0
628 continue; /* don't sign, but no error */
630 if (dkim_private_key_expanded[0] == '/')
632 int privkey_fd, off = 0, len;
634 /* Looks like a filename, load the private key. */
636 memset(big_buffer, 0, big_buffer_size);
638 if ((privkey_fd = open(CS dkim_private_key_expanded, O_RDONLY)) < 0)
640 log_write(0, LOG_MAIN | LOG_PANIC, "unable to open "
641 "private key file for reading: %s",
642 dkim_private_key_expanded);
648 if ((len = read(privkey_fd, big_buffer + off, big_buffer_size - 2 - off)) < 0)
650 (void) close(privkey_fd);
651 log_write(0, LOG_MAIN|LOG_PANIC, "unable to read private key file: %s",
652 dkim_private_key_expanded);
659 (void) close(privkey_fd);
660 big_buffer[off] = '\0';
661 dkim_private_key_expanded = big_buffer;
664 if (!(dkim_hash_expanded = expand_string(dkim->dkim_hash)))
665 { errwhen = US"dkim_hash"; goto expand_bad; }
667 if (dkim->dkim_identity)
668 if (!(dkim_identity_expanded = expand_string(dkim->dkim_identity)))
669 { errwhen = US"dkim_identity"; goto expand_bad; }
670 else if (!*dkim_identity_expanded)
671 dkim_identity_expanded = NULL;
673 /*XXX so we currently nail signing to RSA + this hash.
674 Need to extract algo from privkey and check for disallowed combos. */
676 if (!(sig = pdkim_init_sign(&ctx, dkim_signing_domain,
677 dkim_signing_selector,
678 dkim_private_key_expanded,
683 dkim_private_key_expanded[0] = '\0';
685 pdkim_set_optional(sig,
686 CS dkim_sign_headers_expanded,
687 dkim_identity_expanded,
689 pdkim_canon, -1, 0, 0);
691 if (!ctx.sig) /* link sig to context chain */
695 pdkim_signature * n = ctx.sig;
696 while (n->next) n = n->next;
703 pdkim_feed(&ctx, prefix, Ustrlen(prefix));
705 if (lseek(fd, off, SEEK_SET) < 0)
708 while ((sread = read(fd, &buf, sizeof(buf))) > 0)
709 if ((pdkim_rc = pdkim_feed(&ctx, buf, sread)) != PDKIM_OK)
712 /* Handle failed read above. */
715 debug_printf("DKIM: Error reading -K file.\n");
720 /* Build string of headers, one per signature */
722 if ((pdkim_rc = pdkim_feed_finish(&ctx, &sig, errstr)) != PDKIM_OK)
725 for (sigbuf = NULL; sig; sig = sig->next)
726 sigbuf = string_append(sigbuf, 2, US sig->signature_header, US"\r\n");
728 (void) string_from_gstring(sigbuf);
731 store_pool = old_pool;
736 log_write(0, LOG_MAIN|LOG_PANIC,
737 "DKIM: signing failed: %.100s", pdkim_errstr(pdkim_rc));
743 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand %s: %s",
744 errwhen, expand_string_message);
748 # endif /*!MACRO_PREDEF*/
749 #endif /*!DISABLE_DKIM*/