Revert "Build: tidying"
[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 # ifdef MACRO_PREDEF
18 #  include "macro_predef.h"
19
20 void
21 dkim_params(void)
22 {
23 builtin_macro_create_var(US"_DKIM_SIGN_HEADERS", US PDKIM_DEFAULT_SIGN_HEADERS);
24 }
25 # else  /*!MACRO_PREDEF*/
26
27
28
29
30 int dkim_verify_oldpool;
31 pdkim_ctx *dkim_verify_ctx = NULL;
32 pdkim_signature *dkim_signatures = NULL;
33 pdkim_signature *dkim_cur_sig = NULL;
34 static const uschar * dkim_collect_error = NULL;
35
36
37
38 /*XXX the caller only uses the first record if we return multiple.
39 Could we hand back an allocated string?
40 */
41
42 static int
43 dkim_exim_query_dns_txt(char *name, char *answer)
44 {
45 dns_answer dnsa;
46 dns_scan dnss;
47 dns_record *rr;
48
49 lookup_dnssec_authenticated = NULL;
50 if (dns_lookup(&dnsa, US name, T_TXT, NULL) != DNS_SUCCEED)
51   return PDKIM_FAIL;    /*XXX better error detail?  logging? */
52
53 /* Search for TXT record */
54
55 for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
56      rr;
57      rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
58   if (rr->type == T_TXT)
59     {
60     int rr_offset = 0;
61     int answer_offset = 0;
62
63     /* Copy record content to the answer buffer */
64
65     while (rr_offset < rr->size)
66       {
67       uschar len = rr->data[rr_offset++];
68       snprintf(answer + answer_offset,
69                 PDKIM_DNS_TXT_MAX_RECLEN - answer_offset,
70                 "%.*s", (int)len, CS  (rr->data + rr_offset));
71       rr_offset += len;
72       answer_offset += len;
73       if (answer_offset >= PDKIM_DNS_TXT_MAX_RECLEN)
74         return PDKIM_FAIL;      /*XXX better error detail?  logging? */
75       }
76     return PDKIM_OK;
77     }
78
79 return PDKIM_FAIL;      /*XXX better error detail?  logging? */
80 }
81
82
83 void
84 dkim_exim_init(void)
85 {
86 pdkim_init();
87 }
88
89
90
91 void
92 dkim_exim_verify_init(BOOL dot_stuffing)
93 {
94 /* There is a store-reset between header & body reception
95 so cannot use the main pool. Any allocs done by Exim
96 memory-handling must use the perm pool. */
97
98 dkim_verify_oldpool = store_pool;
99 store_pool = POOL_PERM;
100
101 /* Free previous context if there is one */
102
103 if (dkim_verify_ctx)
104   pdkim_free_ctx(dkim_verify_ctx);
105
106 /* Create new context */
107
108 dkim_verify_ctx = pdkim_init_verify(&dkim_exim_query_dns_txt, dot_stuffing);
109 dkim_collect_input = !!dkim_verify_ctx;
110 dkim_collect_error = NULL;
111
112 /* Start feed up with any cached data */
113 receive_get_cache();
114
115 store_pool = dkim_verify_oldpool;
116 }
117
118
119 void
120 dkim_exim_verify_feed(uschar * data, int len)
121 {
122 int rc;
123
124 store_pool = POOL_PERM;
125 if (  dkim_collect_input
126    && (rc = pdkim_feed(dkim_verify_ctx, data, len)) != PDKIM_OK)
127   {
128   dkim_collect_error = pdkim_errstr(rc);
129   log_write(0, LOG_MAIN,
130              "DKIM: validation error: %.100s", dkim_collect_error);
131   dkim_collect_input = FALSE;
132   }
133 store_pool = dkim_verify_oldpool;
134 }
135
136
137 void
138 dkim_exim_verify_finish(void)
139 {
140 pdkim_signature * sig = NULL;
141 int rc;
142 gstring * g = NULL;
143 const uschar * errstr;
144
145 store_pool = POOL_PERM;
146
147 /* Delete eventual previous signature chain */
148
149 dkim_signers = NULL;
150 dkim_signatures = NULL;
151
152 if (dkim_collect_error)
153   {
154   log_write(0, LOG_MAIN,
155       "DKIM: Error during validation, disabling signature verification: %.100s",
156       dkim_collect_error);
157   dkim_disable_verify = TRUE;
158   goto out;
159   }
160
161 dkim_collect_input = FALSE;
162
163 /* Finish DKIM operation and fetch link to signatures chain */
164
165 rc = pdkim_feed_finish(dkim_verify_ctx, &dkim_signatures, &errstr);
166 if (rc != PDKIM_OK)
167   {
168   log_write(0, LOG_MAIN, "DKIM: validation error: %.100s%s%s", pdkim_errstr(rc),
169             errstr ? ": " : "", errstr ? errstr : US"");
170   goto out;
171   }
172
173 for (sig = dkim_signatures; sig; sig = sig->next)
174   {
175   uschar * s;
176   gstring * logmsg;
177
178   /* Log a line for each signature */
179
180   if (!(s = sig->domain)) s = US"<UNSET>";
181   logmsg = string_append(NULL, 2, "d=", s);
182   if (!(s = sig->selector)) s = US"<UNSET>";
183   logmsg = string_append(logmsg, 2, " s=", s);
184   logmsg = string_append(logmsg, 7, 
185         " c=", sig->canon_headers == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
186         "/",   sig->canon_body    == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
187         " a=", dkim_sig_to_a_tag(sig),
188         string_sprintf(" b=" SIZE_T_FMT,
189                         (int)sig->sighash.len > -1 ? sig->sighash.len * 8 : 0));
190   if ((s= sig->identity)) logmsg = string_append(logmsg, 2, " i=", s);
191   if (sig->created > 0) logmsg = string_cat(logmsg,
192                                       string_sprintf(" t=%lu", sig->created));
193   if (sig->expires > 0) logmsg = string_cat(logmsg,
194                                       string_sprintf(" x=%lu", sig->expires));
195   if (sig->bodylength > -1) logmsg = string_cat(logmsg,
196                                       string_sprintf(" l=%lu", sig->bodylength));
197
198   switch (sig->verify_status)
199     {
200     case PDKIM_VERIFY_NONE:
201       logmsg = string_cat(logmsg, " [not verified]");
202       break;
203
204     case PDKIM_VERIFY_INVALID:
205       logmsg = string_cat(logmsg, " [invalid - ");
206       switch (sig->verify_ext_status)
207         {
208         case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
209           logmsg = string_cat(logmsg,
210                         "public key record (currently?) unavailable]");
211           break;
212
213         case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
214           logmsg = string_cat(logmsg, "overlong public key record]");
215           break;
216
217         case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:
218         case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT:
219           logmsg = string_cat(logmsg, "syntax error in public key record]");
220           break;
221
222         case PDKIM_VERIFY_INVALID_SIGNATURE_ERROR:
223           logmsg = string_cat(logmsg, "signature tag missing or invalid]");
224           break;
225
226         case PDKIM_VERIFY_INVALID_DKIM_VERSION:
227           logmsg = string_cat(logmsg, "unsupported DKIM version]");
228           break;
229
230         default:
231           logmsg = string_cat(logmsg, "unspecified problem]");
232         }
233       break;
234
235     case PDKIM_VERIFY_FAIL:
236       logmsg =
237         string_cat(logmsg, " [verification failed - ");
238       switch (sig->verify_ext_status)
239         {
240         case PDKIM_VERIFY_FAIL_BODY:
241           logmsg = string_cat(logmsg,
242                        "body hash mismatch (body probably modified in transit)]");
243           break;
244
245         case PDKIM_VERIFY_FAIL_MESSAGE:
246           logmsg = string_cat(logmsg,
247                        "signature did not verify (headers probably modified in transit)]");
248         break;
249
250         default:
251           logmsg = string_cat(logmsg, "unspecified reason]");
252         }
253       break;
254
255     case PDKIM_VERIFY_PASS:
256       logmsg = string_cat(logmsg, " [verification succeeded]");
257       break;
258     }
259
260   log_write(0, LOG_MAIN, "DKIM: %s", string_from_gstring(logmsg));
261
262   /* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */
263
264   if (sig->domain)
265     g = string_append_listele(g, ':', sig->domain);
266
267   if (sig->identity)
268     g = string_append_listele(g, ':', sig->identity);
269
270   /* Process next signature */
271   }
272
273 if (g) dkim_signers = g->s;
274
275 out:
276 store_pool = dkim_verify_oldpool;
277 }
278
279
280 void
281 dkim_exim_acl_setup(uschar * id)
282 {
283 pdkim_signature * sig;
284 uschar * cmp_val;
285
286 dkim_cur_sig = NULL;
287 dkim_cur_signer = id;
288
289 if (dkim_disable_verify || !id || !dkim_verify_ctx)
290   return;
291
292 /* Find signature to run ACL on */
293
294 for (sig = dkim_signatures; sig; sig = sig->next)
295   if (  (cmp_val = Ustrchr(id, '@') != NULL ? US sig->identity : US sig->domain)
296      && strcmpic(cmp_val, id) == 0
297      )
298     {
299     dkim_cur_sig = sig;
300
301     /* The "dkim_domain" and "dkim_selector" expansion variables have
302        related globals, since they are used in the signing code too.
303        Instead of inventing separate names for verification, we set
304        them here. This is easy since a domain and selector is guaranteed
305        to be in a signature. The other dkim_* expansion items are
306        dynamically fetched from dkim_cur_sig at expansion time (see
307        function below). */
308
309     dkim_signing_domain = US sig->domain;
310     dkim_signing_selector = US sig->selector;
311     dkim_key_length = sig->sighash.len * 8;
312     return;
313     }
314 }
315
316
317 static uschar *
318 dkim_exim_expand_defaults(int what)
319 {
320 switch (what)
321   {
322   case DKIM_ALGO:               return US"";
323   case DKIM_BODYLENGTH:         return US"9999999999999";
324   case DKIM_CANON_BODY:         return US"";
325   case DKIM_CANON_HEADERS:      return US"";
326   case DKIM_COPIEDHEADERS:      return US"";
327   case DKIM_CREATED:            return US"0";
328   case DKIM_EXPIRES:            return US"9999999999999";
329   case DKIM_HEADERNAMES:        return US"";
330   case DKIM_IDENTITY:           return US"";
331   case DKIM_KEY_GRANULARITY:    return US"*";
332   case DKIM_KEY_SRVTYPE:        return US"*";
333   case DKIM_KEY_NOTES:          return US"";
334   case DKIM_KEY_TESTING:        return US"0";
335   case DKIM_NOSUBDOMAINS:       return US"0";
336   case DKIM_VERIFY_STATUS:      return US"none";
337   case DKIM_VERIFY_REASON:      return US"";
338   default:                      return US"";
339   }
340 }
341
342
343 uschar *
344 dkim_exim_expand_query(int what)
345 {
346 if (!dkim_verify_ctx || dkim_disable_verify || !dkim_cur_sig)
347   return dkim_exim_expand_defaults(what);
348
349 switch (what)
350   {
351   case DKIM_ALGO:
352     return dkim_sig_to_a_tag(dkim_cur_sig);
353
354   case DKIM_BODYLENGTH:
355     return dkim_cur_sig->bodylength >= 0
356       ? string_sprintf("%ld", 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("%lu", 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("%lu", 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 /* Generate signatures for the given file, returning a string.
460 If a prefix is given, prepend it to the file for the calculations.
461 */
462
463 gstring *
464 dkim_exim_sign(int fd, off_t off, uschar * prefix,
465   struct ob_dkim * dkim, const uschar ** errstr)
466 {
467 const uschar * dkim_domain;
468 int sep = 0;
469 gstring * seen_doms = NULL;
470 pdkim_ctx ctx;
471 pdkim_signature * sig;
472 gstring * sigbuf;
473 int pdkim_rc;
474 int sread;
475 uschar buf[4096];
476 int save_errno = 0;
477 int old_pool = store_pool;
478 uschar * errwhen;
479
480 store_pool = POOL_MAIN;
481
482 pdkim_init_context(&ctx, dkim->dot_stuffed, &dkim_exim_query_dns_txt);
483
484 if (!(dkim_domain = expand_cstring(dkim->dkim_domain)))
485   /* expansion error, do not send message. */
486   { errwhen = US"dkim_domain"; goto expand_bad; }
487
488 /* Set $dkim_domain expansion variable to each unique domain in list. */
489
490 while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep, NULL, 0)))
491   {
492   const uschar * dkim_sel;
493   int sel_sep = 0;
494
495   if (dkim_signing_domain[0] == '\0')
496     continue;
497
498   /* Only sign once for each domain, no matter how often it
499   appears in the expanded list. */
500
501   if (match_isinlist(dkim_signing_domain, CUSS &seen_doms,
502       0, NULL, NULL, MCL_STRING, TRUE, NULL) == OK)
503     continue;
504
505   seen_doms = string_append_listele(seen_doms, ':', dkim_signing_domain);
506
507   /* Set $dkim_selector expansion variable to each selector in list,
508   for this domain. */
509
510   if (!(dkim_sel = expand_string(dkim->dkim_selector)))
511   if (!(dkim_signing_selector = expand_string(dkim->dkim_selector)))
512     { errwhen = US"dkim_selector"; goto expand_bad; }
513
514   while ((dkim_signing_selector = string_nextinlist(&dkim_sel, &sel_sep,
515           NULL, 0)))
516     {
517     uschar * dkim_canon_expanded;
518     int pdkim_canon;
519     uschar * dkim_sign_headers_expanded = NULL;
520     uschar * dkim_private_key_expanded;
521     uschar * dkim_hash_expanded;
522     uschar * dkim_identity_expanded = NULL;
523
524     /* Get canonicalization to use */
525
526     dkim_canon_expanded = dkim->dkim_canon
527       ? expand_string(dkim->dkim_canon) : US"relaxed";
528     if (!dkim_canon_expanded)   /* expansion error, do not send message. */
529       { errwhen = US"dkim_canon"; goto expand_bad; }
530
531     if (Ustrcmp(dkim_canon_expanded, "relaxed") == 0)
532       pdkim_canon = PDKIM_CANON_RELAXED;
533     else if (Ustrcmp(dkim_canon_expanded, "simple") == 0)
534       pdkim_canon = PDKIM_CANON_SIMPLE;
535     else
536       {
537       log_write(0, LOG_MAIN,
538                  "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",
539                  dkim_canon_expanded);
540       pdkim_canon = PDKIM_CANON_RELAXED;
541       }
542
543     if (  dkim->dkim_sign_headers
544        && !(dkim_sign_headers_expanded = expand_string(dkim->dkim_sign_headers)))
545       { errwhen = US"dkim_sign_header"; goto expand_bad; }
546     /* else pass NULL, which means default header list */
547
548     /* Get private key to use. */
549
550     if (!(dkim_private_key_expanded = expand_string(dkim->dkim_private_key)))
551       { errwhen = US"dkim_private_key"; goto expand_bad; }
552
553     if (  Ustrlen(dkim_private_key_expanded) == 0
554        || Ustrcmp(dkim_private_key_expanded, "0") == 0
555        || Ustrcmp(dkim_private_key_expanded, "false") == 0
556        )
557       continue;         /* don't sign, but no error */
558
559     if (dkim_private_key_expanded[0] == '/')
560       {
561       int privkey_fd, off = 0, len;
562
563       /* Looks like a filename, load the private key. */
564
565       memset(big_buffer, 0, big_buffer_size);
566
567       if ((privkey_fd = open(CS dkim_private_key_expanded, O_RDONLY)) < 0)
568         {
569         log_write(0, LOG_MAIN | LOG_PANIC, "unable to open "
570                    "private key file for reading: %s",
571                    dkim_private_key_expanded);
572         goto bad;
573         }
574
575       do
576         {
577         if ((len = read(privkey_fd, big_buffer + off, big_buffer_size - 2 - off)) < 0)
578           {
579           (void) close(privkey_fd);
580           log_write(0, LOG_MAIN|LOG_PANIC, "unable to read private key file: %s",
581                      dkim_private_key_expanded);
582           goto bad;
583           }
584         off += len;
585         }
586       while (len > 0);
587
588       (void) close(privkey_fd);
589       big_buffer[off] = '\0';
590       dkim_private_key_expanded = big_buffer;
591       }
592
593     if (!(dkim_hash_expanded = expand_string(dkim->dkim_hash)))
594       { errwhen = US"dkim_hash"; goto expand_bad; }
595
596     if (dkim->dkim_identity)
597       if (!(dkim_identity_expanded = expand_string(dkim->dkim_identity)))
598         { errwhen = US"dkim_identity"; goto expand_bad; }
599       else if (!*dkim_identity_expanded)
600         dkim_identity_expanded = NULL;
601
602   /*XXX so we currently nail signing to RSA + this hash.
603   Need to extract algo from privkey and check for disallowed combos. */
604
605     if (!(sig = pdkim_init_sign(&ctx, dkim_signing_domain,
606                           dkim_signing_selector,
607                           dkim_private_key_expanded,
608                           dkim_hash_expanded,
609                           errstr
610                           )))
611       goto bad;
612     dkim_private_key_expanded[0] = '\0';
613
614     pdkim_set_optional(sig,
615                         CS dkim_sign_headers_expanded,
616                         dkim_identity_expanded,
617                         pdkim_canon,
618                         pdkim_canon, -1, 0, 0);
619
620     if (!ctx.sig)               /* link sig to context chain */
621       ctx.sig = sig;
622     else
623       {
624       pdkim_signature * n = ctx.sig;
625       while (n->next) n = n->next;
626       n->next = sig;
627       }
628     }
629   }
630
631 if (prefix)
632   pdkim_feed(&ctx, prefix, Ustrlen(prefix));
633
634 if (lseek(fd, off, SEEK_SET) < 0)
635   sread = -1;
636 else
637   while ((sread = read(fd, &buf, sizeof(buf))) > 0)
638     if ((pdkim_rc = pdkim_feed(&ctx, buf, sread)) != PDKIM_OK)
639       goto pk_bad;
640
641 /* Handle failed read above. */
642 if (sread == -1)
643   {
644   debug_printf("DKIM: Error reading -K file.\n");
645   save_errno = errno;
646   goto bad;
647   }
648
649 /* Build string of headers, one per signature */
650
651 if ((pdkim_rc = pdkim_feed_finish(&ctx, &sig, errstr)) != PDKIM_OK)
652   goto pk_bad;
653
654 for (sigbuf = NULL; sig; sig = sig->next)
655   sigbuf = string_append(sigbuf, 2, US sig->signature_header, US"\r\n");
656
657 (void) string_from_gstring(sigbuf);
658
659 CLEANUP:
660   store_pool = old_pool;
661   errno = save_errno;
662   return sigbuf;
663
664 pk_bad:
665   log_write(0, LOG_MAIN|LOG_PANIC,
666                 "DKIM: signing failed: %.100s", pdkim_errstr(pdkim_rc));
667 bad:
668   sigbuf = NULL;
669   goto CLEANUP;
670
671 expand_bad:
672   log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand %s: %s",
673               errwhen, expand_string_message);
674   goto bad;
675 }
676
677 # endif /*!MACRO_PREDEF*/
678 #endif  /*!DISABLE_DKIM*/