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