xref: /openbsd/usr.sbin/nsd/nsec3.c (revision 891d7ab6)
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 
14 #include "nsec3.h"
15 #include "iterated_hash.h"
16 #include "namedb.h"
17 #include "nsd.h"
18 #include "answer.h"
19 
20 #define NSEC3_SHA1_HASH 1 /* same type code as DS hash */
21 
22 /* detect is the latter rrset has the same hashalgo, iterations and salt
23    as the base. Does not compare optout bit, or other rdata.
24    base=NULL uses the zone soa_rr. */
25 static int nsec3_rrset_params_ok(rr_type* base, rrset_type* rrset);
26 
27 static void
28 detect_nsec3_params(rr_type* nsec3_apex,
29 	const unsigned char** salt, int* salt_len, int* iter)
30 {
31 	/* always uses first NSEC3 record with SOA bit set */
32 	assert(salt && salt_len && iter);
33 	assert(nsec3_apex);
34 	*salt_len = rdata_atom_data(nsec3_apex->rdatas[3])[0];
35 	*salt = (unsigned char*)(rdata_atom_data(nsec3_apex->rdatas[3])+1);
36 	*iter = read_uint16(rdata_atom_data(nsec3_apex->rdatas[2]));
37 }
38 
39 static const dname_type *
40 nsec3_hash_dname_param(region_type *region, zone_type *zone,
41 	const dname_type *dname, rr_type* param_rr)
42 {
43 	unsigned char hash[SHA_DIGEST_LENGTH];
44 	char b32[SHA_DIGEST_LENGTH*2+1];
45 	const unsigned char* nsec3_salt = NULL;
46 	int nsec3_saltlength = 0;
47 	int nsec3_iterations = 0;
48 
49 	detect_nsec3_params(param_rr, &nsec3_salt,
50 		&nsec3_saltlength, &nsec3_iterations);
51 	iterated_hash(hash, nsec3_salt, nsec3_saltlength, dname_name(dname),
52 		dname->name_size, nsec3_iterations);
53 	b32_ntop(hash, sizeof(hash), b32, sizeof(b32));
54 	dname=dname_parse(region, b32);
55 	dname=dname_concatenate(region, dname, domain_dname(zone->apex));
56 	return dname;
57 }
58 
59 const dname_type *
60 nsec3_hash_dname(region_type *region, zone_type *zone,
61 	const dname_type *dname)
62 {
63 	return nsec3_hash_dname_param(region, zone,
64 		dname, zone->nsec3_soa_rr);
65 }
66 
67 static int
68 nsec3_has_soa(rr_type* rr)
69 {
70 	if(rdata_atom_size(rr->rdatas[5]) >= 3 && /* has types in bitmap */
71 		rdata_atom_data(rr->rdatas[5])[0] == 0 && /* first window = 0, */
72 						/* [1]: windowlen must be >= 1 */
73 		rdata_atom_data(rr->rdatas[5])[2]&0x02)  /* SOA bit set */
74 		return 1;
75 	return 0;
76 }
77 
78 static rr_type*
79 find_zone_nsec3(namedb_type* namedb, zone_type *zone)
80 {
81 	size_t i;
82 	domain_type* domain;
83 	region_type* tmpregion;
84 	/* Check settings in NSEC3PARAM.
85 	   Hash algorithm must be OK. And a NSEC3 with soa bit
86 	   must map to the zone apex.  */
87 	rrset_type* paramset = domain_find_rrset(zone->apex, zone, TYPE_NSEC3PARAM);
88 	if(!paramset || !paramset->rrs || !paramset->rr_count)
89 		return 0;
90 	tmpregion = region_create(xalloc, free);
91 	for(i=0; i<paramset->rr_count; i++)
92 	{
93 		rr_type* rr = &paramset->rrs[i];
94 		const dname_type* hashed_apex;
95 		rrset_type* nsec3_rrset;
96 		size_t j;
97 
98 		if(rdata_atom_data(rr->rdatas[0])[0] != NSEC3_SHA1_HASH) {
99 			log_msg(LOG_ERR, "%s NSEC3PARAM entry %d has unknown hash algo %d",
100 				dname_to_string(domain_dname(zone->apex), NULL), (int)i,
101 				rdata_atom_data(rr->rdatas[0])[0]);
102 			continue;
103 		}
104 		if(rdata_atom_data(rr->rdatas[1])[0] != 0) {
105 			/* draft-nsec3-09: NSEC3PARAM records with flags
106 			   field value other than zero MUST be ignored. */
107 			continue;
108 		}
109 		/* check hash of apex -> NSEC3 with soa bit on */
110 		hashed_apex = nsec3_hash_dname_param(tmpregion,
111 			zone, domain_dname(zone->apex), &paramset->rrs[i]);
112 		domain = domain_table_find(namedb->domains, hashed_apex);
113 		if(!domain) {
114 			log_msg(LOG_ERR, "%s NSEC3PARAM entry %d has no hash(apex).",
115 				dname_to_string(domain_dname(zone->apex), NULL), (int)i);
116 			log_msg(LOG_ERR, "hash(apex)= %s",
117 				dname_to_string(hashed_apex, NULL));
118 			continue;
119 		}
120 		nsec3_rrset = domain_find_rrset(domain, zone, TYPE_NSEC3);
121 		if(!nsec3_rrset) {
122 			log_msg(LOG_ERR, "%s NSEC3PARAM entry %d: hash(apex) has no NSEC3 RRset",
123 				dname_to_string(domain_dname(zone->apex), NULL), (int)i);
124 			continue;
125 		}
126 		/* find SOA bit enabled nsec3, with the same settings */
127 		for(j=0; j<nsec3_rrset->rr_count; j++)
128 		{
129 			const unsigned char *salt1, *salt2;
130 			int saltlen1, saltlen2, iter1, iter2;
131 			if(!nsec3_has_soa(&nsec3_rrset->rrs[j]))
132 				continue;
133 			/* check params OK. Ignores the optout bit. */
134 			detect_nsec3_params(rr, &salt1, &saltlen1, &iter1);
135 			detect_nsec3_params(&nsec3_rrset->rrs[j],
136 				&salt2, &saltlen2, &iter2);
137 			if(saltlen1 == saltlen2 && iter1 == iter2 &&
138 				rdata_atom_data(rr->rdatas[0])[0] == /* algo */
139 					rdata_atom_data(nsec3_rrset->rrs[j].rdatas[0])[0]
140 				&& memcmp(salt1, salt2, saltlen1) == 0) {
141 				/* found it */
142 				DEBUG(DEBUG_QUERY, 1, (LOG_INFO,
143 					"detected NSEC3 for zone %s saltlen=%d iter=%d",
144 					dname_to_string(domain_dname(
145 					zone->apex),0), saltlen2, iter2));
146 				region_destroy(tmpregion);
147 				return &nsec3_rrset->rrs[j];
148 			}
149 		}
150 		log_msg(LOG_ERR, "%s NSEC3PARAM entry %d: hash(apex) no NSEC3 with SOAbit",
151 			dname_to_string(domain_dname(zone->apex), NULL), (int)i);
152 	}
153 	region_destroy(tmpregion);
154 	return 0;
155 }
156 
157 /* check that the rrset has an NSEC3 that uses the same parameters as the
158    zone is using. Pass NSEC3 rrset, and zone must have nsec3_rrset set.
159    if you pass NULL then 0 is returned. */
160 static int
161 nsec3_rrset_params_ok(rr_type* base, rrset_type* rrset)
162 {
163 	rdata_atom_type* prd;
164 	rdata_atom_type* rd;
165 	size_t i;
166 	if(!rrset)
167 		return 0; /* without rrset, no matching params either */
168 	assert(rrset && rrset->zone && (base || rrset->zone->nsec3_soa_rr));
169 	if(!base)
170 		base = rrset->zone->nsec3_soa_rr;
171 	prd = base->rdatas;
172 	for(i=0; i<rrset->rr_count; ++i)
173 	{
174 		rd = rrset->rrs[i].rdatas;
175 		assert(rrset->rrs[i].type == TYPE_NSEC3);
176 		if(rdata_atom_data(rd[0])[0] ==
177 			rdata_atom_data(prd[0])[0] && /* hash algo */
178 		   rdata_atom_data(rd[2])[0] ==
179 			rdata_atom_data(prd[2])[0] && /* iterations 0 */
180 		   rdata_atom_data(rd[2])[1] ==
181 			rdata_atom_data(prd[2])[1] && /* iterations 1 */
182 		   rdata_atom_data(rd[3])[0] ==
183 			rdata_atom_data(prd[3])[0] && /* salt length */
184 		   memcmp(rdata_atom_data(rd[3])+1,
185 			rdata_atom_data(prd[3])+1, rdata_atom_data(rd[3])[0])
186 			== 0 )
187 		{
188 			/* this NSEC3 matches nsec3 parameters from zone */
189 			return 1;
190 		}
191 	}
192 	return 0;
193 }
194 
195 int
196 nsec3_find_cover(namedb_type* db, zone_type* zone,
197 	const dname_type* hashname, domain_type** result)
198 {
199 	rrset_type *rrset;
200 	domain_type *walk;
201 	domain_type *closest_match;
202 	domain_type *closest_encloser;
203 	int exact;
204 
205 	assert(result);
206 	assert(zone->nsec3_soa_rr);
207 
208 	exact = domain_table_search(
209 		db->domains, hashname, &closest_match, &closest_encloser);
210 	/* exact match of hashed domain name + it has an NSEC3? */
211 	if(exact &&
212 	   nsec3_rrset_params_ok(NULL,
213 	   	domain_find_rrset(closest_encloser, zone, TYPE_NSEC3))) {
214 		*result = closest_encloser;
215 		assert(*result != 0);
216 		return 1;
217 	}
218 
219 	/* find covering NSEC3 record, lexicographically before the closest match */
220 	/* use nsec3_lookup to jumpstart the search */
221 	walk = closest_match->nsec3_lookup;
222 	rrset = 0;
223 	while(walk && dname_is_subdomain(domain_dname(walk), domain_dname(zone->apex)))
224 	{
225 		if(nsec3_rrset_params_ok(NULL,
226 			domain_find_rrset(walk, zone, TYPE_NSEC3))) {
227 			/* this rrset is OK NSEC3, exit while */
228 			rrset = domain_find_rrset(walk, zone, TYPE_NSEC3);
229 			break;
230 		}
231 		walk = domain_previous(walk);
232 	}
233 	if(rrset)
234 		*result = walk;
235 	else 	{
236 		/*
237 		 * There are no NSEC3s before the closest match.
238 		 * so the hash name is before the first NSEC3 record in the zone.
239 		 * use last NSEC3, which covers the wraparound in hash space
240 		 *
241 		 * Since the zone has an NSEC3 with the SOA bit set for NSEC3 to turn on,
242 		 * there is also a last nsec3, so find_cover always assigns *result!=0.
243 		 */
244 		*result = zone->nsec3_last;
245 	}
246 	assert(*result != 0);
247 	return 0;
248 }
249 
250 static void
251 prehash_domain(namedb_type* db, zone_type* zone,
252 	domain_type* domain, region_type* region)
253 {
254 	/* find it */
255 	domain_type* result = 0;
256 	const dname_type *wcard, *wcard_child, *hashname;
257 	int exact;
258 
259 	if(!zone->nsec3_soa_rr)
260 	{
261 		/* set to 0 (in case NSEC3 removed after an update) */
262 		domain->nsec3_is_exact = 0;
263 		domain->nsec3_cover = NULL;
264 		domain->nsec3_wcard_child_cover = NULL;
265 		return;
266 	}
267 
268 	hashname = nsec3_hash_dname(region, zone, domain_dname(domain));
269 	exact = nsec3_find_cover(db, zone, hashname, &result);
270 	domain->nsec3_cover = result;
271 	if(exact)
272 		domain->nsec3_is_exact = 1;
273 	else	domain->nsec3_is_exact = 0;
274 
275 	/* find cover for *.domain for wildcard denial */
276 	wcard = dname_parse(region, "*");
277 	wcard_child = dname_concatenate(region, wcard, domain_dname(domain));
278 	hashname = nsec3_hash_dname(region, zone, wcard_child);
279 	exact = nsec3_find_cover(db, zone, hashname, &result);
280 	domain->nsec3_wcard_child_cover = result;
281 
282 	if(exact && !domain_wildcard_child(domain))
283 	{
284 		/* We found an exact match for the *.domain NSEC3 hash,
285 		 * but the domain wildcard child (*.domain) does not exist.
286 		 * Thus there is a hash collision. It will cause servfail
287 		 * for NXdomain queries below this domain.
288 		 */
289 		log_msg(LOG_WARNING, "prehash: collision of wildcard "
290 			"denial for %s. Sign zone with different salt "
291 			"to remove collision.",
292 			dname_to_string(domain_dname(domain),0));
293 	}
294 }
295 
296 static void
297 prehash_ds(namedb_type* db, zone_type* zone,
298 	domain_type* domain, region_type* region)
299 {
300 	domain_type* result = 0;
301 	const dname_type* hashname;
302 	int exact;
303 
304 	if(!zone->nsec3_soa_rr) {
305 		domain->nsec3_ds_parent_is_exact = 0;
306 		domain->nsec3_ds_parent_cover = NULL;
307 		return;
308 	}
309 
310 	/* hash again, other zone could have different hash parameters */
311 	hashname = nsec3_hash_dname(region, zone, domain_dname(domain));
312 	exact = nsec3_find_cover(db, zone, hashname, &result);
313 	if(exact)
314 		domain->nsec3_ds_parent_is_exact = 1;
315 	else 	domain->nsec3_ds_parent_is_exact = 0;
316 	domain->nsec3_ds_parent_cover = result;
317 }
318 
319 static void
320 prehash_zone(struct namedb* db, struct zone* zone)
321 {
322 	domain_type *walk;
323 	domain_type *last_nsec3_node;
324 	region_type *temp_region;
325 	assert(db && zone);
326 
327 	/* find zone settings */
328 	zone->nsec3_soa_rr = find_zone_nsec3(db, zone);
329 	if(!zone->nsec3_soa_rr) {
330 		zone->nsec3_last = 0;
331 		return;
332 	}
333 
334 	temp_region = region_create(xalloc, free);
335 
336 	/* go through entire zone and setup nsec3_lookup speedup */
337 	walk = zone->apex;
338 	last_nsec3_node = NULL;
339 	/* since we walk in sorted order, we pass all NSEC3s in sorted
340 	   order and we can set the lookup ptrs */
341 	while(walk && dname_is_subdomain(
342 		domain_dname(walk), domain_dname(zone->apex)))
343 	{
344 		zone_type* z = domain_find_zone(walk);
345 		if(z && z==zone)
346 		{
347 			if(domain_find_rrset(walk, zone, TYPE_NSEC3))
348 				last_nsec3_node = walk;
349 			walk->nsec3_lookup = last_nsec3_node;
350 		}
351 		walk = domain_next(walk);
352 	}
353 	zone->nsec3_last = last_nsec3_node;
354 
355 	/* go through entire zone */
356 	walk = zone->apex;
357 	while(walk && dname_is_subdomain(
358 		domain_dname(walk), domain_dname(zone->apex)))
359 	{
360 		zone_type* z;
361 		if(!walk->is_existing || domain_has_only_NSEC3(walk, zone)) {
362 			walk->nsec3_cover = NULL;
363 			walk->nsec3_wcard_child_cover = NULL;
364 			walk = domain_next(walk);
365 			continue;
366 		}
367 		z = domain_find_zone(walk);
368 		if(z && z==zone && !domain_is_glue(walk, zone))
369 		{
370 			prehash_domain(db, zone, walk, temp_region);
371 			region_free_all(temp_region);
372 		}
373 		/* prehash the DS (parent zone) */
374 		if(domain_find_rrset(walk, zone, TYPE_DS) ||
375 			(domain_find_rrset(walk, zone, TYPE_NS) &&
376 			 walk != zone->apex))
377 		{
378 			assert(walk != zone->apex /* DS must be above zone cut */);
379 			prehash_ds(db, zone, walk, temp_region);
380 			region_free_all(temp_region);
381 		}
382 		walk = domain_next(walk);
383 	}
384 	region_destroy(temp_region);
385 }
386 
387 void
388 prehash(struct namedb* db, int updated_only)
389 {
390 	zone_type *z;
391 	time_t end, start = time(NULL);
392 	int count = 0;
393 	for(z = db->zones; z; z = z->next)
394 	{
395 		if(!updated_only || z->updated) {
396 			prehash_zone(db, z);
397 			if(z->nsec3_soa_rr)
398 				count++;
399 		}
400 	}
401 	end = time(NULL);
402 	if(count > 0)
403 		VERBOSITY(1, (LOG_INFO, "nsec3-prepare took %d "
404 		"seconds for %d zones.", (int)(end-start), count));
405 }
406 
407 /* add the NSEC3 rrset to the query answer at the given domain */
408 static void
409 nsec3_add_rrset(struct query *query, struct answer *answer,
410 	rr_section_type section, struct domain* domain)
411 {
412 	if(domain) {
413 		rrset_type* rrset = domain_find_rrset(domain, query->zone, TYPE_NSEC3);
414 		if(rrset)
415 			answer_add_rrset(answer, section, domain, rrset);
416 	}
417 }
418 
419 /* this routine does hashing at query-time. slow. */
420 static void
421 nsec3_add_nonexist_proof(struct query *query, struct answer *answer,
422         struct domain *encloser, struct namedb* db, const dname_type* qname)
423 {
424 	const dname_type *to_prove, *hashed;
425 	domain_type *cover=0;
426 	assert(encloser);
427 	/* if query=a.b.c.d encloser=c.d. then proof needed for b.c.d. */
428 	/* if query=a.b.c.d encloser=*.c.d. then proof needed for b.c.d. */
429 	to_prove = dname_partial_copy(query->region, qname,
430 		dname_label_match_count(qname, domain_dname(encloser))+1);
431 	/* generate proof that one label below closest encloser does not exist */
432 	hashed = nsec3_hash_dname(query->region, query->zone, to_prove);
433 	if(nsec3_find_cover(db, query->zone, hashed, &cover))
434 	{
435 		/* exact match, hash collision */
436 		/* the hashed name of the query corresponds to an existing name. */
437 		log_msg(LOG_ERR, "nsec3 hash collision for name=%s",
438 			dname_to_string(to_prove, NULL));
439 		RCODE_SET(query->packet, RCODE_SERVFAIL);
440 		return;
441 	}
442 	else
443 	{
444 		/* cover proves the qname does not exist */
445 		nsec3_add_rrset(query, answer, AUTHORITY_SECTION, cover);
446 	}
447 }
448 
449 static void
450 nsec3_add_closest_encloser_proof(
451 	struct query *query, struct answer *answer,
452 	struct domain *closest_encloser, struct namedb* db,
453 	const dname_type* qname)
454 {
455 	if(!closest_encloser)
456 		return;
457 	/* prove that below closest encloser nothing exists */
458 	nsec3_add_nonexist_proof(query, answer, closest_encloser, db, qname);
459 	/* proof that closest encloser exists */
460 	if(closest_encloser->nsec3_is_exact)
461 		nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
462 			closest_encloser->nsec3_cover);
463 }
464 
465 void
466 nsec3_answer_wildcard(struct query *query, struct answer *answer,
467         struct domain *wildcard, struct namedb* db, const dname_type* qname)
468 {
469 	if(!wildcard)
470 		return;
471 	if(!query->zone->nsec3_soa_rr)
472 		return;
473 	nsec3_add_nonexist_proof(query, answer, wildcard, db, qname);
474 }
475 
476 static void
477 nsec3_add_ds_proof(struct query *query, struct answer *answer,
478 	struct domain *domain, int delegpt)
479 {
480 	/* assert we are above the zone cut */
481 	assert(domain != query->zone->apex);
482 	if(domain->nsec3_ds_parent_is_exact) {
483 		/* use NSEC3 record from above the zone cut. */
484 		nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
485 			domain->nsec3_ds_parent_cover);
486 	} else if (!delegpt && domain->nsec3_is_exact) {
487 		nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
488 			domain->nsec3_cover);
489 	} else {
490 		/* prove closest provable encloser */
491 		domain_type* par = domain->parent;
492 		domain_type* prev_par = 0;
493 
494 		while(par && !par->nsec3_is_exact)
495 		{
496 			prev_par = par;
497 			par = par->parent;
498 		}
499 		assert(par); /* parent zone apex must be provable, thus this ends */
500 		nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
501 			par->nsec3_cover);
502 		/* we took several steps to go to the provable parent, so
503 		   the one below it has no exact nsec3, disprove it.
504 		   disprove is easy, it has a prehashed cover ptr. */
505 		if(prev_par) {
506 			assert(prev_par != domain && !prev_par->nsec3_is_exact);
507 			nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
508 				prev_par->nsec3_cover);
509 		}
510 		/* add optout range from parent zone */
511 		/* note: no check of optout bit, resolver checks it */
512 		nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
513 			domain->nsec3_ds_parent_cover);
514 	}
515 }
516 
517 void
518 nsec3_answer_nodata(struct query *query, struct answer *answer,
519 	struct domain *original)
520 {
521 	if(!query->zone->nsec3_soa_rr)
522 		return;
523 	/* nodata when asking for secure delegation */
524 	if(query->qtype == TYPE_DS)
525 	{
526 		if(original == query->zone->apex) {
527 			/* DS at zone apex, but server not authoritative for parent zone */
528 			/* so answer at the child zone level */
529 			if(original->nsec3_is_exact)
530 				nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
531 					original->nsec3_cover);
532 			return;
533 		}
534 		/* query->zone must be the parent zone */
535 		nsec3_add_ds_proof(query, answer, original, 0);
536 	}
537 	/* the nodata is result from a wildcard match */
538 	else if (original==original->wildcard_child_closest_match
539 		&& label_is_wildcard(dname_name(domain_dname(original)))) {
540 		/* denial for wildcard is already there */
541 		/* add parent proof to have a closest encloser proof for wildcard parent */
542 		if(original->parent && original->parent->nsec3_is_exact)
543 			nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
544 				original->parent->nsec3_cover);
545 		/* proof for wildcard itself */
546 		nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
547 			original->nsec3_cover);
548 	}
549 	else {	/* add nsec3 to prove rrset does not exist */
550 		if(original->nsec3_is_exact)
551 			nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
552 				original->nsec3_cover);
553 	}
554 }
555 
556 void
557 nsec3_answer_delegation(struct query *query, struct answer *answer)
558 {
559 	if(!query->zone->nsec3_soa_rr)
560 		return;
561 	nsec3_add_ds_proof(query, answer, query->delegation_domain, 1);
562 }
563 
564 int
565 domain_has_only_NSEC3(struct domain* domain, struct zone* zone)
566 {
567 	/* check for only NSEC3/RRSIG */
568 	rrset_type* rrset = domain->rrsets;
569 	int nsec3_seen = 0, rrsig_seen = 0;
570 	while(rrset)
571 	{
572 		if(!zone || rrset->zone == zone)
573 		{
574 			if(rrset->rrs[0].type == TYPE_NSEC3)
575 				nsec3_seen = 1;
576 			else if(rrset->rrs[0].type == TYPE_RRSIG)
577 				rrsig_seen = 1;
578 			else
579 				return 0;
580 		}
581 		rrset = rrset->next;
582 	}
583 	return nsec3_seen;
584 }
585 
586 void
587 nsec3_answer_authoritative(struct domain** match, struct query *query,
588 	struct answer *answer, struct domain* closest_encloser,
589 	struct namedb* db, const dname_type* qname)
590 {
591 	if(!query->zone->nsec3_soa_rr)
592 		return;
593 	assert(match);
594 	/* there is a match, this has 1 RRset, which is NSEC3, but qtype is not. */
595 	if(*match &&
596 #if 0
597 		query->qtype != TYPE_NSEC3 &&
598 #endif
599 		domain_has_only_NSEC3(*match, query->zone))
600 	{
601 		/* act as if the NSEC3 domain did not exist, name error */
602 		*match = 0;
603 		/* all nsec3s are directly below the apex, that is closest encloser */
604 		if(query->zone->apex->nsec3_is_exact)
605 			nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
606 				query->zone->apex->nsec3_cover);
607 		/* disprove the nsec3 record. */
608 		nsec3_add_rrset(query, answer, AUTHORITY_SECTION, closest_encloser->nsec3_cover);
609 		/* disprove a wildcard */
610 		nsec3_add_rrset(query, answer, AUTHORITY_SECTION, query->zone->apex->
611 			nsec3_wcard_child_cover);
612 		if (domain_wildcard_child(query->zone->apex)) {
613 			/* wildcard exists below the domain */
614 			/* wildcard and nsec3 domain clash. server failure. */
615 			RCODE_SET(query->packet, RCODE_SERVFAIL);
616 		}
617 		return;
618 	}
619 	if(!*match) {
620 		/* name error, domain does not exist */
621 		nsec3_add_closest_encloser_proof(query, answer, closest_encloser,
622 			db, qname);
623 		nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
624 			closest_encloser->nsec3_wcard_child_cover);
625 	}
626 }
627 
628 #endif /* NSEC3 */
629