receive with DKIM
[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;
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;
55       }
56     return PDKIM_OK;
57     }
58
59 return PDKIM_FAIL;
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 /* 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 store_pool = POOL_PERM;
102 if (  dkim_collect_input
103    && pdkim_feed(dkim_verify_ctx, (char *)data, len) != PDKIM_OK)
104   dkim_collect_input = FALSE;
105 store_pool = dkim_verify_oldpool;
106 }
107
108
109 void
110 dkim_exim_verify_finish(void)
111 {
112 pdkim_signature *sig = NULL;
113 int dkim_signers_size = 0;
114 int dkim_signers_ptr = 0;
115 dkim_signers = NULL;
116
117 store_pool = POOL_PERM;
118
119 /* Delete eventual previous signature chain */
120
121 dkim_signatures = NULL;
122
123 /* If we have arrived here with dkim_collect_input == FALSE, it
124 means there was a processing error somewhere along the way.
125 Log the incident and disable futher verification. */
126
127 if (!dkim_collect_input)
128   {
129   log_write(0, LOG_MAIN,
130              "DKIM: Error while running this message through validation,"
131              " disabling signature verification.");
132   dkim_disable_verify = TRUE;
133   goto out;
134   }
135
136 dkim_collect_input = FALSE;
137
138 /* Finish DKIM operation and fetch link to signatures chain */
139
140 if (pdkim_feed_finish(dkim_verify_ctx, &dkim_signatures) != PDKIM_OK)
141   goto out;
142
143 for (sig = dkim_signatures; sig; sig = sig->next)
144   {
145   int size = 0;
146   int ptr = 0;
147
148   /* Log a line for each signature */
149
150   uschar *logmsg = string_append(NULL, &size, &ptr, 5,
151         string_sprintf("d=%s s=%s c=%s/%s a=%s b=%d ",
152               sig->domain,
153               sig->selector,
154               sig->canon_headers == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
155               sig->canon_body == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
156               sig->algo == PDKIM_ALGO_RSA_SHA256
157               ? "rsa-sha256"
158               : sig->algo == PDKIM_ALGO_RSA_SHA1 ? "rsa-sha1" : "err",
159               (int)sig->sigdata.len > -1 ? sig->sigdata.len * 8 : 0
160               ),
161
162         sig->identity ? string_sprintf("i=%s ", sig->identity) : US"",
163         sig->created > 0 ? string_sprintf("t=%lu ", sig->created) : US"",
164         sig->expires > 0 ? string_sprintf("x=%lu ", sig->expires) : US"",
165         sig->bodylength > -1 ? string_sprintf("l=%lu ", sig->bodylength) : US""
166         );
167
168   switch (sig->verify_status)
169     {
170     case PDKIM_VERIFY_NONE:
171       logmsg = string_append(logmsg, &size, &ptr, 1, "[not verified]");
172       break;
173
174     case PDKIM_VERIFY_INVALID:
175       logmsg = string_append(logmsg, &size, &ptr, 1, "[invalid - ");
176       switch (sig->verify_ext_status)
177         {
178         case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
179           logmsg = string_append(logmsg, &size, &ptr, 1,
180                         "public key record (currently?) unavailable]");
181           break;
182
183         case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
184           logmsg = string_append(logmsg, &size, &ptr, 1,
185                         "overlong public key record]");
186           break;
187
188         case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:
189         case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT:
190           logmsg = string_append(logmsg, &size, &ptr, 1,
191                        "syntax error in public key record]");
192           break;
193
194         case PDKIM_VERIFY_INVALID_SIGNATURE_ERROR:
195           logmsg = string_append(logmsg, &size, &ptr, 1,
196                        "signature tag missing or invalid]");
197           break;
198
199         case PDKIM_VERIFY_INVALID_DKIM_VERSION:
200           logmsg = string_append(logmsg, &size, &ptr, 1,
201                        "unsupported DKIM version]");
202           break;
203
204         default:
205           logmsg = string_append(logmsg, &size, &ptr, 1,
206                         "unspecified problem]");
207         }
208       break;
209
210     case PDKIM_VERIFY_FAIL:
211       logmsg =
212         string_append(logmsg, &size, &ptr, 1, "[verification failed - ");
213       switch (sig->verify_ext_status)
214         {
215         case PDKIM_VERIFY_FAIL_BODY:
216           logmsg = string_append(logmsg, &size, &ptr, 1,
217                        "body hash mismatch (body probably modified in transit)]");
218           break;
219
220         case PDKIM_VERIFY_FAIL_MESSAGE:
221           logmsg = string_append(logmsg, &size, &ptr, 1,
222                        "signature did not verify (headers probably modified in transit)]");
223         break;
224
225         default:
226           logmsg = string_append(logmsg, &size, &ptr, 1, "unspecified reason]");
227         }
228       break;
229
230     case PDKIM_VERIFY_PASS:
231       logmsg =
232         string_append(logmsg, &size, &ptr, 1, "[verification succeeded]");
233       break;
234     }
235
236   logmsg[ptr] = '\0';
237   log_write(0, LOG_MAIN, "DKIM: %s", logmsg);
238
239   /* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */
240
241   dkim_signers = string_append(dkim_signers,
242                                 &dkim_signers_size,
243                                 &dkim_signers_ptr, 2, sig->domain, ":");
244
245   if (sig->identity)
246     dkim_signers = string_append(dkim_signers,
247                                   &dkim_signers_size,
248                                   &dkim_signers_ptr, 2, sig->identity, ":");
249
250   /* Process next signature */
251   }
252
253 /* NULL-terminate and chop the last colon from the domain list */
254
255 if (dkim_signers)
256   {
257   dkim_signers[dkim_signers_ptr] = '\0';
258   if (Ustrlen(dkim_signers) > 0)
259   dkim_signers[Ustrlen(dkim_signers) - 1] = '\0';
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->sigdata.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, uschar * dkim_private_key,
453                 const uschar * dkim_domain, uschar * dkim_selector,
454                 uschar * dkim_canon, uschar * dkim_sign_headers)
455 {
456 int sep = 0;
457 uschar *seen_items = NULL;
458 int seen_items_size = 0;
459 int seen_items_offset = 0;
460 uschar itembuf[256];
461 uschar *dkim_canon_expanded;
462 uschar *dkim_sign_headers_expanded;
463 uschar *dkim_private_key_expanded;
464 pdkim_ctx *ctx = NULL;
465 uschar *rc = NULL;
466 uschar *sigbuf = NULL;
467 int sigsize = 0;
468 int sigptr = 0;
469 pdkim_signature *signature;
470 int pdkim_canon;
471 int pdkim_rc;
472 int sread;
473 char buf[4096];
474 int save_errno = 0;
475 int old_pool = store_pool;
476
477 store_pool = POOL_MAIN;
478
479 if (!(dkim_domain = expand_cstring(dkim_domain)))
480   {
481   /* expansion error, do not send message. */
482   log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
483              "dkim_domain: %s", expand_string_message);
484   rc = NULL;
485   goto CLEANUP;
486   }
487
488 /* Set $dkim_domain expansion variable to each unique domain in list. */
489
490 while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep,
491                                    itembuf, sizeof(itembuf))))
492   {
493   if (!dkim_signing_domain || dkim_signing_domain[0] == '\0')
494     continue;
495
496   /* Only sign once for each domain, no matter how often it
497   appears in the expanded list. */
498
499   if (seen_items)
500     {
501     const uschar *seen_items_list = seen_items;
502     if (match_isinlist(dkim_signing_domain,
503                         &seen_items_list, 0, NULL, NULL, MCL_STRING, TRUE,
504                         NULL) == OK)
505       continue;
506
507     seen_items =
508       string_append(seen_items, &seen_items_size, &seen_items_offset, 1, ":");
509     }
510
511   seen_items =
512     string_append(seen_items, &seen_items_size, &seen_items_offset, 1,
513                  dkim_signing_domain);
514   seen_items[seen_items_offset] = '\0';
515
516   /* Set up $dkim_selector expansion variable. */
517
518   if (!(dkim_signing_selector = expand_string(dkim_selector)))
519     {
520     log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
521                "dkim_selector: %s", expand_string_message);
522     rc = NULL;
523     goto CLEANUP;
524     }
525
526   /* Get canonicalization to use */
527
528   dkim_canon_expanded = dkim_canon ? expand_string(dkim_canon) : US"relaxed";
529   if (!dkim_canon_expanded)
530     {
531     /* expansion error, do not send message. */
532     log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
533                "dkim_canon: %s", expand_string_message);
534     rc = NULL;
535     goto CLEANUP;
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_sign_headers)
552     if (!(dkim_sign_headers_expanded = expand_string(dkim_sign_headers)))
553       {
554       log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
555                  "dkim_sign_headers: %s", expand_string_message);
556       rc = NULL;
557       goto CLEANUP;
558       }
559                         /* else pass NULL, which means default header list */
560
561   /* Get private key to use. */
562
563   if (!(dkim_private_key_expanded = expand_string(dkim_private_key)))
564     {
565     log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
566                "dkim_private_key: %s", expand_string_message);
567     rc = NULL;
568     goto CLEANUP;
569     }
570
571   if (  Ustrlen(dkim_private_key_expanded) == 0
572      || Ustrcmp(dkim_private_key_expanded, "0") == 0
573      || Ustrcmp(dkim_private_key_expanded, "false") == 0
574      )
575     continue;           /* don't sign, but no error */
576
577   if (dkim_private_key_expanded[0] == '/')
578     {
579     int privkey_fd = 0;
580
581     /* Looks like a filename, load the private key. */
582
583     memset(big_buffer, 0, big_buffer_size);
584     privkey_fd = open(CS dkim_private_key_expanded, O_RDONLY);
585     if (privkey_fd < 0)
586       {
587       log_write(0, LOG_MAIN | LOG_PANIC, "unable to open "
588                  "private key file for reading: %s",
589                  dkim_private_key_expanded);
590       rc = NULL;
591       goto CLEANUP;
592       }
593
594     if (read(privkey_fd, big_buffer, big_buffer_size - 2) < 0)
595       {
596       log_write(0, LOG_MAIN|LOG_PANIC, "unable to read private key file: %s",
597                  dkim_private_key_expanded);
598       rc = NULL;
599       goto CLEANUP;
600       }
601
602     (void) close(privkey_fd);
603     dkim_private_key_expanded = big_buffer;
604     }
605
606   ctx = pdkim_init_sign( (char *) dkim_signing_domain,
607                          (char *) dkim_signing_selector,
608                          (char *) dkim_private_key_expanded,
609                          PDKIM_ALGO_RSA_SHA256);
610   pdkim_set_optional(ctx,
611                       (char *) dkim_sign_headers_expanded,
612                       NULL,
613                       pdkim_canon,
614                       pdkim_canon, -1, 0, 0);
615
616   lseek(dkim_fd, 0, SEEK_SET);
617
618   while ((sread = read(dkim_fd, &buf, 4096)) > 0)
619     if (pdkim_feed(ctx, buf, sread) != PDKIM_OK)
620       {
621       rc = NULL;
622       goto CLEANUP;
623       }
624
625   /* Handle failed read above. */
626   if (sread == -1)
627     {
628     debug_printf("DKIM: Error reading -K file.\n");
629     save_errno = errno;
630     rc = NULL;
631     goto CLEANUP;
632     }
633
634   if ((pdkim_rc = pdkim_feed_finish(ctx, &signature)) != PDKIM_OK)
635     {
636     log_write(0, LOG_MAIN|LOG_PANIC, "DKIM: signing failed (RC %d)", pdkim_rc);
637     rc = NULL;
638     goto CLEANUP;
639     }
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
664 #endif