Debug: fix output for acl add_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
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
315rc = pdkim_feed_finish(dkim_verify_ctx, &dkim_signatures, &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
321for (sig = dkim_signatures; sig; sig = sig->next)
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
JH
372{
373pdkim_signature * sig;
374uschar * cmp_val;
18067c75 375int rc = -1;
1cfe5c1c 376
cc55f420
JH
377dkim_verify_status = US"none";
378dkim_verify_reason = US"";
1cfe5c1c
JH
379dkim_cur_signer = id;
380
381if (dkim_disable_verify || !id || !dkim_verify_ctx)
18067c75 382 return OK;
1cfe5c1c 383
18067c75 384/* Find signatures to run ACL on */
1cfe5c1c
JH
385
386for (sig = dkim_signatures; sig; sig = sig->next)
387 if ( (cmp_val = Ustrchr(id, '@') != NULL ? US sig->identity : US sig->domain)
388 && strcmpic(cmp_val, id) == 0
389 )
390 {
1cfe5c1c 391 /* The "dkim_domain" and "dkim_selector" expansion variables have
18067c75
JH
392 related globals, since they are used in the signing code too.
393 Instead of inventing separate names for verification, we set
394 them here. This is easy since a domain and selector is guaranteed
395 to be in a signature. The other dkim_* expansion items are
396 dynamically fetched from dkim_cur_sig at expansion time (see
397 function below). */
1cfe5c1c 398
18067c75 399 dkim_cur_sig = sig;
1cfe5c1c
JH
400 dkim_signing_domain = US sig->domain;
401 dkim_signing_selector = US sig->selector;
dcd03763 402 dkim_key_length = sig->sighash.len * 8;
a79d8834
JH
403
404 /* These two return static strings, so we can compare the addr
405 later to see if the ACL overwrote them. Check that when logging */
406
407 dkim_verify_status = dkim_exim_expand_query(DKIM_VERIFY_STATUS);
408 dkim_verify_reason = dkim_exim_expand_query(DKIM_VERIFY_REASON);
dd33c4e6 409
18067c75
JH
410 if ((rc = dkim_acl_call(id, res_ptr, user_msgptr, log_msgptr)) != OK)
411 return rc;
80a47a2c 412 }
18067c75
JH
413
414if (rc != -1)
415 return rc;
416
417/* No matching sig found. Call ACL once anyway. */
418
419dkim_cur_sig = NULL;
420return dkim_acl_call(id, res_ptr, user_msgptr, log_msgptr);
80a47a2c
TK
421}
422
423
1cfe5c1c
JH
424static uschar *
425dkim_exim_expand_defaults(int what)
426{
427switch (what)
428 {
429 case DKIM_ALGO: return US"";
430 case DKIM_BODYLENGTH: return US"9999999999999";
431 case DKIM_CANON_BODY: return US"";
432 case DKIM_CANON_HEADERS: return US"";
433 case DKIM_COPIEDHEADERS: return US"";
434 case DKIM_CREATED: return US"0";
435 case DKIM_EXPIRES: return US"9999999999999";
436 case DKIM_HEADERNAMES: return US"";
437 case DKIM_IDENTITY: return US"";
438 case DKIM_KEY_GRANULARITY: return US"*";
439 case DKIM_KEY_SRVTYPE: return US"*";
440 case DKIM_KEY_NOTES: return US"";
441 case DKIM_KEY_TESTING: return US"0";
442 case DKIM_NOSUBDOMAINS: return US"0";
443 case DKIM_VERIFY_STATUS: return US"none";
444 case DKIM_VERIFY_REASON: return US"";
445 default: return US"";
446 }
447}
80a47a2c 448
80a47a2c 449
1cfe5c1c
JH
450uschar *
451dkim_exim_expand_query(int what)
452{
453if (!dkim_verify_ctx || dkim_disable_verify || !dkim_cur_sig)
454 return dkim_exim_expand_defaults(what);
455
456switch (what)
457 {
458 case DKIM_ALGO:
d73e45df 459 return dkim_sig_to_a_tag(dkim_cur_sig);
1cfe5c1c
JH
460
461 case DKIM_BODYLENGTH:
462 return dkim_cur_sig->bodylength >= 0
bb07bcd3 463 ? string_sprintf("%ld", dkim_cur_sig->bodylength)
1cfe5c1c
JH
464 : dkim_exim_expand_defaults(what);
465
466 case DKIM_CANON_BODY:
467 switch (dkim_cur_sig->canon_body)
468 {
469 case PDKIM_CANON_RELAXED: return US"relaxed";
470 case PDKIM_CANON_SIMPLE:
471 default: return US"simple";
80a47a2c 472 }
1cfe5c1c
JH
473
474 case DKIM_CANON_HEADERS:
a79d8834
JH
475 switch (dkim_cur_sig->canon_headers)
476 {
477 case PDKIM_CANON_RELAXED: return US"relaxed";
478 case PDKIM_CANON_SIMPLE:
479 default: return US"simple";
480 }
1cfe5c1c
JH
481
482 case DKIM_COPIEDHEADERS:
483 return dkim_cur_sig->copiedheaders
484 ? US dkim_cur_sig->copiedheaders : dkim_exim_expand_defaults(what);
485
486 case DKIM_CREATED:
487 return dkim_cur_sig->created > 0
bb07bcd3 488 ? string_sprintf("%lu", dkim_cur_sig->created)
1cfe5c1c
JH
489 : dkim_exim_expand_defaults(what);
490
491 case DKIM_EXPIRES:
492 return dkim_cur_sig->expires > 0
bb07bcd3 493 ? string_sprintf("%lu", dkim_cur_sig->expires)
1cfe5c1c
JH
494 : dkim_exim_expand_defaults(what);
495
496 case DKIM_HEADERNAMES:
497 return dkim_cur_sig->headernames
2592e6c0 498 ? dkim_cur_sig->headernames : dkim_exim_expand_defaults(what);
1cfe5c1c
JH
499
500 case DKIM_IDENTITY:
501 return dkim_cur_sig->identity
502 ? US dkim_cur_sig->identity : dkim_exim_expand_defaults(what);
503
504 case DKIM_KEY_GRANULARITY:
505 return dkim_cur_sig->pubkey
506 ? dkim_cur_sig->pubkey->granularity
507 ? US dkim_cur_sig->pubkey->granularity
508 : dkim_exim_expand_defaults(what)
509 : dkim_exim_expand_defaults(what);
510
511 case DKIM_KEY_SRVTYPE:
512 return dkim_cur_sig->pubkey
513 ? dkim_cur_sig->pubkey->srvtype
514 ? US dkim_cur_sig->pubkey->srvtype
515 : dkim_exim_expand_defaults(what)
516 : dkim_exim_expand_defaults(what);
517
518 case DKIM_KEY_NOTES:
519 return dkim_cur_sig->pubkey
520 ? dkim_cur_sig->pubkey->notes
521 ? US dkim_cur_sig->pubkey->notes
522 : dkim_exim_expand_defaults(what)
523 : dkim_exim_expand_defaults(what);
524
525 case DKIM_KEY_TESTING:
526 return dkim_cur_sig->pubkey
527 ? dkim_cur_sig->pubkey->testing
528 ? US"1"
529 : dkim_exim_expand_defaults(what)
530 : dkim_exim_expand_defaults(what);
531
532 case DKIM_NOSUBDOMAINS:
533 return dkim_cur_sig->pubkey
534 ? dkim_cur_sig->pubkey->no_subdomaining
535 ? US"1"
536 : dkim_exim_expand_defaults(what)
537 : dkim_exim_expand_defaults(what);
538
539 case DKIM_VERIFY_STATUS:
540 switch (dkim_cur_sig->verify_status)
541 {
542 case PDKIM_VERIFY_INVALID: return US"invalid";
543 case PDKIM_VERIFY_FAIL: return US"fail";
544 case PDKIM_VERIFY_PASS: return US"pass";
545 case PDKIM_VERIFY_NONE:
546 default: return US"none";
80a47a2c 547 }
80a47a2c 548
1cfe5c1c
JH
549 case DKIM_VERIFY_REASON:
550 switch (dkim_cur_sig->verify_ext_status)
551 {
552 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
553 return US"pubkey_unavailable";
df3def24
JH
554 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:return US"pubkey_dns_syntax";
555 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT: return US"pubkey_der_syntax";
1cfe5c1c
JH
556 case PDKIM_VERIFY_FAIL_BODY: return US"bodyhash_mismatch";
557 case PDKIM_VERIFY_FAIL_MESSAGE: return US"signature_incorrect";
558 }
80a47a2c 559
1cfe5c1c
JH
560 default:
561 return US"";
80a47a2c
TK
562 }
563}
564
565
0b8f4f1a 566/* Generate signatures for the given file.
42055a33 567If a prefix is given, prepend it to the file for the calculations.
0b8f4f1a
JH
568
569Return:
570 NULL: error; error string written
571 string: signature header(s), or a zero-length string (not an error)
42055a33
JH
572*/
573
acec9514 574gstring *
42055a33
JH
575dkim_exim_sign(int fd, off_t off, uschar * prefix,
576 struct ob_dkim * dkim, const uschar ** errstr)
55414b25 577{
e983e85a 578const uschar * dkim_domain;
1cfe5c1c 579int sep = 0;
acec9514 580gstring * seen_doms = NULL;
9e70917d
JH
581pdkim_ctx ctx;
582pdkim_signature * sig;
acec9514 583gstring * sigbuf;
1cfe5c1c
JH
584int pdkim_rc;
585int sread;
ef698bf6 586uschar buf[4096];
1cfe5c1c
JH
587int save_errno = 0;
588int old_pool = store_pool;
7c6ec81b 589uschar * errwhen;
1cfe5c1c
JH
590
591store_pool = POOL_MAIN;
592
9e70917d
JH
593pdkim_init_context(&ctx, dkim->dot_stuffed, &dkim_exim_query_dns_txt);
594
e983e85a 595if (!(dkim_domain = expand_cstring(dkim->dkim_domain)))
1cfe5c1c 596 /* expansion error, do not send message. */
7c6ec81b 597 { errwhen = US"dkim_domain"; goto expand_bad; }
1cfe5c1c
JH
598
599/* Set $dkim_domain expansion variable to each unique domain in list. */
600
42055a33 601while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep, NULL, 0)))
1cfe5c1c 602 {
9e70917d
JH
603 const uschar * dkim_sel;
604 int sel_sep = 0;
605
42055a33 606 if (dkim_signing_domain[0] == '\0')
1cfe5c1c
JH
607 continue;
608
609 /* Only sign once for each domain, no matter how often it
610 appears in the expanded list. */
611
9e70917d
JH
612 if (match_isinlist(dkim_signing_domain, CUSS &seen_doms,
613 0, NULL, NULL, MCL_STRING, TRUE, NULL) == OK)
614 continue;
1cfe5c1c 615
acec9514 616 seen_doms = string_append_listele(seen_doms, ':', dkim_signing_domain);
1cfe5c1c 617
9e70917d
JH
618 /* Set $dkim_selector expansion variable to each selector in list,
619 for this domain. */
1cfe5c1c 620
9e70917d 621 if (!(dkim_sel = expand_string(dkim->dkim_selector)))
e983e85a 622 if (!(dkim_signing_selector = expand_string(dkim->dkim_selector)))
7c6ec81b 623 { errwhen = US"dkim_selector"; goto expand_bad; }
1cfe5c1c 624
9e70917d
JH
625 while ((dkim_signing_selector = string_nextinlist(&dkim_sel, &sel_sep,
626 NULL, 0)))
1cfe5c1c 627 {
9e70917d
JH
628 uschar * dkim_canon_expanded;
629 int pdkim_canon;
630 uschar * dkim_sign_headers_expanded = NULL;
631 uschar * dkim_private_key_expanded;
632 uschar * dkim_hash_expanded;
7c6ec81b 633 uschar * dkim_identity_expanded = NULL;
80a47a2c 634
9e70917d 635 /* Get canonicalization to use */
1cfe5c1c 636
9e70917d
JH
637 dkim_canon_expanded = dkim->dkim_canon
638 ? expand_string(dkim->dkim_canon) : US"relaxed";
7c6ec81b
JH
639 if (!dkim_canon_expanded) /* expansion error, do not send message. */
640 { errwhen = US"dkim_canon"; goto expand_bad; }
1cfe5c1c 641
9e70917d
JH
642 if (Ustrcmp(dkim_canon_expanded, "relaxed") == 0)
643 pdkim_canon = PDKIM_CANON_RELAXED;
644 else if (Ustrcmp(dkim_canon_expanded, "simple") == 0)
645 pdkim_canon = PDKIM_CANON_SIMPLE;
646 else
647 {
648 log_write(0, LOG_MAIN,
649 "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",
650 dkim_canon_expanded);
651 pdkim_canon = PDKIM_CANON_RELAXED;
652 }
1cfe5c1c 653
7c6ec81b
JH
654 if ( dkim->dkim_sign_headers
655 && !(dkim_sign_headers_expanded = expand_string(dkim->dkim_sign_headers)))
656 { errwhen = US"dkim_sign_header"; goto expand_bad; }
657 /* else pass NULL, which means default header list */
1cfe5c1c 658
9e70917d 659 /* Get private key to use. */
e983e85a 660
9e70917d 661 if (!(dkim_private_key_expanded = expand_string(dkim->dkim_private_key)))
7c6ec81b 662 { errwhen = US"dkim_private_key"; goto expand_bad; }
80a47a2c 663
9e70917d
JH
664 if ( Ustrlen(dkim_private_key_expanded) == 0
665 || Ustrcmp(dkim_private_key_expanded, "0") == 0
666 || Ustrcmp(dkim_private_key_expanded, "false") == 0
667 )
668 continue; /* don't sign, but no error */
669
670 if (dkim_private_key_expanded[0] == '/')
1cfe5c1c 671 {
9e70917d
JH
672 int privkey_fd, off = 0, len;
673
674 /* Looks like a filename, load the private key. */
675
676 memset(big_buffer, 0, big_buffer_size);
677
678 if ((privkey_fd = open(CS dkim_private_key_expanded, O_RDONLY)) < 0)
59d98039 679 {
9e70917d
JH
680 log_write(0, LOG_MAIN | LOG_PANIC, "unable to open "
681 "private key file for reading: %s",
59d98039
JH
682 dkim_private_key_expanded);
683 goto bad;
684 }
9e70917d
JH
685
686 do
687 {
688 if ((len = read(privkey_fd, big_buffer + off, big_buffer_size - 2 - off)) < 0)
689 {
690 (void) close(privkey_fd);
691 log_write(0, LOG_MAIN|LOG_PANIC, "unable to read private key file: %s",
692 dkim_private_key_expanded);
693 goto bad;
694 }
695 off += len;
696 }
697 while (len > 0);
698
699 (void) close(privkey_fd);
700 big_buffer[off] = '\0';
701 dkim_private_key_expanded = big_buffer;
1ac6b2e7 702 }
80a47a2c 703
9e70917d 704 if (!(dkim_hash_expanded = expand_string(dkim->dkim_hash)))
7c6ec81b
JH
705 { errwhen = US"dkim_hash"; goto expand_bad; }
706
707 if (dkim->dkim_identity)
708 if (!(dkim_identity_expanded = expand_string(dkim->dkim_identity)))
709 { errwhen = US"dkim_identity"; goto expand_bad; }
710 else if (!*dkim_identity_expanded)
711 dkim_identity_expanded = NULL;
1cfe5c1c 712
9e70917d
JH
713 /*XXX so we currently nail signing to RSA + this hash.
714 Need to extract algo from privkey and check for disallowed combos. */
d73e45df 715
9e70917d
JH
716 if (!(sig = pdkim_init_sign(&ctx, dkim_signing_domain,
717 dkim_signing_selector,
718 dkim_private_key_expanded,
719 dkim_hash_expanded,
720 errstr
721 )))
722 goto bad;
723 dkim_private_key_expanded[0] = '\0';
724
725 pdkim_set_optional(sig,
726 CS dkim_sign_headers_expanded,
27fd1318 727 CS dkim_identity_expanded,
9e70917d
JH
728 pdkim_canon,
729 pdkim_canon, -1, 0, 0);
730
cf1cce5e
JH
731 if (!pdkim_set_bodyhash(&ctx, sig))
732 goto bad;
733
9e70917d
JH
734 if (!ctx.sig) /* link sig to context chain */
735 ctx.sig = sig;
736 else
737 {
738 pdkim_signature * n = ctx.sig;
739 while (n->next) n = n->next;
740 n->next = sig;
741 }
80a47a2c 742 }
9e70917d 743 }
0b8f4f1a
JH
744if (!ctx.sig)
745 {
746 DEBUG(D_transport) debug_printf("DKIM: no viable signatures to use\n");
747 sigbuf = string_get(1); /* return a zero-len string */
748 goto CLEANUP;
749 }
80a47a2c 750
d011368a 751if (prefix && (pdkim_rc = pdkim_feed(&ctx, prefix, Ustrlen(prefix))) != PDKIM_OK)
0b8f4f1a 752 goto pk_bad;
80a47a2c 753
9e70917d
JH
754if (lseek(fd, off, SEEK_SET) < 0)
755 sread = -1;
756else
757 while ((sread = read(fd, &buf, sizeof(buf))) > 0)
758 if ((pdkim_rc = pdkim_feed(&ctx, buf, sread)) != PDKIM_OK)
759 goto pk_bad;
80a47a2c 760
9e70917d
JH
761/* Handle failed read above. */
762if (sread == -1)
763 {
764 debug_printf("DKIM: Error reading -K file.\n");
765 save_errno = errno;
766 goto bad;
80a47a2c 767 }
a8e1eeba 768
9e70917d
JH
769/* Build string of headers, one per signature */
770
771if ((pdkim_rc = pdkim_feed_finish(&ctx, &sig, errstr)) != PDKIM_OK)
772 goto pk_bad;
773
acec9514
JH
774for (sigbuf = NULL; sig; sig = sig->next)
775 sigbuf = string_append(sigbuf, 2, US sig->signature_header, US"\r\n");
9e70917d 776
1cfe5c1c 777CLEANUP:
0b8f4f1a 778 (void) string_from_gstring(sigbuf);
f7302073
JH
779 store_pool = old_pool;
780 errno = save_errno;
9e70917d 781 return sigbuf;
f7302073
JH
782
783pk_bad:
784 log_write(0, LOG_MAIN|LOG_PANIC,
785 "DKIM: signing failed: %.100s", pdkim_errstr(pdkim_rc));
786bad:
9e70917d 787 sigbuf = NULL;
f7302073 788 goto CLEANUP;
7c6ec81b
JH
789
790expand_bad:
791 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand %s: %s",
792 errwhen, expand_string_message);
793 goto bad;
93f2d376 794}
80a47a2c 795
dfbcb5ac
JH
796
797
798
799gstring *
800authres_dkim(gstring * g)
801{
802pdkim_signature * sig;
803
804for (sig = dkim_signatures; sig; sig = sig->next)
805 {
806 g = string_catn(g, US";\\n\\tdkim=", 10);
807
808 if (sig->verify_status & PDKIM_VERIFY_POLICY)
809 g = string_append(g, 5,
810 US"policy (", dkim_verify_status, US" - ", dkim_verify_reason, US")");
811 else switch(sig->verify_status)
812 {
813 case PDKIM_VERIFY_NONE: g = string_cat(g, US"none"); break;
814 case PDKIM_VERIFY_INVALID:
815 switch (sig->verify_ext_status)
816 {
817 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
818 g = string_cat(g, US"tmperror (pubkey unavailable)"); break;
819 case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
820 g = string_cat(g, US"permerror (overlong public key record)"); break;
821 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:
822 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT:
823 g = string_cat(g, US"neutral (syntax error in public key record)");
824 break;
825 case PDKIM_VERIFY_INVALID_SIGNATURE_ERROR:
826 g = string_cat(g, US"neutral (signature tag missing or invalid)");
827 break;
828 case PDKIM_VERIFY_INVALID_DKIM_VERSION:
829 g = string_cat(g, US"neutral (unsupported DKIM version)");
830 break;
831 default:
832 g = string_cat(g, US"permerror (unspecified problem)"); break;
833 }
834 break;
835 case PDKIM_VERIFY_FAIL:
836 switch (sig->verify_ext_status)
837 {
838 case PDKIM_VERIFY_FAIL_BODY:
839 g = string_cat(g,
840 US"fail (body hash mismatch; body probably modified in transit)");
841 break;
842 case PDKIM_VERIFY_FAIL_MESSAGE:
843 g = string_cat(g,
844 US"fail (signature did not verify; headers probably modified in transit)");
845 break;
846 default:
847 g = string_cat(g, US"fail (unspecified reason)");
848 break;
849 }
850 break;
851 case PDKIM_VERIFY_PASS: g = string_cat(g, US"pass"); break;
852 default: g = string_cat(g, US"permerror"); break;
853 }
854 if (sig->domain) g = string_append(g, 2, US" header.d=", sig->domain);
855 if (sig->identity) g = string_append(g, 2, US" header.i=", sig->identity);
856 if (sig->selector) g = string_append(g, 2, US" header.s=", sig->selector);
857 g = string_append(g, 2, US" header.a=", dkim_sig_to_a_tag(sig));
858 }
859return g;
860}
861
862
970424a5
JH
863# endif /*!MACRO_PREDEF*/
864#endif /*!DISABLE_DKIM*/