Build: .git may be a file when this repo is a submodule
[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.
d73e45df
JH
39*/
40
1eedc10f
JH
41static uschar *
42dkim_exim_query_dns_txt(char * name)
1cfe5c1c
JH
43{
44dns_answer dnsa;
45dns_scan dnss;
46dns_record *rr;
1eedc10f 47gstring * g = NULL;
80a47a2c 48
1cfe5c1c
JH
49lookup_dnssec_authenticated = NULL;
50if (dns_lookup(&dnsa, US name, T_TXT, NULL) != DNS_SUCCEED)
1eedc10f 51 return NULL; /*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++];
1eedc10f
JH
68
69 g = string_catn(g, US(rr->data + rr_offset), len);
70 if (g->ptr >= PDKIM_DNS_TXT_MAX_RECLEN)
71 goto bad;
72
1cfe5c1c 73 rr_offset += len;
4263f395 74 }
dd33c4e6
HSHR
75
76 /* check if this looks like a DKIM record */
1eedc10f
JH
77 if (strncmp(g->s, "v=", 2) != 0 || strncasecmp(g->s, "v=dkim", 6) == 0)
78 {
79 store_reset(g->s + g->ptr + 1);
80 return string_from_gstring(g);
81 }
82
83 if (g) g->ptr = 0; /* overwrite previous record */
80a47a2c 84 }
80a47a2c 85
1eedc10f
JH
86bad:
87if (g) store_reset(g);
88return NULL; /*XXX better error detail? logging? */
80a47a2c
TK
89}
90
91
2592e6c0
JH
92void
93dkim_exim_init(void)
94{
95pdkim_init();
96}
97
98
ca9cb170 99
1cfe5c1c 100void
e983e85a 101dkim_exim_verify_init(BOOL dot_stuffing)
1cfe5c1c 102{
ca9cb170
JH
103/* There is a store-reset between header & body reception
104so cannot use the main pool. Any allocs done by Exim
105memory-handling must use the perm pool. */
106
107dkim_verify_oldpool = store_pool;
108store_pool = POOL_PERM;
109
1cfe5c1c 110/* Free previous context if there is one */
80a47a2c 111
1cfe5c1c
JH
112if (dkim_verify_ctx)
113 pdkim_free_ctx(dkim_verify_ctx);
80a47a2c 114
1cfe5c1c 115/* Create new context */
80a47a2c 116
e983e85a 117dkim_verify_ctx = pdkim_init_verify(&dkim_exim_query_dns_txt, dot_stuffing);
3b957582 118dkim_collect_input = !!dkim_verify_ctx;
b9df1829 119dkim_collect_error = NULL;
ca9cb170 120
584e96c6
JH
121/* Start feed up with any cached data */
122receive_get_cache();
123
ca9cb170 124store_pool = dkim_verify_oldpool;
80a47a2c
TK
125}
126
127
1cfe5c1c
JH
128void
129dkim_exim_verify_feed(uschar * data, int len)
130{
f7302073
JH
131int rc;
132
ca9cb170 133store_pool = POOL_PERM;
1cfe5c1c 134if ( dkim_collect_input
ef698bf6 135 && (rc = pdkim_feed(dkim_verify_ctx, data, len)) != PDKIM_OK)
f7302073 136 {
b9df1829 137 dkim_collect_error = pdkim_errstr(rc);
f7302073 138 log_write(0, LOG_MAIN,
b9df1829 139 "DKIM: validation error: %.100s", dkim_collect_error);
1cfe5c1c 140 dkim_collect_input = FALSE;
f7302073 141 }
ca9cb170 142store_pool = dkim_verify_oldpool;
80a47a2c
TK
143}
144
145
a79d8834
JH
146/* Log the result for the given signature */
147static void
148dkim_exim_verify_log_sig(pdkim_signature * sig)
1cfe5c1c 149{
cc55f420 150gstring * logmsg;
a79d8834
JH
151uschar * s;
152
cc55f420
JH
153if (!sig) return;
154
2c47372f
JH
155if ( !dkim_verify_overall
156 && dkim_verify_status
157 ? Ustrcmp(dkim_verify_status, US"pass") == 0
158 : sig->verify_status == PDKIM_VERIFY_PASS
159 )
160 dkim_verify_overall = string_copy(sig->domain);
161
162if (!LOGGING(dkim_verbose)) return;
163
aaaa94ea 164logmsg = string_catn(NULL, US"DKIM: ", 6);
a79d8834
JH
165if (!(s = sig->domain)) s = US"<UNSET>";
166logmsg = string_append(logmsg, 2, "d=", s);
167if (!(s = sig->selector)) s = US"<UNSET>";
168logmsg = string_append(logmsg, 2, " s=", s);
dd33c4e6 169logmsg = string_append(logmsg, 7,
a79d8834
JH
170" c=", sig->canon_headers == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
171"/", sig->canon_body == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
172" a=", dkim_sig_to_a_tag(sig),
173string_sprintf(" b=" SIZE_T_FMT,
174 (int)sig->sighash.len > -1 ? sig->sighash.len * 8 : 0));
175if ((s= sig->identity)) logmsg = string_append(logmsg, 2, " i=", s);
176if (sig->created > 0) logmsg = string_cat(logmsg,
177 string_sprintf(" t=%lu", sig->created));
178if (sig->expires > 0) logmsg = string_cat(logmsg,
179 string_sprintf(" x=%lu", sig->expires));
180if (sig->bodylength > -1) logmsg = string_cat(logmsg,
181 string_sprintf(" l=%lu", sig->bodylength));
182
183if ( !dkim_verify_status
184 || ( dkim_verify_status == dkim_exim_expand_query(DKIM_VERIFY_STATUS)
185 && dkim_verify_reason == dkim_exim_expand_query(DKIM_VERIFY_REASON)
186 ) )
1cfe5c1c
JH
187 switch (sig->verify_status)
188 {
189 case PDKIM_VERIFY_NONE:
aaaa94ea 190 logmsg = string_cat(logmsg, US" [not verified]");
80a47a2c 191 break;
1cfe5c1c
JH
192
193 case PDKIM_VERIFY_INVALID:
aaaa94ea 194 logmsg = string_cat(logmsg, US" [invalid - ");
1cfe5c1c
JH
195 switch (sig->verify_ext_status)
196 {
197 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
acec9514 198 logmsg = string_cat(logmsg,
aaaa94ea 199 US"public key record (currently?) unavailable]");
1cfe5c1c
JH
200 break;
201
202 case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
aaaa94ea 203 logmsg = string_cat(logmsg, US"overlong public key record]");
1cfe5c1c
JH
204 break;
205
df3def24
JH
206 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:
207 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT:
aaaa94ea 208 logmsg = string_cat(logmsg, US"syntax error in public key record]");
1cfe5c1c 209 break;
07eeb4df 210
a79d8834 211 case PDKIM_VERIFY_INVALID_SIGNATURE_ERROR:
aaaa94ea 212 logmsg = string_cat(logmsg, US"signature tag missing or invalid]");
a79d8834 213 break;
07eeb4df 214
a79d8834 215 case PDKIM_VERIFY_INVALID_DKIM_VERSION:
aaaa94ea 216 logmsg = string_cat(logmsg, US"unsupported DKIM version]");
a79d8834 217 break;
1cfe5c1c
JH
218
219 default:
aaaa94ea 220 logmsg = string_cat(logmsg, US"unspecified problem]");
1cfe5c1c 221 }
80a47a2c 222 break;
1cfe5c1c
JH
223
224 case PDKIM_VERIFY_FAIL:
aaaa94ea 225 logmsg = string_cat(logmsg, US" [verification failed - ");
1cfe5c1c
JH
226 switch (sig->verify_ext_status)
227 {
228 case PDKIM_VERIFY_FAIL_BODY:
acec9514 229 logmsg = string_cat(logmsg,
aaaa94ea 230 US"body hash mismatch (body probably modified in transit)]");
1cfe5c1c
JH
231 break;
232
233 case PDKIM_VERIFY_FAIL_MESSAGE:
acec9514 234 logmsg = string_cat(logmsg,
aaaa94ea
JH
235 US"signature did not verify "
236 "(headers probably modified in transit)]");
1cfe5c1c
JH
237 break;
238
239 default:
aaaa94ea 240 logmsg = string_cat(logmsg, US"unspecified reason]");
1cfe5c1c 241 }
80a47a2c 242 break;
1cfe5c1c
JH
243
244 case PDKIM_VERIFY_PASS:
aaaa94ea 245 logmsg = string_cat(logmsg, US" [verification succeeded]");
80a47a2c
TK
246 break;
247 }
a79d8834
JH
248else
249 logmsg = string_append(logmsg, 5,
250 US" [", dkim_verify_status, US" - ", dkim_verify_reason, US"]");
251
aaaa94ea 252log_write(0, LOG_MAIN, "%s", string_from_gstring(logmsg));
a79d8834
JH
253return;
254}
255
80a47a2c 256
a79d8834
JH
257/* Log a line for each signature */
258void
259dkim_exim_verify_log_all(void)
260{
261pdkim_signature * sig;
262for (sig = dkim_signatures; sig; sig = sig->next) dkim_exim_verify_log_sig(sig);
263}
1cfe5c1c 264
80a47a2c 265
a79d8834
JH
266void
267dkim_exim_verify_finish(void)
268{
269pdkim_signature * sig;
270int rc;
271gstring * g = NULL;
272const uschar * errstr;
273
274store_pool = POOL_PERM;
275
276/* Delete eventual previous signature chain */
277
278dkim_signers = NULL;
279dkim_signatures = NULL;
280
281if (dkim_collect_error)
282 {
283 log_write(0, LOG_MAIN,
284 "DKIM: Error during validation, disabling signature verification: %.100s",
285 dkim_collect_error);
286 dkim_disable_verify = TRUE;
287 goto out;
288 }
289
290dkim_collect_input = FALSE;
291
292/* Finish DKIM operation and fetch link to signatures chain */
293
294rc = pdkim_feed_finish(dkim_verify_ctx, &dkim_signatures, &errstr);
295if (rc != PDKIM_OK)
296 {
297 log_write(0, LOG_MAIN, "DKIM: validation error: %.100s%s%s", pdkim_errstr(rc),
298 errstr ? ": " : "", errstr ? errstr : US"");
299 goto out;
300 }
301
302/* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */
303
304for (sig = dkim_signatures; sig; sig = sig->next)
305 {
306 if (sig->domain) g = string_append_listele(g, ':', sig->domain);
307 if (sig->identity) g = string_append_listele(g, ':', sig->identity);
80a47a2c
TK
308 }
309
acec9514
JH
310if (g) dkim_signers = g->s;
311
ca9cb170
JH
312out:
313store_pool = dkim_verify_oldpool;
80a47a2c
TK
314}
315
316
18067c75
JH
317
318/* Args as per dkim_exim_acl_run() below */
319static int
320dkim_acl_call(uschar * id, gstring ** res_ptr,
321 uschar ** user_msgptr, uschar ** log_msgptr)
322{
323int rc;
324DEBUG(D_receive)
325 debug_printf("calling acl_smtp_dkim for dkim_cur_signer='%s'\n", id);
326
327rc = acl_check(ACL_WHERE_DKIM, NULL, acl_smtp_dkim, user_msgptr, log_msgptr);
328dkim_exim_verify_log_sig(dkim_cur_sig);
329*res_ptr = string_append_listele(*res_ptr, ':', dkim_verify_status);
330return rc;
331}
332
333
334
335/* For the given identity, run the DKIM ACL once for each matching signature.
336
337Arguments
338 id Identity to look for in dkim signatures
339 res_ptr ptr to growable string-list of status results,
340 appended to per ACL run
341 user_msgptr where to put a user error (for SMTP response)
342 log_msgptr where to put a logging message (not for SMTP response)
343
344Returns: OK access is granted by an ACCEPT verb
345 DISCARD access is granted by a DISCARD verb
346 FAIL access is denied
347 FAIL_DROP access is denied; drop the connection
348 DEFER can't tell at the moment
349 ERROR disaster
350*/
351
352int
353dkim_exim_acl_run(uschar * id, gstring ** res_ptr,
354 uschar ** user_msgptr, uschar ** log_msgptr)
1cfe5c1c
JH
355{
356pdkim_signature * sig;
357uschar * cmp_val;
18067c75 358int rc = -1;
1cfe5c1c 359
cc55f420
JH
360dkim_verify_status = US"none";
361dkim_verify_reason = US"";
1cfe5c1c
JH
362dkim_cur_signer = id;
363
364if (dkim_disable_verify || !id || !dkim_verify_ctx)
18067c75 365 return OK;
1cfe5c1c 366
18067c75 367/* Find signatures to run ACL on */
1cfe5c1c
JH
368
369for (sig = dkim_signatures; sig; sig = sig->next)
370 if ( (cmp_val = Ustrchr(id, '@') != NULL ? US sig->identity : US sig->domain)
371 && strcmpic(cmp_val, id) == 0
372 )
373 {
1cfe5c1c 374 /* The "dkim_domain" and "dkim_selector" expansion variables have
18067c75
JH
375 related globals, since they are used in the signing code too.
376 Instead of inventing separate names for verification, we set
377 them here. This is easy since a domain and selector is guaranteed
378 to be in a signature. The other dkim_* expansion items are
379 dynamically fetched from dkim_cur_sig at expansion time (see
380 function below). */
1cfe5c1c 381
18067c75 382 dkim_cur_sig = sig;
1cfe5c1c
JH
383 dkim_signing_domain = US sig->domain;
384 dkim_signing_selector = US sig->selector;
dcd03763 385 dkim_key_length = sig->sighash.len * 8;
a79d8834
JH
386
387 /* These two return static strings, so we can compare the addr
388 later to see if the ACL overwrote them. Check that when logging */
389
390 dkim_verify_status = dkim_exim_expand_query(DKIM_VERIFY_STATUS);
391 dkim_verify_reason = dkim_exim_expand_query(DKIM_VERIFY_REASON);
dd33c4e6 392
18067c75
JH
393 if ((rc = dkim_acl_call(id, res_ptr, user_msgptr, log_msgptr)) != OK)
394 return rc;
80a47a2c 395 }
18067c75
JH
396
397if (rc != -1)
398 return rc;
399
400/* No matching sig found. Call ACL once anyway. */
401
402dkim_cur_sig = NULL;
403return dkim_acl_call(id, res_ptr, user_msgptr, log_msgptr);
80a47a2c
TK
404}
405
406
1cfe5c1c
JH
407static uschar *
408dkim_exim_expand_defaults(int what)
409{
410switch (what)
411 {
412 case DKIM_ALGO: return US"";
413 case DKIM_BODYLENGTH: return US"9999999999999";
414 case DKIM_CANON_BODY: return US"";
415 case DKIM_CANON_HEADERS: return US"";
416 case DKIM_COPIEDHEADERS: return US"";
417 case DKIM_CREATED: return US"0";
418 case DKIM_EXPIRES: return US"9999999999999";
419 case DKIM_HEADERNAMES: return US"";
420 case DKIM_IDENTITY: return US"";
421 case DKIM_KEY_GRANULARITY: return US"*";
422 case DKIM_KEY_SRVTYPE: return US"*";
423 case DKIM_KEY_NOTES: return US"";
424 case DKIM_KEY_TESTING: return US"0";
425 case DKIM_NOSUBDOMAINS: return US"0";
426 case DKIM_VERIFY_STATUS: return US"none";
427 case DKIM_VERIFY_REASON: return US"";
428 default: return US"";
429 }
430}
80a47a2c 431
80a47a2c 432
1cfe5c1c
JH
433uschar *
434dkim_exim_expand_query(int what)
435{
436if (!dkim_verify_ctx || dkim_disable_verify || !dkim_cur_sig)
437 return dkim_exim_expand_defaults(what);
438
439switch (what)
440 {
441 case DKIM_ALGO:
d73e45df 442 return dkim_sig_to_a_tag(dkim_cur_sig);
1cfe5c1c
JH
443
444 case DKIM_BODYLENGTH:
445 return dkim_cur_sig->bodylength >= 0
bb07bcd3 446 ? string_sprintf("%ld", dkim_cur_sig->bodylength)
1cfe5c1c
JH
447 : dkim_exim_expand_defaults(what);
448
449 case DKIM_CANON_BODY:
450 switch (dkim_cur_sig->canon_body)
451 {
452 case PDKIM_CANON_RELAXED: return US"relaxed";
453 case PDKIM_CANON_SIMPLE:
454 default: return US"simple";
80a47a2c 455 }
1cfe5c1c
JH
456
457 case DKIM_CANON_HEADERS:
a79d8834
JH
458 switch (dkim_cur_sig->canon_headers)
459 {
460 case PDKIM_CANON_RELAXED: return US"relaxed";
461 case PDKIM_CANON_SIMPLE:
462 default: return US"simple";
463 }
1cfe5c1c
JH
464
465 case DKIM_COPIEDHEADERS:
466 return dkim_cur_sig->copiedheaders
467 ? US dkim_cur_sig->copiedheaders : dkim_exim_expand_defaults(what);
468
469 case DKIM_CREATED:
470 return dkim_cur_sig->created > 0
bb07bcd3 471 ? string_sprintf("%lu", dkim_cur_sig->created)
1cfe5c1c
JH
472 : dkim_exim_expand_defaults(what);
473
474 case DKIM_EXPIRES:
475 return dkim_cur_sig->expires > 0
bb07bcd3 476 ? string_sprintf("%lu", dkim_cur_sig->expires)
1cfe5c1c
JH
477 : dkim_exim_expand_defaults(what);
478
479 case DKIM_HEADERNAMES:
480 return dkim_cur_sig->headernames
2592e6c0 481 ? dkim_cur_sig->headernames : dkim_exim_expand_defaults(what);
1cfe5c1c
JH
482
483 case DKIM_IDENTITY:
484 return dkim_cur_sig->identity
485 ? US dkim_cur_sig->identity : dkim_exim_expand_defaults(what);
486
487 case DKIM_KEY_GRANULARITY:
488 return dkim_cur_sig->pubkey
489 ? dkim_cur_sig->pubkey->granularity
490 ? US dkim_cur_sig->pubkey->granularity
491 : dkim_exim_expand_defaults(what)
492 : dkim_exim_expand_defaults(what);
493
494 case DKIM_KEY_SRVTYPE:
495 return dkim_cur_sig->pubkey
496 ? dkim_cur_sig->pubkey->srvtype
497 ? US dkim_cur_sig->pubkey->srvtype
498 : dkim_exim_expand_defaults(what)
499 : dkim_exim_expand_defaults(what);
500
501 case DKIM_KEY_NOTES:
502 return dkim_cur_sig->pubkey
503 ? dkim_cur_sig->pubkey->notes
504 ? US dkim_cur_sig->pubkey->notes
505 : dkim_exim_expand_defaults(what)
506 : dkim_exim_expand_defaults(what);
507
508 case DKIM_KEY_TESTING:
509 return dkim_cur_sig->pubkey
510 ? dkim_cur_sig->pubkey->testing
511 ? US"1"
512 : dkim_exim_expand_defaults(what)
513 : dkim_exim_expand_defaults(what);
514
515 case DKIM_NOSUBDOMAINS:
516 return dkim_cur_sig->pubkey
517 ? dkim_cur_sig->pubkey->no_subdomaining
518 ? US"1"
519 : dkim_exim_expand_defaults(what)
520 : dkim_exim_expand_defaults(what);
521
522 case DKIM_VERIFY_STATUS:
523 switch (dkim_cur_sig->verify_status)
524 {
525 case PDKIM_VERIFY_INVALID: return US"invalid";
526 case PDKIM_VERIFY_FAIL: return US"fail";
527 case PDKIM_VERIFY_PASS: return US"pass";
528 case PDKIM_VERIFY_NONE:
529 default: return US"none";
80a47a2c 530 }
80a47a2c 531
1cfe5c1c
JH
532 case DKIM_VERIFY_REASON:
533 switch (dkim_cur_sig->verify_ext_status)
534 {
535 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
536 return US"pubkey_unavailable";
df3def24
JH
537 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:return US"pubkey_dns_syntax";
538 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT: return US"pubkey_der_syntax";
1cfe5c1c
JH
539 case PDKIM_VERIFY_FAIL_BODY: return US"bodyhash_mismatch";
540 case PDKIM_VERIFY_FAIL_MESSAGE: return US"signature_incorrect";
541 }
80a47a2c 542
1cfe5c1c
JH
543 default:
544 return US"";
80a47a2c
TK
545 }
546}
547
548
0b8f4f1a 549/* Generate signatures for the given file.
42055a33 550If a prefix is given, prepend it to the file for the calculations.
0b8f4f1a
JH
551
552Return:
553 NULL: error; error string written
554 string: signature header(s), or a zero-length string (not an error)
42055a33
JH
555*/
556
acec9514 557gstring *
42055a33
JH
558dkim_exim_sign(int fd, off_t off, uschar * prefix,
559 struct ob_dkim * dkim, const uschar ** errstr)
55414b25 560{
e983e85a 561const uschar * dkim_domain;
1cfe5c1c 562int sep = 0;
acec9514 563gstring * seen_doms = NULL;
9e70917d
JH
564pdkim_ctx ctx;
565pdkim_signature * sig;
acec9514 566gstring * sigbuf;
1cfe5c1c
JH
567int pdkim_rc;
568int sread;
ef698bf6 569uschar buf[4096];
1cfe5c1c
JH
570int save_errno = 0;
571int old_pool = store_pool;
7c6ec81b 572uschar * errwhen;
1cfe5c1c
JH
573
574store_pool = POOL_MAIN;
575
9e70917d
JH
576pdkim_init_context(&ctx, dkim->dot_stuffed, &dkim_exim_query_dns_txt);
577
e983e85a 578if (!(dkim_domain = expand_cstring(dkim->dkim_domain)))
1cfe5c1c 579 /* expansion error, do not send message. */
7c6ec81b 580 { errwhen = US"dkim_domain"; goto expand_bad; }
1cfe5c1c
JH
581
582/* Set $dkim_domain expansion variable to each unique domain in list. */
583
42055a33 584while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep, NULL, 0)))
1cfe5c1c 585 {
9e70917d
JH
586 const uschar * dkim_sel;
587 int sel_sep = 0;
588
42055a33 589 if (dkim_signing_domain[0] == '\0')
1cfe5c1c
JH
590 continue;
591
592 /* Only sign once for each domain, no matter how often it
593 appears in the expanded list. */
594
9e70917d
JH
595 if (match_isinlist(dkim_signing_domain, CUSS &seen_doms,
596 0, NULL, NULL, MCL_STRING, TRUE, NULL) == OK)
597 continue;
1cfe5c1c 598
acec9514 599 seen_doms = string_append_listele(seen_doms, ':', dkim_signing_domain);
1cfe5c1c 600
9e70917d
JH
601 /* Set $dkim_selector expansion variable to each selector in list,
602 for this domain. */
1cfe5c1c 603
9e70917d 604 if (!(dkim_sel = expand_string(dkim->dkim_selector)))
e983e85a 605 if (!(dkim_signing_selector = expand_string(dkim->dkim_selector)))
7c6ec81b 606 { errwhen = US"dkim_selector"; goto expand_bad; }
1cfe5c1c 607
9e70917d
JH
608 while ((dkim_signing_selector = string_nextinlist(&dkim_sel, &sel_sep,
609 NULL, 0)))
1cfe5c1c 610 {
9e70917d
JH
611 uschar * dkim_canon_expanded;
612 int pdkim_canon;
613 uschar * dkim_sign_headers_expanded = NULL;
614 uschar * dkim_private_key_expanded;
615 uschar * dkim_hash_expanded;
7c6ec81b 616 uschar * dkim_identity_expanded = NULL;
80a47a2c 617
9e70917d 618 /* Get canonicalization to use */
1cfe5c1c 619
9e70917d
JH
620 dkim_canon_expanded = dkim->dkim_canon
621 ? expand_string(dkim->dkim_canon) : US"relaxed";
7c6ec81b
JH
622 if (!dkim_canon_expanded) /* expansion error, do not send message. */
623 { errwhen = US"dkim_canon"; goto expand_bad; }
1cfe5c1c 624
9e70917d
JH
625 if (Ustrcmp(dkim_canon_expanded, "relaxed") == 0)
626 pdkim_canon = PDKIM_CANON_RELAXED;
627 else if (Ustrcmp(dkim_canon_expanded, "simple") == 0)
628 pdkim_canon = PDKIM_CANON_SIMPLE;
629 else
630 {
631 log_write(0, LOG_MAIN,
632 "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",
633 dkim_canon_expanded);
634 pdkim_canon = PDKIM_CANON_RELAXED;
635 }
1cfe5c1c 636
7c6ec81b
JH
637 if ( dkim->dkim_sign_headers
638 && !(dkim_sign_headers_expanded = expand_string(dkim->dkim_sign_headers)))
639 { errwhen = US"dkim_sign_header"; goto expand_bad; }
640 /* else pass NULL, which means default header list */
1cfe5c1c 641
9e70917d 642 /* Get private key to use. */
e983e85a 643
9e70917d 644 if (!(dkim_private_key_expanded = expand_string(dkim->dkim_private_key)))
7c6ec81b 645 { errwhen = US"dkim_private_key"; goto expand_bad; }
80a47a2c 646
9e70917d
JH
647 if ( Ustrlen(dkim_private_key_expanded) == 0
648 || Ustrcmp(dkim_private_key_expanded, "0") == 0
649 || Ustrcmp(dkim_private_key_expanded, "false") == 0
650 )
651 continue; /* don't sign, but no error */
652
653 if (dkim_private_key_expanded[0] == '/')
1cfe5c1c 654 {
9e70917d
JH
655 int privkey_fd, off = 0, len;
656
657 /* Looks like a filename, load the private key. */
658
659 memset(big_buffer, 0, big_buffer_size);
660
661 if ((privkey_fd = open(CS dkim_private_key_expanded, O_RDONLY)) < 0)
59d98039 662 {
9e70917d
JH
663 log_write(0, LOG_MAIN | LOG_PANIC, "unable to open "
664 "private key file for reading: %s",
59d98039
JH
665 dkim_private_key_expanded);
666 goto bad;
667 }
9e70917d
JH
668
669 do
670 {
671 if ((len = read(privkey_fd, big_buffer + off, big_buffer_size - 2 - off)) < 0)
672 {
673 (void) close(privkey_fd);
674 log_write(0, LOG_MAIN|LOG_PANIC, "unable to read private key file: %s",
675 dkim_private_key_expanded);
676 goto bad;
677 }
678 off += len;
679 }
680 while (len > 0);
681
682 (void) close(privkey_fd);
683 big_buffer[off] = '\0';
684 dkim_private_key_expanded = big_buffer;
1ac6b2e7 685 }
80a47a2c 686
9e70917d 687 if (!(dkim_hash_expanded = expand_string(dkim->dkim_hash)))
7c6ec81b
JH
688 { errwhen = US"dkim_hash"; goto expand_bad; }
689
690 if (dkim->dkim_identity)
691 if (!(dkim_identity_expanded = expand_string(dkim->dkim_identity)))
692 { errwhen = US"dkim_identity"; goto expand_bad; }
693 else if (!*dkim_identity_expanded)
694 dkim_identity_expanded = NULL;
1cfe5c1c 695
9e70917d
JH
696 /*XXX so we currently nail signing to RSA + this hash.
697 Need to extract algo from privkey and check for disallowed combos. */
d73e45df 698
9e70917d
JH
699 if (!(sig = pdkim_init_sign(&ctx, dkim_signing_domain,
700 dkim_signing_selector,
701 dkim_private_key_expanded,
702 dkim_hash_expanded,
703 errstr
704 )))
705 goto bad;
706 dkim_private_key_expanded[0] = '\0';
707
708 pdkim_set_optional(sig,
709 CS dkim_sign_headers_expanded,
27fd1318 710 CS dkim_identity_expanded,
9e70917d
JH
711 pdkim_canon,
712 pdkim_canon, -1, 0, 0);
713
714 if (!ctx.sig) /* link sig to context chain */
715 ctx.sig = sig;
716 else
717 {
718 pdkim_signature * n = ctx.sig;
719 while (n->next) n = n->next;
720 n->next = sig;
721 }
80a47a2c 722 }
9e70917d 723 }
0b8f4f1a
JH
724if (!ctx.sig)
725 {
726 DEBUG(D_transport) debug_printf("DKIM: no viable signatures to use\n");
727 sigbuf = string_get(1); /* return a zero-len string */
728 goto CLEANUP;
729 }
80a47a2c 730
0b8f4f1a
JH
731if (prefix && (pdkim_feed(&ctx, prefix, Ustrlen(prefix))) != PDKIM_OK)
732 goto pk_bad;
80a47a2c 733
9e70917d
JH
734if (lseek(fd, off, SEEK_SET) < 0)
735 sread = -1;
736else
737 while ((sread = read(fd, &buf, sizeof(buf))) > 0)
738 if ((pdkim_rc = pdkim_feed(&ctx, buf, sread)) != PDKIM_OK)
739 goto pk_bad;
80a47a2c 740
9e70917d
JH
741/* Handle failed read above. */
742if (sread == -1)
743 {
744 debug_printf("DKIM: Error reading -K file.\n");
745 save_errno = errno;
746 goto bad;
80a47a2c 747 }
a8e1eeba 748
9e70917d
JH
749/* Build string of headers, one per signature */
750
751if ((pdkim_rc = pdkim_feed_finish(&ctx, &sig, errstr)) != PDKIM_OK)
752 goto pk_bad;
753
acec9514
JH
754for (sigbuf = NULL; sig; sig = sig->next)
755 sigbuf = string_append(sigbuf, 2, US sig->signature_header, US"\r\n");
9e70917d 756
1cfe5c1c 757CLEANUP:
0b8f4f1a 758 (void) string_from_gstring(sigbuf);
f7302073
JH
759 store_pool = old_pool;
760 errno = save_errno;
9e70917d 761 return sigbuf;
f7302073
JH
762
763pk_bad:
764 log_write(0, LOG_MAIN|LOG_PANIC,
765 "DKIM: signing failed: %.100s", pdkim_errstr(pdkim_rc));
766bad:
9e70917d 767 sigbuf = NULL;
f7302073 768 goto CLEANUP;
7c6ec81b
JH
769
770expand_bad:
771 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand %s: %s",
772 errwhen, expand_string_message);
773 goto bad;
93f2d376 774}
80a47a2c 775
970424a5
JH
776# endif /*!MACRO_PREDEF*/
777#endif /*!DISABLE_DKIM*/