]> git.ipfire.org Git - ipfire-2.x.git/blob - src/patches/dnsmasq/022-Tidy_up_DNSSEC_non-existence_code_Check_zone_status_is_NSEC_proof_bad.patch
bird: New package
[ipfire-2.x.git] / src / patches / dnsmasq / 022-Tidy_up_DNSSEC_non-existence_code_Check_zone_status_is_NSEC_proof_bad.patch
1 From b40f26c0199235073abc37e1e1d6ed93bed372f5 Mon Sep 17 00:00:00 2001
2 From: Simon Kelley <simon@thekelleys.org.uk>
3 Date: Thu, 17 Dec 2015 11:57:26 +0000
4 Subject: [PATCH] Tidy up DNSSEC non-existence code. Check zone status is NSEC
5 proof bad.
6
7 ---
8 src/dnssec.c | 207 +++++++++++++++++++++++++---------------------------------
9 1 file changed, 90 insertions(+), 117 deletions(-)
10
11 diff --git a/src/dnssec.c b/src/dnssec.c
12 index 012b2a6..ddae497 100644
13 --- a/src/dnssec.c
14 +++ b/src/dnssec.c
15 @@ -1367,59 +1367,6 @@ static int hostname_cmp(const char *a, const char *b)
16 }
17 }
18
19 -/* Find all the NSEC or NSEC3 records in a reply.
20 - return an array of pointers to them. */
21 -static int find_nsec_records(struct dns_header *header, size_t plen, unsigned char ***nsecsetp, int *nsecsetl, int class_reqd)
22 -{
23 - static unsigned char **nsecset = NULL;
24 - static int nsecset_sz = 0;
25 -
26 - int type_found = 0;
27 - unsigned char *p = skip_questions(header, plen);
28 - int type, class, rdlen, i, nsecs_found;
29 -
30 - /* Move to NS section */
31 - if (!p || !(p = skip_section(p, ntohs(header->ancount), header, plen)))
32 - return 0;
33 -
34 - for (nsecs_found = 0, i = ntohs(header->nscount); i != 0; i--)
35 - {
36 - unsigned char *pstart = p;
37 -
38 - if (!(p = skip_name(p, header, plen, 10)))
39 - return 0;
40 -
41 - GETSHORT(type, p);
42 - GETSHORT(class, p);
43 - p += 4; /* TTL */
44 - GETSHORT(rdlen, p);
45 -
46 - if (class == class_reqd && (type == T_NSEC || type == T_NSEC3))
47 - {
48 - /* No mixed NSECing 'round here, thankyouverymuch */
49 - if (type_found == T_NSEC && type == T_NSEC3)
50 - return 0;
51 - if (type_found == T_NSEC3 && type == T_NSEC)
52 - return 0;
53 -
54 - type_found = type;
55 -
56 - if (!expand_workspace(&nsecset, &nsecset_sz, nsecs_found))
57 - return 0;
58 -
59 - nsecset[nsecs_found++] = pstart;
60 - }
61 -
62 - if (!ADD_RDLEN(header, p, plen, rdlen))
63 - return 0;
64 - }
65 -
66 - *nsecsetp = nsecset;
67 - *nsecsetl = nsecs_found;
68 -
69 - return type_found;
70 -}
71 -
72 static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsigned char **nsecs, int nsec_count,
73 char *workspace1, char *workspace2, char *name, int type, int *nons)
74 {
75 @@ -1436,12 +1383,12 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
76 {
77 p = nsecs[i];
78 if (!extract_name(header, plen, &p, workspace1, 1, 10))
79 - return STAT_BOGUS;
80 + return 0;
81 p += 8; /* class, type, TTL */
82 GETSHORT(rdlen, p);
83 psave = p;
84 if (!extract_name(header, plen, &p, workspace2, 1, 10))
85 - return STAT_BOGUS;
86 + return 0;
87
88 rc = hostname_cmp(workspace1, name);
89
90 @@ -1449,7 +1396,7 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
91 {
92 /* 4035 para 5.4. Last sentence */
93 if (type == T_NSEC || type == T_RRSIG)
94 - return STAT_SECURE;
95 + return 1;
96
97 /* NSEC with the same name as the RR we're testing, check
98 that the type in question doesn't appear in the type map */
99 @@ -1465,24 +1412,24 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
100 /* A CNAME answer would also be valid, so if there's a CNAME is should
101 have been returned. */
102 if ((p[2] & (0x80 >> T_CNAME)) != 0)
103 - return STAT_BOGUS;
104 + return 0;
105
106 /* If the SOA bit is set for a DS record, then we have the
107 DS from the wrong side of the delegation. */
108 if (type == T_DS && (p[2] & (0x80 >> T_SOA)) != 0)
109 - return STAT_BOGUS;
110 + return 0;
111 }
112
113 while (rdlen >= 2)
114 {
115 if (!CHECK_LEN(header, p, plen, rdlen))
116 - return STAT_BOGUS;
117 + return 0;
118
119 if (p[0] == type >> 8)
120 {
121 /* Does the NSEC say our type exists? */
122 if (offset < p[1] && (p[offset+2] & mask) != 0)
123 - return STAT_BOGUS;
124 + return 0;
125
126 break; /* finshed checking */
127 }
128 @@ -1491,24 +1438,24 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
129 p += p[1];
130 }
131
132 - return STAT_SECURE;
133 + return 1;
134 }
135 else if (rc == -1)
136 {
137 /* Normal case, name falls between NSEC name and next domain name,
138 wrap around case, name falls between NSEC name (rc == -1) and end */
139 if (hostname_cmp(workspace2, name) >= 0 || hostname_cmp(workspace1, workspace2) >= 0)
140 - return STAT_SECURE;
141 + return 1;
142 }
143 else
144 {
145 /* wrap around case, name falls between start and next domain name */
146 if (hostname_cmp(workspace1, workspace2) >= 0 && hostname_cmp(workspace2, name) >=0 )
147 - return STAT_SECURE;
148 + return 1;
149 }
150 }
151
152 - return STAT_BOGUS;
153 + return 0;
154 }
155
156 /* return digest length, or zero on error */
157 @@ -1701,7 +1648,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
158 for (i = 0; i < nsec_count; i++)
159 {
160 if (!(p = skip_name(nsecs[i], header, plen, 15)))
161 - return STAT_BOGUS; /* bad packet */
162 + return 0; /* bad packet */
163
164 p += 10; /* type, class, TTL, rdlen */
165 algo = *p++;
166 @@ -1712,14 +1659,14 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
167
168 /* No usable NSEC3s */
169 if (i == nsec_count)
170 - return STAT_BOGUS;
171 + return 0;
172
173 p++; /* flags */
174 GETSHORT (iterations, p);
175 salt_len = *p++;
176 salt = p;
177 if (!CHECK_LEN(header, salt, plen, salt_len))
178 - return STAT_BOGUS; /* bad packet */
179 + return 0; /* bad packet */
180
181 /* Now prune so we only have NSEC3 records with same iterations, salt and algo */
182 for (i = 0; i < nsec_count; i++)
183 @@ -1730,7 +1677,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
184 nsecs[i] = NULL; /* Speculative, will be restored if OK. */
185
186 if (!(p = skip_name(nsec3p, header, plen, 15)))
187 - return STAT_BOGUS; /* bad packet */
188 + return 0; /* bad packet */
189
190 p += 10; /* type, class, TTL, rdlen */
191
192 @@ -1747,7 +1694,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
193 continue;
194
195 if (!CHECK_LEN(header, p, plen, salt_len))
196 - return STAT_BOGUS; /* bad packet */
197 + return 0; /* bad packet */
198
199 if (memcmp(p, salt, salt_len) != 0)
200 continue;
201 @@ -1758,13 +1705,13 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
202
203 /* Algo is checked as 1 above */
204 if (!(hash = hash_find("sha1")))
205 - return STAT_BOGUS;
206 + return 0;
207
208 if ((digest_len = hash_name(name, &digest, hash, salt, salt_len, iterations)) == 0)
209 - return STAT_BOGUS;
210 + return 0;
211
212 if (check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, nons))
213 - return STAT_SECURE;
214 + return 1;
215
216 /* Can't find an NSEC3 which covers the name directly, we need the "closest encloser NSEC3"
217 or an answer inferred from a wildcard record. */
218 @@ -1780,14 +1727,14 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
219 break;
220
221 if ((digest_len = hash_name(closest_encloser, &digest, hash, salt, salt_len, iterations)) == 0)
222 - return STAT_BOGUS;
223 + return 0;
224
225 for (i = 0; i < nsec_count; i++)
226 if ((p = nsecs[i]))
227 {
228 if (!extract_name(header, plen, &p, workspace1, 1, 0) ||
229 !(base32_len = base32_decode(workspace1, (unsigned char *)workspace2)))
230 - return STAT_BOGUS;
231 + return 0;
232
233 if (digest_len == base32_len &&
234 memcmp(digest, workspace2, digest_len) == 0)
235 @@ -1802,32 +1749,81 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
236 while ((closest_encloser = strchr(closest_encloser, '.')));
237
238 if (!closest_encloser)
239 - return STAT_BOGUS;
240 + return 0;
241
242 /* Look for NSEC3 that proves the non-existence of the next-closest encloser */
243 if ((digest_len = hash_name(next_closest, &digest, hash, salt, salt_len, iterations)) == 0)
244 - return STAT_BOGUS;
245 + return 0;
246
247 if (!check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, NULL))
248 - return STAT_BOGUS;
249 + return 0;
250
251 /* Finally, check that there's no seat of wildcard synthesis */
252 if (!wildname)
253 {
254 if (!(wildcard = strchr(next_closest, '.')) || wildcard == next_closest)
255 - return STAT_BOGUS;
256 + return 0;
257
258 wildcard--;
259 *wildcard = '*';
260
261 if ((digest_len = hash_name(wildcard, &digest, hash, salt, salt_len, iterations)) == 0)
262 - return STAT_BOGUS;
263 + return 0;
264
265 if (!check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, NULL))
266 - return STAT_BOGUS;
267 + return 0;
268 }
269
270 - return STAT_SECURE;
271 + return 1;
272 +}
273 +
274 +static int prove_non_existence(struct dns_header *header, size_t plen, char *keyname, char *name, int qtype, int qclass, char *wildname, int *nons)
275 +{
276 + static unsigned char **nsecset = NULL;
277 + static int nsecset_sz = 0;
278 +
279 + int type_found = 0;
280 + unsigned char *p = skip_questions(header, plen);
281 + int type, class, rdlen, i, nsecs_found;
282 +
283 + /* Move to NS section */
284 + if (!p || !(p = skip_section(p, ntohs(header->ancount), header, plen)))
285 + return 0;
286 +
287 + for (nsecs_found = 0, i = ntohs(header->nscount); i != 0; i--)
288 + {
289 + unsigned char *pstart = p;
290 +
291 + if (!(p = skip_name(p, header, plen, 10)))
292 + return 0;
293 +
294 + GETSHORT(type, p);
295 + GETSHORT(class, p);
296 + p += 4; /* TTL */
297 + GETSHORT(rdlen, p);
298 +
299 + if (class == qclass && (type == T_NSEC || type == T_NSEC3))
300 + {
301 + /* No mixed NSECing 'round here, thankyouverymuch */
302 + if (type_found != 0 && type_found != type)
303 + return 0;
304 +
305 + type_found = type;
306 +
307 + if (!expand_workspace(&nsecset, &nsecset_sz, nsecs_found))
308 + return 0;
309 +
310 + nsecset[nsecs_found++] = pstart;
311 + }
312 +
313 + if (!ADD_RDLEN(header, p, plen, rdlen))
314 + return 0;
315 + }
316 +
317 + if (type_found == T_NSEC)
318 + return prove_non_existence_nsec(header, plen, nsecset, nsecs_found, daemon->workspacename, keyname, name, qtype, nons);
319 + else
320 + return prove_non_existence_nsec3(header, plen, nsecset, nsecs_found, daemon->workspacename, keyname, name, qtype, wildname, nons);
321 }
322
323 /* Check signing status of name.
324 @@ -1925,10 +1921,9 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
325 static unsigned char **targets = NULL;
326 static int target_sz = 0;
327
328 - unsigned char *ans_start, *p1, *p2, **nsecs;
329 + unsigned char *ans_start, *p1, *p2;
330 int type1, class1, rdlen1, type2, class2, rdlen2, qclass, qtype, targetidx;
331 - int i, j, rc, nsec_count;
332 - int nsec_type;
333 + int i, j, rc;
334
335 if (neganswer)
336 *neganswer = 0;
337 @@ -2080,28 +2075,15 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
338 targets[j] = NULL;
339 }
340
341 - if (rc == STAT_SECURE_WILDCARD)
342 - {
343 - /* An attacker replay a wildcard answer with a different
344 - answer and overlay a genuine RR. To prove this
345 - hasn't happened, the answer must prove that
346 - the gennuine record doesn't exist. Check that here. */
347 - if (!(nsec_type = find_nsec_records(header, plen, &nsecs, &nsec_count, class1)))
348 - return STAT_BOGUS; /* No NSECs or bad packet */
349 -
350 - /* Note that we may not yet have validated the NSEC/NSEC3 RRsets. Since the check
351 - below returns either SECURE or BOGUS, that's not a problem. If the RRsets later fail
352 - we'll return BOGUS then. */
353 -
354 - if (nsec_type == T_NSEC)
355 - rc = prove_non_existence_nsec(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, type1, NULL);
356 - else
357 - rc = prove_non_existence_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename,
358 - keyname, name, type1, wildname, NULL);
359 -
360 - if (rc == STAT_BOGUS)
361 - return rc;
362 - }
363 + /* An attacker replay a wildcard answer with a different
364 + answer and overlay a genuine RR. To prove this
365 + hasn't happened, the answer must prove that
366 + the gennuine record doesn't exist. Check that here.
367 + Note that we may not yet have validated the NSEC/NSEC3 RRsets.
368 + That's not a problem since if the RRsets later fail
369 + we'll return BOGUS then. */
370 + if (rc == STAT_SECURE_WILDCARD && !prove_non_existence(header, plen, keyname, name, type1, class1, wildname, NULL))
371 + return STAT_BOGUS;
372 }
373 }
374 }
375 @@ -2124,14 +2106,13 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
376
377 /* For anything other than a DS record, this situation is OK if either
378 the answer is in an unsigned zone, or there's a NSEC records. */
379 - if (!(nsec_type = find_nsec_records(header, plen, &nsecs, &nsec_count, qclass)))
380 + if (!prove_non_existence(header, plen, keyname, name, qtype, qclass, NULL, nons))
381 {
382 /* Empty DS without NSECS */
383 if (qtype == T_DS)
384 return STAT_BOGUS;
385
386 - rc = zone_status(name, qclass, keyname, now);
387 - if (rc != STAT_SECURE)
388 + if ((rc = zone_status(name, qclass, keyname, now)) != STAT_SECURE)
389 {
390 if (class)
391 *class = qclass; /* Class for NEED_DS or NEED_DNSKEY */
392 @@ -2140,14 +2121,6 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
393
394 return STAT_BOGUS; /* signed zone, no NSECs */
395 }
396 -
397 - if (nsec_type == T_NSEC)
398 - rc = prove_non_existence_nsec(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, qtype, nons);
399 - else
400 - rc = prove_non_existence_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, qtype, NULL, nons);
401 -
402 - if (rc != STAT_SECURE)
403 - return rc;
404 }
405
406 return STAT_SECURE;
407 --
408 1.7.10.4
409