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
8 src/dnssec.c | 207 +++++++++++++++++++++++++---------------------------------
9 1 file changed, 90 insertions(+), 117 deletions(-)
11 diff --git a/src/dnssec.c b/src/dnssec.c
12 index 012b2a6..ddae497 100644
15 @@ -1367,59 +1367,6 @@ static int hostname_cmp(const char *a, const char *b)
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)
23 - static unsigned char **nsecset = NULL;
24 - static int nsecset_sz = 0;
27 - unsigned char *p = skip_questions(header, plen);
28 - int type, class, rdlen, i, nsecs_found;
30 - /* Move to NS section */
31 - if (!p || !(p = skip_section(p, ntohs(header->ancount), header, plen)))
34 - for (nsecs_found = 0, i = ntohs(header->nscount); i != 0; i--)
36 - unsigned char *pstart = p;
38 - if (!(p = skip_name(p, header, plen, 10)))
46 - if (class == class_reqd && (type == T_NSEC || type == T_NSEC3))
48 - /* No mixed NSECing 'round here, thankyouverymuch */
49 - if (type_found == T_NSEC && type == T_NSEC3)
51 - if (type_found == T_NSEC3 && type == T_NSEC)
56 - if (!expand_workspace(&nsecset, &nsecset_sz, nsecs_found))
59 - nsecset[nsecs_found++] = pstart;
62 - if (!ADD_RDLEN(header, p, plen, rdlen))
66 - *nsecsetp = nsecset;
67 - *nsecsetl = nsecs_found;
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)
75 @@ -1436,12 +1383,12 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
78 if (!extract_name(header, plen, &p, workspace1, 1, 10))
81 p += 8; /* class, type, TTL */
84 if (!extract_name(header, plen, &p, workspace2, 1, 10))
88 rc = hostname_cmp(workspace1, name);
90 @@ -1449,7 +1396,7 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
92 /* 4035 para 5.4. Last sentence */
93 if (type == T_NSEC || type == T_RRSIG)
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)
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)
115 if (!CHECK_LEN(header, p, plen, rdlen))
119 if (p[0] == type >> 8)
121 /* Does the NSEC say our type exists? */
122 if (offset < p[1] && (p[offset+2] & mask) != 0)
126 break; /* finshed checking */
128 @@ -1491,24 +1438,24 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
132 - return STAT_SECURE;
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;
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;
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++)
160 if (!(p = skip_name(nsecs[i], header, plen, 15)))
161 - return STAT_BOGUS; /* bad packet */
162 + return 0; /* bad packet */
164 p += 10; /* type, class, TTL, rdlen */
166 @@ -1712,14 +1659,14 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
168 /* No usable NSEC3s */
174 GETSHORT (iterations, p);
177 if (!CHECK_LEN(header, salt, plen, salt_len))
178 - return STAT_BOGUS; /* bad packet */
179 + return 0; /* bad packet */
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. */
186 if (!(p = skip_name(nsec3p, header, plen, 15)))
187 - return STAT_BOGUS; /* bad packet */
188 + return 0; /* bad packet */
190 p += 10; /* type, class, TTL, rdlen */
192 @@ -1747,7 +1694,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
195 if (!CHECK_LEN(header, p, plen, salt_len))
196 - return STAT_BOGUS; /* bad packet */
197 + return 0; /* bad packet */
199 if (memcmp(p, salt, salt_len) != 0)
201 @@ -1758,13 +1705,13 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
203 /* Algo is checked as 1 above */
204 if (!(hash = hash_find("sha1")))
208 if ((digest_len = hash_name(name, &digest, hash, salt, salt_len, iterations)) == 0)
212 if (check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, nons))
213 - return STAT_SECURE;
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
221 if ((digest_len = hash_name(closest_encloser, &digest, hash, salt, salt_len, iterations)) == 0)
225 for (i = 0; i < nsec_count; i++)
228 if (!extract_name(header, plen, &p, workspace1, 1, 0) ||
229 !(base32_len = base32_decode(workspace1, (unsigned char *)workspace2)))
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, '.')));
238 if (!closest_encloser)
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)
247 if (!check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, NULL))
251 /* Finally, check that there's no seat of wildcard synthesis */
254 if (!(wildcard = strchr(next_closest, '.')) || wildcard == next_closest)
261 if ((digest_len = hash_name(wildcard, &digest, hash, salt, salt_len, iterations)) == 0)
265 if (!check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, NULL))
270 - return STAT_SECURE;
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)
276 + static unsigned char **nsecset = NULL;
277 + static int nsecset_sz = 0;
279 + int type_found = 0;
280 + unsigned char *p = skip_questions(header, plen);
281 + int type, class, rdlen, i, nsecs_found;
283 + /* Move to NS section */
284 + if (!p || !(p = skip_section(p, ntohs(header->ancount), header, plen)))
287 + for (nsecs_found = 0, i = ntohs(header->nscount); i != 0; i--)
289 + unsigned char *pstart = p;
291 + if (!(p = skip_name(p, header, plen, 10)))
295 + GETSHORT(class, p);
297 + GETSHORT(rdlen, p);
299 + if (class == qclass && (type == T_NSEC || type == T_NSEC3))
301 + /* No mixed NSECing 'round here, thankyouverymuch */
302 + if (type_found != 0 && type_found != type)
307 + if (!expand_workspace(&nsecset, &nsecset_sz, nsecs_found))
310 + nsecset[nsecs_found++] = pstart;
313 + if (!ADD_RDLEN(header, p, plen, rdlen))
317 + if (type_found == T_NSEC)
318 + return prove_non_existence_nsec(header, plen, nsecset, nsecs_found, daemon->workspacename, keyname, name, qtype, nons);
320 + return prove_non_existence_nsec3(header, plen, nsecset, nsecs_found, daemon->workspacename, keyname, name, qtype, wildname, nons);
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;
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;
337 @@ -2080,28 +2075,15 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
341 - if (rc == STAT_SECURE_WILDCARD)
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 */
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. */
354 - if (nsec_type == T_NSEC)
355 - rc = prove_non_existence_nsec(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, type1, NULL);
357 - rc = prove_non_existence_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename,
358 - keyname, name, type1, wildname, NULL);
360 - if (rc == STAT_BOGUS)
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))
375 @@ -2124,14 +2106,13 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
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))
382 /* Empty DS without NSECS */
386 - rc = zone_status(name, qclass, keyname, now);
387 - if (rc != STAT_SECURE)
388 + if ((rc = zone_status(name, qclass, keyname, now)) != STAT_SECURE)
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
394 return STAT_BOGUS; /* signed zone, no NSECs */
397 - if (nsec_type == T_NSEC)
398 - rc = prove_non_existence_nsec(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, qtype, nons);
400 - rc = prove_non_existence_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, qtype, NULL, nons);
402 - if (rc != STAT_SECURE)