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