summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSinan Kaya <okaya@kernel.org>2018-09-24 19:21:02 +0000
committerArmin Kuster <akuster808@gmail.com>2018-09-26 18:51:17 -0700
commitca2870ff8e96c3e82ed2c0cc5607b35fbf8c4b4c (patch)
tree417e4d22e58e8dc0d6507847868891312f824cd3
parent35c6359155d6082d279ba86b94125d684d435dad (diff)
downloadmeta-openembedded-ca2870ff8e96c3e82ed2c0cc5607b35fbf8c4b4c.tar.gz
dnsmasq: CVE-2017-15107
* CVE-2017-15107 A vulnerability was found in Dnsmasq's implementation of DNSSEC. Wildcard synthesized NSEC records could be improperly interpreted to prove the non-existence of hostnames that actually exist. Affects dnsmasq <= 2.78 CVE: CVE-2017-15107 Ref: https://access.redhat.com/security/cve/cve-2017-15107 Signed-off-by: Sinan Kaya <okaya@kernel.org> Signed-off-by: Armin Kuster <akuster808@gmail.com>
-rw-r--r--meta-networking/recipes-support/dnsmasq/dnsmasq_2.78.bb1
-rw-r--r--meta-networking/recipes-support/dnsmasq/files/CVE-2017-15107.patch263
2 files changed, 264 insertions, 0 deletions
diff --git a/meta-networking/recipes-support/dnsmasq/dnsmasq_2.78.bb b/meta-networking/recipes-support/dnsmasq/dnsmasq_2.78.bb
index 4d1dc6e69..d2465f82d 100644
--- a/meta-networking/recipes-support/dnsmasq/dnsmasq_2.78.bb
+++ b/meta-networking/recipes-support/dnsmasq/dnsmasq_2.78.bb
@@ -2,6 +2,7 @@ require dnsmasq.inc
2 2
3SRC_URI += "\ 3SRC_URI += "\
4 file://lua.patch \ 4 file://lua.patch \
5 file://CVE-2017-15107.patch \
5" 6"
6 7
7SRC_URI[dnsmasq-2.78.md5sum] = "3bb97f264c73853f802bf70610150788" 8SRC_URI[dnsmasq-2.78.md5sum] = "3bb97f264c73853f802bf70610150788"
diff --git a/meta-networking/recipes-support/dnsmasq/files/CVE-2017-15107.patch b/meta-networking/recipes-support/dnsmasq/files/CVE-2017-15107.patch
new file mode 100644
index 000000000..701101bcb
--- /dev/null
+++ b/meta-networking/recipes-support/dnsmasq/files/CVE-2017-15107.patch
@@ -0,0 +1,263 @@
1From 5a56e1b78a753d3295564daddc9ce389cc69fd68 Mon Sep 17 00:00:00 2001
2From: Simon Kelley <simon@thekelleys.org.uk>
3Date: Fri, 19 Jan 2018 12:26:08 +0000
4Subject: [PATCH] DNSSEC fix for wildcard NSEC records. CVE-2017-15107 applies.
5
6It's OK for NSEC records to be expanded from wildcards,
7but in that case, the proof of non-existence is only valid
8starting at the wildcard name, *.<domain> NOT the name expanded
9from the wildcard. Without this check it's possible for an
10attacker to craft an NSEC which wrongly proves non-existence
11in a domain which includes a wildcard for NSEC.
12
13Upstream-Status: Backport [http://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=commit;h=4fe6744a220eddd3f1749b40cac3dfc510787de6]
14CVE: CVE-2017-15107
15Signed-off-by: Sinan Kaya <okaya@kernel.org>
16---
17 CHANGELOG | 44 +++++++++++++++++++
18 src/dnssec.c | 117 +++++++++++++++++++++++++++++++++++++++++++++------
19 2 files changed, 147 insertions(+), 14 deletions(-)
20
21diff --git a/CHANGELOG b/CHANGELOG
22index 075fe1a6..5226dce8 100644
23--- a/CHANGELOG
24+++ b/CHANGELOG
25@@ -1,3 +1,47 @@
26+version 2.79
27+ Fix parsing of CNAME arguments, which are confused by extra spaces.
28+ Thanks to Diego Aguirre for spotting the bug.
29+
30+ Where available, use IP_UNICAST_IF or IPV6_UNICAST_IF to bind
31+ upstream servers to an interface, rather than SO_BINDTODEVICE.
32+ Thanks to Beniamino Galvani for the patch.
33+
34+ Always return a SERVFAIL answer to DNS queries without the
35+ recursion desired bit set, UNLESS acting as an authoritative
36+ DNS server. This avoids a potential route to cache snooping.
37+
38+ Add support for Ed25519 signatures in DNSSEC validation.
39+
40+ No longer support RSA/MD5 signatures in DNSSEC validation,
41+ since these are not secure. This behaviour is mandated in
42+ RFC-6944.
43+
44+ Fix incorrect error exit code from dhcp_release6 utility.
45+ Thanks Gaudenz Steinlin for the bug report.
46+
47+ Use SIGINT (instead of overloading SIGHUP) to turn on DNSSEC
48+ time validation when --dnssec-no-timecheck is in use.
49+ Note that this is an incompatible change from earlier releases.
50+
51+ Allow more than one --bridge-interface option to refer to an
52+ interface, so that we can use
53+ --bridge-interface=int1,alias1
54+ --bridge-interface=int1,alias2
55+ as an alternative to
56+ --bridge-interface=int1,alias1,alias2
57+ Thanks to Neil Jerram for work on this.
58+
59+ Fix for DNSSEC with wildcard-derived NSEC records.
60+ It's OK for NSEC records to be expanded from wildcards,
61+ but in that case, the proof of non-existence is only valid
62+ starting at the wildcard name, *.<domain> NOT the name expanded
63+ from the wildcard. Without this check it's possible for an
64+ attacker to craft an NSEC which wrongly proves non-existence.
65+ Thanks to Ralph Dolmans for finding this, and co-ordinating
66+ the vulnerability tracking and fix release.
67+ CVE-2017-15107 applies.
68+
69+
70 version 2.78
71 Fix logic of appending ".<layer>" to PXE basename. Thanks to Chris
72 Novakovic for the patch.
73diff --git a/src/dnssec.c b/src/dnssec.c
74index a74d01ab..1417be56 100644
75--- a/src/dnssec.c
76+++ b/src/dnssec.c
77@@ -424,15 +424,17 @@ static void from_wire(char *name)
78 static int count_labels(char *name)
79 {
80 int i;
81-
82+ char *p;
83+
84 if (*name == 0)
85 return 0;
86
87- for (i = 0; *name; name++)
88- if (*name == '.')
89+ for (p = name, i = 0; *p; p++)
90+ if (*p == '.')
91 i++;
92
93- return i+1;
94+ /* Don't count empty first label. */
95+ return *name == '.' ? i : i+1;
96 }
97
98 /* Implement RFC1982 wrapped compare for 32-bit numbers */
99@@ -1405,8 +1407,8 @@ static int hostname_cmp(const char *a, const char *b)
100 }
101 }
102
103-static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsigned char **nsecs, int nsec_count,
104- char *workspace1, char *workspace2, char *name, int type, int *nons)
105+static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsigned char **nsecs, unsigned char **labels, int nsec_count,
106+ char *workspace1_in, char *workspace2, char *name, int type, int *nons)
107 {
108 int i, rc, rdlen;
109 unsigned char *p, *psave;
110@@ -1419,6 +1421,9 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
111 /* Find NSEC record that proves name doesn't exist */
112 for (i = 0; i < nsec_count; i++)
113 {
114+ char *workspace1 = workspace1_in;
115+ int sig_labels, name_labels;
116+
117 p = nsecs[i];
118 if (!extract_name(header, plen, &p, workspace1, 1, 10))
119 return 0;
120@@ -1427,7 +1432,27 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
121 psave = p;
122 if (!extract_name(header, plen, &p, workspace2, 1, 10))
123 return 0;
124-
125+
126+ /* If NSEC comes from wildcard expansion, use original wildcard
127+ as name for computation. */
128+ sig_labels = *labels[i];
129+ name_labels = count_labels(workspace1);
130+
131+ if (sig_labels < name_labels)
132+ {
133+ int k;
134+ for (k = name_labels - sig_labels; k != 0; k--)
135+ {
136+ while (*workspace1 != '.' && *workspace1 != 0)
137+ workspace1++;
138+ if (k != 1 && *workspace1 == '.')
139+ workspace1++;
140+ }
141+
142+ workspace1--;
143+ *workspace1 = '*';
144+ }
145+
146 rc = hostname_cmp(workspace1, name);
147
148 if (rc == 0)
149@@ -1825,24 +1850,26 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
150
151 static int prove_non_existence(struct dns_header *header, size_t plen, char *keyname, char *name, int qtype, int qclass, char *wildname, int *nons)
152 {
153- static unsigned char **nsecset = NULL;
154- static int nsecset_sz = 0;
155+ static unsigned char **nsecset = NULL, **rrsig_labels = NULL;
156+ static int nsecset_sz = 0, rrsig_labels_sz = 0;
157
158 int type_found = 0;
159- unsigned char *p = skip_questions(header, plen);
160+ unsigned char *auth_start, *p = skip_questions(header, plen);
161 int type, class, rdlen, i, nsecs_found;
162
163 /* Move to NS section */
164 if (!p || !(p = skip_section(p, ntohs(header->ancount), header, plen)))
165 return 0;
166+
167+ auth_start = p;
168
169 for (nsecs_found = 0, i = ntohs(header->nscount); i != 0; i--)
170 {
171 unsigned char *pstart = p;
172
173- if (!(p = skip_name(p, header, plen, 10)))
174+ if (!extract_name(header, plen, &p, daemon->workspacename, 1, 10))
175 return 0;
176-
177+
178 GETSHORT(type, p);
179 GETSHORT(class, p);
180 p += 4; /* TTL */
181@@ -1859,7 +1886,69 @@ static int prove_non_existence(struct dns_header *header, size_t plen, char *key
182 if (!expand_workspace(&nsecset, &nsecset_sz, nsecs_found))
183 return 0;
184
185- nsecset[nsecs_found++] = pstart;
186+ if (type == T_NSEC)
187+ {
188+ /* If we're looking for NSECs, find the corresponding SIGs, to
189+ extract the labels value, which we need in case the NSECs
190+ are the result of wildcard expansion.
191+ Note that the NSEC may not have been validated yet
192+ so if there are multiple SIGs, make sure the label value
193+ is the same in all, to avoid be duped by a rogue one.
194+ If there are no SIGs, that's an error */
195+ unsigned char *p1 = auth_start;
196+ int res, j, rdlen1, type1, class1;
197+
198+ if (!expand_workspace(&rrsig_labels, &rrsig_labels_sz, nsecs_found))
199+ return 0;
200+
201+ rrsig_labels[nsecs_found] = NULL;
202+
203+ for (j = ntohs(header->nscount); j != 0; j--)
204+ {
205+ if (!(res = extract_name(header, plen, &p1, daemon->workspacename, 0, 10)))
206+ return 0;
207+
208+ GETSHORT(type1, p1);
209+ GETSHORT(class1, p1);
210+ p1 += 4; /* TTL */
211+ GETSHORT(rdlen1, p1);
212+
213+ if (!CHECK_LEN(header, p1, plen, rdlen1))
214+ return 0;
215+
216+ if (res == 1 && class1 == qclass && type1 == T_RRSIG)
217+ {
218+ int type_covered;
219+ unsigned char *psav = p1;
220+
221+ if (rdlen < 18)
222+ return 0; /* bad packet */
223+
224+ GETSHORT(type_covered, p1);
225+
226+ if (type_covered == T_NSEC)
227+ {
228+ p1++; /* algo */
229+
230+ /* labels field must be the same in every SIG we find. */
231+ if (!rrsig_labels[nsecs_found])
232+ rrsig_labels[nsecs_found] = p1;
233+ else if (*rrsig_labels[nsecs_found] != *p1) /* algo */
234+ return 0;
235+ }
236+ p1 = psav;
237+ }
238+
239+ if (!ADD_RDLEN(header, p1, plen, rdlen1))
240+ return 0;
241+ }
242+
243+ /* Must have found at least one sig. */
244+ if (!rrsig_labels[nsecs_found])
245+ return 0;
246+ }
247+
248+ nsecset[nsecs_found++] = pstart;
249 }
250
251 if (!ADD_RDLEN(header, p, plen, rdlen))
252@@ -1867,7 +1956,7 @@ static int prove_non_existence(struct dns_header *header, size_t plen, char *key
253 }
254
255 if (type_found == T_NSEC)
256- return prove_non_existence_nsec(header, plen, nsecset, nsecs_found, daemon->workspacename, keyname, name, qtype, nons);
257+ return prove_non_existence_nsec(header, plen, nsecset, rrsig_labels, nsecs_found, daemon->workspacename, keyname, name, qtype, nons);
258 else if (type_found == T_NSEC3)
259 return prove_non_existence_nsec3(header, plen, nsecset, nsecs_found, daemon->workspacename, keyname, name, qtype, wildname, nons);
260 else
261--
2622.19.0
263