TFO: early-data for ClamAV and for readsocket expansion
[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
15#include "pdkim/pdkim.h"
16
ca9cb170 17int dkim_verify_oldpool;
1cfe5c1c 18pdkim_ctx *dkim_verify_ctx = NULL;
80a47a2c 19pdkim_signature *dkim_signatures = NULL;
1cfe5c1c 20pdkim_signature *dkim_cur_sig = NULL;
b9df1829 21static const uschar * dkim_collect_error = NULL;
80a47a2c 22
d73e45df
JH
23
24
25/*XXX the caller only uses the first record if we return multiple.
26Could we hand back an allocated string?
27*/
28
1cfe5c1c
JH
29static int
30dkim_exim_query_dns_txt(char *name, char *answer)
31{
32dns_answer dnsa;
33dns_scan dnss;
34dns_record *rr;
80a47a2c 35
1cfe5c1c
JH
36lookup_dnssec_authenticated = NULL;
37if (dns_lookup(&dnsa, US name, T_TXT, NULL) != DNS_SUCCEED)
f7302073 38 return PDKIM_FAIL; /*XXX better error detail? logging? */
80a47a2c 39
1cfe5c1c 40/* Search for TXT record */
80a47a2c 41
1cfe5c1c
JH
42for (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 {
80a47a2c
TK
47 int rr_offset = 0;
48 int answer_offset = 0;
1cfe5c1c
JH
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,
5903c6ff 57 "%.*s", (int)len, CS (rr->data + rr_offset));
1cfe5c1c
JH
58 rr_offset += len;
59 answer_offset += len;
60 if (answer_offset >= PDKIM_DNS_TXT_MAX_RECLEN)
f7302073 61 return PDKIM_FAIL; /*XXX better error detail? logging? */
4263f395 62 }
1cfe5c1c 63 return PDKIM_OK;
80a47a2c 64 }
80a47a2c 65
f7302073 66return PDKIM_FAIL; /*XXX better error detail? logging? */
80a47a2c
TK
67}
68
69
2592e6c0
JH
70void
71dkim_exim_init(void)
72{
73pdkim_init();
74}
75
76
ca9cb170 77
1cfe5c1c 78void
e983e85a 79dkim_exim_verify_init(BOOL dot_stuffing)
1cfe5c1c 80{
ca9cb170
JH
81/* There is a store-reset between header & body reception
82so cannot use the main pool. Any allocs done by Exim
83memory-handling must use the perm pool. */
84
85dkim_verify_oldpool = store_pool;
86store_pool = POOL_PERM;
87
1cfe5c1c 88/* Free previous context if there is one */
80a47a2c 89
1cfe5c1c
JH
90if (dkim_verify_ctx)
91 pdkim_free_ctx(dkim_verify_ctx);
80a47a2c 92
1cfe5c1c 93/* Create new context */
80a47a2c 94
e983e85a 95dkim_verify_ctx = pdkim_init_verify(&dkim_exim_query_dns_txt, dot_stuffing);
3b957582 96dkim_collect_input = !!dkim_verify_ctx;
b9df1829 97dkim_collect_error = NULL;
ca9cb170 98
584e96c6
JH
99/* Start feed up with any cached data */
100receive_get_cache();
101
ca9cb170 102store_pool = dkim_verify_oldpool;
80a47a2c
TK
103}
104
105
1cfe5c1c
JH
106void
107dkim_exim_verify_feed(uschar * data, int len)
108{
f7302073
JH
109int rc;
110
ca9cb170 111store_pool = POOL_PERM;
1cfe5c1c 112if ( dkim_collect_input
ef698bf6 113 && (rc = pdkim_feed(dkim_verify_ctx, data, len)) != PDKIM_OK)
f7302073 114 {
b9df1829 115 dkim_collect_error = pdkim_errstr(rc);
f7302073 116 log_write(0, LOG_MAIN,
b9df1829 117 "DKIM: validation error: %.100s", dkim_collect_error);
1cfe5c1c 118 dkim_collect_input = FALSE;
f7302073 119 }
ca9cb170 120store_pool = dkim_verify_oldpool;
80a47a2c
TK
121}
122
123
1cfe5c1c
JH
124void
125dkim_exim_verify_finish(void)
126{
41468ba1 127pdkim_signature * sig = NULL;
b9df1829
JH
128int dkim_signers_size = 0, dkim_signers_ptr = 0, rc;
129const uschar * errstr;
1cfe5c1c 130
ca9cb170
JH
131store_pool = POOL_PERM;
132
1cfe5c1c
JH
133/* Delete eventual previous signature chain */
134
41468ba1 135dkim_signers = NULL;
1cfe5c1c
JH
136dkim_signatures = NULL;
137
bd8fbe36 138if (dkim_collect_error)
1cfe5c1c
JH
139 {
140 log_write(0, LOG_MAIN,
b9df1829
JH
141 "DKIM: Error during validation, disabling signature verification: %.100s",
142 dkim_collect_error);
1cfe5c1c 143 dkim_disable_verify = TRUE;
ca9cb170 144 goto out;
1cfe5c1c 145 }
80a47a2c 146
1cfe5c1c 147dkim_collect_input = FALSE;
80a47a2c 148
1cfe5c1c 149/* Finish DKIM operation and fetch link to signatures chain */
80a47a2c 150
b9df1829
JH
151rc = pdkim_feed_finish(dkim_verify_ctx, &dkim_signatures, &errstr);
152if (rc != PDKIM_OK)
f7302073 153 {
b9df1829
JH
154 log_write(0, LOG_MAIN, "DKIM: validation error: %.100s%s%s", pdkim_errstr(rc),
155 errstr ? ": " : "", errstr ? errstr : US"");
ca9cb170 156 goto out;
f7302073 157 }
1cfe5c1c
JH
158
159for (sig = dkim_signatures; sig; sig = sig->next)
160 {
41468ba1
JH
161 int size = 0, ptr = 0;
162 uschar * logmsg = NULL, * s;
1cfe5c1c
JH
163
164 /* Log a line for each signature */
165
41468ba1
JH
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",
d73e45df 173 " a=", dkim_sig_to_a_tag(sig),
41468ba1
JH
174 string_sprintf(" b=%d",
175 (int)sig->sighash.len > -1 ? sig->sighash.len * 8 : 0));
31323b30
HSHR
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,
41468ba1 178 string_sprintf(" t=%lu", sig->created));
31323b30 179 if (sig->expires > 0) logmsg = string_append(logmsg, &size, &ptr, 1,
41468ba1 180 string_sprintf(" x=%lu", sig->expires));
31323b30 181 if (sig->bodylength > -1) logmsg = string_append(logmsg, &size, &ptr, 1,
41468ba1 182 string_sprintf(" l=%lu", sig->bodylength));
1cfe5c1c
JH
183
184 switch (sig->verify_status)
185 {
186 case PDKIM_VERIFY_NONE:
41468ba1 187 logmsg = string_append(logmsg, &size, &ptr, 1, " [not verified]");
80a47a2c 188 break;
1cfe5c1c
JH
189
190 case PDKIM_VERIFY_INVALID:
41468ba1 191 logmsg = string_append(logmsg, &size, &ptr, 1, " [invalid - ");
1cfe5c1c
JH
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
df3def24
JH
204 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:
205 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT:
1cfe5c1c
JH
206 logmsg = string_append(logmsg, &size, &ptr, 1,
207 "syntax error in public key record]");
208 break;
07eeb4df 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;
1cfe5c1c
JH
219
220 default:
221 logmsg = string_append(logmsg, &size, &ptr, 1,
222 "unspecified problem]");
223 }
80a47a2c 224 break;
1cfe5c1c
JH
225
226 case PDKIM_VERIFY_FAIL:
227 logmsg =
41468ba1 228 string_append(logmsg, &size, &ptr, 1, " [verification failed - ");
1cfe5c1c
JH
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 }
80a47a2c 244 break;
1cfe5c1c
JH
245
246 case PDKIM_VERIFY_PASS:
247 logmsg =
41468ba1 248 string_append(logmsg, &size, &ptr, 1, " [verification succeeded]");
80a47a2c
TK
249 break;
250 }
251
1cfe5c1c
JH
252 logmsg[ptr] = '\0';
253 log_write(0, LOG_MAIN, "DKIM: %s", logmsg);
80a47a2c 254
1cfe5c1c
JH
255 /* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */
256
41468ba1 257 if (sig->domain)
4226691b
JH
258 dkim_signers = string_append_listele(dkim_signers, &dkim_signers_size,
259 &dkim_signers_ptr, ':', sig->domain);
1cfe5c1c
JH
260
261 if (sig->identity)
4226691b
JH
262 dkim_signers = string_append_listele(dkim_signers, &dkim_signers_size,
263 &dkim_signers_ptr, ':', sig->identity);
80a47a2c 264
1cfe5c1c 265 /* Process next signature */
80a47a2c
TK
266 }
267
ca9cb170
JH
268out:
269store_pool = dkim_verify_oldpool;
80a47a2c
TK
270}
271
272
1cfe5c1c
JH
273void
274dkim_exim_acl_setup(uschar * id)
275{
276pdkim_signature * sig;
277uschar * cmp_val;
278
1cfe5c1c
JH
279dkim_cur_sig = NULL;
280dkim_cur_signer = id;
281
282if (dkim_disable_verify || !id || !dkim_verify_ctx)
283 return;
284
285/* Find signature to run ACL on */
286
287for (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;
dcd03763 304 dkim_key_length = sig->sighash.len * 8;
1cfe5c1c 305 return;
80a47a2c 306 }
80a47a2c
TK
307}
308
309
1cfe5c1c
JH
310static uschar *
311dkim_exim_expand_defaults(int what)
312{
313switch (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}
80a47a2c 334
80a47a2c 335
1cfe5c1c
JH
336uschar *
337dkim_exim_expand_query(int what)
338{
339if (!dkim_verify_ctx || dkim_disable_verify || !dkim_cur_sig)
340 return dkim_exim_expand_defaults(what);
341
342switch (what)
343 {
344 case DKIM_ALGO:
d73e45df 345 return dkim_sig_to_a_tag(dkim_cur_sig);
1cfe5c1c
JH
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";
80a47a2c 358 }
1cfe5c1c
JH
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
2592e6c0 384 ? dkim_cur_sig->headernames : dkim_exim_expand_defaults(what);
1cfe5c1c
JH
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";
80a47a2c 433 }
80a47a2c 434
1cfe5c1c
JH
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";
df3def24
JH
440 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:return US"pubkey_dns_syntax";
441 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT: return US"pubkey_der_syntax";
1cfe5c1c
JH
442 case PDKIM_VERIFY_FAIL_BODY: return US"bodyhash_mismatch";
443 case PDKIM_VERIFY_FAIL_MESSAGE: return US"signature_incorrect";
444 }
80a47a2c 445
1cfe5c1c
JH
446 default:
447 return US"";
80a47a2c
TK
448 }
449}
450
451
42055a33
JH
452/* Generate signatures for the given file, returning a string.
453If a prefix is given, prepend it to the file for the calculations.
454*/
455
9e70917d 456blob *
42055a33
JH
457dkim_exim_sign(int fd, off_t off, uschar * prefix,
458 struct ob_dkim * dkim, const uschar ** errstr)
55414b25 459{
e983e85a 460const uschar * dkim_domain;
1cfe5c1c 461int sep = 0;
9e70917d
JH
462uschar * seen_doms = NULL;
463int seen_doms_size = 0;
464int seen_doms_offset = 0;
465pdkim_ctx ctx;
466pdkim_signature * sig;
467blob * sigbuf = NULL;
1cfe5c1c 468int sigsize = 0;
1cfe5c1c
JH
469int pdkim_rc;
470int sread;
ef698bf6 471uschar buf[4096];
1cfe5c1c
JH
472int save_errno = 0;
473int old_pool = store_pool;
474
475store_pool = POOL_MAIN;
476
9e70917d
JH
477pdkim_init_context(&ctx, dkim->dot_stuffed, &dkim_exim_query_dns_txt);
478
e983e85a 479if (!(dkim_domain = expand_cstring(dkim->dkim_domain)))
1cfe5c1c
JH
480 {
481 /* expansion error, do not send message. */
482 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
483 "dkim_domain: %s", expand_string_message);
f7302073 484 goto bad;
1cfe5c1c
JH
485 }
486
487/* Set $dkim_domain expansion variable to each unique domain in list. */
488
42055a33 489while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep, NULL, 0)))
1cfe5c1c 490 {
9e70917d
JH
491 const uschar * dkim_sel;
492 int sel_sep = 0;
493
42055a33 494 if (dkim_signing_domain[0] == '\0')
1cfe5c1c
JH
495 continue;
496
497 /* Only sign once for each domain, no matter how often it
498 appears in the expanded list. */
499
9e70917d
JH
500 if (match_isinlist(dkim_signing_domain, CUSS &seen_doms,
501 0, NULL, NULL, MCL_STRING, TRUE, NULL) == OK)
502 continue;
1cfe5c1c 503
9e70917d
JH
504 seen_doms = string_append_listele(seen_doms, &seen_doms_size,
505 &seen_doms_offset, ':', dkim_signing_domain);
1cfe5c1c 506
9e70917d
JH
507 /* Set $dkim_selector expansion variable to each selector in list,
508 for this domain. */
1cfe5c1c 509
9e70917d 510 if (!(dkim_sel = expand_string(dkim->dkim_selector)))
e983e85a 511 if (!(dkim_signing_selector = expand_string(dkim->dkim_selector)))
1cfe5c1c
JH
512 {
513 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
514 "dkim_selector: %s", expand_string_message);
f7302073 515 goto bad;
1cfe5c1c
JH
516 }
517
9e70917d
JH
518 while ((dkim_signing_selector = string_nextinlist(&dkim_sel, &sel_sep,
519 NULL, 0)))
1cfe5c1c 520 {
9e70917d
JH
521 uschar * dkim_canon_expanded;
522 int pdkim_canon;
523 uschar * dkim_sign_headers_expanded = NULL;
524 uschar * dkim_private_key_expanded;
525 uschar * dkim_hash_expanded;
80a47a2c 526
9e70917d 527 /* Get canonicalization to use */
1cfe5c1c 528
9e70917d
JH
529 dkim_canon_expanded = dkim->dkim_canon
530 ? expand_string(dkim->dkim_canon) : US"relaxed";
531 if (!dkim_canon_expanded)
1cfe5c1c 532 {
9e70917d 533 /* expansion error, do not send message. */
1cfe5c1c 534 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
9e70917d 535 "dkim_canon: %s", expand_string_message);
f7302073 536 goto bad;
1cfe5c1c 537 }
1cfe5c1c 538
9e70917d
JH
539 if (Ustrcmp(dkim_canon_expanded, "relaxed") == 0)
540 pdkim_canon = PDKIM_CANON_RELAXED;
541 else if (Ustrcmp(dkim_canon_expanded, "simple") == 0)
542 pdkim_canon = PDKIM_CANON_SIMPLE;
543 else
544 {
545 log_write(0, LOG_MAIN,
546 "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",
547 dkim_canon_expanded);
548 pdkim_canon = PDKIM_CANON_RELAXED;
549 }
1cfe5c1c 550
9e70917d
JH
551 if (dkim->dkim_sign_headers)
552 if (!(dkim_sign_headers_expanded = expand_string(dkim->dkim_sign_headers)))
553 {
554 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
555 "dkim_sign_headers: %s", expand_string_message);
556 goto bad;
557 }
558 /* else pass NULL, which means default header list */
1cfe5c1c 559
9e70917d 560 /* Get private key to use. */
e983e85a 561
9e70917d 562 if (!(dkim_private_key_expanded = expand_string(dkim->dkim_private_key)))
1cfe5c1c 563 {
9e70917d
JH
564 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
565 "dkim_private_key: %s", expand_string_message);
f7302073 566 goto bad;
db4d0902 567 }
80a47a2c 568
9e70917d
JH
569 if ( Ustrlen(dkim_private_key_expanded) == 0
570 || Ustrcmp(dkim_private_key_expanded, "0") == 0
571 || Ustrcmp(dkim_private_key_expanded, "false") == 0
572 )
573 continue; /* don't sign, but no error */
574
575 if (dkim_private_key_expanded[0] == '/')
1cfe5c1c 576 {
9e70917d
JH
577 int privkey_fd, off = 0, len;
578
579 /* Looks like a filename, load the private key. */
580
581 memset(big_buffer, 0, big_buffer_size);
582
583 if ((privkey_fd = open(CS dkim_private_key_expanded, O_RDONLY)) < 0)
59d98039 584 {
9e70917d
JH
585 log_write(0, LOG_MAIN | LOG_PANIC, "unable to open "
586 "private key file for reading: %s",
59d98039
JH
587 dkim_private_key_expanded);
588 goto bad;
589 }
9e70917d
JH
590
591 do
592 {
593 if ((len = read(privkey_fd, big_buffer + off, big_buffer_size - 2 - off)) < 0)
594 {
595 (void) close(privkey_fd);
596 log_write(0, LOG_MAIN|LOG_PANIC, "unable to read private key file: %s",
597 dkim_private_key_expanded);
598 goto bad;
599 }
600 off += len;
601 }
602 while (len > 0);
603
604 (void) close(privkey_fd);
605 big_buffer[off] = '\0';
606 dkim_private_key_expanded = big_buffer;
1ac6b2e7 607 }
80a47a2c 608
9e70917d
JH
609 if (!(dkim_hash_expanded = expand_string(dkim->dkim_hash)))
610 {
611 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
612 "dkim_hash: %s", expand_string_message);
613 goto bad;
614 }
1cfe5c1c 615
9e70917d
JH
616 /*XXX so we currently nail signing to RSA + this hash.
617 Need to extract algo from privkey and check for disallowed combos. */
d73e45df 618
9e70917d
JH
619 if (!(sig = pdkim_init_sign(&ctx, dkim_signing_domain,
620 dkim_signing_selector,
621 dkim_private_key_expanded,
622 dkim_hash_expanded,
623 errstr
624 )))
625 goto bad;
626 dkim_private_key_expanded[0] = '\0';
627
628 pdkim_set_optional(sig,
629 CS dkim_sign_headers_expanded,
630 NULL,
631 pdkim_canon,
632 pdkim_canon, -1, 0, 0);
633
634 if (!ctx.sig) /* link sig to context chain */
635 ctx.sig = sig;
636 else
637 {
638 pdkim_signature * n = ctx.sig;
639 while (n->next) n = n->next;
640 n->next = sig;
641 }
80a47a2c 642 }
9e70917d 643 }
80a47a2c 644
9e70917d
JH
645if (prefix)
646 pdkim_feed(&ctx, prefix, Ustrlen(prefix));
80a47a2c 647
9e70917d
JH
648if (lseek(fd, off, SEEK_SET) < 0)
649 sread = -1;
650else
651 while ((sread = read(fd, &buf, sizeof(buf))) > 0)
652 if ((pdkim_rc = pdkim_feed(&ctx, buf, sread)) != PDKIM_OK)
653 goto pk_bad;
80a47a2c 654
9e70917d
JH
655/* Handle failed read above. */
656if (sread == -1)
657 {
658 debug_printf("DKIM: Error reading -K file.\n");
659 save_errno = errno;
660 goto bad;
80a47a2c 661 }
a8e1eeba 662
9e70917d
JH
663/* Build string of headers, one per signature */
664
665if ((pdkim_rc = pdkim_feed_finish(&ctx, &sig, errstr)) != PDKIM_OK)
666 goto pk_bad;
667
668sigbuf = store_get(sizeof(blob));
669sigbuf->data = NULL;
670sigbuf->len = 0;
671
672while (sig)
1cfe5c1c 673 {
9e70917d
JH
674 int len = sigbuf->len;
675 sigbuf->data = string_append(sigbuf->data, &sigsize, &len, 2,
676 US sig->signature_header, US"\r\n");
677 sigbuf->len = len;
678 sig = sig->next;
1cfe5c1c 679 }
9e70917d
JH
680
681if (sigbuf->data)
682 sigbuf->data[sigbuf->len] = '\0';
1cfe5c1c 683else
9e70917d 684 sigbuf->data = US"";
1cfe5c1c
JH
685
686CLEANUP:
f7302073
JH
687 store_pool = old_pool;
688 errno = save_errno;
9e70917d 689 return sigbuf;
f7302073
JH
690
691pk_bad:
692 log_write(0, LOG_MAIN|LOG_PANIC,
693 "DKIM: signing failed: %.100s", pdkim_errstr(pdkim_rc));
694bad:
9e70917d 695 sigbuf = NULL;
f7302073 696 goto CLEANUP;
93f2d376 697}
80a47a2c
TK
698
699#endif