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