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