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