1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
4 /* Experimental ARC support for Exim
5 Copyright (c) The Exim Maintainers 2021 - 2024
6 Copyright (c) Jeremy Harris 2018 - 2020
8 SPDX-License-Identifier: GPL-2.0-or-later
12 #if defined EXPERIMENTAL_ARC
13 # if defined DISABLE_DKIM
14 # error DKIM must also be enabled for ARC
17 # include "functions.h"
18 # include "miscmods/pdkim.h"
19 # include "miscmods/signing.h"
22 # include "miscmods/dmarc.h"
25 #define ARC_SIGN_OPT_TSTAMP BIT(0)
26 #define ARC_SIGN_OPT_EXPIRE BIT(1)
28 #define ARC_SIGN_DEFAULT_EXPIRE_DELTA (60 * 60 * 24 * 30) /* one month */
30 /******************************************************************************/
32 typedef struct hdr_rlist {
33 struct hdr_rlist * prev;
38 typedef struct arc_line {
39 header_line * complete; /* including the header name; nul-term */
42 /* identified tag contents */
56 /* tag content sub-portions */
63 /* modified copy of b= field in line */
67 typedef struct arc_set {
68 struct arc_set * next;
69 struct arc_set * prev;
76 const uschar * ams_verify_done;
77 BOOL ams_verify_passed;
80 typedef struct arc_ctx {
81 arc_set * arcset_chain;
82 arc_set * arcset_chain_last;
85 #define ARC_HDR_AAR US"ARC-Authentication-Results:"
86 #define ARC_HDRLEN_AAR 27
87 #define ARC_HDR_AMS US"ARC-Message-Signature:"
88 #define ARC_HDRLEN_AMS 22
89 #define ARC_HDR_AS US"ARC-Seal:"
90 #define ARC_HDRLEN_AS 9
91 #define HDR_AR US"Authentication-Results:"
94 typedef enum line_extract {
100 static misc_module_info * arc_dkim_mod_info;
103 static time_t expire;
104 static hdr_rlist * headers_rlist;
105 static arc_ctx arc_sign_ctx = { NULL };
106 static arc_ctx arc_verify_ctx = { NULL };
108 /* We build a context for either Sign or Verify.
110 For Verify, it's a fresh new one for ACL verify=arc - there is no connection
111 with the single line handling done during reception via the DKIM feed.
113 For Verify we do it twice; initially during reception (via the DKIM feed)
114 and then later for the full verification.
116 The former only looks at AMS headers, to discover what hash(es) we need done for
117 ARC on the message body; we call back to the DKIM code to set up so that it does
118 them for us during reception. That call needs info from many of the AMS tags;
119 arc_parse_line() for only the AMS is called asking for all the tag types.
120 That context is then discarded.
122 Later, for Verify, we look at ARC headers again and then grab the hash result
123 from the DKIM layer. arc_parse_line() is called for all 3 line types,
124 gathering info for only 'i' and 'ip' tags from AAR headers,
125 for all tag types from AMS and AS headers.
128 For Sign, while running through the existing headers (before adding any for
129 this signing operation, we "take copies" of the headers, we call
130 arc_parse_line() gathering only the 'i' tag (instance) information.
134 /******************************************************************************/
136 /* We need a module init function, to check on the dkim module being present
137 (and we may as well stack it's modinfo ptr)
139 For now (until we do an arc module), called from exim.c main().
144 uschar * errstr = NULL;
145 if ((arc_dkim_mod_info = misc_mod_find(US"dkim", &errstr)))
147 log_write(0, LOG_MAIN|LOG_PANIC, "arc: %s", errstr);
151 /******************************************************************************/
154 /* Get the instance number from the header.
157 arc_instance_from_hdr(const arc_line * al)
159 const uschar * s = al->i.data;
160 if (!s || !al->i.len) return 0;
161 return (unsigned) atoi(CCS s);
169 while (c && (c == ' ' || c == '\t' || c == '\n' || c == '\r')) c = *++s;
174 /* Locate instance struct on chain, inserting a new one if
175 needed. The chain is in increasing-instance-number order
176 by the "next" link, and we have a "prev" link also.
180 arc_find_set(arc_ctx * ctx, unsigned i)
182 arc_set ** pas, * as, * next, * prev;
184 for (pas = &ctx->arcset_chain, prev = NULL, next = ctx->arcset_chain;
185 as = *pas; pas = &as->next)
187 if (as->instance > i) break;
188 if (as->instance == i)
190 DEBUG(D_acl) debug_printf("ARC: existing instance %u\n", i);
197 DEBUG(D_acl) debug_printf("ARC: new instance %u\n", i);
198 *pas = as = store_get(sizeof(arc_set), GET_UNTAINTED);
199 memset(as, 0, sizeof(arc_set));
206 ctx->arcset_chain_last = as;
212 /* Insert a tag content into the line structure.
213 Note this is a reference to existing data, not a copy.
214 Check for already-seen tag.
215 The string-pointer is on the '=' for entry. Update it past the
216 content (to the ;) on return;
220 arc_insert_tagvalue(arc_line * al, unsigned loff, uschar ** ss)
224 blob * b = (blob *)(US al + loff);
227 /* [FWS] tag-value [FWS] */
229 if (b->data) return US"fail";
230 s = skip_fws(s); /* FWS */
233 while ((c = *s) && c != ';') { len++; s++; }
235 while (len && ((c = s[-1]) == ' ' || c == '\t' || c == '\n' || c == '\r'))
236 { s--; len--; } /* FWS */
242 /* Inspect a header line, noting known tag fields.
243 Check for duplicate named tags.
245 See the file block comment for how this is used.
247 Return: NULL for good, or an error string
251 arc_parse_line(arc_line * al, header_line * h, unsigned off, line_extract_t l_ext)
253 uschar * s = h->text + off;
259 if (l_ext == le_all) /* need to grab rawsig_no_b */
261 al->rawsig_no_b_val.data = store_get(h->slen + 1, GET_TAINTED);
262 memcpy(al->rawsig_no_b_val.data, h->text, off); /* copy the header name blind */
263 r = al->rawsig_no_b_val.data + off;
264 al->rawsig_no_b_val.len = off;
267 /* tag-list = tag-spec *( ";" tag-spec ) [ ";" ] */
274 uschar * fieldstart = s;
275 uschar * bstart = NULL, * bend;
277 /* tag-spec = [FWS] tag-name [FWS] "=" [FWS] tag-value [FWS] */
278 /*X or just a naked FQDN, in a AAR ! */
280 s = skip_fws(s); /* leading FWS */
283 if (!*(s = skip_fws(s))) break; /* FWS */
287 case 'a': /* a= AMS algorithm */
288 if (l_ext == le_all && *s == '=')
290 if (arc_insert_tagvalue(al, offsetof(arc_line, a), &s)) return US"a tag dup";
292 /* substructure: algo-hash (eg. rsa-sha256) */
294 t = al->a_algo.data = al->a.data;
296 if (!*t++ || ++i > al->a.len) return US"no '-' in 'a' value";
298 if (*t++ != '-') return US"no '-' in 'a' value";
300 al->a_hash.len = al->a.len - i - 1;
310 case '=': /* b= AMS signature */
311 if (al->b.data) return US"already b data";
314 /* The signature can have FWS inserted in the content;
315 make a stripped copy */
317 while ((c = *++s) && c != ';')
318 if (c != ' ' && c != '\t' && c != '\n' && c != '\r')
319 g = string_catn(g, s, 1);
320 if (!g) return US"no b= value";
321 al->b.len = len_string_from_gstring(g, &al->b.data);
322 gstring_release_unused(g);
325 case 'h': /* bh= AMS body hash */
326 s = skip_fws(++s); /* FWS */
329 if (al->bh.data) return US"already bh data";
331 /* The bodyhash can have FWS inserted in the content;
332 make a stripped copy */
334 while ((c = *++s) && c != ';')
335 if (c != ' ' && c != '\t' && c != '\n' && c != '\r')
336 g = string_catn(g, s, 1);
337 if (!g) return US"no bh= value";
338 al->bh.len = len_string_from_gstring(g, &al->bh.data);
339 gstring_release_unused(g);
348 if (l_ext == le_all) switch (*s)
350 case '=': /* c= AMS canonicalisation */
351 if (arc_insert_tagvalue(al, offsetof(arc_line, c), &s)) return US"c tag dup";
353 /* substructure: head/body (eg. relaxed/simple)) */
355 t = al->c_head.data = al->c.data;
357 if (!*t++ || ++i > al->a.len) break;
359 if (*t++ == '/') /* /body is optional */
362 al->c_body.len = al->c.len - i - 1;
366 al->c_body.data = US"simple";
370 case 'v': /* cv= AS validity */
373 if (arc_insert_tagvalue(al, offsetof(arc_line, cv), &s))
374 return US"cv tag dup";
378 case 'd': /* d= AMS domain */
379 if (l_ext == le_all && *s == '=')
380 if (arc_insert_tagvalue(al, offsetof(arc_line, d), &s))
381 return US"d tag dup";
383 case 'h': /* h= AMS headers */
385 if (arc_insert_tagvalue(al, offsetof(arc_line, h), &s))
386 return US"h tag dup";
388 case 'i': /* i= ARC set instance */
391 if (arc_insert_tagvalue(al, offsetof(arc_line, i), &s))
392 return US"i tag dup";
393 if (l_ext == le_instance_only)
394 goto done; /* early-out */
397 case 'l': /* l= bodylength */
398 if (l_ext == le_all && *s == '=')
399 if (arc_insert_tagvalue(al, offsetof(arc_line, l), &s))
400 return US"l tag dup";
403 if (*s == '=' && l_ext == le_all)
405 if (arc_insert_tagvalue(al, offsetof(arc_line, s), &s))
406 return US"s tag dup";
408 else if ( l_ext == le_instance_plus_ip
409 && Ustrncmp(s, "mtp.remote-ip", 13) == 0)
410 { /* smtp.remote-ip= AAR reception data */
413 if (*s != '=') return US"smtp.remote_ip tag val";
414 if (arc_insert_tagvalue(al, offsetof(arc_line, ip), &s))
415 return US"ip tag dup";
420 while ((c = *s) && c != ';') s++; /* end of this tag=value */
421 if (c) s++; /* ; after tag-spec */
423 /* for all but the b= tag, copy the field including FWS. For the b=,
424 drop the tag content. */
429 size_t n = bstart - fieldstart;
430 memcpy(r, fieldstart, n); /* FWS "b=" */
432 al->rawsig_no_b_val.len += n;
434 memcpy(r, bend, n); /* FWS ";" */
436 al->rawsig_no_b_val.len += n;
440 size_t n = s - fieldstart;
441 memcpy(r, fieldstart, n);
443 al->rawsig_no_b_val.len += n;
451 /* debug_printf("%s: finshed\n", __FUNCTION__); */
456 /* Insert one header line in the correct set of the chain,
457 adding instances as needed and checking for duplicate lines.
461 arc_insert_hdr(arc_ctx * ctx, header_line * h, unsigned off, unsigned hoff,
462 line_extract_t l_ext, arc_line ** alp_ret)
466 arc_line * al = store_get(sizeof(arc_line), GET_UNTAINTED), ** alp;
469 memset(al, 0, sizeof(arc_line));
471 if ((e = arc_parse_line(al, h, off, l_ext)))
473 DEBUG(D_acl) if (e) debug_printf("ARC: %s\n", e);
474 return string_sprintf("line parse: %s", e);
476 if (!(i = arc_instance_from_hdr(al))) return US"instance find";
477 if (i > 50) return US"overlarge instance number";
478 if (!(as = arc_find_set(ctx, i))) return US"set find";
479 if (*(alp = (arc_line **)(US as + hoff))) return US"dup hdr";
482 if (alp_ret) *alp_ret = al;
488 /* Called for both Sign and Verify */
490 static const uschar *
491 arc_try_header(arc_ctx * ctx, header_line * h, BOOL is_signing)
495 /*debug_printf("consider hdr '%s'\n", h->text);*/
496 if (strncmpic(ARC_HDR_AAR, h->text, ARC_HDRLEN_AAR) == 0)
502 for (s = h->text + h->slen; s[-1] == '\r' || s[-1] == '\n'; )
504 debug_printf("ARC: found AAR: %.*s\n", len, h->text);
506 if ((e = arc_insert_hdr(ctx, h, ARC_HDRLEN_AAR, offsetof(arc_set, hdr_aar),
507 is_signing ? le_instance_only : le_instance_plus_ip, NULL)))
509 DEBUG(D_acl) debug_printf("inserting AAR: %s\n", e);
510 return string_sprintf("inserting AAR: %s", e);
513 else if (strncmpic(ARC_HDR_AMS, h->text, ARC_HDRLEN_AMS) == 0)
521 for (s = h->text + h->slen; s[-1] == '\r' || s[-1] == '\n'; )
523 debug_printf("ARC: found AMS: %.*s\n", len, h->text);
525 if ((e = arc_insert_hdr(ctx, h, ARC_HDRLEN_AMS, offsetof(arc_set, hdr_ams),
526 is_signing ? le_instance_only : le_all, &ams)))
528 DEBUG(D_acl) debug_printf("inserting AMS: %s\n", e);
529 return string_sprintf("inserting AMS: %s", e);
535 ams->c_head.data = US"simple"; ams->c_head.len = 6;
536 ams->c_body = ams->c_head;
539 else if (strncmpic(ARC_HDR_AS, h->text, ARC_HDRLEN_AS) == 0)
545 for (s = h->text + h->slen; s[-1] == '\r' || s[-1] == '\n'; )
547 debug_printf("ARC: found AS: %.*s\n", len, h->text);
549 if ((e = arc_insert_hdr(ctx, h, ARC_HDRLEN_AS, offsetof(arc_set, hdr_as),
550 is_signing ? le_instance_only : le_all, NULL)))
552 DEBUG(D_acl) debug_printf("inserting AS: %s\n", e);
553 return string_sprintf("inserting AS: %s", e);
561 /* Gather the chain of arc sets from the headers.
562 Check for duplicates while that is done. Also build the
563 reverse-order headers list.
564 Called on an ACL verify=arc condition.
566 Return: ARC state if determined, eg. by lack of any ARC chain.
569 static const uschar *
570 arc_vfy_collect_hdrs(arc_ctx * ctx)
573 hdr_rlist * r = NULL, * rprev = NULL;
576 DEBUG(D_acl) debug_printf("ARC: collecting arc sets\n");
577 for (h = header_list; h; h = h->next)
579 r = store_get(sizeof(hdr_rlist), GET_UNTAINTED);
585 if ((e = arc_try_header(ctx, h, FALSE)))
587 arc_state_reason = string_sprintf("collecting headers: %s", e);
593 if (!ctx->arcset_chain) return US"none";
599 arc_cv_match(arc_line * al, const uschar * s)
601 return Ustrncmp(s, al->cv.data, al->cv.len) == 0;
604 /******************************************************************************/
605 /* Service routines provided by the dkim module */
608 arc_dkim_hashname_blob_to_type(const blob * name)
610 typedef int (*fn_t)(const blob *);
611 return (((fn_t *) arc_dkim_mod_info->functions)[DKIM_HASHNAME_TO_TYPE]) (name);
614 arc_dkim_hashtype_to_method(int hashtype)
616 typedef hashmethod (*fn_t)(int);
617 return (((fn_t *) arc_dkim_mod_info->functions)[DKIM_HASHTYPE_TO_METHOD]) (hashtype);
620 arc_dkim_hashname_blob_to_method(const blob * name)
622 typedef hashmethod (*fn_t)(const blob *);
623 return (((fn_t *) arc_dkim_mod_info->functions)[DKIM_HASHNAME_TO_METHOD]) (name);
626 /******************************************************************************/
628 /* Do a "relaxed" canonicalization of a header */
630 arc_relax_header_n(const uschar * text, int len, BOOL append_crlf)
632 typedef uschar * (*fn_t)(const uschar *, int, BOOL);
633 return (((fn_t *) arc_dkim_mod_info->functions)[DKIM_HEADER_RELAX])
634 (text, len, append_crlf);
639 /* Return the hash of headers from the message that the AMS claims it
644 arc_get_verify_hhash(arc_ctx * ctx, arc_line * ams, blob * hhash)
646 const uschar * headernames = string_copyn(ams->h.data, ams->h.len);
650 BOOL relaxed = Ustrncmp(US"relaxed", ams->c_head.data, ams->c_head.len) == 0;
651 hashmethod hm = arc_dkim_hashname_blob_to_method(&ams->a_hash);
656 if (hm < 0 || !exim_sha_init(&hhash_ctx, hm))
659 debug_printf("ARC: hash setup error, possibly nonhandled hashtype\n");
663 /* For each headername in the list from the AMS (walking in order)
664 walk the message headers in reverse order, adding to the hash any
665 found for the first time. For that last point, maintain used-marks
666 on the list of message headers. */
668 DEBUG(D_acl) debug_printf("ARC: AMS header data for verification:\n");
670 for (r = headers_rlist; r; r = r->prev)
672 while ((hn = string_nextinlist(&headernames, &sep, NULL, 0)))
673 for (r = headers_rlist; r; r = r->prev)
675 && strncasecmp(CCS (s = r->h->text), CCS hn, Ustrlen(hn)) == 0
678 if (relaxed) s = arc_relax_header_n(s, r->h->slen, TRUE);
680 DEBUG(D_acl) debug_printf("%Z\n", s);
681 exim_sha_update_string(&hhash_ctx, s);
686 /* Finally add in the signature header (with the b= tag stripped); no CRLF */
688 s = ams->rawsig_no_b_val.data, len = ams->rawsig_no_b_val.len;
690 len = Ustrlen(s = arc_relax_header_n(s, len, FALSE));
691 DEBUG(D_acl) debug_printf("%.*Z\n", len, s);
692 exim_sha_update(&hhash_ctx, s, len);
694 exim_sha_finish(&hhash_ctx, hhash);
696 { debug_printf("ARC: header hash: %.*H\n", hhash->len, hhash->data); }
704 arc_line_to_pubkey(arc_line * al, const uschar ** errstr)
706 typedef const uschar * (*fn_t)(const uschar *, blob **, const uschar **);
708 const uschar * hashes;
709 const uschar * srvtype =
710 (((fn_t *) arc_dkim_mod_info->functions)[DKIM_DNS_PUBKEY])
711 (string_sprintf("%.*s._domainkey.%.*s",
712 (int)al->s.len, al->s.data, (int)al->d.len, al->d.data),
715 /*XXX do we need a blob-string printf %handler? Other types of blob? */
718 { *errstr = US"pubkey dns lookup fail"; return NULL; }
719 if ((Ustrcmp(srvtype, "*") != 0 && Ustrcmp(srvtype, "email") != 0))
721 *errstr = string_sprintf("pubkey format error: srvtype '%s'", srvtype);
725 /* If the pubkey limits use to specified hashes, reject unusable
726 signatures. XXX should we have looked for multiple dns records? */
730 const uschar * list = hashes, * ele;
733 while ((ele = string_nextinlist(&list, &sep, NULL, 0)))
734 if (Ustrncmp(ele, al->a_hash.data, al->a_hash.len) == 0) break;
737 DEBUG(D_acl) debug_printf("pubkey h=%s vs sig a=%.*s\n",
738 hashes, (int)al->a.len, al->a.data);
739 *errstr = US"no usable sig for this pubkey hash list";
749 /* Set up a body hashing method on the given signature-context
750 (creates a new one if needed, or uses an already-present one).
753 signing TRUE for signing, FALSE for verification
754 c canonicalization spec, text form
756 bodylen byte count for message body
758 Return: pointer to hashing method struct
761 static pdkim_bodyhash *
762 arc_set_bodyhash(BOOL signing,
763 const blob * c, const blob * ah, long bodylen)
765 typedef pdkim_bodyhash * (*fn_t)(BOOL,
766 const blob * canon, const blob * hash, long bodylen);
768 return (((fn_t *) arc_dkim_mod_info->functions)[DKIM_SET_BODYHASH])
769 (signing, c, ah, bodylen);
775 static pdkim_bodyhash *
776 arc_ams_setup_vfy_bodyhash(arc_line * ams)
779 long bodylen = ams->l.data
780 ? strtol(CS string_copyn(ams->l.data, ams->l.len), NULL, 10)
785 c->data = US"simple"; /* RFC 6376 (DKIM) default */
789 return arc_set_bodyhash(FALSE, c, &ams->a_hash, bodylen);
795 arc_decode_base64(const uschar * str, blob * b)
797 int dlen = b64decode(str, &b->data, str);
798 if (dlen < 0) b->data = NULL;
805 arc_sig_verify(arc_set * as, arc_line * al, hashmethod hm,
806 blob * hhash_computed, blob * sighash,
807 const uschar * why, const uschar ** errstr_p)
810 const uschar * errstr = NULL;
813 (const blob *, const blob *, hashmethod, const blob *, const uschar **);
815 /* Get the public key from DNS */
818 if (!(pubkey = arc_line_to_pubkey(al, &errstr)))
820 *errstr_p = string_sprintf("%s (%s)", errstr, why);
824 rc = (((fn_t *) arc_dkim_mod_info->functions)[DKIM_SIG_VERIFY])
825 (sighash, hhash_computed, hm, pubkey, &errstr);
832 debug_printf("ARC i=%d %s verify %s\n", as->instance, why, errstr);
835 DEBUG(D_acl) debug_printf("ARC verify %s init: %s\n", why, errstr);
844 /* Verify an AMS. This is a DKIM-sig header, but with an ARC i= tag
845 and without a DKIM v= tag.
848 static const uschar *
849 arc_ams_verify(arc_ctx * ctx, arc_set * as)
851 arc_line * ams = as->hdr_ams;
856 const uschar * errstr;
859 as->ams_verify_done = US"in-progress";
861 /* Check the AMS has all the required tags:
865 "d=" domain (for key lookup)
866 "h=" headers (included in signature)
867 "s=" key-selector (for key lookup)
869 if ( !ams->a.data || !ams->b.data || !ams->bh.data || !ams->d.data
870 || !ams->h.data || !ams->s.data)
872 as->ams_verify_done = arc_state_reason = US"required tag missing";
877 /* The bodyhash should have been created earlier, and the dkim code should
878 have managed calculating it during message input. Find the reference to it. */
880 if (!(b = arc_ams_setup_vfy_bodyhash(ams)))
882 as->ams_verify_done = arc_state_reason = US"internal hash setup error";
888 debug_printf("ARC i=%d AMS Body bytes hashed: %lu\n"
889 " Body %.*s computed: ",
890 as->instance, b->signed_body_bytes,
891 (int)ams->a_hash.len, ams->a_hash.data);
892 debug_printf("%.*H\n", b->bh.len, b->bh.data);
895 /* We know the bh-tag blob is of a nul-term string, so safe as a string */
898 || (arc_decode_base64(ams->bh.data, &sighash), sighash.len != b->bh.len)
899 || memcmp(sighash.data, b->bh.data, b->bh.len) != 0
904 debug_printf("ARC i=%d AMS Body hash from headers: ", as->instance);
905 debug_printf("%.*H\n", sighash.len, sighash.data);
906 debug_printf("ARC i=%d AMS Body hash did NOT match\n", as->instance);
908 return as->ams_verify_done = arc_state_reason = US"AMS body hash miscompare";
911 DEBUG(D_acl) debug_printf("ARC i=%d AMS Body hash compared OK\n", as->instance);
913 /* We know the b-tag blob is of a nul-term string, so safe as a string */
914 arc_decode_base64(ams->b.data, &sighash);
916 arc_get_verify_hhash(ctx, ams, &hhash_computed);
918 if ((hm = arc_dkim_hashname_blob_to_method(&ams->a_hash)) < 0)
920 DEBUG(D_acl) debug_printf("ARC i=%d AMS verify bad a_hash\n", as->instance);
921 return as->ams_verify_done = arc_state_reason = US"AMS sig nonverify";
924 rc = arc_sig_verify(as, ams, hm, &hhash_computed, &sighash, US"AMS", &errstr);
926 return as->ams_verify_done = arc_state_reason =
927 rc == FAIL ? US"AMS sig nonverify" : errstr;
929 DEBUG(D_acl) debug_printf("ARC i=%d AMS verify pass\n", as->instance);
930 as->ams_verify_passed = TRUE;
936 /* Check the sets are instance-continuous and that all
937 members are present. Check that no arc_seals are "fail".
938 Set the highest instance number global.
939 Verify the latest AMS.
942 arc_headers_check(arc_ctx * ctx)
946 BOOL ams_fail_found = FALSE;
948 if (!(as = ctx->arcset_chain_last))
951 for(inst = as->instance; as; as = as->prev, inst--)
953 if (as->instance != inst)
954 arc_state_reason = string_sprintf("i=%d (sequence; expected %d)",
956 else if (!as->hdr_aar || !as->hdr_ams || !as->hdr_as)
957 arc_state_reason = string_sprintf("i=%d (missing header)", as->instance);
958 else if (arc_cv_match(as->hdr_as, US"fail"))
959 arc_state_reason = string_sprintf("i=%d (cv)", as->instance);
963 DEBUG(D_acl) debug_printf("ARC chain fail at %s\n", arc_state_reason);
967 /* Evaluate the oldest-pass AMS validation while we're here.
968 It does not affect the AS chain validation but is reported as
972 if (arc_ams_verify(ctx, as))
973 ams_fail_found = TRUE;
975 arc_oldest_pass = inst;
976 arc_state_reason = NULL;
980 arc_state_reason = string_sprintf("(sequence; expected i=%d)", inst);
981 DEBUG(D_acl) debug_printf("ARC chain fail %s\n", arc_state_reason);
985 arc_received = ctx->arcset_chain_last;
986 arc_received_instance = arc_received->instance;
988 /* We can skip the latest-AMS validation, if we already did it. */
990 as = ctx->arcset_chain_last;
991 if (!as->ams_verify_passed)
993 if (as->ams_verify_done)
995 arc_state_reason = as->ams_verify_done;
998 if (!!arc_ams_verify(ctx, as))
1005 /******************************************************************************/
1006 static const uschar *
1007 arc_seal_verify(arc_ctx * ctx, arc_set * as)
1009 arc_line * hdr_as = as->hdr_as;
1013 blob hhash_computed;
1015 const uschar * errstr;
1018 DEBUG(D_acl) debug_printf("ARC: AS vfy i=%d\n", as->instance);
1020 1. If the value of the "cv" tag on that seal is "fail", the
1021 chain state is "fail" and the algorithm stops here. (This
1022 step SHOULD be skipped if the earlier step (2.1) was
1025 2. In Boolean nomenclature: if ((i == 1 && cv != "none") or (cv
1026 == "none" && i != 1)) then the chain state is "fail" and the
1027 algorithm stops here (note that the ordering of the logic is
1028 structured for short-circuit evaluation).
1031 if ( as->instance == 1 && !arc_cv_match(hdr_as, US"none")
1032 || arc_cv_match(hdr_as, US"none") && as->instance != 1
1035 arc_state_reason = US"seal cv state";
1040 3. Initialize a hash function corresponding to the "a" tag of
1044 hm = arc_dkim_hashname_blob_to_method(&hdr_as->a_hash);
1046 if (hm < 0 || !exim_sha_init(&hhash_ctx, hm))
1049 debug_printf("ARC: hash setup error, possibly nonhandled hashtype\n");
1050 arc_state_reason = US"seal hash setup error";
1055 4. Compute the canonicalized form of the ARC header fields, in
1056 the order described in Section 5.4.2, using the "relaxed"
1057 header canonicalization defined in Section 3.4.2 of
1058 [RFC6376]. Pass the canonicalized result to the hash
1061 Headers are CRLF-separated, but the last one is not crlf-terminated.
1064 DEBUG(D_acl) debug_printf("ARC: AS header data for verification:\n");
1065 for (as2 = ctx->arcset_chain;
1066 as2 && as2->instance <= as->instance;
1074 if (!(s = al->relaxed))
1075 /*XXX dkim module */
1076 al->relaxed = s = arc_relax_header_n(al->complete->text,
1077 al->complete->slen, TRUE);
1079 DEBUG(D_acl) debug_printf("%Z\n", s);
1080 exim_sha_update(&hhash_ctx, s, len);
1083 if (!(s = al->relaxed))
1084 /*XXX dkim module */
1085 al->relaxed = s = arc_relax_header_n(al->complete->text,
1086 al->complete->slen, TRUE);
1088 DEBUG(D_acl) debug_printf("%Z\n", s);
1089 exim_sha_update(&hhash_ctx, s, len);
1092 if (as2->instance == as->instance)
1093 /*XXX dkim module */
1094 s = arc_relax_header_n(al->rawsig_no_b_val.data,
1095 al->rawsig_no_b_val.len, FALSE);
1096 else if (!(s = al->relaxed))
1097 /*XXX dkim module */
1098 al->relaxed = s = arc_relax_header_n(al->complete->text,
1099 al->complete->slen, TRUE);
1101 DEBUG(D_acl) debug_printf("%Z\n", s);
1102 exim_sha_update(&hhash_ctx, s, len);
1106 5. Retrieve the final digest from the hash function.
1109 exim_sha_finish(&hhash_ctx, &hhash_computed);
1112 debug_printf("ARC i=%d AS Header %.*s computed: ",
1113 as->instance, (int)hdr_as->a_hash.len, hdr_as->a_hash.data);
1114 debug_printf("%.*H\n", hhash_computed.len, hhash_computed.data);
1119 6. Retrieve the public key identified by the "s" and "d" tags in
1120 the ARC-Seal, as described in Section 4.1.6.
1122 Done below, in arc_sig_verify().
1124 7. Determine whether the signature portion ("b" tag) of the ARC-
1125 Seal and the digest computed above are valid according to the
1126 public key. (See also Section Section 8.4 for failure case
1129 8. If the signature is not valid, the chain state is "fail" and
1130 the algorithm stops here.
1133 /* We know the b-tag blob is of a nul-term string, so safe as a string */
1134 arc_decode_base64(hdr_as->b.data, &sighash);
1136 rc = arc_sig_verify(as, hdr_as, hm, &hhash_computed, &sighash, US"AS", &errstr);
1139 if (rc == FAIL) arc_state_reason = US"seal sigverify error";
1143 DEBUG(D_acl) debug_printf("ARC: AS vfy i=%d pass\n", as->instance);
1148 static const uschar *
1149 arc_verify_seals(arc_ctx * ctx)
1151 arc_set * as = ctx->arcset_chain_last;
1156 for ( ; as; as = as->prev) if (arc_seal_verify(ctx, as)) return US"fail";
1158 DEBUG(D_acl) debug_printf("ARC: AS vfy overall pass\n");
1161 /******************************************************************************/
1163 /* Do ARC verification. Called from DATA ACL, on a verify = arc
1164 condition. No arguments; we are checking globals.
1166 Return: The ARC state, or NULL on error.
1170 acl_verify_arc(void)
1174 memset(&arc_verify_ctx, 0, sizeof(arc_verify_ctx));
1176 /* AS evaluation, per
1177 https://tools.ietf.org/html/draft-ietf-dmarc-arc-protocol-10#section-6
1179 /* 1. Collect all ARC sets currently on the message. If there were
1180 none, the ARC state is "none" and the algorithm stops here.
1183 if ((res = arc_vfy_collect_hdrs(&arc_verify_ctx)))
1186 /* 2. If the form of any ARC set is invalid (e.g., does not contain
1187 exactly one of each of the three ARC-specific header fields),
1188 then the chain state is "fail" and the algorithm stops here.
1190 1. To avoid the overhead of unnecessary computation and delay
1191 from crypto and DNS operations, the cv value for all ARC-
1192 Seal(s) MAY be checked at this point. If any of the values
1193 are "fail", then the overall state of the chain is "fail" and
1194 the algorithm stops here.
1196 3. Conduct verification of the ARC-Message-Signature header field
1197 bearing the highest instance number. If this verification fails,
1198 then the chain state is "fail" and the algorithm stops here.
1201 if ((res = arc_headers_check(&arc_verify_ctx)))
1204 /* 4. For each ARC-Seal from the "N"th instance to the first, apply the
1207 1. If the value of the "cv" tag on that seal is "fail", the
1208 chain state is "fail" and the algorithm stops here. (This
1209 step SHOULD be skipped if the earlier step (2.1) was
1212 2. In Boolean nomenclature: if ((i == 1 && cv != "none") or (cv
1213 == "none" && i != 1)) then the chain state is "fail" and the
1214 algorithm stops here (note that the ordering of the logic is
1215 structured for short-circuit evaluation).
1217 3. Initialize a hash function corresponding to the "a" tag of
1220 4. Compute the canonicalized form of the ARC header fields, in
1221 the order described in Section 5.4.2, using the "relaxed"
1222 header canonicalization defined in Section 3.4.2 of
1223 [RFC6376]. Pass the canonicalized result to the hash
1226 5. Retrieve the final digest from the hash function.
1228 6. Retrieve the public key identified by the "s" and "d" tags in
1229 the ARC-Seal, as described in Section 4.1.6.
1231 7. Determine whether the signature portion ("b" tag) of the ARC-
1232 Seal and the digest computed above are valid according to the
1233 public key. (See also Section Section 8.4 for failure case
1236 8. If the signature is not valid, the chain state is "fail" and
1237 the algorithm stops here.
1239 5. If all seals pass validation, then the chain state is "pass", and
1240 the algorithm is complete.
1243 if ((res = arc_verify_seals(&arc_verify_ctx)))
1252 /******************************************************************************/
1254 /* Prepend the header to the rlist */
1257 arc_rlist_entry(hdr_rlist * list, const uschar * s, int len)
1259 hdr_rlist * r = store_get(sizeof(hdr_rlist) + sizeof(header_line), GET_UNTAINTED);
1260 header_line * h = r->h = (header_line *)(r+1);
1273 /* Walk the given headers strings identifying each header, and construct
1274 a reverse-order list.
1278 arc_sign_scan_headers(arc_ctx * ctx, gstring * sigheaders)
1281 hdr_rlist * rheaders = NULL;
1283 s = sigheaders ? sigheaders->s : NULL;
1286 const uschar * s2 = s;
1288 /* This works for either NL or CRLF lines; also nul-termination */
1290 if (*s2 == '\n' && s2[1] != '\t' && s2[1] != ' ') break;
1291 s2++; /* move past end of line */
1293 rheaders = arc_rlist_entry(rheaders, s, s2 - s);
1301 /* Return the A-R content, without identity, with line-ending and
1305 arc_sign_find_ar(header_line * headers, const uschar * identity, blob * ret)
1308 int ilen = Ustrlen(identity);
1311 for(h = headers; h; h = h->next)
1313 uschar * s = h->text, c;
1316 if (Ustrncmp(s, HDR_AR, HDRLEN_AR) != 0) continue;
1317 s += HDRLEN_AR, len -= HDRLEN_AR; /* header name */
1319 && (c = *s) && (c == ' ' || c == '\t' || c == '\r' || c == '\n'))
1320 s++, len--; /* FWS */
1321 if (Ustrncmp(s, identity, ilen) != 0) continue;
1322 s += ilen; len -= ilen; /* identity */
1323 if (len <= 0) continue;
1324 if ((c = *s) && c == ';') s++, len--; /* identity terminator */
1326 && (c = *s) && (c == ' ' || c == '\t' || c == '\r' || c == '\n'))
1327 s++, len--; /* FWS */
1328 if (len <= 0) continue;
1338 /* Append a constructed AAR including CRLF. Add it to the arc_ctx too. */
1341 arc_sign_append_aar(gstring * g, arc_ctx * ctx,
1342 const uschar * identity, int instance, blob * ar)
1344 int aar_off = gstring_length(g);
1346 store_get(sizeof(arc_set) + sizeof(arc_line) + sizeof(header_line), GET_UNTAINTED);
1347 arc_line * al = (arc_line *)(as+1);
1348 header_line * h = (header_line *)(al+1);
1350 g = string_catn(g, ARC_HDR_AAR, ARC_HDRLEN_AAR);
1351 g = string_fmt_append(g, " i=%d; %s; smtp.remote-ip=%s;\r\n\t",
1352 instance, identity, sender_host_address);
1353 g = string_catn(g, US ar->data, ar->len);
1355 h->slen = g->ptr - aar_off;
1356 h->text = g->s + aar_off;
1359 as->prev = ctx->arcset_chain_last;
1360 as->instance = instance;
1363 ctx->arcset_chain = as;
1365 ctx->arcset_chain_last->next = as;
1366 ctx->arcset_chain_last = as;
1368 DEBUG(D_transport) debug_printf("ARC: AAR '%.*s'\n", h->slen - 2, h->text);
1375 arc_sig_from_pseudoheader(gstring * hdata, int hashtype, const uschar * privkey,
1376 blob * sig, const uschar * why)
1378 hashmethod hm = /*sig->keytype == KEYTYPE_ED25519*/ FALSE
1380 : arc_dkim_hashtype_to_method(hashtype);
1383 const uschar * errstr;
1384 typedef const uschar * (*fn_t)
1385 (const blob *, hashmethod, const uschar *, blob *);
1390 debug_printf("ARC: %s header data for signing:\n", why);
1391 debug_printf("%.*Z\n", hdata->ptr, hdata->s);
1393 (void) exim_sha_init(&hhash_ctx, hm);
1394 exim_sha_update(&hhash_ctx, hdata->s, hdata->ptr);
1395 exim_sha_finish(&hhash_ctx, &hhash);
1396 debug_printf("ARC: header hash: %.*H\n", hhash.len, hhash.data);
1399 if (FALSE /*need hash for Ed25519 or GCrypt signing*/ )
1402 (void) exim_sha_init(&hhash_ctx, arc_dkim_hashtype_to_method(hashtype));
1403 exim_sha_update(&hhash_ctx, hdata->s, hdata->ptr);
1404 exim_sha_finish(&hhash_ctx, &hhash);
1408 hhash.data = hdata->s;
1409 hhash.len = hdata->ptr;
1412 errstr = (((fn_t *) arc_dkim_mod_info->functions)[DKIM_SIGN_DATA])
1413 (&hhash, hm, privkey, sig);
1416 log_write(0, LOG_MAIN, "ARC: %s signing: %s\n", why, errstr);
1418 debug_printf("private key, or private-key file content, was: '%s'\n",
1429 arc_sign_append_sig(gstring * g, blob * sig)
1431 /*debug_printf("%s: raw sig %.*H\n", __FUNCTION__, sig->len, sig->data);*/
1432 sig->data = b64encode(sig->data, sig->len);
1433 sig->len = Ustrlen(sig->data);
1436 int len = MIN(sig->len, 74);
1437 g = string_catn(g, sig->data, len);
1438 if ((sig->len -= len) == 0) break;
1440 g = string_catn(g, US"\r\n\t ", 5);
1442 g = string_catn(g, US";\r\n", 3);
1443 gstring_release_unused(g);
1444 string_from_gstring(g);
1449 /* Append a constructed AMS including CRLF. Add it to the arc_ctx too. */
1452 arc_sign_append_ams(gstring * g, arc_ctx * ctx, int instance,
1453 const uschar * identity, const uschar * selector, blob * bodyhash,
1454 hdr_rlist * rheaders, const uschar * privkey, unsigned options)
1457 gstring * hdata = NULL;
1459 const blob ams_h = {.data = US"sha256", .len = 6}; /*XXX hardwired */
1460 int hashtype = arc_dkim_hashname_blob_to_type(&ams_h);
1463 arc_line * al = store_get(sizeof(header_line) + sizeof(arc_line), GET_UNTAINTED);
1464 header_line * h = (header_line *)(al+1);
1466 /* debug_printf("%s\n", __FUNCTION__); */
1468 /* Construct the to-be-signed AMS pseudo-header: everything but the sig. */
1470 ams_off = gstring_length(g);
1471 g = string_fmt_append(g, "%s i=%d; a=rsa-sha256; c=relaxed; d=%s; s=%s",
1472 ARC_HDR_AMS, instance, identity, selector); /*XXX hardwired a= */
1473 if (options & ARC_SIGN_OPT_TSTAMP)
1474 g = string_fmt_append(g, "; t=%lu", (u_long)now);
1475 if (options & ARC_SIGN_OPT_EXPIRE)
1476 g = string_fmt_append(g, "; x=%lu", (u_long)expire);
1477 g = string_fmt_append(g, ";\r\n\tbh=%s;\r\n\th=",
1478 b64encode(bodyhash->data, bodyhash->len));
1480 for(col = 3; rheaders; rheaders = rheaders->prev)
1482 const uschar * hnames = US"DKIM-Signature:" PDKIM_DEFAULT_SIGN_HEADERS;
1483 uschar * name, * htext = rheaders->h->text;
1486 /* Spot headers of interest */
1488 while ((name = string_nextinlist(&hnames, &sep, NULL, 0)))
1490 int len = Ustrlen(name);
1491 if (strncasecmp(CCS htext, CCS name, len) == 0)
1493 /* If too long, fold line in h= field */
1495 if (col + len > 78) g = string_catn(g, US"\r\n\t ", 5), col = 3;
1497 /* Add name to h= list */
1499 g = string_catn(g, name, len);
1500 g = string_catn(g, US":", 1);
1503 /* Accumulate header for hashing/signing */
1505 hdata = string_cat(hdata,
1506 /*XXX dkim module */
1507 arc_relax_header_n(htext, rheaders->h->slen, TRUE)); /*XXX hardwired */
1513 /* Lose the last colon from the h= list */
1515 gstring_trim_trailing(g, ':');
1517 g = string_catn(g, US";\r\n\tb=;", 7);
1519 /* Include the pseudo-header in the accumulation */
1521 /*XXX dkim module */
1522 s = arc_relax_header_n(g->s + ams_off, g->ptr - ams_off, FALSE);
1523 hdata = string_cat(hdata, s);
1525 /* Calculate the signature from the accumulation */
1526 /*XXX does that need further relaxation? there are spaces embedded in the b= strings! */
1528 if (!arc_sig_from_pseudoheader(hdata, hashtype, privkey, &sig, US"AMS"))
1531 /* Lose the trailing semicolon from the psuedo-header, and append the signature
1532 (folded over lines) and termination to complete it. */
1535 g = arc_sign_append_sig(g, &sig);
1537 h->slen = g->ptr - ams_off;
1538 h->text = g->s + ams_off;
1540 ctx->arcset_chain_last->hdr_ams = al;
1542 DEBUG(D_transport) debug_printf("ARC: AMS '%.*s'\n", h->slen - 2, h->text);
1548 /* Look for an arc= result in an A-R header blob. We know that its data
1549 happens to be a NUL-term string. */
1552 arc_ar_cv_status(blob * ar)
1554 const uschar * resinfo = ar->data;
1556 uschar * methodspec, * s;
1558 while ((methodspec = string_nextinlist(&resinfo, &sep, NULL, 0)))
1559 if (Ustrncmp(methodspec, US"arc=", 4) == 0)
1562 for (s = methodspec += 4;
1563 (c = *s) && c != ';' && c != ' ' && c != '\r' && c != '\n'; ) s++;
1564 return string_copyn(methodspec, s - methodspec);
1571 /* Build the AS header and prepend it */
1574 arc_sign_prepend_as(gstring * arcset_interim, arc_ctx * ctx,
1575 int instance, const uschar * identity, const uschar * selector, blob * ar,
1576 const uschar * privkey, unsigned options)
1579 uschar * status = arc_ar_cv_status(ar);
1580 arc_line * al = store_get(sizeof(header_line) + sizeof(arc_line), GET_UNTAINTED);
1581 header_line * h = (header_line *)(al+1);
1582 uschar * badline_str;
1584 gstring * hdata = NULL;
1585 const blob as_h = {.data = US"sha256", .len = 6}; /*XXX hardwired */
1586 int hashtype = arc_dkim_hashname_blob_to_type(&as_h);
1592 - no h= tag; implicit coverage
1593 - arc status from A-R
1595 - coverage is just the new ARC set
1596 including self (but with an empty b= in self)
1598 - all ARC set headers, set-number order, aar then ams then as,
1599 including self (but with an empty b= in self)
1601 DEBUG(D_transport) debug_printf("ARC: building AS for status '%s'\n", status);
1603 /* Construct the AS except for the signature */
1605 arcset = string_append(NULL, 9,
1607 US" i=", string_sprintf("%d", instance),
1609 US"; a=rsa-sha256; d=", identity, /*XXX hardwired */
1610 US"; s=", selector); /*XXX same as AMS */
1611 if (options & ARC_SIGN_OPT_TSTAMP)
1612 arcset = string_append(arcset, 2,
1613 US"; t=", string_sprintf("%lu", (u_long)now));
1614 arcset = string_cat(arcset,
1617 h->slen = arcset->ptr;
1618 h->text = arcset->s;
1620 ctx->arcset_chain_last->hdr_as = al;
1622 /* For any but "fail" chain-verify status, walk the entire chain in order by
1623 instance. For fail, only the new arc-set. Accumulate the elements walked. */
1625 for (arc_set * as = Ustrcmp(status, US"fail") == 0
1626 ? ctx->arcset_chain_last : ctx->arcset_chain;
1630 /* Accumulate AAR then AMS then AS. Relaxed canonicalisation
1631 is required per standard. */
1633 badline_str = US"aar";
1634 if (!(l = as->hdr_aar)) goto badline;
1636 /*XXX dkim module */
1637 hdata = string_cat(hdata, arc_relax_header_n(h->text, h->slen, TRUE));
1638 badline_str = US"ams";
1639 if (!(l = as->hdr_ams)) goto badline;
1641 /*XXX dkim module */
1642 hdata = string_cat(hdata, arc_relax_header_n(h->text, h->slen, TRUE));
1643 badline_str = US"as";
1644 if (!(l = as->hdr_as)) goto badline;
1646 /*XXX dkim module */
1647 hdata = string_cat(hdata, arc_relax_header_n(h->text, h->slen, !!as->next));
1650 /* Calculate the signature from the accumulation */
1652 if (!arc_sig_from_pseudoheader(hdata, hashtype, privkey, &sig, US"AS"))
1655 /* Lose the trailing semicolon */
1657 arcset = arc_sign_append_sig(arcset, &sig);
1658 DEBUG(D_transport) debug_printf("ARC: AS '%.*s'\n", arcset->ptr - 2, arcset->s);
1660 /* Finally, append the AMS and AAR to the new AS */
1662 return string_catn(arcset, arcset_interim->s, arcset_interim->ptr);
1666 debug_printf("ARC: while building AS, missing %s in chain\n", badline_str);
1671 /**************************************/
1673 /*XXX not static currently as the smtp tpt calls us */
1674 /* Really returns pdkim_bodyhash* - but there's an ordering
1675 problem for functions.h so call it void* */
1678 arc_ams_setup_sign_bodyhash(void)
1680 blob canon = {.data = US"relaxed", .len = 7}; /*XXX hardwired */
1681 blob hash = {.data = US"sha256", .len = 6}; /*XXX hardwired */
1683 DEBUG(D_transport) debug_printf("ARC: requesting bodyhash\n");
1685 return arc_set_bodyhash(TRUE, &canon, &hash, -1);
1693 memset(&arc_sign_ctx, 0, sizeof(arc_sign_ctx));
1694 headers_rlist = NULL;
1699 /* A "normal" header line, identified by DKIM processing. These arrive before
1700 the call to arc_sign(), which carries any newly-created DKIM headers - and
1701 those go textually before the normal ones in the message.
1703 We have to take the feed from DKIM as, in the transport-filter case, the
1704 headers are not in memory at the time of the call to arc_sign().
1706 Take a copy of the header and construct a reverse-order list.
1707 Also parse ARC-chain headers and build the chain struct, retaining pointers
1711 static const uschar *
1712 arc_header_sign_feed(gstring * g)
1714 uschar * s = string_copy_from_gstring(g);
1715 headers_rlist = arc_rlist_entry(headers_rlist, s, g->ptr);
1716 return arc_try_header(&arc_sign_ctx, headers_rlist->h, TRUE);
1721 /* Per RFCs 6376, 7489 the only allowed chars in either an ADMD id
1722 or a selector are ALPHA/DIGGIT/'-'/'.'
1724 Check, to help catch misconfigurations such as a missing selector
1725 element in the arc_sign list.
1729 arc_valid_id(const uschar * s)
1731 for (uschar c; c = *s++; )
1732 if (!isalnum(c) && c != '-' && c != '.') return FALSE;
1738 /* ARC signing. Called from the smtp transport, if the arc_sign option is set.
1739 The dkim_exim_sign() function has already been called, so will have hashed the
1740 message body for us so long as we requested a hash previously.
1743 signspec Three-element colon-sep list: identity, selector, privkey.
1744 Optional fourth element: comma-sep list of options.
1746 sigheaders Any signature headers already generated, eg. by DKIM, or NULL
1750 Set of headers to prepend to the message, including the supplied sigheaders
1751 but not the plainheaders.
1755 arc_sign(const uschar * signspec, gstring * sigheaders, uschar ** errstr)
1757 const uschar * identity, * selector, * privkey, * opts, * s;
1758 unsigned options = 0;
1760 header_line * headers;
1761 hdr_rlist * rheaders;
1769 /* Parse the signing specification */
1771 if (!(identity = string_nextinlist(&signspec, &sep, NULL, 0)) || !*identity)
1772 { s = US"identity"; goto bad_arg_ret; }
1773 if (!(selector = string_nextinlist(&signspec, &sep, NULL, 0)) || !*selector)
1774 { s = US"selector"; goto bad_arg_ret; }
1775 if (!(privkey = string_nextinlist(&signspec, &sep, NULL, 0)) || !*privkey)
1776 { s = US"privkey"; goto bad_arg_ret; }
1777 if (!arc_valid_id(identity))
1778 { s = US"identity"; goto bad_arg_ret; }
1779 if (!arc_valid_id(selector))
1780 { s = US"selector"; goto bad_arg_ret; }
1781 if (*privkey == '/' && !(privkey = expand_file_big_buffer(privkey)))
1782 goto ret_sigheaders;
1784 if ((opts = string_nextinlist(&signspec, &sep, NULL, 0)))
1787 while ((s = string_nextinlist(&opts, &osep, NULL, 0)))
1788 if (Ustrcmp(s, "timestamps") == 0)
1790 options |= ARC_SIGN_OPT_TSTAMP;
1791 if (!now) now = time(NULL);
1793 else if (Ustrncmp(s, "expire", 6) == 0)
1795 options |= ARC_SIGN_OPT_EXPIRE;
1796 if (*(s += 6) == '=')
1799 if (!(expire = (time_t)atoi(CS ++s)))
1800 expire = ARC_SIGN_DEFAULT_EXPIRE_DELTA;
1801 if (!now) now = time(NULL);
1805 expire = (time_t)atol(CS s);
1808 if (!now) now = time(NULL);
1809 expire = now + ARC_SIGN_DEFAULT_EXPIRE_DELTA;
1814 DEBUG(D_transport) debug_printf("ARC: sign for %s\n", identity);
1816 /* Make an rlist of any new DKIM headers, then add the "normals" rlist to it.
1817 Then scan the list for an A-R header. */
1819 string_from_gstring(sigheaders);
1820 if ((rheaders = arc_sign_scan_headers(&arc_sign_ctx, sigheaders)))
1823 for (rp = &headers_rlist; *rp; ) rp = &(*rp)->prev;
1827 /* Finally, build a normal-order headers list */
1828 /*XXX only needed for hunt-the-AR? */
1829 /*XXX also, we really should be accepting any number of ADMD-matching ARs */
1831 header_line * hnext = NULL;
1832 for (rheaders = headers_rlist; rheaders;
1833 hnext = rheaders->h, rheaders = rheaders->prev)
1834 rheaders->h->next = hnext;
1838 if (!(arc_sign_find_ar(headers, identity, &ar)))
1840 log_write(0, LOG_MAIN, "ARC: no Authentication-Results header for signing");
1841 goto ret_sigheaders;
1844 /* We previously built the data-struct for the existing ARC chain, if any, using a headers
1845 feed from the DKIM module. Use that to give the instance number for the ARC set we are
1849 if (arc_sign_ctx.arcset_chain_last)
1850 debug_printf("ARC: existing chain highest instance: %d\n",
1851 arc_sign_ctx.arcset_chain_last->instance);
1853 debug_printf("ARC: no existing chain\n");
1855 instance = arc_sign_ctx.arcset_chain_last ? arc_sign_ctx.arcset_chain_last->instance + 1 : 1;
1859 - copy the A-R; prepend i= & identity
1862 g = arc_sign_append_aar(g, &arc_sign_ctx, identity, instance, &ar);
1866 - Looks fairly like a DKIM sig
1867 - Cover all DKIM sig headers as well as the usuals
1870 - we must have requested a suitable bodyhash previously
1871 XXX so where was that done? I don't see it!
1872 XXX ah, ok - the smtp tpt calls arc_ams_setup_sign_bodyhash() directly, early
1873 -> should pref use a better named call to make the point, but that
1874 can wait until arc becomes a module
1877 b = arc_ams_setup_sign_bodyhash();
1878 g = arc_sign_append_ams(g, &arc_sign_ctx, instance, identity, selector,
1879 &b->bh, headers_rlist, privkey, options);
1884 - no h= tag; implicit coverage
1885 - arc status from A-R
1887 - coverage is just the new ARC set
1888 including self (but with an empty b= in self)
1890 - all ARC set headers, set-number order, aar then ams then as,
1891 including self (but with an empty b= in self)
1895 g = arc_sign_prepend_as(g, &arc_sign_ctx, instance, identity, selector, &ar,
1898 /* Finally, append the dkim headers and return the lot. */
1900 if (sigheaders) g = string_catn(g, sigheaders->s, sigheaders->ptr);
1903 if (!g) return string_get(1);
1904 (void) string_from_gstring(g);
1905 gstring_release_unused(g);
1910 log_write(0, LOG_MAIN, "ARC: bad signing-specification (%s)", s);
1917 /******************************************************************************/
1919 /* Check to see if the line is an AMS and if so, set up to validate it.
1920 Called from the DKIM input processing. This must be done now as the message
1921 body data is hashed during input.
1923 We call the DKIM code to request a body-hash; it has the facility already
1924 and the hash parameters might be common with other requests.
1927 static const uschar *
1928 arc_header_vfy_feed(gstring * g)
1935 if (strncmpic(ARC_HDR_AMS, g->s, ARC_HDRLEN_AMS) != 0) return US"not AMS";
1937 DEBUG(D_receive) debug_printf("ARC: spotted AMS header\n");
1938 /* Parse the AMS header */
1940 memset(&al, 0, sizeof(arc_line));
1942 h.slen = len_string_from_gstring(g, &h.text);
1943 if ((errstr = arc_parse_line(&al, &h, ARC_HDRLEN_AMS, le_all)))
1945 DEBUG(D_acl) if (errstr) debug_printf("ARC: %s\n", errstr);
1949 if (!al.a_hash.data)
1951 DEBUG(D_acl) debug_printf("ARC: no a_hash from '%.*s'\n", h.slen, h.text);
1958 al.c_body.data = US"simple"; al.c_body.len = 6;
1959 al.c_head = al.c_body;
1962 /* Ask the dkim code to calc a bodyhash with those specs */
1964 if (!(b = arc_ams_setup_vfy_bodyhash(&al)))
1965 return US"dkim hash setup fail";
1967 /* Discard the reference; search again at verify time, knowing that one
1968 should have been created here. */
1973 return US"line parsing error";
1978 /* A header line has been identified by DKIM processing.
1982 is_vfy TRUE for verify mode or FALSE for signing mode
1985 NULL for success, or an error string (probably unused)
1989 arc_header_feed(gstring * g, BOOL is_vfy)
1991 return is_vfy ? arc_header_vfy_feed(g) : arc_header_sign_feed(g);
1996 /******************************************************************************/
1998 /* Construct the list of domains from the ARC chain after validation */
2001 fn_arc_domains(void)
2007 for (as = arc_verify_ctx.arcset_chain, inst = 1; as; as = as->next, inst++)
2009 arc_line * hdr_as = as->hdr_as;
2012 blob * d = &hdr_as->d;
2014 for (; inst < as->instance; inst++)
2015 g = string_catn(g, US":", 1);
2017 g = d->data && d->len
2018 ? string_append_listele_n(g, ':', d->data, d->len)
2019 : string_catn(g, US":", 1);
2022 g = string_catn(g, US":", 1);
2024 if (!g) return US"";
2025 return string_from_gstring(g);
2029 /* Construct an Authentication-Results header portion, for the ARC module */
2032 authres_arc(gstring * g)
2036 arc_line * highest_ams;
2037 int start = 0; /* Compiler quietening */
2038 DEBUG(D_acl) start = gstring_length(g);
2040 g = string_append(g, 2, US";\n\tarc=", arc_state);
2041 if (arc_received_instance > 0)
2043 g = string_fmt_append(g, " (i=%d)", arc_received_instance);
2044 if (arc_state_reason)
2045 g = string_append(g, 3, US"(", arc_state_reason, US")");
2046 g = string_catn(g, US" header.s=", 10);
2047 highest_ams = arc_received->hdr_ams;
2048 g = string_catn(g, highest_ams->s.data, highest_ams->s.len);
2050 g = string_fmt_append(g, " arc.oldest-pass=%d", arc_oldest_pass);
2052 if (sender_host_address)
2053 g = string_append(g, 2, US" smtp.remote-ip=", sender_host_address);
2055 else if (arc_state_reason)
2056 g = string_append(g, 3, US" (", arc_state_reason, US")");
2057 DEBUG(D_acl) debug_printf("ARC:\tauthres '%.*s'\n",
2058 gstring_length(g) - start - 3, g->s + start + 3);
2061 DEBUG(D_acl) debug_printf("ARC:\tno authres\n");
2066 # ifdef SUPPORT_DMARC
2067 /* Append a DMARC history record pair for ARC, to the given history set */
2070 arc_dmarc_hist_append(gstring * g)
2075 int i = Ustrcmp(arc_state, "pass") == 0 ? ARES_RESULT_PASS
2076 : Ustrcmp(arc_state, "fail") == 0 ? ARES_RESULT_FAIL
2077 : ARES_RESULT_UNKNOWN;
2078 g = string_fmt_append(g, "arc %d\n", i);
2079 g = string_fmt_append(g, "arc_policy %d json[",
2080 i == ARES_RESULT_PASS ? DMARC_ARC_POLICY_RESULT_PASS
2081 : i == ARES_RESULT_FAIL ? DMARC_ARC_POLICY_RESULT_FAIL
2082 : DMARC_ARC_POLICY_RESULT_UNUSED);
2083 /*XXX would we prefer this backwards? */
2084 for (arc_set * as = arc_verify_ctx.arcset_chain; as;
2085 as = as->next, first = FALSE)
2087 arc_line * line = as->hdr_as;
2090 blob * d = &line->d;
2091 blob * s = &line->s;
2094 g = string_catn(g, US",", 1);
2096 g = string_fmt_append(g, " (\"i\":%u," /*)*/
2100 d->data ? (int)d->len : 0, d->data && d->len ? d->data : US"",
2101 s->data ? (int)s->len : 0, s->data && s->len ? s->data : US""
2103 if ((line = as->hdr_aar))
2105 blob * ip = &line->ip;
2106 if (ip->data && ip->len)
2107 g = string_fmt_append(g, ", \"ip\":\"%.*s\"", (int)ip->len, ip->data);
2110 g = string_catn(g, US")", 1);
2113 g = string_catn(g, US" ]\n", 3);
2116 g = string_fmt_append(g, "arc %d\narc_policy %d json:[]\n",
2117 ARES_RESULT_UNKNOWN, DMARC_ARC_POLICY_RESULT_UNUSED);
2123 # endif /* DISABLE_DKIM */
2124 #endif /* EXPERIMENTAL_ARC */