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