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