Include address declared malformed, in error message
[exim.git] / src / src / arc.c
index 391af077dd5931b3e2f303283cb15eaba1f7f5b5..ef44672f8fb740747d3aeb5a31603cc15f1c78d7 100644 (file)
@@ -3,7 +3,9 @@
 *************************************************/
 /* Experimental ARC support for Exim
    Copyright (c) Jeremy Harris 2018 - 2020
+   Copyright (c) The Exim Maintainers 2021 - 2022
    License: GPL
+   SPDX-License-Identifier: GPL-2.0-or-later
 */
 
 #include "exim.h"
@@ -141,7 +143,7 @@ for (pas = &ctx->arcset_chain, prev = NULL, next = ctx->arcset_chain;
   }
 
 DEBUG(D_acl) debug_printf("ARC: new instance %u\n", i);
-*pas = as = store_get(sizeof(arc_set), FALSE);
+*pas = as = store_get(sizeof(arc_set), GET_UNTAINTED);
 memset(as, 0, sizeof(arc_set));
 as->next = next;
 as->prev = prev;
@@ -199,7 +201,7 @@ al->complete = h;
 
 if (!instance_only)
   {
-  al->rawsig_no_b_val.data = store_get(h->slen + 1, TRUE);     /* tainted */
+  al->rawsig_no_b_val.data = store_get(h->slen + 1, GET_TAINTED);
   memcpy(al->rawsig_no_b_val.data, h->text, off);      /* copy the header name blind */
   r = al->rawsig_no_b_val.data + off;
   al->rawsig_no_b_val.len = off;
@@ -259,8 +261,7 @@ while ((c = *s))
            if (c != ' ' && c != '\t' && c != '\n' && c != '\r')
              g = string_catn(g, s, 1);
          if (!g) return US"no b= value";
-         al->b.data = string_from_gstring(g);
-         al->b.len = g->ptr;
+         al->b.len = len_string_from_gstring(g, &al->b.data);
          gstring_release_unused(g);
          bend = s;
          break;
@@ -276,8 +277,7 @@ while ((c = *s))
            if (c != ' ' && c != '\t' && c != '\n' && c != '\r')
              g = string_catn(g, s, 1);
          if (!g) return US"no bh= value";
-         al->bh.data = string_from_gstring(g);
-         al->bh.len = g->ptr;
+         al->bh.len = len_string_from_gstring(g, &al->bh.data);
          gstring_release_unused(g);
          break;
        default:
@@ -385,7 +385,7 @@ arc_insert_hdr(arc_ctx * ctx, header_line * h, unsigned off, unsigned hoff,
 {
 unsigned i;
 arc_set * as;
-arc_line * al = store_get(sizeof(arc_line), FALSE), ** alp;
+arc_line * al = store_get(sizeof(arc_line), GET_UNTAINTED), ** alp;
 uschar * e;
 
 memset(al, 0, sizeof(arc_line));
@@ -496,7 +496,7 @@ const uschar * e;
 DEBUG(D_acl) debug_printf("ARC: collecting arc sets\n");
 for (h = header_list; h; h = h->next)
   {
-  r = store_get(sizeof(hdr_rlist), FALSE);
+  r = store_get(sizeof(hdr_rlist), GET_UNTAINTED);
   r->prev = rprev;
   r->used = FALSE;
   r->h = h;
@@ -568,7 +568,7 @@ while ((hn = string_nextinlist(&headernames, &sep, NULL, 0)))
 
       len = Ustrlen(s);
       DEBUG(D_acl) pdkim_quoteprint(s, len);
-      exim_sha_update(&hhash_ctx, s, Ustrlen(s));
+      exim_sha_update_string(&hhash_ctx, s);
       r->used = TRUE;
       break;
       }
@@ -1102,7 +1102,7 @@ out:
 static hdr_rlist *
 arc_rlist_entry(hdr_rlist * list, const uschar * s, int len)
 {
-hdr_rlist * r = store_get(sizeof(hdr_rlist) + sizeof(header_line), FALSE);
+hdr_rlist * r = store_get(sizeof(hdr_rlist) + sizeof(header_line), GET_UNTAINTED);
 header_line * h = r->h = (header_line *)(r+1);
 
 r->prev = list;
@@ -1112,11 +1112,6 @@ h->type = 0;
 h->slen = len;
 h->text = US s;
 
-/* This works for either NL or CRLF lines; also nul-termination */
-while (*++s)
-  if (*s == '\n' && s[1] != '\t' && s[1] != ' ') break;
-s++;           /* move past end of line */
-
 return r;
 }
 
@@ -1194,7 +1189,7 @@ arc_sign_append_aar(gstring * g, arc_ctx * ctx,
 {
 int aar_off = gstring_length(g);
 arc_set * as =
-  store_get(sizeof(arc_set) + sizeof(arc_line) + sizeof(header_line), FALSE);
+  store_get(sizeof(arc_set) + sizeof(arc_line) + sizeof(header_line), GET_UNTAINTED);
 arc_line * al = (arc_line *)(as+1);
 header_line * h = (header_line *)(al+1);
 
@@ -1304,14 +1299,14 @@ int col;
 int hashtype = pdkim_hashname_to_hashtype(US"sha256", 6);      /*XXX hardwired */
 blob sig;
 int ams_off;
-arc_line * al = store_get(sizeof(header_line) + sizeof(arc_line), FALSE);
+arc_line * al = store_get(sizeof(header_line) + sizeof(arc_line), GET_UNTAINTED);
 header_line * h = (header_line *)(al+1);
 
 /* debug_printf("%s\n", __FUNCTION__); */
 
 /* Construct the to-be-signed AMS pseudo-header: everything but the sig. */
 
-ams_off = g->ptr;
+ams_off = gstring_length(g);
 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)
@@ -1355,7 +1350,7 @@ for(col = 3; rheaders; rheaders = rheaders->prev)
 
 /* Lose the last colon from the h= list */
 
-if (g->s[g->ptr - 1] == ':') g->ptr--;
+gstring_trim_trailing(g, ':');
 
 g = string_catn(g, US";\r\n\tb=;", 7);
 
@@ -1373,7 +1368,7 @@ if (!arc_sig_from_pseudoheader(hdata, hashtype, privkey, &sig, US"AMS"))
 /* Lose the trailing semicolon from the psuedo-header, and append the signature
 (folded over lines) and termination to complete it. */
 
-g->ptr--;
+gstring_trim(g, 1);
 g = arc_sign_append_sig(g, &sig);
 
 h->slen = g->ptr - ams_off;
@@ -1419,7 +1414,7 @@ arc_sign_prepend_as(gstring * arcset_interim, arc_ctx * ctx,
 {
 gstring * arcset;
 uschar * status = arc_ar_cv_status(ar);
-arc_line * al = store_get(sizeof(header_line) + sizeof(arc_line), FALSE);
+arc_line * al = store_get(sizeof(header_line) + sizeof(arc_line), GET_UNTAINTED);
 header_line * h = (header_line *)(al+1);
 uschar * badline_str;
 
@@ -1531,6 +1526,7 @@ void
 arc_sign_init(void)
 {
 memset(&arc_sign_ctx, 0, sizeof(arc_sign_ctx));
+headers_rlist = NULL;
 }
 
 
@@ -1550,13 +1546,30 @@ into the copies.
 static const uschar *
 arc_header_sign_feed(gstring * g)
 {
-uschar * s = string_copyn(g->s, g->ptr);
+uschar * s = string_copy_from_gstring(g);
 headers_rlist = arc_rlist_entry(headers_rlist, s, g->ptr);
 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.
@@ -1590,17 +1603,18 @@ expire = now = 0;
 
 /* Parse the signing specification */
 
-identity = string_nextinlist(&signspec, &sep, NULL, 0);
-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);
-  }
+if (!(identity = string_nextinlist(&signspec, &sep, NULL, 0)) || !*identity)
+  { s = US"identity"; goto bad_arg_ret; }
+if (!(selector = string_nextinlist(&signspec, &sep, NULL, 0)) || !*selector)
+  { s = US"selector"; goto bad_arg_ret; }
+if (!(privkey = string_nextinlist(&signspec, &sep, NULL, 0))  || !*privkey)
+  { s = US"privkey"; 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);
+  goto ret_sigheaders;
 
 if ((opts = string_nextinlist(&signspec, &sep, NULL, 0)))
   {
@@ -1659,7 +1673,7 @@ if ((rheaders = arc_sign_scan_headers(&arc_sign_ctx, sigheaders)))
 if (!(arc_sign_find_ar(headers, identity, &ar)))
   {
   log_write(0, LOG_MAIN, "ARC: no Authentication-Results header for signing");
-  return sigheaders ? sigheaders : string_get(0);
+  goto ret_sigheaders;
   }
 
 /* We previously built the data-struct for the existing ARC chain, if any, using a headers
@@ -1715,9 +1729,19 @@ if (g)
 /* Finally, append the dkim headers and return the lot. */
 
 if (sigheaders) g = string_catn(g, sigheaders->s, sigheaders->ptr);
-(void) string_from_gstring(g);
-gstring_release_unused(g);
-return g;
+
+out:
+  if (!g) return string_get(1);
+  (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);
+ret_sigheaders:
+  g = sigheaders;
+  goto out;
 }
 
 
@@ -1746,10 +1770,9 @@ if (strncmpic(ARC_HDR_AMS, g->s, ARC_HDRLEN_AMS) != 0) return US"not AMS";
 DEBUG(D_receive) debug_printf("ARC: spotted AMS header\n");
 /* Parse the AMS header */
 
-h.next = NULL;
-h.slen = g->size;
-h.text = g->s;
 memset(&al, 0, sizeof(arc_line));
+h.next = NULL;
+h.slen = len_string_from_gstring(g, &h.text);
 if ((errstr = arc_parse_line(&al, &h, ARC_HDRLEN_AMS, FALSE)))
   {
   DEBUG(D_acl) if (errstr) debug_printf("ARC: %s\n", errstr);
@@ -1831,7 +1854,8 @@ for (as = arc_verify_ctx.arcset_chain, inst = 1; as; as = as->next, inst++)
   else
     g = string_catn(g, US":", 1);
   }
-return g ? g->s : US"";
+if (!g) return US"";
+return string_from_gstring(g);
 }
 
 
@@ -1844,7 +1868,7 @@ if (arc_state)
   {
   arc_line * highest_ams;
   int start = 0;               /* Compiler quietening */
-  DEBUG(D_acl) start = g->ptr;
+  DEBUG(D_acl) start = gstring_length(g);
 
   g = string_append(g, 2, US";\n\tarc=", arc_state);
   if (arc_received_instance > 0)
@@ -1864,7 +1888,7 @@ if (arc_state)
   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);
+                 gstring_length(g) - start - 3, g->s + start + 3);
   }
 else
   DEBUG(D_acl) debug_printf("ARC:  no authres\n");