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