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 (strncmp(answer, "v=", 2) == 0 && strncasecmp(answer, "v=dkim", 6) != 0)
83 return PDKIM_FAIL; /*XXX better error detail? logging? */
96 dkim_exim_verify_init(BOOL dot_stuffing)
98 /* There is a store-reset between header & body reception
99 so cannot use the main pool. Any allocs done by Exim
100 memory-handling must use the perm pool. */
102 dkim_verify_oldpool = store_pool;
103 store_pool = POOL_PERM;
105 /* Free previous context if there is one */
108 pdkim_free_ctx(dkim_verify_ctx);
110 /* Create new context */
112 dkim_verify_ctx = pdkim_init_verify(&dkim_exim_query_dns_txt, dot_stuffing);
113 dkim_collect_input = !!dkim_verify_ctx;
114 dkim_collect_error = NULL;
116 /* Start feed up with any cached data */
119 store_pool = dkim_verify_oldpool;
124 dkim_exim_verify_feed(uschar * data, int len)
128 store_pool = POOL_PERM;
129 if ( dkim_collect_input
130 && (rc = pdkim_feed(dkim_verify_ctx, data, len)) != PDKIM_OK)
132 dkim_collect_error = pdkim_errstr(rc);
133 log_write(0, LOG_MAIN,
134 "DKIM: validation error: %.100s", dkim_collect_error);
135 dkim_collect_input = FALSE;
137 store_pool = dkim_verify_oldpool;
141 /* Log the result for the given signature */
143 dkim_exim_verify_log_sig(pdkim_signature * sig)
150 if ( !dkim_verify_overall
151 && dkim_verify_status
152 ? Ustrcmp(dkim_verify_status, US"pass") == 0
153 : sig->verify_status == PDKIM_VERIFY_PASS
155 dkim_verify_overall = string_copy(sig->domain);
157 if (!LOGGING(dkim_verbose)) return;
159 logmsg = string_catn(NULL, US"DKIM: ", 6);
160 if (!(s = sig->domain)) s = US"<UNSET>";
161 logmsg = string_append(logmsg, 2, "d=", s);
162 if (!(s = sig->selector)) s = US"<UNSET>";
163 logmsg = string_append(logmsg, 2, " s=", s);
164 logmsg = string_append(logmsg, 7,
165 " c=", sig->canon_headers == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
166 "/", sig->canon_body == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
167 " a=", dkim_sig_to_a_tag(sig),
168 string_sprintf(" b=" SIZE_T_FMT,
169 (int)sig->sighash.len > -1 ? sig->sighash.len * 8 : 0));
170 if ((s= sig->identity)) logmsg = string_append(logmsg, 2, " i=", s);
171 if (sig->created > 0) logmsg = string_cat(logmsg,
172 string_sprintf(" t=%lu", sig->created));
173 if (sig->expires > 0) logmsg = string_cat(logmsg,
174 string_sprintf(" x=%lu", sig->expires));
175 if (sig->bodylength > -1) logmsg = string_cat(logmsg,
176 string_sprintf(" l=%lu", sig->bodylength));
178 if ( !dkim_verify_status
179 || ( dkim_verify_status == dkim_exim_expand_query(DKIM_VERIFY_STATUS)
180 && dkim_verify_reason == dkim_exim_expand_query(DKIM_VERIFY_REASON)
182 switch (sig->verify_status)
184 case PDKIM_VERIFY_NONE:
185 logmsg = string_cat(logmsg, US" [not verified]");
188 case PDKIM_VERIFY_INVALID:
189 logmsg = string_cat(logmsg, US" [invalid - ");
190 switch (sig->verify_ext_status)
192 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
193 logmsg = string_cat(logmsg,
194 US"public key record (currently?) unavailable]");
197 case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
198 logmsg = string_cat(logmsg, US"overlong public key record]");
201 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:
202 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT:
203 logmsg = string_cat(logmsg, US"syntax error in public key record]");
206 case PDKIM_VERIFY_INVALID_SIGNATURE_ERROR:
207 logmsg = string_cat(logmsg, US"signature tag missing or invalid]");
210 case PDKIM_VERIFY_INVALID_DKIM_VERSION:
211 logmsg = string_cat(logmsg, US"unsupported DKIM version]");
215 logmsg = string_cat(logmsg, US"unspecified problem]");
219 case PDKIM_VERIFY_FAIL:
220 logmsg = string_cat(logmsg, US" [verification failed - ");
221 switch (sig->verify_ext_status)
223 case PDKIM_VERIFY_FAIL_BODY:
224 logmsg = string_cat(logmsg,
225 US"body hash mismatch (body probably modified in transit)]");
228 case PDKIM_VERIFY_FAIL_MESSAGE:
229 logmsg = string_cat(logmsg,
230 US"signature did not verify "
231 "(headers probably modified in transit)]");
235 logmsg = string_cat(logmsg, US"unspecified reason]");
239 case PDKIM_VERIFY_PASS:
240 logmsg = string_cat(logmsg, US" [verification succeeded]");
244 logmsg = string_append(logmsg, 5,
245 US" [", dkim_verify_status, US" - ", dkim_verify_reason, US"]");
247 log_write(0, LOG_MAIN, "%s", string_from_gstring(logmsg));
252 /* Log a line for each signature */
254 dkim_exim_verify_log_all(void)
256 pdkim_signature * sig;
257 for (sig = dkim_signatures; sig; sig = sig->next) dkim_exim_verify_log_sig(sig);
262 dkim_exim_verify_finish(void)
264 pdkim_signature * sig;
267 const uschar * errstr;
269 store_pool = POOL_PERM;
271 /* Delete eventual previous signature chain */
274 dkim_signatures = NULL;
276 if (dkim_collect_error)
278 log_write(0, LOG_MAIN,
279 "DKIM: Error during validation, disabling signature verification: %.100s",
281 dkim_disable_verify = TRUE;
285 dkim_collect_input = FALSE;
287 /* Finish DKIM operation and fetch link to signatures chain */
289 rc = pdkim_feed_finish(dkim_verify_ctx, &dkim_signatures, &errstr);
292 log_write(0, LOG_MAIN, "DKIM: validation error: %.100s%s%s", pdkim_errstr(rc),
293 errstr ? ": " : "", errstr ? errstr : US"");
297 /* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */
299 for (sig = dkim_signatures; sig; sig = sig->next)
301 if (sig->domain) g = string_append_listele(g, ':', sig->domain);
302 if (sig->identity) g = string_append_listele(g, ':', sig->identity);
305 if (g) dkim_signers = g->s;
308 store_pool = dkim_verify_oldpool;
313 /* Args as per dkim_exim_acl_run() below */
315 dkim_acl_call(uschar * id, gstring ** res_ptr,
316 uschar ** user_msgptr, uschar ** log_msgptr)
320 debug_printf("calling acl_smtp_dkim for dkim_cur_signer='%s'\n", id);
322 rc = acl_check(ACL_WHERE_DKIM, NULL, acl_smtp_dkim, user_msgptr, log_msgptr);
323 dkim_exim_verify_log_sig(dkim_cur_sig);
324 *res_ptr = string_append_listele(*res_ptr, ':', dkim_verify_status);
330 /* For the given identity, run the DKIM ACL once for each matching signature.
333 id Identity to look for in dkim signatures
334 res_ptr ptr to growable string-list of status results,
335 appended to per ACL run
336 user_msgptr where to put a user error (for SMTP response)
337 log_msgptr where to put a logging message (not for SMTP response)
339 Returns: OK access is granted by an ACCEPT verb
340 DISCARD access is granted by a DISCARD verb
341 FAIL access is denied
342 FAIL_DROP access is denied; drop the connection
343 DEFER can't tell at the moment
348 dkim_exim_acl_run(uschar * id, gstring ** res_ptr,
349 uschar ** user_msgptr, uschar ** log_msgptr)
351 pdkim_signature * sig;
355 dkim_verify_status = US"none";
356 dkim_verify_reason = US"";
357 dkim_cur_signer = id;
359 if (dkim_disable_verify || !id || !dkim_verify_ctx)
362 /* Find signatures to run ACL on */
364 for (sig = dkim_signatures; sig; sig = sig->next)
365 if ( (cmp_val = Ustrchr(id, '@') != NULL ? US sig->identity : US sig->domain)
366 && strcmpic(cmp_val, id) == 0
369 /* The "dkim_domain" and "dkim_selector" expansion variables have
370 related globals, since they are used in the signing code too.
371 Instead of inventing separate names for verification, we set
372 them here. This is easy since a domain and selector is guaranteed
373 to be in a signature. The other dkim_* expansion items are
374 dynamically fetched from dkim_cur_sig at expansion time (see
378 dkim_signing_domain = US sig->domain;
379 dkim_signing_selector = US sig->selector;
380 dkim_key_length = sig->sighash.len * 8;
382 /* These two return static strings, so we can compare the addr
383 later to see if the ACL overwrote them. Check that when logging */
385 dkim_verify_status = dkim_exim_expand_query(DKIM_VERIFY_STATUS);
386 dkim_verify_reason = dkim_exim_expand_query(DKIM_VERIFY_REASON);
388 if ((rc = dkim_acl_call(id, res_ptr, user_msgptr, log_msgptr)) != OK)
395 /* No matching sig found. Call ACL once anyway. */
398 return dkim_acl_call(id, res_ptr, user_msgptr, log_msgptr);
403 dkim_exim_expand_defaults(int what)
407 case DKIM_ALGO: return US"";
408 case DKIM_BODYLENGTH: return US"9999999999999";
409 case DKIM_CANON_BODY: return US"";
410 case DKIM_CANON_HEADERS: return US"";
411 case DKIM_COPIEDHEADERS: return US"";
412 case DKIM_CREATED: return US"0";
413 case DKIM_EXPIRES: return US"9999999999999";
414 case DKIM_HEADERNAMES: return US"";
415 case DKIM_IDENTITY: return US"";
416 case DKIM_KEY_GRANULARITY: return US"*";
417 case DKIM_KEY_SRVTYPE: return US"*";
418 case DKIM_KEY_NOTES: return US"";
419 case DKIM_KEY_TESTING: return US"0";
420 case DKIM_NOSUBDOMAINS: return US"0";
421 case DKIM_VERIFY_STATUS: return US"none";
422 case DKIM_VERIFY_REASON: return US"";
423 default: return US"";
429 dkim_exim_expand_query(int what)
431 if (!dkim_verify_ctx || dkim_disable_verify || !dkim_cur_sig)
432 return dkim_exim_expand_defaults(what);
437 return dkim_sig_to_a_tag(dkim_cur_sig);
439 case DKIM_BODYLENGTH:
440 return dkim_cur_sig->bodylength >= 0
441 ? string_sprintf("%ld", dkim_cur_sig->bodylength)
442 : dkim_exim_expand_defaults(what);
444 case DKIM_CANON_BODY:
445 switch (dkim_cur_sig->canon_body)
447 case PDKIM_CANON_RELAXED: return US"relaxed";
448 case PDKIM_CANON_SIMPLE:
449 default: return US"simple";
452 case DKIM_CANON_HEADERS:
453 switch (dkim_cur_sig->canon_headers)
455 case PDKIM_CANON_RELAXED: return US"relaxed";
456 case PDKIM_CANON_SIMPLE:
457 default: return US"simple";
460 case DKIM_COPIEDHEADERS:
461 return dkim_cur_sig->copiedheaders
462 ? US dkim_cur_sig->copiedheaders : dkim_exim_expand_defaults(what);
465 return dkim_cur_sig->created > 0
466 ? string_sprintf("%lu", dkim_cur_sig->created)
467 : dkim_exim_expand_defaults(what);
470 return dkim_cur_sig->expires > 0
471 ? string_sprintf("%lu", dkim_cur_sig->expires)
472 : dkim_exim_expand_defaults(what);
474 case DKIM_HEADERNAMES:
475 return dkim_cur_sig->headernames
476 ? dkim_cur_sig->headernames : dkim_exim_expand_defaults(what);
479 return dkim_cur_sig->identity
480 ? US dkim_cur_sig->identity : dkim_exim_expand_defaults(what);
482 case DKIM_KEY_GRANULARITY:
483 return dkim_cur_sig->pubkey
484 ? dkim_cur_sig->pubkey->granularity
485 ? US dkim_cur_sig->pubkey->granularity
486 : dkim_exim_expand_defaults(what)
487 : dkim_exim_expand_defaults(what);
489 case DKIM_KEY_SRVTYPE:
490 return dkim_cur_sig->pubkey
491 ? dkim_cur_sig->pubkey->srvtype
492 ? US dkim_cur_sig->pubkey->srvtype
493 : dkim_exim_expand_defaults(what)
494 : dkim_exim_expand_defaults(what);
497 return dkim_cur_sig->pubkey
498 ? dkim_cur_sig->pubkey->notes
499 ? US dkim_cur_sig->pubkey->notes
500 : dkim_exim_expand_defaults(what)
501 : dkim_exim_expand_defaults(what);
503 case DKIM_KEY_TESTING:
504 return dkim_cur_sig->pubkey
505 ? dkim_cur_sig->pubkey->testing
507 : dkim_exim_expand_defaults(what)
508 : dkim_exim_expand_defaults(what);
510 case DKIM_NOSUBDOMAINS:
511 return dkim_cur_sig->pubkey
512 ? dkim_cur_sig->pubkey->no_subdomaining
514 : dkim_exim_expand_defaults(what)
515 : dkim_exim_expand_defaults(what);
517 case DKIM_VERIFY_STATUS:
518 switch (dkim_cur_sig->verify_status)
520 case PDKIM_VERIFY_INVALID: return US"invalid";
521 case PDKIM_VERIFY_FAIL: return US"fail";
522 case PDKIM_VERIFY_PASS: return US"pass";
523 case PDKIM_VERIFY_NONE:
524 default: return US"none";
527 case DKIM_VERIFY_REASON:
528 switch (dkim_cur_sig->verify_ext_status)
530 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
531 return US"pubkey_unavailable";
532 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:return US"pubkey_dns_syntax";
533 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT: return US"pubkey_der_syntax";
534 case PDKIM_VERIFY_FAIL_BODY: return US"bodyhash_mismatch";
535 case PDKIM_VERIFY_FAIL_MESSAGE: return US"signature_incorrect";
544 /* Generate signatures for the given file.
545 If a prefix is given, prepend it to the file for the calculations.
548 NULL: error; error string written
549 string: signature header(s), or a zero-length string (not an error)
553 dkim_exim_sign(int fd, off_t off, uschar * prefix,
554 struct ob_dkim * dkim, const uschar ** errstr)
556 const uschar * dkim_domain;
558 gstring * seen_doms = NULL;
560 pdkim_signature * sig;
566 int old_pool = store_pool;
569 store_pool = POOL_MAIN;
571 pdkim_init_context(&ctx, dkim->dot_stuffed, &dkim_exim_query_dns_txt);
573 if (!(dkim_domain = expand_cstring(dkim->dkim_domain)))
574 /* expansion error, do not send message. */
575 { errwhen = US"dkim_domain"; goto expand_bad; }
577 /* Set $dkim_domain expansion variable to each unique domain in list. */
579 while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep, NULL, 0)))
581 const uschar * dkim_sel;
584 if (dkim_signing_domain[0] == '\0')
587 /* Only sign once for each domain, no matter how often it
588 appears in the expanded list. */
590 if (match_isinlist(dkim_signing_domain, CUSS &seen_doms,
591 0, NULL, NULL, MCL_STRING, TRUE, NULL) == OK)
594 seen_doms = string_append_listele(seen_doms, ':', dkim_signing_domain);
596 /* Set $dkim_selector expansion variable to each selector in list,
599 if (!(dkim_sel = expand_string(dkim->dkim_selector)))
600 if (!(dkim_signing_selector = expand_string(dkim->dkim_selector)))
601 { errwhen = US"dkim_selector"; goto expand_bad; }
603 while ((dkim_signing_selector = string_nextinlist(&dkim_sel, &sel_sep,
606 uschar * dkim_canon_expanded;
608 uschar * dkim_sign_headers_expanded = NULL;
609 uschar * dkim_private_key_expanded;
610 uschar * dkim_hash_expanded;
611 uschar * dkim_identity_expanded = NULL;
613 /* Get canonicalization to use */
615 dkim_canon_expanded = dkim->dkim_canon
616 ? expand_string(dkim->dkim_canon) : US"relaxed";
617 if (!dkim_canon_expanded) /* expansion error, do not send message. */
618 { errwhen = US"dkim_canon"; goto expand_bad; }
620 if (Ustrcmp(dkim_canon_expanded, "relaxed") == 0)
621 pdkim_canon = PDKIM_CANON_RELAXED;
622 else if (Ustrcmp(dkim_canon_expanded, "simple") == 0)
623 pdkim_canon = PDKIM_CANON_SIMPLE;
626 log_write(0, LOG_MAIN,
627 "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",
628 dkim_canon_expanded);
629 pdkim_canon = PDKIM_CANON_RELAXED;
632 if ( dkim->dkim_sign_headers
633 && !(dkim_sign_headers_expanded = expand_string(dkim->dkim_sign_headers)))
634 { errwhen = US"dkim_sign_header"; goto expand_bad; }
635 /* else pass NULL, which means default header list */
637 /* Get private key to use. */
639 if (!(dkim_private_key_expanded = expand_string(dkim->dkim_private_key)))
640 { errwhen = US"dkim_private_key"; goto expand_bad; }
642 if ( Ustrlen(dkim_private_key_expanded) == 0
643 || Ustrcmp(dkim_private_key_expanded, "0") == 0
644 || Ustrcmp(dkim_private_key_expanded, "false") == 0
646 continue; /* don't sign, but no error */
648 if (dkim_private_key_expanded[0] == '/')
650 int privkey_fd, off = 0, len;
652 /* Looks like a filename, load the private key. */
654 memset(big_buffer, 0, big_buffer_size);
656 if ((privkey_fd = open(CS dkim_private_key_expanded, O_RDONLY)) < 0)
658 log_write(0, LOG_MAIN | LOG_PANIC, "unable to open "
659 "private key file for reading: %s",
660 dkim_private_key_expanded);
666 if ((len = read(privkey_fd, big_buffer + off, big_buffer_size - 2 - off)) < 0)
668 (void) close(privkey_fd);
669 log_write(0, LOG_MAIN|LOG_PANIC, "unable to read private key file: %s",
670 dkim_private_key_expanded);
677 (void) close(privkey_fd);
678 big_buffer[off] = '\0';
679 dkim_private_key_expanded = big_buffer;
682 if (!(dkim_hash_expanded = expand_string(dkim->dkim_hash)))
683 { errwhen = US"dkim_hash"; goto expand_bad; }
685 if (dkim->dkim_identity)
686 if (!(dkim_identity_expanded = expand_string(dkim->dkim_identity)))
687 { errwhen = US"dkim_identity"; goto expand_bad; }
688 else if (!*dkim_identity_expanded)
689 dkim_identity_expanded = NULL;
691 /*XXX so we currently nail signing to RSA + this hash.
692 Need to extract algo from privkey and check for disallowed combos. */
694 if (!(sig = pdkim_init_sign(&ctx, dkim_signing_domain,
695 dkim_signing_selector,
696 dkim_private_key_expanded,
701 dkim_private_key_expanded[0] = '\0';
703 pdkim_set_optional(sig,
704 CS dkim_sign_headers_expanded,
705 CS dkim_identity_expanded,
707 pdkim_canon, -1, 0, 0);
709 if (!ctx.sig) /* link sig to context chain */
713 pdkim_signature * n = ctx.sig;
714 while (n->next) n = n->next;
721 DEBUG(D_transport) debug_printf("DKIM: no viable signatures to use\n");
722 sigbuf = string_get(1); /* return a zero-len string */
726 if (prefix && (pdkim_feed(&ctx, prefix, Ustrlen(prefix))) != PDKIM_OK)
729 if (lseek(fd, off, SEEK_SET) < 0)
732 while ((sread = read(fd, &buf, sizeof(buf))) > 0)
733 if ((pdkim_rc = pdkim_feed(&ctx, buf, sread)) != PDKIM_OK)
736 /* Handle failed read above. */
739 debug_printf("DKIM: Error reading -K file.\n");
744 /* Build string of headers, one per signature */
746 if ((pdkim_rc = pdkim_feed_finish(&ctx, &sig, errstr)) != PDKIM_OK)
749 for (sigbuf = NULL; sig; sig = sig->next)
750 sigbuf = string_append(sigbuf, 2, US sig->signature_header, US"\r\n");
753 (void) string_from_gstring(sigbuf);
754 store_pool = old_pool;
759 log_write(0, LOG_MAIN|LOG_PANIC,
760 "DKIM: signing failed: %.100s", pdkim_errstr(pdkim_rc));
766 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand %s: %s",
767 errwhen, expand_string_message);
771 # endif /*!MACRO_PREDEF*/
772 #endif /*!DISABLE_DKIM*/