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)
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)
72 dkim_exim_verify_init(void)
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);
89 dkim_collect_input = !!dkim_verify_ctx;
91 store_pool = dkim_verify_oldpool;
96 dkim_exim_verify_feed(uschar * data, int len)
98 store_pool = POOL_PERM;
99 if ( dkim_collect_input
100 && pdkim_feed(dkim_verify_ctx, (char *)data, len) != PDKIM_OK)
101 dkim_collect_input = FALSE;
102 store_pool = dkim_verify_oldpool;
107 dkim_exim_verify_finish(void)
109 pdkim_signature *sig = NULL;
110 int dkim_signers_size = 0;
111 int dkim_signers_ptr = 0;
114 store_pool = POOL_PERM;
116 /* Delete eventual previous signature chain */
118 dkim_signatures = NULL;
120 /* If we have arrived here with dkim_collect_input == FALSE, it
121 means there was a processing error somewhere along the way.
122 Log the incident and disable futher verification. */
124 if (!dkim_collect_input)
126 log_write(0, LOG_MAIN,
127 "DKIM: Error while running this message through validation,"
128 " disabling signature verification.");
129 dkim_disable_verify = TRUE;
133 dkim_collect_input = FALSE;
135 /* Finish DKIM operation and fetch link to signatures chain */
137 if (pdkim_feed_finish(dkim_verify_ctx, &dkim_signatures) != PDKIM_OK)
140 for (sig = dkim_signatures; sig; sig = sig->next)
145 /* Log a line for each signature */
147 uschar *logmsg = string_append(NULL, &size, &ptr, 5,
148 string_sprintf("d=%s s=%s c=%s/%s a=%s b=%d ",
151 sig->canon_headers == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
152 sig->canon_body == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
153 sig->algo == PDKIM_ALGO_RSA_SHA256 ? "rsa-sha256" : "rsa-sha1",
157 sig->identity ? string_sprintf("i=%s ", sig->identity) : US"",
158 sig->created > 0 ? string_sprintf("t=%lu ", sig->created) : US"",
159 sig->expires > 0 ? string_sprintf("x=%lu ", sig->expires) : US"",
160 sig->bodylength > -1 ? string_sprintf("l=%lu ", sig->bodylength) : US""
163 switch (sig->verify_status)
165 case PDKIM_VERIFY_NONE:
166 logmsg = string_append(logmsg, &size, &ptr, 1, "[not verified]");
169 case PDKIM_VERIFY_INVALID:
170 logmsg = string_append(logmsg, &size, &ptr, 1, "[invalid - ");
171 switch (sig->verify_ext_status)
173 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
174 logmsg = string_append(logmsg, &size, &ptr, 1,
175 "public key record (currently?) unavailable]");
178 case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
179 logmsg = string_append(logmsg, &size, &ptr, 1,
180 "overlong public key record]");
183 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:
184 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT:
185 logmsg = string_append(logmsg, &size, &ptr, 1,
186 "syntax error in public key record]");
190 logmsg = string_append(logmsg, &size, &ptr, 1,
191 "unspecified problem]");
195 case PDKIM_VERIFY_FAIL:
197 string_append(logmsg, &size, &ptr, 1, "[verification failed - ");
198 switch (sig->verify_ext_status)
200 case PDKIM_VERIFY_FAIL_BODY:
201 logmsg = string_append(logmsg, &size, &ptr, 1,
202 "body hash mismatch (body probably modified in transit)]");
205 case PDKIM_VERIFY_FAIL_MESSAGE:
206 logmsg = string_append(logmsg, &size, &ptr, 1,
207 "signature did not verify (headers probably modified in transit)]");
211 logmsg = string_append(logmsg, &size, &ptr, 1, "unspecified reason]");
215 case PDKIM_VERIFY_PASS:
217 string_append(logmsg, &size, &ptr, 1, "[verification succeeded]");
222 log_write(0, LOG_MAIN, "DKIM: %s", logmsg);
224 /* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */
226 dkim_signers = string_append(dkim_signers,
228 &dkim_signers_ptr, 2, sig->domain, ":");
231 dkim_signers = string_append(dkim_signers,
233 &dkim_signers_ptr, 2, sig->identity, ":");
235 /* Process next signature */
238 /* NULL-terminate and chop the last colon from the domain list */
242 dkim_signers[dkim_signers_ptr] = '\0';
243 if (Ustrlen(dkim_signers) > 0)
244 dkim_signers[Ustrlen(dkim_signers) - 1] = '\0';
248 store_pool = dkim_verify_oldpool;
253 dkim_exim_acl_setup(uschar * id)
255 pdkim_signature * sig;
259 dkim_cur_signer = id;
261 if (dkim_disable_verify || !id || !dkim_verify_ctx)
264 /* Find signature to run ACL on */
266 for (sig = dkim_signatures; sig; sig = sig->next)
267 if ( (cmp_val = Ustrchr(id, '@') != NULL ? US sig->identity : US sig->domain)
268 && strcmpic(cmp_val, id) == 0
273 /* The "dkim_domain" and "dkim_selector" expansion variables have
274 related globals, since they are used in the signing code too.
275 Instead of inventing separate names for verification, we set
276 them here. This is easy since a domain and selector is guaranteed
277 to be in a signature. The other dkim_* expansion items are
278 dynamically fetched from dkim_cur_sig at expansion time (see
281 dkim_signing_domain = US sig->domain;
282 dkim_signing_selector = US sig->selector;
283 dkim_key_length = sig->sigdata.len * 8;
290 dkim_exim_expand_defaults(int what)
294 case DKIM_ALGO: return US"";
295 case DKIM_BODYLENGTH: return US"9999999999999";
296 case DKIM_CANON_BODY: return US"";
297 case DKIM_CANON_HEADERS: return US"";
298 case DKIM_COPIEDHEADERS: return US"";
299 case DKIM_CREATED: return US"0";
300 case DKIM_EXPIRES: return US"9999999999999";
301 case DKIM_HEADERNAMES: return US"";
302 case DKIM_IDENTITY: return US"";
303 case DKIM_KEY_GRANULARITY: return US"*";
304 case DKIM_KEY_SRVTYPE: return US"*";
305 case DKIM_KEY_NOTES: return US"";
306 case DKIM_KEY_TESTING: return US"0";
307 case DKIM_NOSUBDOMAINS: return US"0";
308 case DKIM_VERIFY_STATUS: return US"none";
309 case DKIM_VERIFY_REASON: return US"";
310 default: return US"";
316 dkim_exim_expand_query(int what)
318 if (!dkim_verify_ctx || dkim_disable_verify || !dkim_cur_sig)
319 return dkim_exim_expand_defaults(what);
324 switch (dkim_cur_sig->algo)
326 case PDKIM_ALGO_RSA_SHA1: return US"rsa-sha1";
327 case PDKIM_ALGO_RSA_SHA256:
328 default: return US"rsa-sha256";
331 case DKIM_BODYLENGTH:
332 return dkim_cur_sig->bodylength >= 0
333 ? string_sprintf(OFF_T_FMT, (LONGLONG_T) dkim_cur_sig->bodylength)
334 : dkim_exim_expand_defaults(what);
336 case DKIM_CANON_BODY:
337 switch (dkim_cur_sig->canon_body)
339 case PDKIM_CANON_RELAXED: return US"relaxed";
340 case PDKIM_CANON_SIMPLE:
341 default: return US"simple";
344 case DKIM_CANON_HEADERS:
345 switch (dkim_cur_sig->canon_headers)
347 case PDKIM_CANON_RELAXED: return US"relaxed";
348 case PDKIM_CANON_SIMPLE:
349 default: return US"simple";
352 case DKIM_COPIEDHEADERS:
353 return dkim_cur_sig->copiedheaders
354 ? US dkim_cur_sig->copiedheaders : dkim_exim_expand_defaults(what);
357 return dkim_cur_sig->created > 0
358 ? string_sprintf("%llu", dkim_cur_sig->created)
359 : dkim_exim_expand_defaults(what);
362 return dkim_cur_sig->expires > 0
363 ? string_sprintf("%llu", dkim_cur_sig->expires)
364 : dkim_exim_expand_defaults(what);
366 case DKIM_HEADERNAMES:
367 return dkim_cur_sig->headernames
368 ? dkim_cur_sig->headernames : dkim_exim_expand_defaults(what);
371 return dkim_cur_sig->identity
372 ? US dkim_cur_sig->identity : dkim_exim_expand_defaults(what);
374 case DKIM_KEY_GRANULARITY:
375 return dkim_cur_sig->pubkey
376 ? dkim_cur_sig->pubkey->granularity
377 ? US dkim_cur_sig->pubkey->granularity
378 : dkim_exim_expand_defaults(what)
379 : dkim_exim_expand_defaults(what);
381 case DKIM_KEY_SRVTYPE:
382 return dkim_cur_sig->pubkey
383 ? dkim_cur_sig->pubkey->srvtype
384 ? US dkim_cur_sig->pubkey->srvtype
385 : dkim_exim_expand_defaults(what)
386 : dkim_exim_expand_defaults(what);
389 return dkim_cur_sig->pubkey
390 ? dkim_cur_sig->pubkey->notes
391 ? US dkim_cur_sig->pubkey->notes
392 : dkim_exim_expand_defaults(what)
393 : dkim_exim_expand_defaults(what);
395 case DKIM_KEY_TESTING:
396 return dkim_cur_sig->pubkey
397 ? dkim_cur_sig->pubkey->testing
399 : dkim_exim_expand_defaults(what)
400 : dkim_exim_expand_defaults(what);
402 case DKIM_NOSUBDOMAINS:
403 return dkim_cur_sig->pubkey
404 ? dkim_cur_sig->pubkey->no_subdomaining
406 : dkim_exim_expand_defaults(what)
407 : dkim_exim_expand_defaults(what);
409 case DKIM_VERIFY_STATUS:
410 switch (dkim_cur_sig->verify_status)
412 case PDKIM_VERIFY_INVALID: return US"invalid";
413 case PDKIM_VERIFY_FAIL: return US"fail";
414 case PDKIM_VERIFY_PASS: return US"pass";
415 case PDKIM_VERIFY_NONE:
416 default: return US"none";
419 case DKIM_VERIFY_REASON:
420 switch (dkim_cur_sig->verify_ext_status)
422 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
423 return US"pubkey_unavailable";
424 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:return US"pubkey_dns_syntax";
425 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT: return US"pubkey_der_syntax";
426 case PDKIM_VERIFY_FAIL_BODY: return US"bodyhash_mismatch";
427 case PDKIM_VERIFY_FAIL_MESSAGE: return US"signature_incorrect";
437 dkim_exim_sign(int dkim_fd, uschar * dkim_private_key,
438 const uschar * dkim_domain, uschar * dkim_selector,
439 uschar * dkim_canon, uschar * dkim_sign_headers)
442 uschar *seen_items = NULL;
443 int seen_items_size = 0;
444 int seen_items_offset = 0;
446 uschar *dkim_canon_expanded;
447 uschar *dkim_sign_headers_expanded;
448 uschar *dkim_private_key_expanded;
449 pdkim_ctx *ctx = NULL;
451 uschar *sigbuf = NULL;
454 pdkim_signature *signature;
460 int old_pool = store_pool;
462 store_pool = POOL_MAIN;
464 if (!(dkim_domain = expand_cstring(dkim_domain)))
466 /* expansion error, do not send message. */
467 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
468 "dkim_domain: %s", expand_string_message);
473 /* Set $dkim_domain expansion variable to each unique domain in list. */
475 while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep,
476 itembuf, sizeof(itembuf))))
478 if (!dkim_signing_domain || dkim_signing_domain[0] == '\0')
481 /* Only sign once for each domain, no matter how often it
482 appears in the expanded list. */
486 const uschar *seen_items_list = seen_items;
487 if (match_isinlist(dkim_signing_domain,
488 &seen_items_list, 0, NULL, NULL, MCL_STRING, TRUE,
493 string_append(seen_items, &seen_items_size, &seen_items_offset, 1, ":");
497 string_append(seen_items, &seen_items_size, &seen_items_offset, 1,
498 dkim_signing_domain);
499 seen_items[seen_items_offset] = '\0';
501 /* Set up $dkim_selector expansion variable. */
503 if (!(dkim_signing_selector = expand_string(dkim_selector)))
505 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
506 "dkim_selector: %s", expand_string_message);
511 /* Get canonicalization to use */
513 dkim_canon_expanded = dkim_canon ? expand_string(dkim_canon) : US"relaxed";
514 if (!dkim_canon_expanded)
516 /* expansion error, do not send message. */
517 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
518 "dkim_canon: %s", expand_string_message);
523 if (Ustrcmp(dkim_canon_expanded, "relaxed") == 0)
524 pdkim_canon = PDKIM_CANON_RELAXED;
525 else if (Ustrcmp(dkim_canon_expanded, "simple") == 0)
526 pdkim_canon = PDKIM_CANON_SIMPLE;
529 log_write(0, LOG_MAIN,
530 "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",
531 dkim_canon_expanded);
532 pdkim_canon = PDKIM_CANON_RELAXED;
535 dkim_sign_headers_expanded = NULL;
536 if (dkim_sign_headers)
537 if (!(dkim_sign_headers_expanded = expand_string(dkim_sign_headers)))
539 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
540 "dkim_sign_headers: %s", expand_string_message);
544 /* else pass NULL, which means default header list */
546 /* Get private key to use. */
548 if (!(dkim_private_key_expanded = expand_string(dkim_private_key)))
550 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
551 "dkim_private_key: %s", expand_string_message);
556 if ( Ustrlen(dkim_private_key_expanded) == 0
557 || Ustrcmp(dkim_private_key_expanded, "0") == 0
558 || Ustrcmp(dkim_private_key_expanded, "false") == 0
560 continue; /* don't sign, but no error */
562 if (dkim_private_key_expanded[0] == '/')
566 /* Looks like a filename, load the private key. */
568 memset(big_buffer, 0, big_buffer_size);
569 privkey_fd = open(CS dkim_private_key_expanded, O_RDONLY);
572 log_write(0, LOG_MAIN | LOG_PANIC, "unable to open "
573 "private key file for reading: %s",
574 dkim_private_key_expanded);
579 if (read(privkey_fd, big_buffer, big_buffer_size - 2) < 0)
581 log_write(0, LOG_MAIN|LOG_PANIC, "unable to read private key file: %s",
582 dkim_private_key_expanded);
587 (void) close(privkey_fd);
588 dkim_private_key_expanded = big_buffer;
591 ctx = pdkim_init_sign( (char *) dkim_signing_domain,
592 (char *) dkim_signing_selector,
593 (char *) dkim_private_key_expanded,
594 PDKIM_ALGO_RSA_SHA256);
595 pdkim_set_optional(ctx,
596 (char *) dkim_sign_headers_expanded,
599 pdkim_canon, -1, 0, 0);
601 lseek(dkim_fd, 0, SEEK_SET);
603 while ((sread = read(dkim_fd, &buf, 4096)) > 0)
604 if (pdkim_feed(ctx, buf, sread) != PDKIM_OK)
610 /* Handle failed read above. */
613 debug_printf("DKIM: Error reading -K file.\n");
619 if ((pdkim_rc = pdkim_feed_finish(ctx, &signature)) != PDKIM_OK)
621 log_write(0, LOG_MAIN|LOG_PANIC, "DKIM: signing failed (RC %d)", pdkim_rc);
626 sigbuf = string_append(sigbuf, &sigsize, &sigptr, 2,
627 US signature->signature_header, US"\r\n");
635 sigbuf[sigptr] = '\0';
644 store_pool = old_pool;