ARC: fix signing when DKIM-signing is also being done
[exim.git] / src / src / arc.c
index a0e2184bddc513bec500b644ce11ba51b6f2a49c..6f567dc5faf92bad38cd56d2ba48aabfc498fa75 100644 (file)
@@ -631,6 +631,7 @@ arc_ams_setup_vfy_bodyhash(arc_line * ams)
 int canon_head, canon_body;
 long bodylen;
 
+if (!ams->c.data) ams->c.data = US"simple";    /* RFC 6376 (DKIM) default */
 pdkim_cstring_to_canons(ams->c.data, ams->c.len, &canon_head, &canon_body);
 bodylen = ams->l.data
        ? strtol(CS string_copyn(ams->l.data, ams->l.len), NULL, 10) : -1;
@@ -759,7 +760,6 @@ arc_headers_check(arc_ctx * ctx)
 arc_set * as;
 int inst;
 BOOL ams_fail_found = FALSE;
-uschar * ret = NULL;
 
 if (!(as = ctx->arcset_chain))
   return US"none";
@@ -771,10 +771,10 @@ for(inst = 0; as; as = as->next)
      || arc_cv_match(as->hdr_as, US"fail")
      )
     {
-    arc_state_reason = string_sprintf("i=%d fail"
+    arc_state_reason = string_sprintf("i=%d"
       " (cv, sequence or missing header)", as->instance);
-    DEBUG(D_acl) debug_printf("ARC %s\n", arc_state_reason);
-    ret = US"fail";
+    DEBUG(D_acl) debug_printf("ARC chain fail at %s\n", arc_state_reason);
+    return US"fail";
     }
 
   /* Evaluate the oldest-pass AMS validation while we're here.
@@ -791,20 +791,20 @@ for(inst = 0; as; as = as->next)
 
 arc_received = ctx->arcset_chain_last;
 arc_received_instance = inst;
-if (ret)
-  return ret;
 
 /* 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;
 }
 
@@ -993,6 +993,12 @@ acl_verify_arc(void)
 arc_ctx ctx = { NULL };
 const uschar * res;
 
+if (!dkim_verify_ctx)
+  {
+  DEBUG(D_acl) debug_printf("ARC: no DKIM verify context\n");
+  return NULL;
+  }
+
 /* AS evaluation, per
 https://tools.ietf.org/html/draft-ietf-dmarc-arc-protocol-10#section-6
 */
@@ -1096,8 +1102,7 @@ return r;
 
 
 /* Walk the given headers strings identifying each header, and construct
-a reverse-order list.  Also parse ARC-chain headers and build the chain
-struct, retaining pointers into the string.
+a reverse-order list.
 */
 
 static hdr_rlist *
@@ -1233,7 +1238,7 @@ else
 if (  (errstr = exim_dkim_signing_init(privkey, &sctx))
    || (errstr = exim_dkim_sign(&sctx, hm, &hhash, sig)))
   {
-  log_write(0, LOG_MAIN|LOG_PANIC, "ARC: %s signing: %s\n", why, errstr);
+  log_write(0, LOG_MAIN, "ARC: %s signing: %s\n", why, errstr);
   return FALSE;
   }
 return TRUE;
@@ -1375,7 +1380,7 @@ while ((methodspec = string_nextinlist(&resinfo, &sep, NULL, 0)))
          (c = *s) && c != ';' && c != ' ' && c != '\r' && c != '\n'; ) s++;
     return string_copyn(methodspec, s - methodspec);
     }
-return NULL;
+return US"none";
 }
 
 
@@ -1480,6 +1485,14 @@ return pdkim_set_bodyhash(&dkim_sign_ctx,
 
 
 
+void
+arc_sign_init(void)
+{
+memset(&arc_sign_ctx, 0, sizeof(arc_sign_ctx));
+}
+
+
+
 /* A "normal" header line, identified by DKIM processing.  These arrive before
 the call to arc_sign(), which carries any newly-created DKIM headers - and
 those go textually before the normal ones in the message.
@@ -1536,48 +1549,56 @@ selector = string_nextinlist(&signspec, &sep, NULL, 0);
 if (  !*identity | !*selector
    || !(privkey = string_nextinlist(&signspec, &sep, NULL, 0)) || !*privkey)
   {
-  log_write(0, LOG_MAIN|LOG_PANIC, "ARC: bad signing-specification");
-  return NULL;
+  log_write(0, LOG_MAIN, "ARC: bad signing-specification (%s)",
+    !*identity ? "identity" : !*selector ? "selector" : "private-key");
+  return sigheaders ? sigheaders : string_get(0);
   }
 if (*privkey == '/' && !(privkey = expand_file_big_buffer(privkey)))
-  return NULL;
+  return sigheaders ? sigheaders : string_get(0);
 
 DEBUG(D_transport) debug_printf("ARC: sign for %s\n", identity);
 
-/*
-- scan headers for existing ARC chain & A-R (with matching system-identfier)
-  - paniclog & skip on problems (no A-R)
-*/
-
-/* Make an rlist of any new DKIM headers, then add the "normals" rlist to it */
+/* Make an rlist of any new DKIM headers, then add the "normals" rlist to it.
+Then scan the list for an A-R header. */
 
 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? */
-{
-header_line * hnext = NULL;
-for (; rheaders; hnext = rheaders->h, rheaders = rheaders->prev)
-  rheaders->h->next = hnext;
-headers = hnext;
-}
-
-instance = arc_sign_ctx.arcset_chain_last ? arc_sign_ctx.arcset_chain_last->instance + 1 : 1;
+/*XXX also, we really should be accepting any number of ADMD-matching ARs */
+  {
+  header_line * hnext = NULL;
+  for (rheaders = headers_rlist; rheaders;
+       hnext = rheaders->h, rheaders = rheaders->prev)
+    rheaders->h->next = hnext;
+  headers = hnext;
+  }
 
 if (!(arc_sign_find_ar(headers, identity, &ar)))
   {
-  log_write(0, LOG_MAIN|LOG_PANIC, "ARC: no Authentication-Results header for signing");
+  log_write(0, LOG_MAIN, "ARC: no Authentication-Results header for signing");
   return sigheaders ? sigheaders : string_get(0);
   }
 
+/* We previously built the data-struct for the existing ARC chain, if any, using a headers
+feed from the DKIM module.  Use that to give the instance number for the ARC set we are
+about to build. */
+
+DEBUG(D_transport)
+  if (arc_sign_ctx.arcset_chain_last)
+    debug_printf("ARC: existing chain highest instance: %d\n",
+      arc_sign_ctx.arcset_chain_last->instance);
+  else
+    debug_printf("ARC: no existing chain\n");
+
+instance = arc_sign_ctx.arcset_chain_last ? arc_sign_ctx.arcset_chain_last->instance + 1 : 1;
+
 /*
 - Generate AAR
   - copy the A-R; prepend i= & identity
@@ -1725,6 +1746,8 @@ if (arc_state)
     if (sender_host_address)
       g = string_append(g, 2, US" smtp.client-ip=", sender_host_address);
     }
+  else if (arc_state_reason)
+    g = string_append(g, 3, US" (", arc_state_reason, US")");
   DEBUG(D_acl) debug_printf("ARC:  authres '%.*s'\n",
                  g->ptr - start - 3, g->s + start + 3);
   }