summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSana Kazi <Sana.Kazi@kpit.com>2021-05-28 16:01:25 +0000
committerArmin Kuster <akuster808@gmail.com>2021-05-29 11:41:45 -0700
commitc38d2a74f762a792046f3d3c377827b08aade513 (patch)
tree3c438d11251c5ee92fcaf7170a999ef2dc7fdccb
parent587fe58949c7efae60c6eee580bda3233621245e (diff)
downloadmeta-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>
-rw-r--r--meta-networking/recipes-support/dnsmasq/dnsmasq_2.81.bb7
-rw-r--r--meta-networking/recipes-support/dnsmasq/files/CVE-2020-25681.patch370
-rw-r--r--meta-networking/recipes-support/dnsmasq/files/CVE-2020-25684.patch98
-rw-r--r--meta-networking/recipes-support/dnsmasq/files/CVE-2020-25685-1.patch587
-rw-r--r--meta-networking/recipes-support/dnsmasq/files/CVE-2020-25685-2.patch175
-rw-r--r--meta-networking/recipes-support/dnsmasq/files/CVE-2020-25686-1.patch332
-rw-r--r--meta-networking/recipes-support/dnsmasq/files/CVE-2020-25686-2.patch63
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 92415386c..a1dc0f3a0 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"
4SRC_URI[dnsmasq-2.81.sha256sum] = "3c28c68c6c2967c3a96e9b432c0c046a5df17a426d3a43cffe9e693cf05804d0" 4SRC_URI[dnsmasq-2.81.sha256sum] = "3c28c68c6c2967c3a96e9b432c0c046a5df17a426d3a43cffe9e693cf05804d0"
5SRC_URI += "\ 5SRC_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 000000000..675615770
--- /dev/null
+++ b/meta-networking/recipes-support/dnsmasq/files/CVE-2020-25681.patch
@@ -0,0 +1,370 @@
1From 4e96a4be685c9e4445f6ee79ad0b36b9119b502a Mon Sep 17 00:00:00 2001
2From: Simon Kelley <simon@thekelleys.org.uk>
3Date: Wed, 11 Nov 2020 23:25:04 +0000
4Subject: [PATCH] Fix remote buffer overflow CERT VU#434904
5
6The problem is in the sort_rrset() function and allows a remote
7attacker to overwrite memory. Any dnsmasq instance with DNSSEC
8enabled is vulnerable.
9
10Signed-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
16CVE: CVE-2020-25681
17CVE: CVE-2020-25682
18CVE: CVE-2020-25683
19CVE: CVE-2020-25687
20Upstream-Status: Backport [https://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=patch;h=4e96a4be685c9e4445f6ee79ad0b36b9119b502a]
21Comment: Refreshed first two hunks
22
23Index: 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 000000000..f7ff4b27c
--- /dev/null
+++ b/meta-networking/recipes-support/dnsmasq/files/CVE-2020-25684.patch
@@ -0,0 +1,98 @@
1From 257ac0c5f7732cbc6aa96fdd3b06602234593aca Mon Sep 17 00:00:00 2001
2From: Simon Kelley <simon@thekelleys.org.uk>
3Date: Thu, 12 Nov 2020 18:49:23 +0000
4Subject: [PATCH] Check destination of DNS UDP query replies.
5
6At any time, dnsmasq will have a set of sockets open, bound to
7random ports, on which it sends queries to upstream nameservers.
8This patch fixes the existing problem that a reply for ANY in-flight
9query would be accepted via ANY open port, which increases the
10chances of an attacker flooding answers "in the blind" in an
11attempt to poison the DNS cache. CERT VU#434904 refers.
12
13Signed-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
19CVE: CVE-2020-25684
20Upstream-Status: Backport [https://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=patch;h=257ac0c5f7732cbc6aa96fdd3b06602234593aca]
21Comment: No change in any hunk
22
23Index: 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 000000000..5eb582c67
--- /dev/null
+++ b/meta-networking/recipes-support/dnsmasq/files/CVE-2020-25685-1.patch
@@ -0,0 +1,587 @@
1From 2d765867c597db18be9d876c9c17e2c0fe1953cd Mon Sep 17 00:00:00 2001
2From: Simon Kelley <simon@thekelleys.org.uk>
3Date: Thu, 12 Nov 2020 22:06:07 +0000
4Subject: [PATCH] Use SHA-256 to provide security against DNS cache poisoning.
5
6Use the SHA-256 hash function to verify that DNS answers
7received are for the questions originally asked. This replaces
8the slightly insecure SHA-1 (when compiled with DNSSEC) or
9the very insecure CRC32 (otherwise). Refer: CERT VU#434904.
10
11Signed-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
24CVE: CVE-2020-25685
25Upstream-Status: Backport [https://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=patch;h=2024f9729713fd657d65e64c2e4e471baa0a3e5b]
26Comment: No change in any hunk
27
28Index: 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
42Index: 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
55Index: 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);
93Index: 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 */
133Index: 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)
242Index: 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
528Index: 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 000000000..302c42ccc
--- /dev/null
+++ b/meta-networking/recipes-support/dnsmasq/files/CVE-2020-25685-2.patch
@@ -0,0 +1,175 @@
1From 2024f9729713fd657d65e64c2e4e471baa0a3e5b Mon Sep 17 00:00:00 2001
2From: =?utf8?q?Petr=20Men=C5=A1=C3=ADk?= <pemensik@redhat.com>
3Date: Wed, 25 Nov 2020 17:18:55 +0100
4Subject: [PATCH] Support hash function from nettle (only)
5
6Unlike COPTS=-DHAVE_DNSSEC, allow usage of just sha256 function from
7nettle, but keep DNSSEC disabled at build time. Skips use of internal
8hash implementation without support for validation built-in.
9
10Signed-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
20CVE: CVE-2020-25685
21Upstream-Status: Backport [https://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=patch;h=2024f9729713fd657d65e64c2e4e471baa0a3e5b]
22Comment: Refreshed a hunk from pkg-wrapper and second hunk from Makefile
23
24Index: 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)`\"'
50Index: 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
116Index: 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
149Index: 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
163Index: 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 000000000..fd9d0a9b1
--- /dev/null
+++ b/meta-networking/recipes-support/dnsmasq/files/CVE-2020-25686-1.patch
@@ -0,0 +1,332 @@
1From 15b60ddf935a531269bb8c68198de012a4967156 Mon Sep 17 00:00:00 2001
2From: Simon Kelley <simon@thekelleys.org.uk>
3Date: Wed, 18 Nov 2020 18:34:55 +0000
4Subject: [PATCH] Handle multiple identical near simultaneous DNS queries
5 better.
6
7Previously, such queries would all be forwarded
8independently. This is, in theory, inefficent but in practise
9not a problem, _except_ that is means that an answer for any
10of the forwarded queries will be accepted and cached.
11An attacker can send a query multiple times, and for each repeat,
12another {port, ID} becomes capable of accepting the answer he is
13sending in the blind, to random IDs and ports. The chance of a
14succesful attack is therefore multiplied by the number of repeats
15of the query. The new behaviour detects repeated queries and
16merely stores the clients sending repeats so that when the
17first query completes, the answer can be sent to all the
18clients who asked. Refer: CERT VU#434904.
19
20Signed-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
27CVE: CVE-2020-25686
28Upstream-Status: Backport [http://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=commit;h=15b60ddf935a531269bb8c68198de012a4967156]
29Comment: No change in any hunk
30
31Index: 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;
75Index: 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 000000000..a6ffd3726
--- /dev/null
+++ b/meta-networking/recipes-support/dnsmasq/files/CVE-2020-25686-2.patch
@@ -0,0 +1,63 @@
1From 6a6e06fbb0d4690507ceaf2bb6f0d8910f3d4914 Mon Sep 17 00:00:00 2001
2From: Simon Kelley <simon@thekelleys.org.uk>
3Date: Fri, 4 Dec 2020 18:35:11 +0000
4Subject: [PATCH] Small cleanups in frec_src datastucture handling.
5
6Signed-off-by: Sana Kazi <Sana.Kazi@kpit.com>
7---
8 src/forward.c | 22 +++++++++++++---------
9 1 file changed, 13 insertions(+), 9 deletions(-)
10
11CVE: CVE-2020-25686
12Upstream-Status: Backport [http://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=commit;h=6a6e06fbb0d4690507ceaf2bb6f0d8910f3d4914]
13Comment: No change in any hunk
14
15Index: 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;