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