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