Commit 0549c73b authored by Simon Kelley's avatar Simon Kelley

Security fix, CVE-2017-14491 DNS heap buffer overflow.

Fix heap overflow in DNS code. This is a potentially serious
security hole. It allows an attacker who can make DNS
requests to dnsmasq, and who controls the contents of
a domain, which is thereby queried, to overflow
(by 2 bytes) a heap buffer and either crash, or
even take control of, dnsmasq.
parent b697fbb7
......@@ -24,6 +24,18 @@ version 2.78
Juan Manuel Fernandez and Kevin Darbyshire-Bryant for
chasing this one down. CVE-2017-13704 applies.
Fix heap overflow in DNS code. This is a potentially serious
security hole. It allows an attacker who can make DNS
requests to dnsmasq, and who controls the contents of
a domain, which is thereby queried, to overflow
(by 2 bytes) a heap buffer and either crash, or
even take control of, dnsmasq.
CVE-2017-14491 applies.
Credit to Felix Wilhelm, Fermin J. Serna, Gabriel Campana
and Kevin Hamacher of the Google Security Team for
finding this.
version 2.77
Generate an error when configured with a CNAME loop,
......
......@@ -1185,7 +1185,7 @@ u32 rand32(void);
u64 rand64(void);
int legal_hostname(char *name);
char *canonicalise(char *in, int *nomem);
unsigned char *do_rfc1035_name(unsigned char *p, char *sval);
unsigned char *do_rfc1035_name(unsigned char *p, char *sval, char *limit);
void *safe_malloc(size_t size);
void safe_pipe(int *fd, int read_noblock);
void *whine_malloc(size_t size);
......
......@@ -2230,7 +2230,7 @@ size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char
p = (unsigned char *)(header+1);
p = do_rfc1035_name(p, name);
p = do_rfc1035_name(p, name, NULL);
*p++ = 0;
PUTSHORT(type, p);
PUTSHORT(class, p);
......
......@@ -1415,7 +1415,7 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags)
}
p = newp;
end = do_rfc1035_name(p + len, dom);
end = do_rfc1035_name(p + len, dom, NULL);
*end++ = 0;
len = end - p;
free(dom);
......
......@@ -1062,6 +1062,7 @@ int check_for_ignored_address(struct dns_header *header, size_t qlen, struct bog
return 0;
}
int add_resource_record(struct dns_header *header, char *limit, int *truncp, int nameoffset, unsigned char **pp,
unsigned long ttl, int *offset, unsigned short type, unsigned short class, char *format, ...)
{
......@@ -1071,12 +1072,21 @@ int add_resource_record(struct dns_header *header, char *limit, int *truncp, int
unsigned short usval;
long lval;
char *sval;
#define CHECK_LIMIT(size) \
if (limit && p + (size) > (unsigned char*)limit) \
{ \
va_end(ap); \
goto truncated; \
}
if (truncp && *truncp)
return 0;
va_start(ap, format); /* make ap point to 1st unamed argument */
/* nameoffset (1 or 2) + type (2) + class (2) + ttl (4) + 0 (2) */
CHECK_LIMIT(12);
if (nameoffset > 0)
{
PUTSHORT(nameoffset | 0xc000, p);
......@@ -1085,7 +1095,13 @@ int add_resource_record(struct dns_header *header, char *limit, int *truncp, int
{
char *name = va_arg(ap, char *);
if (name)
p = do_rfc1035_name(p, name);
p = do_rfc1035_name(p, name, limit);
if (!p)
{
va_end(ap);
goto truncated;
}
if (nameoffset < 0)
{
PUTSHORT(-nameoffset | 0xc000, p);
......@@ -1106,6 +1122,7 @@ int add_resource_record(struct dns_header *header, char *limit, int *truncp, int
{
#ifdef HAVE_IPV6
case '6':
CHECK_LIMIT(IN6ADDRSZ);
sval = va_arg(ap, char *);
memcpy(p, sval, IN6ADDRSZ);
p += IN6ADDRSZ;
......@@ -1113,36 +1130,47 @@ int add_resource_record(struct dns_header *header, char *limit, int *truncp, int
#endif
case '4':
CHECK_LIMIT(INADDRSZ);
sval = va_arg(ap, char *);
memcpy(p, sval, INADDRSZ);
p += INADDRSZ;
break;
case 'b':
CHECK_LIMIT(1);
usval = va_arg(ap, int);
*p++ = usval;
break;
case 's':
CHECK_LIMIT(2);
usval = va_arg(ap, int);
PUTSHORT(usval, p);
break;
case 'l':
CHECK_LIMIT(4);
lval = va_arg(ap, long);
PUTLONG(lval, p);
break;
case 'd':
/* get domain-name answer arg and store it in RDATA field */
if (offset)
*offset = p - (unsigned char *)header;
p = do_rfc1035_name(p, va_arg(ap, char *));
*p++ = 0;
/* get domain-name answer arg and store it in RDATA field */
if (offset)
*offset = p - (unsigned char *)header;
p = do_rfc1035_name(p, va_arg(ap, char *), limit);
if (!p)
{
va_end(ap);
goto truncated;
}
CHECK_LIMIT(1);
*p++ = 0;
break;
case 't':
usval = va_arg(ap, int);
CHECK_LIMIT(usval);
sval = va_arg(ap, char *);
if (usval != 0)
memcpy(p, sval, usval);
......@@ -1154,20 +1182,24 @@ int add_resource_record(struct dns_header *header, char *limit, int *truncp, int
usval = sval ? strlen(sval) : 0;
if (usval > 255)
usval = 255;
CHECK_LIMIT(usval + 1);
*p++ = (unsigned char)usval;
memcpy(p, sval, usval);
p += usval;
break;
}
#undef CHECK_LIMIT
va_end(ap); /* clean up variable argument pointer */
j = p - sav - 2;
PUTSHORT(j, sav); /* Now, store real RDLength */
/* this has already been checked against limit before */
PUTSHORT(j, sav); /* Now, store real RDLength */
/* check for overflow of buffer */
if (limit && ((unsigned char *)limit - p) < 0)
{
truncated:
if (truncp)
*truncp = 1;
return 0;
......
......@@ -2452,10 +2452,10 @@ static void do_options(struct dhcp_context *context,
if (fqdn_flags & 0x04)
{
p = do_rfc1035_name(p, hostname);
p = do_rfc1035_name(p, hostname, NULL);
if (domain)
{
p = do_rfc1035_name(p, domain);
p = do_rfc1035_name(p, domain, NULL);
*p++ = 0;
}
}
......
......@@ -1479,10 +1479,10 @@ static struct dhcp_netid *add_options(struct state *state, int do_refresh)
if ((p = expand(len + 2)))
{
*(p++) = state->fqdn_flags;
p = do_rfc1035_name(p, state->hostname);
p = do_rfc1035_name(p, state->hostname, NULL);
if (state->send_domain)
{
p = do_rfc1035_name(p, state->send_domain);
p = do_rfc1035_name(p, state->send_domain, NULL);
*p = 0;
}
}
......
......@@ -240,15 +240,20 @@ char *canonicalise(char *in, int *nomem)
return ret;
}
unsigned char *do_rfc1035_name(unsigned char *p, char *sval)
unsigned char *do_rfc1035_name(unsigned char *p, char *sval, char *limit)
{
int j;
while (sval && *sval)
{
if (limit && p + 1 > (unsigned char*)limit)
return p;
unsigned char *cp = p++;
for (j = 0; *sval && (*sval != '.'); sval++, j++)
{
if (limit && p + 1 > (unsigned char*)limit)
return p;
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && *sval == NAME_ESCAPE)
*p++ = (*(++sval))-1;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment