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