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