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 logmsg = string_catn(NULL, US"DKIM: ", 6);
150 if (!(s = sig->domain)) s = US"<UNSET>";
151 logmsg = string_append(logmsg, 2, "d=", s);
152 if (!(s = sig->selector)) s = US"<UNSET>";
153 logmsg = string_append(logmsg, 2, " s=", s);
154 logmsg = string_append(logmsg, 7,
155 " c=", sig->canon_headers == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
156 "/", sig->canon_body == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
157 " a=", dkim_sig_to_a_tag(sig),
158 string_sprintf(" b=" SIZE_T_FMT,
159 (int)sig->sighash.len > -1 ? sig->sighash.len * 8 : 0));
160 if ((s= sig->identity)) logmsg = string_append(logmsg, 2, " i=", s);
161 if (sig->created > 0) logmsg = string_cat(logmsg,
162 string_sprintf(" t=%lu", sig->created));
163 if (sig->expires > 0) logmsg = string_cat(logmsg,
164 string_sprintf(" x=%lu", sig->expires));
165 if (sig->bodylength > -1) logmsg = string_cat(logmsg,
166 string_sprintf(" l=%lu", sig->bodylength));
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 switch (sig->verify_status)
174 case PDKIM_VERIFY_NONE:
175 logmsg = string_cat(logmsg, US" [not verified]");
178 case PDKIM_VERIFY_INVALID:
179 logmsg = string_cat(logmsg, US" [invalid - ");
180 switch (sig->verify_ext_status)
182 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
183 logmsg = string_cat(logmsg,
184 US"public key record (currently?) unavailable]");
187 case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
188 logmsg = string_cat(logmsg, US"overlong public key record]");
191 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:
192 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT:
193 logmsg = string_cat(logmsg, US"syntax error in public key record]");
196 case PDKIM_VERIFY_INVALID_SIGNATURE_ERROR:
197 logmsg = string_cat(logmsg, US"signature tag missing or invalid]");
200 case PDKIM_VERIFY_INVALID_DKIM_VERSION:
201 logmsg = string_cat(logmsg, US"unsupported DKIM version]");
205 logmsg = string_cat(logmsg, US"unspecified problem]");
209 case PDKIM_VERIFY_FAIL:
210 logmsg = string_cat(logmsg, US" [verification failed - ");
211 switch (sig->verify_ext_status)
213 case PDKIM_VERIFY_FAIL_BODY:
214 logmsg = string_cat(logmsg,
215 US"body hash mismatch (body probably modified in transit)]");
218 case PDKIM_VERIFY_FAIL_MESSAGE:
219 logmsg = string_cat(logmsg,
220 US"signature did not verify "
221 "(headers probably modified in transit)]");
225 logmsg = string_cat(logmsg, US"unspecified reason]");
229 case PDKIM_VERIFY_PASS:
230 logmsg = string_cat(logmsg, US" [verification succeeded]");
234 logmsg = string_append(logmsg, 5,
235 US" [", dkim_verify_status, US" - ", dkim_verify_reason, US"]");
237 log_write(0, LOG_MAIN, "%s", string_from_gstring(logmsg));
242 /* Log a line for each signature */
244 dkim_exim_verify_log_all(void)
246 pdkim_signature * sig;
247 for (sig = dkim_signatures; sig; sig = sig->next) dkim_exim_verify_log_sig(sig);
252 dkim_exim_verify_finish(void)
254 pdkim_signature * sig;
257 const uschar * errstr;
259 store_pool = POOL_PERM;
261 /* Delete eventual previous signature chain */
264 dkim_signatures = NULL;
266 if (dkim_collect_error)
268 log_write(0, LOG_MAIN,
269 "DKIM: Error during validation, disabling signature verification: %.100s",
271 dkim_disable_verify = TRUE;
275 dkim_collect_input = FALSE;
277 /* Finish DKIM operation and fetch link to signatures chain */
279 rc = pdkim_feed_finish(dkim_verify_ctx, &dkim_signatures, &errstr);
282 log_write(0, LOG_MAIN, "DKIM: validation error: %.100s%s%s", pdkim_errstr(rc),
283 errstr ? ": " : "", errstr ? errstr : US"");
287 /* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */
289 for (sig = dkim_signatures; sig; sig = sig->next)
291 if (sig->domain) g = string_append_listele(g, ':', sig->domain);
292 if (sig->identity) g = string_append_listele(g, ':', sig->identity);
295 if (g) dkim_signers = g->s;
298 store_pool = dkim_verify_oldpool;
303 /* Args as per dkim_exim_acl_run() below */
305 dkim_acl_call(uschar * id, gstring ** res_ptr,
306 uschar ** user_msgptr, uschar ** log_msgptr)
310 debug_printf("calling acl_smtp_dkim for dkim_cur_signer='%s'\n", id);
312 rc = acl_check(ACL_WHERE_DKIM, NULL, acl_smtp_dkim, user_msgptr, log_msgptr);
313 dkim_exim_verify_log_sig(dkim_cur_sig);
314 *res_ptr = string_append_listele(*res_ptr, ':', dkim_verify_status);
320 /* For the given identity, run the DKIM ACL once for each matching signature.
323 id Identity to look for in dkim signatures
324 res_ptr ptr to growable string-list of status results,
325 appended to per ACL run
326 user_msgptr where to put a user error (for SMTP response)
327 log_msgptr where to put a logging message (not for SMTP response)
329 Returns: OK access is granted by an ACCEPT verb
330 DISCARD access is granted by a DISCARD verb
331 FAIL access is denied
332 FAIL_DROP access is denied; drop the connection
333 DEFER can't tell at the moment
338 dkim_exim_acl_run(uschar * id, gstring ** res_ptr,
339 uschar ** user_msgptr, uschar ** log_msgptr)
341 pdkim_signature * sig;
345 dkim_verify_status = US"none";
346 dkim_verify_reason = US"";
347 dkim_cur_signer = id;
349 if (dkim_disable_verify || !id || !dkim_verify_ctx)
352 /* Find signatures to run ACL on */
354 for (sig = dkim_signatures; sig; sig = sig->next)
355 if ( (cmp_val = Ustrchr(id, '@') != NULL ? US sig->identity : US sig->domain)
356 && strcmpic(cmp_val, id) == 0
359 /* The "dkim_domain" and "dkim_selector" expansion variables have
360 related globals, since they are used in the signing code too.
361 Instead of inventing separate names for verification, we set
362 them here. This is easy since a domain and selector is guaranteed
363 to be in a signature. The other dkim_* expansion items are
364 dynamically fetched from dkim_cur_sig at expansion time (see
368 dkim_signing_domain = US sig->domain;
369 dkim_signing_selector = US sig->selector;
370 dkim_key_length = sig->sighash.len * 8;
372 /* These two return static strings, so we can compare the addr
373 later to see if the ACL overwrote them. Check that when logging */
375 dkim_verify_status = dkim_exim_expand_query(DKIM_VERIFY_STATUS);
376 dkim_verify_reason = dkim_exim_expand_query(DKIM_VERIFY_REASON);
378 if ((rc = dkim_acl_call(id, res_ptr, user_msgptr, log_msgptr)) != OK)
385 /* No matching sig found. Call ACL once anyway. */
388 return dkim_acl_call(id, res_ptr, user_msgptr, log_msgptr);
393 dkim_exim_expand_defaults(int what)
397 case DKIM_ALGO: return US"";
398 case DKIM_BODYLENGTH: return US"9999999999999";
399 case DKIM_CANON_BODY: return US"";
400 case DKIM_CANON_HEADERS: return US"";
401 case DKIM_COPIEDHEADERS: return US"";
402 case DKIM_CREATED: return US"0";
403 case DKIM_EXPIRES: return US"9999999999999";
404 case DKIM_HEADERNAMES: return US"";
405 case DKIM_IDENTITY: return US"";
406 case DKIM_KEY_GRANULARITY: return US"*";
407 case DKIM_KEY_SRVTYPE: return US"*";
408 case DKIM_KEY_NOTES: return US"";
409 case DKIM_KEY_TESTING: return US"0";
410 case DKIM_NOSUBDOMAINS: return US"0";
411 case DKIM_VERIFY_STATUS: return US"none";
412 case DKIM_VERIFY_REASON: return US"";
413 default: return US"";
419 dkim_exim_expand_query(int what)
421 if (!dkim_verify_ctx || dkim_disable_verify || !dkim_cur_sig)
422 return dkim_exim_expand_defaults(what);
427 return dkim_sig_to_a_tag(dkim_cur_sig);
429 case DKIM_BODYLENGTH:
430 return dkim_cur_sig->bodylength >= 0
431 ? string_sprintf("%ld", dkim_cur_sig->bodylength)
432 : dkim_exim_expand_defaults(what);
434 case DKIM_CANON_BODY:
435 switch (dkim_cur_sig->canon_body)
437 case PDKIM_CANON_RELAXED: return US"relaxed";
438 case PDKIM_CANON_SIMPLE:
439 default: return US"simple";
442 case DKIM_CANON_HEADERS:
443 switch (dkim_cur_sig->canon_headers)
445 case PDKIM_CANON_RELAXED: return US"relaxed";
446 case PDKIM_CANON_SIMPLE:
447 default: return US"simple";
450 case DKIM_COPIEDHEADERS:
451 return dkim_cur_sig->copiedheaders
452 ? US dkim_cur_sig->copiedheaders : dkim_exim_expand_defaults(what);
455 return dkim_cur_sig->created > 0
456 ? string_sprintf("%lu", dkim_cur_sig->created)
457 : dkim_exim_expand_defaults(what);
460 return dkim_cur_sig->expires > 0
461 ? string_sprintf("%lu", dkim_cur_sig->expires)
462 : dkim_exim_expand_defaults(what);
464 case DKIM_HEADERNAMES:
465 return dkim_cur_sig->headernames
466 ? dkim_cur_sig->headernames : dkim_exim_expand_defaults(what);
469 return dkim_cur_sig->identity
470 ? US dkim_cur_sig->identity : dkim_exim_expand_defaults(what);
472 case DKIM_KEY_GRANULARITY:
473 return dkim_cur_sig->pubkey
474 ? dkim_cur_sig->pubkey->granularity
475 ? US dkim_cur_sig->pubkey->granularity
476 : dkim_exim_expand_defaults(what)
477 : dkim_exim_expand_defaults(what);
479 case DKIM_KEY_SRVTYPE:
480 return dkim_cur_sig->pubkey
481 ? dkim_cur_sig->pubkey->srvtype
482 ? US dkim_cur_sig->pubkey->srvtype
483 : dkim_exim_expand_defaults(what)
484 : dkim_exim_expand_defaults(what);
487 return dkim_cur_sig->pubkey
488 ? dkim_cur_sig->pubkey->notes
489 ? US dkim_cur_sig->pubkey->notes
490 : dkim_exim_expand_defaults(what)
491 : dkim_exim_expand_defaults(what);
493 case DKIM_KEY_TESTING:
494 return dkim_cur_sig->pubkey
495 ? dkim_cur_sig->pubkey->testing
497 : dkim_exim_expand_defaults(what)
498 : dkim_exim_expand_defaults(what);
500 case DKIM_NOSUBDOMAINS:
501 return dkim_cur_sig->pubkey
502 ? dkim_cur_sig->pubkey->no_subdomaining
504 : dkim_exim_expand_defaults(what)
505 : dkim_exim_expand_defaults(what);
507 case DKIM_VERIFY_STATUS:
508 switch (dkim_cur_sig->verify_status)
510 case PDKIM_VERIFY_INVALID: return US"invalid";
511 case PDKIM_VERIFY_FAIL: return US"fail";
512 case PDKIM_VERIFY_PASS: return US"pass";
513 case PDKIM_VERIFY_NONE:
514 default: return US"none";
517 case DKIM_VERIFY_REASON:
518 switch (dkim_cur_sig->verify_ext_status)
520 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
521 return US"pubkey_unavailable";
522 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:return US"pubkey_dns_syntax";
523 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT: return US"pubkey_der_syntax";
524 case PDKIM_VERIFY_FAIL_BODY: return US"bodyhash_mismatch";
525 case PDKIM_VERIFY_FAIL_MESSAGE: return US"signature_incorrect";
534 /* Generate signatures for the given file, returning a string.
535 If a prefix is given, prepend it to the file for the calculations.
539 dkim_exim_sign(int fd, off_t off, uschar * prefix,
540 struct ob_dkim * dkim, const uschar ** errstr)
542 const uschar * dkim_domain;
544 gstring * seen_doms = NULL;
546 pdkim_signature * sig;
552 int old_pool = store_pool;
555 store_pool = POOL_MAIN;
557 pdkim_init_context(&ctx, dkim->dot_stuffed, &dkim_exim_query_dns_txt);
559 if (!(dkim_domain = expand_cstring(dkim->dkim_domain)))
560 /* expansion error, do not send message. */
561 { errwhen = US"dkim_domain"; goto expand_bad; }
563 /* Set $dkim_domain expansion variable to each unique domain in list. */
565 while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep, NULL, 0)))
567 const uschar * dkim_sel;
570 if (dkim_signing_domain[0] == '\0')
573 /* Only sign once for each domain, no matter how often it
574 appears in the expanded list. */
576 if (match_isinlist(dkim_signing_domain, CUSS &seen_doms,
577 0, NULL, NULL, MCL_STRING, TRUE, NULL) == OK)
580 seen_doms = string_append_listele(seen_doms, ':', dkim_signing_domain);
582 /* Set $dkim_selector expansion variable to each selector in list,
585 if (!(dkim_sel = expand_string(dkim->dkim_selector)))
586 if (!(dkim_signing_selector = expand_string(dkim->dkim_selector)))
587 { errwhen = US"dkim_selector"; goto expand_bad; }
589 while ((dkim_signing_selector = string_nextinlist(&dkim_sel, &sel_sep,
592 uschar * dkim_canon_expanded;
594 uschar * dkim_sign_headers_expanded = NULL;
595 uschar * dkim_private_key_expanded;
596 uschar * dkim_hash_expanded;
597 uschar * dkim_identity_expanded = NULL;
599 /* Get canonicalization to use */
601 dkim_canon_expanded = dkim->dkim_canon
602 ? expand_string(dkim->dkim_canon) : US"relaxed";
603 if (!dkim_canon_expanded) /* expansion error, do not send message. */
604 { errwhen = US"dkim_canon"; goto expand_bad; }
606 if (Ustrcmp(dkim_canon_expanded, "relaxed") == 0)
607 pdkim_canon = PDKIM_CANON_RELAXED;
608 else if (Ustrcmp(dkim_canon_expanded, "simple") == 0)
609 pdkim_canon = PDKIM_CANON_SIMPLE;
612 log_write(0, LOG_MAIN,
613 "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",
614 dkim_canon_expanded);
615 pdkim_canon = PDKIM_CANON_RELAXED;
618 if ( dkim->dkim_sign_headers
619 && !(dkim_sign_headers_expanded = expand_string(dkim->dkim_sign_headers)))
620 { errwhen = US"dkim_sign_header"; goto expand_bad; }
621 /* else pass NULL, which means default header list */
623 /* Get private key to use. */
625 if (!(dkim_private_key_expanded = expand_string(dkim->dkim_private_key)))
626 { errwhen = US"dkim_private_key"; goto expand_bad; }
628 if ( Ustrlen(dkim_private_key_expanded) == 0
629 || Ustrcmp(dkim_private_key_expanded, "0") == 0
630 || Ustrcmp(dkim_private_key_expanded, "false") == 0
632 continue; /* don't sign, but no error */
634 if (dkim_private_key_expanded[0] == '/')
636 int privkey_fd, off = 0, len;
638 /* Looks like a filename, load the private key. */
640 memset(big_buffer, 0, big_buffer_size);
642 if ((privkey_fd = open(CS dkim_private_key_expanded, O_RDONLY)) < 0)
644 log_write(0, LOG_MAIN | LOG_PANIC, "unable to open "
645 "private key file for reading: %s",
646 dkim_private_key_expanded);
652 if ((len = read(privkey_fd, big_buffer + off, big_buffer_size - 2 - off)) < 0)
654 (void) close(privkey_fd);
655 log_write(0, LOG_MAIN|LOG_PANIC, "unable to read private key file: %s",
656 dkim_private_key_expanded);
663 (void) close(privkey_fd);
664 big_buffer[off] = '\0';
665 dkim_private_key_expanded = big_buffer;
668 if (!(dkim_hash_expanded = expand_string(dkim->dkim_hash)))
669 { errwhen = US"dkim_hash"; goto expand_bad; }
671 if (dkim->dkim_identity)
672 if (!(dkim_identity_expanded = expand_string(dkim->dkim_identity)))
673 { errwhen = US"dkim_identity"; goto expand_bad; }
674 else if (!*dkim_identity_expanded)
675 dkim_identity_expanded = NULL;
677 /*XXX so we currently nail signing to RSA + this hash.
678 Need to extract algo from privkey and check for disallowed combos. */
680 if (!(sig = pdkim_init_sign(&ctx, dkim_signing_domain,
681 dkim_signing_selector,
682 dkim_private_key_expanded,
687 dkim_private_key_expanded[0] = '\0';
689 pdkim_set_optional(sig,
690 CS dkim_sign_headers_expanded,
691 CS dkim_identity_expanded,
693 pdkim_canon, -1, 0, 0);
695 if (!ctx.sig) /* link sig to context chain */
699 pdkim_signature * n = ctx.sig;
700 while (n->next) n = n->next;
707 pdkim_feed(&ctx, prefix, Ustrlen(prefix));
709 if (lseek(fd, off, SEEK_SET) < 0)
712 while ((sread = read(fd, &buf, sizeof(buf))) > 0)
713 if ((pdkim_rc = pdkim_feed(&ctx, buf, sread)) != PDKIM_OK)
716 /* Handle failed read above. */
719 debug_printf("DKIM: Error reading -K file.\n");
724 /* Build string of headers, one per signature */
726 if ((pdkim_rc = pdkim_feed_finish(&ctx, &sig, errstr)) != PDKIM_OK)
729 for (sigbuf = NULL; sig; sig = sig->next)
730 sigbuf = string_append(sigbuf, 2, US sig->signature_header, US"\r\n");
732 (void) string_from_gstring(sigbuf);
735 store_pool = old_pool;
740 log_write(0, LOG_MAIN|LOG_PANIC,
741 "DKIM: signing failed: %.100s", pdkim_errstr(pdkim_rc));
747 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand %s: %s",
748 errwhen, expand_string_message);
752 # endif /*!MACRO_PREDEF*/
753 #endif /*!DISABLE_DKIM*/