038adb8b9b3c885529f14c4c70346d2778d39677
[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 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 const uschar * dkim_collect_error = NULL;
22
23
24
25 /*XXX the caller only uses the first record if we return multiple.
26 Could we hand back an allocated string?
27 */
28
29 static int
30 dkim_exim_query_dns_txt(char *name, char *answer)
31 {
32 dns_answer dnsa;
33 dns_scan dnss;
34 dns_record *rr;
35
36 lookup_dnssec_authenticated = NULL;
37 if (dns_lookup(&dnsa, US name, T_TXT, NULL) != DNS_SUCCEED)
38 return PDKIM_FAIL; /*XXX better error detail? logging? */
39
40 /* Search for TXT record */
41
42 for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
43 rr;
44 rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
45 if (rr->type == T_TXT)
46 {
47 int rr_offset = 0;
48 int answer_offset = 0;
49
50 /* Copy record content to the answer buffer */
51
52 while (rr_offset < rr->size)
53 {
54 uschar len = rr->data[rr_offset++];
55 snprintf(answer + answer_offset,
56 PDKIM_DNS_TXT_MAX_RECLEN - answer_offset,
57 "%.*s", (int)len, CS (rr->data + rr_offset));
58 rr_offset += len;
59 answer_offset += len;
60 if (answer_offset >= PDKIM_DNS_TXT_MAX_RECLEN)
61 return PDKIM_FAIL; /*XXX better error detail? logging? */
62 }
63 return PDKIM_OK;
64 }
65
66 return PDKIM_FAIL; /*XXX better error detail? logging? */
67 }
68
69
70 void
71 dkim_exim_init(void)
72 {
73 pdkim_init();
74 }
75
76
77
78 void
79 dkim_exim_verify_init(BOOL dot_stuffing)
80 {
81 /* There is a store-reset between header & body reception
82 so cannot use the main pool. Any allocs done by Exim
83 memory-handling must use the perm pool. */
84
85 dkim_verify_oldpool = store_pool;
86 store_pool = POOL_PERM;
87
88 /* Free previous context if there is one */
89
90 if (dkim_verify_ctx)
91 pdkim_free_ctx(dkim_verify_ctx);
92
93 /* Create new context */
94
95 dkim_verify_ctx = pdkim_init_verify(&dkim_exim_query_dns_txt, dot_stuffing);
96 dkim_collect_input = !!dkim_verify_ctx;
97 dkim_collect_error = NULL;
98
99 /* Start feed up with any cached data */
100 receive_get_cache();
101
102 store_pool = dkim_verify_oldpool;
103 }
104
105
106 void
107 dkim_exim_verify_feed(uschar * data, int len)
108 {
109 int rc;
110
111 store_pool = POOL_PERM;
112 if ( dkim_collect_input
113 && (rc = pdkim_feed(dkim_verify_ctx, data, len)) != PDKIM_OK)
114 {
115 dkim_collect_error = pdkim_errstr(rc);
116 log_write(0, LOG_MAIN,
117 "DKIM: validation error: %.100s", dkim_collect_error);
118 dkim_collect_input = FALSE;
119 }
120 store_pool = dkim_verify_oldpool;
121 }
122
123
124 void
125 dkim_exim_verify_finish(void)
126 {
127 pdkim_signature * sig = NULL;
128 int rc;
129 gstring * g = NULL;
130 const uschar * errstr;
131
132 store_pool = POOL_PERM;
133
134 /* Delete eventual previous signature chain */
135
136 dkim_signers = NULL;
137 dkim_signatures = NULL;
138
139 if (dkim_collect_error)
140 {
141 log_write(0, LOG_MAIN,
142 "DKIM: Error during validation, disabling signature verification: %.100s",
143 dkim_collect_error);
144 dkim_disable_verify = TRUE;
145 goto out;
146 }
147
148 dkim_collect_input = FALSE;
149
150 /* Finish DKIM operation and fetch link to signatures chain */
151
152 rc = pdkim_feed_finish(dkim_verify_ctx, &dkim_signatures, &errstr);
153 if (rc != PDKIM_OK)
154 {
155 log_write(0, LOG_MAIN, "DKIM: validation error: %.100s%s%s", pdkim_errstr(rc),
156 errstr ? ": " : "", errstr ? errstr : US"");
157 goto out;
158 }
159
160 for (sig = dkim_signatures; sig; sig = sig->next)
161 {
162 uschar * s;
163 gstring * logmsg;
164
165 /* Log a line for each signature */
166
167 if (!(s = sig->domain)) s = US"<UNSET>";
168 logmsg = string_append(NULL, 2, "d=", s);
169 if (!(s = sig->selector)) s = US"<UNSET>";
170 logmsg = string_append(logmsg, 2, " s=", s);
171 logmsg = string_append(logmsg, 7,
172 " c=", sig->canon_headers == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
173 "/", sig->canon_body == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
174 " a=", dkim_sig_to_a_tag(sig),
175 string_sprintf(" b=" SIZE_T_FMT,
176 (int)sig->sighash.len > -1 ? sig->sighash.len * 8 : 0));
177 if ((s= sig->identity)) logmsg = string_append(logmsg, 2, " i=", s);
178 if (sig->created > 0) logmsg = string_cat(logmsg,
179 string_sprintf(" t=%lu", sig->created));
180 if (sig->expires > 0) logmsg = string_cat(logmsg,
181 string_sprintf(" x=%lu", sig->expires));
182 if (sig->bodylength > -1) logmsg = string_cat(logmsg,
183 string_sprintf(" l=%lu", sig->bodylength));
184
185 switch (sig->verify_status)
186 {
187 case PDKIM_VERIFY_NONE:
188 logmsg = string_cat(logmsg, " [not verified]");
189 break;
190
191 case PDKIM_VERIFY_INVALID:
192 logmsg = string_cat(logmsg, " [invalid - ");
193 switch (sig->verify_ext_status)
194 {
195 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
196 logmsg = string_cat(logmsg,
197 "public key record (currently?) unavailable]");
198 break;
199
200 case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
201 logmsg = string_cat(logmsg, "overlong public key record]");
202 break;
203
204 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:
205 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT:
206 logmsg = string_cat(logmsg, "syntax error in public key record]");
207 break;
208
209 case PDKIM_VERIFY_INVALID_SIGNATURE_ERROR:
210 logmsg = string_cat(logmsg, "signature tag missing or invalid]");
211 break;
212
213 case PDKIM_VERIFY_INVALID_DKIM_VERSION:
214 logmsg = string_cat(logmsg, "unsupported DKIM version]");
215 break;
216
217 default:
218 logmsg = string_cat(logmsg, "unspecified problem]");
219 }
220 break;
221
222 case PDKIM_VERIFY_FAIL:
223 logmsg =
224 string_cat(logmsg, " [verification failed - ");
225 switch (sig->verify_ext_status)
226 {
227 case PDKIM_VERIFY_FAIL_BODY:
228 logmsg = string_cat(logmsg,
229 "body hash mismatch (body probably modified in transit)]");
230 break;
231
232 case PDKIM_VERIFY_FAIL_MESSAGE:
233 logmsg = string_cat(logmsg,
234 "signature did not verify (headers probably modified in transit)]");
235 break;
236
237 default:
238 logmsg = string_cat(logmsg, "unspecified reason]");
239 }
240 break;
241
242 case PDKIM_VERIFY_PASS:
243 logmsg = string_cat(logmsg, " [verification succeeded]");
244 break;
245 }
246
247 log_write(0, LOG_MAIN, "DKIM: %s", string_from_gstring(logmsg));
248
249 /* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */
250
251 if (sig->domain)
252 g = string_append_listele(g, ':', sig->domain);
253
254 if (sig->identity)
255 g = string_append_listele(g, ':', sig->identity);
256
257 /* Process next signature */
258 }
259
260 if (g) dkim_signers = g->s;
261
262 out:
263 store_pool = dkim_verify_oldpool;
264 }
265
266
267 void
268 dkim_exim_acl_setup(uschar * id)
269 {
270 pdkim_signature * sig;
271 uschar * cmp_val;
272
273 dkim_cur_sig = NULL;
274 dkim_cur_signer = id;
275
276 if (dkim_disable_verify || !id || !dkim_verify_ctx)
277 return;
278
279 /* Find signature to run ACL on */
280
281 for (sig = dkim_signatures; sig; sig = sig->next)
282 if ( (cmp_val = Ustrchr(id, '@') != NULL ? US sig->identity : US sig->domain)
283 && strcmpic(cmp_val, id) == 0
284 )
285 {
286 dkim_cur_sig = sig;
287
288 /* The "dkim_domain" and "dkim_selector" expansion variables have
289 related globals, since they are used in the signing code too.
290 Instead of inventing separate names for verification, we set
291 them here. This is easy since a domain and selector is guaranteed
292 to be in a signature. The other dkim_* expansion items are
293 dynamically fetched from dkim_cur_sig at expansion time (see
294 function below). */
295
296 dkim_signing_domain = US sig->domain;
297 dkim_signing_selector = US sig->selector;
298 dkim_key_length = sig->sighash.len * 8;
299 return;
300 }
301 }
302
303
304 static uschar *
305 dkim_exim_expand_defaults(int what)
306 {
307 switch (what)
308 {
309 case DKIM_ALGO: return US"";
310 case DKIM_BODYLENGTH: return US"9999999999999";
311 case DKIM_CANON_BODY: return US"";
312 case DKIM_CANON_HEADERS: return US"";
313 case DKIM_COPIEDHEADERS: return US"";
314 case DKIM_CREATED: return US"0";
315 case DKIM_EXPIRES: return US"9999999999999";
316 case DKIM_HEADERNAMES: return US"";
317 case DKIM_IDENTITY: return US"";
318 case DKIM_KEY_GRANULARITY: return US"*";
319 case DKIM_KEY_SRVTYPE: return US"*";
320 case DKIM_KEY_NOTES: return US"";
321 case DKIM_KEY_TESTING: return US"0";
322 case DKIM_NOSUBDOMAINS: return US"0";
323 case DKIM_VERIFY_STATUS: return US"none";
324 case DKIM_VERIFY_REASON: return US"";
325 default: return US"";
326 }
327 }
328
329
330 uschar *
331 dkim_exim_expand_query(int what)
332 {
333 if (!dkim_verify_ctx || dkim_disable_verify || !dkim_cur_sig)
334 return dkim_exim_expand_defaults(what);
335
336 switch (what)
337 {
338 case DKIM_ALGO:
339 return dkim_sig_to_a_tag(dkim_cur_sig);
340
341 case DKIM_BODYLENGTH:
342 return dkim_cur_sig->bodylength >= 0
343 ? string_sprintf("%ld", dkim_cur_sig->bodylength)
344 : dkim_exim_expand_defaults(what);
345
346 case DKIM_CANON_BODY:
347 switch (dkim_cur_sig->canon_body)
348 {
349 case PDKIM_CANON_RELAXED: return US"relaxed";
350 case PDKIM_CANON_SIMPLE:
351 default: return US"simple";
352 }
353
354 case DKIM_CANON_HEADERS:
355 switch (dkim_cur_sig->canon_headers)
356 {
357 case PDKIM_CANON_RELAXED: return US"relaxed";
358 case PDKIM_CANON_SIMPLE:
359 default: return US"simple";
360 }
361
362 case DKIM_COPIEDHEADERS:
363 return dkim_cur_sig->copiedheaders
364 ? US dkim_cur_sig->copiedheaders : dkim_exim_expand_defaults(what);
365
366 case DKIM_CREATED:
367 return dkim_cur_sig->created > 0
368 ? string_sprintf("%lu", dkim_cur_sig->created)
369 : dkim_exim_expand_defaults(what);
370
371 case DKIM_EXPIRES:
372 return dkim_cur_sig->expires > 0
373 ? string_sprintf("%lu", dkim_cur_sig->expires)
374 : dkim_exim_expand_defaults(what);
375
376 case DKIM_HEADERNAMES:
377 return dkim_cur_sig->headernames
378 ? dkim_cur_sig->headernames : dkim_exim_expand_defaults(what);
379
380 case DKIM_IDENTITY:
381 return dkim_cur_sig->identity
382 ? US dkim_cur_sig->identity : dkim_exim_expand_defaults(what);
383
384 case DKIM_KEY_GRANULARITY:
385 return dkim_cur_sig->pubkey
386 ? dkim_cur_sig->pubkey->granularity
387 ? US dkim_cur_sig->pubkey->granularity
388 : dkim_exim_expand_defaults(what)
389 : dkim_exim_expand_defaults(what);
390
391 case DKIM_KEY_SRVTYPE:
392 return dkim_cur_sig->pubkey
393 ? dkim_cur_sig->pubkey->srvtype
394 ? US dkim_cur_sig->pubkey->srvtype
395 : dkim_exim_expand_defaults(what)
396 : dkim_exim_expand_defaults(what);
397
398 case DKIM_KEY_NOTES:
399 return dkim_cur_sig->pubkey
400 ? dkim_cur_sig->pubkey->notes
401 ? US dkim_cur_sig->pubkey->notes
402 : dkim_exim_expand_defaults(what)
403 : dkim_exim_expand_defaults(what);
404
405 case DKIM_KEY_TESTING:
406 return dkim_cur_sig->pubkey
407 ? dkim_cur_sig->pubkey->testing
408 ? US"1"
409 : dkim_exim_expand_defaults(what)
410 : dkim_exim_expand_defaults(what);
411
412 case DKIM_NOSUBDOMAINS:
413 return dkim_cur_sig->pubkey
414 ? dkim_cur_sig->pubkey->no_subdomaining
415 ? US"1"
416 : dkim_exim_expand_defaults(what)
417 : dkim_exim_expand_defaults(what);
418
419 case DKIM_VERIFY_STATUS:
420 switch (dkim_cur_sig->verify_status)
421 {
422 case PDKIM_VERIFY_INVALID: return US"invalid";
423 case PDKIM_VERIFY_FAIL: return US"fail";
424 case PDKIM_VERIFY_PASS: return US"pass";
425 case PDKIM_VERIFY_NONE:
426 default: return US"none";
427 }
428
429 case DKIM_VERIFY_REASON:
430 switch (dkim_cur_sig->verify_ext_status)
431 {
432 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
433 return US"pubkey_unavailable";
434 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:return US"pubkey_dns_syntax";
435 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT: return US"pubkey_der_syntax";
436 case PDKIM_VERIFY_FAIL_BODY: return US"bodyhash_mismatch";
437 case PDKIM_VERIFY_FAIL_MESSAGE: return US"signature_incorrect";
438 }
439
440 default:
441 return US"";
442 }
443 }
444
445
446 /* Generate signatures for the given file, returning a string.
447 If a prefix is given, prepend it to the file for the calculations.
448 */
449
450 gstring *
451 dkim_exim_sign(int fd, off_t off, uschar * prefix,
452 struct ob_dkim * dkim, const uschar ** errstr)
453 {
454 const uschar * dkim_domain;
455 int sep = 0;
456 gstring * seen_doms = NULL;
457 pdkim_ctx ctx;
458 pdkim_signature * sig;
459 gstring * sigbuf;
460 int pdkim_rc;
461 int sread;
462 uschar buf[4096];
463 int save_errno = 0;
464 int old_pool = store_pool;
465 uschar * errwhen;
466
467 store_pool = POOL_MAIN;
468
469 pdkim_init_context(&ctx, dkim->dot_stuffed, &dkim_exim_query_dns_txt);
470
471 if (!(dkim_domain = expand_cstring(dkim->dkim_domain)))
472 /* expansion error, do not send message. */
473 { errwhen = US"dkim_domain"; goto expand_bad; }
474
475 /* Set $dkim_domain expansion variable to each unique domain in list. */
476
477 while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep, NULL, 0)))
478 {
479 const uschar * dkim_sel;
480 int sel_sep = 0;
481
482 if (dkim_signing_domain[0] == '\0')
483 continue;
484
485 /* Only sign once for each domain, no matter how often it
486 appears in the expanded list. */
487
488 if (match_isinlist(dkim_signing_domain, CUSS &seen_doms,
489 0, NULL, NULL, MCL_STRING, TRUE, NULL) == OK)
490 continue;
491
492 seen_doms = string_append_listele(seen_doms, ':', dkim_signing_domain);
493
494 /* Set $dkim_selector expansion variable to each selector in list,
495 for this domain. */
496
497 if (!(dkim_sel = expand_string(dkim->dkim_selector)))
498 if (!(dkim_signing_selector = expand_string(dkim->dkim_selector)))
499 { errwhen = US"dkim_selector"; goto expand_bad; }
500
501 while ((dkim_signing_selector = string_nextinlist(&dkim_sel, &sel_sep,
502 NULL, 0)))
503 {
504 uschar * dkim_canon_expanded;
505 int pdkim_canon;
506 uschar * dkim_sign_headers_expanded = NULL;
507 uschar * dkim_private_key_expanded;
508 uschar * dkim_hash_expanded;
509 uschar * dkim_identity_expanded = NULL;
510
511 /* Get canonicalization to use */
512
513 dkim_canon_expanded = dkim->dkim_canon
514 ? expand_string(dkim->dkim_canon) : US"relaxed";
515 if (!dkim_canon_expanded) /* expansion error, do not send message. */
516 { errwhen = US"dkim_canon"; goto expand_bad; }
517
518 if (Ustrcmp(dkim_canon_expanded, "relaxed") == 0)
519 pdkim_canon = PDKIM_CANON_RELAXED;
520 else if (Ustrcmp(dkim_canon_expanded, "simple") == 0)
521 pdkim_canon = PDKIM_CANON_SIMPLE;
522 else
523 {
524 log_write(0, LOG_MAIN,
525 "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",
526 dkim_canon_expanded);
527 pdkim_canon = PDKIM_CANON_RELAXED;
528 }
529
530 if ( dkim->dkim_sign_headers
531 && !(dkim_sign_headers_expanded = expand_string(dkim->dkim_sign_headers)))
532 { errwhen = US"dkim_sign_header"; goto expand_bad; }
533 /* else pass NULL, which means default header list */
534
535 /* Get private key to use. */
536
537 if (!(dkim_private_key_expanded = expand_string(dkim->dkim_private_key)))
538 { errwhen = US"dkim_private_key"; goto expand_bad; }
539
540 if ( Ustrlen(dkim_private_key_expanded) == 0
541 || Ustrcmp(dkim_private_key_expanded, "0") == 0
542 || Ustrcmp(dkim_private_key_expanded, "false") == 0
543 )
544 continue; /* don't sign, but no error */
545
546 if (dkim_private_key_expanded[0] == '/')
547 {
548 int privkey_fd, off = 0, len;
549
550 /* Looks like a filename, load the private key. */
551
552 memset(big_buffer, 0, big_buffer_size);
553
554 if ((privkey_fd = open(CS dkim_private_key_expanded, O_RDONLY)) < 0)
555 {
556 log_write(0, LOG_MAIN | LOG_PANIC, "unable to open "
557 "private key file for reading: %s",
558 dkim_private_key_expanded);
559 goto bad;
560 }
561
562 do
563 {
564 if ((len = read(privkey_fd, big_buffer + off, big_buffer_size - 2 - off)) < 0)
565 {
566 (void) close(privkey_fd);
567 log_write(0, LOG_MAIN|LOG_PANIC, "unable to read private key file: %s",
568 dkim_private_key_expanded);
569 goto bad;
570 }
571 off += len;
572 }
573 while (len > 0);
574
575 (void) close(privkey_fd);
576 big_buffer[off] = '\0';
577 dkim_private_key_expanded = big_buffer;
578 }
579
580 if (!(dkim_hash_expanded = expand_string(dkim->dkim_hash)))
581 { errwhen = US"dkim_hash"; goto expand_bad; }
582
583 if (dkim->dkim_identity)
584 if (!(dkim_identity_expanded = expand_string(dkim->dkim_identity)))
585 { errwhen = US"dkim_identity"; goto expand_bad; }
586 else if (!*dkim_identity_expanded)
587 dkim_identity_expanded = NULL;
588
589 /*XXX so we currently nail signing to RSA + this hash.
590 Need to extract algo from privkey and check for disallowed combos. */
591
592 if (!(sig = pdkim_init_sign(&ctx, dkim_signing_domain,
593 dkim_signing_selector,
594 dkim_private_key_expanded,
595 dkim_hash_expanded,
596 errstr
597 )))
598 goto bad;
599 dkim_private_key_expanded[0] = '\0';
600
601 pdkim_set_optional(sig,
602 CS dkim_sign_headers_expanded,
603 dkim_identity_expanded,
604 pdkim_canon,
605 pdkim_canon, -1, 0, 0);
606
607 if (!ctx.sig) /* link sig to context chain */
608 ctx.sig = sig;
609 else
610 {
611 pdkim_signature * n = ctx.sig;
612 while (n->next) n = n->next;
613 n->next = sig;
614 }
615 }
616 }
617
618 if (prefix)
619 pdkim_feed(&ctx, prefix, Ustrlen(prefix));
620
621 if (lseek(fd, off, SEEK_SET) < 0)
622 sread = -1;
623 else
624 while ((sread = read(fd, &buf, sizeof(buf))) > 0)
625 if ((pdkim_rc = pdkim_feed(&ctx, buf, sread)) != PDKIM_OK)
626 goto pk_bad;
627
628 /* Handle failed read above. */
629 if (sread == -1)
630 {
631 debug_printf("DKIM: Error reading -K file.\n");
632 save_errno = errno;
633 goto bad;
634 }
635
636 /* Build string of headers, one per signature */
637
638 if ((pdkim_rc = pdkim_feed_finish(&ctx, &sig, errstr)) != PDKIM_OK)
639 goto pk_bad;
640
641 for (sigbuf = NULL; sig; sig = sig->next)
642 sigbuf = string_append(sigbuf, 2, US sig->signature_header, US"\r\n");
643
644 (void) string_from_gstring(sigbuf);
645
646 CLEANUP:
647 store_pool = old_pool;
648 errno = save_errno;
649 return sigbuf;
650
651 pk_bad:
652 log_write(0, LOG_MAIN|LOG_PANIC,
653 "DKIM: signing failed: %.100s", pdkim_errstr(pdkim_rc));
654 bad:
655 sigbuf = NULL;
656 goto CLEANUP;
657
658 expand_bad:
659 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand %s: %s",
660 errwhen, expand_string_message);
661 goto bad;
662 }
663
664 #endif