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? */
77 /* check if this looks like a DKIM record */
78 if (strncasecmp(answer, "v=dkim", 6) != 0) continue;
82 return PDKIM_FAIL; /*XXX better error detail? logging? */
95 dkim_exim_verify_init(BOOL dot_stuffing)
97 /* There is a store-reset between header & body reception
98 so cannot use the main pool. Any allocs done by Exim
99 memory-handling must use the perm pool. */
101 dkim_verify_oldpool = store_pool;
102 store_pool = POOL_PERM;
104 /* Free previous context if there is one */
107 pdkim_free_ctx(dkim_verify_ctx);
109 /* Create new context */
111 dkim_verify_ctx = pdkim_init_verify(&dkim_exim_query_dns_txt, dot_stuffing);
112 dkim_collect_input = !!dkim_verify_ctx;
113 dkim_collect_error = NULL;
115 /* Start feed up with any cached data */
118 store_pool = dkim_verify_oldpool;
123 dkim_exim_verify_feed(uschar * data, int len)
127 store_pool = POOL_PERM;
128 if ( dkim_collect_input
129 && (rc = pdkim_feed(dkim_verify_ctx, data, len)) != PDKIM_OK)
131 dkim_collect_error = pdkim_errstr(rc);
132 log_write(0, LOG_MAIN,
133 "DKIM: validation error: %.100s", dkim_collect_error);
134 dkim_collect_input = FALSE;
136 store_pool = dkim_verify_oldpool;
140 /* Log the result for the given signature */
142 dkim_exim_verify_log_sig(pdkim_signature * sig)
149 if ( !dkim_verify_overall
150 && dkim_verify_status
151 ? Ustrcmp(dkim_verify_status, US"pass") == 0
152 : sig->verify_status == PDKIM_VERIFY_PASS
154 dkim_verify_overall = string_copy(sig->domain);
156 if (!LOGGING(dkim_verbose)) return;
158 logmsg = string_catn(NULL, US"DKIM: ", 6);
159 if (!(s = sig->domain)) s = US"<UNSET>";
160 logmsg = string_append(logmsg, 2, "d=", s);
161 if (!(s = sig->selector)) s = US"<UNSET>";
162 logmsg = string_append(logmsg, 2, " s=", s);
163 logmsg = string_append(logmsg, 7,
164 " c=", sig->canon_headers == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
165 "/", sig->canon_body == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
166 " a=", dkim_sig_to_a_tag(sig),
167 string_sprintf(" b=" SIZE_T_FMT,
168 (int)sig->sighash.len > -1 ? sig->sighash.len * 8 : 0));
169 if ((s= sig->identity)) logmsg = string_append(logmsg, 2, " i=", s);
170 if (sig->created > 0) logmsg = string_cat(logmsg,
171 string_sprintf(" t=%lu", sig->created));
172 if (sig->expires > 0) logmsg = string_cat(logmsg,
173 string_sprintf(" x=%lu", sig->expires));
174 if (sig->bodylength > -1) logmsg = string_cat(logmsg,
175 string_sprintf(" l=%lu", sig->bodylength));
177 if ( !dkim_verify_status
178 || ( dkim_verify_status == dkim_exim_expand_query(DKIM_VERIFY_STATUS)
179 && dkim_verify_reason == dkim_exim_expand_query(DKIM_VERIFY_REASON)
181 switch (sig->verify_status)
183 case PDKIM_VERIFY_NONE:
184 logmsg = string_cat(logmsg, US" [not verified]");
187 case PDKIM_VERIFY_INVALID:
188 logmsg = string_cat(logmsg, US" [invalid - ");
189 switch (sig->verify_ext_status)
191 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
192 logmsg = string_cat(logmsg,
193 US"public key record (currently?) unavailable]");
196 case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
197 logmsg = string_cat(logmsg, US"overlong public key record]");
200 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:
201 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT:
202 logmsg = string_cat(logmsg, US"syntax error in public key record]");
205 case PDKIM_VERIFY_INVALID_SIGNATURE_ERROR:
206 logmsg = string_cat(logmsg, US"signature tag missing or invalid]");
209 case PDKIM_VERIFY_INVALID_DKIM_VERSION:
210 logmsg = string_cat(logmsg, US"unsupported DKIM version]");
214 logmsg = string_cat(logmsg, US"unspecified problem]");
218 case PDKIM_VERIFY_FAIL:
219 logmsg = string_cat(logmsg, US" [verification failed - ");
220 switch (sig->verify_ext_status)
222 case PDKIM_VERIFY_FAIL_BODY:
223 logmsg = string_cat(logmsg,
224 US"body hash mismatch (body probably modified in transit)]");
227 case PDKIM_VERIFY_FAIL_MESSAGE:
228 logmsg = string_cat(logmsg,
229 US"signature did not verify "
230 "(headers probably modified in transit)]");
234 logmsg = string_cat(logmsg, US"unspecified reason]");
238 case PDKIM_VERIFY_PASS:
239 logmsg = string_cat(logmsg, US" [verification succeeded]");
243 logmsg = string_append(logmsg, 5,
244 US" [", dkim_verify_status, US" - ", dkim_verify_reason, US"]");
246 log_write(0, LOG_MAIN, "%s", string_from_gstring(logmsg));
251 /* Log a line for each signature */
253 dkim_exim_verify_log_all(void)
255 pdkim_signature * sig;
256 for (sig = dkim_signatures; sig; sig = sig->next) dkim_exim_verify_log_sig(sig);
261 dkim_exim_verify_finish(void)
263 pdkim_signature * sig;
266 const uschar * errstr;
268 store_pool = POOL_PERM;
270 /* Delete eventual previous signature chain */
273 dkim_signatures = NULL;
275 if (dkim_collect_error)
277 log_write(0, LOG_MAIN,
278 "DKIM: Error during validation, disabling signature verification: %.100s",
280 dkim_disable_verify = TRUE;
284 dkim_collect_input = FALSE;
286 /* Finish DKIM operation and fetch link to signatures chain */
288 rc = pdkim_feed_finish(dkim_verify_ctx, &dkim_signatures, &errstr);
291 log_write(0, LOG_MAIN, "DKIM: validation error: %.100s%s%s", pdkim_errstr(rc),
292 errstr ? ": " : "", errstr ? errstr : US"");
296 /* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */
298 for (sig = dkim_signatures; sig; sig = sig->next)
300 if (sig->domain) g = string_append_listele(g, ':', sig->domain);
301 if (sig->identity) g = string_append_listele(g, ':', sig->identity);
304 if (g) dkim_signers = g->s;
307 store_pool = dkim_verify_oldpool;
312 /* Args as per dkim_exim_acl_run() below */
314 dkim_acl_call(uschar * id, gstring ** res_ptr,
315 uschar ** user_msgptr, uschar ** log_msgptr)
319 debug_printf("calling acl_smtp_dkim for dkim_cur_signer='%s'\n", id);
321 rc = acl_check(ACL_WHERE_DKIM, NULL, acl_smtp_dkim, user_msgptr, log_msgptr);
322 dkim_exim_verify_log_sig(dkim_cur_sig);
323 *res_ptr = string_append_listele(*res_ptr, ':', dkim_verify_status);
329 /* For the given identity, run the DKIM ACL once for each matching signature.
332 id Identity to look for in dkim signatures
333 res_ptr ptr to growable string-list of status results,
334 appended to per ACL run
335 user_msgptr where to put a user error (for SMTP response)
336 log_msgptr where to put a logging message (not for SMTP response)
338 Returns: OK access is granted by an ACCEPT verb
339 DISCARD access is granted by a DISCARD verb
340 FAIL access is denied
341 FAIL_DROP access is denied; drop the connection
342 DEFER can't tell at the moment
347 dkim_exim_acl_run(uschar * id, gstring ** res_ptr,
348 uschar ** user_msgptr, uschar ** log_msgptr)
350 pdkim_signature * sig;
354 dkim_verify_status = US"none";
355 dkim_verify_reason = US"";
356 dkim_cur_signer = id;
358 if (dkim_disable_verify || !id || !dkim_verify_ctx)
361 /* Find signatures to run ACL on */
363 for (sig = dkim_signatures; sig; sig = sig->next)
364 if ( (cmp_val = Ustrchr(id, '@') != NULL ? US sig->identity : US sig->domain)
365 && strcmpic(cmp_val, id) == 0
368 /* The "dkim_domain" and "dkim_selector" expansion variables have
369 related globals, since they are used in the signing code too.
370 Instead of inventing separate names for verification, we set
371 them here. This is easy since a domain and selector is guaranteed
372 to be in a signature. The other dkim_* expansion items are
373 dynamically fetched from dkim_cur_sig at expansion time (see
377 dkim_signing_domain = US sig->domain;
378 dkim_signing_selector = US sig->selector;
379 dkim_key_length = sig->sighash.len * 8;
381 /* These two return static strings, so we can compare the addr
382 later to see if the ACL overwrote them. Check that when logging */
384 dkim_verify_status = dkim_exim_expand_query(DKIM_VERIFY_STATUS);
385 dkim_verify_reason = dkim_exim_expand_query(DKIM_VERIFY_REASON);
387 if ((rc = dkim_acl_call(id, res_ptr, user_msgptr, log_msgptr)) != OK)
394 /* No matching sig found. Call ACL once anyway. */
397 return dkim_acl_call(id, res_ptr, user_msgptr, log_msgptr);
402 dkim_exim_expand_defaults(int what)
406 case DKIM_ALGO: return US"";
407 case DKIM_BODYLENGTH: return US"9999999999999";
408 case DKIM_CANON_BODY: return US"";
409 case DKIM_CANON_HEADERS: return US"";
410 case DKIM_COPIEDHEADERS: return US"";
411 case DKIM_CREATED: return US"0";
412 case DKIM_EXPIRES: return US"9999999999999";
413 case DKIM_HEADERNAMES: return US"";
414 case DKIM_IDENTITY: return US"";
415 case DKIM_KEY_GRANULARITY: return US"*";
416 case DKIM_KEY_SRVTYPE: return US"*";
417 case DKIM_KEY_NOTES: return US"";
418 case DKIM_KEY_TESTING: return US"0";
419 case DKIM_NOSUBDOMAINS: return US"0";
420 case DKIM_VERIFY_STATUS: return US"none";
421 case DKIM_VERIFY_REASON: return US"";
422 default: return US"";
428 dkim_exim_expand_query(int what)
430 if (!dkim_verify_ctx || dkim_disable_verify || !dkim_cur_sig)
431 return dkim_exim_expand_defaults(what);
436 return dkim_sig_to_a_tag(dkim_cur_sig);
438 case DKIM_BODYLENGTH:
439 return dkim_cur_sig->bodylength >= 0
440 ? string_sprintf("%ld", dkim_cur_sig->bodylength)
441 : dkim_exim_expand_defaults(what);
443 case DKIM_CANON_BODY:
444 switch (dkim_cur_sig->canon_body)
446 case PDKIM_CANON_RELAXED: return US"relaxed";
447 case PDKIM_CANON_SIMPLE:
448 default: return US"simple";
451 case DKIM_CANON_HEADERS:
452 switch (dkim_cur_sig->canon_headers)
454 case PDKIM_CANON_RELAXED: return US"relaxed";
455 case PDKIM_CANON_SIMPLE:
456 default: return US"simple";
459 case DKIM_COPIEDHEADERS:
460 return dkim_cur_sig->copiedheaders
461 ? US dkim_cur_sig->copiedheaders : dkim_exim_expand_defaults(what);
464 return dkim_cur_sig->created > 0
465 ? string_sprintf("%lu", dkim_cur_sig->created)
466 : dkim_exim_expand_defaults(what);
469 return dkim_cur_sig->expires > 0
470 ? string_sprintf("%lu", dkim_cur_sig->expires)
471 : dkim_exim_expand_defaults(what);
473 case DKIM_HEADERNAMES:
474 return dkim_cur_sig->headernames
475 ? dkim_cur_sig->headernames : dkim_exim_expand_defaults(what);
478 return dkim_cur_sig->identity
479 ? US dkim_cur_sig->identity : dkim_exim_expand_defaults(what);
481 case DKIM_KEY_GRANULARITY:
482 return dkim_cur_sig->pubkey
483 ? dkim_cur_sig->pubkey->granularity
484 ? US dkim_cur_sig->pubkey->granularity
485 : dkim_exim_expand_defaults(what)
486 : dkim_exim_expand_defaults(what);
488 case DKIM_KEY_SRVTYPE:
489 return dkim_cur_sig->pubkey
490 ? dkim_cur_sig->pubkey->srvtype
491 ? US dkim_cur_sig->pubkey->srvtype
492 : dkim_exim_expand_defaults(what)
493 : dkim_exim_expand_defaults(what);
496 return dkim_cur_sig->pubkey
497 ? dkim_cur_sig->pubkey->notes
498 ? US dkim_cur_sig->pubkey->notes
499 : dkim_exim_expand_defaults(what)
500 : dkim_exim_expand_defaults(what);
502 case DKIM_KEY_TESTING:
503 return dkim_cur_sig->pubkey
504 ? dkim_cur_sig->pubkey->testing
506 : dkim_exim_expand_defaults(what)
507 : dkim_exim_expand_defaults(what);
509 case DKIM_NOSUBDOMAINS:
510 return dkim_cur_sig->pubkey
511 ? dkim_cur_sig->pubkey->no_subdomaining
513 : dkim_exim_expand_defaults(what)
514 : dkim_exim_expand_defaults(what);
516 case DKIM_VERIFY_STATUS:
517 switch (dkim_cur_sig->verify_status)
519 case PDKIM_VERIFY_INVALID: return US"invalid";
520 case PDKIM_VERIFY_FAIL: return US"fail";
521 case PDKIM_VERIFY_PASS: return US"pass";
522 case PDKIM_VERIFY_NONE:
523 default: return US"none";
526 case DKIM_VERIFY_REASON:
527 switch (dkim_cur_sig->verify_ext_status)
529 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
530 return US"pubkey_unavailable";
531 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:return US"pubkey_dns_syntax";
532 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT: return US"pubkey_der_syntax";
533 case PDKIM_VERIFY_FAIL_BODY: return US"bodyhash_mismatch";
534 case PDKIM_VERIFY_FAIL_MESSAGE: return US"signature_incorrect";
543 /* Generate signatures for the given file.
544 If a prefix is given, prepend it to the file for the calculations.
547 NULL: error; error string written
548 string: signature header(s), or a zero-length string (not an error)
552 dkim_exim_sign(int fd, off_t off, uschar * prefix,
553 struct ob_dkim * dkim, const uschar ** errstr)
555 const uschar * dkim_domain;
557 gstring * seen_doms = NULL;
559 pdkim_signature * sig;
565 int old_pool = store_pool;
568 store_pool = POOL_MAIN;
570 pdkim_init_context(&ctx, dkim->dot_stuffed, &dkim_exim_query_dns_txt);
572 if (!(dkim_domain = expand_cstring(dkim->dkim_domain)))
573 /* expansion error, do not send message. */
574 { errwhen = US"dkim_domain"; goto expand_bad; }
576 /* Set $dkim_domain expansion variable to each unique domain in list. */
578 while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep, NULL, 0)))
580 const uschar * dkim_sel;
583 if (dkim_signing_domain[0] == '\0')
586 /* Only sign once for each domain, no matter how often it
587 appears in the expanded list. */
589 if (match_isinlist(dkim_signing_domain, CUSS &seen_doms,
590 0, NULL, NULL, MCL_STRING, TRUE, NULL) == OK)
593 seen_doms = string_append_listele(seen_doms, ':', dkim_signing_domain);
595 /* Set $dkim_selector expansion variable to each selector in list,
598 if (!(dkim_sel = expand_string(dkim->dkim_selector)))
599 if (!(dkim_signing_selector = expand_string(dkim->dkim_selector)))
600 { errwhen = US"dkim_selector"; goto expand_bad; }
602 while ((dkim_signing_selector = string_nextinlist(&dkim_sel, &sel_sep,
605 uschar * dkim_canon_expanded;
607 uschar * dkim_sign_headers_expanded = NULL;
608 uschar * dkim_private_key_expanded;
609 uschar * dkim_hash_expanded;
610 uschar * dkim_identity_expanded = NULL;
612 /* Get canonicalization to use */
614 dkim_canon_expanded = dkim->dkim_canon
615 ? expand_string(dkim->dkim_canon) : US"relaxed";
616 if (!dkim_canon_expanded) /* expansion error, do not send message. */
617 { errwhen = US"dkim_canon"; goto expand_bad; }
619 if (Ustrcmp(dkim_canon_expanded, "relaxed") == 0)
620 pdkim_canon = PDKIM_CANON_RELAXED;
621 else if (Ustrcmp(dkim_canon_expanded, "simple") == 0)
622 pdkim_canon = PDKIM_CANON_SIMPLE;
625 log_write(0, LOG_MAIN,
626 "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",
627 dkim_canon_expanded);
628 pdkim_canon = PDKIM_CANON_RELAXED;
631 if ( dkim->dkim_sign_headers
632 && !(dkim_sign_headers_expanded = expand_string(dkim->dkim_sign_headers)))
633 { errwhen = US"dkim_sign_header"; goto expand_bad; }
634 /* else pass NULL, which means default header list */
636 /* Get private key to use. */
638 if (!(dkim_private_key_expanded = expand_string(dkim->dkim_private_key)))
639 { errwhen = US"dkim_private_key"; goto expand_bad; }
641 if ( Ustrlen(dkim_private_key_expanded) == 0
642 || Ustrcmp(dkim_private_key_expanded, "0") == 0
643 || Ustrcmp(dkim_private_key_expanded, "false") == 0
645 continue; /* don't sign, but no error */
647 if (dkim_private_key_expanded[0] == '/')
649 int privkey_fd, off = 0, len;
651 /* Looks like a filename, load the private key. */
653 memset(big_buffer, 0, big_buffer_size);
655 if ((privkey_fd = open(CS dkim_private_key_expanded, O_RDONLY)) < 0)
657 log_write(0, LOG_MAIN | LOG_PANIC, "unable to open "
658 "private key file for reading: %s",
659 dkim_private_key_expanded);
665 if ((len = read(privkey_fd, big_buffer + off, big_buffer_size - 2 - off)) < 0)
667 (void) close(privkey_fd);
668 log_write(0, LOG_MAIN|LOG_PANIC, "unable to read private key file: %s",
669 dkim_private_key_expanded);
676 (void) close(privkey_fd);
677 big_buffer[off] = '\0';
678 dkim_private_key_expanded = big_buffer;
681 if (!(dkim_hash_expanded = expand_string(dkim->dkim_hash)))
682 { errwhen = US"dkim_hash"; goto expand_bad; }
684 if (dkim->dkim_identity)
685 if (!(dkim_identity_expanded = expand_string(dkim->dkim_identity)))
686 { errwhen = US"dkim_identity"; goto expand_bad; }
687 else if (!*dkim_identity_expanded)
688 dkim_identity_expanded = NULL;
690 /*XXX so we currently nail signing to RSA + this hash.
691 Need to extract algo from privkey and check for disallowed combos. */
693 if (!(sig = pdkim_init_sign(&ctx, dkim_signing_domain,
694 dkim_signing_selector,
695 dkim_private_key_expanded,
700 dkim_private_key_expanded[0] = '\0';
702 pdkim_set_optional(sig,
703 CS dkim_sign_headers_expanded,
704 CS dkim_identity_expanded,
706 pdkim_canon, -1, 0, 0);
708 if (!ctx.sig) /* link sig to context chain */
712 pdkim_signature * n = ctx.sig;
713 while (n->next) n = n->next;
720 DEBUG(D_transport) debug_printf("DKIM: no viable signatures to use\n");
721 sigbuf = string_get(1); /* return a zero-len string */
725 if (prefix && (pdkim_feed(&ctx, prefix, Ustrlen(prefix))) != PDKIM_OK)
728 if (lseek(fd, off, SEEK_SET) < 0)
731 while ((sread = read(fd, &buf, sizeof(buf))) > 0)
732 if ((pdkim_rc = pdkim_feed(&ctx, buf, sread)) != PDKIM_OK)
735 /* Handle failed read above. */
738 debug_printf("DKIM: Error reading -K file.\n");
743 /* Build string of headers, one per signature */
745 if ((pdkim_rc = pdkim_feed_finish(&ctx, &sig, errstr)) != PDKIM_OK)
748 for (sigbuf = NULL; sig; sig = sig->next)
749 sigbuf = string_append(sigbuf, 2, US sig->signature_header, US"\r\n");
752 (void) string_from_gstring(sigbuf);
753 store_pool = old_pool;
758 log_write(0, LOG_MAIN|LOG_PANIC,
759 "DKIM: signing failed: %.100s", pdkim_errstr(pdkim_rc));
765 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand %s: %s",
766 errwhen, expand_string_message);
770 # endif /*!MACRO_PREDEF*/
771 #endif /*!DISABLE_DKIM*/