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)
141 gstring * logmsg = string_catn(NULL, "DKIM: ", 6);
144 if (!(s = sig->domain)) s = US"<UNSET>";
145 logmsg = string_append(logmsg, 2, "d=", s);
146 if (!(s = sig->selector)) s = US"<UNSET>";
147 logmsg = string_append(logmsg, 2, " s=", s);
148 logmsg = string_append(logmsg, 7,
149 " c=", sig->canon_headers == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
150 "/", sig->canon_body == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
151 " a=", dkim_sig_to_a_tag(sig),
152 string_sprintf(" b=" SIZE_T_FMT,
153 (int)sig->sighash.len > -1 ? sig->sighash.len * 8 : 0));
154 if ((s= sig->identity)) logmsg = string_append(logmsg, 2, " i=", s);
155 if (sig->created > 0) logmsg = string_cat(logmsg,
156 string_sprintf(" t=%lu", sig->created));
157 if (sig->expires > 0) logmsg = string_cat(logmsg,
158 string_sprintf(" x=%lu", sig->expires));
159 if (sig->bodylength > -1) logmsg = string_cat(logmsg,
160 string_sprintf(" l=%lu", sig->bodylength));
162 if ( !dkim_verify_status
163 || ( dkim_verify_status == dkim_exim_expand_query(DKIM_VERIFY_STATUS)
164 && dkim_verify_reason == dkim_exim_expand_query(DKIM_VERIFY_REASON)
166 switch (sig->verify_status)
168 case PDKIM_VERIFY_NONE:
169 logmsg = string_cat(logmsg, " [not verified]");
172 case PDKIM_VERIFY_INVALID:
173 logmsg = string_cat(logmsg, " [invalid - ");
174 switch (sig->verify_ext_status)
176 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
177 logmsg = string_cat(logmsg,
178 "public key record (currently?) unavailable]");
181 case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
182 logmsg = string_cat(logmsg, "overlong public key record]");
185 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:
186 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT:
187 logmsg = string_cat(logmsg, "syntax error in public key record]");
190 case PDKIM_VERIFY_INVALID_SIGNATURE_ERROR:
191 logmsg = string_cat(logmsg, "signature tag missing or invalid]");
194 case PDKIM_VERIFY_INVALID_DKIM_VERSION:
195 logmsg = string_cat(logmsg, "unsupported DKIM version]");
199 logmsg = string_cat(logmsg, "unspecified problem]");
203 case PDKIM_VERIFY_FAIL:
204 logmsg = string_cat(logmsg, " [verification failed - ");
205 switch (sig->verify_ext_status)
207 case PDKIM_VERIFY_FAIL_BODY:
208 logmsg = string_cat(logmsg,
209 "body hash mismatch (body probably modified in transit)]");
212 case PDKIM_VERIFY_FAIL_MESSAGE:
213 logmsg = string_cat(logmsg,
214 "signature did not verify (headers probably modified in transit)]");
218 logmsg = string_cat(logmsg, "unspecified reason]");
222 case PDKIM_VERIFY_PASS:
223 logmsg = string_cat(logmsg, " [verification succeeded]");
227 logmsg = string_append(logmsg, 5,
228 US" [", dkim_verify_status, US" - ", dkim_verify_reason, US"]");
230 log_write(0, LOG_MAIN, string_from_gstring(logmsg));
235 /* Log a line for "the current" signature */
237 dkim_exim_verify_log_item(void)
239 dkim_exim_verify_log_sig(dkim_cur_sig);
243 /* Log a line for each signature */
245 dkim_exim_verify_log_all(void)
247 pdkim_signature * sig;
248 for (sig = dkim_signatures; sig; sig = sig->next) dkim_exim_verify_log_sig(sig);
253 dkim_exim_verify_finish(void)
255 pdkim_signature * sig;
258 const uschar * errstr;
260 store_pool = POOL_PERM;
262 /* Delete eventual previous signature chain */
265 dkim_signatures = NULL;
267 if (dkim_collect_error)
269 log_write(0, LOG_MAIN,
270 "DKIM: Error during validation, disabling signature verification: %.100s",
272 dkim_disable_verify = TRUE;
276 dkim_collect_input = FALSE;
278 /* Finish DKIM operation and fetch link to signatures chain */
280 rc = pdkim_feed_finish(dkim_verify_ctx, &dkim_signatures, &errstr);
283 log_write(0, LOG_MAIN, "DKIM: validation error: %.100s%s%s", pdkim_errstr(rc),
284 errstr ? ": " : "", errstr ? errstr : US"");
288 /* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */
290 for (sig = dkim_signatures; sig; sig = sig->next)
292 if (sig->domain) g = string_append_listele(g, ':', sig->domain);
293 if (sig->identity) g = string_append_listele(g, ':', sig->identity);
296 if (g) dkim_signers = g->s;
299 store_pool = dkim_verify_oldpool;
304 dkim_exim_acl_setup(uschar * id)
306 pdkim_signature * sig;
310 dkim_cur_signer = id;
312 if (dkim_disable_verify || !id || !dkim_verify_ctx)
315 /* Find signature to run ACL on */
317 for (sig = dkim_signatures; sig; sig = sig->next)
318 if ( (cmp_val = Ustrchr(id, '@') != NULL ? US sig->identity : US sig->domain)
319 && strcmpic(cmp_val, id) == 0
324 /* The "dkim_domain" and "dkim_selector" expansion variables have
325 related globals, since they are used in the signing code too.
326 Instead of inventing separate names for verification, we set
327 them here. This is easy since a domain and selector is guaranteed
328 to be in a signature. The other dkim_* expansion items are
329 dynamically fetched from dkim_cur_sig at expansion time (see
332 dkim_signing_domain = US sig->domain;
333 dkim_signing_selector = US sig->selector;
334 dkim_key_length = sig->sighash.len * 8;
336 /* These two return static strings, so we can compare the addr
337 later to see if the ACL overwrote them. Check that when logging */
339 dkim_verify_status = dkim_exim_expand_query(DKIM_VERIFY_STATUS);
340 dkim_verify_reason = dkim_exim_expand_query(DKIM_VERIFY_REASON);
347 dkim_exim_expand_defaults(int what)
351 case DKIM_ALGO: return US"";
352 case DKIM_BODYLENGTH: return US"9999999999999";
353 case DKIM_CANON_BODY: return US"";
354 case DKIM_CANON_HEADERS: return US"";
355 case DKIM_COPIEDHEADERS: return US"";
356 case DKIM_CREATED: return US"0";
357 case DKIM_EXPIRES: return US"9999999999999";
358 case DKIM_HEADERNAMES: return US"";
359 case DKIM_IDENTITY: return US"";
360 case DKIM_KEY_GRANULARITY: return US"*";
361 case DKIM_KEY_SRVTYPE: return US"*";
362 case DKIM_KEY_NOTES: return US"";
363 case DKIM_KEY_TESTING: return US"0";
364 case DKIM_NOSUBDOMAINS: return US"0";
365 case DKIM_VERIFY_STATUS: return US"none";
366 case DKIM_VERIFY_REASON: return US"";
367 default: return US"";
373 dkim_exim_expand_query(int what)
375 if (!dkim_verify_ctx || dkim_disable_verify || !dkim_cur_sig)
376 return dkim_exim_expand_defaults(what);
381 return dkim_sig_to_a_tag(dkim_cur_sig);
383 case DKIM_BODYLENGTH:
384 return dkim_cur_sig->bodylength >= 0
385 ? string_sprintf("%ld", dkim_cur_sig->bodylength)
386 : dkim_exim_expand_defaults(what);
388 case DKIM_CANON_BODY:
389 switch (dkim_cur_sig->canon_body)
391 case PDKIM_CANON_RELAXED: return US"relaxed";
392 case PDKIM_CANON_SIMPLE:
393 default: return US"simple";
396 case DKIM_CANON_HEADERS:
397 switch (dkim_cur_sig->canon_headers)
399 case PDKIM_CANON_RELAXED: return US"relaxed";
400 case PDKIM_CANON_SIMPLE:
401 default: return US"simple";
404 case DKIM_COPIEDHEADERS:
405 return dkim_cur_sig->copiedheaders
406 ? US dkim_cur_sig->copiedheaders : dkim_exim_expand_defaults(what);
409 return dkim_cur_sig->created > 0
410 ? string_sprintf("%lu", dkim_cur_sig->created)
411 : dkim_exim_expand_defaults(what);
414 return dkim_cur_sig->expires > 0
415 ? string_sprintf("%lu", dkim_cur_sig->expires)
416 : dkim_exim_expand_defaults(what);
418 case DKIM_HEADERNAMES:
419 return dkim_cur_sig->headernames
420 ? dkim_cur_sig->headernames : dkim_exim_expand_defaults(what);
423 return dkim_cur_sig->identity
424 ? US dkim_cur_sig->identity : dkim_exim_expand_defaults(what);
426 case DKIM_KEY_GRANULARITY:
427 return dkim_cur_sig->pubkey
428 ? dkim_cur_sig->pubkey->granularity
429 ? US dkim_cur_sig->pubkey->granularity
430 : dkim_exim_expand_defaults(what)
431 : dkim_exim_expand_defaults(what);
433 case DKIM_KEY_SRVTYPE:
434 return dkim_cur_sig->pubkey
435 ? dkim_cur_sig->pubkey->srvtype
436 ? US dkim_cur_sig->pubkey->srvtype
437 : dkim_exim_expand_defaults(what)
438 : dkim_exim_expand_defaults(what);
441 return dkim_cur_sig->pubkey
442 ? dkim_cur_sig->pubkey->notes
443 ? US dkim_cur_sig->pubkey->notes
444 : dkim_exim_expand_defaults(what)
445 : dkim_exim_expand_defaults(what);
447 case DKIM_KEY_TESTING:
448 return dkim_cur_sig->pubkey
449 ? dkim_cur_sig->pubkey->testing
451 : dkim_exim_expand_defaults(what)
452 : dkim_exim_expand_defaults(what);
454 case DKIM_NOSUBDOMAINS:
455 return dkim_cur_sig->pubkey
456 ? dkim_cur_sig->pubkey->no_subdomaining
458 : dkim_exim_expand_defaults(what)
459 : dkim_exim_expand_defaults(what);
461 case DKIM_VERIFY_STATUS:
462 switch (dkim_cur_sig->verify_status)
464 case PDKIM_VERIFY_INVALID: return US"invalid";
465 case PDKIM_VERIFY_FAIL: return US"fail";
466 case PDKIM_VERIFY_PASS: return US"pass";
467 case PDKIM_VERIFY_NONE:
468 default: return US"none";
471 case DKIM_VERIFY_REASON:
472 switch (dkim_cur_sig->verify_ext_status)
474 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
475 return US"pubkey_unavailable";
476 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:return US"pubkey_dns_syntax";
477 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT: return US"pubkey_der_syntax";
478 case PDKIM_VERIFY_FAIL_BODY: return US"bodyhash_mismatch";
479 case PDKIM_VERIFY_FAIL_MESSAGE: return US"signature_incorrect";
488 /* Generate signatures for the given file, returning a string.
489 If a prefix is given, prepend it to the file for the calculations.
493 dkim_exim_sign(int fd, off_t off, uschar * prefix,
494 struct ob_dkim * dkim, const uschar ** errstr)
496 const uschar * dkim_domain;
498 gstring * seen_doms = NULL;
500 pdkim_signature * sig;
506 int old_pool = store_pool;
509 store_pool = POOL_MAIN;
511 pdkim_init_context(&ctx, dkim->dot_stuffed, &dkim_exim_query_dns_txt);
513 if (!(dkim_domain = expand_cstring(dkim->dkim_domain)))
514 /* expansion error, do not send message. */
515 { errwhen = US"dkim_domain"; goto expand_bad; }
517 /* Set $dkim_domain expansion variable to each unique domain in list. */
519 while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep, NULL, 0)))
521 const uschar * dkim_sel;
524 if (dkim_signing_domain[0] == '\0')
527 /* Only sign once for each domain, no matter how often it
528 appears in the expanded list. */
530 if (match_isinlist(dkim_signing_domain, CUSS &seen_doms,
531 0, NULL, NULL, MCL_STRING, TRUE, NULL) == OK)
534 seen_doms = string_append_listele(seen_doms, ':', dkim_signing_domain);
536 /* Set $dkim_selector expansion variable to each selector in list,
539 if (!(dkim_sel = expand_string(dkim->dkim_selector)))
540 if (!(dkim_signing_selector = expand_string(dkim->dkim_selector)))
541 { errwhen = US"dkim_selector"; goto expand_bad; }
543 while ((dkim_signing_selector = string_nextinlist(&dkim_sel, &sel_sep,
546 uschar * dkim_canon_expanded;
548 uschar * dkim_sign_headers_expanded = NULL;
549 uschar * dkim_private_key_expanded;
550 uschar * dkim_hash_expanded;
551 uschar * dkim_identity_expanded = NULL;
553 /* Get canonicalization to use */
555 dkim_canon_expanded = dkim->dkim_canon
556 ? expand_string(dkim->dkim_canon) : US"relaxed";
557 if (!dkim_canon_expanded) /* expansion error, do not send message. */
558 { errwhen = US"dkim_canon"; goto expand_bad; }
560 if (Ustrcmp(dkim_canon_expanded, "relaxed") == 0)
561 pdkim_canon = PDKIM_CANON_RELAXED;
562 else if (Ustrcmp(dkim_canon_expanded, "simple") == 0)
563 pdkim_canon = PDKIM_CANON_SIMPLE;
566 log_write(0, LOG_MAIN,
567 "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",
568 dkim_canon_expanded);
569 pdkim_canon = PDKIM_CANON_RELAXED;
572 if ( dkim->dkim_sign_headers
573 && !(dkim_sign_headers_expanded = expand_string(dkim->dkim_sign_headers)))
574 { errwhen = US"dkim_sign_header"; goto expand_bad; }
575 /* else pass NULL, which means default header list */
577 /* Get private key to use. */
579 if (!(dkim_private_key_expanded = expand_string(dkim->dkim_private_key)))
580 { errwhen = US"dkim_private_key"; goto expand_bad; }
582 if ( Ustrlen(dkim_private_key_expanded) == 0
583 || Ustrcmp(dkim_private_key_expanded, "0") == 0
584 || Ustrcmp(dkim_private_key_expanded, "false") == 0
586 continue; /* don't sign, but no error */
588 if (dkim_private_key_expanded[0] == '/')
590 int privkey_fd, off = 0, len;
592 /* Looks like a filename, load the private key. */
594 memset(big_buffer, 0, big_buffer_size);
596 if ((privkey_fd = open(CS dkim_private_key_expanded, O_RDONLY)) < 0)
598 log_write(0, LOG_MAIN | LOG_PANIC, "unable to open "
599 "private key file for reading: %s",
600 dkim_private_key_expanded);
606 if ((len = read(privkey_fd, big_buffer + off, big_buffer_size - 2 - off)) < 0)
608 (void) close(privkey_fd);
609 log_write(0, LOG_MAIN|LOG_PANIC, "unable to read private key file: %s",
610 dkim_private_key_expanded);
617 (void) close(privkey_fd);
618 big_buffer[off] = '\0';
619 dkim_private_key_expanded = big_buffer;
622 if (!(dkim_hash_expanded = expand_string(dkim->dkim_hash)))
623 { errwhen = US"dkim_hash"; goto expand_bad; }
625 if (dkim->dkim_identity)
626 if (!(dkim_identity_expanded = expand_string(dkim->dkim_identity)))
627 { errwhen = US"dkim_identity"; goto expand_bad; }
628 else if (!*dkim_identity_expanded)
629 dkim_identity_expanded = NULL;
631 /*XXX so we currently nail signing to RSA + this hash.
632 Need to extract algo from privkey and check for disallowed combos. */
634 if (!(sig = pdkim_init_sign(&ctx, dkim_signing_domain,
635 dkim_signing_selector,
636 dkim_private_key_expanded,
641 dkim_private_key_expanded[0] = '\0';
643 pdkim_set_optional(sig,
644 CS dkim_sign_headers_expanded,
645 dkim_identity_expanded,
647 pdkim_canon, -1, 0, 0);
649 if (!ctx.sig) /* link sig to context chain */
653 pdkim_signature * n = ctx.sig;
654 while (n->next) n = n->next;
661 pdkim_feed(&ctx, prefix, Ustrlen(prefix));
663 if (lseek(fd, off, SEEK_SET) < 0)
666 while ((sread = read(fd, &buf, sizeof(buf))) > 0)
667 if ((pdkim_rc = pdkim_feed(&ctx, buf, sread)) != PDKIM_OK)
670 /* Handle failed read above. */
673 debug_printf("DKIM: Error reading -K file.\n");
678 /* Build string of headers, one per signature */
680 if ((pdkim_rc = pdkim_feed_finish(&ctx, &sig, errstr)) != PDKIM_OK)
683 for (sigbuf = NULL; sig; sig = sig->next)
684 sigbuf = string_append(sigbuf, 2, US sig->signature_header, US"\r\n");
686 (void) string_from_gstring(sigbuf);
689 store_pool = old_pool;
694 log_write(0, LOG_MAIN|LOG_PANIC,
695 "DKIM: signing failed: %.100s", pdkim_errstr(pdkim_rc));
701 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand %s: %s",
702 errwhen, expand_string_message);
706 # endif /*!MACRO_PREDEF*/
707 #endif /*!DISABLE_DKIM*/