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"
17 int dkim_verify_oldpool;
18 pdkim_ctx *dkim_verify_ctx = NULL;
19 pdkim_signature *dkim_signatures = NULL;
20 pdkim_signature *dkim_cur_sig = NULL;
21 static const uschar * dkim_collect_error = NULL;
25 /*XXX the caller only uses the first record if we return multiple.
26 Could we hand back an allocated string?
30 dkim_exim_query_dns_txt(char *name, char *answer)
36 lookup_dnssec_authenticated = NULL;
37 if (dns_lookup(&dnsa, US name, T_TXT, NULL) != DNS_SUCCEED)
38 return PDKIM_FAIL; /*XXX better error detail? logging? */
40 /* Search for TXT record */
42 for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
44 rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
45 if (rr->type == T_TXT)
48 int answer_offset = 0;
50 /* Copy record content to the answer buffer */
52 while (rr_offset < rr->size)
54 uschar len = rr->data[rr_offset++];
55 snprintf(answer + answer_offset,
56 PDKIM_DNS_TXT_MAX_RECLEN - answer_offset,
57 "%.*s", (int)len, CS (rr->data + rr_offset));
60 if (answer_offset >= PDKIM_DNS_TXT_MAX_RECLEN)
61 return PDKIM_FAIL; /*XXX better error detail? logging? */
66 return PDKIM_FAIL; /*XXX better error detail? logging? */
79 dkim_exim_verify_init(BOOL dot_stuffing)
81 /* There is a store-reset between header & body reception
82 so cannot use the main pool. Any allocs done by Exim
83 memory-handling must use the perm pool. */
85 dkim_verify_oldpool = store_pool;
86 store_pool = POOL_PERM;
88 /* Free previous context if there is one */
91 pdkim_free_ctx(dkim_verify_ctx);
93 /* Create new context */
95 dkim_verify_ctx = pdkim_init_verify(&dkim_exim_query_dns_txt, dot_stuffing);
96 dkim_collect_input = !!dkim_verify_ctx;
97 dkim_collect_error = NULL;
99 /* Start feed up with any cached data */
102 store_pool = dkim_verify_oldpool;
107 dkim_exim_verify_feed(uschar * data, int len)
111 store_pool = POOL_PERM;
112 if ( dkim_collect_input
113 && (rc = pdkim_feed(dkim_verify_ctx, data, len)) != PDKIM_OK)
115 dkim_collect_error = pdkim_errstr(rc);
116 log_write(0, LOG_MAIN,
117 "DKIM: validation error: %.100s", dkim_collect_error);
118 dkim_collect_input = FALSE;
120 store_pool = dkim_verify_oldpool;
125 dkim_exim_verify_finish(void)
127 pdkim_signature * sig = NULL;
128 int dkim_signers_size = 0, dkim_signers_ptr = 0, rc;
130 const uschar * errstr;
132 store_pool = POOL_PERM;
134 /* Delete eventual previous signature chain */
137 dkim_signatures = NULL;
139 if (dkim_collect_error)
141 log_write(0, LOG_MAIN,
142 "DKIM: Error during validation, disabling signature verification: %.100s",
144 dkim_disable_verify = TRUE;
148 dkim_collect_input = FALSE;
150 /* Finish DKIM operation and fetch link to signatures chain */
152 rc = pdkim_feed_finish(dkim_verify_ctx, &dkim_signatures, &errstr);
155 log_write(0, LOG_MAIN, "DKIM: validation error: %.100s%s%s", pdkim_errstr(rc),
156 errstr ? ": " : "", errstr ? errstr : US"");
160 for (sig = dkim_signatures; sig; sig = sig->next)
165 /* Log a line for each signature */
167 if (!(s = sig->domain)) s = US"<UNSET>";
168 logmsg = string_append(NULL, 2, "d=", s);
169 if (!(s = sig->selector)) s = US"<UNSET>";
170 logmsg = string_append(logmsg, 2, " s=", s);
171 logmsg = string_append(logmsg, 7,
172 " c=", sig->canon_headers == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
173 "/", sig->canon_body == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
174 " a=", dkim_sig_to_a_tag(sig),
175 string_sprintf(" b=%d",
176 (int)sig->sighash.len > -1 ? sig->sighash.len * 8 : 0));
177 if ((s= sig->identity)) logmsg = string_append(logmsg, 2, " i=", s);
178 if (sig->created > 0) logmsg = string_cat(logmsg,
179 string_sprintf(" t=%lu", sig->created));
180 if (sig->expires > 0) logmsg = string_cat(logmsg,
181 string_sprintf(" x=%lu", sig->expires));
182 if (sig->bodylength > -1) logmsg = string_cat(logmsg,
183 string_sprintf(" l=%lu", sig->bodylength));
185 switch (sig->verify_status)
187 case PDKIM_VERIFY_NONE:
188 logmsg = string_cat(logmsg, " [not verified]");
191 case PDKIM_VERIFY_INVALID:
192 logmsg = string_cat(logmsg, " [invalid - ");
193 switch (sig->verify_ext_status)
195 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
196 logmsg = string_cat(logmsg,
197 "public key record (currently?) unavailable]");
200 case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
201 logmsg = string_cat(logmsg, "overlong public key record]");
204 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:
205 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT:
206 logmsg = string_cat(logmsg, "syntax error in public key record]");
209 case PDKIM_VERIFY_INVALID_SIGNATURE_ERROR:
210 logmsg = string_cat(logmsg, "signature tag missing or invalid]");
213 case PDKIM_VERIFY_INVALID_DKIM_VERSION:
214 logmsg = string_cat(logmsg, "unsupported DKIM version]");
218 logmsg = string_cat(logmsg, "unspecified problem]");
222 case PDKIM_VERIFY_FAIL:
224 string_cat(logmsg, " [verification failed - ");
225 switch (sig->verify_ext_status)
227 case PDKIM_VERIFY_FAIL_BODY:
228 logmsg = string_cat(logmsg,
229 "body hash mismatch (body probably modified in transit)]");
232 case PDKIM_VERIFY_FAIL_MESSAGE:
233 logmsg = string_cat(logmsg,
234 "signature did not verify (headers probably modified in transit)]");
238 logmsg = string_cat(logmsg, "unspecified reason]");
242 case PDKIM_VERIFY_PASS:
243 logmsg = string_cat(logmsg, " [verification succeeded]");
247 log_write(0, LOG_MAIN, "DKIM: %s", string_from_gstring(logmsg));
249 /* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */
252 g = string_append_listele(g, ':', sig->domain);
255 g = string_append_listele(g, ':', sig->identity);
257 /* Process next signature */
260 if (g) dkim_signers = g->s;
263 store_pool = dkim_verify_oldpool;
268 dkim_exim_acl_setup(uschar * id)
270 pdkim_signature * sig;
274 dkim_cur_signer = id;
276 if (dkim_disable_verify || !id || !dkim_verify_ctx)
279 /* Find signature to run ACL on */
281 for (sig = dkim_signatures; sig; sig = sig->next)
282 if ( (cmp_val = Ustrchr(id, '@') != NULL ? US sig->identity : US sig->domain)
283 && strcmpic(cmp_val, id) == 0
288 /* The "dkim_domain" and "dkim_selector" expansion variables have
289 related globals, since they are used in the signing code too.
290 Instead of inventing separate names for verification, we set
291 them here. This is easy since a domain and selector is guaranteed
292 to be in a signature. The other dkim_* expansion items are
293 dynamically fetched from dkim_cur_sig at expansion time (see
296 dkim_signing_domain = US sig->domain;
297 dkim_signing_selector = US sig->selector;
298 dkim_key_length = sig->sighash.len * 8;
305 dkim_exim_expand_defaults(int what)
309 case DKIM_ALGO: return US"";
310 case DKIM_BODYLENGTH: return US"9999999999999";
311 case DKIM_CANON_BODY: return US"";
312 case DKIM_CANON_HEADERS: return US"";
313 case DKIM_COPIEDHEADERS: return US"";
314 case DKIM_CREATED: return US"0";
315 case DKIM_EXPIRES: return US"9999999999999";
316 case DKIM_HEADERNAMES: return US"";
317 case DKIM_IDENTITY: return US"";
318 case DKIM_KEY_GRANULARITY: return US"*";
319 case DKIM_KEY_SRVTYPE: return US"*";
320 case DKIM_KEY_NOTES: return US"";
321 case DKIM_KEY_TESTING: return US"0";
322 case DKIM_NOSUBDOMAINS: return US"0";
323 case DKIM_VERIFY_STATUS: return US"none";
324 case DKIM_VERIFY_REASON: return US"";
325 default: return US"";
331 dkim_exim_expand_query(int what)
333 if (!dkim_verify_ctx || dkim_disable_verify || !dkim_cur_sig)
334 return dkim_exim_expand_defaults(what);
339 return dkim_sig_to_a_tag(dkim_cur_sig);
341 case DKIM_BODYLENGTH:
342 return dkim_cur_sig->bodylength >= 0
343 ? string_sprintf(OFF_T_FMT, (LONGLONG_T) dkim_cur_sig->bodylength)
344 : dkim_exim_expand_defaults(what);
346 case DKIM_CANON_BODY:
347 switch (dkim_cur_sig->canon_body)
349 case PDKIM_CANON_RELAXED: return US"relaxed";
350 case PDKIM_CANON_SIMPLE:
351 default: return US"simple";
354 case DKIM_CANON_HEADERS:
355 switch (dkim_cur_sig->canon_headers)
357 case PDKIM_CANON_RELAXED: return US"relaxed";
358 case PDKIM_CANON_SIMPLE:
359 default: return US"simple";
362 case DKIM_COPIEDHEADERS:
363 return dkim_cur_sig->copiedheaders
364 ? US dkim_cur_sig->copiedheaders : dkim_exim_expand_defaults(what);
367 return dkim_cur_sig->created > 0
368 ? string_sprintf("%llu", dkim_cur_sig->created)
369 : dkim_exim_expand_defaults(what);
372 return dkim_cur_sig->expires > 0
373 ? string_sprintf("%llu", dkim_cur_sig->expires)
374 : dkim_exim_expand_defaults(what);
376 case DKIM_HEADERNAMES:
377 return dkim_cur_sig->headernames
378 ? dkim_cur_sig->headernames : dkim_exim_expand_defaults(what);
381 return dkim_cur_sig->identity
382 ? US dkim_cur_sig->identity : dkim_exim_expand_defaults(what);
384 case DKIM_KEY_GRANULARITY:
385 return dkim_cur_sig->pubkey
386 ? dkim_cur_sig->pubkey->granularity
387 ? US dkim_cur_sig->pubkey->granularity
388 : dkim_exim_expand_defaults(what)
389 : dkim_exim_expand_defaults(what);
391 case DKIM_KEY_SRVTYPE:
392 return dkim_cur_sig->pubkey
393 ? dkim_cur_sig->pubkey->srvtype
394 ? US dkim_cur_sig->pubkey->srvtype
395 : dkim_exim_expand_defaults(what)
396 : dkim_exim_expand_defaults(what);
399 return dkim_cur_sig->pubkey
400 ? dkim_cur_sig->pubkey->notes
401 ? US dkim_cur_sig->pubkey->notes
402 : dkim_exim_expand_defaults(what)
403 : dkim_exim_expand_defaults(what);
405 case DKIM_KEY_TESTING:
406 return dkim_cur_sig->pubkey
407 ? dkim_cur_sig->pubkey->testing
409 : dkim_exim_expand_defaults(what)
410 : dkim_exim_expand_defaults(what);
412 case DKIM_NOSUBDOMAINS:
413 return dkim_cur_sig->pubkey
414 ? dkim_cur_sig->pubkey->no_subdomaining
416 : dkim_exim_expand_defaults(what)
417 : dkim_exim_expand_defaults(what);
419 case DKIM_VERIFY_STATUS:
420 switch (dkim_cur_sig->verify_status)
422 case PDKIM_VERIFY_INVALID: return US"invalid";
423 case PDKIM_VERIFY_FAIL: return US"fail";
424 case PDKIM_VERIFY_PASS: return US"pass";
425 case PDKIM_VERIFY_NONE:
426 default: return US"none";
429 case DKIM_VERIFY_REASON:
430 switch (dkim_cur_sig->verify_ext_status)
432 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
433 return US"pubkey_unavailable";
434 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:return US"pubkey_dns_syntax";
435 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT: return US"pubkey_der_syntax";
436 case PDKIM_VERIFY_FAIL_BODY: return US"bodyhash_mismatch";
437 case PDKIM_VERIFY_FAIL_MESSAGE: return US"signature_incorrect";
446 /* Generate signatures for the given file, returning a string.
447 If a prefix is given, prepend it to the file for the calculations.
451 dkim_exim_sign(int fd, off_t off, uschar * prefix,
452 struct ob_dkim * dkim, const uschar ** errstr)
454 const uschar * dkim_domain;
456 gstring * seen_doms = NULL;
458 pdkim_signature * sig;
464 int old_pool = store_pool;
467 store_pool = POOL_MAIN;
469 pdkim_init_context(&ctx, dkim->dot_stuffed, &dkim_exim_query_dns_txt);
471 if (!(dkim_domain = expand_cstring(dkim->dkim_domain)))
472 /* expansion error, do not send message. */
473 { errwhen = US"dkim_domain"; goto expand_bad; }
475 /* Set $dkim_domain expansion variable to each unique domain in list. */
477 while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep, NULL, 0)))
479 const uschar * dkim_sel;
482 if (dkim_signing_domain[0] == '\0')
485 /* Only sign once for each domain, no matter how often it
486 appears in the expanded list. */
488 if (match_isinlist(dkim_signing_domain, CUSS &seen_doms,
489 0, NULL, NULL, MCL_STRING, TRUE, NULL) == OK)
492 seen_doms = string_append_listele(seen_doms, ':', dkim_signing_domain);
494 /* Set $dkim_selector expansion variable to each selector in list,
497 if (!(dkim_sel = expand_string(dkim->dkim_selector)))
498 if (!(dkim_signing_selector = expand_string(dkim->dkim_selector)))
499 { errwhen = US"dkim_selector"; goto expand_bad; }
501 while ((dkim_signing_selector = string_nextinlist(&dkim_sel, &sel_sep,
504 uschar * dkim_canon_expanded;
506 uschar * dkim_sign_headers_expanded = NULL;
507 uschar * dkim_private_key_expanded;
508 uschar * dkim_hash_expanded;
509 uschar * dkim_identity_expanded = NULL;
511 /* Get canonicalization to use */
513 dkim_canon_expanded = dkim->dkim_canon
514 ? expand_string(dkim->dkim_canon) : US"relaxed";
515 if (!dkim_canon_expanded) /* expansion error, do not send message. */
516 { errwhen = US"dkim_canon"; goto expand_bad; }
518 if (Ustrcmp(dkim_canon_expanded, "relaxed") == 0)
519 pdkim_canon = PDKIM_CANON_RELAXED;
520 else if (Ustrcmp(dkim_canon_expanded, "simple") == 0)
521 pdkim_canon = PDKIM_CANON_SIMPLE;
524 log_write(0, LOG_MAIN,
525 "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",
526 dkim_canon_expanded);
527 pdkim_canon = PDKIM_CANON_RELAXED;
530 if ( dkim->dkim_sign_headers
531 && !(dkim_sign_headers_expanded = expand_string(dkim->dkim_sign_headers)))
532 { errwhen = US"dkim_sign_header"; goto expand_bad; }
533 /* else pass NULL, which means default header list */
535 /* Get private key to use. */
537 if (!(dkim_private_key_expanded = expand_string(dkim->dkim_private_key)))
538 { errwhen = US"dkim_private_key"; goto expand_bad; }
540 if ( Ustrlen(dkim_private_key_expanded) == 0
541 || Ustrcmp(dkim_private_key_expanded, "0") == 0
542 || Ustrcmp(dkim_private_key_expanded, "false") == 0
544 continue; /* don't sign, but no error */
546 if (dkim_private_key_expanded[0] == '/')
548 int privkey_fd, off = 0, len;
550 /* Looks like a filename, load the private key. */
552 memset(big_buffer, 0, big_buffer_size);
554 if ((privkey_fd = open(CS dkim_private_key_expanded, O_RDONLY)) < 0)
556 log_write(0, LOG_MAIN | LOG_PANIC, "unable to open "
557 "private key file for reading: %s",
558 dkim_private_key_expanded);
564 if ((len = read(privkey_fd, big_buffer + off, big_buffer_size - 2 - off)) < 0)
566 (void) close(privkey_fd);
567 log_write(0, LOG_MAIN|LOG_PANIC, "unable to read private key file: %s",
568 dkim_private_key_expanded);
575 (void) close(privkey_fd);
576 big_buffer[off] = '\0';
577 dkim_private_key_expanded = big_buffer;
580 if (!(dkim_hash_expanded = expand_string(dkim->dkim_hash)))
581 { errwhen = US"dkim_hash"; goto expand_bad; }
583 if (dkim->dkim_identity)
584 if (!(dkim_identity_expanded = expand_string(dkim->dkim_identity)))
585 { errwhen = US"dkim_identity"; goto expand_bad; }
586 else if (!*dkim_identity_expanded)
587 dkim_identity_expanded = NULL;
589 /*XXX so we currently nail signing to RSA + this hash.
590 Need to extract algo from privkey and check for disallowed combos. */
592 if (!(sig = pdkim_init_sign(&ctx, dkim_signing_domain,
593 dkim_signing_selector,
594 dkim_private_key_expanded,
599 dkim_private_key_expanded[0] = '\0';
601 pdkim_set_optional(sig,
602 CS dkim_sign_headers_expanded,
603 dkim_identity_expanded,
605 pdkim_canon, -1, 0, 0);
607 if (!ctx.sig) /* link sig to context chain */
611 pdkim_signature * n = ctx.sig;
612 while (n->next) n = n->next;
619 pdkim_feed(&ctx, prefix, Ustrlen(prefix));
621 if (lseek(fd, off, SEEK_SET) < 0)
624 while ((sread = read(fd, &buf, sizeof(buf))) > 0)
625 if ((pdkim_rc = pdkim_feed(&ctx, buf, sread)) != PDKIM_OK)
628 /* Handle failed read above. */
631 debug_printf("DKIM: Error reading -K file.\n");
636 /* Build string of headers, one per signature */
638 if ((pdkim_rc = pdkim_feed_finish(&ctx, &sig, errstr)) != PDKIM_OK)
641 for (sigbuf = NULL; sig; sig = sig->next)
642 sigbuf = string_append(sigbuf, 2, US sig->signature_header, US"\r\n");
644 (void) string_from_gstring(sigbuf);
647 store_pool = old_pool;
652 log_write(0, LOG_MAIN|LOG_PANIC,
653 "DKIM: signing failed: %.100s", pdkim_errstr(pdkim_rc));
659 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand %s: %s",
660 errwhen, expand_string_message);