File patch-CVE-2023-42119.patch of Package exim.18132

diff --git a/src/dns.c b/src/dns.c
index 7d7ee0c04..8dc3695a1 100644
--- a/src/dns.c
+++ b/src/dns.c
@@ -304,7 +304,7 @@ Return: TRUE for a bad result
 static BOOL
 dnss_inc_aptr(const dns_answer * dnsa, dns_scan * dnss, unsigned delta)
 {
-return (dnss->aptr += delta) >= dnsa->answer + dnsa->answerlen;
+return (dnss->aptr += delta) > dnsa->answer + dnsa->answerlen;
 }
 
 /*************************************************
@@ -388,7 +388,7 @@ if (reset != RESET_NEXT)
       TRACE trace = "A-hdr";
       if (dnss_inc_aptr(dnsa, dnss, namelen+8)) goto null_return;
       GETSHORT(dnss->srr.size, dnss->aptr); /* size of data portion */
-      /* skip over it */
+      /* skip over it, checking for a bogus size */
       TRACE trace = "A-skip";
       if (dnss_inc_aptr(dnsa, dnss, dnss->srr.size)) goto null_return;
       }
@@ -428,10 +428,9 @@ GETLONG(dnss->srr.ttl, dnss->aptr);		/* TTL */
 GETSHORT(dnss->srr.size, dnss->aptr);		/* Size of data portion */
 dnss->srr.data = dnss->aptr;			/* The record's data follows */
 
-/* Unchecked increment ok here since no further access on this iteration;
-will be checked on next at "R-name". */
-
-dnss->aptr += dnss->srr.size;			/* Advance to next RR */
+/* skip over it, checking for a bogus size */
+if (dnss_inc_aptr(dnsa, dnss, dnss->srr.size))
+  goto null_return;
 
 /* Return a pointer to the dns_record structure within the dns_answer. This is
 for convenience so that the scans can use nice-looking for loops. */
diff --git a/src/lookups/dnsdb.c b/src/lookups/dnsdb.c
index 355be1b5d..020dc9a52 100644
--- a/src/lookups/dnsdb.c
+++ b/src/lookups/dnsdb.c
@@ -398,43 +398,60 @@ while ((domain = string_nextinlist(&keystring, &sep, NULL, 0)))
 
       if (type == T_TXT || type == T_SPF)
         {
-        if (outsep2 == NULL)	/* output only the first item of data */
-          yield = string_catn(yield, US (rr->data+1), (rr->data)[0]);
+        if (!outsep2)			/* output only the first item of data */
+	  {
+	  uschar n = (rr->data)[0];
+	  /* size byte + data bytes must not excced the RRs length */
+	  if (n + 1 <= rr->size)
+	    yield = string_catn(yield, US (rr->data+1), n);
+	  }
         else
           {
           /* output all items */
           int data_offset = 0;
           while (data_offset < rr->size)
             {
-            uschar chunk_len = (rr->data)[data_offset++];
-            if (outsep2[0] != '\0' && data_offset != 1)
+            uschar chunk_len = (rr->data)[data_offset];
+	    int remain = rr->size - data_offset;
+
+	    /* Apparently there are resolvers that do not check RRs before passing
+	    them on, and glibc fails to do so.  So every application must...
+	    Check for chunk len exceeding RR */
+
+	    if (chunk_len > remain)
+	      chunk_len = remain;
+
+            if (*outsep2  && data_offset != 0)
               yield = string_catn(yield, outsep2, 1);
-            yield = string_catn(yield, US ((rr->data)+data_offset), chunk_len);
+            yield = string_catn(yield, US ((rr->data) + ++data_offset), --chunk_len);
             data_offset += chunk_len;
             }
           }
         }
       else if (type == T_TLSA)
-        {
-        uint8_t usage, selector, matching_type;
-        uint16_t payload_length;
-        uschar s[MAX_TLSA_EXPANDED_SIZE];
-	uschar * sp = s;
-        uschar * p = US rr->data;
+	if (rr->size < 3)
+	  continue;
+	else
+	  {
+	  uint8_t usage, selector, matching_type;
+	  uint16_t payload_length;
+	  uschar s[MAX_TLSA_EXPANDED_SIZE];
+	  uschar * sp = s;
+	  uschar * p = US rr->data;
+
+	  usage = *p++;
+	  selector = *p++;
+	  matching_type = *p++;
+	  /* What's left after removing the first 3 bytes above */
+	  payload_length = rr->size - 3;
+	  sp += sprintf(CS s, "%d%c%d%c%d%c", usage, *outsep2,
+		  selector, *outsep2, matching_type, *outsep2);
+	  /* Now append the cert/identifier, one hex char at a time */
+	  while (payload_length-- > 0 && sp-s < (MAX_TLSA_EXPANDED_SIZE - 4))
+	    sp += sprintf(CS sp, "%02x", *p++);
 
-        usage = *p++;
-        selector = *p++;
-        matching_type = *p++;
-        /* What's left after removing the first 3 bytes above */
-        payload_length = rr->size - 3;
-        sp += sprintf(CS s, "%d%c%d%c%d%c", usage, *outsep2,
-		selector, *outsep2, matching_type, *outsep2);
-        /* Now append the cert/identifier, one hex char at a time */
-	while (payload_length-- > 0 && sp-s < (MAX_TLSA_EXPANDED_SIZE - 4))
-          sp += sprintf(CS sp, "%02x", *p++);
-
-        yield = string_cat(yield, s);
-        }
+	  yield = string_cat(yield, s);
+	  }
       else   /* T_CNAME, T_CSA, T_MX, T_MXH, T_NS, T_PTR, T_SOA, T_SRV */
         {
         int priority, weight, port;
@@ -444,17 +461,20 @@ while ((domain = string_nextinlist(&keystring, &sep, NULL, 0)))
 	switch (type)
 	  {
 	  case T_MXH:
+	    if (rr->size < sizeof(u_int16_t)) continue;
 	    /* mxh ignores the priority number and includes only the hostnames */
 	    GETSHORT(priority, p);
 	    break;
 
 	  case T_MX:
+	    if (rr->size < sizeof(u_int16_t)) continue;
 	    GETSHORT(priority, p);
 	    sprintf(CS s, "%d%c", priority, *outsep2);
 	    yield = string_cat(yield, s);
 	    break;
 
 	  case T_SRV:
+	    if (rr->size < 3*sizeof(u_int16_t)) continue;
 	    GETSHORT(priority, p);
 	    GETSHORT(weight, p);
 	    GETSHORT(port, p);
@@ -464,6 +484,7 @@ while ((domain = string_nextinlist(&keystring, &sep, NULL, 0)))
 	    break;
 
 	  case T_CSA:
+	    if (rr->size < 3*sizeof(u_int16_t)) continue;
 	    /* See acl_verify_csa() for more comments about CSA. */
 	    GETSHORT(priority, p);
 	    GETSHORT(weight, p);
@@ -514,7 +535,7 @@ while ((domain = string_nextinlist(&keystring, &sep, NULL, 0)))
 
 	if (type == T_SOA && outsep2 != NULL)
 	  {
-	  unsigned long serial, refresh, retry, expire, minimum;
+	  unsigned long serial = 0, refresh = 0, retry = 0, expire = 0, minimum = 0;
 
 	  p += rc;
 	  yield = string_catn(yield, outsep2, 1);
@@ -530,8 +551,11 @@ while ((domain = string_nextinlist(&keystring, &sep, NULL, 0)))
 	  else yield = string_cat(yield, s);
 
 	  p += rc;
-	  GETLONG(serial, p); GETLONG(refresh, p);
-	  GETLONG(retry,  p); GETLONG(expire,  p); GETLONG(minimum, p);
+	  if (rr->size >= p - rr->data - 5*sizeof(u_int32_t))
+	    {
+	    GETLONG(serial, p); GETLONG(refresh, p);
+	    GETLONG(retry,  p); GETLONG(expire,  p); GETLONG(minimum, p);
+	    }
 	  sprintf(CS s, "%c%lu%c%lu%c%lu%c%lu%c%lu",
 	    *outsep2, serial, *outsep2, refresh,
 	    *outsep2, retry,  *outsep2, expire,  *outsep2, minimum);
diff --git a/src/spf.c b/src/spf.c
index db6eea3a8..1981d81b6 100644
--- a/src/spf.c
+++ b/src/spf.c
@@ -120,6 +120,7 @@ for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
     switch(rr_type)
       {
       case T_MX:
+	if (rr->size < 2) continue;
 	s += 2;	/* skip the MX precedence field */
       case T_PTR:
 	{
@@ -135,6 +136,7 @@ for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
 	gstring * g = NULL;
 	uschar chunk_len;
 
+	if (rr->size < 1+6) continue;		/* min for version str */
 	if (strncmpic(rr->data+1, US SPF_VER_STR, 6) != 0)
 	  {
 	  HDEBUG(D_host_lookup) debug_printf("not an spf record: %.*s\n",
@@ -142,9 +144,12 @@ for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
 	  continue;
 	  }
 
-	for (int off = 0; off < rr->size; off += chunk_len)
+	/* require 1 byte for the chunk_len */
+	for (int off = 0; off < rr->size - 1; off += chunk_len)
 	  {
-	  if (!(chunk_len = s[off++])) break;
+	  if (  !(chunk_len = s[off++])
+	     || rr->size < off + chunk_len	/* ignore bogus size chunks */
+	     ) break;
 	  g = string_catn(g, s+off, chunk_len);
 	  }
 	if (!g)
openSUSE Build Service is sponsored by