aboutsummaryrefslogtreecommitdiffstats
path: root/main/dnsmasq/CVE-2017-14491.patch
diff options
context:
space:
mode:
Diffstat (limited to 'main/dnsmasq/CVE-2017-14491.patch')
-rw-r--r--main/dnsmasq/CVE-2017-14491.patch263
1 files changed, 263 insertions, 0 deletions
diff --git a/main/dnsmasq/CVE-2017-14491.patch b/main/dnsmasq/CVE-2017-14491.patch
new file mode 100644
index 0000000000..9b62e903e0
--- /dev/null
+++ b/main/dnsmasq/CVE-2017-14491.patch
@@ -0,0 +1,263 @@
+From 445282d13b90712ef90d2c2141d0e19bb1d896d2 Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Mon, 25 Sep 2017 18:17:11 +0100
+Subject: [PATCH] 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.
+
+(original commit 0549c73b7ea6b22a3c49beb4d432f185a81efcbc)
+---
+ src/dnsmasq.h | 2 +-
+ src/dnssec.c | 2 +-
+ src/option.c | 2 +-
+ src/rfc1035.c | 50 +++++++++++++++++++++++++++++++++++++++++---------
+ src/rfc2131.c | 4 ++--
+ src/rfc3315.c | 4 ++--
+ src/util.c | 7 ++++++-
+ 7 files changed, 54 insertions(+), 17 deletions(-)
+
+diff --git a/src/dnsmasq.h b/src/dnsmasq.h
+index 06fae35..7a18898 100644
+--- a/src/dnsmasq.h
++++ b/src/dnsmasq.h
+@@ -1179,7 +1179,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 3330eef..b6cb55f 100644
+--- a/src/dnssec.c
++++ b/src/dnssec.c
+@@ -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);
+diff --git a/src/option.c b/src/option.c
+index 064ef62..22bd19a 100644
+--- a/src/option.c
++++ b/src/option.c
+@@ -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);
+diff --git a/src/rfc1035.c b/src/rfc1035.c
+index 1671883..3397a26 100644
+--- a/src/rfc1035.c
++++ b/src/rfc1035.c
+@@ -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;
+diff --git a/src/rfc2131.c b/src/rfc2131.c
+index a679470..052498c 100644
+--- a/src/rfc2131.c
++++ b/src/rfc2131.c
+@@ -2454,10 +2454,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 054ecd0..715b6db 100644
+--- a/src/rfc3315.c
++++ b/src/rfc3315.c
+@@ -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;
+ }
+ }
+diff --git a/src/util.c b/src/util.c
+index 145e53a..471faa9 100644
+--- a/src/util.c
++++ b/src/util.c
+@@ -227,15 +227,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;
+--
+2.9.5
+