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