Copyright updates:
[exim.git] / src / src / dkim.c
1 /*************************************************
2 *     Exim - an Internet mail transport agent    *
3 *************************************************/
4
5 /* Copyright (c) University of Cambridge, 1995 - 2018 */
6 /* Copyright (c) The Exim Maintainers 2020 */
7 /* See the file NOTICE for conditions of use and distribution. */
8
9 /* Code for DKIM support. Other DKIM relevant code is in
10    receive.c, transport.c and transports/smtp.c */
11
12 #include "exim.h"
13
14 #ifndef DISABLE_DKIM
15
16 # include "pdkim/pdkim.h"
17
18 # ifdef MACRO_PREDEF
19 #  include "macro_predef.h"
20
21 void
22 params_dkim(void)
23 {
24 builtin_macro_create_var(US"_DKIM_SIGN_HEADERS", US PDKIM_DEFAULT_SIGN_HEADERS);
25 builtin_macro_create_var(US"_DKIM_OVERSIGN_HEADERS", US PDKIM_OVERSIGN_HEADERS);
26 }
27 # else  /*!MACRO_PREDEF*/
28
29
30
31 pdkim_ctx dkim_sign_ctx;
32
33 int dkim_verify_oldpool;
34 pdkim_ctx *dkim_verify_ctx = NULL;
35 pdkim_signature *dkim_cur_sig = NULL;
36 static const uschar * dkim_collect_error = NULL;
37
38 #define DKIM_MAX_SIGNATURES 20
39
40
41
42 /* Look up the DKIM record in DNS for the given hostname.
43 Will use the first found if there are multiple.
44 The return string is tainted, having come from off-site.
45 */
46
47 uschar *
48 dkim_exim_query_dns_txt(const uschar * name)
49 {
50 dns_answer * dnsa = store_get_dns_answer();
51 dns_scan dnss;
52 rmark reset_point = store_mark();
53 gstring * g = NULL;
54
55 lookup_dnssec_authenticated = NULL;
56 if (dns_lookup(dnsa, name, T_TXT, NULL) != DNS_SUCCEED)
57   return NULL;  /*XXX better error detail?  logging? */
58
59 /* Search for TXT record */
60
61 for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS);
62      rr;
63      rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
64   if (rr->type == T_TXT)
65     {
66     int rr_offset = 0;
67
68     /* Copy record content to the answer buffer */
69
70     while (rr_offset < rr->size)
71       {
72       uschar len = rr->data[rr_offset++];
73
74       g = string_catn(g, US(rr->data + rr_offset), len);
75       if (g->ptr >= PDKIM_DNS_TXT_MAX_RECLEN)
76         goto bad;
77
78       rr_offset += len;
79       }
80
81     /* check if this looks like a DKIM record */
82     if (Ustrncmp(g->s, "v=", 2) != 0 || strncasecmp(CS g->s, "v=dkim", 6) == 0)
83       {
84       gstring_release_unused(g);
85       return string_from_gstring(g);
86       }
87
88     if (g) g->ptr = 0;          /* overwrite previous record */
89     }
90
91 bad:
92 store_reset(reset_point);
93 return NULL;    /*XXX better error detail?  logging? */
94 }
95
96
97 void
98 dkim_exim_init(void)
99 {
100 if (f.dkim_init_done) return;
101 f.dkim_init_done = TRUE;
102 pdkim_init();
103 }
104
105
106
107 void
108 dkim_exim_verify_init(BOOL dot_stuffing)
109 {
110 dkim_exim_init();
111
112 /* There is a store-reset between header & body reception
113 so cannot use the main pool. Any allocs done by Exim
114 memory-handling must use the perm pool. */
115
116 dkim_verify_oldpool = store_pool;
117 store_pool = POOL_PERM;
118
119 /* Free previous context if there is one */
120
121 if (dkim_verify_ctx)
122   pdkim_free_ctx(dkim_verify_ctx);
123
124 /* Create new context */
125
126 dkim_verify_ctx = pdkim_init_verify(&dkim_exim_query_dns_txt, dot_stuffing);
127 dkim_collect_input = dkim_verify_ctx ? DKIM_MAX_SIGNATURES : 0;
128 dkim_collect_error = NULL;
129
130 /* Start feed up with any cached data */
131 receive_get_cache();
132
133 store_pool = dkim_verify_oldpool;
134 }
135
136
137 void
138 dkim_exim_verify_feed(uschar * data, int len)
139 {
140 int rc;
141
142 store_pool = POOL_PERM;
143 if (  dkim_collect_input
144    && (rc = pdkim_feed(dkim_verify_ctx, data, len)) != PDKIM_OK)
145   {
146   dkim_collect_error = pdkim_errstr(rc);
147   log_write(0, LOG_MAIN,
148              "DKIM: validation error: %.100s", dkim_collect_error);
149   dkim_collect_input = 0;
150   }
151 store_pool = dkim_verify_oldpool;
152 }
153
154
155 /* Log the result for the given signature */
156 static void
157 dkim_exim_verify_log_sig(pdkim_signature * sig)
158 {
159 gstring * logmsg;
160 uschar * s;
161
162 if (!sig) return;
163
164 /* Remember the domain for the first pass result */
165
166 if (  !dkim_verify_overall
167    && dkim_verify_status
168       ? Ustrcmp(dkim_verify_status, US"pass") == 0
169       : sig->verify_status == PDKIM_VERIFY_PASS
170    )
171   dkim_verify_overall = string_copy(sig->domain);
172
173 /* Rewrite the sig result if the ACL overrode it.  This is only
174 needed because the DMARC code (sigh) peeks at the dkim sigs.
175 Mark the sig for this having been done. */
176
177 if (  dkim_verify_status
178    && (  dkim_verify_status != dkim_exim_expand_query(DKIM_VERIFY_STATUS)
179       || dkim_verify_reason != dkim_exim_expand_query(DKIM_VERIFY_REASON)
180    )  )
181   {                     /* overridden by ACL */
182   sig->verify_ext_status = -1;
183   if (Ustrcmp(dkim_verify_status, US"fail") == 0)
184     sig->verify_status = PDKIM_VERIFY_POLICY | PDKIM_VERIFY_FAIL;
185   else if (Ustrcmp(dkim_verify_status, US"invalid") == 0)
186     sig->verify_status = PDKIM_VERIFY_POLICY | PDKIM_VERIFY_INVALID;
187   else if (Ustrcmp(dkim_verify_status, US"none") == 0)
188     sig->verify_status = PDKIM_VERIFY_POLICY | PDKIM_VERIFY_NONE;
189   else if (Ustrcmp(dkim_verify_status, US"pass") == 0)
190     sig->verify_status = PDKIM_VERIFY_POLICY | PDKIM_VERIFY_PASS;
191   else
192     sig->verify_status = -1;
193   }
194
195 if (!LOGGING(dkim_verbose)) return;
196
197
198 logmsg = string_catn(NULL, US"DKIM: ", 6);
199 if (!(s = sig->domain)) s = US"<UNSET>";
200 logmsg = string_append(logmsg, 2, "d=", s);
201 if (!(s = sig->selector)) s = US"<UNSET>";
202 logmsg = string_append(logmsg, 2, " s=", s);
203 logmsg = string_fmt_append(logmsg, " c=%s/%s a=%s b=" SIZE_T_FMT,
204           sig->canon_headers == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
205           sig->canon_body    == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
206           dkim_sig_to_a_tag(sig),
207           (int)sig->sighash.len > -1 ? sig->sighash.len * 8 : (size_t)0);
208 if ((s= sig->identity)) logmsg = string_append(logmsg, 2, " i=", s);
209 if (sig->created > 0) logmsg = string_fmt_append(logmsg, " t=%lu",
210                                     sig->created);
211 if (sig->expires > 0) logmsg = string_fmt_append(logmsg, " x=%lu",
212                                     sig->expires);
213 if (sig->bodylength > -1) logmsg = string_fmt_append(logmsg, " l=%lu",
214                                     sig->bodylength);
215
216 if (sig->verify_status & PDKIM_VERIFY_POLICY)
217   logmsg = string_append(logmsg, 5,
218             US" [", dkim_verify_status, US" - ", dkim_verify_reason, US"]");
219 else
220   switch (sig->verify_status)
221     {
222     case PDKIM_VERIFY_NONE:
223       logmsg = string_cat(logmsg, US" [not verified]");
224       break;
225
226     case PDKIM_VERIFY_INVALID:
227       logmsg = string_cat(logmsg, US" [invalid - ");
228       switch (sig->verify_ext_status)
229         {
230         case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
231           logmsg = string_cat(logmsg,
232                         US"public key record (currently?) unavailable]");
233           break;
234
235         case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
236           logmsg = string_cat(logmsg, US"overlong public key record]");
237           break;
238
239         case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:
240         case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT:
241           logmsg = string_cat(logmsg, US"syntax error in public key record]");
242           break;
243
244         case PDKIM_VERIFY_INVALID_SIGNATURE_ERROR:
245           logmsg = string_cat(logmsg, US"signature tag missing or invalid]");
246           break;
247
248         case PDKIM_VERIFY_INVALID_DKIM_VERSION:
249           logmsg = string_cat(logmsg, US"unsupported DKIM version]");
250           break;
251
252         default:
253           logmsg = string_cat(logmsg, US"unspecified problem]");
254         }
255       break;
256
257     case PDKIM_VERIFY_FAIL:
258       logmsg = string_cat(logmsg, US" [verification failed - ");
259       switch (sig->verify_ext_status)
260         {
261         case PDKIM_VERIFY_FAIL_BODY:
262           logmsg = string_cat(logmsg,
263                US"body hash mismatch (body probably modified in transit)]");
264           break;
265
266         case PDKIM_VERIFY_FAIL_MESSAGE:
267           logmsg = string_cat(logmsg,
268                 US"signature did not verify "
269                 "(headers probably modified in transit)]");
270           break;
271
272         case PDKIM_VERIFY_INVALID_PUBKEY_KEYSIZE:
273           logmsg = string_cat(logmsg,
274                 US"signature invalid (key too short)]");
275           break;
276
277         default:
278           logmsg = string_cat(logmsg, US"unspecified reason]");
279         }
280       break;
281
282     case PDKIM_VERIFY_PASS:
283       logmsg = string_cat(logmsg, US" [verification succeeded]");
284       break;
285     }
286
287 log_write(0, LOG_MAIN, "%s", string_from_gstring(logmsg));
288 return;
289 }
290
291
292 /* Log a line for each signature */
293 void
294 dkim_exim_verify_log_all(void)
295 {
296 for (pdkim_signature * sig = dkim_signatures; sig; sig = sig->next)
297   dkim_exim_verify_log_sig(sig);
298 }
299
300
301 void
302 dkim_exim_verify_finish(void)
303 {
304 int rc;
305 gstring * g = NULL;
306 const uschar * errstr = NULL;
307
308 store_pool = POOL_PERM;
309
310 /* Delete eventual previous signature chain */
311
312 dkim_signers = NULL;
313 dkim_signatures = NULL;
314
315 if (dkim_collect_error)
316   {
317   log_write(0, LOG_MAIN,
318       "DKIM: Error during validation, disabling signature verification: %.100s",
319       dkim_collect_error);
320   f.dkim_disable_verify = TRUE;
321   goto out;
322   }
323
324 dkim_collect_input = 0;
325
326 /* Finish DKIM operation and fetch link to signatures chain */
327
328 rc = pdkim_feed_finish(dkim_verify_ctx, (pdkim_signature **)&dkim_signatures,
329                         &errstr);
330 if (rc != PDKIM_OK && errstr)
331   log_write(0, LOG_MAIN, "DKIM: validation error: %s", errstr);
332
333 /* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */
334
335 for (pdkim_signature * sig = dkim_signatures; sig; sig = sig->next)
336   {
337   if (sig->domain)   g = string_append_listele(g, ':', sig->domain);
338   if (sig->identity) g = string_append_listele(g, ':', sig->identity);
339   }
340
341 if (g) dkim_signers = g->s;
342
343 out:
344 store_pool = dkim_verify_oldpool;
345 }
346
347
348
349 /* Args as per dkim_exim_acl_run() below */
350 static int
351 dkim_acl_call(uschar * id, gstring ** res_ptr,
352   uschar ** user_msgptr, uschar ** log_msgptr)
353 {
354 int rc;
355 DEBUG(D_receive)
356   debug_printf("calling acl_smtp_dkim for dkim_cur_signer='%s'\n", id);
357
358 rc = acl_check(ACL_WHERE_DKIM, NULL, acl_smtp_dkim, user_msgptr, log_msgptr);
359 dkim_exim_verify_log_sig(dkim_cur_sig);
360 *res_ptr = string_append_listele(*res_ptr, ':', dkim_verify_status);
361 return rc;
362 }
363
364
365
366 /* For the given identity, run the DKIM ACL once for each matching signature.
367
368 Arguments
369  id             Identity to look for in dkim signatures
370  res_ptr        ptr to growable string-list of status results,
371                 appended to per ACL run
372  user_msgptr    where to put a user error (for SMTP response)
373  log_msgptr     where to put a logging message (not for SMTP response)
374
375 Returns:       OK         access is granted by an ACCEPT verb
376                DISCARD    access is granted by a DISCARD verb
377                FAIL       access is denied
378                FAIL_DROP  access is denied; drop the connection
379                DEFER      can't tell at the moment
380                ERROR      disaster
381 */
382
383 int
384 dkim_exim_acl_run(uschar * id, gstring ** res_ptr,
385   uschar ** user_msgptr, uschar ** log_msgptr)
386 {
387 uschar * cmp_val;
388 int rc = -1;
389
390 dkim_verify_status = US"none";
391 dkim_verify_reason = US"";
392 dkim_cur_signer = id;
393
394 if (f.dkim_disable_verify || !id || !dkim_verify_ctx)
395   return OK;
396
397 /* Find signatures to run ACL on */
398
399 for (pdkim_signature * sig = dkim_signatures; sig; sig = sig->next)
400   if (  (cmp_val = Ustrchr(id, '@') != NULL ? US sig->identity : US sig->domain)
401      && strcmpic(cmp_val, id) == 0
402      )
403     {
404     /* The "dkim_domain" and "dkim_selector" expansion variables have
405     related globals, since they are used in the signing code too.
406     Instead of inventing separate names for verification, we set
407     them here. This is easy since a domain and selector is guaranteed
408     to be in a signature. The other dkim_* expansion items are
409     dynamically fetched from dkim_cur_sig at expansion time (see
410     dkim_exim_expand_query() below). */
411
412     dkim_cur_sig = sig;
413     dkim_signing_domain = US sig->domain;
414     dkim_signing_selector = US sig->selector;
415     dkim_key_length = sig->keybits;
416
417     /* These two return static strings, so we can compare the addr
418     later to see if the ACL overwrote them.  Check that when logging */
419
420     dkim_verify_status = dkim_exim_expand_query(DKIM_VERIFY_STATUS);
421     dkim_verify_reason = dkim_exim_expand_query(DKIM_VERIFY_REASON);
422
423     if ((rc = dkim_acl_call(id, res_ptr, user_msgptr, log_msgptr)) != OK)
424       return rc;
425     }
426
427 if (rc != -1)
428   return rc;
429
430 /* No matching sig found.  Call ACL once anyway. */
431
432 dkim_cur_sig = NULL;
433 return dkim_acl_call(id, res_ptr, user_msgptr, log_msgptr);
434 }
435
436
437 static uschar *
438 dkim_exim_expand_defaults(int what)
439 {
440 switch (what)
441   {
442   case DKIM_ALGO:               return US"";
443   case DKIM_BODYLENGTH:         return US"9999999999999";
444   case DKIM_CANON_BODY:         return US"";
445   case DKIM_CANON_HEADERS:      return US"";
446   case DKIM_COPIEDHEADERS:      return US"";
447   case DKIM_CREATED:            return US"0";
448   case DKIM_EXPIRES:            return US"9999999999999";
449   case DKIM_HEADERNAMES:        return US"";
450   case DKIM_IDENTITY:           return US"";
451   case DKIM_KEY_GRANULARITY:    return US"*";
452   case DKIM_KEY_SRVTYPE:        return US"*";
453   case DKIM_KEY_NOTES:          return US"";
454   case DKIM_KEY_TESTING:        return US"0";
455   case DKIM_NOSUBDOMAINS:       return US"0";
456   case DKIM_VERIFY_STATUS:      return US"none";
457   case DKIM_VERIFY_REASON:      return US"";
458   default:                      return US"";
459   }
460 }
461
462
463 uschar *
464 dkim_exim_expand_query(int what)
465 {
466 if (!dkim_verify_ctx || f.dkim_disable_verify || !dkim_cur_sig)
467   return dkim_exim_expand_defaults(what);
468
469 switch (what)
470   {
471   case DKIM_ALGO:
472     return dkim_sig_to_a_tag(dkim_cur_sig);
473
474   case DKIM_BODYLENGTH:
475     return dkim_cur_sig->bodylength >= 0
476       ? string_sprintf("%ld", dkim_cur_sig->bodylength)
477       : dkim_exim_expand_defaults(what);
478
479   case DKIM_CANON_BODY:
480     switch (dkim_cur_sig->canon_body)
481       {
482       case PDKIM_CANON_RELAXED: return US"relaxed";
483       case PDKIM_CANON_SIMPLE:
484       default:                  return US"simple";
485       }
486
487   case DKIM_CANON_HEADERS:
488     switch (dkim_cur_sig->canon_headers)
489       {
490       case PDKIM_CANON_RELAXED: return US"relaxed";
491       case PDKIM_CANON_SIMPLE:
492       default:                  return US"simple";
493       }
494
495   case DKIM_COPIEDHEADERS:
496     return dkim_cur_sig->copiedheaders
497       ? US dkim_cur_sig->copiedheaders : dkim_exim_expand_defaults(what);
498
499   case DKIM_CREATED:
500     return dkim_cur_sig->created > 0
501       ? string_sprintf("%lu", dkim_cur_sig->created)
502       : dkim_exim_expand_defaults(what);
503
504   case DKIM_EXPIRES:
505     return dkim_cur_sig->expires > 0
506       ? string_sprintf("%lu", dkim_cur_sig->expires)
507       : dkim_exim_expand_defaults(what);
508
509   case DKIM_HEADERNAMES:
510     return dkim_cur_sig->headernames
511       ? dkim_cur_sig->headernames : dkim_exim_expand_defaults(what);
512
513   case DKIM_IDENTITY:
514     return dkim_cur_sig->identity
515       ? US dkim_cur_sig->identity : dkim_exim_expand_defaults(what);
516
517   case DKIM_KEY_GRANULARITY:
518     return dkim_cur_sig->pubkey
519       ? dkim_cur_sig->pubkey->granularity
520       ? US dkim_cur_sig->pubkey->granularity
521       : dkim_exim_expand_defaults(what)
522       : dkim_exim_expand_defaults(what);
523
524   case DKIM_KEY_SRVTYPE:
525     return dkim_cur_sig->pubkey
526       ? dkim_cur_sig->pubkey->srvtype
527       ? US dkim_cur_sig->pubkey->srvtype
528       : dkim_exim_expand_defaults(what)
529       : dkim_exim_expand_defaults(what);
530
531   case DKIM_KEY_NOTES:
532     return dkim_cur_sig->pubkey
533       ? dkim_cur_sig->pubkey->notes
534       ? US dkim_cur_sig->pubkey->notes
535       : dkim_exim_expand_defaults(what)
536       : dkim_exim_expand_defaults(what);
537
538   case DKIM_KEY_TESTING:
539     return dkim_cur_sig->pubkey
540       ? dkim_cur_sig->pubkey->testing
541       ? US"1"
542       : dkim_exim_expand_defaults(what)
543       : dkim_exim_expand_defaults(what);
544
545   case DKIM_NOSUBDOMAINS:
546     return dkim_cur_sig->pubkey
547       ? dkim_cur_sig->pubkey->no_subdomaining
548       ? US"1"
549       : dkim_exim_expand_defaults(what)
550       : dkim_exim_expand_defaults(what);
551
552   case DKIM_VERIFY_STATUS:
553     switch (dkim_cur_sig->verify_status)
554       {
555       case PDKIM_VERIFY_INVALID:        return US"invalid";
556       case PDKIM_VERIFY_FAIL:           return US"fail";
557       case PDKIM_VERIFY_PASS:           return US"pass";
558       case PDKIM_VERIFY_NONE:
559       default:                          return US"none";
560       }
561
562   case DKIM_VERIFY_REASON:
563     switch (dkim_cur_sig->verify_ext_status)
564       {
565       case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
566                                                 return US"pubkey_unavailable";
567       case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:return US"pubkey_dns_syntax";
568       case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT:  return US"pubkey_der_syntax";
569       case PDKIM_VERIFY_INVALID_PUBKEY_KEYSIZE: return US"pubkey_too_short";
570       case PDKIM_VERIFY_FAIL_BODY:              return US"bodyhash_mismatch";
571       case PDKIM_VERIFY_FAIL_MESSAGE:           return US"signature_incorrect";
572       }
573
574   default:
575     return US"";
576   }
577 }
578
579
580 void
581 dkim_exim_sign_init(void)
582 {
583 int old_pool = store_pool;
584
585 dkim_exim_init();
586 store_pool = POOL_MAIN;
587 pdkim_init_context(&dkim_sign_ctx, FALSE, &dkim_exim_query_dns_txt);
588 store_pool = old_pool;
589 }
590
591
592 /* Generate signatures for the given file.
593 If a prefix is given, prepend it to the file for the calculations.
594
595 Return:
596   NULL:         error; error string written
597   string:       signature header(s), or a zero-length string (not an error)
598 */
599
600 gstring *
601 dkim_exim_sign(int fd, off_t off, uschar * prefix,
602   struct ob_dkim * dkim, const uschar ** errstr)
603 {
604 const uschar * dkim_domain = NULL;
605 int sep = 0;
606 gstring * seen_doms = NULL;
607 pdkim_signature * sig;
608 gstring * sigbuf;
609 int pdkim_rc;
610 int sread;
611 uschar buf[4096];
612 int save_errno = 0;
613 int old_pool = store_pool;
614 uschar * errwhen;
615 const uschar * s;
616
617 if (dkim->dot_stuffed)
618   dkim_sign_ctx.flags |= PDKIM_DOT_TERM;
619
620 store_pool = POOL_MAIN;
621
622 if ((s = dkim->dkim_domain) && !(dkim_domain = expand_cstring(s)))
623   /* expansion error, do not send message. */
624   { errwhen = US"dkim_domain"; goto expand_bad; }
625
626 /* Set $dkim_domain expansion variable to each unique domain in list. */
627
628 if (dkim_domain)
629   while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep, NULL, 0)))
630   {
631   const uschar * dkim_sel;
632   int sel_sep = 0;
633
634   if (dkim_signing_domain[0] == '\0')
635     continue;
636
637   /* Only sign once for each domain, no matter how often it
638   appears in the expanded list. */
639
640   dkim_signing_domain = string_copylc(dkim_signing_domain);
641   if (match_isinlist(dkim_signing_domain, CUSS &seen_doms,
642       0, NULL, NULL, MCL_STRING, TRUE, NULL) == OK)
643     continue;
644
645   seen_doms = string_append_listele(seen_doms, ':', dkim_signing_domain);
646
647   /* Set $dkim_selector expansion variable to each selector in list,
648   for this domain. */
649
650   if (!(dkim_sel = expand_string(dkim->dkim_selector)))
651     { errwhen = US"dkim_selector"; goto expand_bad; }
652
653   while ((dkim_signing_selector = string_nextinlist(&dkim_sel, &sel_sep,
654           NULL, 0)))
655     {
656     uschar * dkim_canon_expanded;
657     int pdkim_canon;
658     uschar * dkim_sign_headers_expanded = NULL;
659     uschar * dkim_private_key_expanded;
660     uschar * dkim_hash_expanded;
661     uschar * dkim_identity_expanded = NULL;
662     uschar * dkim_timestamps_expanded = NULL;
663     unsigned long tval = 0, xval = 0;
664
665     /* Get canonicalization to use */
666
667     dkim_canon_expanded = dkim->dkim_canon
668       ? expand_string(dkim->dkim_canon) : US"relaxed";
669     if (!dkim_canon_expanded)   /* expansion error, do not send message. */
670       { errwhen = US"dkim_canon"; goto expand_bad; }
671
672     if (Ustrcmp(dkim_canon_expanded, "relaxed") == 0)
673       pdkim_canon = PDKIM_CANON_RELAXED;
674     else if (Ustrcmp(dkim_canon_expanded, "simple") == 0)
675       pdkim_canon = PDKIM_CANON_SIMPLE;
676     else
677       {
678       log_write(0, LOG_MAIN,
679                  "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",
680                  dkim_canon_expanded);
681       pdkim_canon = PDKIM_CANON_RELAXED;
682       }
683
684     if (  dkim->dkim_sign_headers
685        && !(dkim_sign_headers_expanded = expand_string(dkim->dkim_sign_headers)))
686       { errwhen = US"dkim_sign_header"; goto expand_bad; }
687     /* else pass NULL, which means default header list */
688
689     /* Get private key to use. */
690
691     if (!(dkim_private_key_expanded = expand_string(dkim->dkim_private_key)))
692       { errwhen = US"dkim_private_key"; goto expand_bad; }
693
694     if (  Ustrlen(dkim_private_key_expanded) == 0
695        || Ustrcmp(dkim_private_key_expanded, "0") == 0
696        || Ustrcmp(dkim_private_key_expanded, "false") == 0
697        )
698       continue;         /* don't sign, but no error */
699
700     if (  dkim_private_key_expanded[0] == '/'
701        && !(dkim_private_key_expanded =
702              expand_file_big_buffer(dkim_private_key_expanded)))
703       goto bad;
704
705     if (!(dkim_hash_expanded = expand_string(dkim->dkim_hash)))
706       { errwhen = US"dkim_hash"; goto expand_bad; }
707
708     if (dkim->dkim_identity)
709       if (!(dkim_identity_expanded = expand_string(dkim->dkim_identity)))
710         { errwhen = US"dkim_identity"; goto expand_bad; }
711       else if (!*dkim_identity_expanded)
712         dkim_identity_expanded = NULL;
713
714     if (dkim->dkim_timestamps)
715       if (!(dkim_timestamps_expanded = expand_string(dkim->dkim_timestamps)))
716         { errwhen = US"dkim_timestamps"; goto expand_bad; }
717       else
718         xval = (tval = (unsigned long) time(NULL))
719               + strtoul(CCS dkim_timestamps_expanded, NULL, 10);
720
721     if (!(sig = pdkim_init_sign(&dkim_sign_ctx, dkim_signing_domain,
722                           dkim_signing_selector,
723                           dkim_private_key_expanded,
724                           dkim_hash_expanded,
725                           errstr
726                           )))
727       goto bad;
728     dkim_private_key_expanded[0] = '\0';
729
730     pdkim_set_optional(sig,
731                         CS dkim_sign_headers_expanded,
732                         CS dkim_identity_expanded,
733                         pdkim_canon,
734                         pdkim_canon, -1, tval, xval);
735
736     if (!pdkim_set_sig_bodyhash(&dkim_sign_ctx, sig))
737       goto bad;
738
739     if (!dkim_sign_ctx.sig)             /* link sig to context chain */
740       dkim_sign_ctx.sig = sig;
741     else
742       {
743       pdkim_signature * n = dkim_sign_ctx.sig;
744       while (n->next) n = n->next;
745       n->next = sig;
746       }
747     }
748   }
749
750 /* We may need to carry on with the data-feed even if there are no DKIM sigs to
751 produce, if some other package (eg. ARC) is signing. */
752
753 if (!dkim_sign_ctx.sig && !dkim->force_bodyhash)
754   {
755   DEBUG(D_transport) debug_printf("DKIM: no viable signatures to use\n");
756   sigbuf = string_get(1);       /* return a zero-len string */
757   }
758 else
759   {
760   if (prefix && (pdkim_rc = pdkim_feed(&dkim_sign_ctx, prefix, Ustrlen(prefix))) != PDKIM_OK)
761     goto pk_bad;
762
763   if (lseek(fd, off, SEEK_SET) < 0)
764     sread = -1;
765   else
766     while ((sread = read(fd, &buf, sizeof(buf))) > 0)
767       if ((pdkim_rc = pdkim_feed(&dkim_sign_ctx, buf, sread)) != PDKIM_OK)
768         goto pk_bad;
769
770   /* Handle failed read above. */
771   if (sread == -1)
772     {
773     debug_printf("DKIM: Error reading -K file.\n");
774     save_errno = errno;
775     goto bad;
776     }
777
778   /* Build string of headers, one per signature */
779
780   if ((pdkim_rc = pdkim_feed_finish(&dkim_sign_ctx, &sig, errstr)) != PDKIM_OK)
781     goto pk_bad;
782
783   if (!sig)
784     {
785     DEBUG(D_transport) debug_printf("DKIM: no signatures to use\n");
786     sigbuf = string_get(1);     /* return a zero-len string */
787     }
788   else for (sigbuf = NULL; sig; sig = sig->next)
789     sigbuf = string_append(sigbuf, 2, US sig->signature_header, US"\r\n");
790   }
791
792 CLEANUP:
793   (void) string_from_gstring(sigbuf);
794   store_pool = old_pool;
795   errno = save_errno;
796   return sigbuf;
797
798 pk_bad:
799   log_write(0, LOG_MAIN|LOG_PANIC,
800                 "DKIM: signing failed: %.100s", pdkim_errstr(pdkim_rc));
801 bad:
802   sigbuf = NULL;
803   goto CLEANUP;
804
805 expand_bad:
806   *errstr = string_sprintf("failed to expand %s: %s",
807               errwhen, expand_string_message);
808   log_write(0, LOG_MAIN | LOG_PANIC, "%s", *errstr);
809   goto bad;
810 }
811
812
813
814
815 gstring *
816 authres_dkim(gstring * g)
817 {
818 int start = 0;          /* compiler quietening */
819
820 DEBUG(D_acl) start = g->ptr;
821
822 for (pdkim_signature * sig = dkim_signatures; sig; sig = sig->next)
823   {
824   g = string_catn(g, US";\n\tdkim=", 8);
825
826   if (sig->verify_status & PDKIM_VERIFY_POLICY)
827     g = string_append(g, 5,
828       US"policy (", dkim_verify_status, US" - ", dkim_verify_reason, US")");
829   else switch(sig->verify_status)
830     {
831     case PDKIM_VERIFY_NONE:    g = string_cat(g, US"none"); break;
832     case PDKIM_VERIFY_INVALID:
833       switch (sig->verify_ext_status)
834         {
835         case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
836           g = string_cat(g, US"tmperror (pubkey unavailable)\n\t\t"); break;
837         case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
838           g = string_cat(g, US"permerror (overlong public key record)\n\t\t"); break;
839         case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:
840         case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT:
841           g = string_cat(g, US"neutral (public key record import problem)\n\t\t");
842           break;
843         case PDKIM_VERIFY_INVALID_SIGNATURE_ERROR:
844           g = string_cat(g, US"neutral (signature tag missing or invalid)\n\t\t");
845           break;
846         case PDKIM_VERIFY_INVALID_DKIM_VERSION:
847           g = string_cat(g, US"neutral (unsupported DKIM version)\n\t\t");
848           break;
849         default:
850           g = string_cat(g, US"permerror (unspecified problem)\n\t\t"); break;
851         }
852       break;
853     case PDKIM_VERIFY_FAIL:
854       switch (sig->verify_ext_status)
855         {
856         case PDKIM_VERIFY_FAIL_BODY:
857           g = string_cat(g,
858             US"fail (body hash mismatch; body probably modified in transit)\n\t\t");
859           break;
860         case PDKIM_VERIFY_FAIL_MESSAGE:
861           g = string_cat(g,
862             US"fail (signature did not verify; headers probably modified in transit)\n\t\t");
863           break;
864         case PDKIM_VERIFY_INVALID_PUBKEY_KEYSIZE:       /* should this really be "polcy"? */
865           g = string_fmt_append(g, "fail (public key too short: %u bits)\n\t\t", sig->keybits);
866           break;
867         default:
868           g = string_cat(g, US"fail (unspecified reason)\n\t\t");
869           break;
870         }
871       break;
872     case PDKIM_VERIFY_PASS:    g = string_cat(g, US"pass"); break;
873     default:                   g = string_cat(g, US"permerror"); break;
874     }
875   if (sig->domain)   g = string_append(g, 2, US" header.d=", sig->domain);
876   if (sig->identity) g = string_append(g, 2, US" header.i=", sig->identity);
877   if (sig->selector) g = string_append(g, 2, US" header.s=", sig->selector);
878   g = string_append(g, 2, US" header.a=", dkim_sig_to_a_tag(sig));
879   }
880
881 DEBUG(D_acl)
882   if (g->ptr == start)
883     debug_printf("DKIM: no authres\n");
884   else
885     debug_printf("DKIM: authres '%.*s'\n", g->ptr - start - 3, g->s + start + 3);
886 return g;
887 }
888
889
890 # endif /*!MACRO_PREDEF*/
891 #endif  /*!DISABLE_DKIM*/