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