DKIM: more care over untrustworthy data during verify
[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 BOOL dkim_collect_error = FALSE;
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 = FALSE;
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   log_write(0, LOG_MAIN,
110              "DKIM: validation error: %.100s", pdkim_errstr(rc));
111   dkim_collect_error = TRUE;
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;
123 int dkim_signers_ptr = 0;
124 int rc;
125
126 store_pool = POOL_PERM;
127
128 /* Delete eventual previous signature chain */
129
130 dkim_signers = NULL;
131 dkim_signatures = NULL;
132
133 if (dkim_collect_error)
134   {
135   log_write(0, LOG_MAIN,
136              "DKIM: Error while running this message through validation,"
137              " disabling signature verification.");
138   dkim_disable_verify = TRUE;
139   goto out;
140   }
141
142 dkim_collect_input = FALSE;
143
144 /* Finish DKIM operation and fetch link to signatures chain */
145
146 if ((rc = pdkim_feed_finish(dkim_verify_ctx, &dkim_signatures)) != PDKIM_OK)
147   {
148   log_write(0, LOG_MAIN,
149              "DKIM: validation error: %.100s", pdkim_errstr(rc));
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 uschar *
452 dkim_exim_sign(int dkim_fd, struct ob_dkim * dkim)
453 {
454 const uschar * dkim_domain;
455 int sep = 0;
456 uschar *seen_items = NULL;
457 int seen_items_size = 0;
458 int seen_items_offset = 0;
459 uschar itembuf[256];
460 uschar *dkim_canon_expanded;
461 uschar *dkim_sign_headers_expanded;
462 uschar *dkim_private_key_expanded;
463 pdkim_ctx *ctx = NULL;
464 uschar *rc = NULL;
465 uschar *sigbuf = NULL;
466 int sigsize = 0;
467 int sigptr = 0;
468 pdkim_signature *signature;
469 int pdkim_canon;
470 int pdkim_rc;
471 int sread;
472 char buf[4096];
473 int save_errno = 0;
474 int old_pool = store_pool;
475
476 store_pool = POOL_MAIN;
477
478 if (!(dkim_domain = expand_cstring(dkim->dkim_domain)))
479   {
480   /* expansion error, do not send message. */
481   log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
482              "dkim_domain: %s", expand_string_message);
483   goto bad;
484   }
485
486 /* Set $dkim_domain expansion variable to each unique domain in list. */
487
488 while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep,
489                                    itembuf, sizeof(itembuf))))
490   {
491   if (!dkim_signing_domain || dkim_signing_domain[0] == '\0')
492     continue;
493
494   /* Only sign once for each domain, no matter how often it
495   appears in the expanded list. */
496
497   if (seen_items)
498     {
499     const uschar *seen_items_list = seen_items;
500     if (match_isinlist(dkim_signing_domain,
501                         &seen_items_list, 0, NULL, NULL, MCL_STRING, TRUE,
502                         NULL) == OK)
503       continue;
504
505     seen_items =
506       string_append(seen_items, &seen_items_size, &seen_items_offset, 1, ":");
507     }
508
509   seen_items =
510     string_append(seen_items, &seen_items_size, &seen_items_offset, 1,
511                  dkim_signing_domain);
512   seen_items[seen_items_offset] = '\0';
513
514   /* Set up $dkim_selector expansion variable. */
515
516   if (!(dkim_signing_selector = expand_string(dkim->dkim_selector)))
517     {
518     log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
519                "dkim_selector: %s", expand_string_message);
520     goto bad;
521     }
522
523   /* Get canonicalization to use */
524
525   dkim_canon_expanded = dkim->dkim_canon
526     ? expand_string(dkim->dkim_canon) : US"relaxed";
527   if (!dkim_canon_expanded)
528     {
529     /* expansion error, do not send message. */
530     log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
531                "dkim_canon: %s", expand_string_message);
532     goto bad;
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->dkim_sign_headers)
549     if (!(dkim_sign_headers_expanded = expand_string(dkim->dkim_sign_headers)))
550       {
551       log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
552                  "dkim_sign_headers: %s", expand_string_message);
553       goto bad;
554       }
555                         /* else pass NULL, which means default header list */
556
557   /* Get private key to use. */
558
559   if (!(dkim_private_key_expanded = expand_string(dkim->dkim_private_key)))
560     {
561     log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
562                "dkim_private_key: %s", expand_string_message);
563     goto bad;
564     }
565
566   if (  Ustrlen(dkim_private_key_expanded) == 0
567      || Ustrcmp(dkim_private_key_expanded, "0") == 0
568      || Ustrcmp(dkim_private_key_expanded, "false") == 0
569      )
570     continue;           /* don't sign, but no error */
571
572   if (dkim_private_key_expanded[0] == '/')
573     {
574     int privkey_fd = 0;
575
576     /* Looks like a filename, load the private key. */
577
578     memset(big_buffer, 0, big_buffer_size);
579
580     if ((privkey_fd = open(CS dkim_private_key_expanded, O_RDONLY)) < 0)
581       {
582       log_write(0, LOG_MAIN | LOG_PANIC, "unable to open "
583                  "private key file for reading: %s",
584                  dkim_private_key_expanded);
585       goto bad;
586       }
587
588     if (read(privkey_fd, big_buffer, big_buffer_size - 2) < 0)
589       {
590       log_write(0, LOG_MAIN|LOG_PANIC, "unable to read private key file: %s",
591                  dkim_private_key_expanded);
592       goto bad;
593       }
594
595     (void) close(privkey_fd);
596     dkim_private_key_expanded = big_buffer;
597     }
598
599   ctx = pdkim_init_sign(CS dkim_signing_domain,
600                         CS dkim_signing_selector,
601                         CS dkim_private_key_expanded,
602                         PDKIM_ALGO_RSA_SHA256,
603                         dkim->dot_stuffed,
604                         &dkim_exim_query_dns_txt
605                         );
606   dkim_private_key_expanded[0] = '\0';
607   pdkim_set_optional(ctx,
608                       CS 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, sizeof(buf))) > 0)
616     if ((pdkim_rc = pdkim_feed(ctx, buf, sread)) != PDKIM_OK)
617       goto pk_bad;
618
619   /* Handle failed read above. */
620   if (sread == -1)
621     {
622     debug_printf("DKIM: Error reading -K file.\n");
623     save_errno = errno;
624     goto bad;
625     }
626
627   if ((pdkim_rc = pdkim_feed_finish(ctx, &signature)) != PDKIM_OK)
628     goto pk_bad;
629
630   sigbuf = string_append(sigbuf, &sigsize, &sigptr, 2,
631                           US signature->signature_header, US"\r\n");
632
633   pdkim_free_ctx(ctx);
634   ctx = NULL;
635   }
636
637 if (sigbuf)
638   {
639   sigbuf[sigptr] = '\0';
640   rc = sigbuf;
641   }
642 else
643   rc = US"";
644
645 CLEANUP:
646   if (ctx)
647     pdkim_free_ctx(ctx);
648   store_pool = old_pool;
649   errno = save_errno;
650   return rc;
651
652 pk_bad:
653   log_write(0, LOG_MAIN|LOG_PANIC,
654                 "DKIM: signing failed: %.100s", pdkim_errstr(pdkim_rc));
655 bad:
656   rc = NULL;
657   goto CLEANUP;
658 }
659
660 #endif