ARC: Add basic error-checking on permitted chars in admd & sel for signing. Bug...
[exim.git] / src / src / arc.c
index 857e0c0468ca6095f0832e9ba6f81ec6afb1cdbb..0617312801f342f4500f21c6096942d27bbf54f9 100644 (file)
@@ -2,7 +2,7 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 /* Experimental ARC support for Exim
-   Copyright (c) Jeremy Harris 2018
+   Copyright (c) Jeremy Harris 2018 - 2020
    License: GPL
 */
 
@@ -381,7 +381,7 @@ adding instances as needed and checking for duplicate lines.
 
 static uschar *
 arc_insert_hdr(arc_ctx * ctx, header_line * h, unsigned off, unsigned hoff,
-  BOOL instance_only)
+  BOOL instance_only, arc_line ** alp_ret)
 {
 unsigned i;
 arc_set * as;
@@ -401,6 +401,7 @@ if (!(as = arc_find_set(ctx, i)))   return US"set find";
 if (*(alp = (arc_line **)(US as + hoff))) return US"dup hdr";
 
 *alp = al;
+if (alp_ret) *alp_ret = al;
 return NULL;
 }
 
@@ -424,7 +425,7 @@ if (strncmpic(ARC_HDR_AAR, h->text, ARC_HDRLEN_AAR) == 0)
     debug_printf("ARC: found AAR: %.*s\n", len, h->text);
     }
   if ((e = arc_insert_hdr(ctx, h, ARC_HDRLEN_AAR, offsetof(arc_set, hdr_aar),
-                         TRUE)))
+                         TRUE, NULL)))
     {
     DEBUG(D_acl) debug_printf("inserting AAR: %s\n", e);
     return US"inserting AAR";
@@ -443,15 +444,13 @@ else if (strncmpic(ARC_HDR_AMS, h->text, ARC_HDRLEN_AMS) == 0)
     debug_printf("ARC: found AMS: %.*s\n", len, h->text);
     }
   if ((e = arc_insert_hdr(ctx, h, ARC_HDRLEN_AMS, offsetof(arc_set, hdr_ams),
-                         instance_only)))
+                         instance_only, &ams)))
     {
     DEBUG(D_acl) debug_printf("inserting AMS: %s\n", e);
     return US"inserting AMS";
     }
 
   /* defaults */
-  /*XXX dubious selection of ams here */
-  ams = ctx->arcset_chain->hdr_ams;
   if (!ams->c.data)
     {
     ams->c_head.data = US"simple"; ams->c_head.len = 6;
@@ -469,7 +468,7 @@ else if (strncmpic(ARC_HDR_AS, h->text, ARC_HDRLEN_AS) == 0)
     debug_printf("ARC: found AS: %.*s\n", len, h->text);
     }
   if ((e = arc_insert_hdr(ctx, h, ARC_HDRLEN_AS, offsetof(arc_set, hdr_as),
-                         instance_only)))
+                         instance_only, NULL)))
     {
     DEBUG(D_acl) debug_printf("inserting AS: %s\n", e);
     return US"inserting AS";
@@ -736,7 +735,7 @@ arc_get_verify_hhash(ctx, ams, &hhash);
 
 /* Setup the interface to the signing library */
 
-if ((errstr = exim_dkim_verify_init(&p->key, KEYFMT_DER, &vctx)))
+if ((errstr = exim_dkim_verify_init(&p->key, KEYFMT_DER, &vctx, NULL)))
   {
   DEBUG(D_acl) debug_printf("ARC verify init: %s\n", errstr);
   as->ams_verify_done = arc_state_reason = US"internal sigverify init error";
@@ -965,7 +964,7 @@ if (!(p = arc_line_to_pubkey(hdr_as)))
 /* We know the b-tag blob is of a nul-term string, so safe as a string */
 pdkim_decode_base64(hdr_as->b.data, &sighash);
 
-if ((errstr = exim_dkim_verify_init(&p->key, KEYFMT_DER, &vctx)))
+if ((errstr = exim_dkim_verify_init(&p->key, KEYFMT_DER, &vctx, NULL)))
   {
   DEBUG(D_acl) debug_printf("ARC verify init: %s\n", errstr);
   return US"fail";
@@ -1193,7 +1192,7 @@ static gstring *
 arc_sign_append_aar(gstring * g, arc_ctx * ctx,
   const uschar * identity, int instance, blob * ar)
 {
-int aar_off = g ? g->ptr : 0;
+int aar_off = gstring_length(g);
 arc_set * as =
   store_get(sizeof(arc_set) + sizeof(arc_line) + sizeof(header_line), FALSE);
 arc_line * al = (arc_line *)(as+1);
@@ -1558,6 +1557,23 @@ return arc_try_header(&arc_sign_ctx, headers_rlist->h, TRUE);
 
 
 
+/* Per RFCs 6376, 7489 the only allowed chars in either an ADMD id
+or a selector are ALPHA/DIGGIT/'-'/'.'
+
+Check, to help catch misconfigurations such as a missing selector
+element in the arc_sign list.
+*/
+
+static BOOL
+arc_valid_id(const uschar * s)
+{
+for (uschar c; c = *s++; )
+  if (!isalnum(c) && c != '-' && c != '.') return FALSE;
+return TRUE;
+}
+
+
+
 /* ARC signing.  Called from the smtp transport, if the arc_sign option is set.
 The dkim_exim_sign() function has already been called, so will have hashed the
 message body for us so long as we requested a hash previously.
@@ -1596,10 +1612,13 @@ selector = string_nextinlist(&signspec, &sep, NULL, 0);
 if (  !*identity || !*selector
    || !(privkey = string_nextinlist(&signspec, &sep, NULL, 0)) || !*privkey)
   {
-  log_write(0, LOG_MAIN, "ARC: bad signing-specification (%s)",
-    !*identity ? "identity" : !*selector ? "selector" : "private-key");
-  return sigheaders ? sigheaders : string_get(0);
+  s = !*identity ? US"identity" : !*selector ? US"selector" : US"private-key";
+  goto bad_arg_ret;
   }
+if (!arc_valid_id(identity))
+  { s = US"identity"; goto bad_arg_ret; }
+if (!arc_valid_id(selector))
+  { s = US"selector"; goto bad_arg_ret; }
 if (*privkey == '/' && !(privkey = expand_file_big_buffer(privkey)))
   return sigheaders ? sigheaders : string_get(0);
 
@@ -1719,6 +1738,11 @@ if (sigheaders) g = string_catn(g, sigheaders->s, sigheaders->ptr);
 (void) string_from_gstring(g);
 gstring_release_unused(g);
 return g;
+
+
+bad_arg_ret:
+  log_write(0, LOG_MAIN, "ARC: bad signing-specification (%s)", s);
+  return sigheaders ? sigheaders : string_get(0);
 }