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