dnsmasq: Backport some security updates
[openwrt/openwrt.git] / package / network / services / dnsmasq / patches / 0102-Fix-remote-buffer-overflow-CERT-VU-434904.patch
1 From 4e96a4be685c9e4445f6ee79ad0b36b9119b502a Mon Sep 17 00:00:00 2001
2 From: Simon Kelley <simon@thekelleys.org.uk>
3 Date: Wed, 11 Nov 2020 23:25:04 +0000
4 Subject: Fix remote buffer overflow CERT VU#434904
5
6 The problem is in the sort_rrset() function and allows a remote
7 attacker to overwrite memory. Any dnsmasq instance with DNSSEC
8 enabled is vulnerable.
9 ---
10 CHANGELOG | 7 +-
11 src/dnssec.c | 273 ++++++++++++++++++++++++++++-----------------------
12 2 files changed, 158 insertions(+), 122 deletions(-)
13
14 --- a/CHANGELOG
15 +++ b/CHANGELOG
16 @@ -1,3 +1,9 @@
17 + Fix a remote buffer overflow problem in the DNSSEC code. Any
18 + dnsmasq with DNSSEC compiled in and enabled is vulnerable to this,
19 + referenced by CERT VU#434904.
20 +
21 +
22 +>>>>>>> Fix remote buffer overflow CERT VU#434904
23 version 2.81
24 Impove cache behaviour for TCP connections. For ease of
25 implementaion, dnsmasq has always forked a new process to handle
26 --- a/src/dnssec.c
27 +++ b/src/dnssec.c
28 @@ -222,138 +222,147 @@ static int check_date_range(u32 date_sta
29 && serial_compare_32(curtime, date_end) == SERIAL_LT;
30 }
31
32 -/* Return bytes of canonicalised rdata, when the return value is zero, the remaining
33 - data, pointed to by *p, should be used raw. */
34 -static int get_rdata(struct dns_header *header, size_t plen, unsigned char *end, char *buff, int bufflen,
35 - unsigned char **p, u16 **desc)
36 +/* Return bytes of canonicalised rrdata one by one.
37 + Init state->ip with the RR, and state->end with the end of same.
38 + Init state->op to NULL.
39 + Init state->desc to RR descriptor.
40 + Init state->buff with a MAXDNAME * 2 buffer.
41 +
42 + After each call which returns 1, state->op points to the next byte of data.
43 + On returning 0, the end has been reached.
44 +*/
45 +struct rdata_state {
46 + u16 *desc;
47 + size_t c;
48 + unsigned char *end, *ip, *op;
49 + char *buff;
50 +};
51 +
52 +static int get_rdata(struct dns_header *header, size_t plen, struct rdata_state *state)
53 {
54 - int d = **desc;
55 + int d;
56
57 - /* No more data needs mangling */
58 - if (d == (u16)-1)
59 + if (state->op && state->c != 1)
60 {
61 - /* If there's more data than we have space for, just return what fits,
62 - we'll get called again for more chunks */
63 - if (end - *p > bufflen)
64 - {
65 - memcpy(buff, *p, bufflen);
66 - *p += bufflen;
67 - return bufflen;
68 - }
69 -
70 - return 0;
71 + state->op++;
72 + state->c--;
73 + return 1;
74 }
75 -
76 - (*desc)++;
77 -
78 - if (d == 0 && extract_name(header, plen, p, buff, 1, 0))
79 - /* domain-name, canonicalise */
80 - return to_wire(buff);
81 - else
82 - {
83 - /* plain data preceding a domain-name, don't run off the end of the data */
84 - if ((end - *p) < d)
85 - d = end - *p;
86 +
87 + while (1)
88 + {
89 + d = *(state->desc);
90
91 - if (d != 0)
92 + if (d == (u16)-1)
93 {
94 - memcpy(buff, *p, d);
95 - *p += d;
96 + /* all the bytes to the end. */
97 + if ((state->c = state->end - state->ip) != 0)
98 + {
99 + state->op = state->ip;
100 + state->ip = state->end;;
101 + }
102 + else
103 + return 0;
104 + }
105 + else
106 + {
107 + state->desc++;
108 +
109 + if (d == (u16)0)
110 + {
111 + /* domain-name, canonicalise */
112 + int len;
113 +
114 + if (!extract_name(header, plen, &state->ip, state->buff, 1, 0) ||
115 + (len = to_wire(state->buff)) == 0)
116 + continue;
117 +
118 + state->c = len;
119 + state->op = (unsigned char *)state->buff;
120 + }
121 + else
122 + {
123 + /* plain data preceding a domain-name, don't run off the end of the data */
124 + if ((state->end - state->ip) < d)
125 + d = state->end - state->ip;
126 +
127 + if (d == 0)
128 + continue;
129 +
130 + state->op = state->ip;
131 + state->c = d;
132 + state->ip += d;
133 + }
134 }
135
136 - return d;
137 + return 1;
138 }
139 }
140
141 -/* Bubble sort the RRset into the canonical order.
142 - Note that the byte-streams from two RRs may get unsynced: consider
143 - RRs which have two domain-names at the start and then other data.
144 - The domain-names may have different lengths in each RR, but sort equal
145 -
146 - ------------
147 - |abcde|fghi|
148 - ------------
149 - |abcd|efghi|
150 - ------------
151 -
152 - leaving the following bytes as deciding the order. Hence the nasty left1 and left2 variables.
153 -*/
154 +/* Bubble sort the RRset into the canonical order. */
155
156 static int sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int rrsetidx,
157 unsigned char **rrset, char *buff1, char *buff2)
158 {
159 - int swap, quit, i, j;
160 + int swap, i, j;
161
162 do
163 {
164 for (swap = 0, i = 0; i < rrsetidx-1; i++)
165 {
166 - int rdlen1, rdlen2, left1, left2, len1, len2, len, rc;
167 - u16 *dp1, *dp2;
168 - unsigned char *end1, *end2;
169 + int rdlen1, rdlen2;
170 + struct rdata_state state1, state2;
171 +
172 /* Note that these have been determined to be OK previously,
173 so we don't need to check for NULL return here. */
174 - unsigned char *p1 = skip_name(rrset[i], header, plen, 10);
175 - unsigned char *p2 = skip_name(rrset[i+1], header, plen, 10);
176 -
177 - p1 += 8; /* skip class, type, ttl */
178 - GETSHORT(rdlen1, p1);
179 - end1 = p1 + rdlen1;
180 -
181 - p2 += 8; /* skip class, type, ttl */
182 - GETSHORT(rdlen2, p2);
183 - end2 = p2 + rdlen2;
184 -
185 - dp1 = dp2 = rr_desc;
186 -
187 - for (quit = 0, left1 = 0, left2 = 0, len1 = 0, len2 = 0; !quit;)
188 + state1.ip = skip_name(rrset[i], header, plen, 10);
189 + state2.ip = skip_name(rrset[i+1], header, plen, 10);
190 + state1.op = state2.op = NULL;
191 + state1.buff = buff1;
192 + state2.buff = buff2;
193 + state1.desc = state2.desc = rr_desc;
194 +
195 + state1.ip += 8; /* skip class, type, ttl */
196 + GETSHORT(rdlen1, state1.ip);
197 + if (!CHECK_LEN(header, state1.ip, plen, rdlen1))
198 + return rrsetidx; /* short packet */
199 + state1.end = state1.ip + rdlen1;
200 +
201 + state2.ip += 8; /* skip class, type, ttl */
202 + GETSHORT(rdlen2, state2.ip);
203 + if (!CHECK_LEN(header, state2.ip, plen, rdlen2))
204 + return rrsetidx; /* short packet */
205 + state2.end = state2.ip + rdlen2;
206 +
207 + while (1)
208 {
209 - if (left1 != 0)
210 - memmove(buff1, buff1 + len1 - left1, left1);
211 -
212 - if ((len1 = get_rdata(header, plen, end1, buff1 + left1, (MAXDNAME * 2) - left1, &p1, &dp1)) == 0)
213 - {
214 - quit = 1;
215 - len1 = end1 - p1;
216 - memcpy(buff1 + left1, p1, len1);
217 - }
218 - len1 += left1;
219 -
220 - if (left2 != 0)
221 - memmove(buff2, buff2 + len2 - left2, left2);
222 -
223 - if ((len2 = get_rdata(header, plen, end2, buff2 + left2, (MAXDNAME *2) - left2, &p2, &dp2)) == 0)
224 - {
225 - quit = 1;
226 - len2 = end2 - p2;
227 - memcpy(buff2 + left2, p2, len2);
228 - }
229 - len2 += left2;
230 -
231 - if (len1 > len2)
232 - left1 = len1 - len2, left2 = 0, len = len2;
233 - else
234 - left2 = len2 - len1, left1 = 0, len = len1;
235 + int ok1, ok2;
236
237 - rc = (len == 0) ? 0 : memcmp(buff1, buff2, len);
238 -
239 - if (rc > 0 || (rc == 0 && quit && len1 > len2))
240 - {
241 - unsigned char *tmp = rrset[i+1];
242 - rrset[i+1] = rrset[i];
243 - rrset[i] = tmp;
244 - swap = quit = 1;
245 - }
246 - else if (rc == 0 && quit && len1 == len2)
247 + ok1 = get_rdata(header, plen, &state1);
248 + ok2 = get_rdata(header, plen, &state2);
249 +
250 + if (!ok1 && !ok2)
251 {
252 /* Two RRs are equal, remove one copy. RFC 4034, para 6.3 */
253 for (j = i+1; j < rrsetidx-1; j++)
254 rrset[j] = rrset[j+1];
255 rrsetidx--;
256 i--;
257 + break;
258 + }
259 + else if (ok1 && (!ok2 || *state1.op > *state2.op))
260 + {
261 + unsigned char *tmp = rrset[i+1];
262 + rrset[i+1] = rrset[i];
263 + rrset[i] = tmp;
264 + swap = 1;
265 + break;
266 }
267 - else if (rc < 0)
268 - quit = 1;
269 + else if (ok2 && (!ok1 || *state2.op > *state1.op))
270 + break;
271 +
272 + /* arrive here when bytes are equal, go round the loop again
273 + and compare the next ones. */
274 }
275 }
276 } while (swap);
277 @@ -549,15 +558,18 @@ static int validate_rrset(time_t now, st
278 wire_len = to_wire(keyname);
279 hash->update(ctx, (unsigned int)wire_len, (unsigned char*)keyname);
280 from_wire(keyname);
281 +
282 +#define RRBUFLEN 300 /* Most RRs are smaller than this. */
283
284 for (i = 0; i < rrsetidx; ++i)
285 {
286 - int seg;
287 - unsigned char *end, *cp;
288 - u16 len, *dp;
289 + int j;
290 + struct rdata_state state;
291 + u16 len;
292 + unsigned char rrbuf[RRBUFLEN];
293
294 p = rrset[i];
295 -
296 +
297 if (!extract_name(header, plen, &p, name, 1, 10))
298 return STAT_BOGUS;
299
300 @@ -566,12 +578,11 @@ static int validate_rrset(time_t now, st
301 /* if more labels than in RRsig name, hash *.<no labels in rrsig labels field> 4035 5.3.2 */
302 if (labels < name_labels)
303 {
304 - int k;
305 - for (k = name_labels - labels; k != 0; k--)
306 + for (j = name_labels - labels; j != 0; j--)
307 {
308 while (*name_start != '.' && *name_start != 0)
309 name_start++;
310 - if (k != 1 && *name_start == '.')
311 + if (j != 1 && *name_start == '.')
312 name_start++;
313 }
314
315 @@ -592,24 +603,44 @@ static int validate_rrset(time_t now, st
316 if (!CHECK_LEN(header, p, plen, rdlen))
317 return STAT_BOGUS;
318
319 - end = p + rdlen;
320 -
321 - /* canonicalise rdata and calculate length of same, use name buffer as workspace.
322 - Note that name buffer is twice MAXDNAME long in DNSSEC mode. */
323 - cp = p;
324 - dp = rr_desc;
325 - for (len = 0; (seg = get_rdata(header, plen, end, name, MAXDNAME * 2, &cp, &dp)) != 0; len += seg);
326 - len += end - cp;
327 - len = htons(len);
328 + /* canonicalise rdata and calculate length of same, use
329 + name buffer as workspace for get_rdata. */
330 + state.ip = p;
331 + state.op = NULL;
332 + state.desc = rr_desc;
333 + state.buff = name;
334 + state.end = p + rdlen;
335 +
336 + for (j = 0; get_rdata(header, plen, &state); j++)
337 + if (j < RRBUFLEN)
338 + rrbuf[j] = *state.op;
339 +
340 + len = htons((u16)j);
341 hash->update(ctx, 2, (unsigned char *)&len);
342 +
343 + /* If the RR is shorter than RRBUFLEN (most of them, in practice)
344 + then we can just digest it now. If it exceeds RRBUFLEN we have to
345 + go back to the start and do it in chunks. */
346 + if (j >= RRBUFLEN)
347 + {
348 + state.ip = p;
349 + state.op = NULL;
350 + state.desc = rr_desc;
351 +
352 + for (j = 0; get_rdata(header, plen, &state); j++)
353 + {
354 + rrbuf[j] = *state.op;
355 +
356 + if (j == RRBUFLEN - 1)
357 + {
358 + hash->update(ctx, RRBUFLEN, rrbuf);
359 + j = -1;
360 + }
361 + }
362 + }
363
364 - /* Now canonicalise again and digest. */
365 - cp = p;
366 - dp = rr_desc;
367 - while ((seg = get_rdata(header, plen, end, name, MAXDNAME * 2, &cp, &dp)))
368 - hash->update(ctx, seg, (unsigned char *)name);
369 - if (cp != end)
370 - hash->update(ctx, end - cp, cp);
371 + if (j != 0)
372 + hash->update(ctx, j, rrbuf);
373 }
374
375 hash->digest(ctx, hash->digest_size, digest);