1 From 8868a04895b27d42d42e364f1a0c0196c1505b04 Mon Sep 17 00:00:00 2001
2 From: Simon Kelley <simon@thekelleys.org.uk>
3 Date: Mon, 25 Sep 2017 18:17:11 +0100
4 Subject: [PATCH 1/9] Security fix, CVE-2017-14491 DNS heap buffer
7 Fix heap overflow in DNS code. This is a potentially serious
8 security hole. It allows an attacker who can make DNS
9 requests to dnsmasq, and who controls the contents of
10 a domain, which is thereby queried, to overflow
11 (by 2 bytes) a heap buffer and either crash, or
12 even take control of, dnsmasq.
17 src/rfc1035.c | 50 +++++++++++++++++++++++++++++++++++++++++---------
18 src/rfc2131.c | 4 ++--
19 src/rfc3315.c | 4 ++--
20 src/util.c | 7 ++++++-
21 7 files changed, 54 insertions(+), 17 deletions(-)
23 diff --git a/src/dnsmasq.h b/src/dnsmasq.h
24 index 1179492..06e5579 100644
27 @@ -1162,7 +1162,7 @@ u32 rand32(void);
29 int legal_hostname(char *c);
30 char *canonicalise(char *s, int *nomem);
31 -unsigned char *do_rfc1035_name(unsigned char *p, char *sval);
32 +unsigned char *do_rfc1035_name(unsigned char *p, char *sval, char *limit);
33 void *safe_malloc(size_t size);
34 void safe_pipe(int *fd, int read_noblock);
35 void *whine_malloc(size_t size);
36 diff --git a/src/dnssec.c b/src/dnssec.c
37 index 3c77c7d..f45c804 100644
40 @@ -2227,7 +2227,7 @@ size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char
42 p = (unsigned char *)(header+1);
44 - p = do_rfc1035_name(p, name);
45 + p = do_rfc1035_name(p, name, NULL);
49 diff --git a/src/option.c b/src/option.c
50 index eb78b1a..3469f53 100644
53 @@ -1378,7 +1378,7 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags)
57 - end = do_rfc1035_name(p + len, dom);
58 + end = do_rfc1035_name(p + len, dom, NULL);
62 diff --git a/src/rfc1035.c b/src/rfc1035.c
63 index 24d08c1..78410d6 100644
66 @@ -1049,6 +1049,7 @@ int check_for_ignored_address(struct dns_header *header, size_t qlen, struct bog
71 int add_resource_record(struct dns_header *header, char *limit, int *truncp, int nameoffset, unsigned char **pp,
72 unsigned long ttl, int *offset, unsigned short type, unsigned short class, char *format, ...)
74 @@ -1058,12 +1059,21 @@ int add_resource_record(struct dns_header *header, char *limit, int *truncp, int
78 +#define CHECK_LIMIT(size) \
79 + if (limit && p + (size) > (unsigned char*)limit) \
85 if (truncp && *truncp)
89 va_start(ap, format); /* make ap point to 1st unamed argument */
92 + /* nameoffset (1 or 2) + type (2) + class (2) + ttl (4) + 0 (2) */
97 PUTSHORT(nameoffset | 0xc000, p);
98 @@ -1072,7 +1082,13 @@ int add_resource_record(struct dns_header *header, char *limit, int *truncp, int
100 char *name = va_arg(ap, char *);
102 - p = do_rfc1035_name(p, name);
103 + p = do_rfc1035_name(p, name, limit);
112 PUTSHORT(-nameoffset | 0xc000, p);
113 @@ -1093,6 +1109,7 @@ int add_resource_record(struct dns_header *header, char *limit, int *truncp, int
117 + CHECK_LIMIT(IN6ADDRSZ);
118 sval = va_arg(ap, char *);
119 memcpy(p, sval, IN6ADDRSZ);
121 @@ -1100,36 +1117,47 @@ int add_resource_record(struct dns_header *header, char *limit, int *truncp, int
125 + CHECK_LIMIT(INADDRSZ);
126 sval = va_arg(ap, char *);
127 memcpy(p, sval, INADDRSZ);
133 usval = va_arg(ap, int);
139 usval = va_arg(ap, int);
145 lval = va_arg(ap, long);
150 - /* get domain-name answer arg and store it in RDATA field */
152 - *offset = p - (unsigned char *)header;
153 - p = do_rfc1035_name(p, va_arg(ap, char *));
155 + /* get domain-name answer arg and store it in RDATA field */
157 + *offset = p - (unsigned char *)header;
158 + p = do_rfc1035_name(p, va_arg(ap, char *), limit);
169 usval = va_arg(ap, int);
170 + CHECK_LIMIT(usval);
171 sval = va_arg(ap, char *);
173 memcpy(p, sval, usval);
174 @@ -1141,20 +1169,24 @@ int add_resource_record(struct dns_header *header, char *limit, int *truncp, int
175 usval = sval ? strlen(sval) : 0;
178 + CHECK_LIMIT(usval + 1);
179 *p++ = (unsigned char)usval;
180 memcpy(p, sval, usval);
186 va_end(ap); /* clean up variable argument pointer */
189 - PUTSHORT(j, sav); /* Now, store real RDLength */
190 + /* this has already been checked against limit before */
191 + PUTSHORT(j, sav); /* Now, store real RDLength */
193 /* check for overflow of buffer */
194 if (limit && ((unsigned char *)limit - p) < 0)
200 diff --git a/src/rfc2131.c b/src/rfc2131.c
201 index 8b99d4b..75893a6 100644
204 @@ -2420,10 +2420,10 @@ static void do_options(struct dhcp_context *context,
206 if (fqdn_flags & 0x04)
208 - p = do_rfc1035_name(p, hostname);
209 + p = do_rfc1035_name(p, hostname, NULL);
212 - p = do_rfc1035_name(p, domain);
213 + p = do_rfc1035_name(p, domain, NULL);
217 diff --git a/src/rfc3315.c b/src/rfc3315.c
218 index 3f4d69c..73bdee4 100644
221 @@ -1472,10 +1472,10 @@ static struct dhcp_netid *add_options(struct state *state, int do_refresh)
222 if ((p = expand(len + 2)))
224 *(p++) = state->fqdn_flags;
225 - p = do_rfc1035_name(p, state->hostname);
226 + p = do_rfc1035_name(p, state->hostname, NULL);
227 if (state->send_domain)
229 - p = do_rfc1035_name(p, state->send_domain);
230 + p = do_rfc1035_name(p, state->send_domain, NULL);
234 diff --git a/src/util.c b/src/util.c
235 index 1a9f228..be9f8a6 100644
238 @@ -218,15 +218,20 @@ char *canonicalise(char *in, int *nomem)
242 -unsigned char *do_rfc1035_name(unsigned char *p, char *sval)
243 +unsigned char *do_rfc1035_name(unsigned char *p, char *sval, char *limit)
247 while (sval && *sval)
249 + if (limit && p + 1 > (unsigned char*)limit)
252 unsigned char *cp = p++;
253 for (j = 0; *sval && (*sval != '.'); sval++, j++)
255 + if (limit && p + 1 > (unsigned char*)limit)
258 if (option_bool(OPT_DNSSEC_VALID) && *sval == NAME_ESCAPE)
259 *p++ = (*(++sval))-1;