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