DKIM: Allow the DKIM ACL to override verification results. Bug 2186
[exim.git] / src / src / dkim.c
1 /*************************************************
2 *     Exim - an Internet mail transport agent    *
3 *************************************************/
4
5 /* Copyright (c) University of Cambridge, 1995 - 2017 */
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 Could we hand back an allocated string?
40 */
41
42 static int
43 dkim_exim_query_dns_txt(char *name, char *answer)
44 {
45 dns_answer dnsa;
46 dns_scan dnss;
47 dns_record *rr;
48
49 lookup_dnssec_authenticated = NULL;
50 if (dns_lookup(&dnsa, US name, T_TXT, NULL) != DNS_SUCCEED)
51   return PDKIM_FAIL;    /*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     int answer_offset = 0;
62
63     /* Copy record content to the answer buffer */
64
65     while (rr_offset < rr->size)
66       {
67       uschar len = rr->data[rr_offset++];
68       snprintf(answer + answer_offset,
69                 PDKIM_DNS_TXT_MAX_RECLEN - answer_offset,
70                 "%.*s", (int)len, CS  (rr->data + rr_offset));
71       rr_offset += len;
72       answer_offset += len;
73       if (answer_offset >= PDKIM_DNS_TXT_MAX_RECLEN)
74         return PDKIM_FAIL;      /*XXX better error detail?  logging? */
75       }
76     return PDKIM_OK;
77     }
78
79 return PDKIM_FAIL;      /*XXX better error detail?  logging? */
80 }
81
82
83 void
84 dkim_exim_init(void)
85 {
86 pdkim_init();
87 }
88
89
90
91 void
92 dkim_exim_verify_init(BOOL dot_stuffing)
93 {
94 /* There is a store-reset between header & body reception
95 so cannot use the main pool. Any allocs done by Exim
96 memory-handling must use the perm pool. */
97
98 dkim_verify_oldpool = store_pool;
99 store_pool = POOL_PERM;
100
101 /* Free previous context if there is one */
102
103 if (dkim_verify_ctx)
104   pdkim_free_ctx(dkim_verify_ctx);
105
106 /* Create new context */
107
108 dkim_verify_ctx = pdkim_init_verify(&dkim_exim_query_dns_txt, dot_stuffing);
109 dkim_collect_input = !!dkim_verify_ctx;
110 dkim_collect_error = NULL;
111
112 /* Start feed up with any cached data */
113 receive_get_cache();
114
115 store_pool = dkim_verify_oldpool;
116 }
117
118
119 void
120 dkim_exim_verify_feed(uschar * data, int len)
121 {
122 int rc;
123
124 store_pool = POOL_PERM;
125 if (  dkim_collect_input
126    && (rc = pdkim_feed(dkim_verify_ctx, data, len)) != PDKIM_OK)
127   {
128   dkim_collect_error = pdkim_errstr(rc);
129   log_write(0, LOG_MAIN,
130              "DKIM: validation error: %.100s", dkim_collect_error);
131   dkim_collect_input = FALSE;
132   }
133 store_pool = dkim_verify_oldpool;
134 }
135
136
137 /* Log the result for the given signature */
138 static void
139 dkim_exim_verify_log_sig(pdkim_signature * sig)
140 {
141 gstring * logmsg = string_catn(NULL, "DKIM: ", 6);
142 uschar * s;
143
144 if (!(s = sig->domain)) s = US"<UNSET>";
145 logmsg = string_append(logmsg, 2, "d=", s);
146 if (!(s = sig->selector)) s = US"<UNSET>";
147 logmsg = string_append(logmsg, 2, " s=", s);
148 logmsg = string_append(logmsg, 7, 
149 " c=", sig->canon_headers == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
150 "/",   sig->canon_body    == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
151 " a=", dkim_sig_to_a_tag(sig),
152 string_sprintf(" b=" SIZE_T_FMT,
153                 (int)sig->sighash.len > -1 ? sig->sighash.len * 8 : 0));
154 if ((s= sig->identity)) logmsg = string_append(logmsg, 2, " i=", s);
155 if (sig->created > 0) logmsg = string_cat(logmsg,
156                               string_sprintf(" t=%lu", sig->created));
157 if (sig->expires > 0) logmsg = string_cat(logmsg,
158                               string_sprintf(" x=%lu", sig->expires));
159 if (sig->bodylength > -1) logmsg = string_cat(logmsg,
160                               string_sprintf(" l=%lu", sig->bodylength));
161
162 if (  !dkim_verify_status
163    || (  dkim_verify_status == dkim_exim_expand_query(DKIM_VERIFY_STATUS)
164       && dkim_verify_reason == dkim_exim_expand_query(DKIM_VERIFY_REASON)
165    )  )
166   switch (sig->verify_status)
167     {
168     case PDKIM_VERIFY_NONE:
169       logmsg = string_cat(logmsg, " [not verified]");
170       break;
171
172     case PDKIM_VERIFY_INVALID:
173       logmsg = string_cat(logmsg, " [invalid - ");
174       switch (sig->verify_ext_status)
175         {
176         case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
177           logmsg = string_cat(logmsg,
178                         "public key record (currently?) unavailable]");
179           break;
180
181         case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
182           logmsg = string_cat(logmsg, "overlong public key record]");
183           break;
184
185         case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:
186         case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT:
187           logmsg = string_cat(logmsg, "syntax error in public key record]");
188           break;
189
190         case PDKIM_VERIFY_INVALID_SIGNATURE_ERROR:
191           logmsg = string_cat(logmsg, "signature tag missing or invalid]");
192           break;
193
194         case PDKIM_VERIFY_INVALID_DKIM_VERSION:
195           logmsg = string_cat(logmsg, "unsupported DKIM version]");
196           break;
197
198         default:
199           logmsg = string_cat(logmsg, "unspecified problem]");
200         }
201       break;
202
203     case PDKIM_VERIFY_FAIL:
204       logmsg = string_cat(logmsg, " [verification failed - ");
205       switch (sig->verify_ext_status)
206         {
207         case PDKIM_VERIFY_FAIL_BODY:
208           logmsg = string_cat(logmsg,
209                        "body hash mismatch (body probably modified in transit)]");
210           break;
211
212         case PDKIM_VERIFY_FAIL_MESSAGE:
213           logmsg = string_cat(logmsg,
214                        "signature did not verify (headers probably modified in transit)]");
215         break;
216
217         default:
218           logmsg = string_cat(logmsg, "unspecified reason]");
219         }
220       break;
221
222     case PDKIM_VERIFY_PASS:
223       logmsg = string_cat(logmsg, " [verification succeeded]");
224       break;
225     }
226 else
227   logmsg = string_append(logmsg, 5,
228             US" [", dkim_verify_status, US" - ", dkim_verify_reason, US"]");
229
230 log_write(0, LOG_MAIN, string_from_gstring(logmsg));
231 return;
232 }
233
234
235 /* Log a line for "the current" signature */
236 void
237 dkim_exim_verify_log_item(void)
238 {
239 dkim_exim_verify_log_sig(dkim_cur_sig);
240 }
241
242
243 /* Log a line for each signature */
244 void
245 dkim_exim_verify_log_all(void)
246 {
247 pdkim_signature * sig;
248 for (sig = dkim_signatures; sig; sig = sig->next) dkim_exim_verify_log_sig(sig);
249 }
250
251
252 void
253 dkim_exim_verify_finish(void)
254 {
255 pdkim_signature * sig;
256 int rc;
257 gstring * g = NULL;
258 const uschar * errstr;
259
260 store_pool = POOL_PERM;
261
262 /* Delete eventual previous signature chain */
263
264 dkim_signers = NULL;
265 dkim_signatures = NULL;
266
267 if (dkim_collect_error)
268   {
269   log_write(0, LOG_MAIN,
270       "DKIM: Error during validation, disabling signature verification: %.100s",
271       dkim_collect_error);
272   dkim_disable_verify = TRUE;
273   goto out;
274   }
275
276 dkim_collect_input = FALSE;
277
278 /* Finish DKIM operation and fetch link to signatures chain */
279
280 rc = pdkim_feed_finish(dkim_verify_ctx, &dkim_signatures, &errstr);
281 if (rc != PDKIM_OK)
282   {
283   log_write(0, LOG_MAIN, "DKIM: validation error: %.100s%s%s", pdkim_errstr(rc),
284             errstr ? ": " : "", errstr ? errstr : US"");
285   goto out;
286   }
287
288 /* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */
289
290 for (sig = dkim_signatures; sig; sig = sig->next)
291   {
292   if (sig->domain)   g = string_append_listele(g, ':', sig->domain);
293   if (sig->identity) g = string_append_listele(g, ':', sig->identity);
294   }
295
296 if (g) dkim_signers = g->s;
297
298 out:
299 store_pool = dkim_verify_oldpool;
300 }
301
302
303 void
304 dkim_exim_acl_setup(uschar * id)
305 {
306 pdkim_signature * sig;
307 uschar * cmp_val;
308
309 dkim_cur_sig = NULL;
310 dkim_cur_signer = id;
311
312 if (dkim_disable_verify || !id || !dkim_verify_ctx)
313   return;
314
315 /* Find signature to run ACL on */
316
317 for (sig = dkim_signatures; sig; sig = sig->next)
318   if (  (cmp_val = Ustrchr(id, '@') != NULL ? US sig->identity : US sig->domain)
319      && strcmpic(cmp_val, id) == 0
320      )
321     {
322     dkim_cur_sig = sig;
323
324     /* The "dkim_domain" and "dkim_selector" expansion variables have
325        related globals, since they are used in the signing code too.
326        Instead of inventing separate names for verification, we set
327        them here. This is easy since a domain and selector is guaranteed
328        to be in a signature. The other dkim_* expansion items are
329        dynamically fetched from dkim_cur_sig at expansion time (see
330        function below). */
331
332     dkim_signing_domain = US sig->domain;
333     dkim_signing_selector = US sig->selector;
334     dkim_key_length = sig->sighash.len * 8;
335
336     /* These two return static strings, so we can compare the addr
337     later to see if the ACL overwrote them.  Check that when logging */
338
339     dkim_verify_status = dkim_exim_expand_query(DKIM_VERIFY_STATUS);
340     dkim_verify_reason = dkim_exim_expand_query(DKIM_VERIFY_REASON);
341     return;
342     }
343 }
344
345
346 static uschar *
347 dkim_exim_expand_defaults(int what)
348 {
349 switch (what)
350   {
351   case DKIM_ALGO:               return US"";
352   case DKIM_BODYLENGTH:         return US"9999999999999";
353   case DKIM_CANON_BODY:         return US"";
354   case DKIM_CANON_HEADERS:      return US"";
355   case DKIM_COPIEDHEADERS:      return US"";
356   case DKIM_CREATED:            return US"0";
357   case DKIM_EXPIRES:            return US"9999999999999";
358   case DKIM_HEADERNAMES:        return US"";
359   case DKIM_IDENTITY:           return US"";
360   case DKIM_KEY_GRANULARITY:    return US"*";
361   case DKIM_KEY_SRVTYPE:        return US"*";
362   case DKIM_KEY_NOTES:          return US"";
363   case DKIM_KEY_TESTING:        return US"0";
364   case DKIM_NOSUBDOMAINS:       return US"0";
365   case DKIM_VERIFY_STATUS:      return US"none";
366   case DKIM_VERIFY_REASON:      return US"";
367   default:                      return US"";
368   }
369 }
370
371
372 uschar *
373 dkim_exim_expand_query(int what)
374 {
375 if (!dkim_verify_ctx || dkim_disable_verify || !dkim_cur_sig)
376   return dkim_exim_expand_defaults(what);
377
378 switch (what)
379   {
380   case DKIM_ALGO:
381     return dkim_sig_to_a_tag(dkim_cur_sig);
382
383   case DKIM_BODYLENGTH:
384     return dkim_cur_sig->bodylength >= 0
385       ? string_sprintf("%ld", dkim_cur_sig->bodylength)
386       : dkim_exim_expand_defaults(what);
387
388   case DKIM_CANON_BODY:
389     switch (dkim_cur_sig->canon_body)
390       {
391       case PDKIM_CANON_RELAXED: return US"relaxed";
392       case PDKIM_CANON_SIMPLE:
393       default:                  return US"simple";
394       }
395
396   case DKIM_CANON_HEADERS:
397     switch (dkim_cur_sig->canon_headers)
398       {
399       case PDKIM_CANON_RELAXED: return US"relaxed";
400       case PDKIM_CANON_SIMPLE:
401       default:                  return US"simple";
402       }
403
404   case DKIM_COPIEDHEADERS:
405     return dkim_cur_sig->copiedheaders
406       ? US dkim_cur_sig->copiedheaders : dkim_exim_expand_defaults(what);
407
408   case DKIM_CREATED:
409     return dkim_cur_sig->created > 0
410       ? string_sprintf("%lu", dkim_cur_sig->created)
411       : dkim_exim_expand_defaults(what);
412
413   case DKIM_EXPIRES:
414     return dkim_cur_sig->expires > 0
415       ? string_sprintf("%lu", dkim_cur_sig->expires)
416       : dkim_exim_expand_defaults(what);
417
418   case DKIM_HEADERNAMES:
419     return dkim_cur_sig->headernames
420       ? dkim_cur_sig->headernames : dkim_exim_expand_defaults(what);
421
422   case DKIM_IDENTITY:
423     return dkim_cur_sig->identity
424       ? US dkim_cur_sig->identity : dkim_exim_expand_defaults(what);
425
426   case DKIM_KEY_GRANULARITY:
427     return dkim_cur_sig->pubkey
428       ? dkim_cur_sig->pubkey->granularity
429       ? US dkim_cur_sig->pubkey->granularity
430       : dkim_exim_expand_defaults(what)
431       : dkim_exim_expand_defaults(what);
432
433   case DKIM_KEY_SRVTYPE:
434     return dkim_cur_sig->pubkey
435       ? dkim_cur_sig->pubkey->srvtype
436       ? US dkim_cur_sig->pubkey->srvtype
437       : dkim_exim_expand_defaults(what)
438       : dkim_exim_expand_defaults(what);
439
440   case DKIM_KEY_NOTES:
441     return dkim_cur_sig->pubkey
442       ? dkim_cur_sig->pubkey->notes
443       ? US dkim_cur_sig->pubkey->notes
444       : dkim_exim_expand_defaults(what)
445       : dkim_exim_expand_defaults(what);
446
447   case DKIM_KEY_TESTING:
448     return dkim_cur_sig->pubkey
449       ? dkim_cur_sig->pubkey->testing
450       ? US"1"
451       : dkim_exim_expand_defaults(what)
452       : dkim_exim_expand_defaults(what);
453
454   case DKIM_NOSUBDOMAINS:
455     return dkim_cur_sig->pubkey
456       ? dkim_cur_sig->pubkey->no_subdomaining
457       ? US"1"
458       : dkim_exim_expand_defaults(what)
459       : dkim_exim_expand_defaults(what);
460
461   case DKIM_VERIFY_STATUS:
462     switch (dkim_cur_sig->verify_status)
463       {
464       case PDKIM_VERIFY_INVALID:        return US"invalid";
465       case PDKIM_VERIFY_FAIL:           return US"fail";
466       case PDKIM_VERIFY_PASS:           return US"pass";
467       case PDKIM_VERIFY_NONE:
468       default:                          return US"none";
469       }
470
471   case DKIM_VERIFY_REASON:
472     switch (dkim_cur_sig->verify_ext_status)
473       {
474       case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
475                                                 return US"pubkey_unavailable";
476       case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:return US"pubkey_dns_syntax";
477       case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT:  return US"pubkey_der_syntax";
478       case PDKIM_VERIFY_FAIL_BODY:              return US"bodyhash_mismatch";
479       case PDKIM_VERIFY_FAIL_MESSAGE:           return US"signature_incorrect";
480       }
481
482   default:
483     return US"";
484   }
485 }
486
487
488 /* Generate signatures for the given file, returning a string.
489 If a prefix is given, prepend it to the file for the calculations.
490 */
491
492 gstring *
493 dkim_exim_sign(int fd, off_t off, uschar * prefix,
494   struct ob_dkim * dkim, const uschar ** errstr)
495 {
496 const uschar * dkim_domain;
497 int sep = 0;
498 gstring * seen_doms = NULL;
499 pdkim_ctx ctx;
500 pdkim_signature * sig;
501 gstring * sigbuf;
502 int pdkim_rc;
503 int sread;
504 uschar buf[4096];
505 int save_errno = 0;
506 int old_pool = store_pool;
507 uschar * errwhen;
508
509 store_pool = POOL_MAIN;
510
511 pdkim_init_context(&ctx, dkim->dot_stuffed, &dkim_exim_query_dns_txt);
512
513 if (!(dkim_domain = expand_cstring(dkim->dkim_domain)))
514   /* expansion error, do not send message. */
515   { errwhen = US"dkim_domain"; goto expand_bad; }
516
517 /* Set $dkim_domain expansion variable to each unique domain in list. */
518
519 while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep, NULL, 0)))
520   {
521   const uschar * dkim_sel;
522   int sel_sep = 0;
523
524   if (dkim_signing_domain[0] == '\0')
525     continue;
526
527   /* Only sign once for each domain, no matter how often it
528   appears in the expanded list. */
529
530   if (match_isinlist(dkim_signing_domain, CUSS &seen_doms,
531       0, NULL, NULL, MCL_STRING, TRUE, NULL) == OK)
532     continue;
533
534   seen_doms = string_append_listele(seen_doms, ':', dkim_signing_domain);
535
536   /* Set $dkim_selector expansion variable to each selector in list,
537   for this domain. */
538
539   if (!(dkim_sel = expand_string(dkim->dkim_selector)))
540   if (!(dkim_signing_selector = expand_string(dkim->dkim_selector)))
541     { errwhen = US"dkim_selector"; goto expand_bad; }
542
543   while ((dkim_signing_selector = string_nextinlist(&dkim_sel, &sel_sep,
544           NULL, 0)))
545     {
546     uschar * dkim_canon_expanded;
547     int pdkim_canon;
548     uschar * dkim_sign_headers_expanded = NULL;
549     uschar * dkim_private_key_expanded;
550     uschar * dkim_hash_expanded;
551     uschar * dkim_identity_expanded = NULL;
552
553     /* Get canonicalization to use */
554
555     dkim_canon_expanded = dkim->dkim_canon
556       ? expand_string(dkim->dkim_canon) : US"relaxed";
557     if (!dkim_canon_expanded)   /* expansion error, do not send message. */
558       { errwhen = US"dkim_canon"; goto expand_bad; }
559
560     if (Ustrcmp(dkim_canon_expanded, "relaxed") == 0)
561       pdkim_canon = PDKIM_CANON_RELAXED;
562     else if (Ustrcmp(dkim_canon_expanded, "simple") == 0)
563       pdkim_canon = PDKIM_CANON_SIMPLE;
564     else
565       {
566       log_write(0, LOG_MAIN,
567                  "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",
568                  dkim_canon_expanded);
569       pdkim_canon = PDKIM_CANON_RELAXED;
570       }
571
572     if (  dkim->dkim_sign_headers
573        && !(dkim_sign_headers_expanded = expand_string(dkim->dkim_sign_headers)))
574       { errwhen = US"dkim_sign_header"; goto expand_bad; }
575     /* else pass NULL, which means default header list */
576
577     /* Get private key to use. */
578
579     if (!(dkim_private_key_expanded = expand_string(dkim->dkim_private_key)))
580       { errwhen = US"dkim_private_key"; goto expand_bad; }
581
582     if (  Ustrlen(dkim_private_key_expanded) == 0
583        || Ustrcmp(dkim_private_key_expanded, "0") == 0
584        || Ustrcmp(dkim_private_key_expanded, "false") == 0
585        )
586       continue;         /* don't sign, but no error */
587
588     if (dkim_private_key_expanded[0] == '/')
589       {
590       int privkey_fd, off = 0, len;
591
592       /* Looks like a filename, load the private key. */
593
594       memset(big_buffer, 0, big_buffer_size);
595
596       if ((privkey_fd = open(CS dkim_private_key_expanded, O_RDONLY)) < 0)
597         {
598         log_write(0, LOG_MAIN | LOG_PANIC, "unable to open "
599                    "private key file for reading: %s",
600                    dkim_private_key_expanded);
601         goto bad;
602         }
603
604       do
605         {
606         if ((len = read(privkey_fd, big_buffer + off, big_buffer_size - 2 - off)) < 0)
607           {
608           (void) close(privkey_fd);
609           log_write(0, LOG_MAIN|LOG_PANIC, "unable to read private key file: %s",
610                      dkim_private_key_expanded);
611           goto bad;
612           }
613         off += len;
614         }
615       while (len > 0);
616
617       (void) close(privkey_fd);
618       big_buffer[off] = '\0';
619       dkim_private_key_expanded = big_buffer;
620       }
621
622     if (!(dkim_hash_expanded = expand_string(dkim->dkim_hash)))
623       { errwhen = US"dkim_hash"; goto expand_bad; }
624
625     if (dkim->dkim_identity)
626       if (!(dkim_identity_expanded = expand_string(dkim->dkim_identity)))
627         { errwhen = US"dkim_identity"; goto expand_bad; }
628       else if (!*dkim_identity_expanded)
629         dkim_identity_expanded = NULL;
630
631   /*XXX so we currently nail signing to RSA + this hash.
632   Need to extract algo from privkey and check for disallowed combos. */
633
634     if (!(sig = pdkim_init_sign(&ctx, dkim_signing_domain,
635                           dkim_signing_selector,
636                           dkim_private_key_expanded,
637                           dkim_hash_expanded,
638                           errstr
639                           )))
640       goto bad;
641     dkim_private_key_expanded[0] = '\0';
642
643     pdkim_set_optional(sig,
644                         CS dkim_sign_headers_expanded,
645                         dkim_identity_expanded,
646                         pdkim_canon,
647                         pdkim_canon, -1, 0, 0);
648
649     if (!ctx.sig)               /* link sig to context chain */
650       ctx.sig = sig;
651     else
652       {
653       pdkim_signature * n = ctx.sig;
654       while (n->next) n = n->next;
655       n->next = sig;
656       }
657     }
658   }
659
660 if (prefix)
661   pdkim_feed(&ctx, prefix, Ustrlen(prefix));
662
663 if (lseek(fd, off, SEEK_SET) < 0)
664   sread = -1;
665 else
666   while ((sread = read(fd, &buf, sizeof(buf))) > 0)
667     if ((pdkim_rc = pdkim_feed(&ctx, buf, sread)) != PDKIM_OK)
668       goto pk_bad;
669
670 /* Handle failed read above. */
671 if (sread == -1)
672   {
673   debug_printf("DKIM: Error reading -K file.\n");
674   save_errno = errno;
675   goto bad;
676   }
677
678 /* Build string of headers, one per signature */
679
680 if ((pdkim_rc = pdkim_feed_finish(&ctx, &sig, errstr)) != PDKIM_OK)
681   goto pk_bad;
682
683 for (sigbuf = NULL; sig; sig = sig->next)
684   sigbuf = string_append(sigbuf, 2, US sig->signature_header, US"\r\n");
685
686 (void) string_from_gstring(sigbuf);
687
688 CLEANUP:
689   store_pool = old_pool;
690   errno = save_errno;
691   return sigbuf;
692
693 pk_bad:
694   log_write(0, LOG_MAIN|LOG_PANIC,
695                 "DKIM: signing failed: %.100s", pdkim_errstr(pdkim_rc));
696 bad:
697   sigbuf = NULL;
698   goto CLEANUP;
699
700 expand_bad:
701   log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand %s: %s",
702               errwhen, expand_string_message);
703   goto bad;
704 }
705
706 # endif /*!MACRO_PREDEF*/
707 #endif  /*!DISABLE_DKIM*/