e027a23b3213d227d3c12cf4659e0844eb14e31e
[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(void)
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);
89 dkim_collect_input = !!dkim_verify_ctx;
90
91 store_pool = dkim_verify_oldpool;
92 }
93
94
95 void
96 dkim_exim_verify_feed(uschar * data, int len)
97 {
98 int rc;
99
100 store_pool = POOL_PERM;
101 if ( dkim_collect_input
102 && (rc = pdkim_feed(dkim_verify_ctx, (char *)data, len)) != PDKIM_OK)
103 {
104 log_write(0, LOG_MAIN,
105 "DKIM: validation error: %.100s", pdkim_errstr(rc));
106 dkim_collect_input = FALSE;
107 }
108 store_pool = dkim_verify_oldpool;
109 }
110
111
112 void
113 dkim_exim_verify_finish(void)
114 {
115 pdkim_signature *sig = NULL;
116 int dkim_signers_size = 0;
117 int dkim_signers_ptr = 0;
118 dkim_signers = NULL;
119 int rc;
120
121 store_pool = POOL_PERM;
122
123 /* Delete eventual previous signature chain */
124
125 dkim_signatures = NULL;
126
127 /* If we have arrived here with dkim_collect_input == FALSE, it
128 means there was a processing error somewhere along the way.
129 Log the incident and disable futher verification. */
130
131 if (!dkim_collect_input)
132 {
133 log_write(0, LOG_MAIN,
134 "DKIM: Error while running this message through validation,"
135 " disabling signature verification.");
136 dkim_disable_verify = TRUE;
137 goto out;
138 }
139
140 dkim_collect_input = FALSE;
141
142 /* Finish DKIM operation and fetch link to signatures chain */
143
144 if ((rc = pdkim_feed_finish(dkim_verify_ctx, &dkim_signatures)) != PDKIM_OK)
145 {
146 log_write(0, LOG_MAIN,
147 "DKIM: validation error: %.100s", pdkim_errstr(rc));
148 goto out;
149 }
150
151 for (sig = dkim_signatures; sig; sig = sig->next)
152 {
153 int size = 0;
154 int ptr = 0;
155
156 /* Log a line for each signature */
157
158 uschar *logmsg = string_append(NULL, &size, &ptr, 5,
159 string_sprintf("d=%s s=%s c=%s/%s a=%s b=%d ",
160 sig->domain,
161 sig->selector,
162 sig->canon_headers == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
163 sig->canon_body == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
164 sig->algo == PDKIM_ALGO_RSA_SHA256
165 ? "rsa-sha256"
166 : sig->algo == PDKIM_ALGO_RSA_SHA1 ? "rsa-sha1" : "err",
167 (int)sig->sigdata.len > -1 ? sig->sigdata.len * 8 : 0
168 ),
169
170 sig->identity ? string_sprintf("i=%s ", sig->identity) : US"",
171 sig->created > 0 ? string_sprintf("t=%lu ", sig->created) : US"",
172 sig->expires > 0 ? string_sprintf("x=%lu ", sig->expires) : US"",
173 sig->bodylength > -1 ? string_sprintf("l=%lu ", sig->bodylength) : US""
174 );
175
176 switch (sig->verify_status)
177 {
178 case PDKIM_VERIFY_NONE:
179 logmsg = string_append(logmsg, &size, &ptr, 1, "[not verified]");
180 break;
181
182 case PDKIM_VERIFY_INVALID:
183 logmsg = string_append(logmsg, &size, &ptr, 1, "[invalid - ");
184 switch (sig->verify_ext_status)
185 {
186 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
187 logmsg = string_append(logmsg, &size, &ptr, 1,
188 "public key record (currently?) unavailable]");
189 break;
190
191 case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
192 logmsg = string_append(logmsg, &size, &ptr, 1,
193 "overlong public key record]");
194 break;
195
196 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:
197 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT:
198 logmsg = string_append(logmsg, &size, &ptr, 1,
199 "syntax error in public key record]");
200 break;
201
202 case PDKIM_VERIFY_INVALID_SIGNATURE_ERROR:
203 logmsg = string_append(logmsg, &size, &ptr, 1,
204 "signature tag missing or invalid]");
205 break;
206
207 case PDKIM_VERIFY_INVALID_DKIM_VERSION:
208 logmsg = string_append(logmsg, &size, &ptr, 1,
209 "unsupported DKIM version]");
210 break;
211
212 default:
213 logmsg = string_append(logmsg, &size, &ptr, 1,
214 "unspecified problem]");
215 }
216 break;
217
218 case PDKIM_VERIFY_FAIL:
219 logmsg =
220 string_append(logmsg, &size, &ptr, 1, "[verification failed - ");
221 switch (sig->verify_ext_status)
222 {
223 case PDKIM_VERIFY_FAIL_BODY:
224 logmsg = string_append(logmsg, &size, &ptr, 1,
225 "body hash mismatch (body probably modified in transit)]");
226 break;
227
228 case PDKIM_VERIFY_FAIL_MESSAGE:
229 logmsg = string_append(logmsg, &size, &ptr, 1,
230 "signature did not verify (headers probably modified in transit)]");
231 break;
232
233 default:
234 logmsg = string_append(logmsg, &size, &ptr, 1, "unspecified reason]");
235 }
236 break;
237
238 case PDKIM_VERIFY_PASS:
239 logmsg =
240 string_append(logmsg, &size, &ptr, 1, "[verification succeeded]");
241 break;
242 }
243
244 logmsg[ptr] = '\0';
245 log_write(0, LOG_MAIN, "DKIM: %s", logmsg);
246
247 /* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */
248
249 dkim_signers = string_append(dkim_signers,
250 &dkim_signers_size,
251 &dkim_signers_ptr, 2, sig->domain, ":");
252
253 if (sig->identity)
254 dkim_signers = string_append(dkim_signers,
255 &dkim_signers_size,
256 &dkim_signers_ptr, 2, sig->identity, ":");
257
258 /* Process next signature */
259 }
260
261 /* NULL-terminate and chop the last colon from the domain list */
262
263 if (dkim_signers)
264 {
265 dkim_signers[dkim_signers_ptr] = '\0';
266 if (Ustrlen(dkim_signers) > 0)
267 dkim_signers[Ustrlen(dkim_signers) - 1] = '\0';
268 }
269
270 out:
271 store_pool = dkim_verify_oldpool;
272 }
273
274
275 void
276 dkim_exim_acl_setup(uschar * id)
277 {
278 pdkim_signature * sig;
279 uschar * cmp_val;
280
281 dkim_cur_sig = NULL;
282 dkim_cur_signer = id;
283
284 if (dkim_disable_verify || !id || !dkim_verify_ctx)
285 return;
286
287 /* Find signature to run ACL on */
288
289 for (sig = dkim_signatures; sig; sig = sig->next)
290 if ( (cmp_val = Ustrchr(id, '@') != NULL ? US sig->identity : US sig->domain)
291 && strcmpic(cmp_val, id) == 0
292 )
293 {
294 dkim_cur_sig = sig;
295
296 /* The "dkim_domain" and "dkim_selector" expansion variables have
297 related globals, since they are used in the signing code too.
298 Instead of inventing separate names for verification, we set
299 them here. This is easy since a domain and selector is guaranteed
300 to be in a signature. The other dkim_* expansion items are
301 dynamically fetched from dkim_cur_sig at expansion time (see
302 function below). */
303
304 dkim_signing_domain = US sig->domain;
305 dkim_signing_selector = US sig->selector;
306 dkim_key_length = sig->sigdata.len * 8;
307 return;
308 }
309 }
310
311
312 static uschar *
313 dkim_exim_expand_defaults(int what)
314 {
315 switch (what)
316 {
317 case DKIM_ALGO: return US"";
318 case DKIM_BODYLENGTH: return US"9999999999999";
319 case DKIM_CANON_BODY: return US"";
320 case DKIM_CANON_HEADERS: return US"";
321 case DKIM_COPIEDHEADERS: return US"";
322 case DKIM_CREATED: return US"0";
323 case DKIM_EXPIRES: return US"9999999999999";
324 case DKIM_HEADERNAMES: return US"";
325 case DKIM_IDENTITY: return US"";
326 case DKIM_KEY_GRANULARITY: return US"*";
327 case DKIM_KEY_SRVTYPE: return US"*";
328 case DKIM_KEY_NOTES: return US"";
329 case DKIM_KEY_TESTING: return US"0";
330 case DKIM_NOSUBDOMAINS: return US"0";
331 case DKIM_VERIFY_STATUS: return US"none";
332 case DKIM_VERIFY_REASON: return US"";
333 default: return US"";
334 }
335 }
336
337
338 uschar *
339 dkim_exim_expand_query(int what)
340 {
341 if (!dkim_verify_ctx || dkim_disable_verify || !dkim_cur_sig)
342 return dkim_exim_expand_defaults(what);
343
344 switch (what)
345 {
346 case DKIM_ALGO:
347 switch (dkim_cur_sig->algo)
348 {
349 case PDKIM_ALGO_RSA_SHA1: return US"rsa-sha1";
350 case PDKIM_ALGO_RSA_SHA256:
351 default: return US"rsa-sha256";
352 }
353
354 case DKIM_BODYLENGTH:
355 return dkim_cur_sig->bodylength >= 0
356 ? string_sprintf(OFF_T_FMT, (LONGLONG_T) 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("%llu", 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("%llu", 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 uschar *
460 dkim_exim_sign(int dkim_fd, uschar * dkim_private_key,
461 const uschar * dkim_domain, uschar * dkim_selector,
462 uschar * dkim_canon, uschar * dkim_sign_headers)
463 {
464 int sep = 0;
465 uschar *seen_items = NULL;
466 int seen_items_size = 0;
467 int seen_items_offset = 0;
468 uschar itembuf[256];
469 uschar *dkim_canon_expanded;
470 uschar *dkim_sign_headers_expanded;
471 uschar *dkim_private_key_expanded;
472 pdkim_ctx *ctx = NULL;
473 uschar *rc = NULL;
474 uschar *sigbuf = NULL;
475 int sigsize = 0;
476 int sigptr = 0;
477 pdkim_signature *signature;
478 int pdkim_canon;
479 int pdkim_rc;
480 int sread;
481 char buf[4096];
482 int save_errno = 0;
483 int old_pool = store_pool;
484
485 store_pool = POOL_MAIN;
486
487 if (!(dkim_domain = expand_cstring(dkim_domain)))
488 {
489 /* expansion error, do not send message. */
490 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
491 "dkim_domain: %s", expand_string_message);
492 goto bad;
493 }
494
495 /* Set $dkim_domain expansion variable to each unique domain in list. */
496
497 while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep,
498 itembuf, sizeof(itembuf))))
499 {
500 if (!dkim_signing_domain || dkim_signing_domain[0] == '\0')
501 continue;
502
503 /* Only sign once for each domain, no matter how often it
504 appears in the expanded list. */
505
506 if (seen_items)
507 {
508 const uschar *seen_items_list = seen_items;
509 if (match_isinlist(dkim_signing_domain,
510 &seen_items_list, 0, NULL, NULL, MCL_STRING, TRUE,
511 NULL) == OK)
512 continue;
513
514 seen_items =
515 string_append(seen_items, &seen_items_size, &seen_items_offset, 1, ":");
516 }
517
518 seen_items =
519 string_append(seen_items, &seen_items_size, &seen_items_offset, 1,
520 dkim_signing_domain);
521 seen_items[seen_items_offset] = '\0';
522
523 /* Set up $dkim_selector expansion variable. */
524
525 if (!(dkim_signing_selector = expand_string(dkim_selector)))
526 {
527 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
528 "dkim_selector: %s", expand_string_message);
529 goto bad;
530 }
531
532 /* Get canonicalization to use */
533
534 dkim_canon_expanded = dkim_canon ? expand_string(dkim_canon) : US"relaxed";
535 if (!dkim_canon_expanded)
536 {
537 /* expansion error, do not send message. */
538 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
539 "dkim_canon: %s", expand_string_message);
540 goto bad;
541 }
542
543 if (Ustrcmp(dkim_canon_expanded, "relaxed") == 0)
544 pdkim_canon = PDKIM_CANON_RELAXED;
545 else if (Ustrcmp(dkim_canon_expanded, "simple") == 0)
546 pdkim_canon = PDKIM_CANON_SIMPLE;
547 else
548 {
549 log_write(0, LOG_MAIN,
550 "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",
551 dkim_canon_expanded);
552 pdkim_canon = PDKIM_CANON_RELAXED;
553 }
554
555 dkim_sign_headers_expanded = NULL;
556 if (dkim_sign_headers)
557 if (!(dkim_sign_headers_expanded = expand_string(dkim_sign_headers)))
558 {
559 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
560 "dkim_sign_headers: %s", expand_string_message);
561 goto bad;
562 }
563 /* else pass NULL, which means default header list */
564
565 /* Get private key to use. */
566
567 if (!(dkim_private_key_expanded = expand_string(dkim_private_key)))
568 {
569 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
570 "dkim_private_key: %s", expand_string_message);
571 goto bad;
572 }
573
574 if ( Ustrlen(dkim_private_key_expanded) == 0
575 || Ustrcmp(dkim_private_key_expanded, "0") == 0
576 || Ustrcmp(dkim_private_key_expanded, "false") == 0
577 )
578 continue; /* don't sign, but no error */
579
580 if (dkim_private_key_expanded[0] == '/')
581 {
582 int privkey_fd = 0;
583
584 /* Looks like a filename, load the private key. */
585
586 memset(big_buffer, 0, big_buffer_size);
587 privkey_fd = open(CS dkim_private_key_expanded, O_RDONLY);
588 if (privkey_fd < 0)
589 {
590 log_write(0, LOG_MAIN | LOG_PANIC, "unable to open "
591 "private key file for reading: %s",
592 dkim_private_key_expanded);
593 goto bad;
594 }
595
596 if (read(privkey_fd, big_buffer, big_buffer_size - 2) < 0)
597 {
598 log_write(0, LOG_MAIN|LOG_PANIC, "unable to read private key file: %s",
599 dkim_private_key_expanded);
600 goto bad;
601 }
602
603 (void) close(privkey_fd);
604 dkim_private_key_expanded = big_buffer;
605 }
606
607 ctx = pdkim_init_sign( CS dkim_signing_domain,
608 CS dkim_signing_selector,
609 CS dkim_private_key_expanded,
610 PDKIM_ALGO_RSA_SHA256);
611 pdkim_set_optional(ctx,
612 (char *) dkim_sign_headers_expanded,
613 NULL,
614 pdkim_canon,
615 pdkim_canon, -1, 0, 0);
616
617 lseek(dkim_fd, 0, SEEK_SET);
618
619 while ((sread = read(dkim_fd, &buf, 4096)) > 0)
620 if ((pdkim_rc = pdkim_feed(ctx, buf, sread)) != PDKIM_OK)
621 goto pk_bad;
622
623 /* Handle failed read above. */
624 if (sread == -1)
625 {
626 debug_printf("DKIM: Error reading -K file.\n");
627 save_errno = errno;
628 goto bad;
629 }
630
631 if ((pdkim_rc = pdkim_feed_finish(ctx, &signature)) != PDKIM_OK)
632 goto pk_bad;
633
634 sigbuf = string_append(sigbuf, &sigsize, &sigptr, 2,
635 US signature->signature_header, US"\r\n");
636
637 pdkim_free_ctx(ctx);
638 ctx = NULL;
639 }
640
641 if (sigbuf)
642 {
643 sigbuf[sigptr] = '\0';
644 rc = sigbuf;
645 }
646 else
647 rc = US"";
648
649 CLEANUP:
650 if (ctx)
651 pdkim_free_ctx(ctx);
652 store_pool = old_pool;
653 errno = save_errno;
654 return rc;
655
656 pk_bad:
657 log_write(0, LOG_MAIN|LOG_PANIC,
658 "DKIM: signing failed: %.100s", pdkim_errstr(pdkim_rc));
659 bad:
660 rc = NULL;
661 goto CLEANUP;
662 }
663
664 #endif