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