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