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;
138 dkim_exim_verify_finish(void)
140 pdkim_signature * sig = NULL;
143 const uschar * errstr;
145 store_pool = POOL_PERM;
147 /* Delete eventual previous signature chain */
150 dkim_signatures = NULL;
152 if (dkim_collect_error)
154 log_write(0, LOG_MAIN,
155 "DKIM: Error during validation, disabling signature verification: %.100s",
157 dkim_disable_verify = TRUE;
161 dkim_collect_input = FALSE;
163 /* Finish DKIM operation and fetch link to signatures chain */
165 rc = pdkim_feed_finish(dkim_verify_ctx, &dkim_signatures, &errstr);
168 log_write(0, LOG_MAIN, "DKIM: validation error: %.100s%s%s", pdkim_errstr(rc),
169 errstr ? ": " : "", errstr ? errstr : US"");
173 for (sig = dkim_signatures; sig; sig = sig->next)
178 /* Log a line for each signature */
180 if (!(s = sig->domain)) s = US"<UNSET>";
181 logmsg = string_append(NULL, 2, "d=", s);
182 if (!(s = sig->selector)) s = US"<UNSET>";
183 logmsg = string_append(logmsg, 2, " s=", s);
184 logmsg = string_append(logmsg, 7,
185 " c=", sig->canon_headers == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
186 "/", sig->canon_body == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
187 " a=", dkim_sig_to_a_tag(sig),
188 string_sprintf(" b=" SIZE_T_FMT,
189 (int)sig->sighash.len > -1 ? sig->sighash.len * 8 : 0));
190 if ((s= sig->identity)) logmsg = string_append(logmsg, 2, " i=", s);
191 if (sig->created > 0) logmsg = string_cat(logmsg,
192 string_sprintf(" t=%lu", sig->created));
193 if (sig->expires > 0) logmsg = string_cat(logmsg,
194 string_sprintf(" x=%lu", sig->expires));
195 if (sig->bodylength > -1) logmsg = string_cat(logmsg,
196 string_sprintf(" l=%lu", sig->bodylength));
198 switch (sig->verify_status)
200 case PDKIM_VERIFY_NONE:
201 logmsg = string_cat(logmsg, " [not verified]");
204 case PDKIM_VERIFY_INVALID:
205 logmsg = string_cat(logmsg, " [invalid - ");
206 switch (sig->verify_ext_status)
208 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
209 logmsg = string_cat(logmsg,
210 "public key record (currently?) unavailable]");
213 case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
214 logmsg = string_cat(logmsg, "overlong public key record]");
217 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:
218 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT:
219 logmsg = string_cat(logmsg, "syntax error in public key record]");
222 case PDKIM_VERIFY_INVALID_SIGNATURE_ERROR:
223 logmsg = string_cat(logmsg, "signature tag missing or invalid]");
226 case PDKIM_VERIFY_INVALID_DKIM_VERSION:
227 logmsg = string_cat(logmsg, "unsupported DKIM version]");
231 logmsg = string_cat(logmsg, "unspecified problem]");
235 case PDKIM_VERIFY_FAIL:
237 string_cat(logmsg, " [verification failed - ");
238 switch (sig->verify_ext_status)
240 case PDKIM_VERIFY_FAIL_BODY:
241 logmsg = string_cat(logmsg,
242 "body hash mismatch (body probably modified in transit)]");
245 case PDKIM_VERIFY_FAIL_MESSAGE:
246 logmsg = string_cat(logmsg,
247 "signature did not verify (headers probably modified in transit)]");
251 logmsg = string_cat(logmsg, "unspecified reason]");
255 case PDKIM_VERIFY_PASS:
256 logmsg = string_cat(logmsg, " [verification succeeded]");
260 log_write(0, LOG_MAIN, "DKIM: %s", string_from_gstring(logmsg));
262 /* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */
265 g = string_append_listele(g, ':', sig->domain);
268 g = string_append_listele(g, ':', sig->identity);
270 /* Process next signature */
273 if (g) dkim_signers = g->s;
276 store_pool = dkim_verify_oldpool;
281 dkim_exim_acl_setup(uschar * id)
283 pdkim_signature * sig;
287 dkim_cur_signer = id;
289 if (dkim_disable_verify || !id || !dkim_verify_ctx)
292 /* Find signature to run ACL on */
294 for (sig = dkim_signatures; sig; sig = sig->next)
295 if ( (cmp_val = Ustrchr(id, '@') != NULL ? US sig->identity : US sig->domain)
296 && strcmpic(cmp_val, id) == 0
301 /* The "dkim_domain" and "dkim_selector" expansion variables have
302 related globals, since they are used in the signing code too.
303 Instead of inventing separate names for verification, we set
304 them here. This is easy since a domain and selector is guaranteed
305 to be in a signature. The other dkim_* expansion items are
306 dynamically fetched from dkim_cur_sig at expansion time (see
309 dkim_signing_domain = US sig->domain;
310 dkim_signing_selector = US sig->selector;
311 dkim_key_length = sig->sighash.len * 8;
318 dkim_exim_expand_defaults(int what)
322 case DKIM_ALGO: return US"";
323 case DKIM_BODYLENGTH: return US"9999999999999";
324 case DKIM_CANON_BODY: return US"";
325 case DKIM_CANON_HEADERS: return US"";
326 case DKIM_COPIEDHEADERS: return US"";
327 case DKIM_CREATED: return US"0";
328 case DKIM_EXPIRES: return US"9999999999999";
329 case DKIM_HEADERNAMES: return US"";
330 case DKIM_IDENTITY: return US"";
331 case DKIM_KEY_GRANULARITY: return US"*";
332 case DKIM_KEY_SRVTYPE: return US"*";
333 case DKIM_KEY_NOTES: return US"";
334 case DKIM_KEY_TESTING: return US"0";
335 case DKIM_NOSUBDOMAINS: return US"0";
336 case DKIM_VERIFY_STATUS: return US"none";
337 case DKIM_VERIFY_REASON: return US"";
338 default: return US"";
344 dkim_exim_expand_query(int what)
346 if (!dkim_verify_ctx || dkim_disable_verify || !dkim_cur_sig)
347 return dkim_exim_expand_defaults(what);
352 return dkim_sig_to_a_tag(dkim_cur_sig);
354 case DKIM_BODYLENGTH:
355 return dkim_cur_sig->bodylength >= 0
356 ? string_sprintf("%ld", dkim_cur_sig->bodylength)
357 : dkim_exim_expand_defaults(what);
359 case DKIM_CANON_BODY:
360 switch (dkim_cur_sig->canon_body)
362 case PDKIM_CANON_RELAXED: return US"relaxed";
363 case PDKIM_CANON_SIMPLE:
364 default: return US"simple";
367 case DKIM_CANON_HEADERS:
368 switch (dkim_cur_sig->canon_headers)
370 case PDKIM_CANON_RELAXED: return US"relaxed";
371 case PDKIM_CANON_SIMPLE:
372 default: return US"simple";
375 case DKIM_COPIEDHEADERS:
376 return dkim_cur_sig->copiedheaders
377 ? US dkim_cur_sig->copiedheaders : dkim_exim_expand_defaults(what);
380 return dkim_cur_sig->created > 0
381 ? string_sprintf("%lu", dkim_cur_sig->created)
382 : dkim_exim_expand_defaults(what);
385 return dkim_cur_sig->expires > 0
386 ? string_sprintf("%lu", dkim_cur_sig->expires)
387 : dkim_exim_expand_defaults(what);
389 case DKIM_HEADERNAMES:
390 return dkim_cur_sig->headernames
391 ? dkim_cur_sig->headernames : dkim_exim_expand_defaults(what);
394 return dkim_cur_sig->identity
395 ? US dkim_cur_sig->identity : dkim_exim_expand_defaults(what);
397 case DKIM_KEY_GRANULARITY:
398 return dkim_cur_sig->pubkey
399 ? dkim_cur_sig->pubkey->granularity
400 ? US dkim_cur_sig->pubkey->granularity
401 : dkim_exim_expand_defaults(what)
402 : dkim_exim_expand_defaults(what);
404 case DKIM_KEY_SRVTYPE:
405 return dkim_cur_sig->pubkey
406 ? dkim_cur_sig->pubkey->srvtype
407 ? US dkim_cur_sig->pubkey->srvtype
408 : dkim_exim_expand_defaults(what)
409 : dkim_exim_expand_defaults(what);
412 return dkim_cur_sig->pubkey
413 ? dkim_cur_sig->pubkey->notes
414 ? US dkim_cur_sig->pubkey->notes
415 : dkim_exim_expand_defaults(what)
416 : dkim_exim_expand_defaults(what);
418 case DKIM_KEY_TESTING:
419 return dkim_cur_sig->pubkey
420 ? dkim_cur_sig->pubkey->testing
422 : dkim_exim_expand_defaults(what)
423 : dkim_exim_expand_defaults(what);
425 case DKIM_NOSUBDOMAINS:
426 return dkim_cur_sig->pubkey
427 ? dkim_cur_sig->pubkey->no_subdomaining
429 : dkim_exim_expand_defaults(what)
430 : dkim_exim_expand_defaults(what);
432 case DKIM_VERIFY_STATUS:
433 switch (dkim_cur_sig->verify_status)
435 case PDKIM_VERIFY_INVALID: return US"invalid";
436 case PDKIM_VERIFY_FAIL: return US"fail";
437 case PDKIM_VERIFY_PASS: return US"pass";
438 case PDKIM_VERIFY_NONE:
439 default: return US"none";
442 case DKIM_VERIFY_REASON:
443 switch (dkim_cur_sig->verify_ext_status)
445 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
446 return US"pubkey_unavailable";
447 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:return US"pubkey_dns_syntax";
448 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT: return US"pubkey_der_syntax";
449 case PDKIM_VERIFY_FAIL_BODY: return US"bodyhash_mismatch";
450 case PDKIM_VERIFY_FAIL_MESSAGE: return US"signature_incorrect";
459 /* Generate signatures for the given file, returning a string.
460 If a prefix is given, prepend it to the file for the calculations.
464 dkim_exim_sign(int fd, off_t off, uschar * prefix,
465 struct ob_dkim * dkim, const uschar ** errstr)
467 const uschar * dkim_domain;
469 gstring * seen_doms = NULL;
471 pdkim_signature * sig;
477 int old_pool = store_pool;
480 store_pool = POOL_MAIN;
482 pdkim_init_context(&ctx, dkim->dot_stuffed, &dkim_exim_query_dns_txt);
484 if (!(dkim_domain = expand_cstring(dkim->dkim_domain)))
485 /* expansion error, do not send message. */
486 { errwhen = US"dkim_domain"; goto expand_bad; }
488 /* Set $dkim_domain expansion variable to each unique domain in list. */
490 while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep, NULL, 0)))
492 const uschar * dkim_sel;
495 if (dkim_signing_domain[0] == '\0')
498 /* Only sign once for each domain, no matter how often it
499 appears in the expanded list. */
501 if (match_isinlist(dkim_signing_domain, CUSS &seen_doms,
502 0, NULL, NULL, MCL_STRING, TRUE, NULL) == OK)
505 seen_doms = string_append_listele(seen_doms, ':', dkim_signing_domain);
507 /* Set $dkim_selector expansion variable to each selector in list,
510 if (!(dkim_sel = expand_string(dkim->dkim_selector)))
511 if (!(dkim_signing_selector = expand_string(dkim->dkim_selector)))
512 { errwhen = US"dkim_selector"; goto expand_bad; }
514 while ((dkim_signing_selector = string_nextinlist(&dkim_sel, &sel_sep,
517 uschar * dkim_canon_expanded;
519 uschar * dkim_sign_headers_expanded = NULL;
520 uschar * dkim_private_key_expanded;
521 uschar * dkim_hash_expanded;
522 uschar * dkim_identity_expanded = NULL;
524 /* Get canonicalization to use */
526 dkim_canon_expanded = dkim->dkim_canon
527 ? expand_string(dkim->dkim_canon) : US"relaxed";
528 if (!dkim_canon_expanded) /* expansion error, do not send message. */
529 { errwhen = US"dkim_canon"; goto expand_bad; }
531 if (Ustrcmp(dkim_canon_expanded, "relaxed") == 0)
532 pdkim_canon = PDKIM_CANON_RELAXED;
533 else if (Ustrcmp(dkim_canon_expanded, "simple") == 0)
534 pdkim_canon = PDKIM_CANON_SIMPLE;
537 log_write(0, LOG_MAIN,
538 "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",
539 dkim_canon_expanded);
540 pdkim_canon = PDKIM_CANON_RELAXED;
543 if ( dkim->dkim_sign_headers
544 && !(dkim_sign_headers_expanded = expand_string(dkim->dkim_sign_headers)))
545 { errwhen = US"dkim_sign_header"; goto expand_bad; }
546 /* else pass NULL, which means default header list */
548 /* Get private key to use. */
550 if (!(dkim_private_key_expanded = expand_string(dkim->dkim_private_key)))
551 { errwhen = US"dkim_private_key"; goto expand_bad; }
553 if ( Ustrlen(dkim_private_key_expanded) == 0
554 || Ustrcmp(dkim_private_key_expanded, "0") == 0
555 || Ustrcmp(dkim_private_key_expanded, "false") == 0
557 continue; /* don't sign, but no error */
559 if (dkim_private_key_expanded[0] == '/')
561 int privkey_fd, off = 0, len;
563 /* Looks like a filename, load the private key. */
565 memset(big_buffer, 0, big_buffer_size);
567 if ((privkey_fd = open(CS dkim_private_key_expanded, O_RDONLY)) < 0)
569 log_write(0, LOG_MAIN | LOG_PANIC, "unable to open "
570 "private key file for reading: %s",
571 dkim_private_key_expanded);
577 if ((len = read(privkey_fd, big_buffer + off, big_buffer_size - 2 - off)) < 0)
579 (void) close(privkey_fd);
580 log_write(0, LOG_MAIN|LOG_PANIC, "unable to read private key file: %s",
581 dkim_private_key_expanded);
588 (void) close(privkey_fd);
589 big_buffer[off] = '\0';
590 dkim_private_key_expanded = big_buffer;
593 if (!(dkim_hash_expanded = expand_string(dkim->dkim_hash)))
594 { errwhen = US"dkim_hash"; goto expand_bad; }
596 if (dkim->dkim_identity)
597 if (!(dkim_identity_expanded = expand_string(dkim->dkim_identity)))
598 { errwhen = US"dkim_identity"; goto expand_bad; }
599 else if (!*dkim_identity_expanded)
600 dkim_identity_expanded = NULL;
602 /*XXX so we currently nail signing to RSA + this hash.
603 Need to extract algo from privkey and check for disallowed combos. */
605 if (!(sig = pdkim_init_sign(&ctx, dkim_signing_domain,
606 dkim_signing_selector,
607 dkim_private_key_expanded,
612 dkim_private_key_expanded[0] = '\0';
614 pdkim_set_optional(sig,
615 CS dkim_sign_headers_expanded,
616 dkim_identity_expanded,
618 pdkim_canon, -1, 0, 0);
620 if (!ctx.sig) /* link sig to context chain */
624 pdkim_signature * n = ctx.sig;
625 while (n->next) n = n->next;
632 pdkim_feed(&ctx, prefix, Ustrlen(prefix));
634 if (lseek(fd, off, SEEK_SET) < 0)
637 while ((sread = read(fd, &buf, sizeof(buf))) > 0)
638 if ((pdkim_rc = pdkim_feed(&ctx, buf, sread)) != PDKIM_OK)
641 /* Handle failed read above. */
644 debug_printf("DKIM: Error reading -K file.\n");
649 /* Build string of headers, one per signature */
651 if ((pdkim_rc = pdkim_feed_finish(&ctx, &sig, errstr)) != PDKIM_OK)
654 for (sigbuf = NULL; sig; sig = sig->next)
655 sigbuf = string_append(sigbuf, 2, US sig->signature_header, US"\r\n");
657 (void) string_from_gstring(sigbuf);
660 store_pool = old_pool;
665 log_write(0, LOG_MAIN|LOG_PANIC,
666 "DKIM: signing failed: %.100s", pdkim_errstr(pdkim_rc));
672 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand %s: %s",
673 errwhen, expand_string_message);
677 # endif /*!MACRO_PREDEF*/
678 #endif /*!DISABLE_DKIM*/