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 "pdkim/pdkim.h"
19 # include "pdkim/signing.h"
22 # include "miscmods/dmarc.h"
25 extern pdkim_ctx * dkim_verify_ctx;
26 extern pdkim_ctx dkim_sign_ctx;
28 #define ARC_SIGN_OPT_TSTAMP BIT(0)
29 #define ARC_SIGN_OPT_EXPIRE BIT(1)
31 #define ARC_SIGN_DEFAULT_EXPIRE_DELTA (60 * 60 * 24 * 30) /* one month */
33 /******************************************************************************/
35 typedef struct hdr_rlist {
36 struct hdr_rlist * prev;
41 typedef struct arc_line {
42 header_line * complete; /* including the header name; nul-term */
45 /* identified tag contents */
59 /* tag content sub-portions */
66 /* modified copy of b= field in line */
70 typedef struct arc_set {
71 struct arc_set * next;
72 struct arc_set * prev;
79 const uschar * ams_verify_done;
80 BOOL ams_verify_passed;
83 typedef struct arc_ctx {
84 arc_set * arcset_chain;
85 arc_set * arcset_chain_last;
88 #define ARC_HDR_AAR US"ARC-Authentication-Results:"
89 #define ARC_HDRLEN_AAR 27
90 #define ARC_HDR_AMS US"ARC-Message-Signature:"
91 #define ARC_HDRLEN_AMS 22
92 #define ARC_HDR_AS US"ARC-Seal:"
93 #define ARC_HDRLEN_AS 9
94 #define HDR_AR US"Authentication-Results:"
97 typedef enum line_extract {
104 static time_t expire;
105 static hdr_rlist * headers_rlist;
106 static arc_ctx arc_sign_ctx = { NULL };
107 static arc_ctx arc_verify_ctx = { NULL };
109 /* We build a context for either Sign or Verify.
111 For Verify, it's a fresh new one for ACL verify=arc - there is no connection
112 with the single line handling done during reception via the DKIM feed.
114 For Verify we do it twice; initially during reception (via the DKIM feed)
115 and then later for the full verification.
117 The former only looks at AMS headers, to discover what hash(es) we need done for
118 ARC on the message body; we call back to the DKIM code to set up so that it does
119 them for us during reception. That call needs info from many of the AMS tags;
120 arc_parse_line() for only the AMS is called asking for all the tag types.
121 That context is then discarded.
123 Later, for Verify, we look at ARC headers again and then grab the hash result
124 from the DKIM layer. arc_parse_line() is called for all 3 line types,
125 gathering info for only 'i' and 'ip' tags from AAR headers,
126 for all tag types from AMS and AS headers.
129 For Sign, while running through the existing headers (before adding any for
130 this signing operation, we "take copies" of the headers, we call
131 arc_parse_line() gathering only the 'i' tag (instance) information.
135 /******************************************************************************/
138 /* Get the instance number from the header.
141 arc_instance_from_hdr(const arc_line * al)
143 const uschar * s = al->i.data;
144 if (!s || !al->i.len) return 0;
145 return (unsigned) atoi(CCS s);
153 while (c && (c == ' ' || c == '\t' || c == '\n' || c == '\r')) c = *++s;
158 /* Locate instance struct on chain, inserting a new one if
159 needed. The chain is in increasing-instance-number order
160 by the "next" link, and we have a "prev" link also.
164 arc_find_set(arc_ctx * ctx, unsigned i)
166 arc_set ** pas, * as, * next, * prev;
168 for (pas = &ctx->arcset_chain, prev = NULL, next = ctx->arcset_chain;
169 as = *pas; pas = &as->next)
171 if (as->instance > i) break;
172 if (as->instance == i)
174 DEBUG(D_acl) debug_printf("ARC: existing instance %u\n", i);
181 DEBUG(D_acl) debug_printf("ARC: new instance %u\n", i);
182 *pas = as = store_get(sizeof(arc_set), GET_UNTAINTED);
183 memset(as, 0, sizeof(arc_set));
190 ctx->arcset_chain_last = as;
196 /* Insert a tag content into the line structure.
197 Note this is a reference to existing data, not a copy.
198 Check for already-seen tag.
199 The string-pointer is on the '=' for entry. Update it past the
200 content (to the ;) on return;
204 arc_insert_tagvalue(arc_line * al, unsigned loff, uschar ** ss)
208 blob * b = (blob *)(US al + loff);
211 /* [FWS] tag-value [FWS] */
213 if (b->data) return US"fail";
214 s = skip_fws(s); /* FWS */
217 while ((c = *s) && c != ';') { len++; s++; }
219 while (len && ((c = s[-1]) == ' ' || c == '\t' || c == '\n' || c == '\r'))
220 { s--; len--; } /* FWS */
226 /* Inspect a header line, noting known tag fields.
227 Check for duplicate named tags.
229 See the file block comment for how this is used.
231 Return: NULL for good, or an error string
235 arc_parse_line(arc_line * al, header_line * h, unsigned off, line_extract_t l_ext)
237 uschar * s = h->text + off;
243 if (l_ext == le_all) /* need to grab rawsig_no_b */
245 al->rawsig_no_b_val.data = store_get(h->slen + 1, GET_TAINTED);
246 memcpy(al->rawsig_no_b_val.data, h->text, off); /* copy the header name blind */
247 r = al->rawsig_no_b_val.data + off;
248 al->rawsig_no_b_val.len = off;
251 /* tag-list = tag-spec *( ";" tag-spec ) [ ";" ] */
258 uschar * fieldstart = s;
259 uschar * bstart = NULL, * bend;
261 /* tag-spec = [FWS] tag-name [FWS] "=" [FWS] tag-value [FWS] */
262 /*X or just a naked FQDN, in a AAR ! */
264 s = skip_fws(s); /* leading FWS */
267 if (!*(s = skip_fws(s))) break; /* FWS */
271 case 'a': /* a= AMS algorithm */
272 if (l_ext == le_all && *s == '=')
274 if (arc_insert_tagvalue(al, offsetof(arc_line, a), &s)) return US"a tag dup";
276 /* substructure: algo-hash (eg. rsa-sha256) */
278 t = al->a_algo.data = al->a.data;
280 if (!*t++ || ++i > al->a.len) return US"no '-' in 'a' value";
282 if (*t++ != '-') return US"no '-' in 'a' value";
284 al->a_hash.len = al->a.len - i - 1;
294 case '=': /* b= AMS signature */
295 if (al->b.data) return US"already b data";
298 /* The signature can have FWS inserted in the content;
299 make a stripped copy */
301 while ((c = *++s) && c != ';')
302 if (c != ' ' && c != '\t' && c != '\n' && c != '\r')
303 g = string_catn(g, s, 1);
304 if (!g) return US"no b= value";
305 al->b.len = len_string_from_gstring(g, &al->b.data);
306 gstring_release_unused(g);
309 case 'h': /* bh= AMS body hash */
310 s = skip_fws(++s); /* FWS */
313 if (al->bh.data) return US"already bh data";
315 /* The bodyhash can have FWS inserted in the content;
316 make a stripped copy */
318 while ((c = *++s) && c != ';')
319 if (c != ' ' && c != '\t' && c != '\n' && c != '\r')
320 g = string_catn(g, s, 1);
321 if (!g) return US"no bh= value";
322 al->bh.len = len_string_from_gstring(g, &al->bh.data);
323 gstring_release_unused(g);
332 if (l_ext == le_all) switch (*s)
334 case '=': /* c= AMS canonicalisation */
335 if (arc_insert_tagvalue(al, offsetof(arc_line, c), &s)) return US"c tag dup";
337 /* substructure: head/body (eg. relaxed/simple)) */
339 t = al->c_head.data = al->c.data;
341 if (!*t++ || ++i > al->a.len) break;
343 if (*t++ == '/') /* /body is optional */
346 al->c_body.len = al->c.len - i - 1;
350 al->c_body.data = US"simple";
354 case 'v': /* cv= AS validity */
357 if (arc_insert_tagvalue(al, offsetof(arc_line, cv), &s))
358 return US"cv tag dup";
362 case 'd': /* d= AMS domain */
363 if (l_ext == le_all && *s == '=')
364 if (arc_insert_tagvalue(al, offsetof(arc_line, d), &s))
365 return US"d tag dup";
367 case 'h': /* h= AMS headers */
369 if (arc_insert_tagvalue(al, offsetof(arc_line, h), &s))
370 return US"h tag dup";
372 case 'i': /* i= ARC set instance */
375 if (arc_insert_tagvalue(al, offsetof(arc_line, i), &s))
376 return US"i tag dup";
377 if (l_ext == le_instance_only)
378 goto done; /* early-out */
381 case 'l': /* l= bodylength */
382 if (l_ext == le_all && *s == '=')
383 if (arc_insert_tagvalue(al, offsetof(arc_line, l), &s))
384 return US"l tag dup";
387 if (*s == '=' && l_ext == le_all)
389 if (arc_insert_tagvalue(al, offsetof(arc_line, s), &s))
390 return US"s tag dup";
392 else if ( l_ext == le_instance_plus_ip
393 && Ustrncmp(s, "mtp.remote-ip", 13) == 0)
394 { /* smtp.remote-ip= AAR reception data */
397 if (*s != '=') return US"smtp.remote_ip tag val";
398 if (arc_insert_tagvalue(al, offsetof(arc_line, ip), &s))
399 return US"ip tag dup";
404 while ((c = *s) && c != ';') s++; /* end of this tag=value */
405 if (c) s++; /* ; after tag-spec */
407 /* for all but the b= tag, copy the field including FWS. For the b=,
408 drop the tag content. */
413 size_t n = bstart - fieldstart;
414 memcpy(r, fieldstart, n); /* FWS "b=" */
416 al->rawsig_no_b_val.len += n;
418 memcpy(r, bend, n); /* FWS ";" */
420 al->rawsig_no_b_val.len += n;
424 size_t n = s - fieldstart;
425 memcpy(r, fieldstart, n);
427 al->rawsig_no_b_val.len += n;
435 /* debug_printf("%s: finshed\n", __FUNCTION__); */
440 /* Insert one header line in the correct set of the chain,
441 adding instances as needed and checking for duplicate lines.
445 arc_insert_hdr(arc_ctx * ctx, header_line * h, unsigned off, unsigned hoff,
446 line_extract_t l_ext, arc_line ** alp_ret)
450 arc_line * al = store_get(sizeof(arc_line), GET_UNTAINTED), ** alp;
453 memset(al, 0, sizeof(arc_line));
455 if ((e = arc_parse_line(al, h, off, l_ext)))
457 DEBUG(D_acl) if (e) debug_printf("ARC: %s\n", e);
458 return string_sprintf("line parse: %s", e);
460 if (!(i = arc_instance_from_hdr(al))) return US"instance find";
461 if (i > 50) return US"overlarge instance number";
462 if (!(as = arc_find_set(ctx, i))) return US"set find";
463 if (*(alp = (arc_line **)(US as + hoff))) return US"dup hdr";
466 if (alp_ret) *alp_ret = al;
472 /* Called for both Sign and Verify */
474 static const uschar *
475 arc_try_header(arc_ctx * ctx, header_line * h, BOOL is_signing)
479 /*debug_printf("consider hdr '%s'\n", h->text);*/
480 if (strncmpic(ARC_HDR_AAR, h->text, ARC_HDRLEN_AAR) == 0)
486 for (s = h->text + h->slen; s[-1] == '\r' || s[-1] == '\n'; )
488 debug_printf("ARC: found AAR: %.*s\n", len, h->text);
490 if ((e = arc_insert_hdr(ctx, h, ARC_HDRLEN_AAR, offsetof(arc_set, hdr_aar),
491 is_signing ? le_instance_only : le_instance_plus_ip, NULL)))
493 DEBUG(D_acl) debug_printf("inserting AAR: %s\n", e);
494 return string_sprintf("inserting AAR: %s", e);
497 else if (strncmpic(ARC_HDR_AMS, h->text, ARC_HDRLEN_AMS) == 0)
505 for (s = h->text + h->slen; s[-1] == '\r' || s[-1] == '\n'; )
507 debug_printf("ARC: found AMS: %.*s\n", len, h->text);
509 if ((e = arc_insert_hdr(ctx, h, ARC_HDRLEN_AMS, offsetof(arc_set, hdr_ams),
510 is_signing ? le_instance_only : le_all, &ams)))
512 DEBUG(D_acl) debug_printf("inserting AMS: %s\n", e);
513 return string_sprintf("inserting AMS: %s", e);
519 ams->c_head.data = US"simple"; ams->c_head.len = 6;
520 ams->c_body = ams->c_head;
523 else if (strncmpic(ARC_HDR_AS, h->text, ARC_HDRLEN_AS) == 0)
529 for (s = h->text + h->slen; s[-1] == '\r' || s[-1] == '\n'; )
531 debug_printf("ARC: found AS: %.*s\n", len, h->text);
533 if ((e = arc_insert_hdr(ctx, h, ARC_HDRLEN_AS, offsetof(arc_set, hdr_as),
534 is_signing ? le_instance_only : le_all, NULL)))
536 DEBUG(D_acl) debug_printf("inserting AS: %s\n", e);
537 return string_sprintf("inserting AS: %s", e);
545 /* Gather the chain of arc sets from the headers.
546 Check for duplicates while that is done. Also build the
547 reverse-order headers list.
548 Called on an ACL verify=arc condition.
550 Return: ARC state if determined, eg. by lack of any ARC chain.
553 static const uschar *
554 arc_vfy_collect_hdrs(arc_ctx * ctx)
557 hdr_rlist * r = NULL, * rprev = NULL;
560 DEBUG(D_acl) debug_printf("ARC: collecting arc sets\n");
561 for (h = header_list; h; h = h->next)
563 r = store_get(sizeof(hdr_rlist), GET_UNTAINTED);
569 if ((e = arc_try_header(ctx, h, FALSE)))
571 arc_state_reason = string_sprintf("collecting headers: %s", e);
577 if (!ctx->arcset_chain) return US"none";
583 arc_cv_match(arc_line * al, const uschar * s)
585 return Ustrncmp(s, al->cv.data, al->cv.len) == 0;
588 /******************************************************************************/
590 /* Return the hash of headers from the message that the AMS claims it
595 arc_get_verify_hhash(arc_ctx * ctx, arc_line * ams, blob * hhash)
597 const uschar * headernames = string_copyn(ams->h.data, ams->h.len);
601 BOOL relaxed = Ustrncmp(US"relaxed", ams->c_head.data, ams->c_head.len) == 0;
602 int hashtype = pdkim_hashname_to_hashtype(
603 ams->a_hash.data, ams->a_hash.len);
609 || !exim_sha_init(&hhash_ctx, pdkim_hashes[hashtype].exim_hashmethod))
612 debug_printf("ARC: hash setup error, possibly nonhandled hashtype\n");
616 /* For each headername in the list from the AMS (walking in order)
617 walk the message headers in reverse order, adding to the hash any
618 found for the first time. For that last point, maintain used-marks
619 on the list of message headers. */
621 DEBUG(D_acl) debug_printf("ARC: AMS header data for verification:\n");
623 for (r = headers_rlist; r; r = r->prev)
625 while ((hn = string_nextinlist(&headernames, &sep, NULL, 0)))
626 for (r = headers_rlist; r; r = r->prev)
628 && strncasecmp(CCS (s = r->h->text), CCS hn, Ustrlen(hn)) == 0
631 if (relaxed) s = pdkim_relax_header_n(s, r->h->slen, TRUE);
633 DEBUG(D_acl) debug_printf("%Z\n", s);
634 exim_sha_update_string(&hhash_ctx, s);
639 /* Finally add in the signature header (with the b= tag stripped); no CRLF */
641 s = ams->rawsig_no_b_val.data, len = ams->rawsig_no_b_val.len;
643 len = Ustrlen(s = pdkim_relax_header_n(s, len, FALSE));
644 DEBUG(D_acl) debug_printf("%.*Z\n", len, s);
645 exim_sha_update(&hhash_ctx, s, len);
647 exim_sha_finish(&hhash_ctx, hhash);
649 { debug_printf("ARC: header hash: %.*H\n", hhash->len, hhash->data); }
656 static pdkim_pubkey *
657 arc_line_to_pubkey(arc_line * al)
662 if (!(dns_txt = dkim_exim_query_dns_txt(string_sprintf("%.*s._domainkey.%.*s",
663 (int)al->s.len, al->s.data, (int)al->d.len, al->d.data))))
665 DEBUG(D_acl) debug_printf("pubkey dns lookup fail\n");
669 if ( !(p = pdkim_parse_pubkey_record(dns_txt))
670 || (Ustrcmp(p->srvtype, "*") != 0 && Ustrcmp(p->srvtype, "email") != 0)
673 DEBUG(D_acl) debug_printf("pubkey dns lookup format error\n");
677 /* If the pubkey limits use to specified hashes, reject unusable
678 signatures. XXX should we have looked for multiple dns records? */
682 const uschar * list = p->hashes, * ele;
685 while ((ele = string_nextinlist(&list, &sep, NULL, 0)))
686 if (Ustrncmp(ele, al->a_hash.data, al->a_hash.len) == 0) break;
689 DEBUG(D_acl) debug_printf("pubkey h=%s vs sig a=%.*s\n",
690 p->hashes, (int)al->a.len, al->a.data);
700 static pdkim_bodyhash *
701 arc_ams_setup_vfy_bodyhash(arc_line * ams)
703 int canon_head = -1, canon_body = -1;
706 if (!ams->c.data) ams->c.data = US"simple"; /* RFC 6376 (DKIM) default */
707 pdkim_cstring_to_canons(ams->c.data, ams->c.len, &canon_head, &canon_body);
708 bodylen = ams->l.data
709 ? strtol(CS string_copyn(ams->l.data, ams->l.len), NULL, 10) : -1;
711 return pdkim_set_bodyhash(dkim_verify_ctx,
712 pdkim_hashname_to_hashtype(ams->a_hash.data, ams->a_hash.len),
719 /* Verify an AMS. This is a DKIM-sig header, but with an ARC i= tag
720 and without a DKIM v= tag.
723 static const uschar *
724 arc_ams_verify(arc_ctx * ctx, arc_set * as)
726 arc_line * ams = as->hdr_ams;
733 const uschar * errstr;
735 as->ams_verify_done = US"in-progress";
737 /* Check the AMS has all the required tags:
741 "d=" domain (for key lookup)
742 "h=" headers (included in signature)
743 "s=" key-selector (for key lookup)
745 if ( !ams->a.data || !ams->b.data || !ams->bh.data || !ams->d.data
746 || !ams->h.data || !ams->s.data)
748 as->ams_verify_done = arc_state_reason = US"required tag missing";
753 /* The bodyhash should have been created earlier, and the dkim code should
754 have managed calculating it during message input. Find the reference to it. */
756 if (!(b = arc_ams_setup_vfy_bodyhash(ams)))
758 as->ams_verify_done = arc_state_reason = US"internal hash setup error";
764 debug_printf("ARC i=%d AMS Body bytes hashed: %lu\n"
765 " Body %.*s computed: ",
766 as->instance, b->signed_body_bytes,
767 (int)ams->a_hash.len, ams->a_hash.data);
768 debug_printf("%.*H\n", b->bh.len, b->bh.data);
771 /* We know the bh-tag blob is of a nul-term string, so safe as a string */
774 || (pdkim_decode_base64(ams->bh.data, &sighash), sighash.len != b->bh.len)
775 || memcmp(sighash.data, b->bh.data, b->bh.len) != 0
780 debug_printf("ARC i=%d AMS Body hash from headers: ", as->instance);
781 debug_printf("%.*H\n", sighash.len, sighash.data);
782 debug_printf("ARC i=%d AMS Body hash did NOT match\n", as->instance);
784 return as->ams_verify_done = arc_state_reason = US"AMS body hash miscompare";
787 DEBUG(D_acl) debug_printf("ARC i=%d AMS Body hash compared OK\n", as->instance);
789 /* Get the public key from DNS */
791 if (!(p = arc_line_to_pubkey(ams)))
792 return as->ams_verify_done = arc_state_reason = US"pubkey problem";
794 /* We know the b-tag blob is of a nul-term string, so safe as a string */
795 pdkim_decode_base64(ams->b.data, &sighash);
797 arc_get_verify_hhash(ctx, ams, &hhash);
799 /* Setup the interface to the signing library */
801 if ((errstr = exim_dkim_verify_init(&p->key, KEYFMT_DER, &vctx, NULL)))
803 DEBUG(D_acl) debug_printf("ARC verify init: %s\n", errstr);
804 as->ams_verify_done = arc_state_reason = US"internal sigverify init error";
808 hashtype = pdkim_hashname_to_hashtype(ams->a_hash.data, ams->a_hash.len);
811 DEBUG(D_acl) debug_printf("ARC i=%d AMS verify bad a_hash\n", as->instance);
812 return as->ams_verify_done = arc_state_reason = US"AMS sig nonverify";
815 if ((errstr = exim_dkim_verify(&vctx,
816 pdkim_hashes[hashtype].exim_hashmethod, &hhash, &sighash)))
818 DEBUG(D_acl) debug_printf("ARC i=%d AMS verify %s\n", as->instance, errstr);
819 return as->ams_verify_done = arc_state_reason = US"AMS sig nonverify";
822 DEBUG(D_acl) debug_printf("ARC i=%d AMS verify pass\n", as->instance);
823 as->ams_verify_passed = TRUE;
829 /* Check the sets are instance-continuous and that all
830 members are present. Check that no arc_seals are "fail".
831 Set the highest instance number global.
832 Verify the latest AMS.
835 arc_headers_check(arc_ctx * ctx)
839 BOOL ams_fail_found = FALSE;
841 if (!(as = ctx->arcset_chain_last))
844 for(inst = as->instance; as; as = as->prev, inst--)
846 if (as->instance != inst)
847 arc_state_reason = string_sprintf("i=%d (sequence; expected %d)",
849 else if (!as->hdr_aar || !as->hdr_ams || !as->hdr_as)
850 arc_state_reason = string_sprintf("i=%d (missing header)", as->instance);
851 else if (arc_cv_match(as->hdr_as, US"fail"))
852 arc_state_reason = string_sprintf("i=%d (cv)", as->instance);
856 DEBUG(D_acl) debug_printf("ARC chain fail at %s\n", arc_state_reason);
860 /* Evaluate the oldest-pass AMS validation while we're here.
861 It does not affect the AS chain validation but is reported as
865 if (arc_ams_verify(ctx, as))
866 ams_fail_found = TRUE;
868 arc_oldest_pass = inst;
869 arc_state_reason = NULL;
873 arc_state_reason = string_sprintf("(sequence; expected i=%d)", inst);
874 DEBUG(D_acl) debug_printf("ARC chain fail %s\n", arc_state_reason);
878 arc_received = ctx->arcset_chain_last;
879 arc_received_instance = arc_received->instance;
881 /* We can skip the latest-AMS validation, if we already did it. */
883 as = ctx->arcset_chain_last;
884 if (!as->ams_verify_passed)
886 if (as->ams_verify_done)
888 arc_state_reason = as->ams_verify_done;
891 if (!!arc_ams_verify(ctx, as))
898 /******************************************************************************/
899 static const uschar *
900 arc_seal_verify(arc_ctx * ctx, arc_set * as)
902 arc_line * hdr_as = as->hdr_as;
910 const uschar * errstr;
912 DEBUG(D_acl) debug_printf("ARC: AS vfy i=%d\n", as->instance);
914 1. If the value of the "cv" tag on that seal is "fail", the
915 chain state is "fail" and the algorithm stops here. (This
916 step SHOULD be skipped if the earlier step (2.1) was
919 2. In Boolean nomenclature: if ((i == 1 && cv != "none") or (cv
920 == "none" && i != 1)) then the chain state is "fail" and the
921 algorithm stops here (note that the ordering of the logic is
922 structured for short-circuit evaluation).
925 if ( as->instance == 1 && !arc_cv_match(hdr_as, US"none")
926 || arc_cv_match(hdr_as, US"none") && as->instance != 1
929 arc_state_reason = US"seal cv state";
934 3. Initialize a hash function corresponding to the "a" tag of
938 hashtype = pdkim_hashname_to_hashtype(hdr_as->a_hash.data, hdr_as->a_hash.len);
941 || !exim_sha_init(&hhash_ctx, pdkim_hashes[hashtype].exim_hashmethod))
944 debug_printf("ARC: hash setup error, possibly nonhandled hashtype\n");
945 arc_state_reason = US"seal hash setup error";
950 4. Compute the canonicalized form of the ARC header fields, in
951 the order described in Section 5.4.2, using the "relaxed"
952 header canonicalization defined in Section 3.4.2 of
953 [RFC6376]. Pass the canonicalized result to the hash
956 Headers are CRLF-separated, but the last one is not crlf-terminated.
959 DEBUG(D_acl) debug_printf("ARC: AS header data for verification:\n");
960 for (as2 = ctx->arcset_chain;
961 as2 && as2->instance <= as->instance;
969 if (!(s = al->relaxed))
970 al->relaxed = s = pdkim_relax_header_n(al->complete->text,
971 al->complete->slen, TRUE);
973 DEBUG(D_acl) debug_printf("%Z\n", s);
974 exim_sha_update(&hhash_ctx, s, len);
977 if (!(s = al->relaxed))
978 al->relaxed = s = pdkim_relax_header_n(al->complete->text,
979 al->complete->slen, TRUE);
981 DEBUG(D_acl) debug_printf("%Z\n", s);
982 exim_sha_update(&hhash_ctx, s, len);
985 if (as2->instance == as->instance)
986 s = pdkim_relax_header_n(al->rawsig_no_b_val.data,
987 al->rawsig_no_b_val.len, FALSE);
988 else if (!(s = al->relaxed))
989 al->relaxed = s = pdkim_relax_header_n(al->complete->text,
990 al->complete->slen, TRUE);
992 DEBUG(D_acl) debug_printf("%Z\n", s);
993 exim_sha_update(&hhash_ctx, s, len);
997 5. Retrieve the final digest from the hash function.
1000 exim_sha_finish(&hhash_ctx, &hhash_computed);
1003 debug_printf("ARC i=%d AS Header %.*s computed: ",
1004 as->instance, (int)hdr_as->a_hash.len, hdr_as->a_hash.data);
1005 debug_printf("%.*H\n", hhash_computed.len, hhash_computed.data);
1010 6. Retrieve the public key identified by the "s" and "d" tags in
1011 the ARC-Seal, as described in Section 4.1.6.
1014 if (!(p = arc_line_to_pubkey(hdr_as)))
1015 return US"pubkey problem";
1018 7. Determine whether the signature portion ("b" tag) of the ARC-
1019 Seal and the digest computed above are valid according to the
1020 public key. (See also Section Section 8.4 for failure case
1023 8. If the signature is not valid, the chain state is "fail" and
1024 the algorithm stops here.
1027 /* We know the b-tag blob is of a nul-term string, so safe as a string */
1028 pdkim_decode_base64(hdr_as->b.data, &sighash);
1030 if ((errstr = exim_dkim_verify_init(&p->key, KEYFMT_DER, &vctx, NULL)))
1032 DEBUG(D_acl) debug_printf("ARC verify init: %s\n", errstr);
1036 if ((errstr = exim_dkim_verify(&vctx,
1037 pdkim_hashes[hashtype].exim_hashmethod,
1038 &hhash_computed, &sighash)))
1041 debug_printf("ARC i=%d AS headers verify: %s\n", as->instance, errstr);
1042 arc_state_reason = US"seal sigverify error";
1046 DEBUG(D_acl) debug_printf("ARC: AS vfy i=%d pass\n", as->instance);
1051 static const uschar *
1052 arc_verify_seals(arc_ctx * ctx)
1054 arc_set * as = ctx->arcset_chain_last;
1059 for ( ; as; as = as->prev) if (arc_seal_verify(ctx, as)) return US"fail";
1061 DEBUG(D_acl) debug_printf("ARC: AS vfy overall pass\n");
1064 /******************************************************************************/
1066 /* Do ARC verification. Called from DATA ACL, on a verify = arc
1067 condition. No arguments; we are checking globals.
1069 Return: The ARC state, or NULL on error.
1073 acl_verify_arc(void)
1077 memset(&arc_verify_ctx, 0, sizeof(arc_verify_ctx));
1079 if (!dkim_verify_ctx)
1081 DEBUG(D_acl) debug_printf("ARC: no DKIM verify context\n");
1085 /* AS evaluation, per
1086 https://tools.ietf.org/html/draft-ietf-dmarc-arc-protocol-10#section-6
1088 /* 1. Collect all ARC sets currently on the message. If there were
1089 none, the ARC state is "none" and the algorithm stops here.
1092 if ((res = arc_vfy_collect_hdrs(&arc_verify_ctx)))
1095 /* 2. If the form of any ARC set is invalid (e.g., does not contain
1096 exactly one of each of the three ARC-specific header fields),
1097 then the chain state is "fail" and the algorithm stops here.
1099 1. To avoid the overhead of unnecessary computation and delay
1100 from crypto and DNS operations, the cv value for all ARC-
1101 Seal(s) MAY be checked at this point. If any of the values
1102 are "fail", then the overall state of the chain is "fail" and
1103 the algorithm stops here.
1105 3. Conduct verification of the ARC-Message-Signature header field
1106 bearing the highest instance number. If this verification fails,
1107 then the chain state is "fail" and the algorithm stops here.
1110 if ((res = arc_headers_check(&arc_verify_ctx)))
1113 /* 4. For each ARC-Seal from the "N"th instance to the first, apply the
1116 1. If the value of the "cv" tag on that seal is "fail", the
1117 chain state is "fail" and the algorithm stops here. (This
1118 step SHOULD be skipped if the earlier step (2.1) was
1121 2. In Boolean nomenclature: if ((i == 1 && cv != "none") or (cv
1122 == "none" && i != 1)) then the chain state is "fail" and the
1123 algorithm stops here (note that the ordering of the logic is
1124 structured for short-circuit evaluation).
1126 3. Initialize a hash function corresponding to the "a" tag of
1129 4. Compute the canonicalized form of the ARC header fields, in
1130 the order described in Section 5.4.2, using the "relaxed"
1131 header canonicalization defined in Section 3.4.2 of
1132 [RFC6376]. Pass the canonicalized result to the hash
1135 5. Retrieve the final digest from the hash function.
1137 6. Retrieve the public key identified by the "s" and "d" tags in
1138 the ARC-Seal, as described in Section 4.1.6.
1140 7. Determine whether the signature portion ("b" tag) of the ARC-
1141 Seal and the digest computed above are valid according to the
1142 public key. (See also Section Section 8.4 for failure case
1145 8. If the signature is not valid, the chain state is "fail" and
1146 the algorithm stops here.
1148 5. If all seals pass validation, then the chain state is "pass", and
1149 the algorithm is complete.
1152 if ((res = arc_verify_seals(&arc_verify_ctx)))
1161 /******************************************************************************/
1163 /* Prepend the header to the rlist */
1166 arc_rlist_entry(hdr_rlist * list, const uschar * s, int len)
1168 hdr_rlist * r = store_get(sizeof(hdr_rlist) + sizeof(header_line), GET_UNTAINTED);
1169 header_line * h = r->h = (header_line *)(r+1);
1182 /* Walk the given headers strings identifying each header, and construct
1183 a reverse-order list.
1187 arc_sign_scan_headers(arc_ctx * ctx, gstring * sigheaders)
1190 hdr_rlist * rheaders = NULL;
1192 s = sigheaders ? sigheaders->s : NULL;
1195 const uschar * s2 = s;
1197 /* This works for either NL or CRLF lines; also nul-termination */
1199 if (*s2 == '\n' && s2[1] != '\t' && s2[1] != ' ') break;
1200 s2++; /* move past end of line */
1202 rheaders = arc_rlist_entry(rheaders, s, s2 - s);
1210 /* Return the A-R content, without identity, with line-ending and
1214 arc_sign_find_ar(header_line * headers, const uschar * identity, blob * ret)
1217 int ilen = Ustrlen(identity);
1220 for(h = headers; h; h = h->next)
1222 uschar * s = h->text, c;
1225 if (Ustrncmp(s, HDR_AR, HDRLEN_AR) != 0) continue;
1226 s += HDRLEN_AR, len -= HDRLEN_AR; /* header name */
1228 && (c = *s) && (c == ' ' || c == '\t' || c == '\r' || c == '\n'))
1229 s++, len--; /* FWS */
1230 if (Ustrncmp(s, identity, ilen) != 0) continue;
1231 s += ilen; len -= ilen; /* identity */
1232 if (len <= 0) continue;
1233 if ((c = *s) && c == ';') s++, len--; /* identity terminator */
1235 && (c = *s) && (c == ' ' || c == '\t' || c == '\r' || c == '\n'))
1236 s++, len--; /* FWS */
1237 if (len <= 0) continue;
1247 /* Append a constructed AAR including CRLF. Add it to the arc_ctx too. */
1250 arc_sign_append_aar(gstring * g, arc_ctx * ctx,
1251 const uschar * identity, int instance, blob * ar)
1253 int aar_off = gstring_length(g);
1255 store_get(sizeof(arc_set) + sizeof(arc_line) + sizeof(header_line), GET_UNTAINTED);
1256 arc_line * al = (arc_line *)(as+1);
1257 header_line * h = (header_line *)(al+1);
1259 g = string_catn(g, ARC_HDR_AAR, ARC_HDRLEN_AAR);
1260 g = string_fmt_append(g, " i=%d; %s; smtp.remote-ip=%s;\r\n\t",
1261 instance, identity, sender_host_address);
1262 g = string_catn(g, US ar->data, ar->len);
1264 h->slen = g->ptr - aar_off;
1265 h->text = g->s + aar_off;
1268 as->prev = ctx->arcset_chain_last;
1269 as->instance = instance;
1272 ctx->arcset_chain = as;
1274 ctx->arcset_chain_last->next = as;
1275 ctx->arcset_chain_last = as;
1277 DEBUG(D_transport) debug_printf("ARC: AAR '%.*s'\n", h->slen - 2, h->text);
1284 arc_sig_from_pseudoheader(gstring * hdata, int hashtype, const uschar * privkey,
1285 blob * sig, const uschar * why)
1287 hashmethod hm = /*sig->keytype == KEYTYPE_ED25519*/ FALSE
1288 ? HASH_SHA2_512 : pdkim_hashes[hashtype].exim_hashmethod;
1291 const uschar * errstr;
1296 debug_printf("ARC: %s header data for signing:\n", why);
1297 debug_printf("%.*Z\n", hdata->ptr, hdata->s);
1299 (void) exim_sha_init(&hhash_ctx, pdkim_hashes[hashtype].exim_hashmethod);
1300 exim_sha_update(&hhash_ctx, hdata->s, hdata->ptr);
1301 exim_sha_finish(&hhash_ctx, &hhash);
1302 debug_printf("ARC: header hash: %.*H\n", hhash.len, hhash.data);
1305 if (FALSE /*need hash for Ed25519 or GCrypt signing*/ )
1308 (void) exim_sha_init(&hhash_ctx, pdkim_hashes[hashtype].exim_hashmethod);
1309 exim_sha_update(&hhash_ctx, hdata->s, hdata->ptr);
1310 exim_sha_finish(&hhash_ctx, &hhash);
1314 hhash.data = hdata->s;
1315 hhash.len = hdata->ptr;
1318 if ( (errstr = exim_dkim_signing_init(privkey, &sctx))
1319 || (errstr = exim_dkim_sign(&sctx, hm, &hhash, sig)))
1321 log_write(0, LOG_MAIN, "ARC: %s signing: %s\n", why, errstr);
1323 debug_printf("private key, or private-key file content, was: '%s'\n",
1333 arc_sign_append_sig(gstring * g, blob * sig)
1335 /*debug_printf("%s: raw sig %.*H\n", __FUNCTION__, sig->len, sig->data);*/
1336 sig->data = pdkim_encode_base64(sig);
1337 sig->len = Ustrlen(sig->data);
1340 int len = MIN(sig->len, 74);
1341 g = string_catn(g, sig->data, len);
1342 if ((sig->len -= len) == 0) break;
1344 g = string_catn(g, US"\r\n\t ", 5);
1346 g = string_catn(g, US";\r\n", 3);
1347 gstring_release_unused(g);
1348 string_from_gstring(g);
1353 /* Append a constructed AMS including CRLF. Add it to the arc_ctx too. */
1356 arc_sign_append_ams(gstring * g, arc_ctx * ctx, int instance,
1357 const uschar * identity, const uschar * selector, blob * bodyhash,
1358 hdr_rlist * rheaders, const uschar * privkey, unsigned options)
1361 gstring * hdata = NULL;
1363 int hashtype = pdkim_hashname_to_hashtype(US"sha256", 6); /*XXX hardwired */
1366 arc_line * al = store_get(sizeof(header_line) + sizeof(arc_line), GET_UNTAINTED);
1367 header_line * h = (header_line *)(al+1);
1369 /* debug_printf("%s\n", __FUNCTION__); */
1371 /* Construct the to-be-signed AMS pseudo-header: everything but the sig. */
1373 ams_off = gstring_length(g);
1374 g = string_fmt_append(g, "%s i=%d; a=rsa-sha256; c=relaxed; d=%s; s=%s",
1375 ARC_HDR_AMS, instance, identity, selector); /*XXX hardwired a= */
1376 if (options & ARC_SIGN_OPT_TSTAMP)
1377 g = string_fmt_append(g, "; t=%lu", (u_long)now);
1378 if (options & ARC_SIGN_OPT_EXPIRE)
1379 g = string_fmt_append(g, "; x=%lu", (u_long)expire);
1380 g = string_fmt_append(g, ";\r\n\tbh=%s;\r\n\th=",
1381 pdkim_encode_base64(bodyhash));
1383 for(col = 3; rheaders; rheaders = rheaders->prev)
1385 const uschar * hnames = US"DKIM-Signature:" PDKIM_DEFAULT_SIGN_HEADERS;
1386 uschar * name, * htext = rheaders->h->text;
1389 /* Spot headers of interest */
1391 while ((name = string_nextinlist(&hnames, &sep, NULL, 0)))
1393 int len = Ustrlen(name);
1394 if (strncasecmp(CCS htext, CCS name, len) == 0)
1396 /* If too long, fold line in h= field */
1398 if (col + len > 78) g = string_catn(g, US"\r\n\t ", 5), col = 3;
1400 /* Add name to h= list */
1402 g = string_catn(g, name, len);
1403 g = string_catn(g, US":", 1);
1406 /* Accumulate header for hashing/signing */
1408 hdata = string_cat(hdata,
1409 pdkim_relax_header_n(htext, rheaders->h->slen, TRUE)); /*XXX hardwired */
1415 /* Lose the last colon from the h= list */
1417 gstring_trim_trailing(g, ':');
1419 g = string_catn(g, US";\r\n\tb=;", 7);
1421 /* Include the pseudo-header in the accumulation */
1423 s = pdkim_relax_header_n(g->s + ams_off, g->ptr - ams_off, FALSE);
1424 hdata = string_cat(hdata, s);
1426 /* Calculate the signature from the accumulation */
1427 /*XXX does that need further relaxation? there are spaces embedded in the b= strings! */
1429 if (!arc_sig_from_pseudoheader(hdata, hashtype, privkey, &sig, US"AMS"))
1432 /* Lose the trailing semicolon from the psuedo-header, and append the signature
1433 (folded over lines) and termination to complete it. */
1436 g = arc_sign_append_sig(g, &sig);
1438 h->slen = g->ptr - ams_off;
1439 h->text = g->s + ams_off;
1441 ctx->arcset_chain_last->hdr_ams = al;
1443 DEBUG(D_transport) debug_printf("ARC: AMS '%.*s'\n", h->slen - 2, h->text);
1449 /* Look for an arc= result in an A-R header blob. We know that its data
1450 happens to be a NUL-term string. */
1453 arc_ar_cv_status(blob * ar)
1455 const uschar * resinfo = ar->data;
1457 uschar * methodspec, * s;
1459 while ((methodspec = string_nextinlist(&resinfo, &sep, NULL, 0)))
1460 if (Ustrncmp(methodspec, US"arc=", 4) == 0)
1463 for (s = methodspec += 4;
1464 (c = *s) && c != ';' && c != ' ' && c != '\r' && c != '\n'; ) s++;
1465 return string_copyn(methodspec, s - methodspec);
1472 /* Build the AS header and prepend it */
1475 arc_sign_prepend_as(gstring * arcset_interim, arc_ctx * ctx,
1476 int instance, const uschar * identity, const uschar * selector, blob * ar,
1477 const uschar * privkey, unsigned options)
1480 uschar * status = arc_ar_cv_status(ar);
1481 arc_line * al = store_get(sizeof(header_line) + sizeof(arc_line), GET_UNTAINTED);
1482 header_line * h = (header_line *)(al+1);
1483 uschar * badline_str;
1485 gstring * hdata = NULL;
1486 int hashtype = pdkim_hashname_to_hashtype(US"sha256", 6); /*XXX hardwired */
1492 - no h= tag; implicit coverage
1493 - arc status from A-R
1495 - coverage is just the new ARC set
1496 including self (but with an empty b= in self)
1498 - all ARC set headers, set-number order, aar then ams then as,
1499 including self (but with an empty b= in self)
1501 DEBUG(D_transport) debug_printf("ARC: building AS for status '%s'\n", status);
1503 /* Construct the AS except for the signature */
1505 arcset = string_append(NULL, 9,
1507 US" i=", string_sprintf("%d", instance),
1509 US"; a=rsa-sha256; d=", identity, /*XXX hardwired */
1510 US"; s=", selector); /*XXX same as AMS */
1511 if (options & ARC_SIGN_OPT_TSTAMP)
1512 arcset = string_append(arcset, 2,
1513 US"; t=", string_sprintf("%lu", (u_long)now));
1514 arcset = string_cat(arcset,
1517 h->slen = arcset->ptr;
1518 h->text = arcset->s;
1520 ctx->arcset_chain_last->hdr_as = al;
1522 /* For any but "fail" chain-verify status, walk the entire chain in order by
1523 instance. For fail, only the new arc-set. Accumulate the elements walked. */
1525 for (arc_set * as = Ustrcmp(status, US"fail") == 0
1526 ? ctx->arcset_chain_last : ctx->arcset_chain;
1530 /* Accumulate AAR then AMS then AS. Relaxed canonicalisation
1531 is required per standard. */
1533 badline_str = US"aar";
1534 if (!(l = as->hdr_aar)) goto badline;
1536 hdata = string_cat(hdata, pdkim_relax_header_n(h->text, h->slen, TRUE));
1537 badline_str = US"ams";
1538 if (!(l = as->hdr_ams)) goto badline;
1540 hdata = string_cat(hdata, pdkim_relax_header_n(h->text, h->slen, TRUE));
1541 badline_str = US"as";
1542 if (!(l = as->hdr_as)) goto badline;
1544 hdata = string_cat(hdata, pdkim_relax_header_n(h->text, h->slen, !!as->next));
1547 /* Calculate the signature from the accumulation */
1549 if (!arc_sig_from_pseudoheader(hdata, hashtype, privkey, &sig, US"AS"))
1552 /* Lose the trailing semicolon */
1554 arcset = arc_sign_append_sig(arcset, &sig);
1555 DEBUG(D_transport) debug_printf("ARC: AS '%.*s'\n", arcset->ptr - 2, arcset->s);
1557 /* Finally, append the AMS and AAR to the new AS */
1559 return string_catn(arcset, arcset_interim->s, arcset_interim->ptr);
1563 debug_printf("ARC: while building AS, missing %s in chain\n", badline_str);
1568 /**************************************/
1570 /* Return pointer to pdkim_bodyhash for given hash method, creating new
1575 arc_ams_setup_sign_bodyhash(void)
1577 int canon_head, canon_body;
1579 DEBUG(D_transport) debug_printf("ARC: requesting bodyhash\n");
1580 pdkim_cstring_to_canons(US"relaxed", 7, &canon_head, &canon_body); /*XXX hardwired */
1581 return pdkim_set_bodyhash(&dkim_sign_ctx,
1582 pdkim_hashname_to_hashtype(US"sha256", 6), /*XXX hardwired */
1592 memset(&arc_sign_ctx, 0, sizeof(arc_sign_ctx));
1593 headers_rlist = NULL;
1598 /* A "normal" header line, identified by DKIM processing. These arrive before
1599 the call to arc_sign(), which carries any newly-created DKIM headers - and
1600 those go textually before the normal ones in the message.
1602 We have to take the feed from DKIM as, in the transport-filter case, the
1603 headers are not in memory at the time of the call to arc_sign().
1605 Take a copy of the header and construct a reverse-order list.
1606 Also parse ARC-chain headers and build the chain struct, retaining pointers
1610 static const uschar *
1611 arc_header_sign_feed(gstring * g)
1613 uschar * s = string_copy_from_gstring(g);
1614 headers_rlist = arc_rlist_entry(headers_rlist, s, g->ptr);
1615 return arc_try_header(&arc_sign_ctx, headers_rlist->h, TRUE);
1620 /* Per RFCs 6376, 7489 the only allowed chars in either an ADMD id
1621 or a selector are ALPHA/DIGGIT/'-'/'.'
1623 Check, to help catch misconfigurations such as a missing selector
1624 element in the arc_sign list.
1628 arc_valid_id(const uschar * s)
1630 for (uschar c; c = *s++; )
1631 if (!isalnum(c) && c != '-' && c != '.') return FALSE;
1637 /* ARC signing. Called from the smtp transport, if the arc_sign option is set.
1638 The dkim_exim_sign() function has already been called, so will have hashed the
1639 message body for us so long as we requested a hash previously.
1642 signspec Three-element colon-sep list: identity, selector, privkey.
1643 Optional fourth element: comma-sep list of options.
1645 sigheaders Any signature headers already generated, eg. by DKIM, or NULL
1649 Set of headers to prepend to the message, including the supplied sigheaders
1650 but not the plainheaders.
1654 arc_sign(const uschar * signspec, gstring * sigheaders, uschar ** errstr)
1656 const uschar * identity, * selector, * privkey, * opts, * s;
1657 unsigned options = 0;
1659 header_line * headers;
1660 hdr_rlist * rheaders;
1668 /* Parse the signing specification */
1670 if (!(identity = string_nextinlist(&signspec, &sep, NULL, 0)) || !*identity)
1671 { s = US"identity"; goto bad_arg_ret; }
1672 if (!(selector = string_nextinlist(&signspec, &sep, NULL, 0)) || !*selector)
1673 { s = US"selector"; goto bad_arg_ret; }
1674 if (!(privkey = string_nextinlist(&signspec, &sep, NULL, 0)) || !*privkey)
1675 { s = US"privkey"; goto bad_arg_ret; }
1676 if (!arc_valid_id(identity))
1677 { s = US"identity"; goto bad_arg_ret; }
1678 if (!arc_valid_id(selector))
1679 { s = US"selector"; goto bad_arg_ret; }
1680 if (*privkey == '/' && !(privkey = expand_file_big_buffer(privkey)))
1681 goto ret_sigheaders;
1683 if ((opts = string_nextinlist(&signspec, &sep, NULL, 0)))
1686 while ((s = string_nextinlist(&opts, &osep, NULL, 0)))
1687 if (Ustrcmp(s, "timestamps") == 0)
1689 options |= ARC_SIGN_OPT_TSTAMP;
1690 if (!now) now = time(NULL);
1692 else if (Ustrncmp(s, "expire", 6) == 0)
1694 options |= ARC_SIGN_OPT_EXPIRE;
1695 if (*(s += 6) == '=')
1698 if (!(expire = (time_t)atoi(CS ++s)))
1699 expire = ARC_SIGN_DEFAULT_EXPIRE_DELTA;
1700 if (!now) now = time(NULL);
1704 expire = (time_t)atol(CS s);
1707 if (!now) now = time(NULL);
1708 expire = now + ARC_SIGN_DEFAULT_EXPIRE_DELTA;
1713 DEBUG(D_transport) debug_printf("ARC: sign for %s\n", identity);
1715 /* Make an rlist of any new DKIM headers, then add the "normals" rlist to it.
1716 Then scan the list for an A-R header. */
1718 string_from_gstring(sigheaders);
1719 if ((rheaders = arc_sign_scan_headers(&arc_sign_ctx, sigheaders)))
1722 for (rp = &headers_rlist; *rp; ) rp = &(*rp)->prev;
1726 /* Finally, build a normal-order headers list */
1727 /*XXX only needed for hunt-the-AR? */
1728 /*XXX also, we really should be accepting any number of ADMD-matching ARs */
1730 header_line * hnext = NULL;
1731 for (rheaders = headers_rlist; rheaders;
1732 hnext = rheaders->h, rheaders = rheaders->prev)
1733 rheaders->h->next = hnext;
1737 if (!(arc_sign_find_ar(headers, identity, &ar)))
1739 log_write(0, LOG_MAIN, "ARC: no Authentication-Results header for signing");
1740 goto ret_sigheaders;
1743 /* We previously built the data-struct for the existing ARC chain, if any, using a headers
1744 feed from the DKIM module. Use that to give the instance number for the ARC set we are
1748 if (arc_sign_ctx.arcset_chain_last)
1749 debug_printf("ARC: existing chain highest instance: %d\n",
1750 arc_sign_ctx.arcset_chain_last->instance);
1752 debug_printf("ARC: no existing chain\n");
1754 instance = arc_sign_ctx.arcset_chain_last ? arc_sign_ctx.arcset_chain_last->instance + 1 : 1;
1758 - copy the A-R; prepend i= & identity
1761 g = arc_sign_append_aar(g, &arc_sign_ctx, identity, instance, &ar);
1765 - Looks fairly like a DKIM sig
1766 - Cover all DKIM sig headers as well as the usuals
1769 - we must have requested a suitable bodyhash previously
1772 b = arc_ams_setup_sign_bodyhash();
1773 g = arc_sign_append_ams(g, &arc_sign_ctx, instance, identity, selector,
1774 &b->bh, headers_rlist, privkey, options);
1779 - no h= tag; implicit coverage
1780 - arc status from A-R
1782 - coverage is just the new ARC set
1783 including self (but with an empty b= in self)
1785 - all ARC set headers, set-number order, aar then ams then as,
1786 including self (but with an empty b= in self)
1790 g = arc_sign_prepend_as(g, &arc_sign_ctx, instance, identity, selector, &ar,
1793 /* Finally, append the dkim headers and return the lot. */
1795 if (sigheaders) g = string_catn(g, sigheaders->s, sigheaders->ptr);
1798 if (!g) return string_get(1);
1799 (void) string_from_gstring(g);
1800 gstring_release_unused(g);
1805 log_write(0, LOG_MAIN, "ARC: bad signing-specification (%s)", s);
1812 /******************************************************************************/
1814 /* Check to see if the line is an AMS and if so, set up to validate it.
1815 Called from the DKIM input processing. This must be done now as the message
1816 body data is hashed during input.
1818 We call the DKIM code to request a body-hash; it has the facility already
1819 and the hash parameters might be common with other requests.
1822 static const uschar *
1823 arc_header_vfy_feed(gstring * g)
1830 if (!dkim_verify_ctx) return US"no dkim context";
1832 if (strncmpic(ARC_HDR_AMS, g->s, ARC_HDRLEN_AMS) != 0) return US"not AMS";
1834 DEBUG(D_receive) debug_printf("ARC: spotted AMS header\n");
1835 /* Parse the AMS header */
1837 memset(&al, 0, sizeof(arc_line));
1839 h.slen = len_string_from_gstring(g, &h.text);
1840 if ((errstr = arc_parse_line(&al, &h, ARC_HDRLEN_AMS, le_all)))
1842 DEBUG(D_acl) if (errstr) debug_printf("ARC: %s\n", errstr);
1846 if (!al.a_hash.data)
1848 DEBUG(D_acl) debug_printf("ARC: no a_hash from '%.*s'\n", h.slen, h.text);
1855 al.c_body.data = US"simple"; al.c_body.len = 6;
1856 al.c_head = al.c_body;
1859 /* Ask the dkim code to calc a bodyhash with those specs */
1861 if (!(b = arc_ams_setup_vfy_bodyhash(&al)))
1862 return US"dkim hash setup fail";
1864 /* Discard the reference; search again at verify time, knowing that one
1865 should have been created here. */
1870 return US"line parsing error";
1875 /* A header line has been identified by DKIM processing.
1879 is_vfy TRUE for verify mode or FALSE for signing mode
1882 NULL for success, or an error string (probably unused)
1886 arc_header_feed(gstring * g, BOOL is_vfy)
1888 return is_vfy ? arc_header_vfy_feed(g) : arc_header_sign_feed(g);
1893 /******************************************************************************/
1895 /* Construct the list of domains from the ARC chain after validation */
1898 fn_arc_domains(void)
1904 for (as = arc_verify_ctx.arcset_chain, inst = 1; as; as = as->next, inst++)
1906 arc_line * hdr_as = as->hdr_as;
1909 blob * d = &hdr_as->d;
1911 for (; inst < as->instance; inst++)
1912 g = string_catn(g, US":", 1);
1914 g = d->data && d->len
1915 ? string_append_listele_n(g, ':', d->data, d->len)
1916 : string_catn(g, US":", 1);
1919 g = string_catn(g, US":", 1);
1921 if (!g) return US"";
1922 return string_from_gstring(g);
1926 /* Construct an Authentication-Results header portion, for the ARC module */
1929 authres_arc(gstring * g)
1933 arc_line * highest_ams;
1934 int start = 0; /* Compiler quietening */
1935 DEBUG(D_acl) start = gstring_length(g);
1937 g = string_append(g, 2, US";\n\tarc=", arc_state);
1938 if (arc_received_instance > 0)
1940 g = string_fmt_append(g, " (i=%d)", arc_received_instance);
1941 if (arc_state_reason)
1942 g = string_append(g, 3, US"(", arc_state_reason, US")");
1943 g = string_catn(g, US" header.s=", 10);
1944 highest_ams = arc_received->hdr_ams;
1945 g = string_catn(g, highest_ams->s.data, highest_ams->s.len);
1947 g = string_fmt_append(g, " arc.oldest-pass=%d", arc_oldest_pass);
1949 if (sender_host_address)
1950 g = string_append(g, 2, US" smtp.remote-ip=", sender_host_address);
1952 else if (arc_state_reason)
1953 g = string_append(g, 3, US" (", arc_state_reason, US")");
1954 DEBUG(D_acl) debug_printf("ARC:\tauthres '%.*s'\n",
1955 gstring_length(g) - start - 3, g->s + start + 3);
1958 DEBUG(D_acl) debug_printf("ARC:\tno authres\n");
1963 # ifdef SUPPORT_DMARC
1964 /* Append a DMARC history record pair for ARC, to the given history set */
1967 arc_dmarc_hist_append(gstring * g)
1972 int i = Ustrcmp(arc_state, "pass") == 0 ? ARES_RESULT_PASS
1973 : Ustrcmp(arc_state, "fail") == 0 ? ARES_RESULT_FAIL
1974 : ARES_RESULT_UNKNOWN;
1975 g = string_fmt_append(g, "arc %d\n", i);
1976 g = string_fmt_append(g, "arc_policy %d json[",
1977 i == ARES_RESULT_PASS ? DMARC_ARC_POLICY_RESULT_PASS
1978 : i == ARES_RESULT_FAIL ? DMARC_ARC_POLICY_RESULT_FAIL
1979 : DMARC_ARC_POLICY_RESULT_UNUSED);
1980 /*XXX would we prefer this backwards? */
1981 for (arc_set * as = arc_verify_ctx.arcset_chain; as;
1982 as = as->next, first = FALSE)
1984 arc_line * line = as->hdr_as;
1987 blob * d = &line->d;
1988 blob * s = &line->s;
1991 g = string_catn(g, US",", 1);
1993 g = string_fmt_append(g, " (\"i\":%u," /*)*/
1997 d->data ? (int)d->len : 0, d->data && d->len ? d->data : US"",
1998 s->data ? (int)s->len : 0, s->data && s->len ? s->data : US""
2000 if ((line = as->hdr_aar))
2002 blob * ip = &line->ip;
2003 if (ip->data && ip->len)
2004 g = string_fmt_append(g, ", \"ip\":\"%.*s\"", (int)ip->len, ip->data);
2007 g = string_catn(g, US")", 1);
2010 g = string_catn(g, US" ]\n", 3);
2013 g = string_fmt_append(g, "arc %d\narc_policy %d json:[]\n",
2014 ARES_RESULT_UNKNOWN, DMARC_ARC_POLICY_RESULT_UNUSED);
2020 # endif /* DISABLE_DKIM */
2021 #endif /* EXPERIMENTAL_ARC */