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