X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/4398228719841b08524520b8c07faeb3bee89705..56ac062a3ff94fc4e1bbfc2293119c079a4e980b:/src/src/arc.c diff --git a/src/src/arc.c b/src/src/arc.c index c860aed36..6c4bcc641 100644 --- a/src/src/arc.c +++ b/src/src/arc.c @@ -93,6 +93,7 @@ static time_t now; static time_t expire; static hdr_rlist * headers_rlist; static arc_ctx arc_sign_ctx = { NULL }; +static arc_ctx arc_verify_ctx = { NULL }; /******************************************************************************/ @@ -382,7 +383,7 @@ static uschar * arc_insert_hdr(arc_ctx * ctx, header_line * h, unsigned off, unsigned hoff, BOOL instance_only) { -int i; +unsigned i; arc_set * as; arc_line * al = store_get(sizeof(arc_line)), ** alp; uschar * e; @@ -395,6 +396,7 @@ if ((e = arc_parse_line(al, h, off, instance_only))) return US"line parse"; } if (!(i = arc_instance_from_hdr(al))) return US"instance find"; +if (i > 50) return US"overlarge instance number"; if (!(as = arc_find_set(ctx, i))) return US"set find"; if (*(alp = (arc_line **)(US as + hoff))) return US"dup hdr"; @@ -767,24 +769,26 @@ arc_headers_check(arc_ctx * ctx) arc_set * as; int inst; BOOL ams_fail_found = FALSE; -uschar * ret = NULL; -if (!(as = ctx->arcset_chain)) +if (!(as = ctx->arcset_chain_last)) return US"none"; -for(inst = 0; as; as = as->next) +for(inst = as->instance; as; as = as->prev, inst--) { - if ( as->instance != ++inst - || !as->hdr_aar || !as->hdr_ams || !as->hdr_as - || arc_cv_match(as->hdr_as, US"fail") - ) - { - arc_state_reason = string_sprintf("i=%d" - " (cv, sequence or missing header)", as->instance); - DEBUG(D_acl) debug_printf("ARC chain fail at %s\n", arc_state_reason); - return US"fail"; - } + if (as->instance != inst) + arc_state_reason = string_sprintf("i=%d (sequence; expected %d)", + as->instance, inst); + else if (!as->hdr_aar || !as->hdr_ams || !as->hdr_as) + arc_state_reason = string_sprintf("i=%d (missing header)", as->instance); + else if (arc_cv_match(as->hdr_as, US"fail")) + arc_state_reason = string_sprintf("i=%d (cv)", as->instance); + else + goto good; + + DEBUG(D_acl) debug_printf("ARC chain fail at %s\n", arc_state_reason); + return US"fail"; + good: /* Evaluate the oldest-pass AMS validation while we're here. It does not affect the AS chain validation but is reported as auxilary info. */ @@ -796,23 +800,29 @@ for(inst = 0; as; as = as->next) arc_oldest_pass = inst; arc_state_reason = NULL; } +if (inst != 0) + { + arc_state_reason = string_sprintf("(sequence; expected i=%d)", inst); + DEBUG(D_acl) debug_printf("ARC chain fail %s\n", arc_state_reason); + return US"fail"; + } arc_received = ctx->arcset_chain_last; -arc_received_instance = inst; -if (ret) - return ret; +arc_received_instance = arc_received->instance; /* 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) +if (!as->ams_verify_passed) { - arc_state_reason = as->ams_verify_done; - return US"fail"; + if (as->ams_verify_done) + { + arc_state_reason = as->ams_verify_done; + return US"fail"; + } + if (!!arc_ams_verify(ctx, as)) + return US"fail"; } -if (!!arc_ams_verify(ctx, as)) - return US"fail"; - return NULL; } @@ -974,16 +984,13 @@ return NULL; static const uschar * arc_verify_seals(arc_ctx * ctx) { -arc_set * as = ctx->arcset_chain; +arc_set * as = ctx->arcset_chain_last; if (!as) return US"none"; -while (as) - { - if (arc_seal_verify(ctx, as)) return US"fail"; - as = as->next; - } +for ( ; as; as = as->prev) if (arc_seal_verify(ctx, as)) return US"fail"; + DEBUG(D_acl) debug_printf("ARC: AS vfy overall pass\n"); return NULL; } @@ -998,9 +1005,10 @@ Return: The ARC state, or NULL on error. const uschar * acl_verify_arc(void) { -arc_ctx ctx = { NULL }; const uschar * res; +memset(&arc_verify_ctx, 0, sizeof(arc_verify_ctx)); + if (!dkim_verify_ctx) { DEBUG(D_acl) debug_printf("ARC: no DKIM verify context\n"); @@ -1014,7 +1022,7 @@ https://tools.ietf.org/html/draft-ietf-dmarc-arc-protocol-10#section-6 none, the ARC state is "none" and the algorithm stops here. */ -if ((res = arc_vfy_collect_hdrs(&ctx))) +if ((res = arc_vfy_collect_hdrs(&arc_verify_ctx))) goto out; /* 2. If the form of any ARC set is invalid (e.g., does not contain @@ -1032,7 +1040,7 @@ if ((res = arc_vfy_collect_hdrs(&ctx))) then the chain state is "fail" and the algorithm stops here. */ -if ((res = arc_headers_check(&ctx))) +if ((res = arc_headers_check(&arc_verify_ctx))) goto out; /* 4. For each ARC-Seal from the "N"th instance to the first, apply the @@ -1074,7 +1082,7 @@ if ((res = arc_headers_check(&ctx))) the algorithm is complete. */ -if ((res = arc_verify_seals(&ctx))) +if ((res = arc_verify_seals(&arc_verify_ctx))) goto out; res = US"pass"; @@ -1186,7 +1194,7 @@ arc_line * al = (arc_line *)(as+1); header_line * h = (header_line *)(al+1); g = string_catn(g, ARC_HDR_AAR, ARC_HDRLEN_AAR); -g = string_cat(g, string_sprintf(" i=%d; %s;\r\n\t", instance, identity)); +g = string_fmt_append(g, " i=%d; %s;\r\n\t", instance, identity); g = string_catn(g, US ar->data, ar->len); h->slen = g->ptr - aar_off; @@ -1247,6 +1255,9 @@ if ( (errstr = exim_dkim_signing_init(privkey, &sctx)) || (errstr = exim_dkim_sign(&sctx, hm, &hhash, sig))) { log_write(0, LOG_MAIN, "ARC: %s signing: %s\n", why, errstr); + DEBUG(D_transport) + debug_printf("private key, or private-key file content, was: '%s'\n", + privkey); return FALSE; } return TRUE; @@ -1296,20 +1307,14 @@ header_line * h = (header_line *)(al+1); /* Construct the to-be-signed AMS pseudo-header: everything but the sig. */ ams_off = g->ptr; -g = string_append(g, 7, - ARC_HDR_AMS, - US" i=", string_sprintf("%d", instance), - US"; a=rsa-sha256; c=relaxed; d=", identity, /*XXX hardwired */ - US"; s=", selector); +g = string_fmt_append(g, "%s i=%d; a=rsa-sha256; c=relaxed; d=%s; s=%s", + ARC_HDR_AMS, instance, identity, selector); /*XXX hardwired a= */ if (options & ARC_SIGN_OPT_TSTAMP) - g = string_append(g, 2, - US"; t=", string_sprintf("%lu", (u_long)now)); + g = string_fmt_append(g, "; t=%lu", (u_long)now); if (options & ARC_SIGN_OPT_EXPIRE) - g = string_append(g, 2, - US"; x=", string_sprintf("%lu", (u_long)expire)); -g = string_append(g, 3, - US";\r\n\tbh=", pdkim_encode_base64(bodyhash), - US";\r\n\th="); + g = string_fmt_append(g, "; x=%lu", (u_long)expire); +g = string_fmt_append(g, ";\r\n\tbh=%s;\r\n\th=", + pdkim_encode_base64(bodyhash)); for(col = 3; rheaders; rheaders = rheaders->prev) { @@ -1569,7 +1574,7 @@ expire = now = 0; identity = string_nextinlist(&signspec, &sep, NULL, 0); selector = string_nextinlist(&signspec, &sep, NULL, 0); -if ( !*identity | !*selector +if ( !*identity || !*selector || !(privkey = string_nextinlist(&signspec, &sep, NULL, 0)) || !*privkey) { log_write(0, LOG_MAIN, "ARC: bad signing-specification (%s)", @@ -1594,13 +1599,13 @@ if ((opts = string_nextinlist(&signspec, &sep, NULL, 0))) if (*(s += 6) == '=') if (*++s == '+') { - if (!(expire = (time_t)atoi(++s))) + if (!(expire = (time_t)atoi(CS ++s))) expire = ARC_SIGN_DEFAULT_EXPIRE_DELTA; if (!now) now = time(NULL); expire += now; } else - expire = (time_t)atol(s); + expire = (time_t)atol(CS s); else { if (!now) now = time(NULL); @@ -1618,18 +1623,17 @@ string_from_gstring(sigheaders); if ((rheaders = arc_sign_scan_headers(&arc_sign_ctx, sigheaders))) { hdr_rlist ** rp; - for (rp = &rheaders; *rp; ) rp = &(*rp)->prev; - *rp = headers_rlist; - headers_rlist = rheaders; + for (rp = &headers_rlist; *rp; ) rp = &(*rp)->prev; + *rp = rheaders; } -else - rheaders = headers_rlist; /* Finally, build a normal-order headers list */ /*XXX only needed for hunt-the-AR? */ +/*XXX also, we really should be accepting any number of ADMD-matching ARs */ { header_line * hnext = NULL; - for (; rheaders; hnext = rheaders->h, rheaders = rheaders->prev) + for (rheaders = headers_rlist; rheaders; + hnext = rheaders->h, rheaders = rheaders->prev) rheaders->h->next = hnext; headers = hnext; } @@ -1686,12 +1690,13 @@ g = arc_sign_append_ams(g, &arc_sign_ctx, instance, identity, selector, including self (but with an empty b= in self) */ -g = arc_sign_prepend_as(g, &arc_sign_ctx, instance, identity, selector, &ar, +if (g) + g = arc_sign_prepend_as(g, &arc_sign_ctx, instance, identity, selector, &ar, privkey, options); /* Finally, append the dkim headers and return the lot. */ -g = string_catn(g, sigheaders->s, sigheaders->ptr); +if (sigheaders) g = string_catn(g, sigheaders->s, sigheaders->ptr); (void) string_from_gstring(g); gstring_reset_unused(g); return g; @@ -1773,7 +1778,37 @@ return is_vfy ? arc_header_vfy_feed(g) : arc_header_sign_feed(g); /******************************************************************************/ -/* Construct an Authenticate-Results header portion, for the ARC module */ +/* Construct the list of domains from the ARC chain after validation */ + +uschar * +fn_arc_domains(void) +{ +arc_set * as; +unsigned inst; +gstring * g = NULL; + +for (as = arc_verify_ctx.arcset_chain, inst = 1; as; as = as->next, inst++) + { + arc_line * hdr_as = as->hdr_as; + if (hdr_as) + { + blob * d = &hdr_as->d; + + for (; inst < as->instance; inst++) + g = string_catn(g, US":", 1); + + g = d->data && d->len + ? string_append_listele_n(g, ':', d->data, d->len) + : string_catn(g, US":", 1); + } + else + g = string_catn(g, US":", 1); + } +return g ? g->s : US""; +} + + +/* Construct an Authentication-Results header portion, for the ARC module */ gstring * authres_arc(gstring * g) @@ -1787,19 +1822,17 @@ if (arc_state) 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")"); + g = string_fmt_append(g, " (i=%d)", arc_received_instance); 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); - g = string_append(g, 2, - US" arc.oldest-pass=", string_sprintf("%d", arc_oldest_pass)); + g = string_fmt_append(g, " arc.oldest-pass=%d", arc_oldest_pass); if (sender_host_address) - g = string_append(g, 2, US" smtp.client-ip=", sender_host_address); + g = string_append(g, 2, US" smtp.remote-ip=", sender_host_address); } else if (arc_state_reason) g = string_append(g, 3, US" (", arc_state_reason, US")");