summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSeonah Moon <seonah1.moon@samsung.com>2017-10-16 18:27:03 +0900
committerSeonah Moon <seonah1.moon@samsung.com>2018-01-25 17:59:13 +0900
commitc89c363213d85f0916451cddacf359be0400dcbb (patch)
treeb858ccdd39039b04eaa1e3a439536a36750c74fb
parent883c52234c45d62c130ccd518e422f9c9625f296 (diff)
downloaddnsmasq-c89c363213d85f0916451cddacf359be0400dcbb.tar.gz
dnsmasq-c89c363213d85f0916451cddacf359be0400dcbb.tar.bz2
dnsmasq-c89c363213d85f0916451cddacf359be0400dcbb.zip
Security fix, CVE-2017-14491, DNS heap buffer overflow.(1)
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. http://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=commit;h=0549c73b7ea6b22a3c49beb4d432f185a81efcbc Change-Id: I3cc432632f51e89b888f3a5d999ba422c134847a Signed-off-by: Seonah Moon <seonah1.moon@samsung.com>
-rw-r--r--src/dnsmasq.h2
-rw-r--r--src/dnssec.c2
-rw-r--r--src/option.c2
-rw-r--r--src/rfc1035.c51
-rw-r--r--src/rfc2131.c4
-rw-r--r--src/rfc3315.c4
-rw-r--r--src/util.c7
7 files changed, 54 insertions, 18 deletions
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index cf1a782..ef15e6d 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -1169,7 +1169,7 @@ u32 rand32(void);
u64 rand64(void);
int legal_hostname(char *c);
char *canonicalise(char *s, 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);
diff --git a/src/dnssec.c b/src/dnssec.c
index 4deda24..830f304 100644
--- a/src/dnssec.c
+++ b/src/dnssec.c
@@ -2264,7 +2264,7 @@ size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, i
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);
diff --git a/src/option.c b/src/option.c
index ecc2619..ed204fb 100644
--- a/src/option.c
+++ b/src/option.c
@@ -1348,7 +1348,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);
diff --git a/src/rfc1035.c b/src/rfc1035.c
index 56647b0..605196a 100644
--- a/src/rfc1035.c
+++ b/src/rfc1035.c
@@ -1371,12 +1371,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);
@@ -1384,8 +1393,14 @@ int add_resource_record(struct dns_header *header, char *limit, int *truncp, int
else
{
char *name = va_arg(ap, char *);
- if (name)
- p = do_rfc1035_name(p, name);
+ if (name)
+ p = do_rfc1035_name(p, name, limit);
+ if (!p)
+ {
+ va_end(ap);
+ goto truncated;
+ }
+
if (nameoffset < 0)
{
PUTSHORT(-nameoffset | 0xc000, p);
@@ -1406,6 +1421,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;
@@ -1413,36 +1429,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);
@@ -1454,20 +1481,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;
+ /* 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;
diff --git a/src/rfc2131.c b/src/rfc2131.c
index 9f69ed5..bcfa5d6 100644
--- a/src/rfc2131.c
+++ b/src/rfc2131.c
@@ -2352,10 +2352,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;
}
}
diff --git a/src/rfc3315.c b/src/rfc3315.c
index 2665d0d..62818d4 100644
--- a/src/rfc3315.c
+++ b/src/rfc3315.c
@@ -1472,10 +1472,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;
}
}
diff --git a/src/util.c b/src/util.c
index 469eaed..66fa103 100644
--- a/src/util.c
+++ b/src/util.c
@@ -218,15 +218,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;