arc_line * hdr_ams;
arc_line * hdr_as;
- BOOL ams_verify_done;
+ const uschar * ams_verify_done;
BOOL ams_verify_passed;
} arc_set;
static unsigned
arc_instance_from_hdr(const arc_line * al)
{
-uschar * s = al->i.data;
+const uschar * s = al->i.data;
if (!s || !al->i.len) return 0;
-return (unsigned) atoi(s);
+return (unsigned) atoi(CCS s);
}
arc_parse_line(arc_line * al, header_line * h, unsigned off, BOOL instance_only)
{
uschar * s = h->text + off;
-uschar * r;
+uschar * r = NULL; /* compiler-quietening */
uschar c;
al->complete = h;
arc_vfy_collect_hdrs(arc_ctx * ctx)
{
header_line * h;
-hdr_rlist * r, * rprev = NULL;
+hdr_rlist * r = NULL, * rprev = NULL;
const uschar * e;
DEBUG(D_acl) debug_printf("ARC: collecting arc sets\n");
rprev = r;
if ((e = arc_try_header(ctx, h, FALSE)))
- return e;
+ {
+ arc_state_reason = string_sprintf("collecting headers: %s", e);
+ return US"fail";
+ }
}
headers_rlist = r;
break;
}
-/* Finally add in the signature header (with the b= tag stripped) */
+/* Finally add in the signature header (with the b= tag stripped); no CRLF */
s = ams->rawsig_no_b_val.data, len = ams->rawsig_no_b_val.len;
if (relaxed)
- len = Ustrlen(s = pdkim_relax_header_n(s, len, TRUE));
+ len = Ustrlen(s = pdkim_relax_header_n(s, len, FALSE));
DEBUG(D_acl) pdkim_quoteprint(s, len);
exim_sha_update(&hhash_ctx, s, len);
if (!ele)
{
DEBUG(D_acl) debug_printf("pubkey h=%s vs sig a=%.*s\n",
- p->hashes, al->a.len, al->a.data);
+ p->hashes, (int)al->a.len, al->a.data);
return NULL;
}
}
and without a DKIM v= tag.
*/
-static uschar *
+static const uschar *
arc_ams_verify(arc_ctx * ctx, arc_set * as)
{
arc_line * ams = as->hdr_ams;
int hashtype;
const uschar * errstr;
-as->ams_verify_done = TRUE;
+as->ams_verify_done = US"in-progress";
/* Check the AMS has all the required tags:
"a=" algorithm
*/
if ( !ams->a.data || !ams->b.data || !ams->bh.data || !ams->d.data
|| !ams->h.data || !ams->s.data)
+ {
+ as->ams_verify_done = arc_state_reason = US"required tag missing";
return US"fail";
+ }
/* The bodyhash should have been created earlier, and the dkim code should
have managed calculating it during message input. Find the reference to it. */
if (!(b = arc_ams_setup_vfy_bodyhash(ams)))
+ {
+ as->ams_verify_done = arc_state_reason = US"internal hash setup error";
return US"fail";
+ }
DEBUG(D_acl)
{
debug_printf("ARC i=%d AMS Body bytes hashed: %lu\n"
" Body %.*s computed: ",
as->instance, b->signed_body_bytes,
- ams->a_hash.len, ams->a_hash.data);
+ (int)ams->a_hash.len, ams->a_hash.data);
pdkim_hexprint(CUS b->bh.data, b->bh.len);
}
pdkim_hexprint(sighash.data, sighash.len);
debug_printf("ARC i=%d AMS Body hash did NOT match\n", as->instance);
}
- return US"body hash compare mismatch";
+ return as->ams_verify_done = arc_state_reason = US"AMS body hash miscompare";
}
DEBUG(D_acl) debug_printf("ARC i=%d AMS Body hash compared OK\n", as->instance);
/* Get the public key from DNS */
if (!(p = arc_line_to_pubkey(ams)))
- return US"pubkey problem";
+ return as->ams_verify_done = arc_state_reason = US"pubkey problem";
/* We know the b-tag blob is of a nul-term string, so safe as a string */
pdkim_decode_base64(ams->b.data, &sighash);
if ((errstr = exim_dkim_verify_init(&p->key, KEYFMT_DER, &vctx)))
{
DEBUG(D_acl) debug_printf("ARC verify init: %s\n", errstr);
+ as->ams_verify_done = arc_state_reason = US"internal sigverify init error";
return US"fail";
}
pdkim_hashes[hashtype].exim_hashmethod, &hhash, &sighash)))
{
DEBUG(D_acl) debug_printf("ARC i=%d AMS verify %s\n", as->instance, errstr);
- return US"ams sig verify fail";
+ return as->ams_verify_done = arc_state_reason = US"AMS sig nonverify";
}
DEBUG(D_acl) debug_printf("ARC i=%d AMS verify pass\n", as->instance);
|| arc_cv_match(as->hdr_as, US"fail")
)
{
- DEBUG(D_acl) debug_printf("ARC i=%d fail"
- " (cv, sequence or missing header)\n", as->instance);
+ arc_state_reason = string_sprintf("i=%d fail"
+ " (cv, sequence or missing header)", as->instance);
+ DEBUG(D_acl) debug_printf("ARC %s\n", arc_state_reason);
ret = US"fail";
}
ams_fail_found = TRUE;
else
arc_oldest_pass = inst;
+ arc_state_reason = NULL;
}
arc_received = ctx->arcset_chain_last;
/* We can skip the latest-AMS validation, if we already did it. */
as = ctx->arcset_chain_last;
-if (as->ams_verify_done ? !as->ams_verify_passed : !!arc_ams_verify(ctx, as))
+if (as->ams_verify_done && !as->ams_verify_passed)
+ {
+ arc_state_reason = as->ams_verify_done;
+ return US"fail";
+ }
+if (!!arc_ams_verify(ctx, as))
return US"fail";
return NULL;
if ( as->instance == 1 && !arc_cv_match(hdr_as, US"none")
|| arc_cv_match(hdr_as, US"none") && as->instance != 1
)
+ {
+ arc_state_reason = US"seal cv state";
return US"fail";
+ }
/*
3. Initialize a hash function corresponding to the "a" tag of
{
DEBUG(D_acl)
debug_printf("ARC: hash setup error, possibly nonhandled hashtype\n");
+ arc_state_reason = US"seal hash setup error";
return US"fail";
}
header canonicalization defined in Section 3.4.2 of
[RFC6376]. Pass the canonicalized result to the hash
function.
+
+Headers are CRLF-separated, but the last one is not crlf-terminated.
*/
DEBUG(D_acl) debug_printf("ARC: AS header data for verification:\n");
al = as2->hdr_as;
if (as2->instance == as->instance)
s = pdkim_relax_header_n(al->rawsig_no_b_val.data,
- al->rawsig_no_b_val.len, TRUE);
+ al->rawsig_no_b_val.len, FALSE);
else if (!(s = al->relaxed))
al->relaxed = s = pdkim_relax_header_n(al->complete->text,
al->complete->slen, TRUE);
DEBUG(D_acl)
{
debug_printf("ARC i=%d AS Header %.*s computed: ",
- as->instance, hdr_as->a_hash.len, hdr_as->a_hash.data);
+ as->instance, (int)hdr_as->a_hash.len, hdr_as->a_hash.data);
pdkim_hexprint(hhash_computed.data, hhash_computed.len);
}
{
DEBUG(D_acl)
debug_printf("ARC i=%d AS headers verify: %s\n", as->instance, errstr);
+ arc_state_reason = US"seal sigverify error";
return US"fail";
}
s = sigheaders ? sigheaders->s : NULL;
if (s) while (*s)
{
- uschar * s2;
+ const uschar * s2 = s;
/* This works for either NL or CRLF lines; also nul-termination */
while (*++s2)
g = string_catn(g, US";\r\n\tb=;", 7);
/* Include the pseudo-header in the accumulation */
-/*XXX should that be prepended rather than appended? */
-/*XXX also need to include at the verify stage */
-s = pdkim_relax_header_n(g->s + ams_off, g->ptr - ams_off, TRUE);
+s = pdkim_relax_header_n(g->s + ams_off, g->ptr - ams_off, FALSE);
hdata = string_cat(hdata, s);
/* Calculate the signature from the accumulation */
uschar * status = arc_ar_cv_status(ar);
arc_line * al = store_get(sizeof(header_line) + sizeof(arc_line));
header_line * h = (header_line *)(al+1);
-uschar * s;
gstring * hdata = NULL;
int hashtype = pdkim_hashname_to_hashtype(US"sha256", 6); /*XXX hardwired */
ARC_HDR_AS,
US" i=", string_sprintf("%d", instance),
US"; cv=", status,
- US"; a=rsa-sha256; c=relaxed; d=", identity, /*XXX hardwired */
+ US"; a=rsa-sha256; d=", identity, /*XXX hardwired */
US"; s=", selector, /*XXX same as AMS */
US";\r\n\t b=;");
h = as->hdr_ams->complete;
hdata = string_cat(hdata, pdkim_relax_header_n(h->text, h->slen, TRUE));
h = as->hdr_as->complete;
- hdata = string_cat(hdata, pdkim_relax_header_n(h->text, h->slen, TRUE));
+ hdata = string_cat(hdata, pdkim_relax_header_n(h->text, h->slen, !!as->next));
}
/* Calculate the signature from the accumulation */
if (arc_state)
{
arc_line * highest_ams;
- int start;
+ int start = 0; /* Compiler quietening */
DEBUG(D_acl) start = g->ptr;
g = string_append(g, 2, US";\n\tarc=", arc_state);
if (arc_received_instance > 0)
{
g = string_append(g, 3, US" (i=",
- string_sprintf("%d", arc_received_instance), US") header.s=");
+ string_sprintf("%d", arc_received_instance), US")");
+ if (arc_state_reason)
+ g = string_append(g, 3, US"(", arc_state_reason, US")");
+ g = string_catn(g, US" header.s=", 10);
highest_ams = arc_received->hdr_ams;
g = string_catn(g, highest_ams->s.data, highest_ams->s.len);