tidying: coverity fixes
[exim.git] / src / src / dkim.c
1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
4
5 /* Copyright (c) University of Cambridge, 1995 - 2017 */
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 static const uschar * dkim_collect_error = NULL;
22
23 static int
24 dkim_exim_query_dns_txt(char *name, char *answer)
25 {
26 dns_answer dnsa;
27 dns_scan dnss;
28 dns_record *rr;
29
30 lookup_dnssec_authenticated = NULL;
31 if (dns_lookup(&dnsa, US name, T_TXT, NULL) != DNS_SUCCEED)
32 return PDKIM_FAIL; /*XXX better error detail? logging? */
33
34 /* Search for TXT record */
35
36 for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
37 rr;
38 rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
39 if (rr->type == T_TXT)
40 {
41 int rr_offset = 0;
42 int answer_offset = 0;
43
44 /* Copy record content to the answer buffer */
45
46 while (rr_offset < rr->size)
47 {
48 uschar len = rr->data[rr_offset++];
49 snprintf(answer + answer_offset,
50 PDKIM_DNS_TXT_MAX_RECLEN - answer_offset,
51 "%.*s", (int)len, (char *) (rr->data + rr_offset));
52 rr_offset += len;
53 answer_offset += len;
54 if (answer_offset >= PDKIM_DNS_TXT_MAX_RECLEN)
55 return PDKIM_FAIL; /*XXX better error detail? logging? */
56 }
57 return PDKIM_OK;
58 }
59
60 return PDKIM_FAIL; /*XXX better error detail? logging? */
61 }
62
63
64 void
65 dkim_exim_init(void)
66 {
67 pdkim_init();
68 }
69
70
71
72 void
73 dkim_exim_verify_init(BOOL dot_stuffing)
74 {
75 /* There is a store-reset between header & body reception
76 so cannot use the main pool. Any allocs done by Exim
77 memory-handling must use the perm pool. */
78
79 dkim_verify_oldpool = store_pool;
80 store_pool = POOL_PERM;
81
82 /* Free previous context if there is one */
83
84 if (dkim_verify_ctx)
85 pdkim_free_ctx(dkim_verify_ctx);
86
87 /* Create new context */
88
89 dkim_verify_ctx = pdkim_init_verify(&dkim_exim_query_dns_txt, dot_stuffing);
90 dkim_collect_input = !!dkim_verify_ctx;
91 dkim_collect_error = NULL;
92
93 /* Start feed up with any cached data */
94 receive_get_cache();
95
96 store_pool = dkim_verify_oldpool;
97 }
98
99
100 void
101 dkim_exim_verify_feed(uschar * data, int len)
102 {
103 int rc;
104
105 store_pool = POOL_PERM;
106 if ( dkim_collect_input
107 && (rc = pdkim_feed(dkim_verify_ctx, CS data, len)) != PDKIM_OK)
108 {
109 dkim_collect_error = pdkim_errstr(rc);
110 log_write(0, LOG_MAIN,
111 "DKIM: validation error: %.100s", dkim_collect_error);
112 dkim_collect_input = FALSE;
113 }
114 store_pool = dkim_verify_oldpool;
115 }
116
117
118 void
119 dkim_exim_verify_finish(void)
120 {
121 pdkim_signature * sig = NULL;
122 int dkim_signers_size = 0, dkim_signers_ptr = 0, rc;
123 const uschar * errstr;
124
125 store_pool = POOL_PERM;
126
127 /* Delete eventual previous signature chain */
128
129 dkim_signers = NULL;
130 dkim_signatures = NULL;
131
132 if (dkim_collect_error)
133 {
134 log_write(0, LOG_MAIN,
135 "DKIM: Error during validation, disabling signature verification: %.100s",
136 dkim_collect_error);
137 dkim_disable_verify = TRUE;
138 goto out;
139 }
140
141 dkim_collect_input = FALSE;
142
143 /* Finish DKIM operation and fetch link to signatures chain */
144
145 rc = pdkim_feed_finish(dkim_verify_ctx, &dkim_signatures, &errstr);
146 if (rc != PDKIM_OK)
147 {
148 log_write(0, LOG_MAIN, "DKIM: validation error: %.100s%s%s", pdkim_errstr(rc),
149 errstr ? ": " : "", errstr ? errstr : US"");
150 goto out;
151 }
152
153 for (sig = dkim_signatures; sig; sig = sig->next)
154 {
155 int size = 0, ptr = 0;
156 uschar * logmsg = NULL, * s;
157
158 /* Log a line for each signature */
159
160 if (!(s = sig->domain)) s = US"<UNSET>";
161 logmsg = string_append(logmsg, &size, &ptr, 2, "d=", s);
162 if (!(s = sig->selector)) s = US"<UNSET>";
163 logmsg = string_append(logmsg, &size, &ptr, 2, " s=", s);
164 logmsg = string_append(logmsg, &size, &ptr, 7,
165 " c=", sig->canon_headers == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
166 "/", sig->canon_body == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
167 " a=", sig->algo == PDKIM_ALGO_RSA_SHA256
168 ? "rsa-sha256"
169 : sig->algo == PDKIM_ALGO_RSA_SHA1 ? "rsa-sha1" : "err",
170 string_sprintf(" b=%d",
171 (int)sig->sighash.len > -1 ? sig->sighash.len * 8 : 0));
172 if ((s= sig->identity)) string_append(logmsg, &size, &ptr, 2, " i=", s);
173 if (sig->created > 0) string_append(logmsg, &size, &ptr, 1,
174 string_sprintf(" t=%lu", sig->created));
175 if (sig->expires > 0) string_append(logmsg, &size, &ptr, 1,
176 string_sprintf(" x=%lu", sig->expires));
177 if (sig->bodylength > -1) string_append(logmsg, &size, &ptr, 1,
178 string_sprintf(" l=%lu", sig->bodylength));
179
180 switch (sig->verify_status)
181 {
182 case PDKIM_VERIFY_NONE:
183 logmsg = string_append(logmsg, &size, &ptr, 1, " [not verified]");
184 break;
185
186 case PDKIM_VERIFY_INVALID:
187 logmsg = string_append(logmsg, &size, &ptr, 1, " [invalid - ");
188 switch (sig->verify_ext_status)
189 {
190 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
191 logmsg = string_append(logmsg, &size, &ptr, 1,
192 "public key record (currently?) unavailable]");
193 break;
194
195 case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
196 logmsg = string_append(logmsg, &size, &ptr, 1,
197 "overlong public key record]");
198 break;
199
200 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:
201 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT:
202 logmsg = string_append(logmsg, &size, &ptr, 1,
203 "syntax error in public key record]");
204 break;
205
206 case PDKIM_VERIFY_INVALID_SIGNATURE_ERROR:
207 logmsg = string_append(logmsg, &size, &ptr, 1,
208 "signature tag missing or invalid]");
209 break;
210
211 case PDKIM_VERIFY_INVALID_DKIM_VERSION:
212 logmsg = string_append(logmsg, &size, &ptr, 1,
213 "unsupported DKIM version]");
214 break;
215
216 default:
217 logmsg = string_append(logmsg, &size, &ptr, 1,
218 "unspecified problem]");
219 }
220 break;
221
222 case PDKIM_VERIFY_FAIL:
223 logmsg =
224 string_append(logmsg, &size, &ptr, 1, " [verification failed - ");
225 switch (sig->verify_ext_status)
226 {
227 case PDKIM_VERIFY_FAIL_BODY:
228 logmsg = string_append(logmsg, &size, &ptr, 1,
229 "body hash mismatch (body probably modified in transit)]");
230 break;
231
232 case PDKIM_VERIFY_FAIL_MESSAGE:
233 logmsg = string_append(logmsg, &size, &ptr, 1,
234 "signature did not verify (headers probably modified in transit)]");
235 break;
236
237 default:
238 logmsg = string_append(logmsg, &size, &ptr, 1, "unspecified reason]");
239 }
240 break;
241
242 case PDKIM_VERIFY_PASS:
243 logmsg =
244 string_append(logmsg, &size, &ptr, 1, " [verification succeeded]");
245 break;
246 }
247
248 logmsg[ptr] = '\0';
249 log_write(0, LOG_MAIN, "DKIM: %s", logmsg);
250
251 /* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */
252
253 if (sig->domain)
254 dkim_signers = string_append_listele(dkim_signers, ':', sig->domain);
255
256 if (sig->identity)
257 dkim_signers = string_append_listele(dkim_signers, ':', sig->identity);
258
259 /* Process next signature */
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->sighash.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 /* Generate signatures for the given file, returning a string.
452 If a prefix is given, prepend it to the file for the calculations.
453 */
454
455 uschar *
456 dkim_exim_sign(int fd, off_t off, uschar * prefix,
457 struct ob_dkim * dkim, const uschar ** errstr)
458 {
459 const uschar * dkim_domain;
460 int sep = 0;
461 uschar *seen_items = NULL;
462 int seen_items_size = 0;
463 int seen_items_offset = 0;
464 uschar *dkim_canon_expanded;
465 uschar *dkim_sign_headers_expanded;
466 uschar *dkim_private_key_expanded;
467 pdkim_ctx *ctx = NULL;
468 uschar *rc = NULL;
469 uschar *sigbuf = NULL;
470 int sigsize = 0;
471 int sigptr = 0;
472 pdkim_signature *signature;
473 int pdkim_canon;
474 int pdkim_rc;
475 int sread;
476 char buf[4096];
477 int save_errno = 0;
478 int old_pool = store_pool;
479
480 store_pool = POOL_MAIN;
481
482 if (!(dkim_domain = expand_cstring(dkim->dkim_domain)))
483 {
484 /* expansion error, do not send message. */
485 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
486 "dkim_domain: %s", expand_string_message);
487 goto bad;
488 }
489
490 /* Set $dkim_domain expansion variable to each unique domain in list. */
491
492 while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep, NULL, 0)))
493 {
494 if (dkim_signing_domain[0] == '\0')
495 continue;
496
497 /* Only sign once for each domain, no matter how often it
498 appears in the expanded list. */
499
500 if (seen_items)
501 {
502 const uschar *seen_items_list = seen_items;
503 if (match_isinlist(dkim_signing_domain,
504 &seen_items_list, 0, NULL, NULL, MCL_STRING, TRUE,
505 NULL) == OK)
506 continue;
507
508 seen_items =
509 string_append(seen_items, &seen_items_size, &seen_items_offset, 1, ":");
510 }
511
512 seen_items =
513 string_append(seen_items, &seen_items_size, &seen_items_offset, 1,
514 dkim_signing_domain);
515 seen_items[seen_items_offset] = '\0';
516
517 /* Set up $dkim_selector expansion variable. */
518
519 if (!(dkim_signing_selector = expand_string(dkim->dkim_selector)))
520 {
521 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
522 "dkim_selector: %s", expand_string_message);
523 goto bad;
524 }
525
526 /* Get canonicalization to use */
527
528 dkim_canon_expanded = dkim->dkim_canon
529 ? expand_string(dkim->dkim_canon) : US"relaxed";
530 if (!dkim_canon_expanded)
531 {
532 /* expansion error, do not send message. */
533 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
534 "dkim_canon: %s", expand_string_message);
535 goto bad;
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->dkim_sign_headers)
552 if (!(dkim_sign_headers_expanded = expand_string(dkim->dkim_sign_headers)))
553 {
554 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
555 "dkim_sign_headers: %s", expand_string_message);
556 goto bad;
557 }
558 /* else pass NULL, which means default header list */
559
560 /* Get private key to use. */
561
562 if (!(dkim_private_key_expanded = expand_string(dkim->dkim_private_key)))
563 {
564 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
565 "dkim_private_key: %s", expand_string_message);
566 goto bad;
567 }
568
569 if ( Ustrlen(dkim_private_key_expanded) == 0
570 || Ustrcmp(dkim_private_key_expanded, "0") == 0
571 || Ustrcmp(dkim_private_key_expanded, "false") == 0
572 )
573 continue; /* don't sign, but no error */
574
575 if (dkim_private_key_expanded[0] == '/')
576 {
577 int privkey_fd, off = 0, len;
578
579 /* Looks like a filename, load the private key. */
580
581 memset(big_buffer, 0, big_buffer_size);
582
583 if ((privkey_fd = open(CS dkim_private_key_expanded, O_RDONLY)) < 0)
584 {
585 log_write(0, LOG_MAIN | LOG_PANIC, "unable to open "
586 "private key file for reading: %s",
587 dkim_private_key_expanded);
588 goto bad;
589 }
590
591 do
592 {
593 if ((len = read(privkey_fd, big_buffer + off, big_buffer_size - 2 - off)) < 0)
594 {
595 (void) close(privkey_fd);
596 log_write(0, LOG_MAIN|LOG_PANIC, "unable to read private key file: %s",
597 dkim_private_key_expanded);
598 goto bad;
599 }
600 off += len;
601 }
602 while (len > 0);
603
604 (void) close(privkey_fd);
605 big_buffer[off] = '\0';
606 dkim_private_key_expanded = big_buffer;
607 }
608
609 if (!(ctx = pdkim_init_sign(CS dkim_signing_domain,
610 CS dkim_signing_selector,
611 CS dkim_private_key_expanded,
612 PDKIM_ALGO_RSA_SHA256,
613 dkim->dot_stuffed,
614 &dkim_exim_query_dns_txt,
615 errstr
616 )))
617 goto bad;
618 dkim_private_key_expanded[0] = '\0';
619 pdkim_set_optional(ctx,
620 CS dkim_sign_headers_expanded,
621 NULL,
622 pdkim_canon,
623 pdkim_canon, -1, 0, 0);
624
625 if (prefix)
626 pdkim_feed(ctx, prefix, Ustrlen(prefix));
627
628 if (lseek(fd, off, SEEK_SET) < 0)
629 sread = -1;
630 else
631 while ((sread = read(fd, &buf, sizeof(buf))) > 0)
632 if ((pdkim_rc = pdkim_feed(ctx, buf, sread)) != PDKIM_OK)
633 goto pk_bad;
634
635 /* Handle failed read above. */
636 if (sread == -1)
637 {
638 debug_printf("DKIM: Error reading -K file.\n");
639 save_errno = errno;
640 goto bad;
641 }
642
643 if ((pdkim_rc = pdkim_feed_finish(ctx, &signature, errstr)) != PDKIM_OK)
644 goto pk_bad;
645
646 sigbuf = string_append(sigbuf, &sigsize, &sigptr, 2,
647 US signature->signature_header, US"\r\n");
648
649 pdkim_free_ctx(ctx);
650 ctx = NULL;
651 }
652
653 if (sigbuf)
654 {
655 sigbuf[sigptr] = '\0';
656 rc = sigbuf;
657 }
658 else
659 rc = US"";
660
661 CLEANUP:
662 if (ctx)
663 pdkim_free_ctx(ctx);
664 store_pool = old_pool;
665 errno = save_errno;
666 return rc;
667
668 pk_bad:
669 log_write(0, LOG_MAIN|LOG_PANIC,
670 "DKIM: signing failed: %.100s", pdkim_errstr(pdkim_rc));
671 bad:
672 rc = NULL;
673 goto CLEANUP;
674 }
675
676 #endif