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