dnsmasq: Backport some security updates
[librecmc/librecmc.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);