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