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