String handling: refactor the expanding-string routines and users to use a descriptor...
[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 const uschar * dkim_collect_error = NULL;
22
23
24
25 /*XXX the caller only uses the first record if we return multiple.
26 Could we hand back an allocated string?
27 */
28
29 static int
30 dkim_exim_query_dns_txt(char *name, char *answer)
31 {
32 dns_answer dnsa;
33 dns_scan dnss;
34 dns_record *rr;
35
36 lookup_dnssec_authenticated = NULL;
37 if (dns_lookup(&dnsa, US name, T_TXT, NULL) != DNS_SUCCEED)
38   return PDKIM_FAIL;    /*XXX better error detail?  logging? */
39
40 /* Search for TXT record */
41
42 for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
43      rr;
44      rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
45   if (rr->type == T_TXT)
46     {
47     int rr_offset = 0;
48     int answer_offset = 0;
49
50     /* Copy record content to the answer buffer */
51
52     while (rr_offset < rr->size)
53       {
54       uschar len = rr->data[rr_offset++];
55       snprintf(answer + answer_offset,
56                 PDKIM_DNS_TXT_MAX_RECLEN - answer_offset,
57                 "%.*s", (int)len, CS  (rr->data + rr_offset));
58       rr_offset += len;
59       answer_offset += len;
60       if (answer_offset >= PDKIM_DNS_TXT_MAX_RECLEN)
61         return PDKIM_FAIL;      /*XXX better error detail?  logging? */
62       }
63     return PDKIM_OK;
64     }
65
66 return PDKIM_FAIL;      /*XXX better error detail?  logging? */
67 }
68
69
70 void
71 dkim_exim_init(void)
72 {
73 pdkim_init();
74 }
75
76
77
78 void
79 dkim_exim_verify_init(BOOL dot_stuffing)
80 {
81 /* There is a store-reset between header & body reception
82 so cannot use the main pool. Any allocs done by Exim
83 memory-handling must use the perm pool. */
84
85 dkim_verify_oldpool = store_pool;
86 store_pool = POOL_PERM;
87
88 /* Free previous context if there is one */
89
90 if (dkim_verify_ctx)
91   pdkim_free_ctx(dkim_verify_ctx);
92
93 /* Create new context */
94
95 dkim_verify_ctx = pdkim_init_verify(&dkim_exim_query_dns_txt, dot_stuffing);
96 dkim_collect_input = !!dkim_verify_ctx;
97 dkim_collect_error = NULL;
98
99 /* Start feed up with any cached data */
100 receive_get_cache();
101
102 store_pool = dkim_verify_oldpool;
103 }
104
105
106 void
107 dkim_exim_verify_feed(uschar * data, int len)
108 {
109 int rc;
110
111 store_pool = POOL_PERM;
112 if (  dkim_collect_input
113    && (rc = pdkim_feed(dkim_verify_ctx, data, len)) != PDKIM_OK)
114   {
115   dkim_collect_error = pdkim_errstr(rc);
116   log_write(0, LOG_MAIN,
117              "DKIM: validation error: %.100s", dkim_collect_error);
118   dkim_collect_input = FALSE;
119   }
120 store_pool = dkim_verify_oldpool;
121 }
122
123
124 void
125 dkim_exim_verify_finish(void)
126 {
127 pdkim_signature * sig = NULL;
128 int dkim_signers_size = 0, dkim_signers_ptr = 0, rc;
129 gstring * g = NULL;
130 const uschar * errstr;
131
132 store_pool = POOL_PERM;
133
134 /* Delete eventual previous signature chain */
135
136 dkim_signers = NULL;
137 dkim_signatures = NULL;
138
139 if (dkim_collect_error)
140   {
141   log_write(0, LOG_MAIN,
142       "DKIM: Error during validation, disabling signature verification: %.100s",
143       dkim_collect_error);
144   dkim_disable_verify = TRUE;
145   goto out;
146   }
147
148 dkim_collect_input = FALSE;
149
150 /* Finish DKIM operation and fetch link to signatures chain */
151
152 rc = pdkim_feed_finish(dkim_verify_ctx, &dkim_signatures, &errstr);
153 if (rc != PDKIM_OK)
154   {
155   log_write(0, LOG_MAIN, "DKIM: validation error: %.100s%s%s", pdkim_errstr(rc),
156             errstr ? ": " : "", errstr ? errstr : US"");
157   goto out;
158   }
159
160 for (sig = dkim_signatures; sig; sig = sig->next)
161   {
162   uschar * s;
163   gstring * logmsg;
164
165   /* Log a line for each signature */
166
167   if (!(s = sig->domain)) s = US"<UNSET>";
168   logmsg = string_append(NULL, 2, "d=", s);
169   if (!(s = sig->selector)) s = US"<UNSET>";
170   logmsg = string_append(logmsg, 2, " s=", s);
171   logmsg = string_append(logmsg, 7, 
172         " c=", sig->canon_headers == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
173         "/",   sig->canon_body    == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
174         " a=", dkim_sig_to_a_tag(sig),
175         string_sprintf(" b=%d",
176                         (int)sig->sighash.len > -1 ? sig->sighash.len * 8 : 0));
177   if ((s= sig->identity)) logmsg = string_append(logmsg, 2, " i=", s);
178   if (sig->created > 0) logmsg = string_cat(logmsg,
179                                       string_sprintf(" t=%lu", sig->created));
180   if (sig->expires > 0) logmsg = string_cat(logmsg,
181                                       string_sprintf(" x=%lu", sig->expires));
182   if (sig->bodylength > -1) logmsg = string_cat(logmsg,
183                                       string_sprintf(" l=%lu", sig->bodylength));
184
185   switch (sig->verify_status)
186     {
187     case PDKIM_VERIFY_NONE:
188       logmsg = string_cat(logmsg, " [not verified]");
189       break;
190
191     case PDKIM_VERIFY_INVALID:
192       logmsg = string_cat(logmsg, " [invalid - ");
193       switch (sig->verify_ext_status)
194         {
195         case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
196           logmsg = string_cat(logmsg,
197                         "public key record (currently?) unavailable]");
198           break;
199
200         case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
201           logmsg = string_cat(logmsg, "overlong public key record]");
202           break;
203
204         case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:
205         case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT:
206           logmsg = string_cat(logmsg, "syntax error in public key record]");
207           break;
208
209         case PDKIM_VERIFY_INVALID_SIGNATURE_ERROR:
210           logmsg = string_cat(logmsg, "signature tag missing or invalid]");
211           break;
212
213         case PDKIM_VERIFY_INVALID_DKIM_VERSION:
214           logmsg = string_cat(logmsg, "unsupported DKIM version]");
215           break;
216
217         default:
218           logmsg = string_cat(logmsg, "unspecified problem]");
219         }
220       break;
221
222     case PDKIM_VERIFY_FAIL:
223       logmsg =
224         string_cat(logmsg, " [verification failed - ");
225       switch (sig->verify_ext_status)
226         {
227         case PDKIM_VERIFY_FAIL_BODY:
228           logmsg = string_cat(logmsg,
229                        "body hash mismatch (body probably modified in transit)]");
230           break;
231
232         case PDKIM_VERIFY_FAIL_MESSAGE:
233           logmsg = string_cat(logmsg,
234                        "signature did not verify (headers probably modified in transit)]");
235         break;
236
237         default:
238           logmsg = string_cat(logmsg, "unspecified reason]");
239         }
240       break;
241
242     case PDKIM_VERIFY_PASS:
243       logmsg = string_cat(logmsg, " [verification succeeded]");
244       break;
245     }
246
247   log_write(0, LOG_MAIN, "DKIM: %s", string_from_gstring(logmsg));
248
249   /* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */
250
251   if (sig->domain)
252     g = string_append_listele(g, ':', sig->domain);
253
254   if (sig->identity)
255     g = string_append_listele(g, ':', sig->identity);
256
257   /* Process next signature */
258   }
259
260 if (g) dkim_signers = g->s;
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->sighash.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     return dkim_sig_to_a_tag(dkim_cur_sig);
340
341   case DKIM_BODYLENGTH:
342     return dkim_cur_sig->bodylength >= 0
343       ? string_sprintf(OFF_T_FMT, (LONGLONG_T) dkim_cur_sig->bodylength)
344       : dkim_exim_expand_defaults(what);
345
346   case DKIM_CANON_BODY:
347     switch (dkim_cur_sig->canon_body)
348       {
349       case PDKIM_CANON_RELAXED: return US"relaxed";
350       case PDKIM_CANON_SIMPLE:
351       default:                  return US"simple";
352       }
353
354   case DKIM_CANON_HEADERS:
355   switch (dkim_cur_sig->canon_headers)
356     {
357     case PDKIM_CANON_RELAXED:   return US"relaxed";
358     case PDKIM_CANON_SIMPLE:
359     default:                    return US"simple";
360     }
361
362   case DKIM_COPIEDHEADERS:
363     return dkim_cur_sig->copiedheaders
364       ? US dkim_cur_sig->copiedheaders : dkim_exim_expand_defaults(what);
365
366   case DKIM_CREATED:
367     return dkim_cur_sig->created > 0
368       ? string_sprintf("%llu", dkim_cur_sig->created)
369       : dkim_exim_expand_defaults(what);
370
371   case DKIM_EXPIRES:
372     return dkim_cur_sig->expires > 0
373       ? string_sprintf("%llu", dkim_cur_sig->expires)
374       : dkim_exim_expand_defaults(what);
375
376   case DKIM_HEADERNAMES:
377     return dkim_cur_sig->headernames
378       ? dkim_cur_sig->headernames : dkim_exim_expand_defaults(what);
379
380   case DKIM_IDENTITY:
381     return dkim_cur_sig->identity
382       ? US dkim_cur_sig->identity : dkim_exim_expand_defaults(what);
383
384   case DKIM_KEY_GRANULARITY:
385     return dkim_cur_sig->pubkey
386       ? dkim_cur_sig->pubkey->granularity
387       ? US dkim_cur_sig->pubkey->granularity
388       : dkim_exim_expand_defaults(what)
389       : dkim_exim_expand_defaults(what);
390
391   case DKIM_KEY_SRVTYPE:
392     return dkim_cur_sig->pubkey
393       ? dkim_cur_sig->pubkey->srvtype
394       ? US dkim_cur_sig->pubkey->srvtype
395       : dkim_exim_expand_defaults(what)
396       : dkim_exim_expand_defaults(what);
397
398   case DKIM_KEY_NOTES:
399     return dkim_cur_sig->pubkey
400       ? dkim_cur_sig->pubkey->notes
401       ? US dkim_cur_sig->pubkey->notes
402       : dkim_exim_expand_defaults(what)
403       : dkim_exim_expand_defaults(what);
404
405   case DKIM_KEY_TESTING:
406     return dkim_cur_sig->pubkey
407       ? dkim_cur_sig->pubkey->testing
408       ? US"1"
409       : dkim_exim_expand_defaults(what)
410       : dkim_exim_expand_defaults(what);
411
412   case DKIM_NOSUBDOMAINS:
413     return dkim_cur_sig->pubkey
414       ? dkim_cur_sig->pubkey->no_subdomaining
415       ? US"1"
416       : dkim_exim_expand_defaults(what)
417       : dkim_exim_expand_defaults(what);
418
419   case DKIM_VERIFY_STATUS:
420     switch (dkim_cur_sig->verify_status)
421       {
422       case PDKIM_VERIFY_INVALID:        return US"invalid";
423       case PDKIM_VERIFY_FAIL:           return US"fail";
424       case PDKIM_VERIFY_PASS:           return US"pass";
425       case PDKIM_VERIFY_NONE:
426       default:                          return US"none";
427       }
428
429   case DKIM_VERIFY_REASON:
430     switch (dkim_cur_sig->verify_ext_status)
431       {
432       case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
433                                                 return US"pubkey_unavailable";
434       case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:return US"pubkey_dns_syntax";
435       case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT:  return US"pubkey_der_syntax";
436       case PDKIM_VERIFY_FAIL_BODY:              return US"bodyhash_mismatch";
437       case PDKIM_VERIFY_FAIL_MESSAGE:           return US"signature_incorrect";
438       }
439
440   default:
441     return US"";
442   }
443 }
444
445
446 /* Generate signatures for the given file, returning a string.
447 If a prefix is given, prepend it to the file for the calculations.
448 */
449
450 gstring *
451 dkim_exim_sign(int fd, off_t off, uschar * prefix,
452   struct ob_dkim * dkim, const uschar ** errstr)
453 {
454 const uschar * dkim_domain;
455 int sep = 0;
456 gstring * seen_doms = NULL;
457 pdkim_ctx ctx;
458 pdkim_signature * sig;
459 gstring * sigbuf;
460 int pdkim_rc;
461 int sread;
462 uschar buf[4096];
463 int save_errno = 0;
464 int old_pool = store_pool;
465 uschar * errwhen;
466
467 store_pool = POOL_MAIN;
468
469 pdkim_init_context(&ctx, dkim->dot_stuffed, &dkim_exim_query_dns_txt);
470
471 if (!(dkim_domain = expand_cstring(dkim->dkim_domain)))
472   /* expansion error, do not send message. */
473   { errwhen = US"dkim_domain"; goto expand_bad; }
474
475 /* Set $dkim_domain expansion variable to each unique domain in list. */
476
477 while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep, NULL, 0)))
478   {
479   const uschar * dkim_sel;
480   int sel_sep = 0;
481
482   if (dkim_signing_domain[0] == '\0')
483     continue;
484
485   /* Only sign once for each domain, no matter how often it
486   appears in the expanded list. */
487
488   if (match_isinlist(dkim_signing_domain, CUSS &seen_doms,
489       0, NULL, NULL, MCL_STRING, TRUE, NULL) == OK)
490     continue;
491
492   seen_doms = string_append_listele(seen_doms, ':', dkim_signing_domain);
493
494   /* Set $dkim_selector expansion variable to each selector in list,
495   for this domain. */
496
497   if (!(dkim_sel = expand_string(dkim->dkim_selector)))
498   if (!(dkim_signing_selector = expand_string(dkim->dkim_selector)))
499     { errwhen = US"dkim_selector"; goto expand_bad; }
500
501   while ((dkim_signing_selector = string_nextinlist(&dkim_sel, &sel_sep,
502           NULL, 0)))
503     {
504     uschar * dkim_canon_expanded;
505     int pdkim_canon;
506     uschar * dkim_sign_headers_expanded = NULL;
507     uschar * dkim_private_key_expanded;
508     uschar * dkim_hash_expanded;
509     uschar * dkim_identity_expanded = NULL;
510
511     /* Get canonicalization to use */
512
513     dkim_canon_expanded = dkim->dkim_canon
514       ? expand_string(dkim->dkim_canon) : US"relaxed";
515     if (!dkim_canon_expanded)   /* expansion error, do not send message. */
516       { errwhen = US"dkim_canon"; goto expand_bad; }
517
518     if (Ustrcmp(dkim_canon_expanded, "relaxed") == 0)
519       pdkim_canon = PDKIM_CANON_RELAXED;
520     else if (Ustrcmp(dkim_canon_expanded, "simple") == 0)
521       pdkim_canon = PDKIM_CANON_SIMPLE;
522     else
523       {
524       log_write(0, LOG_MAIN,
525                  "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",
526                  dkim_canon_expanded);
527       pdkim_canon = PDKIM_CANON_RELAXED;
528       }
529
530     if (  dkim->dkim_sign_headers
531        && !(dkim_sign_headers_expanded = expand_string(dkim->dkim_sign_headers)))
532       { errwhen = US"dkim_sign_header"; goto expand_bad; }
533     /* else pass NULL, which means default header list */
534
535     /* Get private key to use. */
536
537     if (!(dkim_private_key_expanded = expand_string(dkim->dkim_private_key)))
538       { errwhen = US"dkim_private_key"; goto expand_bad; }
539
540     if (  Ustrlen(dkim_private_key_expanded) == 0
541        || Ustrcmp(dkim_private_key_expanded, "0") == 0
542        || Ustrcmp(dkim_private_key_expanded, "false") == 0
543        )
544       continue;         /* don't sign, but no error */
545
546     if (dkim_private_key_expanded[0] == '/')
547       {
548       int privkey_fd, off = 0, len;
549
550       /* Looks like a filename, load the private key. */
551
552       memset(big_buffer, 0, big_buffer_size);
553
554       if ((privkey_fd = open(CS dkim_private_key_expanded, O_RDONLY)) < 0)
555         {
556         log_write(0, LOG_MAIN | LOG_PANIC, "unable to open "
557                    "private key file for reading: %s",
558                    dkim_private_key_expanded);
559         goto bad;
560         }
561
562       do
563         {
564         if ((len = read(privkey_fd, big_buffer + off, big_buffer_size - 2 - off)) < 0)
565           {
566           (void) close(privkey_fd);
567           log_write(0, LOG_MAIN|LOG_PANIC, "unable to read private key file: %s",
568                      dkim_private_key_expanded);
569           goto bad;
570           }
571         off += len;
572         }
573       while (len > 0);
574
575       (void) close(privkey_fd);
576       big_buffer[off] = '\0';
577       dkim_private_key_expanded = big_buffer;
578       }
579
580     if (!(dkim_hash_expanded = expand_string(dkim->dkim_hash)))
581       { errwhen = US"dkim_hash"; goto expand_bad; }
582
583     if (dkim->dkim_identity)
584       if (!(dkim_identity_expanded = expand_string(dkim->dkim_identity)))
585         { errwhen = US"dkim_identity"; goto expand_bad; }
586       else if (!*dkim_identity_expanded)
587         dkim_identity_expanded = NULL;
588
589   /*XXX so we currently nail signing to RSA + this hash.
590   Need to extract algo from privkey and check for disallowed combos. */
591
592     if (!(sig = pdkim_init_sign(&ctx, dkim_signing_domain,
593                           dkim_signing_selector,
594                           dkim_private_key_expanded,
595                           dkim_hash_expanded,
596                           errstr
597                           )))
598       goto bad;
599     dkim_private_key_expanded[0] = '\0';
600
601     pdkim_set_optional(sig,
602                         CS dkim_sign_headers_expanded,
603                         dkim_identity_expanded,
604                         pdkim_canon,
605                         pdkim_canon, -1, 0, 0);
606
607     if (!ctx.sig)               /* link sig to context chain */
608       ctx.sig = sig;
609     else
610       {
611       pdkim_signature * n = ctx.sig;
612       while (n->next) n = n->next;
613       n->next = sig;
614       }
615     }
616   }
617
618 if (prefix)
619   pdkim_feed(&ctx, prefix, Ustrlen(prefix));
620
621 if (lseek(fd, off, SEEK_SET) < 0)
622   sread = -1;
623 else
624   while ((sread = read(fd, &buf, sizeof(buf))) > 0)
625     if ((pdkim_rc = pdkim_feed(&ctx, buf, sread)) != PDKIM_OK)
626       goto pk_bad;
627
628 /* Handle failed read above. */
629 if (sread == -1)
630   {
631   debug_printf("DKIM: Error reading -K file.\n");
632   save_errno = errno;
633   goto bad;
634   }
635
636 /* Build string of headers, one per signature */
637
638 if ((pdkim_rc = pdkim_feed_finish(&ctx, &sig, errstr)) != PDKIM_OK)
639   goto pk_bad;
640
641 for (sigbuf = NULL; sig; sig = sig->next)
642   sigbuf = string_append(sigbuf, 2, US sig->signature_header, US"\r\n");
643
644 (void) string_from_gstring(sigbuf);
645
646 CLEANUP:
647   store_pool = old_pool;
648   errno = save_errno;
649   return sigbuf;
650
651 pk_bad:
652   log_write(0, LOG_MAIN|LOG_PANIC,
653                 "DKIM: signing failed: %.100s", pdkim_errstr(pdkim_rc));
654 bad:
655   sigbuf = NULL;
656   goto CLEANUP;
657
658 expand_bad:
659   log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand %s: %s",
660               errwhen, expand_string_message);
661   goto bad;
662 }
663
664 #endif