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