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