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