Browse code

dnsmasq: fix multiple CVEs

Reported by Google
* CVE-2017-14491: DNS - 2 byte heap based overflow
* CVE-2017-14492: DHCP - heap based overflow
* CVE-2017-14493: DHCP - stack based overflow
* CVE-2017-14494: DHCP - info leak
* CVE-2017-14495: DNS - OOM DoS
* CVE-2017-14496: DNS - DoS Integer underflow

Change-Id: I138e34b35e9cd9e21d74c2088d6ecb411ab124d0
Reviewed-on: http://photon-jenkins.eng.vmware.com:8082/3884
Reviewed-by: Anish Swaminathan <anishs@vmware.com>
Tested-by: Anish Swaminathan <anishs@vmware.com>

Alexey Makhalov authored on 2017/09/28 03:10:55
Showing 2 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,358 @@
0
+diff -Naur dnsmasq-2.76.orig/src/dnsmasq.h dnsmasq-2.76/src/dnsmasq.h
1
+--- dnsmasq-2.76.orig/src/dnsmasq.h	2016-05-18 07:51:54.000000000 -0700
2
+@@ -1161,7 +1161,7 @@
3
+ u64 rand64(void);
4
+ int legal_hostname(char *c);
5
+ char *canonicalise(char *s, int *nomem);
6
+-unsigned char *do_rfc1035_name(unsigned char *p, char *sval);
7
++unsigned char *do_rfc1035_name(unsigned char *p, char *sval, char *limit);
8
+ void *safe_malloc(size_t size);
9
+ void safe_pipe(int *fd, int read_noblock);
10
+ void *whine_malloc(size_t size);
11
+diff -Naur dnsmasq-2.76.orig/src/dnssec.c dnsmasq-2.76/src/dnssec.c
12
+--- dnsmasq-2.76.orig/src/dnssec.c	2016-05-18 07:51:54.000000000 -0700
13
+@@ -2227,7 +2227,7 @@
14
+ 
15
+   p = (unsigned char *)(header+1);
16
+ 	
17
+-  p = do_rfc1035_name(p, name);
18
++  p = do_rfc1035_name(p, name, NULL);
19
+   *p++ = 0;
20
+   PUTSHORT(type, p);
21
+   PUTSHORT(class, p);
22
+diff -Naur dnsmasq-2.76.orig/src/edns0.c dnsmasq-2.76/src/edns0.c
23
+--- dnsmasq-2.76.orig/src/edns0.c	2016-05-18 07:51:54.000000000 -0700
24
+@@ -144,7 +144,7 @@
25
+ 	  GETSHORT(len, p);
26
+ 	  
27
+ 	  /* malformed option, delete the whole OPT RR and start again. */
28
+-	  if (i + len > rdlen)
29
++	  if (i + 4 + len > rdlen)
30
+ 	    {
31
+ 	      rdlen = 0;
32
+ 	      is_last = 0;
33
+@@ -159,7 +159,7 @@
34
+ 	      /* delete option if we're to replace it. */
35
+ 	      p -= 4;
36
+ 	      rdlen -= len + 4;
37
+-	      memcpy(p, p+len+4, rdlen - i);
38
++	      memmove(p, p+len+4, rdlen - i);
39
+ 	      PUTSHORT(rdlen, lenp);
40
+ 	      lenp -= 2;
41
+ 	    }
42
+@@ -192,7 +192,15 @@
43
+ 	  !(p = skip_section(p, 
44
+ 			     ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount), 
45
+ 			     header, plen)))
46
++      {
47
++	free(buff);
48
+ 	return plen;
49
++      }
50
++      if (p + 11 > limit)
51
++      {
52
++        free(buff);
53
++        return plen; /* Too big */
54
++      }
55
+       *p++ = 0; /* empty name */
56
+       PUTSHORT(T_OPT, p);
57
+       PUTSHORT(udp_sz, p); /* max packet length, 512 if not given in EDNS0 header */
58
+@@ -204,6 +212,11 @@
59
+       /* Copy back any options */
60
+       if (buff)
61
+ 	{
62
++          if (p + rdlen > limit)
63
++          {
64
++            free(buff);
65
++            return plen; /* Too big */
66
++          }
67
+ 	  memcpy(p, buff, rdlen);
68
+ 	  free(buff);
69
+ 	  p += rdlen;
70
+@@ -217,8 +230,12 @@
71
+   /* Add new option */
72
+   if (optno != 0 && replace != 2)
73
+     {
74
++      if (p + 4 > limit)
75
++       return plen; /* Too big */
76
+       PUTSHORT(optno, p);
77
+       PUTSHORT(optlen, p);
78
++      if (p + optlen > limit)
79
++       return plen; /* Too big */
80
+       memcpy(p, opt, optlen);
81
+       p += optlen;  
82
+       PUTSHORT(p - datap, lenp);
83
+diff -Naur dnsmasq-2.76.orig/src/forward.c dnsmasq-2.76/src/forward.c
84
+--- dnsmasq-2.76.orig/src/forward.c	2016-05-18 07:51:54.000000000 -0700
85
+@@ -1410,6 +1410,10 @@
86
+ 	udp_size = daemon->edns_pktsz;
87
+     }
88
+ 
89
++  // Make sure the udp size is not smaller than the incoming message so that we
90
++  // do not underflow
91
++  if (udp_size < n) udp_size = n;
92
++
93
+ #ifdef HAVE_AUTH
94
+   if (auth_dns)
95
+     {
96
+diff -Naur dnsmasq-2.76.orig/src/option.c dnsmasq-2.76/src/option.c
97
+--- dnsmasq-2.76.orig/src/option.c	2016-05-18 07:51:54.000000000 -0700
98
+@@ -1378,7 +1378,7 @@
99
+ 		    }
100
+ 		  
101
+ 		  p = newp;
102
+-		  end = do_rfc1035_name(p + len, dom);
103
++		  end = do_rfc1035_name(p + len, dom, NULL);
104
+ 		  *end++ = 0;
105
+ 		  len = end - p;
106
+ 		  free(dom);
107
+diff -Naur dnsmasq-2.76.orig/src/radv.c dnsmasq-2.76/src/radv.c
108
+--- dnsmasq-2.76.orig/src/radv.c	2016-05-18 07:51:54.000000000 -0700
109
+@@ -198,6 +198,9 @@
110
+       /* look for link-layer address option for logging */
111
+       if (sz >= 16 && packet[8] == ICMP6_OPT_SOURCE_MAC && (packet[9] * 8) + 8 <= sz)
112
+ 	{
113
++	  if ((packet[9] * 8 - 2) * 3 - 1 >= MAXDNAME) {
114
++	    return;
115
++	  }
116
+ 	  print_mac(daemon->namebuff, &packet[10], (packet[9] * 8) - 2);
117
+ 	  mac = daemon->namebuff;
118
+ 	}
119
+diff -Naur dnsmasq-2.76.orig/src/rfc1035.c dnsmasq-2.76/src/rfc1035.c
120
+--- dnsmasq-2.76.orig/src/rfc1035.c	2016-05-18 07:51:54.000000000 -0700
121
+@@ -37,7 +37,7 @@
122
+ 	/* end marker */
123
+ 	{
124
+ 	  /* check that there are the correct no of bytes after the name */
125
+-	  if (!CHECK_LEN(header, p, plen, extrabytes))
126
++	  if (!CHECK_LEN(header, p1 ? p1 : p, plen, extrabytes))
127
+ 	    return 0;
128
+ 	  
129
+ 	  if (isExtract)
130
+@@ -485,6 +485,8 @@
131
+ 	    {
132
+ 	      unsigned int i, len = *p1;
133
+ 	      unsigned char *p2 = p1;
134
++	      if ((p1 + len - p) >= rdlen)
135
++	        return 0; /* bad packet */
136
+ 	      /* make counted string zero-term  and sanitise */
137
+ 	      for (i = 0; i < len; i++)
138
+ 		{
139
+@@ -1058,12 +1060,21 @@
140
+   unsigned short usval;
141
+   long lval;
142
+   char *sval;
143
++#define CHECK_LIMIT(size) \
144
++  if (limit && p + (size) > (unsigned char*)limit) \
145
++    { \
146
++      va_end(ap); \
147
++      goto truncated; \
148
++    }
149
+ 
150
+   if (truncp && *truncp)
151
+     return 0;
152
+- 
153
++
154
+   va_start(ap, format);   /* make ap point to 1st unamed argument */
155
+-  
156
++
157
++  /* nameoffset (1 or 2) + type (2) + class (2) + ttl (4) + 0 (2) */
158
++  CHECK_LIMIT(12);
159
++
160
+   if (nameoffset > 0)
161
+     {
162
+       PUTSHORT(nameoffset | 0xc000, p);
163
+@@ -1072,7 +1083,13 @@
164
+     {
165
+       char *name = va_arg(ap, char *);
166
+       if (name)
167
+-	p = do_rfc1035_name(p, name);
168
++	p = do_rfc1035_name(p, name, limit);
169
++        if (!p)
170
++          {
171
++            va_end(ap);
172
++            goto truncated;
173
++          }
174
++
175
+       if (nameoffset < 0)
176
+ 	{
177
+ 	  PUTSHORT(-nameoffset | 0xc000, p);
178
+@@ -1093,6 +1110,7 @@
179
+       {
180
+ #ifdef HAVE_IPV6
181
+       case '6':
182
++        CHECK_LIMIT(IN6ADDRSZ);
183
+ 	sval = va_arg(ap, char *); 
184
+ 	memcpy(p, sval, IN6ADDRSZ);
185
+ 	p += IN6ADDRSZ;
186
+@@ -1100,36 +1118,47 @@
187
+ #endif
188
+ 	
189
+       case '4':
190
++        CHECK_LIMIT(INADDRSZ);
191
+ 	sval = va_arg(ap, char *); 
192
+ 	memcpy(p, sval, INADDRSZ);
193
+ 	p += INADDRSZ;
194
+ 	break;
195
+ 	
196
+       case 'b':
197
++        CHECK_LIMIT(1);
198
+ 	usval = va_arg(ap, int);
199
+ 	*p++ = usval;
200
+ 	break;
201
+ 	
202
+       case 's':
203
++        CHECK_LIMIT(2);
204
+ 	usval = va_arg(ap, int);
205
+ 	PUTSHORT(usval, p);
206
+ 	break;
207
+ 	
208
+       case 'l':
209
++        CHECK_LIMIT(4);
210
+ 	lval = va_arg(ap, long);
211
+ 	PUTLONG(lval, p);
212
+ 	break;
213
+ 	
214
+       case 'd':
215
+-	/* get domain-name answer arg and store it in RDATA field */
216
+-	if (offset)
217
+-	  *offset = p - (unsigned char *)header;
218
+-	p = do_rfc1035_name(p, va_arg(ap, char *));
219
+-	*p++ = 0;
220
++        /* get domain-name answer arg and store it in RDATA field */
221
++        if (offset)
222
++          *offset = p - (unsigned char *)header;
223
++        p = do_rfc1035_name(p, va_arg(ap, char *), limit);
224
++        if (!p)
225
++          {
226
++            va_end(ap);
227
++            goto truncated;
228
++          }
229
++        CHECK_LIMIT(1);
230
++        *p++ = 0;
231
+ 	break;
232
+ 	
233
+       case 't':
234
+ 	usval = va_arg(ap, int);
235
++        CHECK_LIMIT(usval);
236
+ 	sval = va_arg(ap, char *);
237
+ 	if (usval != 0)
238
+ 	  memcpy(p, sval, usval);
239
+@@ -1141,20 +1170,24 @@
240
+ 	usval = sval ? strlen(sval) : 0;
241
+ 	if (usval > 255)
242
+ 	  usval = 255;
243
++        CHECK_LIMIT(usval + 1);
244
+ 	*p++ = (unsigned char)usval;
245
+ 	memcpy(p, sval, usval);
246
+ 	p += usval;
247
+ 	break;
248
+       }
249
+ 
250
++#undef CHECK_LIMIT
251
+   va_end(ap);	/* clean up variable argument pointer */
252
+   
253
+   j = p - sav - 2;
254
+-  PUTSHORT(j, sav);     /* Now, store real RDLength */
255
++ /* this has already been checked against limit before */
256
++ PUTSHORT(j, sav);     /* Now, store real RDLength */
257
+   
258
+   /* check for overflow of buffer */
259
+   if (limit && ((unsigned char *)limit - p) < 0)
260
+     {
261
++truncated:
262
+       if (truncp)
263
+ 	*truncp = 1;
264
+       return 0;
265
+diff -Naur dnsmasq-2.76.orig/src/rfc2131.c dnsmasq-2.76/src/rfc2131.c
266
+--- dnsmasq-2.76.orig/src/rfc2131.c	2016-05-18 07:51:54.000000000 -0700
267
+@@ -155,7 +155,7 @@
268
+ 	  for (offset = 0; offset < (len - 5); offset += elen + 5)
269
+ 	    {
270
+ 	      elen = option_uint(opt, offset + 4 , 1);
271
+-	      if (option_uint(opt, offset, 4) == BRDBAND_FORUM_IANA)
272
++	      if (option_uint(opt, offset, 4) == BRDBAND_FORUM_IANA && offset + elen + 5 <= len)
273
+ 		{
274
+ 		  unsigned char *x = option_ptr(opt, offset + 5);
275
+ 		  unsigned char *y = option_ptr(opt, offset + elen + 5);
276
+@@ -2419,10 +2419,10 @@
277
+ 
278
+ 	      if (fqdn_flags & 0x04)
279
+ 		{
280
+-		  p = do_rfc1035_name(p, hostname);
281
++		  p = do_rfc1035_name(p, hostname, NULL);
282
+ 		  if (domain)
283
+ 		    {
284
+-		      p = do_rfc1035_name(p, domain);
285
++		      p = do_rfc1035_name(p, domain, NULL);
286
+ 		      *p++ = 0;
287
+ 		    }
288
+ 		}
289
+diff -Naur dnsmasq-2.76.orig/src/rfc3315.c dnsmasq-2.76/src/rfc3315.c
290
+--- dnsmasq-2.76.orig/src/rfc3315.c	2016-05-18 07:51:54.000000000 -0700
291
+@@ -206,6 +206,9 @@
292
+   /* RFC-6939 */
293
+   if ((opt = opt6_find(opts, end, OPTION6_CLIENT_MAC, 3)))
294
+     {
295
++      if (opt6_len(opt) - 2 > DHCP_CHADDR_MAX) {
296
++        return 0;
297
++      }
298
+       state->mac_type = opt6_uint(opt, 0, 2);
299
+       state->mac_len = opt6_len(opt) - 2;
300
+       memcpy(&state->mac[0], opt6_ptr(opt, 2), state->mac_len);
301
+@@ -213,6 +216,9 @@
302
+   
303
+   for (opt = opts; opt; opt = opt6_next(opt, end))
304
+     {
305
++      if (opt6_ptr(opt, 0) + opt6_len(opt) >= end) {
306
++        return 0;
307
++      }
308
+       int o = new_opt6(opt6_type(opt));
309
+       if (opt6_type(opt) == OPTION6_RELAY_MSG)
310
+ 	{
311
+@@ -1472,10 +1478,10 @@
312
+       if ((p = expand(len + 2)))
313
+ 	{
314
+ 	  *(p++) = state->fqdn_flags;
315
+-	  p = do_rfc1035_name(p, state->hostname);
316
++	  p = do_rfc1035_name(p, state->hostname, NULL);
317
+ 	  if (state->send_domain)
318
+ 	    {
319
+-	      p = do_rfc1035_name(p, state->send_domain);
320
++	      p = do_rfc1035_name(p, state->send_domain, NULL);
321
+ 	      *p = 0;
322
+ 	    }
323
+ 	}
324
+diff -Naur dnsmasq-2.76.orig/src/util.c dnsmasq-2.76/src/util.c
325
+--- dnsmasq-2.76.orig/src/util.c	2016-05-18 07:51:54.000000000 -0700
326
+@@ -218,15 +218,20 @@
327
+   return ret;
328
+ }
329
+ 
330
+-unsigned char *do_rfc1035_name(unsigned char *p, char *sval)
331
++unsigned char *do_rfc1035_name(unsigned char *p, char *sval, char *limit)
332
+ {
333
+   int j;
334
+   
335
+   while (sval && *sval)
336
+     {
337
++      if (limit && p + 1 > (unsigned char*)limit)
338
++        return p;
339
++
340
+       unsigned char *cp = p++;
341
+       for (j = 0; *sval && (*sval != '.'); sval++, j++)
342
+ 	{
343
++          if (limit && p + 1 > (unsigned char*)limit)
344
++            return p;
345
+ #ifdef HAVE_DNSSEC
346
+ 	  if (option_bool(OPT_DNSSEC_VALID) && *sval == NAME_ESCAPE)
347
+ 	    *p++ = (*(++sval))-1;
... ...
@@ -1,7 +1,7 @@
1 1
 Summary:	DNS proxy with integrated DHCP server
2 2
 Name:		dnsmasq
3 3
 Version:	2.76
4
-Release:	1%{?dist}
4
+Release:	2%{?dist}
5 5
 License:	GPLv2 or GPLv3
6 6
 Group:		System Environment/Daemons
7 7
 URL:		http://www.thekelleys.org.uk/dnsmasq/
... ...
@@ -10,12 +10,14 @@ Source:	        %{name}-%{version}.tar.xz
10 10
 Vendor:		VMware, Inc.
11 11
 Distribution:	Photon
12 12
 Provides:	dnsmasq
13
+Patch0:		dnsmasq.patch
13 14
 
14 15
 %description
15 16
 Dnsmasq a lightweight, caching DNS proxy with integrated DHCP server.
16 17
 
17 18
 %prep
18 19
 %setup -q
20
+%patch0 -p1
19 21
 
20 22
 %build
21 23
 make %{?_smp_mflags}
... ...
@@ -66,6 +68,8 @@ rm -rf %{buildroot}
66 66
 %config  /usr/share/dnsmasq/trust-anchors.conf
67 67
 
68 68
 %changelog
69
+*   Wed Sep 27 2017 Alexey Makhalov <amakhalov@vmware.com> 2.76-2
70
+-   Fix CVE-2017-14491..CVE-2017-14496
69 71
 *   Sun Nov 27 2016 Vinay Kulkarni <kulkarniv@vmware.com> 2.76-1
70 72
 -   Upgrade to 2.76 to address CVE-2015-8899
71 73
 *   Tue May 24 2016 Priyesh Padmavilasom <ppadmavilasom@vmware.com> 2.75-2