1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) University of Cambridge, 1995 - 2016 */
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 BOOL dkim_collect_error = FALSE;
24 dkim_exim_query_dns_txt(char *name, char *answer)
30 lookup_dnssec_authenticated = NULL;
31 if (dns_lookup(&dnsa, US name, T_TXT, NULL) != DNS_SUCCEED)
32 return PDKIM_FAIL; /*XXX better error detail? logging? */
34 /* Search for TXT record */
36 for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
38 rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
39 if (rr->type == T_TXT)
42 int answer_offset = 0;
44 /* Copy record content to the answer buffer */
46 while (rr_offset < rr->size)
48 uschar len = rr->data[rr_offset++];
49 snprintf(answer + answer_offset,
50 PDKIM_DNS_TXT_MAX_RECLEN - answer_offset,
51 "%.*s", (int)len, (char *) (rr->data + rr_offset));
54 if (answer_offset >= PDKIM_DNS_TXT_MAX_RECLEN)
55 return PDKIM_FAIL; /*XXX better error detail? logging? */
60 return PDKIM_FAIL; /*XXX better error detail? logging? */
73 dkim_exim_verify_init(BOOL dot_stuffing)
75 /* There is a store-reset between header & body reception
76 so cannot use the main pool. Any allocs done by Exim
77 memory-handling must use the perm pool. */
79 dkim_verify_oldpool = store_pool;
80 store_pool = POOL_PERM;
82 /* Free previous context if there is one */
85 pdkim_free_ctx(dkim_verify_ctx);
87 /* Create new context */
89 dkim_verify_ctx = pdkim_init_verify(&dkim_exim_query_dns_txt, dot_stuffing);
90 dkim_collect_input = !!dkim_verify_ctx;
91 dkim_collect_error = FALSE;
93 /* Start feed up with any cached data */
96 store_pool = dkim_verify_oldpool;
101 dkim_exim_verify_feed(uschar * data, int len)
105 store_pool = POOL_PERM;
106 if ( dkim_collect_input
107 && (rc = pdkim_feed(dkim_verify_ctx, CS data, len)) != PDKIM_OK)
109 log_write(0, LOG_MAIN,
110 "DKIM: validation error: %.100s", pdkim_errstr(rc));
111 dkim_collect_error = TRUE;
112 dkim_collect_input = FALSE;
114 store_pool = dkim_verify_oldpool;
119 dkim_exim_verify_finish(void)
121 pdkim_signature *sig = NULL;
122 int dkim_signers_size = 0;
123 int dkim_signers_ptr = 0;
127 store_pool = POOL_PERM;
129 /* Delete eventual previous signature chain */
131 dkim_signatures = NULL;
133 if (dkim_collect_error)
135 log_write(0, LOG_MAIN,
136 "DKIM: Error while running this message through validation,"
137 " disabling signature verification.");
138 dkim_disable_verify = TRUE;
142 dkim_collect_input = FALSE;
144 /* Finish DKIM operation and fetch link to signatures chain */
146 if ((rc = pdkim_feed_finish(dkim_verify_ctx, &dkim_signatures)) != PDKIM_OK)
148 log_write(0, LOG_MAIN,
149 "DKIM: validation error: %.100s", pdkim_errstr(rc));
153 for (sig = dkim_signatures; sig; sig = sig->next)
158 /* Log a line for each signature */
160 uschar *logmsg = string_append(NULL, &size, &ptr, 5,
161 string_sprintf("d=%s s=%s c=%s/%s a=%s b=%d ",
164 sig->canon_headers == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
165 sig->canon_body == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
166 sig->algo == PDKIM_ALGO_RSA_SHA256
168 : sig->algo == PDKIM_ALGO_RSA_SHA1 ? "rsa-sha1" : "err",
169 (int)sig->sighash.len > -1 ? sig->sighash.len * 8 : 0
172 sig->identity ? string_sprintf("i=%s ", sig->identity) : US"",
173 sig->created > 0 ? string_sprintf("t=%lu ", sig->created) : US"",
174 sig->expires > 0 ? string_sprintf("x=%lu ", sig->expires) : US"",
175 sig->bodylength > -1 ? string_sprintf("l=%lu ", sig->bodylength) : US""
178 switch (sig->verify_status)
180 case PDKIM_VERIFY_NONE:
181 logmsg = string_append(logmsg, &size, &ptr, 1, "[not verified]");
184 case PDKIM_VERIFY_INVALID:
185 logmsg = string_append(logmsg, &size, &ptr, 1, "[invalid - ");
186 switch (sig->verify_ext_status)
188 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
189 logmsg = string_append(logmsg, &size, &ptr, 1,
190 "public key record (currently?) unavailable]");
193 case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
194 logmsg = string_append(logmsg, &size, &ptr, 1,
195 "overlong public key record]");
198 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:
199 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT:
200 logmsg = string_append(logmsg, &size, &ptr, 1,
201 "syntax error in public key record]");
204 case PDKIM_VERIFY_INVALID_SIGNATURE_ERROR:
205 logmsg = string_append(logmsg, &size, &ptr, 1,
206 "signature tag missing or invalid]");
209 case PDKIM_VERIFY_INVALID_DKIM_VERSION:
210 logmsg = string_append(logmsg, &size, &ptr, 1,
211 "unsupported DKIM version]");
215 logmsg = string_append(logmsg, &size, &ptr, 1,
216 "unspecified problem]");
220 case PDKIM_VERIFY_FAIL:
222 string_append(logmsg, &size, &ptr, 1, "[verification failed - ");
223 switch (sig->verify_ext_status)
225 case PDKIM_VERIFY_FAIL_BODY:
226 logmsg = string_append(logmsg, &size, &ptr, 1,
227 "body hash mismatch (body probably modified in transit)]");
230 case PDKIM_VERIFY_FAIL_MESSAGE:
231 logmsg = string_append(logmsg, &size, &ptr, 1,
232 "signature did not verify (headers probably modified in transit)]");
236 logmsg = string_append(logmsg, &size, &ptr, 1, "unspecified reason]");
240 case PDKIM_VERIFY_PASS:
242 string_append(logmsg, &size, &ptr, 1, "[verification succeeded]");
247 log_write(0, LOG_MAIN, "DKIM: %s", logmsg);
249 /* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */
251 dkim_signers = string_append(dkim_signers,
253 &dkim_signers_ptr, 2, sig->domain, ":");
256 dkim_signers = string_append(dkim_signers,
258 &dkim_signers_ptr, 2, sig->identity, ":");
260 /* Process next signature */
263 /* NULL-terminate and chop the last colon from the domain list */
267 dkim_signers[dkim_signers_ptr] = '\0';
268 if (Ustrlen(dkim_signers) > 0)
269 dkim_signers[Ustrlen(dkim_signers) - 1] = '\0';
273 store_pool = dkim_verify_oldpool;
278 dkim_exim_acl_setup(uschar * id)
280 pdkim_signature * sig;
284 dkim_cur_signer = id;
286 if (dkim_disable_verify || !id || !dkim_verify_ctx)
289 /* Find signature to run ACL on */
291 for (sig = dkim_signatures; sig; sig = sig->next)
292 if ( (cmp_val = Ustrchr(id, '@') != NULL ? US sig->identity : US sig->domain)
293 && strcmpic(cmp_val, id) == 0
298 /* The "dkim_domain" and "dkim_selector" expansion variables have
299 related globals, since they are used in the signing code too.
300 Instead of inventing separate names for verification, we set
301 them here. This is easy since a domain and selector is guaranteed
302 to be in a signature. The other dkim_* expansion items are
303 dynamically fetched from dkim_cur_sig at expansion time (see
306 dkim_signing_domain = US sig->domain;
307 dkim_signing_selector = US sig->selector;
308 dkim_key_length = sig->sighash.len * 8;
315 dkim_exim_expand_defaults(int what)
319 case DKIM_ALGO: return US"";
320 case DKIM_BODYLENGTH: return US"9999999999999";
321 case DKIM_CANON_BODY: return US"";
322 case DKIM_CANON_HEADERS: return US"";
323 case DKIM_COPIEDHEADERS: return US"";
324 case DKIM_CREATED: return US"0";
325 case DKIM_EXPIRES: return US"9999999999999";
326 case DKIM_HEADERNAMES: return US"";
327 case DKIM_IDENTITY: return US"";
328 case DKIM_KEY_GRANULARITY: return US"*";
329 case DKIM_KEY_SRVTYPE: return US"*";
330 case DKIM_KEY_NOTES: return US"";
331 case DKIM_KEY_TESTING: return US"0";
332 case DKIM_NOSUBDOMAINS: return US"0";
333 case DKIM_VERIFY_STATUS: return US"none";
334 case DKIM_VERIFY_REASON: return US"";
335 default: return US"";
341 dkim_exim_expand_query(int what)
343 if (!dkim_verify_ctx || dkim_disable_verify || !dkim_cur_sig)
344 return dkim_exim_expand_defaults(what);
349 switch (dkim_cur_sig->algo)
351 case PDKIM_ALGO_RSA_SHA1: return US"rsa-sha1";
352 case PDKIM_ALGO_RSA_SHA256:
353 default: return US"rsa-sha256";
356 case DKIM_BODYLENGTH:
357 return dkim_cur_sig->bodylength >= 0
358 ? string_sprintf(OFF_T_FMT, (LONGLONG_T) dkim_cur_sig->bodylength)
359 : dkim_exim_expand_defaults(what);
361 case DKIM_CANON_BODY:
362 switch (dkim_cur_sig->canon_body)
364 case PDKIM_CANON_RELAXED: return US"relaxed";
365 case PDKIM_CANON_SIMPLE:
366 default: return US"simple";
369 case DKIM_CANON_HEADERS:
370 switch (dkim_cur_sig->canon_headers)
372 case PDKIM_CANON_RELAXED: return US"relaxed";
373 case PDKIM_CANON_SIMPLE:
374 default: return US"simple";
377 case DKIM_COPIEDHEADERS:
378 return dkim_cur_sig->copiedheaders
379 ? US dkim_cur_sig->copiedheaders : dkim_exim_expand_defaults(what);
382 return dkim_cur_sig->created > 0
383 ? string_sprintf("%llu", dkim_cur_sig->created)
384 : dkim_exim_expand_defaults(what);
387 return dkim_cur_sig->expires > 0
388 ? string_sprintf("%llu", dkim_cur_sig->expires)
389 : dkim_exim_expand_defaults(what);
391 case DKIM_HEADERNAMES:
392 return dkim_cur_sig->headernames
393 ? dkim_cur_sig->headernames : dkim_exim_expand_defaults(what);
396 return dkim_cur_sig->identity
397 ? US dkim_cur_sig->identity : dkim_exim_expand_defaults(what);
399 case DKIM_KEY_GRANULARITY:
400 return dkim_cur_sig->pubkey
401 ? dkim_cur_sig->pubkey->granularity
402 ? US dkim_cur_sig->pubkey->granularity
403 : dkim_exim_expand_defaults(what)
404 : dkim_exim_expand_defaults(what);
406 case DKIM_KEY_SRVTYPE:
407 return dkim_cur_sig->pubkey
408 ? dkim_cur_sig->pubkey->srvtype
409 ? US dkim_cur_sig->pubkey->srvtype
410 : dkim_exim_expand_defaults(what)
411 : dkim_exim_expand_defaults(what);
414 return dkim_cur_sig->pubkey
415 ? dkim_cur_sig->pubkey->notes
416 ? US dkim_cur_sig->pubkey->notes
417 : dkim_exim_expand_defaults(what)
418 : dkim_exim_expand_defaults(what);
420 case DKIM_KEY_TESTING:
421 return dkim_cur_sig->pubkey
422 ? dkim_cur_sig->pubkey->testing
424 : dkim_exim_expand_defaults(what)
425 : dkim_exim_expand_defaults(what);
427 case DKIM_NOSUBDOMAINS:
428 return dkim_cur_sig->pubkey
429 ? dkim_cur_sig->pubkey->no_subdomaining
431 : dkim_exim_expand_defaults(what)
432 : dkim_exim_expand_defaults(what);
434 case DKIM_VERIFY_STATUS:
435 switch (dkim_cur_sig->verify_status)
437 case PDKIM_VERIFY_INVALID: return US"invalid";
438 case PDKIM_VERIFY_FAIL: return US"fail";
439 case PDKIM_VERIFY_PASS: return US"pass";
440 case PDKIM_VERIFY_NONE:
441 default: return US"none";
444 case DKIM_VERIFY_REASON:
445 switch (dkim_cur_sig->verify_ext_status)
447 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
448 return US"pubkey_unavailable";
449 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:return US"pubkey_dns_syntax";
450 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT: return US"pubkey_der_syntax";
451 case PDKIM_VERIFY_FAIL_BODY: return US"bodyhash_mismatch";
452 case PDKIM_VERIFY_FAIL_MESSAGE: return US"signature_incorrect";
462 dkim_exim_sign(int dkim_fd, struct ob_dkim * dkim)
464 const uschar * dkim_domain;
466 uschar *seen_items = NULL;
467 int seen_items_size = 0;
468 int seen_items_offset = 0;
470 uschar *dkim_canon_expanded;
471 uschar *dkim_sign_headers_expanded;
472 uschar *dkim_private_key_expanded;
473 pdkim_ctx *ctx = NULL;
475 uschar *sigbuf = NULL;
478 pdkim_signature *signature;
484 int old_pool = store_pool;
486 store_pool = POOL_MAIN;
488 if (!(dkim_domain = expand_cstring(dkim->dkim_domain)))
490 /* expansion error, do not send message. */
491 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
492 "dkim_domain: %s", expand_string_message);
496 /* Set $dkim_domain expansion variable to each unique domain in list. */
498 while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep,
499 itembuf, sizeof(itembuf))))
501 if (!dkim_signing_domain || dkim_signing_domain[0] == '\0')
504 /* Only sign once for each domain, no matter how often it
505 appears in the expanded list. */
509 const uschar *seen_items_list = seen_items;
510 if (match_isinlist(dkim_signing_domain,
511 &seen_items_list, 0, NULL, NULL, MCL_STRING, TRUE,
516 string_append(seen_items, &seen_items_size, &seen_items_offset, 1, ":");
520 string_append(seen_items, &seen_items_size, &seen_items_offset, 1,
521 dkim_signing_domain);
522 seen_items[seen_items_offset] = '\0';
524 /* Set up $dkim_selector expansion variable. */
526 if (!(dkim_signing_selector = expand_string(dkim->dkim_selector)))
528 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
529 "dkim_selector: %s", expand_string_message);
533 /* Get canonicalization to use */
535 dkim_canon_expanded = dkim->dkim_canon
536 ? expand_string(dkim->dkim_canon) : US"relaxed";
537 if (!dkim_canon_expanded)
539 /* expansion error, do not send message. */
540 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
541 "dkim_canon: %s", expand_string_message);
545 if (Ustrcmp(dkim_canon_expanded, "relaxed") == 0)
546 pdkim_canon = PDKIM_CANON_RELAXED;
547 else if (Ustrcmp(dkim_canon_expanded, "simple") == 0)
548 pdkim_canon = PDKIM_CANON_SIMPLE;
551 log_write(0, LOG_MAIN,
552 "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",
553 dkim_canon_expanded);
554 pdkim_canon = PDKIM_CANON_RELAXED;
557 dkim_sign_headers_expanded = NULL;
558 if (dkim->dkim_sign_headers)
559 if (!(dkim_sign_headers_expanded = expand_string(dkim->dkim_sign_headers)))
561 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
562 "dkim_sign_headers: %s", expand_string_message);
565 /* else pass NULL, which means default header list */
567 /* Get private key to use. */
569 if (!(dkim_private_key_expanded = expand_string(dkim->dkim_private_key)))
571 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
572 "dkim_private_key: %s", expand_string_message);
576 if ( Ustrlen(dkim_private_key_expanded) == 0
577 || Ustrcmp(dkim_private_key_expanded, "0") == 0
578 || Ustrcmp(dkim_private_key_expanded, "false") == 0
580 continue; /* don't sign, but no error */
582 if (dkim_private_key_expanded[0] == '/')
586 /* Looks like a filename, load the private key. */
588 memset(big_buffer, 0, big_buffer_size);
590 if ((privkey_fd = open(CS dkim_private_key_expanded, O_RDONLY)) < 0)
592 log_write(0, LOG_MAIN | LOG_PANIC, "unable to open "
593 "private key file for reading: %s",
594 dkim_private_key_expanded);
598 if (read(privkey_fd, big_buffer, big_buffer_size - 2) < 0)
600 log_write(0, LOG_MAIN|LOG_PANIC, "unable to read private key file: %s",
601 dkim_private_key_expanded);
605 (void) close(privkey_fd);
606 dkim_private_key_expanded = big_buffer;
609 ctx = pdkim_init_sign(CS dkim_signing_domain,
610 CS dkim_signing_selector,
611 CS dkim_private_key_expanded,
612 PDKIM_ALGO_RSA_SHA256,
614 &dkim_exim_query_dns_txt
616 dkim_private_key_expanded[0] = '\0';
617 pdkim_set_optional(ctx,
618 CS dkim_sign_headers_expanded,
621 pdkim_canon, -1, 0, 0);
623 lseek(dkim_fd, 0, SEEK_SET);
625 while ((sread = read(dkim_fd, &buf, sizeof(buf))) > 0)
626 if ((pdkim_rc = pdkim_feed(ctx, buf, sread)) != PDKIM_OK)
629 /* Handle failed read above. */
632 debug_printf("DKIM: Error reading -K file.\n");
637 if ((pdkim_rc = pdkim_feed_finish(ctx, &signature)) != PDKIM_OK)
640 sigbuf = string_append(sigbuf, &sigsize, &sigptr, 2,
641 US signature->signature_header, US"\r\n");
649 sigbuf[sigptr] = '\0';
658 store_pool = old_pool;
663 log_write(0, LOG_MAIN|LOG_PANIC,
664 "DKIM: signing failed: %.100s", pdkim_errstr(pdkim_rc));