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