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