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