DKIM: support multiple hash methods
[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 uschar *
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_items = NULL;
463 int seen_items_size = 0;
464 int seen_items_offset = 0;
465 uschar *dkim_canon_expanded;
466 uschar *dkim_sign_headers_expanded;
467 uschar *dkim_private_key_expanded;
468 uschar *dkim_hash_expanded;
469 pdkim_ctx *ctx = NULL;
470 uschar *rc = NULL;
471 uschar *sigbuf = NULL;
472 int sigsize = 0;
473 int sigptr = 0;
474 pdkim_signature *signature;
475 int pdkim_canon;
476 int pdkim_rc;
477 int sread;
478 uschar buf[4096];
479 int save_errno = 0;
480 int old_pool = store_pool;
481
482 store_pool = POOL_MAIN;
483
484 if (!(dkim_domain = expand_cstring(dkim->dkim_domain)))
485   {
486   /* expansion error, do not send message. */
487   log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
488              "dkim_domain: %s", expand_string_message);
489   goto bad;
490   }
491
492 /* Set $dkim_domain expansion variable to each unique domain in list. */
493
494 while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep, NULL, 0)))
495   {
496   if (dkim_signing_domain[0] == '\0')
497     continue;
498
499   /* Only sign once for each domain, no matter how often it
500   appears in the expanded list. */
501
502   if (seen_items)
503     {
504     const uschar *seen_items_list = seen_items;
505     if (match_isinlist(dkim_signing_domain,
506                         &seen_items_list, 0, NULL, NULL, MCL_STRING, TRUE,
507                         NULL) == OK)
508       continue;
509
510     seen_items =
511       string_append(seen_items, &seen_items_size, &seen_items_offset, 1, ":");
512     }
513
514   seen_items =
515     string_append(seen_items, &seen_items_size, &seen_items_offset, 1,
516                  dkim_signing_domain);
517   seen_items[seen_items_offset] = '\0';
518
519   /* Set up $dkim_selector expansion variable. */
520
521   if (!(dkim_signing_selector = expand_string(dkim->dkim_selector)))
522     {
523     log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
524                "dkim_selector: %s", expand_string_message);
525     goto bad;
526     }
527
528   /* Get canonicalization to use */
529
530   dkim_canon_expanded = dkim->dkim_canon
531     ? expand_string(dkim->dkim_canon) : US"relaxed";
532   if (!dkim_canon_expanded)
533     {
534     /* expansion error, do not send message. */
535     log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
536                "dkim_canon: %s", expand_string_message);
537     goto bad;
538     }
539
540   if (Ustrcmp(dkim_canon_expanded, "relaxed") == 0)
541     pdkim_canon = PDKIM_CANON_RELAXED;
542   else if (Ustrcmp(dkim_canon_expanded, "simple") == 0)
543     pdkim_canon = PDKIM_CANON_SIMPLE;
544   else
545     {
546     log_write(0, LOG_MAIN,
547                "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",
548                dkim_canon_expanded);
549     pdkim_canon = PDKIM_CANON_RELAXED;
550     }
551
552   dkim_sign_headers_expanded = NULL;
553   if (dkim->dkim_sign_headers)
554     if (!(dkim_sign_headers_expanded = expand_string(dkim->dkim_sign_headers)))
555       {
556       log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
557                  "dkim_sign_headers: %s", expand_string_message);
558       goto bad;
559       }
560                         /* else pass NULL, which means default header list */
561
562   /* Get private key to use. */
563
564   if (!(dkim_private_key_expanded = expand_string(dkim->dkim_private_key)))
565     {
566     log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
567                "dkim_private_key: %s", expand_string_message);
568     goto bad;
569     }
570
571   if (  Ustrlen(dkim_private_key_expanded) == 0
572      || Ustrcmp(dkim_private_key_expanded, "0") == 0
573      || Ustrcmp(dkim_private_key_expanded, "false") == 0
574      )
575     continue;           /* don't sign, but no error */
576
577   if (dkim_private_key_expanded[0] == '/')
578     {
579     int privkey_fd, off = 0, len;
580
581     /* Looks like a filename, load the private key. */
582
583     memset(big_buffer, 0, big_buffer_size);
584
585     if ((privkey_fd = open(CS dkim_private_key_expanded, O_RDONLY)) < 0)
586       {
587       log_write(0, LOG_MAIN | LOG_PANIC, "unable to open "
588                  "private key file for reading: %s",
589                  dkim_private_key_expanded);
590       goto bad;
591       }
592
593     do
594       {
595       if ((len = read(privkey_fd, big_buffer + off, big_buffer_size - 2 - off)) < 0)
596         {
597         (void) close(privkey_fd);
598         log_write(0, LOG_MAIN|LOG_PANIC, "unable to read private key file: %s",
599                    dkim_private_key_expanded);
600         goto bad;
601         }
602       off += len;
603       }
604     while (len > 0);
605
606     (void) close(privkey_fd);
607     big_buffer[off] = '\0';
608     dkim_private_key_expanded = big_buffer;
609     }
610
611   if (!(dkim_hash_expanded = expand_string(dkim->dkim_hash)))
612     {
613     log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
614                "dkim_hash: %s", expand_string_message);
615     goto bad;
616     }
617
618 /*XXX so we currently nail signing to RSA + given hash.
619 Need to extract algo from privkey and check for disallowed combos. */
620
621   if (!(ctx = pdkim_init_sign(dkim_signing_domain,
622                         dkim_signing_selector,
623                         dkim_private_key_expanded,
624                         dkim_hash_expanded,
625                         dkim->dot_stuffed,
626                         &dkim_exim_query_dns_txt,
627                         errstr
628                         )))
629     goto bad;
630   dkim_private_key_expanded[0] = '\0';
631   pdkim_set_optional(ctx,
632                       CS dkim_sign_headers_expanded,
633                       NULL,
634                       pdkim_canon,
635                       pdkim_canon, -1, 0, 0);
636
637   if (prefix)
638     pdkim_feed(ctx, prefix, Ustrlen(prefix));
639
640   if (lseek(fd, off, SEEK_SET) < 0)
641     sread = -1;
642   else
643     while ((sread = read(fd, &buf, sizeof(buf))) > 0)
644       if ((pdkim_rc = pdkim_feed(ctx, buf, sread)) != PDKIM_OK)
645         goto pk_bad;
646
647   /* Handle failed read above. */
648   if (sread == -1)
649     {
650     debug_printf("DKIM: Error reading -K file.\n");
651     save_errno = errno;
652     goto bad;
653     }
654
655   if ((pdkim_rc = pdkim_feed_finish(ctx, &signature, errstr)) != PDKIM_OK)
656     goto pk_bad;
657
658   sigbuf = string_append(sigbuf, &sigsize, &sigptr, 2,
659                           US signature->signature_header, US"\r\n");
660
661   pdkim_free_ctx(ctx);
662   ctx = NULL;
663   }
664
665 if (sigbuf)
666   {
667   sigbuf[sigptr] = '\0';
668   rc = sigbuf;
669   }
670 else
671   rc = US"";
672
673 CLEANUP:
674   if (ctx)
675     pdkim_free_ctx(ctx);
676   store_pool = old_pool;
677   errno = save_errno;
678   return rc;
679
680 pk_bad:
681   log_write(0, LOG_MAIN|LOG_PANIC,
682                 "DKIM: signing failed: %.100s", pdkim_errstr(pdkim_rc));
683 bad:
684   rc = NULL;
685   goto CLEANUP;
686 }
687
688 #endif