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;
23 dkim_exim_query_dns_txt(char *name, char *answer)
29 lookup_dnssec_authenticated = NULL;
30 if (dns_lookup(&dnsa, US name, T_TXT, NULL) != DNS_SUCCEED)
31 return PDKIM_FAIL; /*XXX better error detail? logging? */
33 /* Search for TXT record */
35 for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
37 rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
38 if (rr->type == T_TXT)
41 int answer_offset = 0;
43 /* Copy record content to the answer buffer */
45 while (rr_offset < rr->size)
47 uschar len = rr->data[rr_offset++];
48 snprintf(answer + answer_offset,
49 PDKIM_DNS_TXT_MAX_RECLEN - answer_offset,
50 "%.*s", (int)len, (char *) (rr->data + rr_offset));
53 if (answer_offset >= PDKIM_DNS_TXT_MAX_RECLEN)
54 return PDKIM_FAIL; /*XXX better error detail? logging? */
59 return PDKIM_FAIL; /*XXX better error detail? logging? */
72 dkim_exim_verify_init(BOOL dot_stuffing)
74 /* There is a store-reset between header & body reception
75 so cannot use the main pool. Any allocs done by Exim
76 memory-handling must use the perm pool. */
78 dkim_verify_oldpool = store_pool;
79 store_pool = POOL_PERM;
81 /* Free previous context if there is one */
84 pdkim_free_ctx(dkim_verify_ctx);
86 /* Create new context */
88 dkim_verify_ctx = pdkim_init_verify(&dkim_exim_query_dns_txt, dot_stuffing);
89 dkim_collect_input = !!dkim_verify_ctx;
91 /* Start feed up with any cached data */
94 store_pool = dkim_verify_oldpool;
99 dkim_exim_verify_feed(uschar * data, int len)
103 store_pool = POOL_PERM;
104 if ( dkim_collect_input
105 && (rc = pdkim_feed(dkim_verify_ctx, CS data, len)) != PDKIM_OK)
107 log_write(0, LOG_MAIN,
108 "DKIM: validation error: %.100s", pdkim_errstr(rc));
109 dkim_collect_input = FALSE;
111 store_pool = dkim_verify_oldpool;
116 dkim_exim_verify_finish(void)
118 pdkim_signature *sig = NULL;
119 int dkim_signers_size = 0;
120 int dkim_signers_ptr = 0;
124 store_pool = POOL_PERM;
126 /* Delete eventual previous signature chain */
128 dkim_signatures = NULL;
130 /* If we have arrived here with dkim_collect_input == FALSE, it
131 means there was a processing error somewhere along the way.
132 Log the incident and disable futher verification. */
134 if (!dkim_collect_input)
136 log_write(0, LOG_MAIN,
137 "DKIM: Error while running this message through validation,"
138 " disabling signature verification.");
139 dkim_disable_verify = TRUE;
143 dkim_collect_input = FALSE;
145 /* Finish DKIM operation and fetch link to signatures chain */
147 if ((rc = pdkim_feed_finish(dkim_verify_ctx, &dkim_signatures)) != PDKIM_OK)
149 log_write(0, LOG_MAIN,
150 "DKIM: validation error: %.100s", pdkim_errstr(rc));
154 for (sig = dkim_signatures; sig; sig = sig->next)
159 /* Log a line for each signature */
161 uschar *logmsg = string_append(NULL, &size, &ptr, 5,
162 string_sprintf("d=%s s=%s c=%s/%s a=%s b=%d ",
165 sig->canon_headers == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
166 sig->canon_body == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
167 sig->algo == PDKIM_ALGO_RSA_SHA256
169 : sig->algo == PDKIM_ALGO_RSA_SHA1 ? "rsa-sha1" : "err",
170 (int)sig->sigdata.len > -1 ? sig->sigdata.len * 8 : 0
173 sig->identity ? string_sprintf("i=%s ", sig->identity) : US"",
174 sig->created > 0 ? string_sprintf("t=%lu ", sig->created) : US"",
175 sig->expires > 0 ? string_sprintf("x=%lu ", sig->expires) : US"",
176 sig->bodylength > -1 ? string_sprintf("l=%lu ", sig->bodylength) : US""
179 switch (sig->verify_status)
181 case PDKIM_VERIFY_NONE:
182 logmsg = string_append(logmsg, &size, &ptr, 1, "[not verified]");
185 case PDKIM_VERIFY_INVALID:
186 logmsg = string_append(logmsg, &size, &ptr, 1, "[invalid - ");
187 switch (sig->verify_ext_status)
189 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
190 logmsg = string_append(logmsg, &size, &ptr, 1,
191 "public key record (currently?) unavailable]");
194 case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
195 logmsg = string_append(logmsg, &size, &ptr, 1,
196 "overlong public key record]");
199 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:
200 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT:
201 logmsg = string_append(logmsg, &size, &ptr, 1,
202 "syntax error in public key record]");
205 case PDKIM_VERIFY_INVALID_SIGNATURE_ERROR:
206 logmsg = string_append(logmsg, &size, &ptr, 1,
207 "signature tag missing or invalid]");
210 case PDKIM_VERIFY_INVALID_DKIM_VERSION:
211 logmsg = string_append(logmsg, &size, &ptr, 1,
212 "unsupported DKIM version]");
216 logmsg = string_append(logmsg, &size, &ptr, 1,
217 "unspecified problem]");
221 case PDKIM_VERIFY_FAIL:
223 string_append(logmsg, &size, &ptr, 1, "[verification failed - ");
224 switch (sig->verify_ext_status)
226 case PDKIM_VERIFY_FAIL_BODY:
227 logmsg = string_append(logmsg, &size, &ptr, 1,
228 "body hash mismatch (body probably modified in transit)]");
231 case PDKIM_VERIFY_FAIL_MESSAGE:
232 logmsg = string_append(logmsg, &size, &ptr, 1,
233 "signature did not verify (headers probably modified in transit)]");
237 logmsg = string_append(logmsg, &size, &ptr, 1, "unspecified reason]");
241 case PDKIM_VERIFY_PASS:
243 string_append(logmsg, &size, &ptr, 1, "[verification succeeded]");
248 log_write(0, LOG_MAIN, "DKIM: %s", logmsg);
250 /* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */
252 dkim_signers = string_append(dkim_signers,
254 &dkim_signers_ptr, 2, sig->domain, ":");
257 dkim_signers = string_append(dkim_signers,
259 &dkim_signers_ptr, 2, sig->identity, ":");
261 /* Process next signature */
264 /* NULL-terminate and chop the last colon from the domain list */
268 dkim_signers[dkim_signers_ptr] = '\0';
269 if (Ustrlen(dkim_signers) > 0)
270 dkim_signers[Ustrlen(dkim_signers) - 1] = '\0';
274 store_pool = dkim_verify_oldpool;
279 dkim_exim_acl_setup(uschar * id)
281 pdkim_signature * sig;
285 dkim_cur_signer = id;
287 if (dkim_disable_verify || !id || !dkim_verify_ctx)
290 /* Find signature to run ACL on */
292 for (sig = dkim_signatures; sig; sig = sig->next)
293 if ( (cmp_val = Ustrchr(id, '@') != NULL ? US sig->identity : US sig->domain)
294 && strcmpic(cmp_val, id) == 0
299 /* The "dkim_domain" and "dkim_selector" expansion variables have
300 related globals, since they are used in the signing code too.
301 Instead of inventing separate names for verification, we set
302 them here. This is easy since a domain and selector is guaranteed
303 to be in a signature. The other dkim_* expansion items are
304 dynamically fetched from dkim_cur_sig at expansion time (see
307 dkim_signing_domain = US sig->domain;
308 dkim_signing_selector = US sig->selector;
309 dkim_key_length = sig->sigdata.len * 8;
316 dkim_exim_expand_defaults(int what)
320 case DKIM_ALGO: return US"";
321 case DKIM_BODYLENGTH: return US"9999999999999";
322 case DKIM_CANON_BODY: return US"";
323 case DKIM_CANON_HEADERS: return US"";
324 case DKIM_COPIEDHEADERS: return US"";
325 case DKIM_CREATED: return US"0";
326 case DKIM_EXPIRES: return US"9999999999999";
327 case DKIM_HEADERNAMES: return US"";
328 case DKIM_IDENTITY: return US"";
329 case DKIM_KEY_GRANULARITY: return US"*";
330 case DKIM_KEY_SRVTYPE: return US"*";
331 case DKIM_KEY_NOTES: return US"";
332 case DKIM_KEY_TESTING: return US"0";
333 case DKIM_NOSUBDOMAINS: return US"0";
334 case DKIM_VERIFY_STATUS: return US"none";
335 case DKIM_VERIFY_REASON: return US"";
336 default: return US"";
342 dkim_exim_expand_query(int what)
344 if (!dkim_verify_ctx || dkim_disable_verify || !dkim_cur_sig)
345 return dkim_exim_expand_defaults(what);
350 switch (dkim_cur_sig->algo)
352 case PDKIM_ALGO_RSA_SHA1: return US"rsa-sha1";
353 case PDKIM_ALGO_RSA_SHA256:
354 default: return US"rsa-sha256";
357 case DKIM_BODYLENGTH:
358 return dkim_cur_sig->bodylength >= 0
359 ? string_sprintf(OFF_T_FMT, (LONGLONG_T) dkim_cur_sig->bodylength)
360 : dkim_exim_expand_defaults(what);
362 case DKIM_CANON_BODY:
363 switch (dkim_cur_sig->canon_body)
365 case PDKIM_CANON_RELAXED: return US"relaxed";
366 case PDKIM_CANON_SIMPLE:
367 default: return US"simple";
370 case DKIM_CANON_HEADERS:
371 switch (dkim_cur_sig->canon_headers)
373 case PDKIM_CANON_RELAXED: return US"relaxed";
374 case PDKIM_CANON_SIMPLE:
375 default: return US"simple";
378 case DKIM_COPIEDHEADERS:
379 return dkim_cur_sig->copiedheaders
380 ? US dkim_cur_sig->copiedheaders : dkim_exim_expand_defaults(what);
383 return dkim_cur_sig->created > 0
384 ? string_sprintf("%llu", dkim_cur_sig->created)
385 : dkim_exim_expand_defaults(what);
388 return dkim_cur_sig->expires > 0
389 ? string_sprintf("%llu", dkim_cur_sig->expires)
390 : dkim_exim_expand_defaults(what);
392 case DKIM_HEADERNAMES:
393 return dkim_cur_sig->headernames
394 ? dkim_cur_sig->headernames : dkim_exim_expand_defaults(what);
397 return dkim_cur_sig->identity
398 ? US dkim_cur_sig->identity : dkim_exim_expand_defaults(what);
400 case DKIM_KEY_GRANULARITY:
401 return dkim_cur_sig->pubkey
402 ? dkim_cur_sig->pubkey->granularity
403 ? US dkim_cur_sig->pubkey->granularity
404 : dkim_exim_expand_defaults(what)
405 : dkim_exim_expand_defaults(what);
407 case DKIM_KEY_SRVTYPE:
408 return dkim_cur_sig->pubkey
409 ? dkim_cur_sig->pubkey->srvtype
410 ? US dkim_cur_sig->pubkey->srvtype
411 : dkim_exim_expand_defaults(what)
412 : dkim_exim_expand_defaults(what);
415 return dkim_cur_sig->pubkey
416 ? dkim_cur_sig->pubkey->notes
417 ? US dkim_cur_sig->pubkey->notes
418 : dkim_exim_expand_defaults(what)
419 : dkim_exim_expand_defaults(what);
421 case DKIM_KEY_TESTING:
422 return dkim_cur_sig->pubkey
423 ? dkim_cur_sig->pubkey->testing
425 : dkim_exim_expand_defaults(what)
426 : dkim_exim_expand_defaults(what);
428 case DKIM_NOSUBDOMAINS:
429 return dkim_cur_sig->pubkey
430 ? dkim_cur_sig->pubkey->no_subdomaining
432 : dkim_exim_expand_defaults(what)
433 : dkim_exim_expand_defaults(what);
435 case DKIM_VERIFY_STATUS:
436 switch (dkim_cur_sig->verify_status)
438 case PDKIM_VERIFY_INVALID: return US"invalid";
439 case PDKIM_VERIFY_FAIL: return US"fail";
440 case PDKIM_VERIFY_PASS: return US"pass";
441 case PDKIM_VERIFY_NONE:
442 default: return US"none";
445 case DKIM_VERIFY_REASON:
446 switch (dkim_cur_sig->verify_ext_status)
448 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
449 return US"pubkey_unavailable";
450 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:return US"pubkey_dns_syntax";
451 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT: return US"pubkey_der_syntax";
452 case PDKIM_VERIFY_FAIL_BODY: return US"bodyhash_mismatch";
453 case PDKIM_VERIFY_FAIL_MESSAGE: return US"signature_incorrect";
463 dkim_exim_sign(int dkim_fd, struct ob_dkim * dkim)
465 const uschar * dkim_domain;
467 uschar *seen_items = NULL;
468 int seen_items_size = 0;
469 int seen_items_offset = 0;
471 uschar *dkim_canon_expanded;
472 uschar *dkim_sign_headers_expanded;
473 uschar *dkim_private_key_expanded;
474 pdkim_ctx *ctx = NULL;
476 uschar *sigbuf = NULL;
479 pdkim_signature *signature;
485 int old_pool = store_pool;
487 store_pool = POOL_MAIN;
489 if (!(dkim_domain = expand_cstring(dkim->dkim_domain)))
491 /* expansion error, do not send message. */
492 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
493 "dkim_domain: %s", expand_string_message);
497 /* Set $dkim_domain expansion variable to each unique domain in list. */
499 while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep,
500 itembuf, sizeof(itembuf))))
502 if (!dkim_signing_domain || dkim_signing_domain[0] == '\0')
505 /* Only sign once for each domain, no matter how often it
506 appears in the expanded list. */
510 const uschar *seen_items_list = seen_items;
511 if (match_isinlist(dkim_signing_domain,
512 &seen_items_list, 0, NULL, NULL, MCL_STRING, TRUE,
517 string_append(seen_items, &seen_items_size, &seen_items_offset, 1, ":");
521 string_append(seen_items, &seen_items_size, &seen_items_offset, 1,
522 dkim_signing_domain);
523 seen_items[seen_items_offset] = '\0';
525 /* Set up $dkim_selector expansion variable. */
527 if (!(dkim_signing_selector = expand_string(dkim->dkim_selector)))
529 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
530 "dkim_selector: %s", expand_string_message);
534 /* Get canonicalization to use */
536 dkim_canon_expanded = dkim->dkim_canon
537 ? expand_string(dkim->dkim_canon) : US"relaxed";
538 if (!dkim_canon_expanded)
540 /* expansion error, do not send message. */
541 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
542 "dkim_canon: %s", expand_string_message);
546 if (Ustrcmp(dkim_canon_expanded, "relaxed") == 0)
547 pdkim_canon = PDKIM_CANON_RELAXED;
548 else if (Ustrcmp(dkim_canon_expanded, "simple") == 0)
549 pdkim_canon = PDKIM_CANON_SIMPLE;
552 log_write(0, LOG_MAIN,
553 "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",
554 dkim_canon_expanded);
555 pdkim_canon = PDKIM_CANON_RELAXED;
558 dkim_sign_headers_expanded = NULL;
559 if (dkim->dkim_sign_headers)
560 if (!(dkim_sign_headers_expanded = expand_string(dkim->dkim_sign_headers)))
562 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
563 "dkim_sign_headers: %s", expand_string_message);
566 /* else pass NULL, which means default header list */
568 /* Get private key to use. */
570 if (!(dkim_private_key_expanded = expand_string(dkim->dkim_private_key)))
572 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
573 "dkim_private_key: %s", expand_string_message);
577 if ( Ustrlen(dkim_private_key_expanded) == 0
578 || Ustrcmp(dkim_private_key_expanded, "0") == 0
579 || Ustrcmp(dkim_private_key_expanded, "false") == 0
581 continue; /* don't sign, but no error */
583 if (dkim_private_key_expanded[0] == '/')
587 /* Looks like a filename, load the private key. */
589 memset(big_buffer, 0, big_buffer_size);
591 if ((privkey_fd = open(CS dkim_private_key_expanded, O_RDONLY)) < 0)
593 log_write(0, LOG_MAIN | LOG_PANIC, "unable to open "
594 "private key file for reading: %s",
595 dkim_private_key_expanded);
599 if (read(privkey_fd, big_buffer, big_buffer_size - 2) < 0)
601 log_write(0, LOG_MAIN|LOG_PANIC, "unable to read private key file: %s",
602 dkim_private_key_expanded);
606 (void) close(privkey_fd);
607 dkim_private_key_expanded = big_buffer;
610 ctx = pdkim_init_sign(CS dkim_signing_domain,
611 CS dkim_signing_selector,
612 CS dkim_private_key_expanded,
613 PDKIM_ALGO_RSA_SHA256,
615 &dkim_exim_query_dns_txt
617 dkim_private_key_expanded[0] = '\0';
618 pdkim_set_optional(ctx,
619 CS dkim_sign_headers_expanded,
622 pdkim_canon, -1, 0, 0);
624 lseek(dkim_fd, 0, SEEK_SET);
626 while ((sread = read(dkim_fd, &buf, sizeof(buf))) > 0)
627 if ((pdkim_rc = pdkim_feed(ctx, buf, sread)) != PDKIM_OK)
630 /* Handle failed read above. */
633 debug_printf("DKIM: Error reading -K file.\n");
638 if ((pdkim_rc = pdkim_feed_finish(ctx, &signature)) != PDKIM_OK)
641 sigbuf = string_append(sigbuf, &sigsize, &sigptr, 2,
642 US signature->signature_header, US"\r\n");
650 sigbuf[sigptr] = '\0';
659 store_pool = old_pool;
664 log_write(0, LOG_MAIN|LOG_PANIC,
665 "DKIM: signing failed: %.100s", pdkim_errstr(pdkim_rc));