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