ARC: fix crash on signing with missing key file
[exim.git] / src / src / arc.c
index 53d06de67cb6c62ef58bd77d26bd12e251699dba..58811274ef8b9efac8b1dbc720d2a12c99d8e536 100644 (file)
@@ -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 };
 
 
 /******************************************************************************/
@@ -769,22 +770,25 @@ arc_set * as;
 int inst;
 BOOL ams_fail_found = FALSE;
 
-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,9 +800,15 @@ 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;
+arc_received_instance = arc_received->instance;
 
 /* We can skip the latest-AMS validation, if we already did it. */
 
@@ -998,9 +1008,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 +1025,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 +1043,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 +1085,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";
@@ -1685,7 +1696,8 @@ 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. */
@@ -1772,7 +1784,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)