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