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