Copyright year bumps for substantive changes 2017
[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 BOOL dkim_collect_error = FALSE;
22
23 static int
24 dkim_exim_query_dns_txt(char *name, char *answer)
25 {
26 dns_answer dnsa;
27 dns_scan dnss;
28 dns_record *rr;
29
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? */
33
34 /* Search for TXT record */
35
36 for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
37 rr;
38 rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
39 if (rr->type == T_TXT)
40 {
41 int rr_offset = 0;
42 int answer_offset = 0;
43
44 /* Copy record content to the answer buffer */
45
46 while (rr_offset < rr->size)
47 {
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));
52 rr_offset += len;
53 answer_offset += len;
54 if (answer_offset >= PDKIM_DNS_TXT_MAX_RECLEN)
55 return PDKIM_FAIL; /*XXX better error detail? logging? */
56 }
57 return PDKIM_OK;
58 }
59
60 return PDKIM_FAIL; /*XXX better error detail? logging? */
61 }
62
63
64 void
65 dkim_exim_init(void)
66 {
67 pdkim_init();
68 }
69
70
71
72 void
73 dkim_exim_verify_init(BOOL dot_stuffing)
74 {
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. */
78
79 dkim_verify_oldpool = store_pool;
80 store_pool = POOL_PERM;
81
82 /* Free previous context if there is one */
83
84 if (dkim_verify_ctx)
85 pdkim_free_ctx(dkim_verify_ctx);
86
87 /* Create new context */
88
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;
92
93 /* Start feed up with any cached data */
94 receive_get_cache();
95
96 store_pool = dkim_verify_oldpool;
97 }
98
99
100 void
101 dkim_exim_verify_feed(uschar * data, int len)
102 {
103 int rc;
104
105 store_pool = POOL_PERM;
106 if ( dkim_collect_input
107 && (rc = pdkim_feed(dkim_verify_ctx, CS data, len)) != PDKIM_OK)
108 {
109 log_write(0, LOG_MAIN,
110 "DKIM: validation error: %.100s", pdkim_errstr(rc));
111 dkim_collect_error = TRUE;
112 dkim_collect_input = FALSE;
113 }
114 store_pool = dkim_verify_oldpool;
115 }
116
117
118 void
119 dkim_exim_verify_finish(void)
120 {
121 pdkim_signature *sig = NULL;
122 int dkim_signers_size = 0;
123 int dkim_signers_ptr = 0;
124 dkim_signers = NULL;
125 int rc;
126
127 store_pool = POOL_PERM;
128
129 /* Delete eventual previous signature chain */
130
131 dkim_signatures = NULL;
132
133 if (dkim_collect_error)
134 {
135 log_write(0, LOG_MAIN,
136 "DKIM: Error while running this message through validation,"
137 " disabling signature verification.");
138 dkim_disable_verify = TRUE;
139 goto out;
140 }
141
142 dkim_collect_input = FALSE;
143
144 /* Finish DKIM operation and fetch link to signatures chain */
145
146 if ((rc = pdkim_feed_finish(dkim_verify_ctx, &dkim_signatures)) != PDKIM_OK)
147 {
148 log_write(0, LOG_MAIN,
149 "DKIM: validation error: %.100s", pdkim_errstr(rc));
150 goto out;
151 }
152
153 for (sig = dkim_signatures; sig; sig = sig->next)
154 {
155 int size = 0;
156 int ptr = 0;
157
158 /* Log a line for each signature */
159
160 uschar *logmsg = string_append(NULL, &size, &ptr, 5,
161 string_sprintf("d=%s s=%s c=%s/%s a=%s b=%d ",
162 sig->domain,
163 sig->selector,
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
167 ? "rsa-sha256"
168 : sig->algo == PDKIM_ALGO_RSA_SHA1 ? "rsa-sha1" : "err",
169 (int)sig->sighash.len > -1 ? sig->sighash.len * 8 : 0
170 ),
171
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""
176 );
177
178 switch (sig->verify_status)
179 {
180 case PDKIM_VERIFY_NONE:
181 logmsg = string_append(logmsg, &size, &ptr, 1, "[not verified]");
182 break;
183
184 case PDKIM_VERIFY_INVALID:
185 logmsg = string_append(logmsg, &size, &ptr, 1, "[invalid - ");
186 switch (sig->verify_ext_status)
187 {
188 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
189 logmsg = string_append(logmsg, &size, &ptr, 1,
190 "public key record (currently?) unavailable]");
191 break;
192
193 case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
194 logmsg = string_append(logmsg, &size, &ptr, 1,
195 "overlong public key record]");
196 break;
197
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]");
202 break;
203
204 case PDKIM_VERIFY_INVALID_SIGNATURE_ERROR:
205 logmsg = string_append(logmsg, &size, &ptr, 1,
206 "signature tag missing or invalid]");
207 break;
208
209 case PDKIM_VERIFY_INVALID_DKIM_VERSION:
210 logmsg = string_append(logmsg, &size, &ptr, 1,
211 "unsupported DKIM version]");
212 break;
213
214 default:
215 logmsg = string_append(logmsg, &size, &ptr, 1,
216 "unspecified problem]");
217 }
218 break;
219
220 case PDKIM_VERIFY_FAIL:
221 logmsg =
222 string_append(logmsg, &size, &ptr, 1, "[verification failed - ");
223 switch (sig->verify_ext_status)
224 {
225 case PDKIM_VERIFY_FAIL_BODY:
226 logmsg = string_append(logmsg, &size, &ptr, 1,
227 "body hash mismatch (body probably modified in transit)]");
228 break;
229
230 case PDKIM_VERIFY_FAIL_MESSAGE:
231 logmsg = string_append(logmsg, &size, &ptr, 1,
232 "signature did not verify (headers probably modified in transit)]");
233 break;
234
235 default:
236 logmsg = string_append(logmsg, &size, &ptr, 1, "unspecified reason]");
237 }
238 break;
239
240 case PDKIM_VERIFY_PASS:
241 logmsg =
242 string_append(logmsg, &size, &ptr, 1, "[verification succeeded]");
243 break;
244 }
245
246 logmsg[ptr] = '\0';
247 log_write(0, LOG_MAIN, "DKIM: %s", logmsg);
248
249 /* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */
250
251 dkim_signers = string_append(dkim_signers,
252 &dkim_signers_size,
253 &dkim_signers_ptr, 2, sig->domain, ":");
254
255 if (sig->identity)
256 dkim_signers = string_append(dkim_signers,
257 &dkim_signers_size,
258 &dkim_signers_ptr, 2, sig->identity, ":");
259
260 /* Process next signature */
261 }
262
263 /* NULL-terminate and chop the last colon from the domain list */
264
265 if (dkim_signers)
266 {
267 dkim_signers[dkim_signers_ptr] = '\0';
268 if (Ustrlen(dkim_signers) > 0)
269 dkim_signers[Ustrlen(dkim_signers) - 1] = '\0';
270 }
271
272 out:
273 store_pool = dkim_verify_oldpool;
274 }
275
276
277 void
278 dkim_exim_acl_setup(uschar * id)
279 {
280 pdkim_signature * sig;
281 uschar * cmp_val;
282
283 dkim_cur_sig = NULL;
284 dkim_cur_signer = id;
285
286 if (dkim_disable_verify || !id || !dkim_verify_ctx)
287 return;
288
289 /* Find signature to run ACL on */
290
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
294 )
295 {
296 dkim_cur_sig = sig;
297
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
304 function below). */
305
306 dkim_signing_domain = US sig->domain;
307 dkim_signing_selector = US sig->selector;
308 dkim_key_length = sig->sighash.len * 8;
309 return;
310 }
311 }
312
313
314 static uschar *
315 dkim_exim_expand_defaults(int what)
316 {
317 switch (what)
318 {
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"";
336 }
337 }
338
339
340 uschar *
341 dkim_exim_expand_query(int what)
342 {
343 if (!dkim_verify_ctx || dkim_disable_verify || !dkim_cur_sig)
344 return dkim_exim_expand_defaults(what);
345
346 switch (what)
347 {
348 case DKIM_ALGO:
349 switch (dkim_cur_sig->algo)
350 {
351 case PDKIM_ALGO_RSA_SHA1: return US"rsa-sha1";
352 case PDKIM_ALGO_RSA_SHA256:
353 default: return US"rsa-sha256";
354 }
355
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);
360
361 case DKIM_CANON_BODY:
362 switch (dkim_cur_sig->canon_body)
363 {
364 case PDKIM_CANON_RELAXED: return US"relaxed";
365 case PDKIM_CANON_SIMPLE:
366 default: return US"simple";
367 }
368
369 case DKIM_CANON_HEADERS:
370 switch (dkim_cur_sig->canon_headers)
371 {
372 case PDKIM_CANON_RELAXED: return US"relaxed";
373 case PDKIM_CANON_SIMPLE:
374 default: return US"simple";
375 }
376
377 case DKIM_COPIEDHEADERS:
378 return dkim_cur_sig->copiedheaders
379 ? US dkim_cur_sig->copiedheaders : dkim_exim_expand_defaults(what);
380
381 case DKIM_CREATED:
382 return dkim_cur_sig->created > 0
383 ? string_sprintf("%llu", dkim_cur_sig->created)
384 : dkim_exim_expand_defaults(what);
385
386 case DKIM_EXPIRES:
387 return dkim_cur_sig->expires > 0
388 ? string_sprintf("%llu", dkim_cur_sig->expires)
389 : dkim_exim_expand_defaults(what);
390
391 case DKIM_HEADERNAMES:
392 return dkim_cur_sig->headernames
393 ? dkim_cur_sig->headernames : dkim_exim_expand_defaults(what);
394
395 case DKIM_IDENTITY:
396 return dkim_cur_sig->identity
397 ? US dkim_cur_sig->identity : dkim_exim_expand_defaults(what);
398
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);
405
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);
412
413 case DKIM_KEY_NOTES:
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);
419
420 case DKIM_KEY_TESTING:
421 return dkim_cur_sig->pubkey
422 ? dkim_cur_sig->pubkey->testing
423 ? US"1"
424 : dkim_exim_expand_defaults(what)
425 : dkim_exim_expand_defaults(what);
426
427 case DKIM_NOSUBDOMAINS:
428 return dkim_cur_sig->pubkey
429 ? dkim_cur_sig->pubkey->no_subdomaining
430 ? US"1"
431 : dkim_exim_expand_defaults(what)
432 : dkim_exim_expand_defaults(what);
433
434 case DKIM_VERIFY_STATUS:
435 switch (dkim_cur_sig->verify_status)
436 {
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";
442 }
443
444 case DKIM_VERIFY_REASON:
445 switch (dkim_cur_sig->verify_ext_status)
446 {
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";
453 }
454
455 default:
456 return US"";
457 }
458 }
459
460
461 uschar *
462 dkim_exim_sign(int dkim_fd, struct ob_dkim * dkim)
463 {
464 const uschar * dkim_domain;
465 int sep = 0;
466 uschar *seen_items = NULL;
467 int seen_items_size = 0;
468 int seen_items_offset = 0;
469 uschar itembuf[256];
470 uschar *dkim_canon_expanded;
471 uschar *dkim_sign_headers_expanded;
472 uschar *dkim_private_key_expanded;
473 pdkim_ctx *ctx = NULL;
474 uschar *rc = NULL;
475 uschar *sigbuf = NULL;
476 int sigsize = 0;
477 int sigptr = 0;
478 pdkim_signature *signature;
479 int pdkim_canon;
480 int pdkim_rc;
481 int sread;
482 char buf[4096];
483 int save_errno = 0;
484 int old_pool = store_pool;
485
486 store_pool = POOL_MAIN;
487
488 if (!(dkim_domain = expand_cstring(dkim->dkim_domain)))
489 {
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);
493 goto bad;
494 }
495
496 /* Set $dkim_domain expansion variable to each unique domain in list. */
497
498 while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep,
499 itembuf, sizeof(itembuf))))
500 {
501 if (!dkim_signing_domain || dkim_signing_domain[0] == '\0')
502 continue;
503
504 /* Only sign once for each domain, no matter how often it
505 appears in the expanded list. */
506
507 if (seen_items)
508 {
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,
512 NULL) == OK)
513 continue;
514
515 seen_items =
516 string_append(seen_items, &seen_items_size, &seen_items_offset, 1, ":");
517 }
518
519 seen_items =
520 string_append(seen_items, &seen_items_size, &seen_items_offset, 1,
521 dkim_signing_domain);
522 seen_items[seen_items_offset] = '\0';
523
524 /* Set up $dkim_selector expansion variable. */
525
526 if (!(dkim_signing_selector = expand_string(dkim->dkim_selector)))
527 {
528 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
529 "dkim_selector: %s", expand_string_message);
530 goto bad;
531 }
532
533 /* Get canonicalization to use */
534
535 dkim_canon_expanded = dkim->dkim_canon
536 ? expand_string(dkim->dkim_canon) : US"relaxed";
537 if (!dkim_canon_expanded)
538 {
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);
542 goto bad;
543 }
544
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;
549 else
550 {
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;
555 }
556
557 dkim_sign_headers_expanded = NULL;
558 if (dkim->dkim_sign_headers)
559 if (!(dkim_sign_headers_expanded = expand_string(dkim->dkim_sign_headers)))
560 {
561 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
562 "dkim_sign_headers: %s", expand_string_message);
563 goto bad;
564 }
565 /* else pass NULL, which means default header list */
566
567 /* Get private key to use. */
568
569 if (!(dkim_private_key_expanded = expand_string(dkim->dkim_private_key)))
570 {
571 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
572 "dkim_private_key: %s", expand_string_message);
573 goto bad;
574 }
575
576 if ( Ustrlen(dkim_private_key_expanded) == 0
577 || Ustrcmp(dkim_private_key_expanded, "0") == 0
578 || Ustrcmp(dkim_private_key_expanded, "false") == 0
579 )
580 continue; /* don't sign, but no error */
581
582 if (dkim_private_key_expanded[0] == '/')
583 {
584 int privkey_fd = 0;
585
586 /* Looks like a filename, load the private key. */
587
588 memset(big_buffer, 0, big_buffer_size);
589
590 if ((privkey_fd = open(CS dkim_private_key_expanded, O_RDONLY)) < 0)
591 {
592 log_write(0, LOG_MAIN | LOG_PANIC, "unable to open "
593 "private key file for reading: %s",
594 dkim_private_key_expanded);
595 goto bad;
596 }
597
598 if (read(privkey_fd, big_buffer, big_buffer_size - 2) < 0)
599 {
600 log_write(0, LOG_MAIN|LOG_PANIC, "unable to read private key file: %s",
601 dkim_private_key_expanded);
602 goto bad;
603 }
604
605 (void) close(privkey_fd);
606 dkim_private_key_expanded = big_buffer;
607 }
608
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,
613 dkim->dot_stuffed,
614 &dkim_exim_query_dns_txt
615 );
616 dkim_private_key_expanded[0] = '\0';
617 pdkim_set_optional(ctx,
618 CS dkim_sign_headers_expanded,
619 NULL,
620 pdkim_canon,
621 pdkim_canon, -1, 0, 0);
622
623 lseek(dkim_fd, 0, SEEK_SET);
624
625 while ((sread = read(dkim_fd, &buf, sizeof(buf))) > 0)
626 if ((pdkim_rc = pdkim_feed(ctx, buf, sread)) != PDKIM_OK)
627 goto pk_bad;
628
629 /* Handle failed read above. */
630 if (sread == -1)
631 {
632 debug_printf("DKIM: Error reading -K file.\n");
633 save_errno = errno;
634 goto bad;
635 }
636
637 if ((pdkim_rc = pdkim_feed_finish(ctx, &signature)) != PDKIM_OK)
638 goto pk_bad;
639
640 sigbuf = string_append(sigbuf, &sigsize, &sigptr, 2,
641 US signature->signature_header, US"\r\n");
642
643 pdkim_free_ctx(ctx);
644 ctx = NULL;
645 }
646
647 if (sigbuf)
648 {
649 sigbuf[sigptr] = '\0';
650 rc = sigbuf;
651 }
652 else
653 rc = US"";
654
655 CLEANUP:
656 if (ctx)
657 pdkim_free_ctx(ctx);
658 store_pool = old_pool;
659 errno = save_errno;
660 return rc;
661
662 pk_bad:
663 log_write(0, LOG_MAIN|LOG_PANIC,
664 "DKIM: signing failed: %.100s", pdkim_errstr(pdkim_rc));
665 bad:
666 rc = NULL;
667 goto CLEANUP;
668 }
669
670 #endif