Mailing List Archive

Hotfix for some of the ZDI vulnerabilities
Hi,

below you find a patch that fixes some (probably three?) of what I guess are
the vulnerabilities reported by ZDI.

Please note that the patch is only mildly tested, it is developed based on
the git master branch, but can be applied to older versions with minor
massaging. If you go back far enough, proxy.c was part of smtp_in.c, but if
you adjust for that, the patch can be made to apply there, too.

Obviously, I have no idea whether this actually addresses what ZDI has
reported, but if not, these probably should be fixed, too, and if so, given
the fact that I managed to rather easily find these vulnerabilities based
on the information that's publicly available, I don't think there is much
point to trying to keep this secret any longer--if anything, it's
counterproductive.

Also mind you that this is a hot fix, it's neither elegant, nor does it do
any useful error reporting, the goal was simply to prevent out of bounds
accesses.

Florian

---

diff --git a/src/src/auths/external.c b/src/src/auths/external.c
index 078aad0..54966e6 100644
--- a/src/src/auths/external.c
+++ b/src/src/auths/external.c
@@ -101,6 +101,9 @@ if (expand_nmax == 0) /* skip if rxd data */
if ((rc = auth_prompt(CUS"")) != OK)
return rc;

+if (expand_nmax != 1)
+ return FAIL;
+
if (ob->server_param2)
{
uschar * s = expand_string(ob->server_param2);
diff --git a/src/src/auths/spa.c b/src/src/auths/spa.c
index 222ccea..66967d6 100644
--- a/src/src/auths/spa.c
+++ b/src/src/auths/spa.c
@@ -166,12 +166,18 @@ if (auth_get_no64_data(&data, msgbuf) != OK)
return FAIL;

/* dump client response */
-if (spa_base64_to_bits(CS &response, sizeof(response), CCS data) < 0)
+int l = spa_base64_to_bits(CS &response, sizeof(response), CCS data);
+if (l < 0)
{
DEBUG(D_auth) debug_printf("auth_spa_server(): bad base64 data in "
"response: %s\n", data);
return FAIL;
}
+if(l < (char *)&response.buffer - (char *)&response)return FAIL;
+unsigned long o = IVAL(&response.uUser.offset, 0);
+if((l < o) || (l - o < SVAL(&response.uUser.len, 0)))return FAIL;
+o = IVAL(&response.ntResponse.offset, 0);
+if((l < o) || (l - o < 24))return FAIL;

/***************************************************************
PH 07-Aug-2003: The original code here was this:
@@ -346,7 +352,10 @@ if (!smtp_read_response(sx, US buffer, buffsize, '3', timeout))

/* convert the challenge into the challenge struct */
DSPA("\n\n%s authenticator: challenge (%s)\n\n", ablock->name, buffer + 4);
-spa_base64_to_bits(CS (&challenge), sizeof(challenge), CCS (buffer + 4));
+int l = spa_base64_to_bits(CS (&challenge), sizeof(challenge), CCS (buffer + 4));
+if((l < 0) || (l < (char *)&challenge.buffer - (char *)&challenge))return FAIL;
+unsigned long o = IVAL(&challenge.uDomain.offset, 0);
+if((l < o) || (l - o < SVAL(&challenge.uDomain.len, 0)))return FAIL;

spa_build_auth_response(&challenge, &response, CS username, CS password);
spa_bits_to_base64(US msgbuf, US &response, spa_request_length(&response));
diff --git a/src/src/proxy.c b/src/src/proxy.c
index fbce111..8dd7034 100644
--- a/src/src/proxy.c
+++ b/src/src/proxy.c
@@ -93,6 +93,8 @@ while (capacity > 0)
do { ret = read(fd, to, 1); } while (ret == -1 && errno == EINTR && !had_command_timeout);
if (ret == -1)
return -1;
+ if (!ret)
+ break;
have++;
if (last)
return have;
@@ -254,6 +256,8 @@ if ((ret == PROXY_INITIAL_READ) && (memcmp(&hdr.v2, v2sig, sizeof(v2sig)) == 0))
goto proxyfail;
}

+ if (ret < 16)
+ goto proxyfail;
/* The v2 header will always be 16 bytes per the spec. */
size = 16 + ntohs(hdr.v2.len);
DEBUG(D_receive) debug_printf("Detected PROXYv2 header, size %d (limit %d)\n",
@@ -274,7 +278,7 @@ if ((ret == PROXY_INITIAL_READ) && (memcmp(&hdr.v2, v2sig, sizeof(v2sig)) == 0))
{
retmore = read(fd, (uschar*)&hdr + ret, size-ret);
} while (retmore == -1 && errno == EINTR && !had_command_timeout);
- if (retmore == -1)
+ if (retmore < 1)
goto proxyfail;
DEBUG(D_receive) proxy_debug(US &hdr, ret, ret + retmore);
ret += retmore;
@@ -297,6 +301,8 @@ if (ret >= 16 && memcmp(&hdr.v2, v2sig, 12) == 0)
switch (hdr.v2.fam)
{
case 0x11: /* TCPv4 address type */
+ if (ret < 28)
+ goto proxyfail;
iptype = US"IPv4";
tmpaddr.sin_addr.s_addr = hdr.v2.addr.ip4.src_addr;
inet_ntop(AF_INET, &tmpaddr.sin_addr, CS &tmpip, sizeof(tmpip));
@@ -323,6 +329,8 @@ if (ret >= 16 && memcmp(&hdr.v2, v2sig, 12) == 0)
proxy_external_port = tmpport;
goto done;
case 0x21: /* TCPv6 address type */
+ if (ret < 52)
+ goto proxyfail;
iptype = US"IPv6";
memmove(tmpaddr6.sin6_addr.s6_addr, hdr.v2.addr.ip6.src_addr, 16);
inet_ntop(AF_INET6, &tmpaddr6.sin6_addr, CS &tmpip6, sizeof(tmpip6));
@@ -381,10 +389,13 @@ else if (ret >= 8 && memcmp(hdr.v1.line, "PROXY", 5) == 0)
goto proxyfail;
ret += r2;

+ if(ret > 107)
+ goto proxyfail;
+ hdr.v1.line[ret] = 0;
p = string_copy(hdr.v1.line);
end = memchr(p, '\r', ret - 1);

- if (!end || (end == (uschar*)&hdr + ret) || end[1] != '\n')
+ if (!end || end[1] != '\n')
{
DEBUG(D_receive) debug_printf("Partial or invalid PROXY header\n");
goto proxyfail;

--
## subscription configuration (requires account):
## https://lists.exim.org/mailman3/postorius/lists/exim-dev.lists.exim.org/
## unsubscribe (doesn't require an account):
## exim-dev-unsubscribe@lists.exim.org
## Exim details at http://www.exim.org/
## Please use the Wiki with this list - http://wiki.exim.org/
Re: Hotfix for some of the ZDI vulnerabilities [ In reply to ]
Hi,

as the patches in spa-auth-fixes don't really fix the vulnerabilities that
they are presumably supposed to fix and introduce new bugs, I added another
fix to my hotfix for a vulnerability that those patches apparently try to
address and that I had overlooked previously, and I also added fixes for
the numerous DNS OOB accesses, both in the dnsdb lookup code, as well as
pretty much anywhere else that DNS is being used.

As before, the patch is only a hotfix that tries to do the bare minimum to
prevent exploitation and is only tested superficially, it's still based on
62ebdc13d2e889666221bc18d6fed022554daf64, and needs a bit more massaging to
apply to older versions.

I'll send a separate email about the bugs in the patches in
spa-auth-fixes ...

Regards, Florian

---

diff --git a/src/src/acl.c b/src/src/acl.c
index 118e4b3..ed13cb5 100644
--- a/src/src/acl.c
+++ b/src/src/acl.c
@@ -1434,6 +1434,9 @@ for (rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS);

/* Extract the numerical SRV fields (p is incremented) */

+ if (rr->size < 6) continue;
+ if (dnsa->answer + dnsa->answerlen - rr->data < 6) continue;
+
GETSHORT(priority, p);
GETSHORT(weight, p);
GETSHORT(port, p);
@@ -1475,8 +1478,9 @@ for (rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS);
client's IP address is listed as one of the SRV target addresses. Save the
target hostname then break to scan the additional data for its addresses. */

- (void)dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, p,
+ int r = dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, p,
(DN_EXPAND_ARG4_TYPE)target, TARGET_SIZE);
+ if ((r < 0) || (rr->size - 6 < r)) continue;

DEBUG(D_acl) debug_printf_indent("CSA target is %s\n", target);

diff --git a/src/src/auths/auth-spa.c b/src/src/auths/auth-spa.c
index bcf88c8..f6c845c 100644
--- a/src/src/auths/auth-spa.c
+++ b/src/src/auths/auth-spa.c
@@ -1212,7 +1212,7 @@ char versionString[] = "libntlm version 0.21";

#define spa_bytes_add(ptr, header, buf, count) \
{ \
-if (buf && (count) != 0) /* we hate -Wint-in-bool-contex */ \
+if (buf && (count) != 0 && (sizeof((ptr)->buffer) - (ptr)->bufIndex < (count))) /* we hate -Wint-in-bool-contex */ \
{ \
SSVAL(&ptr->header.len,0,count); \
SSVAL(&ptr->header.maxlen,0,count); \
diff --git a/src/src/auths/external.c b/src/src/auths/external.c
index 078aad0..54966e6 100644
--- a/src/src/auths/external.c
+++ b/src/src/auths/external.c
@@ -101,6 +101,9 @@ if (expand_nmax == 0) /* skip if rxd data */
if ((rc = auth_prompt(CUS"")) != OK)
return rc;

+if (expand_nmax != 1)
+ return FAIL;
+
if (ob->server_param2)
{
uschar * s = expand_string(ob->server_param2);
diff --git a/src/src/auths/spa.c b/src/src/auths/spa.c
index 222ccea..66967d6 100644
--- a/src/src/auths/spa.c
+++ b/src/src/auths/spa.c
@@ -166,12 +166,18 @@ if (auth_get_no64_data(&data, msgbuf) != OK)
return FAIL;

/* dump client response */
-if (spa_base64_to_bits(CS &response, sizeof(response), CCS data) < 0)
+int l = spa_base64_to_bits(CS &response, sizeof(response), CCS data);
+if (l < 0)
{
DEBUG(D_auth) debug_printf("auth_spa_server(): bad base64 data in "
"response: %s\n", data);
return FAIL;
}
+if(l < (char *)&response.buffer - (char *)&response)return FAIL;
+unsigned long o = IVAL(&response.uUser.offset, 0);
+if((l < o) || (l - o < SVAL(&response.uUser.len, 0)))return FAIL;
+o = IVAL(&response.ntResponse.offset, 0);
+if((l < o) || (l - o < 24))return FAIL;

/***************************************************************
PH 07-Aug-2003: The original code here was this:
@@ -346,7 +352,10 @@ if (!smtp_read_response(sx, US buffer, buffsize, '3', timeout))

/* convert the challenge into the challenge struct */
DSPA("\n\n%s authenticator: challenge (%s)\n\n", ablock->name, buffer + 4);
-spa_base64_to_bits(CS (&challenge), sizeof(challenge), CCS (buffer + 4));
+int l = spa_base64_to_bits(CS (&challenge), sizeof(challenge), CCS (buffer + 4));
+if((l < 0) || (l < (char *)&challenge.buffer - (char *)&challenge))return FAIL;
+unsigned long o = IVAL(&challenge.uDomain.offset, 0);
+if((l < o) || (l - o < SVAL(&challenge.uDomain.len, 0)))return FAIL;

spa_build_auth_response(&challenge, &response, CS username, CS password);
spa_bits_to_base64(US msgbuf, US &response, spa_request_length(&response));
diff --git a/src/src/dkim.c b/src/src/dkim.c
index a49c8d7..f23c0b4 100644
--- a/src/src/dkim.c
+++ b/src/src/dkim.c
@@ -64,10 +64,12 @@ for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS);
rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
if (rr->type == T_TXT)
{ /* Copy record content to the answer buffer */
+ if (dnsa->answer + dnsa->answerlen - rr->data < rr->size) goto bad;
for (int rr_offset = 0; rr_offset < rr->size; )
{
uschar len = rr->data[rr_offset++];

+ if (rr->size - rr_offset < len) goto bad;
g = string_catn(g, US(rr->data + rr_offset), len);
if (g->ptr >= PDKIM_DNS_TXT_MAX_RECLEN)
goto bad;
diff --git a/src/src/dmarc.c b/src/src/dmarc.c
index 042ebe9..911c189 100644
--- a/src/src/dmarc.c
+++ b/src/src/dmarc.c
@@ -230,7 +230,7 @@ int rc = dns_lookup(dnsa, string_sprintf("_dmarc.%s", dom), T_TXT, NULL);
if (rc == DNS_SUCCEED)
for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
- if (rr->type == T_TXT && rr->size > 3)
+ if (rr->type == T_TXT && rr->size > 3 && dnsa->answer + dnsa->answerlen - rr->data >= rr->size)
{
uschar *record = string_copyn_taint(US rr->data, rr->size, GET_TAINTED);
store_free_dns_answer(dnsa);
diff --git a/src/src/dns.c b/src/src/dns.c
index d39b4b5..f016413 100644
--- a/src/src/dns.c
+++ b/src/src/dns.c
@@ -388,6 +388,7 @@ if (reset != RESET_NEXT)
/* skip name, type, class & TTL */
TRACE trace = "A-hdr";
if (dnss_inc_aptr(dnsa, dnss, namelen+8)) goto null_return;
+ if (dnsa->answer + dnsa->answerlen - dnss->aptr < 2) goto null_return;
GETSHORT(dnss->srr.size, dnss->aptr); /* size of data portion */
/* skip over it */
TRACE trace = "A-skip";
@@ -422,9 +423,11 @@ from the following bytes. */
TRACE trace = "R-name";
if (dnss_inc_aptr(dnsa, dnss, namelen)) goto null_return;

+if (dnsa->answer + dnsa->answerlen - dnss->aptr < 2) goto null_return;
GETSHORT(dnss->srr.type, dnss->aptr); /* Record type */
TRACE trace = "R-class";
if (dnss_inc_aptr(dnsa, dnss, 2)) goto null_return; /* Don't want class */
+if (dnsa->answer + dnsa->answerlen - dnss->aptr < 6) goto null_return;
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 */
@@ -1088,9 +1091,11 @@ for (int i = 0; i <= dns_cname_loops; i++)

/* DNS data comes from the outside, hence tainted */
data = store_get(256, GET_TAINTED);
- if (dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen,
- cname_rr.data, (DN_EXPAND_ARG4_TYPE)data, 256) < 0)
+ int r;
+ if ((r = dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen,
+ cname_rr.data, (DN_EXPAND_ARG4_TYPE)data, 256)) < 0)
return DNS_FAIL;
+ if (cname_rr.size < r) return DNS_FAIL;
name = data;

if (!dns_is_secure(dnsa))
@@ -1258,6 +1263,8 @@ switch (type)
const uschar * p = rr->data;

/* Extract the numerical SRV fields (p is incremented) */
+ if (rr->size < 6) return DNS_NOMATCH;
+ if (dnsa->answer + dnsa->answerlen - p < 6) return DNS_NOMATCH;
GETSHORT(priority, p);
GETSHORT(dummy_weight, p);
GETSHORT(port, p);
@@ -1312,6 +1319,7 @@ uschar * dnsa_lim = dnsa->answer + dnsa->answerlen;
if (rr->type == T_A)
{
uschar *p = US rr->data;
+ if (rr->size >= 4)
if (p + 4 <= dnsa_lim)
{
/* the IP is not regarded as tainted */
@@ -1325,6 +1333,7 @@ if (rr->type == T_A)

else
{
+ if (rr->size >= 16)
if (rr->data + 16 <= dnsa_lim)
{
struct in6_addr in6;
diff --git a/src/src/dnsbl.c b/src/src/dnsbl.c
index 1172d61..5fccd0d 100644
--- a/src/src/dnsbl.c
+++ b/src/src/dnsbl.c
@@ -363,8 +363,12 @@ if (cb->rc == DNS_SUCCEED)
rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
if (rr->type == T_TXT)
{
+ if (rr->size < 1) continue;
+ if (dnsa->answer + dnsa->answerlen - rr->data < 1) continue;
int len = (rr->data)[0];
if (len > 511) len = 127;
+ if (rr->size < 1 + len) continue;
+ if (dnsa->answer + dnsa->answerlen - rr->data < 1 + len) continue;
store_pool = POOL_PERM;
cb->text = string_copyn_taint(CUS (rr->data+1), len, GET_TAINTED);
store_pool = old_pool;
diff --git a/src/src/host.c b/src/src/host.c
index 3e5a886..c03cd53 100644
--- a/src/src/host.c
+++ b/src/src/host.c
@@ -1652,13 +1652,15 @@ while ((ordername = string_nextinlist(&list, &sep, NULL, 0)))
/* If an overlong response was received, the data will have been
truncated and dn_expand may fail. */

- if (dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen,
- US (rr->data), (DN_EXPAND_ARG4_TYPE)(s), ssize) < 0)
+ int r;
+ if ((r = dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen,
+ US (rr->data), (DN_EXPAND_ARG4_TYPE)(s), ssize)) < 0)
{
log_write(0, LOG_MAIN, "host name alias list truncated for %s",
sender_host_address);
break;
}
+ if (rr->size < r) break;

store_release_above(s + (slen = Ustrlen(s)) + 1);
if (!*s)
@@ -2725,6 +2727,8 @@ for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS);
const uschar * s = rr->data; /* MUST be unsigned for GETSHORT */
uschar data[256];

+ if (rr->size < 2) continue;
+ if (dnsa->answer + dnsa->answerlen - s < 2) continue;
GETSHORT(precedence, s); /* Pointer s is advanced */

/* For MX records, we use a random "weight" which causes multiple records of
@@ -2737,14 +2741,17 @@ for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS);
/* SRV records are specified with a port and a weight. The weight is used
in a special algorithm. However, to start with, we just use it to order the
records of equal priority (precedence). */
+ if (rr->size < 6) continue;
+ if (dnsa->answer + dnsa->answerlen - s < 4) continue;
GETSHORT(weight, s);
GETSHORT(port, s);
}

/* Get the name of the host pointed to. */

- (void)dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, s,
+ int r = dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, s,
(DN_EXPAND_ARG4_TYPE)data, sizeof(data));
+ if ((r < 0) || (r > rr->size - (s - rr->data))) continue;

/* Check that we haven't already got this host on the chain; if we have,
keep only the lower precedence. This situation shouldn't occur, but you
@@ -3160,6 +3167,7 @@ switch (rc)
uint16_t payload_length = rr->size - 3;
uschar s[MAX_TLSA_EXPANDED_SIZE], * sp = s, * p = US rr->data;

+ if (dnsa->answer + dnsa->answerlen - p < rr->size) continue;
sp += sprintf(CS sp, "%d ", *p++); /* usage */
sp += sprintf(CS sp, "%d ", *p++); /* selector */
sp += sprintf(CS sp, "%d ", *p++); /* matchtype */
diff --git a/src/src/lookups/dnsdb.c b/src/src/lookups/dnsdb.c
index 1563eda..0016f1b 100644
--- a/src/src/lookups/dnsdb.c
+++ b/src/src/lookups/dnsdb.c
@@ -394,8 +394,27 @@ while ((domain = string_nextinlist(&keystring, &sep, NULL, 0)))
if (type == T_TXT || type == T_SPF)
{
if (!outsep2) /* output only the first item of data */
+ {
+ if ((rr->size >= 1) &&
+ (dnsa->answer + dnsa->answerlen - rr->data >= 1) &&
+ (rr->size >= 1 + (rr->data)[0]) &&
+ (dnsa->answer + dnsa->answerlen - rr->data >= 1 + (rr->data)[0]))
yield = string_catn(yield, US (rr->data+1), (rr->data)[0]);
+ }
else
+ {
+ int err = 0;
+ for (unsigned data_offset = 0; data_offset < rr->size; )
+ {
+ err = 1;
+ if (dnsa->answer + dnsa->answerlen - rr->data < data_offset + 1) break;
+ uschar chunk_len = (rr->data)[data_offset++];
+ if (rr->size < data_offset + chunk_len) break;
+ if (dnsa->answer + dnsa->answerlen - rr->data < data_offset + chunk_len) break;
+ data_offset += chunk_len;
+ err = 0;
+ }
+ if (!err)
for (unsigned data_offset = 0; data_offset < rr->size; )
{
uschar chunk_len = (rr->data)[data_offset];
@@ -404,6 +423,7 @@ while ((domain = string_nextinlist(&keystring, &sep, NULL, 0)))
yield = string_catn(yield, US ((rr->data) + ++data_offset), chunk_len);
data_offset += chunk_len;
}
+ }
}
else if (type == T_TLSA)
{
@@ -413,6 +433,8 @@ while ((domain = string_nextinlist(&keystring, &sep, NULL, 0)))
uschar * sp = s;
uschar * p = US rr->data;

+ if ((dnsa->answer + dnsa->answerlen - rr->data >= rr->size) && (rr->size >= 3))
+ {
usage = *p++;
selector = *p++;
matching_type = *p++;
@@ -425,6 +447,7 @@ while ((domain = string_nextinlist(&keystring, &sep, NULL, 0)))
sp += sprintf(CS sp, "%02x", *p++);

yield = string_cat(yield, s);
+ }
}
else /* T_CNAME, T_CSA, T_MX, T_MXH, T_NS, T_PTR, T_SOA, T_SRV */
{
@@ -436,16 +459,19 @@ while ((domain = string_nextinlist(&keystring, &sep, NULL, 0)))
{
case T_MXH:
/* mxh ignores the priority number and includes only the hostnames */
+ if((rr->size < 2) || (dnsa->answer + dnsa->answerlen - rr->data < 2)) continue;
GETSHORT(priority, p);
break;

case T_MX:
+ if((rr->size < 2) || (dnsa->answer + dnsa->answerlen - rr->data < 2)) continue;
GETSHORT(priority, p);
sprintf(CS s, "%d%c", priority, *outsep2);
yield = string_cat(yield, s);
break;

case T_SRV:
+ if((rr->size < 6) || (dnsa->answer + dnsa->answerlen - rr->data < 6)) continue;
GETSHORT(priority, p);
GETSHORT(weight, p);
GETSHORT(port, p);
@@ -455,6 +481,7 @@ while ((domain = string_nextinlist(&keystring, &sep, NULL, 0)))
break;

case T_CSA:
+ if((rr->size < 6) || (dnsa->answer + dnsa->answerlen - rr->data < 6)) continue;
/* See acl_verify_csa() for more comments about CSA. */
GETSHORT(priority, p);
GETSHORT(weight, p);
@@ -501,7 +528,8 @@ while ((domain = string_nextinlist(&keystring, &sep, NULL, 0)))
"domain=%s", dns_text_type(type), domain);
break;
}
- else yield = string_cat(yield, s);
+ if (rr->size - (p - rr->data) < rc) break;
+ yield = string_cat(yield, s);

if (type == T_SOA && outsep2 != NULL)
{
@@ -518,9 +546,12 @@ while ((domain = string_nextinlist(&keystring, &sep, NULL, 0)))
"domain=%s", dns_text_type(type), domain);
break;
}
- else yield = string_cat(yield, s);
+ if (rr->size - (p - rr->data) < rc) break;
+ yield = string_cat(yield, s);

p += rc;
+ if (rr->size - (p - rr->data) < 20) break;
+ if (dnsa->answer + dnsa->answerlen - p < 20) break;
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",
diff --git a/src/src/proxy.c b/src/src/proxy.c
index fbce111..8dd7034 100644
--- a/src/src/proxy.c
+++ b/src/src/proxy.c
@@ -93,6 +93,8 @@ while (capacity > 0)
do { ret = read(fd, to, 1); } while (ret == -1 && errno == EINTR && !had_command_timeout);
if (ret == -1)
return -1;
+ if (!ret)
+ break;
have++;
if (last)
return have;
@@ -254,6 +256,8 @@ if ((ret == PROXY_INITIAL_READ) && (memcmp(&hdr.v2, v2sig, sizeof(v2sig)) == 0))
goto proxyfail;
}

+ if (ret < 16)
+ goto proxyfail;
/* The v2 header will always be 16 bytes per the spec. */
size = 16 + ntohs(hdr.v2.len);
DEBUG(D_receive) debug_printf("Detected PROXYv2 header, size %d (limit %d)\n",
@@ -274,7 +278,7 @@ if ((ret == PROXY_INITIAL_READ) && (memcmp(&hdr.v2, v2sig, sizeof(v2sig)) == 0))
{
retmore = read(fd, (uschar*)&hdr + ret, size-ret);
} while (retmore == -1 && errno == EINTR && !had_command_timeout);
- if (retmore == -1)
+ if (retmore < 1)
goto proxyfail;
DEBUG(D_receive) proxy_debug(US &hdr, ret, ret + retmore);
ret += retmore;
@@ -297,6 +301,8 @@ if (ret >= 16 && memcmp(&hdr.v2, v2sig, 12) == 0)
switch (hdr.v2.fam)
{
case 0x11: /* TCPv4 address type */
+ if (ret < 28)
+ goto proxyfail;
iptype = US"IPv4";
tmpaddr.sin_addr.s_addr = hdr.v2.addr.ip4.src_addr;
inet_ntop(AF_INET, &tmpaddr.sin_addr, CS &tmpip, sizeof(tmpip));
@@ -323,6 +329,8 @@ if (ret >= 16 && memcmp(&hdr.v2, v2sig, 12) == 0)
proxy_external_port = tmpport;
goto done;
case 0x21: /* TCPv6 address type */
+ if (ret < 52)
+ goto proxyfail;
iptype = US"IPv6";
memmove(tmpaddr6.sin6_addr.s6_addr, hdr.v2.addr.ip6.src_addr, 16);
inet_ntop(AF_INET6, &tmpaddr6.sin6_addr, CS &tmpip6, sizeof(tmpip6));
@@ -381,10 +389,13 @@ else if (ret >= 8 && memcmp(hdr.v1.line, "PROXY", 5) == 0)
goto proxyfail;
ret += r2;

+ if(ret > 107)
+ goto proxyfail;
+ hdr.v1.line[ret] = 0;
p = string_copy(hdr.v1.line);
end = memchr(p, '\r', ret - 1);

- if (!end || (end == (uschar*)&hdr + ret) || end[1] != '\n')
+ if (!end || end[1] != '\n')
{
DEBUG(D_receive) debug_printf("Partial or invalid PROXY header\n");
goto proxyfail;
diff --git a/src/src/spf.c b/src/src/spf.c
index 3d83f07..908005f 100644
--- a/src/src/spf.c
+++ b/src/src/spf.c
@@ -121,12 +121,15 @@ for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
switch(rr_type)
{
case T_MX:
+ if (rr->size < 2) continue;
+ if (dnsa->answer + dnsa->answerlen - s < 2) continue;
s += 2; /* skip the MX precedence field */
case T_PTR:
{
uschar * buf = store_malloc(256);
- (void)dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, s,
+ int r = dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, s,
(DN_EXPAND_ARG4_TYPE)buf, 256);
+ if ((r < 0) || (rr->size < r)) continue;
s = buf;
break;
}
@@ -136,6 +139,12 @@ for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
gstring * g = NULL;
uschar chunk_len;

+ if (rr->size < 1) continue;
+ if (dnsa->answer + dnsa->answerlen - rr->data < 1) continue;
+ chunk_len = rr->data[0];
+ if (chunk_len < 6) continue;
+ if (rr->size < 1 + chunk_len) continue;
+ if (dnsa->answer + dnsa->answerlen - rr->data < 1 + chunk_len) continue;
if (strncmpic(rr->data+1, US SPF_VER_STR, 6) != 0)
{
HDEBUG(D_host_lookup) debug_printf("not an spf record: %.*s\n",
@@ -143,6 +152,15 @@ for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
continue;
}

+ int err = 1;
+ if (dnsa->answer + dnsa->answerlen - s >= rr->size)
+ for (int off = 0; off < rr->size; off += chunk_len)
+ {
+ if (!(chunk_len = s[off++])) { err = 0; break; }
+ if (rr->size - off < chunk_len) break;
+ g = string_catn(g, s+off, chunk_len);
+ }
+ if (!err)
for (int off = 0; off < rr->size; off += chunk_len)
{
if (!(chunk_len = s[off++])) break;
@@ -161,7 +179,9 @@ for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
default:
{
uschar * buf = store_malloc(dnsa->answerlen + 1);
- s = memcpy(buf, s, dnsa->answerlen + 1);
+ size_t l = dnsa->answer + dnsa->answerlen - s;
+ s = memcpy(buf, s, l);
+ memset(s + l, 0, dnsa->answerlen + 1 - l);
break;
}
}
diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c
index e706b63..2832345 100644
--- a/src/src/tls-gnu.c
+++ b/src/src/tls-gnu.c
@@ -3264,6 +3264,7 @@ for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
) if (rr->type == T_TLSA && rr->size > 3)
{
const uschar * p = rr->data;
+ if(dnsa->answer + dnsa->answerlen - rr->data < rr->size) continue;
/*XXX need somehow to mark rr and its data as tainted. Doues this mean copying it? */
uint8_t usage = p[0], sel = p[1], type = p[2];

diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c
index 0162256..81e006d 100644
--- a/src/src/tls-openssl.c
+++ b/src/src/tls-openssl.c
@@ -3886,6 +3886,7 @@ for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
uint8_t usage, selector, mtype;
const char * mdname;

+ if((rr->size < 3) || (dnsa->answer + dnsa->answerlen - rr->data < rr->size)) continue;
usage = *p++;

/* Only DANE-TA(2) and DANE-EE(3) are supported */

--
## subscription configuration (requires account):
## https://lists.exim.org/mailman3/postorius/lists/exim-dev.lists.exim.org/
## unsubscribe (doesn't require an account):
## exim-dev-unsubscribe@lists.exim.org
## Exim details at http://www.exim.org/
## Please use the Wiki with this list - http://wiki.exim.org/