xref: /openbsd/usr.sbin/nsd/nsec3.c (revision 898184e3)
1 /*
2  * nsec3.c -- nsec3 handling.
3  *
4  * Copyright (c) 2001-2011, NLnet Labs. All rights reserved.
5  *
6  * See LICENSE for the license.
7  *
8  */
9 #include <config.h>
10 #ifdef NSEC3
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <errno.h>
14 
15 #include "nsec3.h"
16 #include "iterated_hash.h"
17 #include "namedb.h"
18 #include "nsd.h"
19 #include "answer.h"
20 
21 #define NSEC3_SHA1_HASH 1 /* same type code as DS hash */
22 
23 
24 static void
25 detect_nsec3_params(rr_type* nsec3_apex,
26 	const unsigned char** salt, int* salt_len, int* iter)
27 {
28 	/* always uses first NSEC3 record with SOA bit set */
29 	assert(salt && salt_len && iter);
30 	assert(nsec3_apex);
31 	*salt_len = rdata_atom_data(nsec3_apex->rdatas[3])[0];
32 	*salt = (unsigned char*)(rdata_atom_data(nsec3_apex->rdatas[3])+1);
33 	*iter = read_uint16(rdata_atom_data(nsec3_apex->rdatas[2]));
34 }
35 
36 static const dname_type *
37 nsec3_hash_dname_param(region_type *region, zone_type *zone,
38 	const dname_type *dname, rr_type* param_rr)
39 {
40 	unsigned char hash[SHA_DIGEST_LENGTH];
41 	char b32[SHA_DIGEST_LENGTH*2+1];
42 	const unsigned char* nsec3_salt = NULL;
43 	int nsec3_saltlength = 0;
44 	int nsec3_iterations = 0;
45 
46 	detect_nsec3_params(param_rr, &nsec3_salt,
47 		&nsec3_saltlength, &nsec3_iterations);
48 	iterated_hash(hash, nsec3_salt, nsec3_saltlength, dname_name(dname),
49 		dname->name_size, nsec3_iterations);
50 	b32_ntop(hash, sizeof(hash), b32, sizeof(b32));
51 	dname=dname_parse(region, b32);
52 	dname=dname_concatenate(region, dname, domain_dname(zone->apex));
53 	return dname;
54 }
55 
56 const dname_type *
57 nsec3_hash_dname(region_type *region, zone_type *zone,
58 	const dname_type *dname)
59 {
60 	return nsec3_hash_dname_param(region, zone,
61 		dname, zone->nsec3_soa_rr);
62 }
63 
64 static int
65 nsec3_has_soa(rr_type* rr)
66 {
67 	if(rdata_atom_size(rr->rdatas[5]) >= 3 && /* has types in bitmap */
68 		rdata_atom_data(rr->rdatas[5])[0] == 0 && /* first window = 0, */
69 						/* [1]: windowlen must be >= 1 */
70 		rdata_atom_data(rr->rdatas[5])[2]&0x02)  /* SOA bit set */
71 		return 1;
72 	return 0;
73 }
74 
75 static rr_type*
76 find_zone_nsec3(namedb_type* namedb, zone_type *zone)
77 {
78 	size_t i;
79 	domain_type* domain;
80 	region_type* tmpregion;
81 	/* Check settings in NSEC3PARAM.
82 	   Hash algorithm must be OK. And a NSEC3 with soa bit
83 	   must map to the zone apex.  */
84 	rrset_type* paramset = domain_find_rrset(zone->apex, zone, TYPE_NSEC3PARAM);
85 	if(!paramset || !paramset->rrs || !paramset->rr_count)
86 		return NULL;
87 	tmpregion = region_create(xalloc, free);
88 	for(i=0; i < paramset->rr_count; i++) {
89 		rr_type* rr = &paramset->rrs[i];
90 		const dname_type* hashed_apex;
91 		rrset_type* nsec3_rrset;
92 		size_t j;
93 		const unsigned char *salt1;
94 		int saltlen1, iter1;
95 
96 		if(rdata_atom_data(rr->rdatas[0])[0] != NSEC3_SHA1_HASH) {
97 			log_msg(LOG_ERR, "%s NSEC3PARAM entry %d has unknown hash algo %d",
98 				dname_to_string(domain_dname(zone->apex), NULL), (int)i,
99 				rdata_atom_data(rr->rdatas[0])[0]);
100 			continue;
101 		}
102 		if(rdata_atom_data(rr->rdatas[1])[0] != 0) {
103 			/* draft-nsec3-09: NSEC3PARAM records with flags
104 			   field value other than zero MUST be ignored. */
105 			continue;
106 		}
107 		/* check hash of apex -> NSEC3 with soa bit on */
108 		hashed_apex = nsec3_hash_dname_param(tmpregion,
109 			zone, domain_dname(zone->apex), &paramset->rrs[i]);
110 		domain = domain_table_find(namedb->domains, hashed_apex);
111 		if(!domain) {
112 			log_msg(LOG_ERR, "%s NSEC3PARAM entry %d has no hash(apex).",
113 				dname_to_string(domain_dname(zone->apex), NULL), (int)i);
114 			log_msg(LOG_ERR, "hash(apex)= %s",
115 				dname_to_string(hashed_apex, NULL));
116 			continue;
117 		}
118 		nsec3_rrset = domain_find_rrset(domain, zone, TYPE_NSEC3);
119 		if(!nsec3_rrset) {
120 			log_msg(LOG_ERR, "%s NSEC3PARAM entry %d: hash(apex) has no NSEC3 RRset",
121 				dname_to_string(domain_dname(zone->apex), NULL), (int)i);
122 			continue;
123 		}
124 		detect_nsec3_params(rr, &salt1, &saltlen1, &iter1);
125 		/* find SOA bit enabled nsec3, with the same settings */
126 		for(j=0; j < nsec3_rrset->rr_count; j++) {
127 			const unsigned char *salt2;
128 			int saltlen2, iter2;
129 			if(!nsec3_has_soa(&nsec3_rrset->rrs[j]))
130 				continue;
131 			/* check params OK. Ignores the optout bit. */
132 			detect_nsec3_params(&nsec3_rrset->rrs[j],
133 				&salt2, &saltlen2, &iter2);
134 			if(saltlen1 == saltlen2 && iter1 == iter2 &&
135 				rdata_atom_data(rr->rdatas[0])[0] == /* algo */
136 				rdata_atom_data(nsec3_rrset->rrs[j].rdatas[0])[0]
137 				&& memcmp(salt1, salt2, saltlen1) == 0) {
138 				/* found it */
139 				DEBUG(DEBUG_QUERY, 1, (LOG_INFO,
140 					"detected NSEC3 for zone %s saltlen=%d iter=%d",
141 					dname_to_string(domain_dname(
142 					zone->apex),0), saltlen2, iter2));
143 				region_destroy(tmpregion);
144 				return &nsec3_rrset->rrs[j];
145 			}
146 		}
147 		log_msg(LOG_ERR, "%s NSEC3PARAM entry %d: hash(apex) no NSEC3 with SOAbit",
148 			dname_to_string(domain_dname(zone->apex), NULL), (int)i);
149 	}
150 	region_destroy(tmpregion);
151 	return NULL;
152 }
153 
154 /* check that the rrset has an NSEC3 that uses the same parameters as the
155    zone is using. Pass NSEC3 rrset, and zone must have nsec3_rrset set.
156    if you pass NULL then 0 is returned. */
157 static int
158 nsec3_rrset_params_ok(rr_type* base, rrset_type* rrset)
159 {
160 	rdata_atom_type* prd;
161 	size_t i;
162 	if(!rrset)
163 		return 0; /* without rrset, no matching params either */
164 	assert(rrset && rrset->zone && (base || rrset->zone->nsec3_soa_rr));
165 	if(!base)
166 		base = rrset->zone->nsec3_soa_rr;
167 	prd = base->rdatas;
168 	for(i=0; i < rrset->rr_count; ++i) {
169 		rdata_atom_type* rd = rrset->rrs[i].rdatas;
170 		assert(rrset->rrs[i].type == TYPE_NSEC3);
171 		if(rdata_atom_data(rd[0])[0] ==
172 			rdata_atom_data(prd[0])[0] && /* hash algo */
173 		   rdata_atom_data(rd[2])[0] ==
174 			rdata_atom_data(prd[2])[0] && /* iterations 0 */
175 		   rdata_atom_data(rd[2])[1] ==
176 			rdata_atom_data(prd[2])[1] && /* iterations 1 */
177 		   rdata_atom_data(rd[3])[0] ==
178 			rdata_atom_data(prd[3])[0] && /* salt length */
179 		   memcmp(rdata_atom_data(rd[3])+1,
180 			rdata_atom_data(prd[3])+1, rdata_atom_data(rd[3])[0])
181 			== 0 )
182 		{
183 			/* this NSEC3 matches nsec3 parameters from zone */
184 			return 1;
185 		}
186 	}
187 	return 0;
188 }
189 
190 #ifdef FULL_PREHASH
191 
192 int
193 nsec3_find_cover(namedb_type* db, zone_type* zone,
194 	const dname_type* hashname, domain_type** result)
195 {
196 	rrset_type *rrset;
197 	domain_type *walk;
198 	domain_type *closest_match;
199 	domain_type *closest_encloser;
200 	int exact;
201 
202 	assert(result);
203 	assert(zone->nsec3_soa_rr);
204 
205 	exact = domain_table_search(
206 		db->domains, hashname, &closest_match, &closest_encloser);
207 	/* exact match of hashed domain name + it has an NSEC3? */
208 	if(exact &&
209 	   nsec3_rrset_params_ok(NULL,
210 	   	domain_find_rrset(closest_encloser, zone, TYPE_NSEC3))) {
211 		*result = closest_encloser;
212 		assert(*result != 0);
213 		return 1;
214 	}
215 
216 	/* find covering NSEC3 record, lexicographically before the closest match */
217 	/* use nsec3_lookup to jumpstart the search */
218 	walk = closest_match->nsec3_lookup;
219 	rrset = 0;
220 	while(walk && dname_is_subdomain(domain_dname(walk), domain_dname(zone->apex)))
221 	{
222 		if(nsec3_rrset_params_ok(NULL,
223 			domain_find_rrset(walk, zone, TYPE_NSEC3))) {
224 			/* this rrset is OK NSEC3, exit while */
225 			rrset = domain_find_rrset(walk, zone, TYPE_NSEC3);
226 			break;
227 		}
228 		walk = domain_previous(walk);
229 	}
230 	if(rrset)
231 		*result = walk;
232 	else 	{
233 		/*
234 		 * There are no NSEC3s before the closest match.
235 		 * so the hash name is before the first NSEC3 record in the zone.
236 		 * use last NSEC3, which covers the wraparound in hash space
237 		 *
238 		 * Since the zone has an NSEC3 with the SOA bit set for NSEC3 to turn on,
239 		 * there is also a last nsec3, so find_cover always assigns *result!=0.
240 		 */
241 		*result = zone->nsec3_last;
242 	}
243 	assert(*result != 0);
244 	return 0;
245 }
246 
247 #else
248 
249 int
250 nsec3_find_cover(namedb_type* ATTR_UNUSED(db), zone_type* zone,
251 	const dname_type* hashname, struct nsec3_domain **result)
252 {
253 	rbnode_t *node;
254 	int exact;
255 
256 	assert(result);
257 	if (!zone->nsec3_domains)
258 		return 0;
259 
260 	exact = rbtree_find_less_equal(zone->nsec3_domains, hashname, &node);
261 	if (!node) {
262 		exact = 0;
263 		node = rbtree_last(zone->nsec3_domains);
264 	}
265 
266 	while (node != RBTREE_NULL) {
267 		struct rrset *nsec3_rrset;
268 		struct nsec3_domain *nsec3_domain =
269 			(struct nsec3_domain *) node;
270 		nsec3_rrset = domain_find_rrset(nsec3_domain->nsec3_domain,
271 			zone, TYPE_NSEC3);
272 		if (!nsec3_rrset) {
273 			/*
274 			 * RRset in zone->nsec3_domains whose type != NSEC3
275 			 * If we get here, something is seriously wrong!
276 			 */
277 			return 0;
278 		}
279 		if (nsec3_rrset_params_ok(NULL, nsec3_rrset) != 0) {
280 			*result = nsec3_domain;
281 			return exact;
282                 }
283 		exact = 0; /* No match, so we're looking for closest match */
284 		node = rbtree_previous(node);
285 	}
286 	/*
287 	 * If we reach this point, *result == NULL.  This should
288 	 * never happen since the zone should have one NSEC3 record with
289 	 * the SOA bit set, which matches a NSEC3PARAM RR in the zone.
290 	 */
291 	return exact;
292 }
293 #endif
294 
295 #ifdef FULL_PREHASH
296 static void
297 prehash_domain_r(namedb_type* db, zone_type* zone,
298 	domain_type* domain, region_type* region)
299 {
300 	int exact;
301 	const dname_type *wcard, *wcard_child, *hashname;
302 	domain_type* result = 0;
303 	if(!zone->nsec3_soa_rr)
304 	{
305 		/* set to 0 (in case NSEC3 removed after an update) */
306 		domain->nsec3_is_exact = 0;
307 		domain->nsec3_cover = NULL;
308 		domain->nsec3_wcard_child_cover = NULL;
309 		return;
310 	}
311 
312 	hashname = nsec3_hash_dname(region, zone, domain_dname(domain));
313 	exact = nsec3_find_cover(db, zone, hashname, &result);
314 	domain->nsec3_cover = result;
315 	if(exact)
316 		domain->nsec3_is_exact = 1;
317 	else	domain->nsec3_is_exact = 0;
318 
319 	/* find cover for *.domain for wildcard denial */
320 	wcard = dname_parse(region, "*");
321 	wcard_child = dname_concatenate(region, wcard, domain_dname(domain));
322 	hashname = nsec3_hash_dname(region, zone, wcard_child);
323 	exact = nsec3_find_cover(db, zone, hashname, &result);
324 	domain->nsec3_wcard_child_cover = result;
325 
326 	if(exact && !domain_wildcard_child(domain))
327 	{
328 		/* We found an exact match for the *.domain NSEC3 hash,
329 		 * but the domain wildcard child (*.domain) does not exist.
330 		 * Thus there is a hash collision. It will cause servfail
331 		 * for NXdomain queries below this domain.
332 		 */
333 		log_msg(LOG_WARNING, "prehash: collision of wildcard "
334 			"denial for %s. Sign zone with different salt "
335 			"to remove collision.",
336 			dname_to_string(domain_dname(domain),0));
337 	}
338 }
339 
340 #else
341 
342 static void
343 prehash_domain_r(namedb_type* db, zone_type* zone,
344 	domain_type* domain, region_type* region)
345 {
346 	int exact;
347 	const dname_type *hashname;
348 	struct nsec3_domain* result = NULL;
349 
350 	domain->nsec3_cover = NULL;
351 	hashname = nsec3_hash_dname(region, zone, domain_dname(domain));
352 	exact = nsec3_find_cover(db, zone, hashname, &result);
353 	if (result && exact)
354     {
355 		result->covers = domain;
356 		domain->nsec3_cover = result->nsec3_domain;
357 	}
358 	return;
359 }
360 #endif
361 
362 static void
363 prehash_domain(namedb_type* db, zone_type* zone,
364 	domain_type* domain, region_type* region)
365 {
366     prehash_domain_r(db, zone, domain, region);
367 }
368 
369 #ifdef FULL_PREHASH
370 static void
371 prehash_ds(namedb_type* db, zone_type* zone,
372 	domain_type* domain, region_type* region)
373 {
374 	domain_type* result = 0;
375 	const dname_type* hashname;
376 	int exact;
377 
378 	if(!zone->nsec3_soa_rr) {
379 		domain->nsec3_ds_parent_is_exact = 0;
380 		domain->nsec3_ds_parent_cover = NULL;
381 		return;
382 	}
383 
384 	/* hash again, other zone could have different hash parameters */
385 	hashname = nsec3_hash_dname(region, zone, domain_dname(domain));
386 	exact = nsec3_find_cover(db, zone, hashname, &result);
387 	if(exact)
388 		domain->nsec3_ds_parent_is_exact = 1;
389 	else 	domain->nsec3_ds_parent_is_exact = 0;
390 	domain->nsec3_ds_parent_cover = result;
391 }
392 #endif
393 
394 #ifndef FULL_PREHASH
395 struct domain *
396 find_last_nsec3_domain(struct zone *zone)
397 {
398 	rbnode_t *node;
399 	if (zone->nsec3_domains == NULL) {
400 		return NULL;
401 	}
402 	node = rbtree_last(zone->nsec3_domains);
403         if (node == RBTREE_NULL) {
404                 return NULL;
405         }
406 	return ((struct nsec3_domain *) node)->nsec3_domain;
407 }
408 
409 void
410 prehash_zone_incremental(struct namedb *db, struct zone *zone)
411 {
412 	region_type *temp_region;
413 	rbnode_t *node;
414 	/* find zone NSEC3PARAM settings */
415 	zone->nsec3_soa_rr = find_zone_nsec3(db, zone);
416         if (zone->nsec3_soa_rr == NULL) {
417                 zone->nsec3_last = NULL;
418                 return;
419         }
420         if (db->nsec3_mod_domains == NULL) {
421                 return;
422         }
423 	zone->nsec3_last = find_last_nsec3_domain(zone);
424         temp_region = region_create(xalloc, free);
425 	node = rbtree_first(db->nsec3_mod_domains);
426 	while (node != RBTREE_NULL) {
427 		struct nsec3_mod_domain *nsec3_mod_domain =
428 			(struct nsec3_mod_domain *) node;
429 		struct domain *walk = nsec3_mod_domain->domain;
430 		struct domain *domain_zone_apex;
431 
432 		if (!walk ||
433 			(dname_is_subdomain(domain_dname(walk),
434 				domain_dname(zone->apex)) == 0)) {
435 			node = rbtree_next(node);
436 			continue;
437 		}
438 		if (walk->nsec3_cover != NULL) {
439 			node = rbtree_next(node);
440 			continue;
441 		}
442 		/* Empty Terminal */
443 		if (!walk->is_existing) {
444 			walk->nsec3_cover = NULL;
445 			node = rbtree_next(node);
446 			continue;
447 		}
448 
449 		/*
450 		 * Don't hash NSEC3 only nodes, unless possibly
451 		 * part of a weird case where node is empty nonterminal
452 		 * requiring NSEC3 but node name also is the hashed
453 		 * node name of another node requiring NSEC3.
454 		 * NSEC3 Empty Nonterminal with NSEC3 RRset present.
455 		 */
456 		if (domain_has_only_NSEC3(walk, zone) != 0) {
457 			struct domain *next_domain = domain_next(walk);
458 			if ((next_domain == NULL) ||
459 			    (next_domain->parent != walk)) {
460 				walk->nsec3_cover = NULL;
461 				node = rbtree_next(node);
462 				continue;
463 			}
464 		}
465 
466 		/*
467 		 * Identify domain nodes that belong to the zone
468 		 * which are not glue records.  What if you hit a
469 		 * record that's in two zones but which has no
470 		 * cut point between the zones. Not valid but
471 		 * someone is gonna try it sometime.
472 		 * This implementation doesn't link an NSEC3
473 		 * record to the domain.
474 		 */
475 		domain_zone_apex = domain_find_zone_apex(walk);
476 		if ((domain_zone_apex != NULL) &&
477 		    (domain_zone_apex == zone->apex) &&
478 		    (domain_is_glue(walk, zone) == 0)) {
479 
480                         prehash_domain(db, zone, walk, temp_region);
481 			region_free_all(temp_region);
482 		}
483 
484 		node = rbtree_next(node);
485 	}
486 	namedb_nsec3_mod_domains_destroy(db);
487 	region_destroy(temp_region);
488 }
489 
490 void
491 prehash_zone(struct namedb* db, struct zone* zone)
492 {
493 	domain_type *walk;
494 	domain_type *last_nsec3_node;
495 	region_type *temp_region;
496 	assert(db && zone);
497 
498 	/* find zone settings */
499 	zone->nsec3_soa_rr = find_zone_nsec3(db, zone);
500 	if(!zone->nsec3_soa_rr) {
501 		zone->nsec3_last = NULL;
502 		return;
503 	}
504 	temp_region = region_create(xalloc, free);
505 
506 	/* go through entire zone and setup nsec3_lookup speedup */
507 	walk = zone->apex;
508 	last_nsec3_node = NULL;
509 	/* since we walk in sorted order, we pass all NSEC3s in sorted
510 	   order and we can set the lookup ptrs */
511 	while(walk && dname_is_subdomain(
512 		domain_dname(walk), domain_dname(zone->apex)))
513 	{
514 		struct domain *domain_zone_apex;
515 
516 		if (walk->nsec3_cover != NULL) {
517 			walk = domain_next(walk);
518 			continue;
519 		}
520 		/* Empty Terminal */
521 		if (walk->is_existing == 0) {
522 			walk->nsec3_cover = NULL;
523 			walk = domain_next(walk);
524 			continue;
525 		}
526 
527 		/*
528 		 * Don't hash NSEC3 only nodes, unless possibly
529 		 * part of a weird case where node is empty nonterminal
530 		 * requiring NSEC3 but node name also is the hashed
531 		 * node name of another node requiring NSEC3.
532 		 * NSEC3 Empty Nonterminal with NSEC3 RRset present.
533 		 */
534 		if (domain_has_only_NSEC3(walk, zone)) {
535 			struct domain *next_domain = domain_next(walk);
536 			if ((next_domain == NULL) ||
537 			    (next_domain->parent != walk)) {
538 				walk->nsec3_cover = NULL;
539 				walk = domain_next(walk);
540 				continue;
541 			}
542 		}
543 
544 		/*
545 		 * Identify domain nodes that belong to the zone
546 		 * which are not glue records.  What if you hit a
547 		 * record that's in two zones but which has no
548 		 * cut point between the zones. Not valid but
549 		 * someone is gonna try it sometime.
550 		 * This implementation doesn't link an NSEC3
551 		 * record to the domain.
552 		 */
553 		domain_zone_apex = domain_find_zone_apex(walk);
554 		if ((domain_zone_apex != NULL) &&
555 		    (domain_zone_apex == zone->apex) &&
556 		    (domain_is_glue(walk, zone) == 0))
557 		{
558 			prehash_domain(db, zone, walk, temp_region);
559 			region_free_all(temp_region);
560 		}
561 		walk = domain_next(walk);
562 	}
563 	region_destroy(temp_region);
564 }
565 
566 #else
567 
568 static void
569 prehash_zone(struct namedb* db, struct zone* zone)
570 {
571 	domain_type *walk;
572 	domain_type *last_nsec3_node;
573 	region_type *temp_region;
574 	assert(db && zone);
575 
576 	/* find zone settings */
577 	zone->nsec3_soa_rr = find_zone_nsec3(db, zone);
578 	if(!zone->nsec3_soa_rr) {
579 		zone->nsec3_last = NULL;
580 		return;
581 	}
582 	temp_region = region_create(xalloc, free);
583 
584 	/* go through entire zone and setup nsec3_lookup speedup */
585 	walk = zone->apex;
586 	last_nsec3_node = NULL;
587 	/* since we walk in sorted order, we pass all NSEC3s in sorted
588 	   order and we can set the lookup ptrs */
589 	while(walk && dname_is_subdomain(
590 		domain_dname(walk), domain_dname(zone->apex)))
591 	{
592 		zone_type* z = domain_find_zone(walk);
593 		if(z && z==zone)
594 		{
595 			if(domain_find_rrset(walk, zone, TYPE_NSEC3))
596 				last_nsec3_node = walk;
597 			walk->nsec3_lookup = last_nsec3_node;
598 		}
599 		walk = domain_next(walk);
600 	}
601 	zone->nsec3_last = last_nsec3_node;
602 
603 	/* go through entire zone */
604 	walk = zone->apex;
605 	while(walk && dname_is_subdomain(
606 		domain_dname(walk), domain_dname(zone->apex)))
607 	{
608 		zone_type* z;
609 		if(!walk->is_existing && domain_has_only_NSEC3(walk, zone)) {
610 			walk->nsec3_cover = NULL;
611 			walk->nsec3_wcard_child_cover = NULL;
612 			walk = domain_next(walk);
613 			continue;
614 		}
615 		z = domain_find_zone(walk);
616 		if(z && z==zone && !domain_is_glue(walk, zone))
617 		{
618 			prehash_domain(db, zone, walk, temp_region);
619 			region_free_all(temp_region);
620 		}
621 		/* prehash the DS (parent zone) */
622 		if(domain_find_rrset(walk, zone, TYPE_DS) ||
623 			(domain_find_rrset(walk, zone, TYPE_NS) &&
624 			 walk != zone->apex))
625 		{
626 			assert(walk != zone->apex /* DS must be above zone cut */);
627 			prehash_ds(db, zone, walk, temp_region);
628 			region_free_all(temp_region);
629 		}
630 		walk = domain_next(walk);
631 	}
632 	region_destroy(temp_region);
633 }
634 #endif
635 
636 void
637 prehash(struct namedb* db, int updated_only)
638 {
639 	zone_type *z;
640 	time_t end, start = time(NULL);
641 	int count = 0;
642 	for(z = db->zones; z; z = z->next)
643 	{
644 		if(!updated_only || z->updated) {
645 			prehash_zone(db, z);
646 			if(z->nsec3_soa_rr)
647 				count++;
648 		}
649 	}
650 	end = time(NULL);
651 	if(count > 0)
652 		VERBOSITY(1, (LOG_INFO, "nsec3-prepare took %d "
653 		"seconds for %d zones.", (int)(end-start), count));
654 }
655 
656 
657 #ifndef FULL_PREHASH
658 static void
659 nsec3_hash_and_find_cover(struct region *region,
660 	struct namedb *db, const struct dname *domain_dname,
661 	struct zone *zone, int *exact, struct domain **result)
662 {
663 	dname_type const *hash_dname;
664 	struct nsec3_domain *nsec3_domain;
665 
666 	*result = NULL;
667 	*exact = 0;
668 	hash_dname = nsec3_hash_dname(region, zone, domain_dname);
669 	*exact = nsec3_find_cover(db, zone, hash_dname, &nsec3_domain);
670         if (nsec3_domain != NULL) {
671                 *result = nsec3_domain->nsec3_domain;
672         }
673         return;
674 }
675 
676 static void
677 nsec3_hash_and_find_wild_cover(struct region *region,
678 	struct namedb *db, struct domain *domain,
679 	struct zone *zone, int *exact, struct domain **result)
680 {
681 	struct dname const *wcard_child;
682 	/* find cover for *.domain for wildcard denial */
683 	(void) dname_make_wildcard(region, domain_dname(domain),
684 		&wcard_child);
685         nsec3_hash_and_find_cover(region, db, wcard_child, zone, exact,
686 		result);
687 	if ((*exact != 0) &&
688 		(domain_wildcard_child(domain) == NULL)) {
689 		/* We found an exact match for the *.domain NSEC3 hash,
690 		 * but the domain wildcard child (*.domain) does not exist.
691 		 * Thus there is a hash collision. It will cause servfail
692 		 * for NXdomain queries below this domain.
693 		 */
694 		log_msg(LOG_WARNING,
695 			"collision of wildcard denial for %s. "
696 			"Sign zone with different salt to remove collision.",
697 			dname_to_string(domain_dname(domain), NULL));
698 	}
699 }
700 #endif
701 
702 
703 /* add the NSEC3 rrset to the query answer at the given domain */
704 static void
705 nsec3_add_rrset(struct query *query, struct answer *answer,
706 	rr_section_type section, struct domain* domain)
707 {
708 	if(domain) {
709 		rrset_type* rrset = domain_find_rrset(domain, query->zone, TYPE_NSEC3);
710 		if(rrset)
711 			answer_add_rrset(answer, section, domain, rrset);
712 	}
713 }
714 
715 /* this routine does hashing at query-time. slow. */
716 static void
717 nsec3_add_nonexist_proof(struct query *query, struct answer *answer,
718         struct domain *encloser, struct namedb* db, const dname_type* qname)
719 {
720 	const dname_type *to_prove;
721 #ifdef FULL_PREHASH
722 	const dname_type *hashed;
723 #else
724 	int exact = 0;
725 #endif
726 	domain_type *cover = NULL;
727 	assert(encloser);
728 	/* if query=a.b.c.d encloser=c.d. then proof needed for b.c.d. */
729 	/* if query=a.b.c.d encloser=*.c.d. then proof needed for b.c.d. */
730 	to_prove = dname_partial_copy(query->region, qname,
731 		dname_label_match_count(qname, domain_dname(encloser))+1);
732 	/* generate proof that one label below closest encloser does not exist */
733 #ifdef FULL_PREHASH
734 	hashed = nsec3_hash_dname(query->region, query->zone, to_prove);
735 	if(nsec3_find_cover(db, query->zone, hashed, &cover))
736 #else
737 	nsec3_hash_and_find_cover(query->region, db, to_prove, query->zone,
738 		&exact, &cover);
739 	if (exact)
740 #endif
741 	{
742 		/* exact match, hash collision */
743 		/* the hashed name of the query corresponds to an existing name. */
744 		log_msg(LOG_ERR, "nsec3 hash collision for name=%s",
745 			dname_to_string(to_prove, NULL));
746 		RCODE_SET(query->packet, RCODE_SERVFAIL);
747 		return;
748 	}
749 	else if (cover) {
750 		/* cover proves the qname does not exist */
751 		nsec3_add_rrset(query, answer, AUTHORITY_SECTION, cover);
752 	}
753 }
754 
755 static void
756 nsec3_add_closest_encloser_proof(
757 	struct query *query, struct answer *answer,
758 	struct domain *closest_encloser, struct namedb* db,
759 	const dname_type* qname)
760 {
761 	if(!closest_encloser)
762 		return;
763 	/* prove that below closest encloser nothing exists */
764 	nsec3_add_nonexist_proof(query, answer, closest_encloser, db, qname);
765 	/* proof that closest encloser exists */
766 #ifdef FULL_PREHASH
767 	if(closest_encloser->nsec3_is_exact)
768 #else
769 	if(closest_encloser->nsec3_cover)
770 #endif
771 		nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
772 			closest_encloser->nsec3_cover);
773 }
774 
775 
776 void
777 nsec3_answer_wildcard(struct query *query, struct answer *answer,
778         struct domain *wildcard, struct namedb* db, const dname_type* qname)
779 {
780 	if(!wildcard)
781 		return;
782 	if(!query->zone->nsec3_soa_rr)
783 		return;
784 	nsec3_add_nonexist_proof(query, answer, wildcard, db, qname);
785 }
786 
787 
788 static void
789 nsec3_add_ds_proof(struct query *query, struct answer *answer,
790 	struct domain *domain, int delegpt)
791 {
792 #ifndef FULL_PREHASH
793 	struct domain * ds_parent_cover = NULL;
794 	int exact = 0;
795 #endif
796 	/* assert we are above the zone cut */
797 	assert(domain != query->zone->apex);
798 
799 #ifdef FULL_PREHASH
800 	if(domain->nsec3_ds_parent_is_exact) {
801 		/* use NSEC3 record from above the zone cut. */
802 		nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
803 			domain->nsec3_ds_parent_cover);
804 	} else if (!delegpt && domain->nsec3_is_exact) {
805 #else
806 	nsec3_hash_and_find_cover(query->region, NULL, domain_dname(domain),
807 		query->zone, &exact, &ds_parent_cover);
808 	if (exact) {
809 		/* use NSEC3 record from above the zone cut. */
810 		if (ds_parent_cover) {
811 			nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
812 				ds_parent_cover);
813 		}
814 	} else if (!delegpt && domain->nsec3_cover) {
815 #endif
816 		nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
817 			domain->nsec3_cover);
818 	} else {
819 		/* prove closest provable encloser */
820 		domain_type* par = domain->parent;
821 		domain_type* prev_par = NULL;
822 		assert(par); /* must be provable, thus there must be a parent */
823 
824 #ifdef FULL_PREHASH
825 		while(!par->nsec3_is_exact)
826 #else
827 		while(!par->nsec3_cover)
828 #endif
829 		{
830 			prev_par = par;
831 			par = par->parent;
832 			assert(par); /* parent zone apex must be provable, thus this ends */
833 		}
834 		nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
835 			par->nsec3_cover);
836 		/* we took several steps to go to the provable parent, so
837 		   the one below it has no exact nsec3, disprove it.
838 		   disprove is easy, it has a prehashed cover ptr. */
839 		if(prev_par) {
840 #ifdef FULL_PREHASH
841 			assert(prev_par != domain && !prev_par->nsec3_is_exact);
842 			nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
843 				prev_par->nsec3_cover);
844 #else
845 			struct domain *prev_parent_cover = NULL;
846 			nsec3_hash_and_find_cover(query->region, NULL,
847 				domain_dname(prev_par), query->zone,
848 				&exact, &prev_parent_cover);
849 			if (prev_parent_cover) {
850 				nsec3_add_rrset(query, answer,
851 					AUTHORITY_SECTION, prev_parent_cover);
852 			}
853 #endif
854 		}
855 
856 		/* use NSEC3 record from above the zone cut. */
857 		/* add optout range from parent zone */
858 		/* note: no check of optout bit, resolver checks it */
859 #ifdef FULL_PREHASH
860 		nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
861 			domain->nsec3_ds_parent_cover);
862 #else
863 		nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
864 			ds_parent_cover);
865 #endif
866 	}
867 }
868 
869 
870 void
871 nsec3_answer_nodata(struct query *query, struct answer *answer,
872 	struct domain *original)
873 {
874 	if(!query->zone->nsec3_soa_rr)
875 		return;
876 
877 	/* nodata when asking for secure delegation */
878 	if(query->qtype == TYPE_DS)
879 	{
880 		if(original == query->zone->apex) {
881 			/* DS at zone apex, but server not authoritative for parent zone */
882 			/* so answer at the child zone level */
883 #ifdef FULL_PREHASH
884 			if(original->nsec3_is_exact)
885 #else
886 			if(original->nsec3_cover)
887 #endif
888 				nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
889 					original->nsec3_cover);
890 			return;
891 		}
892 		/* query->zone must be the parent zone */
893 		nsec3_add_ds_proof(query, answer, original, 0);
894 	}
895 	/* the nodata is result from a wildcard match */
896 	else if (original==original->wildcard_child_closest_match
897 		&& label_is_wildcard(dname_name(domain_dname(original)))) {
898 #ifndef FULL_PREHASH
899 		struct domain* original_cover;
900 		int exact;
901 #endif
902 		/* denial for wildcard is already there */
903 
904 		/* add parent proof to have a closest encloser proof for wildcard parent */
905 		/* in other words: nsec3 matching closest encloser */
906 #ifdef FULL_PREHASH
907 		if(original->parent && original->parent->nsec3_is_exact)
908 #else
909 		if(original->parent && original->parent->nsec3_cover)
910 #endif
911 			nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
912 				original->parent->nsec3_cover);
913 
914 		/* proof for wildcard itself */
915 		/* in other words: nsec3 matching source of synthesis */
916 #ifdef FULL_PREHASH
917 		nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
918 			original->nsec3_cover);
919 #else
920 		original_cover = original->nsec3_cover;
921 		if (!original_cover) { /* not exact */
922 			nsec3_hash_and_find_cover(query->region, NULL,
923 				domain_dname(original), query->zone,
924 				&exact, &original_cover);
925                 }
926 		if (original_cover) {
927 	                nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
928 				original_cover);
929 		}
930 #endif
931 
932 	}
933 	else { /* add nsec3 to prove rrset does not exist */
934 #ifdef FULL_PREHASH
935 		if(original->nsec3_is_exact)
936 #else
937 		if (original->nsec3_cover != NULL)
938 #endif
939 			nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
940 				original->nsec3_cover);
941 	}
942 }
943 
944 void
945 nsec3_answer_delegation(struct query *query, struct answer *answer)
946 {
947 	if(!query->zone->nsec3_soa_rr)
948 		return;
949 	nsec3_add_ds_proof(query, answer, query->delegation_domain, 1);
950 }
951 
952 int
953 domain_has_only_NSEC3(struct domain* domain, struct zone* zone)
954 {
955 	/* check for only NSEC3/RRSIG */
956 	rrset_type* rrset = domain->rrsets;
957 	int nsec3_seen = 0;
958 	while(rrset)
959 	{
960 		if(!zone || rrset->zone == zone)
961 		{
962 			if(rrset->rrs[0].type == TYPE_NSEC3)
963 				nsec3_seen = 1;
964 			else if(rrset->rrs[0].type != TYPE_RRSIG)
965 				return 0;
966 		}
967 		rrset = rrset->next;
968 	}
969 	return nsec3_seen;
970 }
971 
972 void
973 nsec3_answer_authoritative(struct domain** match, struct query *query,
974 	struct answer *answer, struct domain* closest_encloser,
975 	struct namedb* db, const dname_type* qname)
976 {
977 #ifndef FULL_PREHASH
978 	struct domain *cover_domain = NULL;
979 	int exact = 0;
980 #endif
981 
982 	if(!query->zone->nsec3_soa_rr)
983 		return;
984 	assert(match);
985 	/* there is a match, this has 1 RRset, which is NSEC3, but qtype is not. */
986         /* !is_existing: no RR types exist at the QNAME, nor at any descendant of QNAME */
987 	if(*match && !(*match)->is_existing &&
988 #if 0
989 		query->qtype != TYPE_NSEC3 &&
990 #endif
991 		domain_has_only_NSEC3(*match, query->zone))
992 	{
993 		/* act as if the NSEC3 domain did not exist, name error */
994 		*match = 0;
995 		/* all nsec3s are directly below the apex, that is closest encloser */
996 #ifdef FULL_PREHASH
997 		if(query->zone->apex->nsec3_is_exact)
998 #else
999 		if(query->zone->apex->nsec3_cover)
1000 #endif
1001 			nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
1002 				query->zone->apex->nsec3_cover);
1003 
1004 		/* disprove the nsec3 record. */
1005 		nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
1006 			closest_encloser->nsec3_cover);
1007 		/* disprove a wildcard */
1008 #ifdef FULL_PREHASH
1009 		nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
1010             query->zone->apex->nsec3_wcard_child_cover);
1011 #else
1012 		cover_domain = NULL;
1013 		nsec3_hash_and_find_cover(query->region, db,
1014 			domain_dname(closest_encloser),
1015 			query->zone, &exact, &cover_domain);
1016 		if (cover_domain)
1017 	                nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
1018 				cover_domain);
1019 #endif
1020 		if (domain_wildcard_child(query->zone->apex)) {
1021 			/* wildcard exists below the domain */
1022 			/* wildcard and nsec3 domain clash. server failure. */
1023 			RCODE_SET(query->packet, RCODE_SERVFAIL);
1024 		}
1025 		return;
1026 	}
1027 	else if(*match && (*match)->is_existing &&
1028 #if 0
1029 		query->qtype != TYPE_NSEC3 &&
1030 #endif
1031 		domain_has_only_NSEC3(*match, query->zone))
1032 	{
1033 		/* this looks like a NSEC3 domain, but is actually an empty non-terminal. */
1034 		nsec3_answer_nodata(query, answer, *match);
1035 		return;
1036 	}
1037 	if(!*match) {
1038 		/* name error, domain does not exist */
1039 		nsec3_add_closest_encloser_proof(query, answer,
1040 			closest_encloser, db, qname);
1041 #ifdef FULL_PREHASH
1042 		nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
1043 			closest_encloser->nsec3_wcard_child_cover);
1044 #else
1045 		cover_domain = NULL;
1046 		nsec3_hash_and_find_wild_cover(query->region, db,
1047 			closest_encloser, query->zone, &exact, &cover_domain);
1048 		if (cover_domain)
1049 			nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
1050 				cover_domain);
1051 #endif
1052 	}
1053 }
1054 
1055 #endif /* NSEC3 */
1056