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