1 From fbc5205702c7f6f431d9f1043c553d7fb62ddfdb Mon Sep 17 00:00:00 2001
2 From: Simon Kelley <simon@thekelleys.org.uk>
3 Date: Tue, 23 Dec 2014 15:46:08 +0000
4 Subject: [PATCH 19/87] Fix problems validating NSEC3 and wildcards.
7 src/dnssec.c | 253 ++++++++++++++++++++++++++++++-----------------------------
8 1 file changed, 128 insertions(+), 125 deletions(-)
10 diff --git a/src/dnssec.c b/src/dnssec.c
11 index 3208ac701149..9350d3e8c963 100644
14 @@ -615,6 +615,7 @@ static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int
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
24 name is unchanged on exit. keyname is used as workspace and trashed.
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)
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
36 for (k = name_labels - labels; k != 0; k--)
37 - while (*name_start != '.' && *name_start != 0)
40 + while (*name_start != '.' && *name_start != 0)
47 + *wildcard_out = name_start+1;
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)
61 @@ -1443,11 +1452,88 @@ static int base32_decode(char *in, unsigned char *out)
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)
68 + int i, hash_len, salt_len, base32_len, rdlen;
69 + unsigned char *p, *psave;
71 + for (i = 0; i < nsec_count; i++)
74 + if (!extract_name(header, plen, &p, workspace1, 1, 0) ||
75 + !(base32_len = base32_decode(workspace1, (unsigned char *)workspace2)))
78 + p += 8; /* class, type, TTL */
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 */
86 + if (!CHECK_LEN(header, p, plen, hash_len))
89 + if (digest_len == base32_len && hash_len == base32_len)
91 + int rc = memcmp(workspace2, digest, digest_len);
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. */
98 + int offset = (type & 0xff) >> 3;
99 + int mask = 0x80 >> (type & 0x07);
101 + p += hash_len; /* skip next-domain hash */
102 + rdlen -= p - psave;
104 + if (!CHECK_LEN(header, p, plen, rdlen))
109 + if (p[0] == type >> 8)
111 + /* Does the NSEC3 say our type exists? */
112 + if (offset < p[1] && (p[offset+2] & mask) != 0)
115 + break; /* finshed checking */
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)
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)
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)
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;
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")))
156 - /* Now, we need the "closest encloser NSEC3" */
157 + if ((digest_len = hash_name(name, &digest, hash, salt, salt_len, iterations)) == 0)
160 + if (check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count))
161 + return STAT_SECURE;
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;
168 @@ -1529,6 +1622,9 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
169 if (*closest_encloser == '.')
172 + if (wildname && hostname_isequal(closest_encloser, wildname))
175 if ((digest_len = hash_name(closest_encloser, &digest, hash, salt, salt_len, iterations)) == 0)
178 @@ -1551,127 +1647,33 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
180 while ((closest_encloser = strchr(closest_encloser, '.')));
182 - /* No usable NSEC3s */
183 - if (i == nsec_count)
184 + if (!closest_encloser)
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. */
192 - unsigned char *psave;
193 - int offset = (type & 0xff) >> 3;
194 - int mask = 0x80 >> (type & 0x07);
196 - p += 8; /* class, type, TTL */
197 - GETSHORT(rdlen, p);
199 - p += 5 + salt_len; /* algo, flags, iterations, salt_len, salt */
201 - if (!CHECK_LEN(header, p, plen, hash_len))
202 - return STAT_BOGUS; /* bad packet */
204 - rdlen -= p - psave;
208 - if (!CHECK_LEN(header, p, plen, rdlen))
211 - if (p[0] == type >> 8)
213 - /* Does the NSEC3 say our type exists? */
214 - if (offset < p[1] && (p[offset+2] & mask) != 0)
217 - break; /* finshed checking */
224 - return STAT_SECURE;
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)
231 - for (i = 0; i < nsec_count; i++)
232 - if ((p = nsecs[i]))
234 - if (!extract_name(header, plen, &p, workspace1, 1, 0) ||
235 - !(base32_len = base32_decode(workspace1, (unsigned char *)workspace2)))
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 */
241 - if (!CHECK_LEN(header, p, plen, hash_len))
244 - if (digest_len == base32_len && hash_len == base32_len)
246 - if (memcmp(workspace2, digest, digest_len) <= 0)
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;
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;
262 - /* Finally, check that there's no seat of wildcard synthesis */
263 - if (!(wildcard = strchr(next_closest, '.')) || wildcard == next_closest)
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))
273 - for (i = 0; i < nsec_count; i++)
274 - if ((p = nsecs[i]))
276 - if (!extract_name(header, plen, &p, workspace1, 1, 0) ||
277 - !(base32_len = base32_decode(workspace1, (unsigned char *)workspace2)))
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 */
283 - if (!CHECK_LEN(header, p, plen, hash_len))
286 - if (digest_len == base32_len && hash_len == base32_len)
288 - if (memcmp(workspace2, digest, digest_len) <= 0)
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;
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;
303 + /* Finally, check that there's no seat of wildcard synthesis */
306 + if (!(wildcard = strchr(next_closest, '.')) || wildcard == next_closest)
312 + if ((digest_len = hash_name(wildcard, &digest, hash, salt, salt_len, iterations)) == 0)
315 + if (!check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count))
320 + return STAT_SECURE;
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
326 struct blockdata *key;
329 - rc = validate_rrset(now, header, plen, class1, type1, name, keyname, NULL, 0, 0, 0);
332 + rc = validate_rrset(now, header, plen, class1, type1, name, keyname, &wildname, NULL, 0, 0, 0);
334 if (rc == STAT_SECURE_WILDCARD)
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);
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);
343 if (rc != STAT_SECURE)
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);
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);
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;
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)
362 if (rc == STAT_NO_SIG)