41540b39d396d71a300394c27baa764ff33d491a
[exim.git] / src / src / dkim.c
1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
4
5 /* Copyright (c) University of Cambridge, 1995 - 2017 */
6 /* See the file NOTICE for conditions of use and distribution. */
7
8 /* Code for DKIM support. Other DKIM relevant code is in
9 receive.c, transport.c and transports/smtp.c */
10
11 #include "exim.h"
12
13 #ifndef DISABLE_DKIM
14
15 # include "pdkim/pdkim.h"
16
17 # ifdef MACRO_PREDEF
18 # include "macro_predef.h"
19
20 void
21 dkim_params(void)
22 {
23 builtin_macro_create_var(US"_DKIM_SIGN_HEADERS", US PDKIM_DEFAULT_SIGN_HEADERS);
24 }
25 # else /*!MACRO_PREDEF*/
26
27
28
29
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;
35
36
37
38 /*XXX the caller only uses the first record if we return multiple.
39 Could we hand back an allocated string?
40 */
41
42 static int
43 dkim_exim_query_dns_txt(char *name, char *answer)
44 {
45 dns_answer dnsa;
46 dns_scan dnss;
47 dns_record *rr;
48
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? */
52
53 /* Search for TXT record */
54
55 for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
56 rr;
57 rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
58 if (rr->type == T_TXT)
59 {
60 int rr_offset = 0;
61 int answer_offset = 0;
62
63 /* Copy record content to the answer buffer */
64
65 while (rr_offset < rr->size)
66 {
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));
71 rr_offset += len;
72 answer_offset += len;
73 if (answer_offset >= PDKIM_DNS_TXT_MAX_RECLEN)
74 return PDKIM_FAIL; /*XXX better error detail? logging? */
75 }
76 return PDKIM_OK;
77 }
78
79 return PDKIM_FAIL; /*XXX better error detail? logging? */
80 }
81
82
83 void
84 dkim_exim_init(void)
85 {
86 pdkim_init();
87 }
88
89
90
91 void
92 dkim_exim_verify_init(BOOL dot_stuffing)
93 {
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. */
97
98 dkim_verify_oldpool = store_pool;
99 store_pool = POOL_PERM;
100
101 /* Free previous context if there is one */
102
103 if (dkim_verify_ctx)
104 pdkim_free_ctx(dkim_verify_ctx);
105
106 /* Create new context */
107
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;
111
112 /* Start feed up with any cached data */
113 receive_get_cache();
114
115 store_pool = dkim_verify_oldpool;
116 }
117
118
119 void
120 dkim_exim_verify_feed(uschar * data, int len)
121 {
122 int rc;
123
124 store_pool = POOL_PERM;
125 if ( dkim_collect_input
126 && (rc = pdkim_feed(dkim_verify_ctx, data, len)) != PDKIM_OK)
127 {
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;
132 }
133 store_pool = dkim_verify_oldpool;
134 }
135
136
137 void
138 dkim_exim_verify_finish(void)
139 {
140 pdkim_signature * sig = NULL;
141 int rc;
142 gstring * g = NULL;
143 const uschar * errstr;
144
145 store_pool = POOL_PERM;
146
147 /* Delete eventual previous signature chain */
148
149 dkim_signers = NULL;
150 dkim_signatures = NULL;
151
152 if (dkim_collect_error)
153 {
154 log_write(0, LOG_MAIN,
155 "DKIM: Error during validation, disabling signature verification: %.100s",
156 dkim_collect_error);
157 dkim_disable_verify = TRUE;
158 goto out;
159 }
160
161 dkim_collect_input = FALSE;
162
163 /* Finish DKIM operation and fetch link to signatures chain */
164
165 rc = pdkim_feed_finish(dkim_verify_ctx, &dkim_signatures, &errstr);
166 if (rc != PDKIM_OK)
167 {
168 log_write(0, LOG_MAIN, "DKIM: validation error: %.100s%s%s", pdkim_errstr(rc),
169 errstr ? ": " : "", errstr ? errstr : US"");
170 goto out;
171 }
172
173 for (sig = dkim_signatures; sig; sig = sig->next)
174 {
175 uschar * s;
176 gstring * logmsg;
177
178 /* Log a line for each signature */
179
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));
197
198 switch (sig->verify_status)
199 {
200 case PDKIM_VERIFY_NONE:
201 logmsg = string_cat(logmsg, " [not verified]");
202 break;
203
204 case PDKIM_VERIFY_INVALID:
205 logmsg = string_cat(logmsg, " [invalid - ");
206 switch (sig->verify_ext_status)
207 {
208 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
209 logmsg = string_cat(logmsg,
210 "public key record (currently?) unavailable]");
211 break;
212
213 case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
214 logmsg = string_cat(logmsg, "overlong public key record]");
215 break;
216
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]");
220 break;
221
222 case PDKIM_VERIFY_INVALID_SIGNATURE_ERROR:
223 logmsg = string_cat(logmsg, "signature tag missing or invalid]");
224 break;
225
226 case PDKIM_VERIFY_INVALID_DKIM_VERSION:
227 logmsg = string_cat(logmsg, "unsupported DKIM version]");
228 break;
229
230 default:
231 logmsg = string_cat(logmsg, "unspecified problem]");
232 }
233 break;
234
235 case PDKIM_VERIFY_FAIL:
236 logmsg =
237 string_cat(logmsg, " [verification failed - ");
238 switch (sig->verify_ext_status)
239 {
240 case PDKIM_VERIFY_FAIL_BODY:
241 logmsg = string_cat(logmsg,
242 "body hash mismatch (body probably modified in transit)]");
243 break;
244
245 case PDKIM_VERIFY_FAIL_MESSAGE:
246 logmsg = string_cat(logmsg,
247 "signature did not verify (headers probably modified in transit)]");
248 break;
249
250 default:
251 logmsg = string_cat(logmsg, "unspecified reason]");
252 }
253 break;
254
255 case PDKIM_VERIFY_PASS:
256 logmsg = string_cat(logmsg, " [verification succeeded]");
257 break;
258 }
259
260 log_write(0, LOG_MAIN, "DKIM: %s", string_from_gstring(logmsg));
261
262 /* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */
263
264 if (sig->domain)
265 g = string_append_listele(g, ':', sig->domain);
266
267 if (sig->identity)
268 g = string_append_listele(g, ':', sig->identity);
269
270 /* Process next signature */
271 }
272
273 if (g) dkim_signers = g->s;
274
275 out:
276 store_pool = dkim_verify_oldpool;
277 }
278
279
280 void
281 dkim_exim_acl_setup(uschar * id)
282 {
283 pdkim_signature * sig;
284 uschar * cmp_val;
285
286 dkim_cur_sig = NULL;
287 dkim_cur_signer = id;
288
289 if (dkim_disable_verify || !id || !dkim_verify_ctx)
290 return;
291
292 /* Find signature to run ACL on */
293
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
297 )
298 {
299 dkim_cur_sig = sig;
300
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
307 function below). */
308
309 dkim_signing_domain = US sig->domain;
310 dkim_signing_selector = US sig->selector;
311 dkim_key_length = sig->sighash.len * 8;
312 return;
313 }
314 }
315
316
317 static uschar *
318 dkim_exim_expand_defaults(int what)
319 {
320 switch (what)
321 {
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"";
339 }
340 }
341
342
343 uschar *
344 dkim_exim_expand_query(int what)
345 {
346 if (!dkim_verify_ctx || dkim_disable_verify || !dkim_cur_sig)
347 return dkim_exim_expand_defaults(what);
348
349 switch (what)
350 {
351 case DKIM_ALGO:
352 return dkim_sig_to_a_tag(dkim_cur_sig);
353
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);
358
359 case DKIM_CANON_BODY:
360 switch (dkim_cur_sig->canon_body)
361 {
362 case PDKIM_CANON_RELAXED: return US"relaxed";
363 case PDKIM_CANON_SIMPLE:
364 default: return US"simple";
365 }
366
367 case DKIM_CANON_HEADERS:
368 switch (dkim_cur_sig->canon_headers)
369 {
370 case PDKIM_CANON_RELAXED: return US"relaxed";
371 case PDKIM_CANON_SIMPLE:
372 default: return US"simple";
373 }
374
375 case DKIM_COPIEDHEADERS:
376 return dkim_cur_sig->copiedheaders
377 ? US dkim_cur_sig->copiedheaders : dkim_exim_expand_defaults(what);
378
379 case DKIM_CREATED:
380 return dkim_cur_sig->created > 0
381 ? string_sprintf("%lu", dkim_cur_sig->created)
382 : dkim_exim_expand_defaults(what);
383
384 case DKIM_EXPIRES:
385 return dkim_cur_sig->expires > 0
386 ? string_sprintf("%lu", dkim_cur_sig->expires)
387 : dkim_exim_expand_defaults(what);
388
389 case DKIM_HEADERNAMES:
390 return dkim_cur_sig->headernames
391 ? dkim_cur_sig->headernames : dkim_exim_expand_defaults(what);
392
393 case DKIM_IDENTITY:
394 return dkim_cur_sig->identity
395 ? US dkim_cur_sig->identity : dkim_exim_expand_defaults(what);
396
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);
403
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);
410
411 case DKIM_KEY_NOTES:
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);
417
418 case DKIM_KEY_TESTING:
419 return dkim_cur_sig->pubkey
420 ? dkim_cur_sig->pubkey->testing
421 ? US"1"
422 : dkim_exim_expand_defaults(what)
423 : dkim_exim_expand_defaults(what);
424
425 case DKIM_NOSUBDOMAINS:
426 return dkim_cur_sig->pubkey
427 ? dkim_cur_sig->pubkey->no_subdomaining
428 ? US"1"
429 : dkim_exim_expand_defaults(what)
430 : dkim_exim_expand_defaults(what);
431
432 case DKIM_VERIFY_STATUS:
433 switch (dkim_cur_sig->verify_status)
434 {
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";
440 }
441
442 case DKIM_VERIFY_REASON:
443 switch (dkim_cur_sig->verify_ext_status)
444 {
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";
451 }
452
453 default:
454 return US"";
455 }
456 }
457
458
459 /* Generate signatures for the given file, returning a string.
460 If a prefix is given, prepend it to the file for the calculations.
461 */
462
463 gstring *
464 dkim_exim_sign(int fd, off_t off, uschar * prefix,
465 struct ob_dkim * dkim, const uschar ** errstr)
466 {
467 const uschar * dkim_domain;
468 int sep = 0;
469 gstring * seen_doms = NULL;
470 pdkim_ctx ctx;
471 pdkim_signature * sig;
472 gstring * sigbuf;
473 int pdkim_rc;
474 int sread;
475 uschar buf[4096];
476 int save_errno = 0;
477 int old_pool = store_pool;
478 uschar * errwhen;
479
480 store_pool = POOL_MAIN;
481
482 pdkim_init_context(&ctx, dkim->dot_stuffed, &dkim_exim_query_dns_txt);
483
484 if (!(dkim_domain = expand_cstring(dkim->dkim_domain)))
485 /* expansion error, do not send message. */
486 { errwhen = US"dkim_domain"; goto expand_bad; }
487
488 /* Set $dkim_domain expansion variable to each unique domain in list. */
489
490 while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep, NULL, 0)))
491 {
492 const uschar * dkim_sel;
493 int sel_sep = 0;
494
495 if (dkim_signing_domain[0] == '\0')
496 continue;
497
498 /* Only sign once for each domain, no matter how often it
499 appears in the expanded list. */
500
501 if (match_isinlist(dkim_signing_domain, CUSS &seen_doms,
502 0, NULL, NULL, MCL_STRING, TRUE, NULL) == OK)
503 continue;
504
505 seen_doms = string_append_listele(seen_doms, ':', dkim_signing_domain);
506
507 /* Set $dkim_selector expansion variable to each selector in list,
508 for this domain. */
509
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; }
513
514 while ((dkim_signing_selector = string_nextinlist(&dkim_sel, &sel_sep,
515 NULL, 0)))
516 {
517 uschar * dkim_canon_expanded;
518 int pdkim_canon;
519 uschar * dkim_sign_headers_expanded = NULL;
520 uschar * dkim_private_key_expanded;
521 uschar * dkim_hash_expanded;
522 uschar * dkim_identity_expanded = NULL;
523
524 /* Get canonicalization to use */
525
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; }
530
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;
535 else
536 {
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;
541 }
542
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 */
547
548 /* Get private key to use. */
549
550 if (!(dkim_private_key_expanded = expand_string(dkim->dkim_private_key)))
551 { errwhen = US"dkim_private_key"; goto expand_bad; }
552
553 if ( Ustrlen(dkim_private_key_expanded) == 0
554 || Ustrcmp(dkim_private_key_expanded, "0") == 0
555 || Ustrcmp(dkim_private_key_expanded, "false") == 0
556 )
557 continue; /* don't sign, but no error */
558
559 if (dkim_private_key_expanded[0] == '/')
560 {
561 int privkey_fd, off = 0, len;
562
563 /* Looks like a filename, load the private key. */
564
565 memset(big_buffer, 0, big_buffer_size);
566
567 if ((privkey_fd = open(CS dkim_private_key_expanded, O_RDONLY)) < 0)
568 {
569 log_write(0, LOG_MAIN | LOG_PANIC, "unable to open "
570 "private key file for reading: %s",
571 dkim_private_key_expanded);
572 goto bad;
573 }
574
575 do
576 {
577 if ((len = read(privkey_fd, big_buffer + off, big_buffer_size - 2 - off)) < 0)
578 {
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);
582 goto bad;
583 }
584 off += len;
585 }
586 while (len > 0);
587
588 (void) close(privkey_fd);
589 big_buffer[off] = '\0';
590 dkim_private_key_expanded = big_buffer;
591 }
592
593 if (!(dkim_hash_expanded = expand_string(dkim->dkim_hash)))
594 { errwhen = US"dkim_hash"; goto expand_bad; }
595
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;
601
602 /*XXX so we currently nail signing to RSA + this hash.
603 Need to extract algo from privkey and check for disallowed combos. */
604
605 if (!(sig = pdkim_init_sign(&ctx, dkim_signing_domain,
606 dkim_signing_selector,
607 dkim_private_key_expanded,
608 dkim_hash_expanded,
609 errstr
610 )))
611 goto bad;
612 dkim_private_key_expanded[0] = '\0';
613
614 pdkim_set_optional(sig,
615 CS dkim_sign_headers_expanded,
616 dkim_identity_expanded,
617 pdkim_canon,
618 pdkim_canon, -1, 0, 0);
619
620 if (!ctx.sig) /* link sig to context chain */
621 ctx.sig = sig;
622 else
623 {
624 pdkim_signature * n = ctx.sig;
625 while (n->next) n = n->next;
626 n->next = sig;
627 }
628 }
629 }
630
631 if (prefix)
632 pdkim_feed(&ctx, prefix, Ustrlen(prefix));
633
634 if (lseek(fd, off, SEEK_SET) < 0)
635 sread = -1;
636 else
637 while ((sread = read(fd, &buf, sizeof(buf))) > 0)
638 if ((pdkim_rc = pdkim_feed(&ctx, buf, sread)) != PDKIM_OK)
639 goto pk_bad;
640
641 /* Handle failed read above. */
642 if (sread == -1)
643 {
644 debug_printf("DKIM: Error reading -K file.\n");
645 save_errno = errno;
646 goto bad;
647 }
648
649 /* Build string of headers, one per signature */
650
651 if ((pdkim_rc = pdkim_feed_finish(&ctx, &sig, errstr)) != PDKIM_OK)
652 goto pk_bad;
653
654 for (sigbuf = NULL; sig; sig = sig->next)
655 sigbuf = string_append(sigbuf, 2, US sig->signature_header, US"\r\n");
656
657 (void) string_from_gstring(sigbuf);
658
659 CLEANUP:
660 store_pool = old_pool;
661 errno = save_errno;
662 return sigbuf;
663
664 pk_bad:
665 log_write(0, LOG_MAIN|LOG_PANIC,
666 "DKIM: signing failed: %.100s", pdkim_errstr(pdkim_rc));
667 bad:
668 sigbuf = NULL;
669 goto CLEANUP;
670
671 expand_bad:
672 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand %s: %s",
673 errwhen, expand_string_message);
674 goto bad;
675 }
676
677 # endif /*!MACRO_PREDEF*/
678 #endif /*!DISABLE_DKIM*/