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
6 The problem is in the sort_rrset() function and allows a remote
7 attacker to overwrite memory. Any dnsmasq instance with DNSSEC
11 src/dnssec.c | 273 ++++++++++++++++++++++++++++-----------------------
12 2 files changed, 158 insertions(+), 122 deletions(-)
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.
22 +>>>>>>> Fix remote buffer overflow CERT VU#434904
24 Impove cache behaviour for TCP connections. For ease of
25 implementaion, dnsmasq has always forked a new process to handle
28 @@ -222,138 +222,147 @@ static int check_date_range(u32 date_sta
29 && serial_compare_32(curtime, date_end) == SERIAL_LT;
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.
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.
48 + unsigned char *end, *ip, *op;
52 +static int get_rdata(struct dns_header *header, size_t plen, struct rdata_state *state)
57 - /* No more data needs mangling */
59 + if (state->op && state->c != 1)
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)
65 - memcpy(buff, *p, bufflen);
78 - if (d == 0 && extract_name(header, plen, p, buff, 1, 0))
79 - /* domain-name, canonicalise */
80 - return to_wire(buff);
83 - /* plain data preceding a domain-name, don't run off the end of the data */
94 - memcpy(buff, *p, d);
96 + /* all the bytes to the end. */
97 + if ((state->c = state->end - state->ip) != 0)
99 + state->op = state->ip;
100 + state->ip = state->end;;
111 + /* domain-name, canonicalise */
114 + if (!extract_name(header, plen, &state->ip, state->buff, 1, 0) ||
115 + (len = to_wire(state->buff)) == 0)
119 + state->op = (unsigned char *)state->buff;
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;
130 + state->op = state->ip;
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
152 - leaving the following bytes as deciding the order. Hence the nasty left1 and left2 variables.
154 +/* Bubble sort the RRset into the canonical order. */
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)
159 - int swap, quit, i, j;
164 for (swap = 0, i = 0; i < rrsetidx-1; i++)
166 - int rdlen1, rdlen2, left1, left2, len1, len2, len, rc;
168 - unsigned char *end1, *end2;
169 + int rdlen1, rdlen2;
170 + struct rdata_state state1, state2;
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);
177 - p1 += 8; /* skip class, type, ttl */
178 - GETSHORT(rdlen1, p1);
179 - end1 = p1 + rdlen1;
181 - p2 += 8; /* skip class, type, ttl */
182 - GETSHORT(rdlen2, p2);
183 - end2 = p2 + rdlen2;
185 - dp1 = dp2 = rr_desc;
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;
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;
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;
210 - memmove(buff1, buff1 + len1 - left1, left1);
212 - if ((len1 = get_rdata(header, plen, end1, buff1 + left1, (MAXDNAME * 2) - left1, &p1, &dp1)) == 0)
216 - memcpy(buff1 + left1, p1, len1);
221 - memmove(buff2, buff2 + len2 - left2, left2);
223 - if ((len2 = get_rdata(header, plen, end2, buff2 + left2, (MAXDNAME *2) - left2, &p2, &dp2)) == 0)
227 - memcpy(buff2 + left2, p2, len2);
232 - left1 = len1 - len2, left2 = 0, len = len2;
234 - left2 = len2 - len1, left1 = 0, len = len1;
237 - rc = (len == 0) ? 0 : memcmp(buff1, buff2, len);
239 - if (rc > 0 || (rc == 0 && quit && len1 > len2))
241 - unsigned char *tmp = rrset[i+1];
242 - rrset[i+1] = rrset[i];
246 - else if (rc == 0 && quit && len1 == len2)
247 + ok1 = get_rdata(header, plen, &state1);
248 + ok2 = get_rdata(header, plen, &state2);
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];
259 + else if (ok1 && (!ok2 || *state1.op > *state2.op))
261 + unsigned char *tmp = rrset[i+1];
262 + rrset[i+1] = rrset[i];
269 + else if (ok2 && (!ok1 || *state2.op > *state1.op))
272 + /* arrive here when bytes are equal, go round the loop again
273 + and compare the next ones. */
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);
282 +#define RRBUFLEN 300 /* Most RRs are smaller than this. */
284 for (i = 0; i < rrsetidx; ++i)
287 - unsigned char *end, *cp;
290 + struct rdata_state state;
292 + unsigned char rrbuf[RRBUFLEN];
297 if (!extract_name(header, plen, &p, name, 1, 10))
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)
305 - for (k = name_labels - labels; k != 0; k--)
306 + for (j = name_labels - labels; j != 0; j--)
308 while (*name_start != '.' && *name_start != 0)
310 - if (k != 1 && *name_start == '.')
311 + if (j != 1 && *name_start == '.')
315 @@ -592,24 +603,44 @@ static int validate_rrset(time_t now, st
316 if (!CHECK_LEN(header, p, plen, rdlen))
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. */
325 - for (len = 0; (seg = get_rdata(header, plen, end, name, MAXDNAME * 2, &cp, &dp)) != 0; len += seg);
328 + /* canonicalise rdata and calculate length of same, use
329 + name buffer as workspace for get_rdata. */
332 + state.desc = rr_desc;
334 + state.end = p + rdlen;
336 + for (j = 0; get_rdata(header, plen, &state); j++)
338 + rrbuf[j] = *state.op;
340 + len = htons((u16)j);
341 hash->update(ctx, 2, (unsigned char *)&len);
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. */
350 + state.desc = rr_desc;
352 + for (j = 0; get_rdata(header, plen, &state); j++)
354 + rrbuf[j] = *state.op;
356 + if (j == RRBUFLEN - 1)
358 + hash->update(ctx, RRBUFLEN, rrbuf);
364 - /* Now canonicalise again and digest. */
367 - while ((seg = get_rdata(header, plen, end, name, MAXDNAME * 2, &cp, &dp)))
368 - hash->update(ctx, seg, (unsigned char *)name);
370 - hash->update(ctx, end - cp, cp);
372 + hash->update(ctx, j, rrbuf);
375 hash->digest(ctx, hash->digest_size, digest);