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