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