]> git.ipfire.org Git - ipfire-2.x.git/blame - src/patches/dnsmasq/0019-Fix-problems-validating-NSEC3-and-wildcards.patch
dnsmasq: Import patches from upstream
[ipfire-2.x.git] / src / patches / dnsmasq / 0019-Fix-problems-validating-NSEC3-and-wildcards.patch
CommitLineData
6644c1c7
MT
1From fbc5205702c7f6f431d9f1043c553d7fb62ddfdb Mon Sep 17 00:00:00 2001
2From: Simon Kelley <simon@thekelleys.org.uk>
3Date: Tue, 23 Dec 2014 15:46:08 +0000
efbd3a9a 4Subject: [PATCH 19/98] Fix problems validating NSEC3 and wildcards.
6644c1c7
MT
5
6---
7 src/dnssec.c | 253 ++++++++++++++++++++++++++++++-----------------------------
8 1 file changed, 128 insertions(+), 125 deletions(-)
9
10diff --git a/src/dnssec.c b/src/dnssec.c
11index 3208ac701149..9350d3e8c963 100644
12--- a/src/dnssec.c
13+++ b/src/dnssec.c
14@@ -615,6 +615,7 @@ static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int
15 Return code:
16 STAT_SECURE if it validates.
17 STAT_SECURE_WILDCARD if it validates and is the result of wildcard expansion.
18+ (In this case *wildcard_out points to the "body" of the wildcard within name.)
19 STAT_NO_SIG no RRsigs found.
20 STAT_INSECURE RRset empty.
21 STAT_BOGUS signature is wrong, bad packet.
22@@ -625,8 +626,8 @@ static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int
23
24 name is unchanged on exit. keyname is used as workspace and trashed.
25 */
26-static int validate_rrset(time_t now, struct dns_header *header, size_t plen, int class,
27- int type, char *name, char *keyname, struct blockdata *key, int keylen, int algo_in, int keytag_in)
28+static int validate_rrset(time_t now, struct dns_header *header, size_t plen, int class, int type,
29+ char *name, char *keyname, char **wildcard_out, struct blockdata *key, int keylen, int algo_in, int keytag_in)
30 {
31 static unsigned char **rrset = NULL, **sigs = NULL;
32 static int rrset_sz = 0, sig_sz = 0;
33@@ -798,8 +799,16 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
34 {
35 int k;
36 for (k = name_labels - labels; k != 0; k--)
37- while (*name_start != '.' && *name_start != 0)
38- name_start++;
39+ {
40+ while (*name_start != '.' && *name_start != 0)
41+ name_start++;
42+ if (k != 1)
43+ name_start++;
44+ }
45+
46+ if (wildcard_out)
47+ *wildcard_out = name_start+1;
48+
49 name_start--;
50 *name_start = '*';
51 }
52@@ -974,7 +983,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
53 if (recp1->addr.ds.keylen == (int)hash->digest_size &&
54 (ds_digest = blockdata_retrieve(recp1->addr.key.keydata, recp1->addr.ds.keylen, NULL)) &&
55 memcmp(ds_digest, digest, recp1->addr.ds.keylen) == 0 &&
56- validate_rrset(now, header, plen, class, T_DNSKEY, name, keyname, key, rdlen - 4, algo, keytag) == STAT_SECURE)
57+ validate_rrset(now, header, plen, class, T_DNSKEY, name, keyname, NULL, key, rdlen - 4, algo, keytag) == STAT_SECURE)
58 {
59 valid = 1;
60 break;
61@@ -1443,11 +1452,88 @@ static int base32_decode(char *in, unsigned char *out)
62 return p - out;
63 }
64
65+static int check_nsec3_coverage(struct dns_header *header, size_t plen, int digest_len, unsigned char *digest, int type,
66+ char *workspace1, char *workspace2, unsigned char **nsecs, int nsec_count)
67+{
68+ int i, hash_len, salt_len, base32_len, rdlen;
69+ unsigned char *p, *psave;
70+
71+ for (i = 0; i < nsec_count; i++)
72+ if ((p = nsecs[i]))
73+ {
74+ if (!extract_name(header, plen, &p, workspace1, 1, 0) ||
75+ !(base32_len = base32_decode(workspace1, (unsigned char *)workspace2)))
76+ return 0;
77+
78+ p += 8; /* class, type, TTL */
79+ GETSHORT(rdlen, p);
80+ psave = p;
81+ p += 4; /* algo, flags, iterations */
82+ salt_len = *p++; /* salt_len */
83+ p += salt_len; /* salt */
84+ hash_len = *p++; /* p now points to next hashed name */
85+
86+ if (!CHECK_LEN(header, p, plen, hash_len))
87+ return 0;
88+
89+ if (digest_len == base32_len && hash_len == base32_len)
90+ {
91+ int rc = memcmp(workspace2, digest, digest_len);
92+
93+ if (rc == 0)
94+ {
95+ /* We found an NSEC3 whose hashed name exactly matches the query, so
96+ we just need to check the type map. p points to the RR data for the record. */
97+
98+ int offset = (type & 0xff) >> 3;
99+ int mask = 0x80 >> (type & 0x07);
100+
101+ p += hash_len; /* skip next-domain hash */
102+ rdlen -= p - psave;
103+
104+ if (!CHECK_LEN(header, p, plen, rdlen))
105+ return 0;
106+
107+ while (rdlen >= 2)
108+ {
109+ if (p[0] == type >> 8)
110+ {
111+ /* Does the NSEC3 say our type exists? */
112+ if (offset < p[1] && (p[offset+2] & mask) != 0)
113+ return STAT_BOGUS;
114+
115+ break; /* finshed checking */
116+ }
117+
118+ rdlen -= p[1];
119+ p += p[1];
120+ }
121+
122+ return 1;
123+ }
124+ else if (rc <= 0)
125+ {
126+ /* Normal case, hash falls between NSEC3 name-hash and next domain name-hash,
127+ wrap around case, name-hash falls between NSEC3 name-hash and end */
128+ if (memcmp(p, digest, digest_len) > 0 || memcmp(workspace2, p, digest_len) > 0)
129+ return 1;
130+ }
131+ else
132+ {
133+ /* wrap around case, name falls between start and next domain name */
134+ if (memcmp(workspace2, p, digest_len) > 0 && memcmp(p, digest, digest_len) > 0)
135+ return 1;
136+ }
137+ }
138+ }
139+ return 0;
140+}
141+
142 static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, unsigned char **nsecs, int nsec_count,
143- char *workspace1, char *workspace2, char *name, int type)
144+ char *workspace1, char *workspace2, char *name, int type, char *wildname)
145 {
146 unsigned char *salt, *p, *digest;
147- int digest_len, i, iterations, salt_len, hash_len, base32_len, algo = 0;
148+ int digest_len, i, iterations, salt_len, base32_len, algo = 0;
149 struct nettle_hash const *hash;
150 char *closest_encloser, *next_closest, *wildcard;
151
152@@ -1520,7 +1606,14 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
153 if (!(hash = hash_find("sha1")))
154 return STAT_BOGUS;
155
156- /* Now, we need the "closest encloser NSEC3" */
157+ if ((digest_len = hash_name(name, &digest, hash, salt, salt_len, iterations)) == 0)
158+ return STAT_BOGUS;
159+
160+ if (check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count))
161+ return STAT_SECURE;
162+
163+ /* Can't find an NSEC3 which covers the name directly, we need the "closest encloser NSEC3"
164+ or an answer inferred from a wildcard record. */
165 closest_encloser = name;
166 next_closest = NULL;
167
168@@ -1529,6 +1622,9 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
169 if (*closest_encloser == '.')
170 closest_encloser++;
171
172+ if (wildname && hostname_isequal(closest_encloser, wildname))
173+ break;
174+
175 if ((digest_len = hash_name(closest_encloser, &digest, hash, salt, salt_len, iterations)) == 0)
176 return STAT_BOGUS;
177
178@@ -1551,127 +1647,33 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
179 }
180 while ((closest_encloser = strchr(closest_encloser, '.')));
181
182- /* No usable NSEC3s */
183- if (i == nsec_count)
184+ if (!closest_encloser)
185 return STAT_BOGUS;
186
187- if (!next_closest)
188- {
189- /* We found an NSEC3 whose hashed name exactly matches the query, so
190- Now we just need to check the type map. p points to the RR data for the record. */
191- int rdlen;
192- unsigned char *psave;
193- int offset = (type & 0xff) >> 3;
194- int mask = 0x80 >> (type & 0x07);
195-
196- p += 8; /* class, type, TTL */
197- GETSHORT(rdlen, p);
198- psave = p;
199- p += 5 + salt_len; /* algo, flags, iterations, salt_len, salt */
200- hash_len = *p++;
201- if (!CHECK_LEN(header, p, plen, hash_len))
202- return STAT_BOGUS; /* bad packet */
203- p += hash_len;
204- rdlen -= p - psave;
205-
206- while (rdlen >= 2)
207- {
208- if (!CHECK_LEN(header, p, plen, rdlen))
209- return STAT_BOGUS;
210-
211- if (p[0] == type >> 8)
212- {
213- /* Does the NSEC3 say our type exists? */
214- if (offset < p[1] && (p[offset+2] & mask) != 0)
215- return STAT_BOGUS;
216-
217- break; /* finshed checking */
218- }
219-
220- rdlen -= p[1];
221- p += p[1];
222- }
223-
224- return STAT_SECURE;
225- }
226-
227 /* Look for NSEC3 that proves the non-existence of the next-closest encloser */
228 if ((digest_len = hash_name(next_closest, &digest, hash, salt, salt_len, iterations)) == 0)
229 return STAT_BOGUS;
230
231- for (i = 0; i < nsec_count; i++)
232- if ((p = nsecs[i]))
233- {
234- if (!extract_name(header, plen, &p, workspace1, 1, 0) ||
235- !(base32_len = base32_decode(workspace1, (unsigned char *)workspace2)))
236- return STAT_BOGUS;
237-
238- p += 15 + salt_len; /* class, type, TTL, rdlen, algo, flags, iterations, salt_len, salt */
239- hash_len = *p++; /* p now points to next hashed name */
240-
241- if (!CHECK_LEN(header, p, plen, hash_len))
242- return STAT_BOGUS;
243-
244- if (digest_len == base32_len && hash_len == base32_len)
245- {
246- if (memcmp(workspace2, digest, digest_len) <= 0)
247- {
248- /* Normal case, hash falls between NSEC3 name-hash and next domain name-hash,
249- wrap around case, name-hash falls between NSEC3 name-hash and end */
250- if (memcmp(p, digest, digest_len) > 0 || memcmp(workspace2, p, digest_len) > 0)
251- return STAT_SECURE;
252- }
253- else
254- {
255- /* wrap around case, name falls between start and next domain name */
256- if (memcmp(workspace2, p, digest_len) > 0 && memcmp(p, digest, digest_len) > 0)
257- return STAT_SECURE;
258- }
259- }
260- }
261-
262- /* Finally, check that there's no seat of wildcard synthesis */
263- if (!(wildcard = strchr(next_closest, '.')) || wildcard == next_closest)
264- return STAT_BOGUS;
265-
266- wildcard--;
267- *wildcard = '*';
268-
269- if ((digest_len = hash_name(wildcard, &digest, hash, salt, salt_len, iterations)) == 0)
270+ if (!check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count))
271 return STAT_BOGUS;
272
273- for (i = 0; i < nsec_count; i++)
274- if ((p = nsecs[i]))
275- {
276- if (!extract_name(header, plen, &p, workspace1, 1, 0) ||
277- !(base32_len = base32_decode(workspace1, (unsigned char *)workspace2)))
278- return STAT_BOGUS;
279-
280- p += 15 + salt_len; /* class, type, TTL, rdlen, algo, flags, iterations, salt_len, salt */
281- hash_len = *p++; /* p now points to next hashed name */
282-
283- if (!CHECK_LEN(header, p, plen, hash_len))
284- return STAT_BOGUS;
285-
286- if (digest_len == base32_len && hash_len == base32_len)
287- {
288- if (memcmp(workspace2, digest, digest_len) <= 0)
289- {
290- /* Normal case, hash falls between NSEC3 name-hash and next domain name-hash,
291- wrap around case, name-hash falls between NSEC3 name-hash and end */
292- if (memcmp(p, digest, digest_len) > 0 || memcmp(workspace2, p, digest_len) > 0)
293- return STAT_SECURE;
294- }
295- else
296- {
297- /* wrap around case, name falls between start and next domain name */
298- if (memcmp(workspace2, p, digest_len) > 0 && memcmp(p, digest, digest_len) > 0)
299- return STAT_SECURE;
300- }
301- }
302- }
303+ /* Finally, check that there's no seat of wildcard synthesis */
304+ if (!wildname)
305+ {
306+ if (!(wildcard = strchr(next_closest, '.')) || wildcard == next_closest)
307+ return STAT_BOGUS;
308+
309+ wildcard--;
310+ *wildcard = '*';
311+
312+ if ((digest_len = hash_name(wildcard, &digest, hash, salt, salt_len, iterations)) == 0)
313+ return STAT_BOGUS;
314+
315+ if (!check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count))
316+ return STAT_BOGUS;
317+ }
318
319- return STAT_BOGUS;
320+ return STAT_SECURE;
321 }
322
323 /* Validate all the RRsets in the answer and authority sections of the reply (4035:3.2.3) */
324@@ -1792,8 +1794,9 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
325 struct all_addr a;
326 struct blockdata *key;
327 struct crec *crecp;
328-
329- rc = validate_rrset(now, header, plen, class1, type1, name, keyname, NULL, 0, 0, 0);
330+ char *wildname;
331+
332+ rc = validate_rrset(now, header, plen, class1, type1, name, keyname, &wildname, NULL, 0, 0, 0);
333
334 if (rc == STAT_SECURE_WILDCARD)
335 {
336@@ -1807,7 +1810,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
337 if (nsec_type == T_NSEC)
338 rc = prove_non_existence_nsec(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, type1);
339 else
340- rc = prove_non_existence_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, type1);
341+ rc = prove_non_existence_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, type1, wildname);
342
343 if (rc != STAT_SECURE)
344 return rc;
345@@ -1933,7 +1936,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
346 if (nsec_type == T_NSEC)
347 return prove_non_existence_nsec(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, qtype);
348 else
349- return prove_non_existence_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, qtype);
350+ return prove_non_existence_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, qtype, NULL);
351 }
352
353 /* Chase the CNAME chain in the packet until the first record which _doesn't validate.
354@@ -1980,7 +1983,7 @@ int dnssec_chase_cname(time_t now, struct dns_header *header, size_t plen, char
355 return STAT_INSECURE;
356
357 /* validate CNAME chain, return if insecure or need more data */
358- rc = validate_rrset(now, header, plen, class, type, name, keyname, NULL, 0, 0, 0);
359+ rc = validate_rrset(now, header, plen, class, type, name, keyname, NULL, NULL, 0, 0, 0);
360 if (rc != STAT_SECURE)
361 {
362 if (rc == STAT_NO_SIG)
363--
3642.1.0
365