Fix non-ARC 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;
80a47a2c 33pdkim_signature *dkim_signatures = NULL;
1cfe5c1c 34pdkim_signature *dkim_cur_sig = NULL;
b9df1829 35static const uschar * dkim_collect_error = NULL;
80a47a2c 36
d73e45df
JH
37
38
39/*XXX the caller only uses the first record if we return multiple.
d73e45df
JH
40*/
41
617d3932
JH
42uschar *
43dkim_exim_query_dns_txt(uschar * name)
1cfe5c1c
JH
44{
45dns_answer dnsa;
46dns_scan dnss;
47dns_record *rr;
1eedc10f 48gstring * g = NULL;
80a47a2c 49
1cfe5c1c 50lookup_dnssec_authenticated = NULL;
617d3932 51if (dns_lookup(&dnsa, name, T_TXT, NULL) != DNS_SUCCEED)
1eedc10f 52 return NULL; /*XXX better error detail? logging? */
80a47a2c 53
1cfe5c1c 54/* Search for TXT record */
80a47a2c 55
1cfe5c1c
JH
56for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
57 rr;
58 rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
59 if (rr->type == T_TXT)
60 {
80a47a2c 61 int rr_offset = 0;
1cfe5c1c
JH
62
63 /* Copy record content to the answer buffer */
64
65 while (rr_offset < rr->size)
66 {
67 uschar len = rr->data[rr_offset++];
1eedc10f
JH
68
69 g = string_catn(g, US(rr->data + rr_offset), len);
70 if (g->ptr >= PDKIM_DNS_TXT_MAX_RECLEN)
71 goto bad;
72
1cfe5c1c 73 rr_offset += len;
4263f395 74 }
dd33c4e6
HSHR
75
76 /* check if this looks like a DKIM record */
d011368a 77 if (Ustrncmp(g->s, "v=", 2) != 0 || strncasecmp(CS g->s, "v=dkim", 6) == 0)
1eedc10f 78 {
617d3932 79 gstring_reset_unused(g);
1eedc10f
JH
80 return string_from_gstring(g);
81 }
82
83 if (g) g->ptr = 0; /* overwrite previous record */
80a47a2c 84 }
80a47a2c 85
1eedc10f
JH
86bad:
87if (g) store_reset(g);
88return NULL; /*XXX better error detail? logging? */
80a47a2c
TK
89}
90
91
2592e6c0
JH
92void
93dkim_exim_init(void)
94{
95pdkim_init();
96}
97
98
ca9cb170 99
1cfe5c1c 100void
e983e85a 101dkim_exim_verify_init(BOOL dot_stuffing)
1cfe5c1c 102{
ca9cb170
JH
103/* There is a store-reset between header & body reception
104so cannot use the main pool. Any allocs done by Exim
105memory-handling must use the perm pool. */
106
107dkim_verify_oldpool = store_pool;
108store_pool = POOL_PERM;
109
1cfe5c1c 110/* Free previous context if there is one */
80a47a2c 111
1cfe5c1c
JH
112if (dkim_verify_ctx)
113 pdkim_free_ctx(dkim_verify_ctx);
80a47a2c 114
1cfe5c1c 115/* Create new context */
80a47a2c 116
e983e85a 117dkim_verify_ctx = pdkim_init_verify(&dkim_exim_query_dns_txt, dot_stuffing);
3b957582 118dkim_collect_input = !!dkim_verify_ctx;
b9df1829 119dkim_collect_error = NULL;
ca9cb170 120
584e96c6
JH
121/* Start feed up with any cached data */
122receive_get_cache();
123
ca9cb170 124store_pool = dkim_verify_oldpool;
80a47a2c
TK
125}
126
127
1cfe5c1c
JH
128void
129dkim_exim_verify_feed(uschar * data, int len)
130{
f7302073
JH
131int rc;
132
ca9cb170 133store_pool = POOL_PERM;
1cfe5c1c 134if ( dkim_collect_input
ef698bf6 135 && (rc = pdkim_feed(dkim_verify_ctx, data, len)) != PDKIM_OK)
f7302073 136 {
b9df1829 137 dkim_collect_error = pdkim_errstr(rc);
f7302073 138 log_write(0, LOG_MAIN,
b9df1829 139 "DKIM: validation error: %.100s", dkim_collect_error);
1cfe5c1c 140 dkim_collect_input = FALSE;
f7302073 141 }
ca9cb170 142store_pool = dkim_verify_oldpool;
80a47a2c
TK
143}
144
145
a79d8834
JH
146/* Log the result for the given signature */
147static void
148dkim_exim_verify_log_sig(pdkim_signature * sig)
1cfe5c1c 149{
cc55f420 150gstring * logmsg;
a79d8834
JH
151uschar * s;
152
cc55f420
JH
153if (!sig) return;
154
6678c382 155/* Remember the domain for the first pass result */
dfbcb5ac 156
2c47372f
JH
157if ( !dkim_verify_overall
158 && dkim_verify_status
159 ? Ustrcmp(dkim_verify_status, US"pass") == 0
160 : sig->verify_status == PDKIM_VERIFY_PASS
161 )
162 dkim_verify_overall = string_copy(sig->domain);
163
6678c382
JH
164/* Rewrite the sig result if the ACL overrode it. This is only
165needed because the DMARC code (sigh) peeks at the dkim sigs.
166Mark the sig for this having been done. */
167
168if ( dkim_verify_status
169 && ( dkim_verify_status != dkim_exim_expand_query(DKIM_VERIFY_STATUS)
170 || dkim_verify_reason != dkim_exim_expand_query(DKIM_VERIFY_REASON)
171 ) )
172 { /* overridden by ACL */
173 sig->verify_ext_status = -1;
174 if (Ustrcmp(dkim_verify_status, US"fail") == 0)
175 sig->verify_status = PDKIM_VERIFY_POLICY | PDKIM_VERIFY_FAIL;
176 else if (Ustrcmp(dkim_verify_status, US"invalid") == 0)
177 sig->verify_status = PDKIM_VERIFY_POLICY | PDKIM_VERIFY_INVALID;
178 else if (Ustrcmp(dkim_verify_status, US"none") == 0)
179 sig->verify_status = PDKIM_VERIFY_POLICY | PDKIM_VERIFY_NONE;
180 else if (Ustrcmp(dkim_verify_status, US"pass") == 0)
181 sig->verify_status = PDKIM_VERIFY_POLICY | PDKIM_VERIFY_PASS;
182 else
183 sig->verify_status = -1;
184 }
185
2c47372f
JH
186if (!LOGGING(dkim_verbose)) return;
187
6678c382 188
aaaa94ea 189logmsg = string_catn(NULL, US"DKIM: ", 6);
a79d8834
JH
190if (!(s = sig->domain)) s = US"<UNSET>";
191logmsg = string_append(logmsg, 2, "d=", s);
192if (!(s = sig->selector)) s = US"<UNSET>";
193logmsg = string_append(logmsg, 2, " s=", s);
dd33c4e6 194logmsg = string_append(logmsg, 7,
dfbcb5ac
JH
195 " c=", sig->canon_headers == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
196 "/", sig->canon_body == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
197 " a=", dkim_sig_to_a_tag(sig),
a79d8834
JH
198string_sprintf(" b=" SIZE_T_FMT,
199 (int)sig->sighash.len > -1 ? sig->sighash.len * 8 : 0));
200if ((s= sig->identity)) logmsg = string_append(logmsg, 2, " i=", s);
201if (sig->created > 0) logmsg = string_cat(logmsg,
202 string_sprintf(" t=%lu", sig->created));
203if (sig->expires > 0) logmsg = string_cat(logmsg,
204 string_sprintf(" x=%lu", sig->expires));
205if (sig->bodylength > -1) logmsg = string_cat(logmsg,
206 string_sprintf(" l=%lu", sig->bodylength));
207
dfbcb5ac
JH
208if (sig->verify_status & PDKIM_VERIFY_POLICY)
209 logmsg = string_append(logmsg, 5,
210 US" [", dkim_verify_status, US" - ", dkim_verify_reason, US"]");
211else
1cfe5c1c
JH
212 switch (sig->verify_status)
213 {
214 case PDKIM_VERIFY_NONE:
aaaa94ea 215 logmsg = string_cat(logmsg, US" [not verified]");
80a47a2c 216 break;
1cfe5c1c
JH
217
218 case PDKIM_VERIFY_INVALID:
aaaa94ea 219 logmsg = string_cat(logmsg, US" [invalid - ");
1cfe5c1c
JH
220 switch (sig->verify_ext_status)
221 {
222 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
acec9514 223 logmsg = string_cat(logmsg,
aaaa94ea 224 US"public key record (currently?) unavailable]");
1cfe5c1c
JH
225 break;
226
227 case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
aaaa94ea 228 logmsg = string_cat(logmsg, US"overlong public key record]");
1cfe5c1c
JH
229 break;
230
df3def24
JH
231 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:
232 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT:
aaaa94ea 233 logmsg = string_cat(logmsg, US"syntax error in public key record]");
1cfe5c1c 234 break;
07eeb4df 235
a79d8834 236 case PDKIM_VERIFY_INVALID_SIGNATURE_ERROR:
aaaa94ea 237 logmsg = string_cat(logmsg, US"signature tag missing or invalid]");
a79d8834 238 break;
07eeb4df 239
a79d8834 240 case PDKIM_VERIFY_INVALID_DKIM_VERSION:
aaaa94ea 241 logmsg = string_cat(logmsg, US"unsupported DKIM version]");
a79d8834 242 break;
1cfe5c1c
JH
243
244 default:
aaaa94ea 245 logmsg = string_cat(logmsg, US"unspecified problem]");
1cfe5c1c 246 }
80a47a2c 247 break;
1cfe5c1c
JH
248
249 case PDKIM_VERIFY_FAIL:
aaaa94ea 250 logmsg = string_cat(logmsg, US" [verification failed - ");
1cfe5c1c
JH
251 switch (sig->verify_ext_status)
252 {
253 case PDKIM_VERIFY_FAIL_BODY:
acec9514 254 logmsg = string_cat(logmsg,
aaaa94ea 255 US"body hash mismatch (body probably modified in transit)]");
1cfe5c1c
JH
256 break;
257
258 case PDKIM_VERIFY_FAIL_MESSAGE:
acec9514 259 logmsg = string_cat(logmsg,
aaaa94ea
JH
260 US"signature did not verify "
261 "(headers probably modified in transit)]");
dfbcb5ac 262 break;
1cfe5c1c
JH
263
264 default:
aaaa94ea 265 logmsg = string_cat(logmsg, US"unspecified reason]");
1cfe5c1c 266 }
80a47a2c 267 break;
1cfe5c1c
JH
268
269 case PDKIM_VERIFY_PASS:
aaaa94ea 270 logmsg = string_cat(logmsg, US" [verification succeeded]");
80a47a2c
TK
271 break;
272 }
a79d8834 273
aaaa94ea 274log_write(0, LOG_MAIN, "%s", string_from_gstring(logmsg));
a79d8834
JH
275return;
276}
277
80a47a2c 278
a79d8834
JH
279/* Log a line for each signature */
280void
281dkim_exim_verify_log_all(void)
282{
283pdkim_signature * sig;
284for (sig = dkim_signatures; sig; sig = sig->next) dkim_exim_verify_log_sig(sig);
285}
1cfe5c1c 286
80a47a2c 287
a79d8834
JH
288void
289dkim_exim_verify_finish(void)
290{
291pdkim_signature * sig;
292int rc;
293gstring * g = NULL;
286b9d5f 294const uschar * errstr = NULL;
a79d8834
JH
295
296store_pool = POOL_PERM;
297
298/* Delete eventual previous signature chain */
299
300dkim_signers = NULL;
301dkim_signatures = NULL;
302
303if (dkim_collect_error)
304 {
305 log_write(0, LOG_MAIN,
306 "DKIM: Error during validation, disabling signature verification: %.100s",
307 dkim_collect_error);
308 dkim_disable_verify = TRUE;
309 goto out;
310 }
311
312dkim_collect_input = FALSE;
313
314/* Finish DKIM operation and fetch link to signatures chain */
315
316rc = pdkim_feed_finish(dkim_verify_ctx, &dkim_signatures, &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
398 function 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*/