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.
42 dkim_exim_query_dns_txt(char * name)
49 lookup_dnssec_authenticated = NULL;
50 if (dns_lookup(&dnsa, US name, T_TXT, NULL) != DNS_SUCCEED)
51 return NULL; /*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)
62 /* Copy record content to the answer buffer */
64 while (rr_offset < rr->size)
66 uschar len = rr->data[rr_offset++];
68 g = string_catn(g, US(rr->data + rr_offset), len);
69 if (g->ptr >= PDKIM_DNS_TXT_MAX_RECLEN)
75 /* check if this looks like a DKIM record */
76 if (Ustrncmp(g->s, "v=", 2) != 0 || strncasecmp(CS g->s, "v=dkim", 6) == 0)
78 store_reset(g->s + g->ptr + 1);
79 return string_from_gstring(g);
82 if (g) g->ptr = 0; /* overwrite previous record */
86 if (g) store_reset(g);
87 return NULL; /*XXX better error detail? logging? */
100 dkim_exim_verify_init(BOOL dot_stuffing)
102 /* There is a store-reset between header & body reception
103 so cannot use the main pool. Any allocs done by Exim
104 memory-handling must use the perm pool. */
106 dkim_verify_oldpool = store_pool;
107 store_pool = POOL_PERM;
109 /* Free previous context if there is one */
112 pdkim_free_ctx(dkim_verify_ctx);
114 /* Create new context */
116 dkim_verify_ctx = pdkim_init_verify(&dkim_exim_query_dns_txt, dot_stuffing);
117 dkim_collect_input = !!dkim_verify_ctx;
118 dkim_collect_error = NULL;
120 /* Start feed up with any cached data */
123 store_pool = dkim_verify_oldpool;
128 dkim_exim_verify_feed(uschar * data, int len)
132 store_pool = POOL_PERM;
133 if ( dkim_collect_input
134 && (rc = pdkim_feed(dkim_verify_ctx, data, len)) != PDKIM_OK)
136 dkim_collect_error = pdkim_errstr(rc);
137 log_write(0, LOG_MAIN,
138 "DKIM: validation error: %.100s", dkim_collect_error);
139 dkim_collect_input = FALSE;
141 store_pool = dkim_verify_oldpool;
145 /* Log the result for the given signature */
147 dkim_exim_verify_log_sig(pdkim_signature * sig)
154 if ( !dkim_verify_overall
155 && dkim_verify_status
156 ? Ustrcmp(dkim_verify_status, US"pass") == 0
157 : sig->verify_status == PDKIM_VERIFY_PASS
159 dkim_verify_overall = string_copy(sig->domain);
161 if (!LOGGING(dkim_verbose)) return;
163 logmsg = string_catn(NULL, US"DKIM: ", 6);
164 if (!(s = sig->domain)) s = US"<UNSET>";
165 logmsg = string_append(logmsg, 2, "d=", s);
166 if (!(s = sig->selector)) s = US"<UNSET>";
167 logmsg = string_append(logmsg, 2, " s=", s);
168 logmsg = string_append(logmsg, 7,
169 " c=", sig->canon_headers == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
170 "/", sig->canon_body == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
171 " a=", dkim_sig_to_a_tag(sig),
172 string_sprintf(" b=" SIZE_T_FMT,
173 (int)sig->sighash.len > -1 ? sig->sighash.len * 8 : 0));
174 if ((s= sig->identity)) logmsg = string_append(logmsg, 2, " i=", s);
175 if (sig->created > 0) logmsg = string_cat(logmsg,
176 string_sprintf(" t=%lu", sig->created));
177 if (sig->expires > 0) logmsg = string_cat(logmsg,
178 string_sprintf(" x=%lu", sig->expires));
179 if (sig->bodylength > -1) logmsg = string_cat(logmsg,
180 string_sprintf(" l=%lu", sig->bodylength));
182 if ( !dkim_verify_status
183 || ( dkim_verify_status == dkim_exim_expand_query(DKIM_VERIFY_STATUS)
184 && dkim_verify_reason == dkim_exim_expand_query(DKIM_VERIFY_REASON)
186 switch (sig->verify_status)
188 case PDKIM_VERIFY_NONE:
189 logmsg = string_cat(logmsg, US" [not verified]");
192 case PDKIM_VERIFY_INVALID:
193 logmsg = string_cat(logmsg, US" [invalid - ");
194 switch (sig->verify_ext_status)
196 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
197 logmsg = string_cat(logmsg,
198 US"public key record (currently?) unavailable]");
201 case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
202 logmsg = string_cat(logmsg, US"overlong public key record]");
205 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:
206 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT:
207 logmsg = string_cat(logmsg, US"syntax error in public key record]");
210 case PDKIM_VERIFY_INVALID_SIGNATURE_ERROR:
211 logmsg = string_cat(logmsg, US"signature tag missing or invalid]");
214 case PDKIM_VERIFY_INVALID_DKIM_VERSION:
215 logmsg = string_cat(logmsg, US"unsupported DKIM version]");
219 logmsg = string_cat(logmsg, US"unspecified problem]");
223 case PDKIM_VERIFY_FAIL:
224 logmsg = string_cat(logmsg, US" [verification failed - ");
225 switch (sig->verify_ext_status)
227 case PDKIM_VERIFY_FAIL_BODY:
228 logmsg = string_cat(logmsg,
229 US"body hash mismatch (body probably modified in transit)]");
232 case PDKIM_VERIFY_FAIL_MESSAGE:
233 logmsg = string_cat(logmsg,
234 US"signature did not verify "
235 "(headers probably modified in transit)]");
239 logmsg = string_cat(logmsg, US"unspecified reason]");
243 case PDKIM_VERIFY_PASS:
244 logmsg = string_cat(logmsg, US" [verification succeeded]");
248 logmsg = string_append(logmsg, 5,
249 US" [", dkim_verify_status, US" - ", dkim_verify_reason, US"]");
251 log_write(0, LOG_MAIN, "%s", string_from_gstring(logmsg));
256 /* Log a line for each signature */
258 dkim_exim_verify_log_all(void)
260 pdkim_signature * sig;
261 for (sig = dkim_signatures; sig; sig = sig->next) dkim_exim_verify_log_sig(sig);
266 dkim_exim_verify_finish(void)
268 pdkim_signature * sig;
271 const uschar * errstr;
273 store_pool = POOL_PERM;
275 /* Delete eventual previous signature chain */
278 dkim_signatures = NULL;
280 if (dkim_collect_error)
282 log_write(0, LOG_MAIN,
283 "DKIM: Error during validation, disabling signature verification: %.100s",
285 dkim_disable_verify = TRUE;
289 dkim_collect_input = FALSE;
291 /* Finish DKIM operation and fetch link to signatures chain */
293 rc = pdkim_feed_finish(dkim_verify_ctx, &dkim_signatures, &errstr);
296 log_write(0, LOG_MAIN, "DKIM: validation error: %.100s%s%s", pdkim_errstr(rc),
297 errstr ? ": " : "", errstr ? errstr : US"");
301 /* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */
303 for (sig = dkim_signatures; sig; sig = sig->next)
305 if (sig->domain) g = string_append_listele(g, ':', sig->domain);
306 if (sig->identity) g = string_append_listele(g, ':', sig->identity);
309 if (g) dkim_signers = g->s;
312 store_pool = dkim_verify_oldpool;
317 /* Args as per dkim_exim_acl_run() below */
319 dkim_acl_call(uschar * id, gstring ** res_ptr,
320 uschar ** user_msgptr, uschar ** log_msgptr)
324 debug_printf("calling acl_smtp_dkim for dkim_cur_signer='%s'\n", id);
326 rc = acl_check(ACL_WHERE_DKIM, NULL, acl_smtp_dkim, user_msgptr, log_msgptr);
327 dkim_exim_verify_log_sig(dkim_cur_sig);
328 *res_ptr = string_append_listele(*res_ptr, ':', dkim_verify_status);
334 /* For the given identity, run the DKIM ACL once for each matching signature.
337 id Identity to look for in dkim signatures
338 res_ptr ptr to growable string-list of status results,
339 appended to per ACL run
340 user_msgptr where to put a user error (for SMTP response)
341 log_msgptr where to put a logging message (not for SMTP response)
343 Returns: OK access is granted by an ACCEPT verb
344 DISCARD access is granted by a DISCARD verb
345 FAIL access is denied
346 FAIL_DROP access is denied; drop the connection
347 DEFER can't tell at the moment
352 dkim_exim_acl_run(uschar * id, gstring ** res_ptr,
353 uschar ** user_msgptr, uschar ** log_msgptr)
355 pdkim_signature * sig;
359 dkim_verify_status = US"none";
360 dkim_verify_reason = US"";
361 dkim_cur_signer = id;
363 if (dkim_disable_verify || !id || !dkim_verify_ctx)
366 /* Find signatures to run ACL on */
368 for (sig = dkim_signatures; sig; sig = sig->next)
369 if ( (cmp_val = Ustrchr(id, '@') != NULL ? US sig->identity : US sig->domain)
370 && strcmpic(cmp_val, id) == 0
373 /* The "dkim_domain" and "dkim_selector" expansion variables have
374 related globals, since they are used in the signing code too.
375 Instead of inventing separate names for verification, we set
376 them here. This is easy since a domain and selector is guaranteed
377 to be in a signature. The other dkim_* expansion items are
378 dynamically fetched from dkim_cur_sig at expansion time (see
382 dkim_signing_domain = US sig->domain;
383 dkim_signing_selector = US sig->selector;
384 dkim_key_length = sig->sighash.len * 8;
386 /* These two return static strings, so we can compare the addr
387 later to see if the ACL overwrote them. Check that when logging */
389 dkim_verify_status = dkim_exim_expand_query(DKIM_VERIFY_STATUS);
390 dkim_verify_reason = dkim_exim_expand_query(DKIM_VERIFY_REASON);
392 if ((rc = dkim_acl_call(id, res_ptr, user_msgptr, log_msgptr)) != OK)
399 /* No matching sig found. Call ACL once anyway. */
402 return dkim_acl_call(id, res_ptr, user_msgptr, log_msgptr);
407 dkim_exim_expand_defaults(int what)
411 case DKIM_ALGO: return US"";
412 case DKIM_BODYLENGTH: return US"9999999999999";
413 case DKIM_CANON_BODY: return US"";
414 case DKIM_CANON_HEADERS: return US"";
415 case DKIM_COPIEDHEADERS: return US"";
416 case DKIM_CREATED: return US"0";
417 case DKIM_EXPIRES: return US"9999999999999";
418 case DKIM_HEADERNAMES: return US"";
419 case DKIM_IDENTITY: return US"";
420 case DKIM_KEY_GRANULARITY: return US"*";
421 case DKIM_KEY_SRVTYPE: return US"*";
422 case DKIM_KEY_NOTES: return US"";
423 case DKIM_KEY_TESTING: return US"0";
424 case DKIM_NOSUBDOMAINS: return US"0";
425 case DKIM_VERIFY_STATUS: return US"none";
426 case DKIM_VERIFY_REASON: return US"";
427 default: return US"";
433 dkim_exim_expand_query(int what)
435 if (!dkim_verify_ctx || dkim_disable_verify || !dkim_cur_sig)
436 return dkim_exim_expand_defaults(what);
441 return dkim_sig_to_a_tag(dkim_cur_sig);
443 case DKIM_BODYLENGTH:
444 return dkim_cur_sig->bodylength >= 0
445 ? string_sprintf("%ld", dkim_cur_sig->bodylength)
446 : dkim_exim_expand_defaults(what);
448 case DKIM_CANON_BODY:
449 switch (dkim_cur_sig->canon_body)
451 case PDKIM_CANON_RELAXED: return US"relaxed";
452 case PDKIM_CANON_SIMPLE:
453 default: return US"simple";
456 case DKIM_CANON_HEADERS:
457 switch (dkim_cur_sig->canon_headers)
459 case PDKIM_CANON_RELAXED: return US"relaxed";
460 case PDKIM_CANON_SIMPLE:
461 default: return US"simple";
464 case DKIM_COPIEDHEADERS:
465 return dkim_cur_sig->copiedheaders
466 ? US dkim_cur_sig->copiedheaders : dkim_exim_expand_defaults(what);
469 return dkim_cur_sig->created > 0
470 ? string_sprintf("%lu", dkim_cur_sig->created)
471 : dkim_exim_expand_defaults(what);
474 return dkim_cur_sig->expires > 0
475 ? string_sprintf("%lu", dkim_cur_sig->expires)
476 : dkim_exim_expand_defaults(what);
478 case DKIM_HEADERNAMES:
479 return dkim_cur_sig->headernames
480 ? dkim_cur_sig->headernames : dkim_exim_expand_defaults(what);
483 return dkim_cur_sig->identity
484 ? US dkim_cur_sig->identity : dkim_exim_expand_defaults(what);
486 case DKIM_KEY_GRANULARITY:
487 return dkim_cur_sig->pubkey
488 ? dkim_cur_sig->pubkey->granularity
489 ? US dkim_cur_sig->pubkey->granularity
490 : dkim_exim_expand_defaults(what)
491 : dkim_exim_expand_defaults(what);
493 case DKIM_KEY_SRVTYPE:
494 return dkim_cur_sig->pubkey
495 ? dkim_cur_sig->pubkey->srvtype
496 ? US dkim_cur_sig->pubkey->srvtype
497 : dkim_exim_expand_defaults(what)
498 : dkim_exim_expand_defaults(what);
501 return dkim_cur_sig->pubkey
502 ? dkim_cur_sig->pubkey->notes
503 ? US dkim_cur_sig->pubkey->notes
504 : dkim_exim_expand_defaults(what)
505 : dkim_exim_expand_defaults(what);
507 case DKIM_KEY_TESTING:
508 return dkim_cur_sig->pubkey
509 ? dkim_cur_sig->pubkey->testing
511 : dkim_exim_expand_defaults(what)
512 : dkim_exim_expand_defaults(what);
514 case DKIM_NOSUBDOMAINS:
515 return dkim_cur_sig->pubkey
516 ? dkim_cur_sig->pubkey->no_subdomaining
518 : dkim_exim_expand_defaults(what)
519 : dkim_exim_expand_defaults(what);
521 case DKIM_VERIFY_STATUS:
522 switch (dkim_cur_sig->verify_status)
524 case PDKIM_VERIFY_INVALID: return US"invalid";
525 case PDKIM_VERIFY_FAIL: return US"fail";
526 case PDKIM_VERIFY_PASS: return US"pass";
527 case PDKIM_VERIFY_NONE:
528 default: return US"none";
531 case DKIM_VERIFY_REASON:
532 switch (dkim_cur_sig->verify_ext_status)
534 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
535 return US"pubkey_unavailable";
536 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:return US"pubkey_dns_syntax";
537 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT: return US"pubkey_der_syntax";
538 case PDKIM_VERIFY_FAIL_BODY: return US"bodyhash_mismatch";
539 case PDKIM_VERIFY_FAIL_MESSAGE: return US"signature_incorrect";
548 /* Generate signatures for the given file.
549 If a prefix is given, prepend it to the file for the calculations.
552 NULL: error; error string written
553 string: signature header(s), or a zero-length string (not an error)
557 dkim_exim_sign(int fd, off_t off, uschar * prefix,
558 struct ob_dkim * dkim, const uschar ** errstr)
560 const uschar * dkim_domain;
562 gstring * seen_doms = NULL;
564 pdkim_signature * sig;
570 int old_pool = store_pool;
573 store_pool = POOL_MAIN;
575 pdkim_init_context(&ctx, dkim->dot_stuffed, &dkim_exim_query_dns_txt);
577 if (!(dkim_domain = expand_cstring(dkim->dkim_domain)))
578 /* expansion error, do not send message. */
579 { errwhen = US"dkim_domain"; goto expand_bad; }
581 /* Set $dkim_domain expansion variable to each unique domain in list. */
583 while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep, NULL, 0)))
585 const uschar * dkim_sel;
588 if (dkim_signing_domain[0] == '\0')
591 /* Only sign once for each domain, no matter how often it
592 appears in the expanded list. */
594 if (match_isinlist(dkim_signing_domain, CUSS &seen_doms,
595 0, NULL, NULL, MCL_STRING, TRUE, NULL) == OK)
598 seen_doms = string_append_listele(seen_doms, ':', dkim_signing_domain);
600 /* Set $dkim_selector expansion variable to each selector in list,
603 if (!(dkim_sel = expand_string(dkim->dkim_selector)))
604 if (!(dkim_signing_selector = expand_string(dkim->dkim_selector)))
605 { errwhen = US"dkim_selector"; goto expand_bad; }
607 while ((dkim_signing_selector = string_nextinlist(&dkim_sel, &sel_sep,
610 uschar * dkim_canon_expanded;
612 uschar * dkim_sign_headers_expanded = NULL;
613 uschar * dkim_private_key_expanded;
614 uschar * dkim_hash_expanded;
615 uschar * dkim_identity_expanded = NULL;
617 /* Get canonicalization to use */
619 dkim_canon_expanded = dkim->dkim_canon
620 ? expand_string(dkim->dkim_canon) : US"relaxed";
621 if (!dkim_canon_expanded) /* expansion error, do not send message. */
622 { errwhen = US"dkim_canon"; goto expand_bad; }
624 if (Ustrcmp(dkim_canon_expanded, "relaxed") == 0)
625 pdkim_canon = PDKIM_CANON_RELAXED;
626 else if (Ustrcmp(dkim_canon_expanded, "simple") == 0)
627 pdkim_canon = PDKIM_CANON_SIMPLE;
630 log_write(0, LOG_MAIN,
631 "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",
632 dkim_canon_expanded);
633 pdkim_canon = PDKIM_CANON_RELAXED;
636 if ( dkim->dkim_sign_headers
637 && !(dkim_sign_headers_expanded = expand_string(dkim->dkim_sign_headers)))
638 { errwhen = US"dkim_sign_header"; goto expand_bad; }
639 /* else pass NULL, which means default header list */
641 /* Get private key to use. */
643 if (!(dkim_private_key_expanded = expand_string(dkim->dkim_private_key)))
644 { errwhen = US"dkim_private_key"; goto expand_bad; }
646 if ( Ustrlen(dkim_private_key_expanded) == 0
647 || Ustrcmp(dkim_private_key_expanded, "0") == 0
648 || Ustrcmp(dkim_private_key_expanded, "false") == 0
650 continue; /* don't sign, but no error */
652 if (dkim_private_key_expanded[0] == '/')
654 int privkey_fd, off = 0, len;
656 /* Looks like a filename, load the private key. */
658 memset(big_buffer, 0, big_buffer_size);
660 if ((privkey_fd = open(CS dkim_private_key_expanded, O_RDONLY)) < 0)
662 log_write(0, LOG_MAIN | LOG_PANIC, "unable to open "
663 "private key file for reading: %s",
664 dkim_private_key_expanded);
670 if ((len = read(privkey_fd, big_buffer + off, big_buffer_size - 2 - off)) < 0)
672 (void) close(privkey_fd);
673 log_write(0, LOG_MAIN|LOG_PANIC, "unable to read private key file: %s",
674 dkim_private_key_expanded);
681 (void) close(privkey_fd);
682 big_buffer[off] = '\0';
683 dkim_private_key_expanded = big_buffer;
686 if (!(dkim_hash_expanded = expand_string(dkim->dkim_hash)))
687 { errwhen = US"dkim_hash"; goto expand_bad; }
689 if (dkim->dkim_identity)
690 if (!(dkim_identity_expanded = expand_string(dkim->dkim_identity)))
691 { errwhen = US"dkim_identity"; goto expand_bad; }
692 else if (!*dkim_identity_expanded)
693 dkim_identity_expanded = NULL;
695 /*XXX so we currently nail signing to RSA + this hash.
696 Need to extract algo from privkey and check for disallowed combos. */
698 if (!(sig = pdkim_init_sign(&ctx, dkim_signing_domain,
699 dkim_signing_selector,
700 dkim_private_key_expanded,
705 dkim_private_key_expanded[0] = '\0';
707 pdkim_set_optional(sig,
708 CS dkim_sign_headers_expanded,
709 CS dkim_identity_expanded,
711 pdkim_canon, -1, 0, 0);
713 if (!pdkim_set_bodyhash(&ctx, sig))
716 if (!ctx.sig) /* link sig to context chain */
720 pdkim_signature * n = ctx.sig;
721 while (n->next) n = n->next;
728 DEBUG(D_transport) debug_printf("DKIM: no viable signatures to use\n");
729 sigbuf = string_get(1); /* return a zero-len string */
733 if (prefix && (pdkim_rc = pdkim_feed(&ctx, prefix, Ustrlen(prefix))) != PDKIM_OK)
736 if (lseek(fd, off, SEEK_SET) < 0)
739 while ((sread = read(fd, &buf, sizeof(buf))) > 0)
740 if ((pdkim_rc = pdkim_feed(&ctx, buf, sread)) != PDKIM_OK)
743 /* Handle failed read above. */
746 debug_printf("DKIM: Error reading -K file.\n");
751 /* Build string of headers, one per signature */
753 if ((pdkim_rc = pdkim_feed_finish(&ctx, &sig, errstr)) != PDKIM_OK)
756 for (sigbuf = NULL; sig; sig = sig->next)
757 sigbuf = string_append(sigbuf, 2, US sig->signature_header, US"\r\n");
760 (void) string_from_gstring(sigbuf);
761 store_pool = old_pool;
766 log_write(0, LOG_MAIN|LOG_PANIC,
767 "DKIM: signing failed: %.100s", pdkim_errstr(pdkim_rc));
773 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand %s: %s",
774 errwhen, expand_string_message);
778 # endif /*!MACRO_PREDEF*/
779 #endif /*!DISABLE_DKIM*/