diff options
author | Sana Kazi <Sana.Kazi@kpit.com> | 2021-05-28 16:01:25 +0000 |
---|---|---|
committer | Armin Kuster <akuster808@gmail.com> | 2021-05-29 11:41:45 -0700 |
commit | c38d2a74f762a792046f3d3c377827b08aade513 (patch) | |
tree | 3c438d11251c5ee92fcaf7170a999ef2dc7fdccb | |
parent | 587fe58949c7efae60c6eee580bda3233621245e (diff) | |
download | meta-openembedded-c38d2a74f762a792046f3d3c377827b08aade513.tar.gz |
dnsmasq: Add fixes for CVEs reported for dnsmasq
Applied single patch for below listed CVEs:
CVE-2020-25681
CVE-2020-25682
CVE-2020-25683
CVE-2020-25687
as they are fixed by single commit
http://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=commit;h=4e96a4be685c9e4445f6ee79ad0b36b9119b502a
Link: https://www.openwall.com/lists/oss-security/2021/01/19/1
Also, applied patch for below listed CVEs:
CVE-2020-25684
CVE-2020-25685
CVE-2020-25686
all CVEs applicable to v2.81
Signed-off-by: Sana Kazi <Sana.Kazi@kpit.com>
Signed-off-by: Nisha Parrakat <nishaparrakat@gmail.com>
[Refreshed patches]
Signed-off-by: Armin Kuster <akuster808@gmail.com>
7 files changed, 1631 insertions, 1 deletions
diff --git a/meta-networking/recipes-support/dnsmasq/dnsmasq_2.81.bb b/meta-networking/recipes-support/dnsmasq/dnsmasq_2.81.bb index 92415386c2..a1dc0f3a0a 100644 --- a/meta-networking/recipes-support/dnsmasq/dnsmasq_2.81.bb +++ b/meta-networking/recipes-support/dnsmasq/dnsmasq_2.81.bb | |||
@@ -4,5 +4,10 @@ SRC_URI[dnsmasq-2.81.md5sum] = "e43808177a773014b5892ccba238f7a8" | |||
4 | SRC_URI[dnsmasq-2.81.sha256sum] = "3c28c68c6c2967c3a96e9b432c0c046a5df17a426d3a43cffe9e693cf05804d0" | 4 | SRC_URI[dnsmasq-2.81.sha256sum] = "3c28c68c6c2967c3a96e9b432c0c046a5df17a426d3a43cffe9e693cf05804d0" |
5 | SRC_URI += "\ | 5 | SRC_URI += "\ |
6 | file://lua.patch \ | 6 | file://lua.patch \ |
7 | file://CVE-2020-25681.patch \ | ||
8 | file://CVE-2020-25684.patch \ | ||
9 | file://CVE-2020-25685-1.patch \ | ||
10 | file://CVE-2020-25685-2.patch \ | ||
11 | file://CVE-2020-25686-1.patch \ | ||
12 | file://CVE-2020-25686-2.patch \ | ||
7 | " | 13 | " |
8 | |||
diff --git a/meta-networking/recipes-support/dnsmasq/files/CVE-2020-25681.patch b/meta-networking/recipes-support/dnsmasq/files/CVE-2020-25681.patch new file mode 100644 index 0000000000..6756157700 --- /dev/null +++ b/meta-networking/recipes-support/dnsmasq/files/CVE-2020-25681.patch | |||
@@ -0,0 +1,370 @@ | |||
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: [PATCH] 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 | Signed-off-by: Sana Kazi <Sana.Kazi@kpit.com> | ||
11 | --- | ||
12 | CHANGELOG | 7 +- | ||
13 | src/dnssec.c | 273 ++++++++++++++++++++++++++++----------------------- | ||
14 | 2 files changed, 158 insertions(+), 122 deletions(-) | ||
15 | |||
16 | CVE: CVE-2020-25681 | ||
17 | CVE: CVE-2020-25682 | ||
18 | CVE: CVE-2020-25683 | ||
19 | CVE: CVE-2020-25687 | ||
20 | Upstream-Status: Backport [https://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=patch;h=4e96a4be685c9e4445f6ee79ad0b36b9119b502a] | ||
21 | Comment: Refreshed first two hunks | ||
22 | |||
23 | Index: dnsmasq-2.81/src/dnssec.c | ||
24 | =================================================================== | ||
25 | --- dnsmasq-2.81.orig/src/dnssec.c | ||
26 | +++ dnsmasq-2.81/src/dnssec.c | ||
27 | @@ -223,138 +223,144 @@ static int check_date_range(unsigned lon | ||
28 | && serial_compare_32(curtime, date_end) == SERIAL_LT; | ||
29 | } | ||
30 | |||
31 | -/* Return bytes of canonicalised rdata, when the return value is zero, the remaining | ||
32 | - data, pointed to by *p, should be used raw. */ | ||
33 | -static int get_rdata(struct dns_header *header, size_t plen, unsigned char *end, char *buff, int bufflen, | ||
34 | - unsigned char **p, u16 **desc) | ||
35 | +/* Return bytes of canonicalised rrdata one by one. | ||
36 | + Init state->ip with the RR, and state->end with the end of same. | ||
37 | + Init state->op to NULL. | ||
38 | + Init state->desc to RR descriptor. | ||
39 | + Init state->buff with a MAXDNAME * 2 buffer. | ||
40 | + | ||
41 | + After each call which returns 1, state->op points to the next byte of data. | ||
42 | + On returning 0, the end has been reached. | ||
43 | +*/ | ||
44 | +struct rdata_state { | ||
45 | + u16 *desc; | ||
46 | + size_t c; | ||
47 | + unsigned char *end, *ip, *op; | ||
48 | + char *buff; | ||
49 | +}; | ||
50 | + | ||
51 | +static int get_rdata(struct dns_header *header, size_t plen, struct rdata_state *state) | ||
52 | { | ||
53 | - int d = **desc; | ||
54 | + int d; | ||
55 | |||
56 | - /* No more data needs mangling */ | ||
57 | - if (d == (u16)-1) | ||
58 | + if (state->op && state->c != 1) | ||
59 | { | ||
60 | - /* If there's more data than we have space for, just return what fits, | ||
61 | - we'll get called again for more chunks */ | ||
62 | - if (end - *p > bufflen) | ||
63 | - { | ||
64 | - memcpy(buff, *p, bufflen); | ||
65 | - *p += bufflen; | ||
66 | - return bufflen; | ||
67 | - } | ||
68 | - | ||
69 | - return 0; | ||
70 | + state->op++; | ||
71 | + state->c--; | ||
72 | + return 1; | ||
73 | } | ||
74 | - | ||
75 | - (*desc)++; | ||
76 | - | ||
77 | - if (d == 0 && extract_name(header, plen, p, buff, 1, 0)) | ||
78 | - /* domain-name, canonicalise */ | ||
79 | - return to_wire(buff); | ||
80 | - else | ||
81 | - { | ||
82 | - /* plain data preceding a domain-name, don't run off the end of the data */ | ||
83 | - if ((end - *p) < d) | ||
84 | - d = end - *p; | ||
85 | - | ||
86 | - if (d != 0) | ||
87 | + | ||
88 | + while (1) | ||
89 | + { | ||
90 | + d = *(state->desc); | ||
91 | + if (d == (u16)-1) | ||
92 | { | ||
93 | - memcpy(buff, *p, d); | ||
94 | - *p += d; | ||
95 | + /* all the bytes to the end. */ | ||
96 | + if ((state->c = state->end - state->ip) != 0) | ||
97 | + { | ||
98 | + state->op = state->ip; | ||
99 | + state->ip = state->end;; | ||
100 | + } | ||
101 | + else | ||
102 | + return 0; | ||
103 | + } | ||
104 | + else | ||
105 | + { | ||
106 | + state->desc++; | ||
107 | + | ||
108 | + if (d == (u16)0) | ||
109 | + { | ||
110 | + /* domain-name, canonicalise */ | ||
111 | + int len; | ||
112 | + | ||
113 | + if (!extract_name(header, plen, &state->ip, state->buff, 1, 0) || | ||
114 | + (len = to_wire(state->buff)) == 0) | ||
115 | + continue; | ||
116 | + | ||
117 | + state->c = len; | ||
118 | + state->op = (unsigned char *)state->buff; | ||
119 | + } | ||
120 | + else | ||
121 | + { | ||
122 | + /* plain data preceding a domain-name, don't run off the end of the data */ | ||
123 | + if ((state->end - state->ip) < d) | ||
124 | + d = state->end - state->ip; | ||
125 | + | ||
126 | + if (d == 0) | ||
127 | + continue; | ||
128 | + | ||
129 | + state->op = state->ip; | ||
130 | + state->c = d; | ||
131 | + state->ip += d; | ||
132 | + } | ||
133 | } | ||
134 | |||
135 | - return d; | ||
136 | + return 1; | ||
137 | } | ||
138 | } | ||
139 | |||
140 | -/* Bubble sort the RRset into the canonical order. | ||
141 | - Note that the byte-streams from two RRs may get unsynced: consider | ||
142 | - RRs which have two domain-names at the start and then other data. | ||
143 | - The domain-names may have different lengths in each RR, but sort equal | ||
144 | - | ||
145 | - ------------ | ||
146 | - |abcde|fghi| | ||
147 | - ------------ | ||
148 | - |abcd|efghi| | ||
149 | - ------------ | ||
150 | - | ||
151 | - leaving the following bytes as deciding the order. Hence the nasty left1 and left2 variables. | ||
152 | -*/ | ||
153 | +/* Bubble sort the RRset into the canonical order. */ | ||
154 | |||
155 | static int sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int rrsetidx, | ||
156 | unsigned char **rrset, char *buff1, char *buff2) | ||
157 | { | ||
158 | - int swap, quit, i, j; | ||
159 | + int swap, i, j; | ||
160 | |||
161 | do | ||
162 | { | ||
163 | for (swap = 0, i = 0; i < rrsetidx-1; i++) | ||
164 | { | ||
165 | - int rdlen1, rdlen2, left1, left2, len1, len2, len, rc; | ||
166 | - u16 *dp1, *dp2; | ||
167 | - unsigned char *end1, *end2; | ||
168 | + int rdlen1, rdlen2; | ||
169 | + struct rdata_state state1, state2; | ||
170 | + | ||
171 | /* Note that these have been determined to be OK previously, | ||
172 | so we don't need to check for NULL return here. */ | ||
173 | - unsigned char *p1 = skip_name(rrset[i], header, plen, 10); | ||
174 | - unsigned char *p2 = skip_name(rrset[i+1], header, plen, 10); | ||
175 | - | ||
176 | - p1 += 8; /* skip class, type, ttl */ | ||
177 | - GETSHORT(rdlen1, p1); | ||
178 | - end1 = p1 + rdlen1; | ||
179 | - | ||
180 | - p2 += 8; /* skip class, type, ttl */ | ||
181 | - GETSHORT(rdlen2, p2); | ||
182 | - end2 = p2 + rdlen2; | ||
183 | - | ||
184 | - dp1 = dp2 = rr_desc; | ||
185 | - | ||
186 | - for (quit = 0, left1 = 0, left2 = 0, len1 = 0, len2 = 0; !quit;) | ||
187 | + state1.ip = skip_name(rrset[i], header, plen, 10); | ||
188 | + state2.ip = skip_name(rrset[i+1], header, plen, 10); | ||
189 | + state1.op = state2.op = NULL; | ||
190 | + state1.buff = buff1; | ||
191 | + state2.buff = buff2; | ||
192 | + state1.desc = state2.desc = rr_desc; | ||
193 | + | ||
194 | + state1.ip += 8; /* skip class, type, ttl */ | ||
195 | + GETSHORT(rdlen1, state1.ip); | ||
196 | + if (!CHECK_LEN(header, state1.ip, plen, rdlen1)) | ||
197 | + return rrsetidx; /* short packet */ | ||
198 | + state1.end = state1.ip + rdlen1; | ||
199 | + state2.ip += 8; /* skip class, type, ttl */ | ||
200 | + GETSHORT(rdlen2, state2.ip); | ||
201 | + if (!CHECK_LEN(header, state2.ip, plen, rdlen2)) | ||
202 | + return rrsetidx; /* short packet */ | ||
203 | + state2.end = state2.ip + rdlen2; | ||
204 | + | ||
205 | + while (1) | ||
206 | { | ||
207 | - if (left1 != 0) | ||
208 | - memmove(buff1, buff1 + len1 - left1, left1); | ||
209 | - | ||
210 | - if ((len1 = get_rdata(header, plen, end1, buff1 + left1, (MAXDNAME * 2) - left1, &p1, &dp1)) == 0) | ||
211 | - { | ||
212 | - quit = 1; | ||
213 | - len1 = end1 - p1; | ||
214 | - memcpy(buff1 + left1, p1, len1); | ||
215 | + int ok1, ok2; | ||
216 | + ok1 = get_rdata(header, plen, &state1); | ||
217 | + ok2 = get_rdata(header, plen, &state2); | ||
218 | + | ||
219 | + if (!ok1 && !ok2) | ||
220 | + { | ||
221 | + /* Two RRs are equal, remove one copy. RFC 4034, para 6.3 */ | ||
222 | + for (j = i+1; j < rrsetidx-1; j++) | ||
223 | + rrset[j] = rrset[j+1]; | ||
224 | + rrsetidx--; | ||
225 | + i--; | ||
226 | + break; | ||
227 | } | ||
228 | - len1 += left1; | ||
229 | - | ||
230 | - if (left2 != 0) | ||
231 | - memmove(buff2, buff2 + len2 - left2, left2); | ||
232 | - | ||
233 | - if ((len2 = get_rdata(header, plen, end2, buff2 + left2, (MAXDNAME *2) - left2, &p2, &dp2)) == 0) | ||
234 | - { | ||
235 | - quit = 1; | ||
236 | - len2 = end2 - p2; | ||
237 | - memcpy(buff2 + left2, p2, len2); | ||
238 | - } | ||
239 | - len2 += left2; | ||
240 | - | ||
241 | - if (len1 > len2) | ||
242 | - left1 = len1 - len2, left2 = 0, len = len2; | ||
243 | - else | ||
244 | - left2 = len2 - len1, left1 = 0, len = len1; | ||
245 | - | ||
246 | - rc = (len == 0) ? 0 : memcmp(buff1, buff2, len); | ||
247 | - | ||
248 | - if (rc > 0 || (rc == 0 && quit && len1 > len2)) | ||
249 | + else if (ok1 && (!ok2 || *state1.op > *state2.op)) | ||
250 | { | ||
251 | unsigned char *tmp = rrset[i+1]; | ||
252 | rrset[i+1] = rrset[i]; | ||
253 | rrset[i] = tmp; | ||
254 | - swap = quit = 1; | ||
255 | - } | ||
256 | - else if (rc == 0 && quit && len1 == len2) | ||
257 | - { | ||
258 | - /* Two RRs are equal, remove one copy. RFC 4034, para 6.3 */ | ||
259 | - for (j = i+1; j < rrsetidx-1; j++) | ||
260 | - rrset[j] = rrset[j+1]; | ||
261 | - rrsetidx--; | ||
262 | - i--; | ||
263 | + swap = 1; | ||
264 | + break; | ||
265 | } | ||
266 | - else if (rc < 0) | ||
267 | - quit = 1; | ||
268 | + else if (ok2 && (!ok1 || *state2.op > *state1.op)) | ||
269 | + break; | ||
270 | + | ||
271 | + /* arrive here when bytes are equal, go round the loop again | ||
272 | + and compare the next ones. */ | ||
273 | } | ||
274 | } | ||
275 | } while (swap); | ||
276 | @@ -569,12 +575,15 @@ static int validate_rrset(time_t now, st | ||
277 | wire_len = to_wire(keyname); | ||
278 | hash->update(ctx, (unsigned int)wire_len, (unsigned char*)keyname); | ||
279 | from_wire(keyname); | ||
280 | + | ||
281 | +#define RRBUFLEN 300 /* Most RRs are smaller than this. */ | ||
282 | |||
283 | for (i = 0; i < rrsetidx; ++i) | ||
284 | { | ||
285 | - int seg; | ||
286 | - unsigned char *end, *cp; | ||
287 | - u16 len, *dp; | ||
288 | + int j; | ||
289 | + struct rdata_state state; | ||
290 | + u16 len; | ||
291 | + unsigned char rrbuf[RRBUFLEN]; | ||
292 | |||
293 | p = rrset[i]; | ||
294 | |||
295 | @@ -586,12 +595,11 @@ static int validate_rrset(time_t now, st | ||
296 | /* if more labels than in RRsig name, hash *.<no labels in rrsig labels field> 4035 5.3.2 */ | ||
297 | if (labels < name_labels) | ||
298 | { | ||
299 | - int k; | ||
300 | - for (k = name_labels - labels; k != 0; k--) | ||
301 | + for (j = name_labels - labels; j != 0; j--) | ||
302 | { | ||
303 | while (*name_start != '.' && *name_start != 0) | ||
304 | name_start++; | ||
305 | - if (k != 1 && *name_start == '.') | ||
306 | + if (j != 1 && *name_start == '.') | ||
307 | name_start++; | ||
308 | } | ||
309 | |||
310 | @@ -612,24 +620,44 @@ static int validate_rrset(time_t now, st | ||
311 | if (!CHECK_LEN(header, p, plen, rdlen)) | ||
312 | return STAT_BOGUS; | ||
313 | |||
314 | - end = p + rdlen; | ||
315 | - | ||
316 | - /* canonicalise rdata and calculate length of same, use name buffer as workspace. | ||
317 | - Note that name buffer is twice MAXDNAME long in DNSSEC mode. */ | ||
318 | - cp = p; | ||
319 | - dp = rr_desc; | ||
320 | - for (len = 0; (seg = get_rdata(header, plen, end, name, MAXDNAME * 2, &cp, &dp)) != 0; len += seg); | ||
321 | - len += end - cp; | ||
322 | - len = htons(len); | ||
323 | + /* canonicalise rdata and calculate length of same, use | ||
324 | + name buffer as workspace for get_rdata. */ | ||
325 | + state.ip = p; | ||
326 | + state.op = NULL; | ||
327 | + state.desc = rr_desc; | ||
328 | + state.buff = name; | ||
329 | + state.end = p + rdlen; | ||
330 | + | ||
331 | + for (j = 0; get_rdata(header, plen, &state); j++) | ||
332 | + if (j < RRBUFLEN) | ||
333 | + rrbuf[j] = *state.op; | ||
334 | + | ||
335 | + len = htons((u16)j); | ||
336 | hash->update(ctx, 2, (unsigned char *)&len); | ||
337 | + | ||
338 | + /* If the RR is shorter than RRBUFLEN (most of them, in practice) | ||
339 | + then we can just digest it now. If it exceeds RRBUFLEN we have to | ||
340 | + go back to the start and do it in chunks. */ | ||
341 | + if (j >= RRBUFLEN) | ||
342 | + { | ||
343 | + state.ip = p; | ||
344 | + state.op = NULL; | ||
345 | + state.desc = rr_desc; | ||
346 | + | ||
347 | + for (j = 0; get_rdata(header, plen, &state); j++) | ||
348 | + { | ||
349 | + rrbuf[j] = *state.op; | ||
350 | + | ||
351 | + if (j == RRBUFLEN - 1) | ||
352 | + { | ||
353 | + hash->update(ctx, RRBUFLEN, rrbuf); | ||
354 | + j = -1; | ||
355 | + } | ||
356 | + } | ||
357 | + } | ||
358 | |||
359 | - /* Now canonicalise again and digest. */ | ||
360 | - cp = p; | ||
361 | - dp = rr_desc; | ||
362 | - while ((seg = get_rdata(header, plen, end, name, MAXDNAME * 2, &cp, &dp))) | ||
363 | - hash->update(ctx, seg, (unsigned char *)name); | ||
364 | - if (cp != end) | ||
365 | - hash->update(ctx, end - cp, cp); | ||
366 | + if (j != 0) | ||
367 | + hash->update(ctx, j, rrbuf); | ||
368 | } | ||
369 | |||
370 | hash->digest(ctx, hash->digest_size, digest); | ||
diff --git a/meta-networking/recipes-support/dnsmasq/files/CVE-2020-25684.patch b/meta-networking/recipes-support/dnsmasq/files/CVE-2020-25684.patch new file mode 100644 index 0000000000..f7ff4b27cc --- /dev/null +++ b/meta-networking/recipes-support/dnsmasq/files/CVE-2020-25684.patch | |||
@@ -0,0 +1,98 @@ | |||
1 | From 257ac0c5f7732cbc6aa96fdd3b06602234593aca Mon Sep 17 00:00:00 2001 | ||
2 | From: Simon Kelley <simon@thekelleys.org.uk> | ||
3 | Date: Thu, 12 Nov 2020 18:49:23 +0000 | ||
4 | Subject: [PATCH] Check destination of DNS UDP query replies. | ||
5 | |||
6 | At any time, dnsmasq will have a set of sockets open, bound to | ||
7 | random ports, on which it sends queries to upstream nameservers. | ||
8 | This patch fixes the existing problem that a reply for ANY in-flight | ||
9 | query would be accepted via ANY open port, which increases the | ||
10 | chances of an attacker flooding answers "in the blind" in an | ||
11 | attempt to poison the DNS cache. CERT VU#434904 refers. | ||
12 | |||
13 | Signed-off-by: Sana Kazi <Sana.Kazi@kpit.com> | ||
14 | --- | ||
15 | CHANGELOG | 6 +++++- | ||
16 | src/forward.c | 37 ++++++++++++++++++++++++++++--------- | ||
17 | 2 files changed, 33 insertions(+), 10 deletions(-) | ||
18 | |||
19 | CVE: CVE-2020-25684 | ||
20 | Upstream-Status: Backport [https://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=patch;h=257ac0c5f7732cbc6aa96fdd3b06602234593aca] | ||
21 | Comment: No change in any hunk | ||
22 | |||
23 | Index: dnsmasq-2.81/src/forward.c | ||
24 | =================================================================== | ||
25 | --- dnsmasq-2.81.orig/src/forward.c | ||
26 | +++ dnsmasq-2.81/src/forward.c | ||
27 | @@ -16,7 +16,7 @@ | ||
28 | |||
29 | #include "dnsmasq.h" | ||
30 | |||
31 | -static struct frec *lookup_frec(unsigned short id, void *hash); | ||
32 | +static struct frec *lookup_frec(unsigned short id, int fd, int family, void *hash); | ||
33 | static struct frec *lookup_frec_by_sender(unsigned short id, | ||
34 | union mysockaddr *addr, | ||
35 | void *hash); | ||
36 | @@ -805,7 +805,7 @@ void reply_query(int fd, int family, tim | ||
37 | crc = questions_crc(header, n, daemon->namebuff); | ||
38 | #endif | ||
39 | |||
40 | - if (!(forward = lookup_frec(ntohs(header->id), hash))) | ||
41 | + if (!(forward = lookup_frec(ntohs(header->id), fd, family, hash))) | ||
42 | return; | ||
43 | |||
44 | #ifdef HAVE_DUMPFILE | ||
45 | @@ -2338,14 +2338,25 @@ struct frec *get_new_frec(time_t now, in | ||
46 | } | ||
47 | |||
48 | /* crc is all-ones if not known. */ | ||
49 | -static struct frec *lookup_frec(unsigned short id, void *hash) | ||
50 | +static struct frec *lookup_frec(unsigned short id, int fd, int family, void *hash) | ||
51 | { | ||
52 | struct frec *f; | ||
53 | |||
54 | for(f = daemon->frec_list; f; f = f->next) | ||
55 | if (f->sentto && f->new_id == id && | ||
56 | (!hash || memcmp(hash, f->hash, HASH_SIZE) == 0)) | ||
57 | - return f; | ||
58 | + { | ||
59 | + /* sent from random port */ | ||
60 | + if (family == AF_INET && f->rfd4 && f->rfd4->fd == fd) | ||
61 | + return f; | ||
62 | + | ||
63 | + if (family == AF_INET6 && f->rfd6 && f->rfd6->fd == fd) | ||
64 | + return f; | ||
65 | + | ||
66 | + /* sent to upstream from bound socket. */ | ||
67 | + if (f->sentto->sfd && f->sentto->sfd->fd == fd) | ||
68 | + return f; | ||
69 | + } | ||
70 | |||
71 | return NULL; | ||
72 | } | ||
73 | @@ -2406,12 +2417,20 @@ void server_gone(struct server *server) | ||
74 | static unsigned short get_id(void) | ||
75 | { | ||
76 | unsigned short ret = 0; | ||
77 | + struct frec *f; | ||
78 | |||
79 | - do | ||
80 | - ret = rand16(); | ||
81 | - while (lookup_frec(ret, NULL)); | ||
82 | - | ||
83 | - return ret; | ||
84 | + while (1) | ||
85 | + { | ||
86 | + ret = rand16(); | ||
87 | + | ||
88 | + /* ensure id is unique. */ | ||
89 | + for (f = daemon->frec_list; f; f = f->next) | ||
90 | + if (f->sentto && f->new_id == ret) | ||
91 | + break; | ||
92 | + | ||
93 | + if (!f) | ||
94 | + return ret; | ||
95 | + } | ||
96 | } | ||
97 | |||
98 | |||
diff --git a/meta-networking/recipes-support/dnsmasq/files/CVE-2020-25685-1.patch b/meta-networking/recipes-support/dnsmasq/files/CVE-2020-25685-1.patch new file mode 100644 index 0000000000..5eb582c671 --- /dev/null +++ b/meta-networking/recipes-support/dnsmasq/files/CVE-2020-25685-1.patch | |||
@@ -0,0 +1,587 @@ | |||
1 | From 2d765867c597db18be9d876c9c17e2c0fe1953cd Mon Sep 17 00:00:00 2001 | ||
2 | From: Simon Kelley <simon@thekelleys.org.uk> | ||
3 | Date: Thu, 12 Nov 2020 22:06:07 +0000 | ||
4 | Subject: [PATCH] Use SHA-256 to provide security against DNS cache poisoning. | ||
5 | |||
6 | Use the SHA-256 hash function to verify that DNS answers | ||
7 | received are for the questions originally asked. This replaces | ||
8 | the slightly insecure SHA-1 (when compiled with DNSSEC) or | ||
9 | the very insecure CRC32 (otherwise). Refer: CERT VU#434904. | ||
10 | |||
11 | Signed-off-by: Sana Kazi <Sana.Kazi@kpit.com> | ||
12 | --- | ||
13 | CHANGELOG | 5 + | ||
14 | Makefile | 3 +- | ||
15 | bld/Android.mk | 2 +- | ||
16 | src/dnsmasq.h | 11 +- | ||
17 | src/dnssec.c | 31 ----- | ||
18 | src/forward.c | 43 ++----- | ||
19 | src/hash_questions.c | 281 +++++++++++++++++++++++++++++++++++++++++++ | ||
20 | src/rfc1035.c | 49 -------- | ||
21 | 8 files changed, 301 insertions(+), 124 deletions(-) | ||
22 | create mode 100644 src/hash_questions.c | ||
23 | |||
24 | CVE: CVE-2020-25685 | ||
25 | Upstream-Status: Backport [https://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=patch;h=2024f9729713fd657d65e64c2e4e471baa0a3e5b] | ||
26 | Comment: No change in any hunk | ||
27 | |||
28 | Index: dnsmasq-2.81/Makefile | ||
29 | =================================================================== | ||
30 | --- dnsmasq-2.81.orig/Makefile | ||
31 | +++ dnsmasq-2.81/Makefile | ||
32 | @@ -77,7 +77,8 @@ objs = cache.o rfc1035.o util.o option.o | ||
33 | helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \ | ||
34 | dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o \ | ||
35 | domain.o dnssec.o blockdata.o tables.o loop.o inotify.o \ | ||
36 | - poll.o rrfilter.o edns0.o arp.o crypto.o dump.o ubus.o metrics.o | ||
37 | + poll.o rrfilter.o edns0.o arp.o crypto.o dump.o ubus.o \ | ||
38 | + metrics.o hash_questions.o | ||
39 | |||
40 | hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \ | ||
41 | dns-protocol.h radv-protocol.h ip6addr.h metrics.h | ||
42 | Index: dnsmasq-2.81/bld/Android.mk | ||
43 | =================================================================== | ||
44 | --- dnsmasq-2.81.orig/bld/Android.mk | ||
45 | +++ dnsmasq-2.81/bld/Android.mk | ||
46 | @@ -11,7 +11,7 @@ LOCAL_SRC_FILES := bpf.c cache.c dbus.c | ||
47 | radv.c slaac.c auth.c ipset.c domain.c \ | ||
48 | dnssec.c dnssec-openssl.c blockdata.c tables.c \ | ||
49 | loop.c inotify.c poll.c rrfilter.c edns0.c arp.c \ | ||
50 | - crypto.c dump.c ubus.c | ||
51 | + crypto.c dump.c ubus.c metrics.c hash_questions.c | ||
52 | |||
53 | LOCAL_MODULE := dnsmasq | ||
54 | |||
55 | Index: dnsmasq-2.81/src/dnsmasq.h | ||
56 | =================================================================== | ||
57 | --- dnsmasq-2.81.orig/src/dnsmasq.h | ||
58 | +++ dnsmasq-2.81/src/dnsmasq.h | ||
59 | @@ -654,11 +654,7 @@ struct hostsfile { | ||
60 | #define FREC_TEST_PKTSZ 256 | ||
61 | #define FREC_HAS_EXTRADATA 512 | ||
62 | |||
63 | -#ifdef HAVE_DNSSEC | ||
64 | -#define HASH_SIZE 20 /* SHA-1 digest size */ | ||
65 | -#else | ||
66 | -#define HASH_SIZE sizeof(int) | ||
67 | -#endif | ||
68 | +#define HASH_SIZE 32 /* SHA-256 digest size */ | ||
69 | |||
70 | struct frec { | ||
71 | union mysockaddr source; | ||
72 | @@ -1218,7 +1214,6 @@ int check_for_bogus_wildcard(struct dns_ | ||
73 | struct bogus_addr *baddr, time_t now); | ||
74 | int check_for_ignored_address(struct dns_header *header, size_t qlen, struct bogus_addr *baddr); | ||
75 | int check_for_local_domain(char *name, time_t now); | ||
76 | -unsigned int questions_crc(struct dns_header *header, size_t plen, char *name); | ||
77 | size_t resize_packet(struct dns_header *header, size_t plen, | ||
78 | unsigned char *pheader, size_t hlen); | ||
79 | int add_resource_record(struct dns_header *header, char *limit, int *truncp, | ||
80 | @@ -1243,9 +1238,11 @@ int dnssec_validate_reply(time_t now, st | ||
81 | int check_unsigned, int *neganswer, int *nons, int *nsec_ttl); | ||
82 | int dnskey_keytag(int alg, int flags, unsigned char *key, int keylen); | ||
83 | size_t filter_rrsigs(struct dns_header *header, size_t plen); | ||
84 | -unsigned char* hash_questions(struct dns_header *header, size_t plen, char *name); | ||
85 | int setup_timestamp(void); | ||
86 | |||
87 | +/* hash_questions.c */ | ||
88 | +unsigned char *hash_questions(struct dns_header *header, size_t plen, char *name); | ||
89 | + | ||
90 | /* crypto.c */ | ||
91 | const struct nettle_hash *hash_find(char *name); | ||
92 | int hash_init(const struct nettle_hash *hash, void **ctxp, unsigned char **digestp); | ||
93 | Index: dnsmasq-2.81/src/dnssec.c | ||
94 | =================================================================== | ||
95 | --- dnsmasq-2.81.orig/src/dnssec.c | ||
96 | +++ dnsmasq-2.81/src/dnssec.c | ||
97 | @@ -2084,35 +2084,4 @@ size_t dnssec_generate_query(struct dns_ | ||
98 | return ret; | ||
99 | } | ||
100 | |||
101 | -unsigned char* hash_questions(struct dns_header *header, size_t plen, char *name) | ||
102 | -{ | ||
103 | - int q; | ||
104 | - unsigned int len; | ||
105 | - unsigned char *p = (unsigned char *)(header+1); | ||
106 | - const struct nettle_hash *hash; | ||
107 | - void *ctx; | ||
108 | - unsigned char *digest; | ||
109 | - | ||
110 | - if (!(hash = hash_find("sha1")) || !hash_init(hash, &ctx, &digest)) | ||
111 | - return NULL; | ||
112 | - | ||
113 | - for (q = ntohs(header->qdcount); q != 0; q--) | ||
114 | - { | ||
115 | - if (!extract_name(header, plen, &p, name, 1, 4)) | ||
116 | - break; /* bad packet */ | ||
117 | - | ||
118 | - len = to_wire(name); | ||
119 | - hash->update(ctx, len, (unsigned char *)name); | ||
120 | - /* CRC the class and type as well */ | ||
121 | - hash->update(ctx, 4, p); | ||
122 | - | ||
123 | - p += 4; | ||
124 | - if (!CHECK_LEN(header, p, plen, 0)) | ||
125 | - break; /* bad packet */ | ||
126 | - } | ||
127 | - | ||
128 | - hash->digest(ctx, hash->digest_size, digest); | ||
129 | - return digest; | ||
130 | -} | ||
131 | - | ||
132 | #endif /* HAVE_DNSSEC */ | ||
133 | Index: dnsmasq-2.81/src/forward.c | ||
134 | =================================================================== | ||
135 | --- dnsmasq-2.81.orig/src/forward.c | ||
136 | +++ dnsmasq-2.81/src/forward.c | ||
137 | @@ -256,19 +256,16 @@ static int forward_query(int udpfd, unio | ||
138 | union all_addr *addrp = NULL; | ||
139 | unsigned int flags = 0; | ||
140 | struct server *start = NULL; | ||
141 | -#ifdef HAVE_DNSSEC | ||
142 | void *hash = hash_questions(header, plen, daemon->namebuff); | ||
143 | +#ifdef HAVE_DNSSEC | ||
144 | int do_dnssec = 0; | ||
145 | -#else | ||
146 | - unsigned int crc = questions_crc(header, plen, daemon->namebuff); | ||
147 | - void *hash = &crc; | ||
148 | #endif | ||
149 | unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL); | ||
150 | unsigned char *oph = find_pseudoheader(header, plen, NULL, NULL, NULL, NULL); | ||
151 | (void)do_bit; | ||
152 | |||
153 | /* may be no servers available. */ | ||
154 | - if (forward || (hash && (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, hash)))) | ||
155 | + if (forward || (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, hash))) | ||
156 | { | ||
157 | /* If we didn't get an answer advertising a maximal packet in EDNS, | ||
158 | fall back to 1280, which should work everywhere on IPv6. | ||
159 | @@ -769,9 +766,6 @@ void reply_query(int fd, int family, tim | ||
160 | size_t nn; | ||
161 | struct server *server; | ||
162 | void *hash; | ||
163 | -#ifndef HAVE_DNSSEC | ||
164 | - unsigned int crc; | ||
165 | -#endif | ||
166 | |||
167 | /* packet buffer overwritten */ | ||
168 | daemon->srv_save = NULL; | ||
169 | @@ -798,12 +792,7 @@ void reply_query(int fd, int family, tim | ||
170 | if (difftime(now, server->pktsz_reduced) > UDP_TEST_TIME) | ||
171 | server->edns_pktsz = daemon->edns_pktsz; | ||
172 | |||
173 | -#ifdef HAVE_DNSSEC | ||
174 | hash = hash_questions(header, n, daemon->namebuff); | ||
175 | -#else | ||
176 | - hash = &crc; | ||
177 | - crc = questions_crc(header, n, daemon->namebuff); | ||
178 | -#endif | ||
179 | |||
180 | if (!(forward = lookup_frec(ntohs(header->id), fd, family, hash))) | ||
181 | return; | ||
182 | @@ -1115,8 +1104,7 @@ void reply_query(int fd, int family, tim | ||
183 | log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, daemon->keyname, (union all_addr *)&(server->addr.in6.sin6_addr), | ||
184 | querystr("dnssec-query", querytype)); | ||
185 | |||
186 | - if ((hash = hash_questions(header, nn, daemon->namebuff))) | ||
187 | - memcpy(new->hash, hash, HASH_SIZE); | ||
188 | + memcpy(new->hash, hash_questions(header, nn, daemon->namebuff), HASH_SIZE); | ||
189 | new->new_id = get_id(); | ||
190 | header->id = htons(new->new_id); | ||
191 | /* Save query for retransmission */ | ||
192 | @@ -1969,15 +1957,9 @@ unsigned char *tcp_request(int confd, ti | ||
193 | if (!flags && last_server) | ||
194 | { | ||
195 | struct server *firstsendto = NULL; | ||
196 | -#ifdef HAVE_DNSSEC | ||
197 | - unsigned char *newhash, hash[HASH_SIZE]; | ||
198 | - if ((newhash = hash_questions(header, (unsigned int)size, daemon->namebuff))) | ||
199 | - memcpy(hash, newhash, HASH_SIZE); | ||
200 | - else | ||
201 | - memset(hash, 0, HASH_SIZE); | ||
202 | -#else | ||
203 | - unsigned int crc = questions_crc(header, (unsigned int)size, daemon->namebuff); | ||
204 | -#endif | ||
205 | + unsigned char hash[HASH_SIZE]; | ||
206 | + memcpy(hash, hash_questions(header, (unsigned int)size, daemon->namebuff), HASH_SIZE); | ||
207 | + | ||
208 | /* Loop round available servers until we succeed in connecting to one. | ||
209 | Note that this code subtly ensures that consecutive queries on this connection | ||
210 | which can go to the same server, do so. */ | ||
211 | @@ -2116,20 +2098,11 @@ unsigned char *tcp_request(int confd, ti | ||
212 | /* If the crc of the question section doesn't match the crc we sent, then | ||
213 | someone might be attempting to insert bogus values into the cache by | ||
214 | sending replies containing questions and bogus answers. */ | ||
215 | -#ifdef HAVE_DNSSEC | ||
216 | - newhash = hash_questions(header, (unsigned int)m, daemon->namebuff); | ||
217 | - if (!newhash || memcmp(hash, newhash, HASH_SIZE) != 0) | ||
218 | + if (memcmp(hash, hash_questions(header, (unsigned int)m, daemon->namebuff), HASH_SIZE) != 0) | ||
219 | { | ||
220 | m = 0; | ||
221 | break; | ||
222 | } | ||
223 | -#else | ||
224 | - if (crc != questions_crc(header, (unsigned int)m, daemon->namebuff)) | ||
225 | - { | ||
226 | - m = 0; | ||
227 | - break; | ||
228 | - } | ||
229 | -#endif | ||
230 | |||
231 | m = process_reply(header, now, last_server, (unsigned int)m, | ||
232 | option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec, cache_secure, bogusanswer, | ||
233 | @@ -2344,7 +2317,7 @@ static struct frec *lookup_frec(unsigned | ||
234 | |||
235 | for(f = daemon->frec_list; f; f = f->next) | ||
236 | if (f->sentto && f->new_id == id && | ||
237 | - (!hash || memcmp(hash, f->hash, HASH_SIZE) == 0)) | ||
238 | + (memcmp(hash, f->hash, HASH_SIZE) == 0)) | ||
239 | { | ||
240 | /* sent from random port */ | ||
241 | if (family == AF_INET && f->rfd4 && f->rfd4->fd == fd) | ||
242 | Index: dnsmasq-2.81/src/hash_questions.c | ||
243 | =================================================================== | ||
244 | --- /dev/null | ||
245 | +++ dnsmasq-2.81/src/hash_questions.c | ||
246 | @@ -0,0 +1,281 @@ | ||
247 | +/* Copyright (c) 2012-2020 Simon Kelley | ||
248 | + | ||
249 | + This program is free software; you can redistribute it and/or modify | ||
250 | + it under the terms of the GNU General Public License as published by | ||
251 | + the Free Software Foundation; version 2 dated June, 1991, or | ||
252 | + (at your option) version 3 dated 29 June, 2007. | ||
253 | + | ||
254 | + This program is distributed in the hope that it will be useful, | ||
255 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
256 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
257 | + GNU General Public License for more details. | ||
258 | + | ||
259 | + You should have received a copy of the GNU General Public License | ||
260 | + along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
261 | +*/ | ||
262 | + | ||
263 | + | ||
264 | +/* Hash the question section. This is used to safely detect query | ||
265 | + retransmission and to detect answers to questions we didn't ask, which | ||
266 | + might be poisoning attacks. Note that we decode the name rather | ||
267 | + than CRC the raw bytes, since replies might be compressed differently. | ||
268 | + We ignore case in the names for the same reason. | ||
269 | + | ||
270 | + The hash used is SHA-256. If we're building with DNSSEC support, | ||
271 | + we use the Nettle cypto library. If not, we prefer not to | ||
272 | + add a dependency on Nettle, and use a stand-alone implementaion. | ||
273 | +*/ | ||
274 | + | ||
275 | +#include "dnsmasq.h" | ||
276 | + | ||
277 | +#ifdef HAVE_DNSSEC | ||
278 | +unsigned char *hash_questions(struct dns_header *header, size_t plen, char *name) | ||
279 | +{ | ||
280 | + int q; | ||
281 | + unsigned char *p = (unsigned char *)(header+1); | ||
282 | + const struct nettle_hash *hash; | ||
283 | + void *ctx; | ||
284 | + unsigned char *digest; | ||
285 | + | ||
286 | + if (!(hash = hash_find("sha256")) || !hash_init(hash, &ctx, &digest)) | ||
287 | + { | ||
288 | + /* don't think this can ever happen. */ | ||
289 | + static unsigned char dummy[HASH_SIZE]; | ||
290 | + static int warned = 0; | ||
291 | + | ||
292 | + if (warned) | ||
293 | + my_syslog(LOG_ERR, _("Failed to create SHA-256 hash object")); | ||
294 | + warned = 1; | ||
295 | + | ||
296 | + return dummy; | ||
297 | + } | ||
298 | + | ||
299 | + for (q = ntohs(header->qdcount); q != 0; q--) | ||
300 | + { | ||
301 | + char *cp, c; | ||
302 | + | ||
303 | + if (!extract_name(header, plen, &p, name, 1, 4)) | ||
304 | + break; /* bad packet */ | ||
305 | + | ||
306 | + for (cp = name; (c = *cp); cp++) | ||
307 | + if (c >= 'A' && c <= 'Z') | ||
308 | + *cp += 'a' - 'A'; | ||
309 | + | ||
310 | + hash->update(ctx, cp - name, (unsigned char *)name); | ||
311 | + /* CRC the class and type as well */ | ||
312 | + hash->update(ctx, 4, p); | ||
313 | + | ||
314 | + p += 4; | ||
315 | + if (!CHECK_LEN(header, p, plen, 0)) | ||
316 | + break; /* bad packet */ | ||
317 | + } | ||
318 | + | ||
319 | + hash->digest(ctx, hash->digest_size, digest); | ||
320 | + return digest; | ||
321 | +} | ||
322 | + | ||
323 | +#else /* HAVE_DNSSEC */ | ||
324 | + | ||
325 | +#define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest | ||
326 | +typedef unsigned char BYTE; // 8-bit byte | ||
327 | +typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines | ||
328 | + | ||
329 | +typedef struct { | ||
330 | + BYTE data[64]; | ||
331 | + WORD datalen; | ||
332 | + unsigned long long bitlen; | ||
333 | + WORD state[8]; | ||
334 | +} SHA256_CTX; | ||
335 | + | ||
336 | +static void sha256_init(SHA256_CTX *ctx); | ||
337 | +static void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len); | ||
338 | +static void sha256_final(SHA256_CTX *ctx, BYTE hash[]); | ||
339 | + | ||
340 | + | ||
341 | +unsigned char *hash_questions(struct dns_header *header, size_t plen, char *name) | ||
342 | +{ | ||
343 | + int q; | ||
344 | + unsigned char *p = (unsigned char *)(header+1); | ||
345 | + SHA256_CTX ctx; | ||
346 | + static BYTE digest[SHA256_BLOCK_SIZE]; | ||
347 | + | ||
348 | + sha256_init(&ctx); | ||
349 | + | ||
350 | + for (q = ntohs(header->qdcount); q != 0; q--) | ||
351 | + { | ||
352 | + char *cp, c; | ||
353 | + | ||
354 | + if (!extract_name(header, plen, &p, name, 1, 4)) | ||
355 | + break; /* bad packet */ | ||
356 | + | ||
357 | + for (cp = name; (c = *cp); cp++) | ||
358 | + if (c >= 'A' && c <= 'Z') | ||
359 | + *cp += 'a' - 'A'; | ||
360 | + | ||
361 | + sha256_update(&ctx, (BYTE *)name, cp - name); | ||
362 | + /* CRC the class and type as well */ | ||
363 | + sha256_update(&ctx, (BYTE *)p, 4); | ||
364 | + | ||
365 | + p += 4; | ||
366 | + if (!CHECK_LEN(header, p, plen, 0)) | ||
367 | + break; /* bad packet */ | ||
368 | + } | ||
369 | + | ||
370 | + sha256_final(&ctx, digest); | ||
371 | + return (unsigned char *)digest; | ||
372 | +} | ||
373 | + | ||
374 | +/* Code from here onwards comes from https://github.com/B-Con/crypto-algorithms | ||
375 | + and was written by Brad Conte (brad@bradconte.com), to whom all credit is given. | ||
376 | + | ||
377 | + This code is in the public domain, and the copyright notice at the head of this | ||
378 | + file does not apply to it. | ||
379 | +*/ | ||
380 | + | ||
381 | + | ||
382 | +/****************************** MACROS ******************************/ | ||
383 | +#define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b)))) | ||
384 | +#define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b)))) | ||
385 | + | ||
386 | +#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z))) | ||
387 | +#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) | ||
388 | +#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22)) | ||
389 | +#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25)) | ||
390 | +#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3)) | ||
391 | +#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10)) | ||
392 | + | ||
393 | +/**************************** VARIABLES *****************************/ | ||
394 | +static const WORD k[64] = { | ||
395 | + 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5, | ||
396 | + 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174, | ||
397 | + 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da, | ||
398 | + 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967, | ||
399 | + 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85, | ||
400 | + 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070, | ||
401 | + 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3, | ||
402 | + 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 | ||
403 | +}; | ||
404 | + | ||
405 | +/*********************** FUNCTION DEFINITIONS ***********************/ | ||
406 | +static void sha256_transform(SHA256_CTX *ctx, const BYTE data[]) | ||
407 | +{ | ||
408 | + WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; | ||
409 | + | ||
410 | + for (i = 0, j = 0; i < 16; ++i, j += 4) | ||
411 | + m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]); | ||
412 | + for ( ; i < 64; ++i) | ||
413 | + m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16]; | ||
414 | + | ||
415 | + a = ctx->state[0]; | ||
416 | + b = ctx->state[1]; | ||
417 | + c = ctx->state[2]; | ||
418 | + d = ctx->state[3]; | ||
419 | + e = ctx->state[4]; | ||
420 | + f = ctx->state[5]; | ||
421 | + g = ctx->state[6]; | ||
422 | + h = ctx->state[7]; | ||
423 | + | ||
424 | + for (i = 0; i < 64; ++i) | ||
425 | + { | ||
426 | + t1 = h + EP1(e) + CH(e,f,g) + k[i] + m[i]; | ||
427 | + t2 = EP0(a) + MAJ(a,b,c); | ||
428 | + h = g; | ||
429 | + g = f; | ||
430 | + f = e; | ||
431 | + e = d + t1; | ||
432 | + d = c; | ||
433 | + c = b; | ||
434 | + b = a; | ||
435 | + a = t1 + t2; | ||
436 | + } | ||
437 | + | ||
438 | + ctx->state[0] += a; | ||
439 | + ctx->state[1] += b; | ||
440 | + ctx->state[2] += c; | ||
441 | + ctx->state[3] += d; | ||
442 | + ctx->state[4] += e; | ||
443 | + ctx->state[5] += f; | ||
444 | + ctx->state[6] += g; | ||
445 | + ctx->state[7] += h; | ||
446 | +} | ||
447 | + | ||
448 | +static void sha256_init(SHA256_CTX *ctx) | ||
449 | +{ | ||
450 | + ctx->datalen = 0; | ||
451 | + ctx->bitlen = 0; | ||
452 | + ctx->state[0] = 0x6a09e667; | ||
453 | + ctx->state[1] = 0xbb67ae85; | ||
454 | + ctx->state[2] = 0x3c6ef372; | ||
455 | + ctx->state[3] = 0xa54ff53a; | ||
456 | + ctx->state[4] = 0x510e527f; | ||
457 | + ctx->state[5] = 0x9b05688c; | ||
458 | + ctx->state[6] = 0x1f83d9ab; | ||
459 | + ctx->state[7] = 0x5be0cd19; | ||
460 | +} | ||
461 | + | ||
462 | +static void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len) | ||
463 | +{ | ||
464 | + WORD i; | ||
465 | + | ||
466 | + for (i = 0; i < len; ++i) | ||
467 | + { | ||
468 | + ctx->data[ctx->datalen] = data[i]; | ||
469 | + ctx->datalen++; | ||
470 | + if (ctx->datalen == 64) { | ||
471 | + sha256_transform(ctx, ctx->data); | ||
472 | + ctx->bitlen += 512; | ||
473 | + ctx->datalen = 0; | ||
474 | + } | ||
475 | + } | ||
476 | +} | ||
477 | + | ||
478 | +static void sha256_final(SHA256_CTX *ctx, BYTE hash[]) | ||
479 | +{ | ||
480 | + WORD i; | ||
481 | + | ||
482 | + i = ctx->datalen; | ||
483 | + | ||
484 | + // Pad whatever data is left in the buffer. | ||
485 | + if (ctx->datalen < 56) | ||
486 | + { | ||
487 | + ctx->data[i++] = 0x80; | ||
488 | + while (i < 56) | ||
489 | + ctx->data[i++] = 0x00; | ||
490 | + } | ||
491 | + else | ||
492 | + { | ||
493 | + ctx->data[i++] = 0x80; | ||
494 | + while (i < 64) | ||
495 | + ctx->data[i++] = 0x00; | ||
496 | + sha256_transform(ctx, ctx->data); | ||
497 | + memset(ctx->data, 0, 56); | ||
498 | + } | ||
499 | + | ||
500 | + // Append to the padding the total message's length in bits and transform. | ||
501 | + ctx->bitlen += ctx->datalen * 8; | ||
502 | + ctx->data[63] = ctx->bitlen; | ||
503 | + ctx->data[62] = ctx->bitlen >> 8; | ||
504 | + ctx->data[61] = ctx->bitlen >> 16; | ||
505 | + ctx->data[60] = ctx->bitlen >> 24; | ||
506 | + ctx->data[59] = ctx->bitlen >> 32; | ||
507 | + ctx->data[58] = ctx->bitlen >> 40; | ||
508 | + ctx->data[57] = ctx->bitlen >> 48; | ||
509 | + ctx->data[56] = ctx->bitlen >> 56; | ||
510 | + sha256_transform(ctx, ctx->data); | ||
511 | + | ||
512 | + // Since this implementation uses little endian byte ordering and SHA uses big endian, | ||
513 | + // reverse all the bytes when copying the final state to the output hash. | ||
514 | + for (i = 0; i < 4; ++i) | ||
515 | + { | ||
516 | + hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff; | ||
517 | + hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff; | ||
518 | + hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff; | ||
519 | + hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff; | ||
520 | + hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff; | ||
521 | + hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff; | ||
522 | + hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff; | ||
523 | + hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff; | ||
524 | + } | ||
525 | +} | ||
526 | + | ||
527 | +#endif | ||
528 | Index: dnsmasq-2.81/src/rfc1035.c | ||
529 | =================================================================== | ||
530 | --- dnsmasq-2.81.orig/src/rfc1035.c | ||
531 | +++ dnsmasq-2.81/src/rfc1035.c | ||
532 | @@ -333,55 +333,6 @@ unsigned char *skip_section(unsigned cha | ||
533 | return ansp; | ||
534 | } | ||
535 | |||
536 | -/* CRC the question section. This is used to safely detect query | ||
537 | - retransmission and to detect answers to questions we didn't ask, which | ||
538 | - might be poisoning attacks. Note that we decode the name rather | ||
539 | - than CRC the raw bytes, since replies might be compressed differently. | ||
540 | - We ignore case in the names for the same reason. Return all-ones | ||
541 | - if there is not question section. */ | ||
542 | -#ifndef HAVE_DNSSEC | ||
543 | -unsigned int questions_crc(struct dns_header *header, size_t plen, char *name) | ||
544 | -{ | ||
545 | - int q; | ||
546 | - unsigned int crc = 0xffffffff; | ||
547 | - unsigned char *p1, *p = (unsigned char *)(header+1); | ||
548 | - | ||
549 | - for (q = ntohs(header->qdcount); q != 0; q--) | ||
550 | - { | ||
551 | - if (!extract_name(header, plen, &p, name, 1, 4)) | ||
552 | - return crc; /* bad packet */ | ||
553 | - | ||
554 | - for (p1 = (unsigned char *)name; *p1; p1++) | ||
555 | - { | ||
556 | - int i = 8; | ||
557 | - char c = *p1; | ||
558 | - | ||
559 | - if (c >= 'A' && c <= 'Z') | ||
560 | - c += 'a' - 'A'; | ||
561 | - | ||
562 | - crc ^= c << 24; | ||
563 | - while (i--) | ||
564 | - crc = crc & 0x80000000 ? (crc << 1) ^ 0x04c11db7 : crc << 1; | ||
565 | - } | ||
566 | - | ||
567 | - /* CRC the class and type as well */ | ||
568 | - for (p1 = p; p1 < p+4; p1++) | ||
569 | - { | ||
570 | - int i = 8; | ||
571 | - crc ^= *p1 << 24; | ||
572 | - while (i--) | ||
573 | - crc = crc & 0x80000000 ? (crc << 1) ^ 0x04c11db7 : crc << 1; | ||
574 | - } | ||
575 | - | ||
576 | - p += 4; | ||
577 | - if (!CHECK_LEN(header, p, plen, 0)) | ||
578 | - return crc; /* bad packet */ | ||
579 | - } | ||
580 | - | ||
581 | - return crc; | ||
582 | -} | ||
583 | -#endif | ||
584 | - | ||
585 | size_t resize_packet(struct dns_header *header, size_t plen, unsigned char *pheader, size_t hlen) | ||
586 | { | ||
587 | unsigned char *ansp = skip_questions(header, plen); | ||
diff --git a/meta-networking/recipes-support/dnsmasq/files/CVE-2020-25685-2.patch b/meta-networking/recipes-support/dnsmasq/files/CVE-2020-25685-2.patch new file mode 100644 index 0000000000..302c42ccca --- /dev/null +++ b/meta-networking/recipes-support/dnsmasq/files/CVE-2020-25685-2.patch | |||
@@ -0,0 +1,175 @@ | |||
1 | From 2024f9729713fd657d65e64c2e4e471baa0a3e5b Mon Sep 17 00:00:00 2001 | ||
2 | From: =?utf8?q?Petr=20Men=C5=A1=C3=ADk?= <pemensik@redhat.com> | ||
3 | Date: Wed, 25 Nov 2020 17:18:55 +0100 | ||
4 | Subject: [PATCH] Support hash function from nettle (only) | ||
5 | |||
6 | Unlike COPTS=-DHAVE_DNSSEC, allow usage of just sha256 function from | ||
7 | nettle, but keep DNSSEC disabled at build time. Skips use of internal | ||
8 | hash implementation without support for validation built-in. | ||
9 | |||
10 | Signed-off-by: Sana Kazi <Sana.Kazi@kpit.com> | ||
11 | --- | ||
12 | Makefile | 8 +++++--- | ||
13 | bld/pkg-wrapper | 41 ++++++++++++++++++++++------------------- | ||
14 | src/config.h | 8 ++++++++ | ||
15 | src/crypto.c | 7 +++++++ | ||
16 | src/dnsmasq.h | 2 +- | ||
17 | src/hash_questions.c | 2 +- | ||
18 | 6 files changed, 44 insertions(+), 24 deletions(-) | ||
19 | |||
20 | CVE: CVE-2020-25685 | ||
21 | Upstream-Status: Backport [https://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=patch;h=2024f9729713fd657d65e64c2e4e471baa0a3e5b] | ||
22 | Comment: Refreshed a hunk from pkg-wrapper and second hunk from Makefile | ||
23 | |||
24 | Index: dnsmasq-2.81/Makefile | ||
25 | =================================================================== | ||
26 | --- dnsmasq-2.81.orig/Makefile | ||
27 | +++ dnsmasq-2.81/Makefile | ||
28 | @@ -53,7 +53,7 @@ top?=$(CURDIR) | ||
29 | |||
30 | dbus_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DBUS $(PKG_CONFIG) --cflags dbus-1` | ||
31 | dbus_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DBUS $(PKG_CONFIG) --libs dbus-1` | ||
32 | -ubus_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_UBUS "" --copy -lubox -lubus` | ||
33 | +ubus_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_UBUS "" --copy '-lubox -lubus'` | ||
34 | idn_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_IDN $(PKG_CONFIG) --cflags libidn` | ||
35 | idn_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_IDN $(PKG_CONFIG) --libs libidn` | ||
36 | idn2_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LIBIDN2 $(PKG_CONFIG) --cflags libidn2` | ||
37 | @@ -62,8 +62,10 @@ ct_cflags = `echo $(COPTS) | $(top)/ | ||
38 | ct_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_CONNTRACK $(PKG_CONFIG) --libs libnetfilter_conntrack` | ||
39 | lua_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --cflags lua` | ||
40 | lua_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --libs lua` | ||
41 | -nettle_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG) --cflags nettle hogweed` | ||
42 | -nettle_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG) --libs nettle hogweed` | ||
43 | +nettle_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG) --cflags 'nettle hogweed' \ | ||
44 | + HAVE_NETTLEHASH $(PKG_CONFIG) --cflags nettle` | ||
45 | +nettle_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG) --libs 'nettle hogweed' \ | ||
46 | + HAVE_NETTLEHASH $(PKG_CONFIG) --libs nettle` | ||
47 | gmp_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC NO_GMP --copy -lgmp` | ||
48 | sunos_libs = `if uname | grep SunOS >/dev/null 2>&1; then echo -lsocket -lnsl -lposix4; fi` | ||
49 | version = -DVERSION='\"`$(top)/bld/get-version $(top)`\"' | ||
50 | Index: dnsmasq-2.81/bld/pkg-wrapper | ||
51 | =================================================================== | ||
52 | --- dnsmasq-2.81.orig/bld/pkg-wrapper | ||
53 | +++ dnsmasq-2.81/bld/pkg-wrapper | ||
54 | @@ -1,35 +1,37 @@ | ||
55 | #!/bin/sh | ||
56 | |||
57 | -search=$1 | ||
58 | -shift | ||
59 | -pkg=$1 | ||
60 | -shift | ||
61 | -op=$1 | ||
62 | -shift | ||
63 | - | ||
64 | in=`cat` | ||
65 | |||
66 | -if grep "^\#[[:space:]]*define[[:space:]]*$search" config.h >/dev/null 2>&1 || \ | ||
67 | - echo $in | grep $search >/dev/null 2>&1; then | ||
68 | +search() | ||
69 | +{ | ||
70 | + grep "^\#[[:space:]]*define[[:space:]]*$1" config.h >/dev/null 2>&1 || \ | ||
71 | + echo $in | grep $1 >/dev/null 2>&1 | ||
72 | +} | ||
73 | + | ||
74 | +while [ "$#" -gt 0 ]; do | ||
75 | + search=$1 | ||
76 | + pkg=$2 | ||
77 | + op=$3 | ||
78 | + lib=$4 | ||
79 | + shift 4 | ||
80 | +if search "$search"; then | ||
81 | + | ||
82 | # Nasty, nasty, in --copy, arg 2 (if non-empty) is another config to search for, used with NO_GMP | ||
83 | if [ $op = "--copy" ]; then | ||
84 | if [ -z "$pkg" ]; then | ||
85 | - pkg="$*" | ||
86 | - elif grep "^\#[[:space:]]*define[[:space:]]*$pkg" config.h >/dev/null 2>&1 || \ | ||
87 | - echo $in | grep $pkg >/dev/null 2>&1; then | ||
88 | + pkg="$lib" | ||
89 | + elif search "$pkg"; then | ||
90 | pkg="" | ||
91 | else | ||
92 | - pkg="$*" | ||
93 | + pkg="$lib" | ||
94 | fi | ||
95 | - elif grep "^\#[[:space:]]*define[[:space:]]*${search}_STATIC" config.h >/dev/null 2>&1 || \ | ||
96 | - echo $in | grep ${search}_STATIC >/dev/null 2>&1; then | ||
97 | - pkg=`$pkg --static $op $*` | ||
98 | + elif search "${search}_STATIC"; then | ||
99 | + pkg=`$pkg --static $op $lib` | ||
100 | else | ||
101 | - pkg=`$pkg $op $*` | ||
102 | + pkg=`$pkg $op $lib` | ||
103 | fi | ||
104 | |||
105 | - if grep "^\#[[:space:]]*define[[:space:]]*${search}_STATIC" config.h >/dev/null 2>&1 || \ | ||
106 | - echo $in | grep ${search}_STATIC >/dev/null 2>&1; then | ||
107 | + if search "${search}_STATIC"; then | ||
108 | if [ $op = "--libs" ] || [ $op = "--copy" ]; then | ||
109 | echo "-Wl,-Bstatic $pkg -Wl,-Bdynamic" | ||
110 | else | ||
111 | @@ -40,3 +42,4 @@ if grep "^\#[[:space:]]*define[[:space:] | ||
112 | fi | ||
113 | fi | ||
114 | |||
115 | +done | ||
116 | Index: dnsmasq-2.81/src/config.h | ||
117 | =================================================================== | ||
118 | --- dnsmasq-2.81.orig/src/config.h | ||
119 | +++ dnsmasq-2.81/src/config.h | ||
120 | @@ -118,6 +118,9 @@ HAVE_AUTH | ||
121 | define this to include the facility to act as an authoritative DNS | ||
122 | server for one or more zones. | ||
123 | |||
124 | +HAVE_NETTLEHASH | ||
125 | + include just hash function from nettle, but no DNSSEC. | ||
126 | + | ||
127 | HAVE_DNSSEC | ||
128 | include DNSSEC validator. | ||
129 | |||
130 | @@ -185,6 +188,7 @@ RESOLVFILE | ||
131 | /* #define HAVE_IDN */ | ||
132 | /* #define HAVE_LIBIDN2 */ | ||
133 | /* #define HAVE_CONNTRACK */ | ||
134 | +/* #define HAVE_NETTLEHASH */ | ||
135 | /* #define HAVE_DNSSEC */ | ||
136 | |||
137 | |||
138 | @@ -418,6 +422,10 @@ static char *compile_opts = | ||
139 | "no-" | ||
140 | #endif | ||
141 | "auth " | ||
142 | +#if !defined(HAVE_NETTLEHASH) && !defined(HAVE_DNSSEC) | ||
143 | +"no-" | ||
144 | +#endif | ||
145 | +"nettlehash " | ||
146 | #ifndef HAVE_DNSSEC | ||
147 | "no-" | ||
148 | #endif | ||
149 | Index: dnsmasq-2.81/src/dnsmasq.h | ||
150 | =================================================================== | ||
151 | --- dnsmasq-2.81.orig/src/dnsmasq.h | ||
152 | +++ dnsmasq-2.81/src/dnsmasq.h | ||
153 | @@ -161,6 +161,9 @@ extern int capget(cap_user_header_t head | ||
154 | # include <nettle/nettle-meta.h> | ||
155 | #endif | ||
156 | |||
157 | +#if defined(HAVE_DNSSEC) || defined(HAVE_NETTLEHASH) | ||
158 | +# include <nettle/nettle-meta.h> | ||
159 | +#endif | ||
160 | /* daemon is function in the C library.... */ | ||
161 | #define daemon dnsmasq_daemon | ||
162 | |||
163 | Index: dnsmasq-2.81/src/hash_questions.c | ||
164 | =================================================================== | ||
165 | --- dnsmasq-2.81.orig/src/hash_questions.c | ||
166 | +++ dnsmasq-2.81/src/hash_questions.c | ||
167 | @@ -28,7 +28,7 @@ | ||
168 | |||
169 | #include "dnsmasq.h" | ||
170 | |||
171 | -#ifdef HAVE_DNSSEC | ||
172 | +#if defined(HAVE_DNSSEC) || defined(HAVE_NETTLEHASH) | ||
173 | unsigned char *hash_questions(struct dns_header *header, size_t plen, char *name) | ||
174 | { | ||
175 | int q; | ||
diff --git a/meta-networking/recipes-support/dnsmasq/files/CVE-2020-25686-1.patch b/meta-networking/recipes-support/dnsmasq/files/CVE-2020-25686-1.patch new file mode 100644 index 0000000000..fd9d0a9b16 --- /dev/null +++ b/meta-networking/recipes-support/dnsmasq/files/CVE-2020-25686-1.patch | |||
@@ -0,0 +1,332 @@ | |||
1 | From 15b60ddf935a531269bb8c68198de012a4967156 Mon Sep 17 00:00:00 2001 | ||
2 | From: Simon Kelley <simon@thekelleys.org.uk> | ||
3 | Date: Wed, 18 Nov 2020 18:34:55 +0000 | ||
4 | Subject: [PATCH] Handle multiple identical near simultaneous DNS queries | ||
5 | better. | ||
6 | |||
7 | Previously, such queries would all be forwarded | ||
8 | independently. This is, in theory, inefficent but in practise | ||
9 | not a problem, _except_ that is means that an answer for any | ||
10 | of the forwarded queries will be accepted and cached. | ||
11 | An attacker can send a query multiple times, and for each repeat, | ||
12 | another {port, ID} becomes capable of accepting the answer he is | ||
13 | sending in the blind, to random IDs and ports. The chance of a | ||
14 | succesful attack is therefore multiplied by the number of repeats | ||
15 | of the query. The new behaviour detects repeated queries and | ||
16 | merely stores the clients sending repeats so that when the | ||
17 | first query completes, the answer can be sent to all the | ||
18 | clients who asked. Refer: CERT VU#434904. | ||
19 | |||
20 | Signed-off-by: Sana Kazi <Sana.Kazi@kpit.com> | ||
21 | --- | ||
22 | CHANGELOG | 16 +++++- | ||
23 | src/dnsmasq.h | 19 ++++--- | ||
24 | src/forward.c | 142 ++++++++++++++++++++++++++++++++++++++++++-------- | ||
25 | 3 files changed, 147 insertions(+), 30 deletions(-) | ||
26 | |||
27 | CVE: CVE-2020-25686 | ||
28 | Upstream-Status: Backport [http://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=commit;h=15b60ddf935a531269bb8c68198de012a4967156] | ||
29 | Comment: No change in any hunk | ||
30 | |||
31 | Index: dnsmasq-2.81/src/dnsmasq.h | ||
32 | =================================================================== | ||
33 | --- dnsmasq-2.81.orig/src/dnsmasq.h | ||
34 | +++ dnsmasq-2.81/src/dnsmasq.h | ||
35 | @@ -655,19 +655,24 @@ struct hostsfile { | ||
36 | #define FREC_DO_QUESTION 64 | ||
37 | #define FREC_ADDED_PHEADER 128 | ||
38 | #define FREC_TEST_PKTSZ 256 | ||
39 | -#define FREC_HAS_EXTRADATA 512 | ||
40 | +#define FREC_HAS_EXTRADATA 512 | ||
41 | +#define FREC_HAS_PHEADER 1024 | ||
42 | |||
43 | #define HASH_SIZE 32 /* SHA-256 digest size */ | ||
44 | |||
45 | struct frec { | ||
46 | - union mysockaddr source; | ||
47 | - union all_addr dest; | ||
48 | + struct frec_src { | ||
49 | + union mysockaddr source; | ||
50 | + union all_addr dest; | ||
51 | + unsigned int iface, log_id; | ||
52 | + unsigned short orig_id; | ||
53 | + struct frec_src *next; | ||
54 | + } frec_src; | ||
55 | struct server *sentto; /* NULL means free */ | ||
56 | struct randfd *rfd4; | ||
57 | struct randfd *rfd6; | ||
58 | - unsigned int iface; | ||
59 | - unsigned short orig_id, new_id; | ||
60 | - int log_id, fd, forwardall, flags; | ||
61 | + unsigned short new_id; | ||
62 | + int fd, forwardall, flags; | ||
63 | time_t time; | ||
64 | unsigned char *hash[HASH_SIZE]; | ||
65 | #ifdef HAVE_DNSSEC | ||
66 | @@ -1085,6 +1090,8 @@ extern struct daemon { | ||
67 | int back_to_the_future; | ||
68 | #endif | ||
69 | struct frec *frec_list; | ||
70 | + struct frec_src *free_frec_src; | ||
71 | + int frec_src_count; | ||
72 | struct serverfd *sfds; | ||
73 | struct irec *interfaces; | ||
74 | struct listener *listeners; | ||
75 | Index: dnsmasq-2.81/src/forward.c | ||
76 | =================================================================== | ||
77 | --- dnsmasq-2.81.orig/src/forward.c | ||
78 | +++ dnsmasq-2.81/src/forward.c | ||
79 | @@ -20,6 +20,8 @@ static struct frec *lookup_frec(unsigned | ||
80 | static struct frec *lookup_frec_by_sender(unsigned short id, | ||
81 | union mysockaddr *addr, | ||
82 | void *hash); | ||
83 | +static struct frec *lookup_frec_by_query(void *hash, unsigned int flags); | ||
84 | + | ||
85 | static unsigned short get_id(void); | ||
86 | static void free_frec(struct frec *f); | ||
87 | |||
88 | @@ -255,6 +257,7 @@ static int forward_query(int udpfd, unio | ||
89 | int type = SERV_DO_DNSSEC, norebind = 0; | ||
90 | union all_addr *addrp = NULL; | ||
91 | unsigned int flags = 0; | ||
92 | + unsigned int fwd_flags = 0; | ||
93 | struct server *start = NULL; | ||
94 | void *hash = hash_questions(header, plen, daemon->namebuff); | ||
95 | #ifdef HAVE_DNSSEC | ||
96 | @@ -263,7 +266,18 @@ static int forward_query(int udpfd, unio | ||
97 | unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL); | ||
98 | unsigned char *oph = find_pseudoheader(header, plen, NULL, NULL, NULL, NULL); | ||
99 | (void)do_bit; | ||
100 | - | ||
101 | + | ||
102 | + if (header->hb4 & HB4_CD) | ||
103 | + fwd_flags |= FREC_CHECKING_DISABLED; | ||
104 | + if (ad_reqd) | ||
105 | + fwd_flags |= FREC_AD_QUESTION; | ||
106 | + if (oph) | ||
107 | + fwd_flags |= FREC_HAS_PHEADER; | ||
108 | +#ifdef HAVE_DNSSEC | ||
109 | + if (do_bit) | ||
110 | + fwd_flags |= FREC_DO_QUESTION; | ||
111 | +#endif | ||
112 | + | ||
113 | /* may be no servers available. */ | ||
114 | if (forward || (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, hash))) | ||
115 | { | ||
116 | @@ -336,6 +350,39 @@ static int forward_query(int udpfd, unio | ||
117 | } | ||
118 | else | ||
119 | { | ||
120 | + /* Query from new source, but the same query may be in progress | ||
121 | + from another source. If so, just add this client to the | ||
122 | + list that will get the reply. | ||
123 | + | ||
124 | + Note that is the EDNS client subnet option is in use, we can't do this, | ||
125 | + as the clients (and therefore query EDNS options) will be different | ||
126 | + for each query. The EDNS subnet code has checks to avoid | ||
127 | + attacks in this case. */ | ||
128 | + if (!option_bool(OPT_CLIENT_SUBNET) && (forward = lookup_frec_by_query(hash, fwd_flags))) | ||
129 | + { | ||
130 | + /* Note whine_malloc() zeros memory. */ | ||
131 | + if (!daemon->free_frec_src && | ||
132 | + daemon->frec_src_count < daemon->ftabsize && | ||
133 | + (daemon->free_frec_src = whine_malloc(sizeof(struct frec_src)))) | ||
134 | + daemon->frec_src_count++; | ||
135 | + | ||
136 | + /* If we've been spammed with many duplicates, just drop the query. */ | ||
137 | + if (daemon->free_frec_src) | ||
138 | + { | ||
139 | + struct frec_src *new = daemon->free_frec_src; | ||
140 | + daemon->free_frec_src = new->next; | ||
141 | + new->next = forward->frec_src.next; | ||
142 | + forward->frec_src.next = new; | ||
143 | + new->orig_id = ntohs(header->id); | ||
144 | + new->source = *udpaddr; | ||
145 | + new->dest = *dst_addr; | ||
146 | + new->log_id = daemon->log_id; | ||
147 | + new->iface = dst_iface; | ||
148 | + } | ||
149 | + | ||
150 | + return 1; | ||
151 | + } | ||
152 | + | ||
153 | if (gotname) | ||
154 | flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind); | ||
155 | |||
156 | @@ -343,22 +390,22 @@ static int forward_query(int udpfd, unio | ||
157 | do_dnssec = type & SERV_DO_DNSSEC; | ||
158 | #endif | ||
159 | type &= ~SERV_DO_DNSSEC; | ||
160 | - | ||
161 | + | ||
162 | if (daemon->servers && !flags) | ||
163 | forward = get_new_frec(now, NULL, NULL); | ||
164 | /* table full - flags == 0, return REFUSED */ | ||
165 | |||
166 | if (forward) | ||
167 | { | ||
168 | - forward->source = *udpaddr; | ||
169 | - forward->dest = *dst_addr; | ||
170 | - forward->iface = dst_iface; | ||
171 | - forward->orig_id = ntohs(header->id); | ||
172 | + forward->frec_src.source = *udpaddr; | ||
173 | + forward->frec_src.orig_id = ntohs(header->id); | ||
174 | + forward->frec_src.dest = *dst_addr; | ||
175 | + forward->frec_src.iface = dst_iface; | ||
176 | forward->new_id = get_id(); | ||
177 | forward->fd = udpfd; | ||
178 | memcpy(forward->hash, hash, HASH_SIZE); | ||
179 | forward->forwardall = 0; | ||
180 | - forward->flags = 0; | ||
181 | + forward->flags = fwd_flags; | ||
182 | if (norebind) | ||
183 | forward->flags |= FREC_NOREBIND; | ||
184 | if (header->hb4 & HB4_CD) | ||
185 | @@ -413,9 +460,9 @@ static int forward_query(int udpfd, unio | ||
186 | unsigned char *pheader; | ||
187 | |||
188 | /* If a query is retried, use the log_id for the retry when logging the answer. */ | ||
189 | - forward->log_id = daemon->log_id; | ||
190 | + forward->frec_src.log_id = daemon->log_id; | ||
191 | |||
192 | - plen = add_edns0_config(header, plen, ((unsigned char *)header) + PACKETSZ, &forward->source, now, &subnet); | ||
193 | + plen = add_edns0_config(header, plen, ((unsigned char *)header) + PACKETSZ, &forward->frec_src.source, now, &subnet); | ||
194 | |||
195 | if (subnet) | ||
196 | forward->flags |= FREC_HAS_SUBNET; | ||
197 | @@ -552,7 +599,7 @@ static int forward_query(int udpfd, unio | ||
198 | return 1; | ||
199 | |||
200 | /* could not send on, prepare to return */ | ||
201 | - header->id = htons(forward->orig_id); | ||
202 | + header->id = htons(forward->frec_src.orig_id); | ||
203 | free_frec(forward); /* cancel */ | ||
204 | } | ||
205 | |||
206 | @@ -804,8 +851,8 @@ void reply_query(int fd, int family, tim | ||
207 | |||
208 | /* log_query gets called indirectly all over the place, so | ||
209 | pass these in global variables - sorry. */ | ||
210 | - daemon->log_display_id = forward->log_id; | ||
211 | - daemon->log_source_addr = &forward->source; | ||
212 | + daemon->log_display_id = forward->frec_src.log_id; | ||
213 | + daemon->log_source_addr = &forward->frec_src.source; | ||
214 | |||
215 | if (daemon->ignore_addr && RCODE(header) == NOERROR && | ||
216 | check_for_ignored_address(header, n, daemon->ignore_addr)) | ||
217 | @@ -1077,6 +1124,7 @@ void reply_query(int fd, int family, tim | ||
218 | new->sentto = server; | ||
219 | new->rfd4 = NULL; | ||
220 | new->rfd6 = NULL; | ||
221 | + new->frec_src.next = NULL; | ||
222 | new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY | FREC_HAS_EXTRADATA); | ||
223 | new->forwardall = 0; | ||
224 | |||
225 | @@ -1212,9 +1260,11 @@ void reply_query(int fd, int family, tim | ||
226 | |||
227 | if ((nn = process_reply(header, now, forward->sentto, (size_t)n, check_rebind, no_cache_dnssec, cache_secure, bogusanswer, | ||
228 | forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION, | ||
229 | - forward->flags & FREC_ADDED_PHEADER, forward->flags & FREC_HAS_SUBNET, &forward->source))) | ||
230 | + forward->flags & FREC_ADDED_PHEADER, forward->flags & FREC_HAS_SUBNET, &forward->frec_src.source))) | ||
231 | { | ||
232 | - header->id = htons(forward->orig_id); | ||
233 | + struct frec_src *src; | ||
234 | + | ||
235 | + header->id = htons(forward->frec_src.orig_id); | ||
236 | header->hb4 |= HB4_RA; /* recursion if available */ | ||
237 | #ifdef HAVE_DNSSEC | ||
238 | /* We added an EDNSO header for the purpose of getting DNSSEC RRs, and set the value of the UDP payload size | ||
239 | @@ -1230,13 +1280,26 @@ void reply_query(int fd, int family, tim | ||
240 | } | ||
241 | #endif | ||
242 | |||
243 | + for (src = &forward->frec_src; src; src = src->next) | ||
244 | + { | ||
245 | + header->id = htons(src->orig_id); | ||
246 | + | ||
247 | #ifdef HAVE_DUMPFILE | ||
248 | - dump_packet(DUMP_REPLY, daemon->packet, (size_t)nn, NULL, &forward->source); | ||
249 | + dump_packet(DUMP_REPLY, daemon->packet, (size_t)nn, NULL, &src->source); | ||
250 | #endif | ||
251 | - | ||
252 | - send_from(forward->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, nn, | ||
253 | - &forward->source, &forward->dest, forward->iface); | ||
254 | + | ||
255 | + send_from(forward->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, nn, | ||
256 | + &src->source, &src->dest, src->iface); | ||
257 | + | ||
258 | + if (option_bool(OPT_EXTRALOG) && src != &forward->frec_src) | ||
259 | + { | ||
260 | + daemon->log_display_id = src->log_id; | ||
261 | + daemon->log_source_addr = &src->source; | ||
262 | + log_query(F_UPSTREAM, "query", NULL, "duplicate"); | ||
263 | + } | ||
264 | + } | ||
265 | } | ||
266 | + | ||
267 | free_frec(forward); /* cancel */ | ||
268 | } | ||
269 | } | ||
270 | @@ -2198,6 +2261,17 @@ void free_rfd(struct randfd *rfd) | ||
271 | |||
272 | static void free_frec(struct frec *f) | ||
273 | { | ||
274 | + struct frec_src *src, *tmp; | ||
275 | + | ||
276 | + /* add back to freelist of not the record builtin to every frec. */ | ||
277 | + for (src = f->frec_src.next; src; src = tmp) | ||
278 | + { | ||
279 | + tmp = src->next; | ||
280 | + src->next = daemon->free_frec_src; | ||
281 | + daemon->free_frec_src = src; | ||
282 | + } | ||
283 | + | ||
284 | + f->frec_src.next = NULL; | ||
285 | free_rfd(f->rfd4); | ||
286 | f->rfd4 = NULL; | ||
287 | f->sentto = NULL; | ||
288 | @@ -2339,17 +2413,39 @@ static struct frec *lookup_frec_by_sende | ||
289 | void *hash) | ||
290 | { | ||
291 | struct frec *f; | ||
292 | + struct frec_src *src; | ||
293 | + | ||
294 | + for (f = daemon->frec_list; f; f = f->next) | ||
295 | + if (f->sentto && | ||
296 | + !(f->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) && | ||
297 | + memcmp(hash, f->hash, HASH_SIZE) == 0) | ||
298 | + for (src = &f->frec_src; src; src = src->next) | ||
299 | + if (src->orig_id == id && | ||
300 | + sockaddr_isequal(&src->source, addr)) | ||
301 | + return f; | ||
302 | + | ||
303 | + return NULL; | ||
304 | +} | ||
305 | + | ||
306 | +static struct frec *lookup_frec_by_query(void *hash, unsigned int flags) | ||
307 | +{ | ||
308 | + struct frec *f; | ||
309 | + | ||
310 | + /* FREC_DNSKEY and FREC_DS_QUERY are never set in flags, so the test below | ||
311 | + ensures that no frec created for internal DNSSEC query can be returned here. */ | ||
312 | + | ||
313 | +#define FLAGMASK (FREC_CHECKING_DISABLED | FREC_AD_QUESTION | FREC_DO_QUESTION \ | ||
314 | + | FREC_HAS_PHEADER | FREC_DNSKEY_QUERY | FREC_DS_QUERY) | ||
315 | |||
316 | for(f = daemon->frec_list; f; f = f->next) | ||
317 | if (f->sentto && | ||
318 | - f->orig_id == id && | ||
319 | - memcmp(hash, f->hash, HASH_SIZE) == 0 && | ||
320 | - sockaddr_isequal(&f->source, addr)) | ||
321 | + (f->flags & FLAGMASK) == flags && | ||
322 | + memcmp(hash, f->hash, HASH_SIZE) == 0) | ||
323 | return f; | ||
324 | - | ||
325 | + | ||
326 | return NULL; | ||
327 | } | ||
328 | - | ||
329 | + | ||
330 | /* Send query packet again, if we can. */ | ||
331 | void resend_query() | ||
332 | { | ||
diff --git a/meta-networking/recipes-support/dnsmasq/files/CVE-2020-25686-2.patch b/meta-networking/recipes-support/dnsmasq/files/CVE-2020-25686-2.patch new file mode 100644 index 0000000000..a6ffd37260 --- /dev/null +++ b/meta-networking/recipes-support/dnsmasq/files/CVE-2020-25686-2.patch | |||
@@ -0,0 +1,63 @@ | |||
1 | From 6a6e06fbb0d4690507ceaf2bb6f0d8910f3d4914 Mon Sep 17 00:00:00 2001 | ||
2 | From: Simon Kelley <simon@thekelleys.org.uk> | ||
3 | Date: Fri, 4 Dec 2020 18:35:11 +0000 | ||
4 | Subject: [PATCH] Small cleanups in frec_src datastucture handling. | ||
5 | |||
6 | Signed-off-by: Sana Kazi <Sana.Kazi@kpit.com> | ||
7 | --- | ||
8 | src/forward.c | 22 +++++++++++++--------- | ||
9 | 1 file changed, 13 insertions(+), 9 deletions(-) | ||
10 | |||
11 | CVE: CVE-2020-25686 | ||
12 | Upstream-Status: Backport [http://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=commit;h=6a6e06fbb0d4690507ceaf2bb6f0d8910f3d4914] | ||
13 | Comment: No change in any hunk | ||
14 | |||
15 | Index: dnsmasq-2.81/src/forward.c | ||
16 | =================================================================== | ||
17 | --- dnsmasq-2.81.orig/src/forward.c | ||
18 | +++ dnsmasq-2.81/src/forward.c | ||
19 | @@ -364,7 +364,10 @@ static int forward_query(int udpfd, unio | ||
20 | if (!daemon->free_frec_src && | ||
21 | daemon->frec_src_count < daemon->ftabsize && | ||
22 | (daemon->free_frec_src = whine_malloc(sizeof(struct frec_src)))) | ||
23 | - daemon->frec_src_count++; | ||
24 | + { | ||
25 | + daemon->frec_src_count++; | ||
26 | + daemon->free_frec_src->next = NULL; | ||
27 | + } | ||
28 | |||
29 | /* If we've been spammed with many duplicates, just drop the query. */ | ||
30 | if (daemon->free_frec_src) | ||
31 | @@ -401,6 +404,7 @@ static int forward_query(int udpfd, unio | ||
32 | forward->frec_src.orig_id = ntohs(header->id); | ||
33 | forward->frec_src.dest = *dst_addr; | ||
34 | forward->frec_src.iface = dst_iface; | ||
35 | + forward->frec_src.next = NULL; | ||
36 | forward->new_id = get_id(); | ||
37 | forward->fd = udpfd; | ||
38 | memcpy(forward->hash, hash, HASH_SIZE); | ||
39 | @@ -2261,16 +2265,16 @@ void free_rfd(struct randfd *rfd) | ||
40 | |||
41 | static void free_frec(struct frec *f) | ||
42 | { | ||
43 | - struct frec_src *src, *tmp; | ||
44 | - | ||
45 | - /* add back to freelist of not the record builtin to every frec. */ | ||
46 | - for (src = f->frec_src.next; src; src = tmp) | ||
47 | + struct frec_src *last; | ||
48 | + | ||
49 | + /* add back to freelist if not the record builtin to every frec. */ | ||
50 | + for (last = f->frec_src.next; last && last->next; last = last->next) ; | ||
51 | + if (last) | ||
52 | { | ||
53 | - tmp = src->next; | ||
54 | - src->next = daemon->free_frec_src; | ||
55 | - daemon->free_frec_src = src; | ||
56 | + last->next = daemon->free_frec_src; | ||
57 | + daemon->free_frec_src = f->frec_src.next; | ||
58 | } | ||
59 | - | ||
60 | + | ||
61 | f->frec_src.next = NULL; | ||
62 | free_rfd(f->rfd4); | ||
63 | f->rfd4 = NULL; | ||