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