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