1238106Sdes/*
2238106Sdes * validator/val_nsec.c - validator NSEC denial of existance functions.
3238106Sdes *
4238106Sdes * Copyright (c) 2007, NLnet Labs. All rights reserved.
5238106Sdes *
6238106Sdes * This software is open source.
7238106Sdes *
8238106Sdes * Redistribution and use in source and binary forms, with or without
9238106Sdes * modification, are permitted provided that the following conditions
10238106Sdes * are met:
11238106Sdes *
12238106Sdes * Redistributions of source code must retain the above copyright notice,
13238106Sdes * this list of conditions and the following disclaimer.
14238106Sdes *
15238106Sdes * Redistributions in binary form must reproduce the above copyright notice,
16238106Sdes * this list of conditions and the following disclaimer in the documentation
17238106Sdes * and/or other materials provided with the distribution.
18238106Sdes *
19238106Sdes * Neither the name of the NLNET LABS nor the names of its contributors may
20238106Sdes * be used to endorse or promote products derived from this software without
21238106Sdes * specific prior written permission.
22238106Sdes *
23238106Sdes * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24269257Sdes * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25269257Sdes * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26269257Sdes * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27269257Sdes * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28269257Sdes * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29269257Sdes * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30269257Sdes * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31269257Sdes * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32269257Sdes * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33269257Sdes * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34238106Sdes */
35238106Sdes
36238106Sdes/**
37238106Sdes * \file
38238106Sdes *
39238106Sdes * This file contains helper functions for the validator module.
40238106Sdes * The functions help with NSEC checking, the different NSEC proofs
41238106Sdes * for denial of existance, and proofs for presence of types.
42238106Sdes */
43238106Sdes#include "config.h"
44238106Sdes#include "validator/val_nsec.h"
45238106Sdes#include "validator/val_utils.h"
46238106Sdes#include "util/data/msgreply.h"
47238106Sdes#include "util/data/dname.h"
48238106Sdes#include "util/net_help.h"
49238106Sdes#include "util/module.h"
50238106Sdes#include "services/cache/rrset.h"
51238106Sdes
52238106Sdes/** get ttl of rrset */
53238106Sdesstatic uint32_t
54238106Sdesrrset_get_ttl(struct ub_packed_rrset_key* k)
55238106Sdes{
56238106Sdes	struct packed_rrset_data* d = (struct packed_rrset_data*)k->entry.data;
57238106Sdes	return d->ttl;
58238106Sdes}
59238106Sdes
60238106Sdesint
61238106Sdesnsecbitmap_has_type_rdata(uint8_t* bitmap, size_t len, uint16_t type)
62238106Sdes{
63238106Sdes	/* Check type present in NSEC typemap with bitmap arg */
64238106Sdes	/* bitmasks for determining type-lowerbits presence */
65238106Sdes	uint8_t masks[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
66238106Sdes	uint8_t type_window = type>>8;
67238106Sdes	uint8_t type_low = type&0xff;
68238106Sdes	uint8_t win, winlen;
69238106Sdes	/* read each of the type bitmap windows and see if the searched
70238106Sdes	 * type is amongst it */
71238106Sdes	while(len > 0) {
72238106Sdes		if(len < 3) /* bad window, at least window# winlen bitmap */
73238106Sdes			return 0;
74238106Sdes		win = *bitmap++;
75238106Sdes		winlen = *bitmap++;
76238106Sdes		len -= 2;
77238106Sdes		if(len < winlen || winlen < 1 || winlen > 32)
78238106Sdes			return 0;	/* bad window length */
79238106Sdes		if(win == type_window) {
80238106Sdes			/* search window bitmap for the correct byte */
81238106Sdes			/* mybyte is 0 if we need the first byte */
82238106Sdes			size_t mybyte = type_low>>3;
83238106Sdes			if(winlen <= mybyte)
84238106Sdes				return 0; /* window too short */
85238106Sdes			return (int)(bitmap[mybyte] & masks[type_low&0x7]);
86238106Sdes		} else {
87238106Sdes			/* not the window we are looking for */
88238106Sdes			bitmap += winlen;
89238106Sdes			len -= winlen;
90238106Sdes		}
91238106Sdes	}
92238106Sdes	/* end of bitmap reached, no type found */
93238106Sdes	return 0;
94238106Sdes}
95238106Sdes
96238106Sdesint
97238106Sdesnsec_has_type(struct ub_packed_rrset_key* nsec, uint16_t type)
98238106Sdes{
99238106Sdes	struct packed_rrset_data* d = (struct packed_rrset_data*)nsec->
100238106Sdes		entry.data;
101238106Sdes	size_t len;
102238106Sdes	if(!d || d->count == 0 || d->rr_len[0] < 2+1)
103238106Sdes		return 0;
104238106Sdes	len = dname_valid(d->rr_data[0]+2, d->rr_len[0]-2);
105238106Sdes	if(!len)
106238106Sdes		return 0;
107238106Sdes	return nsecbitmap_has_type_rdata(d->rr_data[0]+2+len,
108238106Sdes		d->rr_len[0]-2-len, type);
109238106Sdes}
110238106Sdes
111238106Sdes/**
112238106Sdes * Get next owner name from nsec record
113238106Sdes * @param nsec: the nsec RRset.
114238106Sdes *	If there are multiple RRs, then this will only return one of them.
115238106Sdes * @param nm: the next name is returned.
116238106Sdes * @param ln: length of nm is returned.
117238106Sdes * @return false on a bad NSEC RR (too short, malformed dname).
118238106Sdes */
119238106Sdesstatic int
120238106Sdesnsec_get_next(struct ub_packed_rrset_key* nsec, uint8_t** nm, size_t* ln)
121238106Sdes{
122238106Sdes	struct packed_rrset_data* d = (struct packed_rrset_data*)nsec->
123238106Sdes		entry.data;
124238106Sdes	if(!d || d->count == 0 || d->rr_len[0] < 2+1) {
125238106Sdes		*nm = 0;
126238106Sdes		*ln = 0;
127238106Sdes		return 0;
128238106Sdes	}
129238106Sdes	*nm = d->rr_data[0]+2;
130238106Sdes	*ln = dname_valid(*nm, d->rr_len[0]-2);
131238106Sdes	if(!*ln) {
132238106Sdes		*nm = 0;
133238106Sdes		*ln = 0;
134238106Sdes		return 0;
135238106Sdes	}
136238106Sdes	return 1;
137238106Sdes}
138238106Sdes
139238106Sdes/**
140238106Sdes * For an NSEC that matches the DS queried for, check absence of DS type.
141238106Sdes *
142238106Sdes * @param nsec: NSEC for proof, must be trusted.
143238106Sdes * @param qinfo: what is queried for.
144238106Sdes * @return if secure the nsec proves that no DS is present, or
145238106Sdes *	insecure if it proves it is not a delegation point.
146238106Sdes *	or bogus if something was wrong.
147238106Sdes */
148238106Sdesstatic enum sec_status
149238106Sdesval_nsec_proves_no_ds(struct ub_packed_rrset_key* nsec,
150238106Sdes	struct query_info* qinfo)
151238106Sdes{
152238106Sdes	log_assert(qinfo->qtype == LDNS_RR_TYPE_DS);
153238106Sdes	log_assert(ntohs(nsec->rk.type) == LDNS_RR_TYPE_NSEC);
154238106Sdes
155238106Sdes	if(nsec_has_type(nsec, LDNS_RR_TYPE_SOA) && qinfo->qname_len != 1) {
156238106Sdes		/* SOA present means that this is the NSEC from the child,
157238106Sdes		 * not the parent (so it is the wrong one). */
158238106Sdes		return sec_status_bogus;
159238106Sdes	}
160238106Sdes	if(nsec_has_type(nsec, LDNS_RR_TYPE_DS)) {
161238106Sdes		/* DS present means that there should have been a positive
162238106Sdes		 * response to the DS query, so there is something wrong. */
163238106Sdes		return sec_status_bogus;
164238106Sdes	}
165238106Sdes
166238106Sdes	if(!nsec_has_type(nsec, LDNS_RR_TYPE_NS)) {
167238106Sdes		/* If there is no NS at this point at all, then this
168238106Sdes		 * doesn't prove anything one way or the other. */
169238106Sdes		return sec_status_insecure;
170238106Sdes	}
171238106Sdes	/* Otherwise, this proves no DS. */
172238106Sdes	return sec_status_secure;
173238106Sdes}
174238106Sdes
175238106Sdes/** check security status from cache or verify rrset, returns true if secure */
176238106Sdesstatic int
177238106Sdesnsec_verify_rrset(struct module_env* env, struct val_env* ve,
178238106Sdes	struct ub_packed_rrset_key* nsec, struct key_entry_key* kkey,
179238106Sdes	char** reason)
180238106Sdes{
181238106Sdes	struct packed_rrset_data* d = (struct packed_rrset_data*)
182238106Sdes		nsec->entry.data;
183238106Sdes	if(d->security == sec_status_secure)
184238106Sdes		return 1;
185238106Sdes	rrset_check_sec_status(env->rrset_cache, nsec, *env->now);
186238106Sdes	if(d->security == sec_status_secure)
187238106Sdes		return 1;
188238106Sdes	d->security = val_verify_rrset_entry(env, ve, nsec, kkey, reason);
189238106Sdes	if(d->security == sec_status_secure) {
190238106Sdes		rrset_update_sec_status(env->rrset_cache, nsec, *env->now);
191238106Sdes		return 1;
192238106Sdes	}
193238106Sdes	return 0;
194238106Sdes}
195238106Sdes
196238106Sdesenum sec_status
197238106Sdesval_nsec_prove_nodata_dsreply(struct module_env* env, struct val_env* ve,
198238106Sdes	struct query_info* qinfo, struct reply_info* rep,
199269257Sdes	struct key_entry_key* kkey, time_t* proof_ttl, char** reason)
200238106Sdes{
201238106Sdes	struct ub_packed_rrset_key* nsec = reply_find_rrset_section_ns(
202238106Sdes		rep, qinfo->qname, qinfo->qname_len, LDNS_RR_TYPE_NSEC,
203238106Sdes		qinfo->qclass);
204238106Sdes	enum sec_status sec;
205238106Sdes	size_t i;
206238106Sdes	uint8_t* wc = NULL, *ce = NULL;
207238106Sdes	int valid_nsec = 0;
208238106Sdes	struct ub_packed_rrset_key* wc_nsec = NULL;
209238106Sdes
210238106Sdes	/* If we have a NSEC at the same name, it must prove one
211238106Sdes	 * of two things
212238106Sdes	 * --
213238106Sdes	 * 1) this is a delegation point and there is no DS
214238106Sdes	 * 2) this is not a delegation point */
215238106Sdes	if(nsec) {
216238106Sdes		if(!nsec_verify_rrset(env, ve, nsec, kkey, reason)) {
217238106Sdes			verbose(VERB_ALGO, "NSEC RRset for the "
218238106Sdes				"referral did not verify.");
219238106Sdes			return sec_status_bogus;
220238106Sdes		}
221238106Sdes		sec = val_nsec_proves_no_ds(nsec, qinfo);
222238106Sdes		if(sec == sec_status_bogus) {
223238106Sdes			/* something was wrong. */
224238106Sdes			*reason = "NSEC does not prove absence of DS";
225238106Sdes			return sec;
226238106Sdes		} else if(sec == sec_status_insecure) {
227238106Sdes			/* this wasn't a delegation point. */
228238106Sdes			return sec;
229238106Sdes		} else if(sec == sec_status_secure) {
230238106Sdes			/* this proved no DS. */
231238106Sdes			*proof_ttl = ub_packed_rrset_ttl(nsec);
232238106Sdes			return sec;
233238106Sdes		}
234238106Sdes		/* if unchecked, fall through to next proof */
235238106Sdes	}
236238106Sdes
237238106Sdes	/* Otherwise, there is no NSEC at qname. This could be an ENT.
238238106Sdes	 * (ENT=empty non terminal). If not, this is broken. */
239238106Sdes
240238106Sdes	/* verify NSEC rrsets in auth section */
241238106Sdes	for(i=rep->an_numrrsets; i < rep->an_numrrsets+rep->ns_numrrsets;
242238106Sdes		i++) {
243238106Sdes		if(rep->rrsets[i]->rk.type != htons(LDNS_RR_TYPE_NSEC))
244238106Sdes			continue;
245238106Sdes		if(!nsec_verify_rrset(env, ve, rep->rrsets[i], kkey, reason)) {
246238106Sdes			verbose(VERB_ALGO, "NSEC for empty non-terminal "
247238106Sdes				"did not verify.");
248238106Sdes			return sec_status_bogus;
249238106Sdes		}
250238106Sdes		if(nsec_proves_nodata(rep->rrsets[i], qinfo, &wc)) {
251238106Sdes			verbose(VERB_ALGO, "NSEC for empty non-terminal "
252238106Sdes				"proved no DS.");
253238106Sdes			*proof_ttl = rrset_get_ttl(rep->rrsets[i]);
254238106Sdes			if(wc && dname_is_wild(rep->rrsets[i]->rk.dname))
255238106Sdes				wc_nsec = rep->rrsets[i];
256238106Sdes			valid_nsec = 1;
257238106Sdes		}
258238106Sdes		if(val_nsec_proves_name_error(rep->rrsets[i], qinfo->qname)) {
259238106Sdes			ce = nsec_closest_encloser(qinfo->qname,
260238106Sdes				rep->rrsets[i]);
261238106Sdes		}
262238106Sdes	}
263238106Sdes	if(wc && !ce)
264238106Sdes		valid_nsec = 0;
265238106Sdes	else if(wc && ce) {
266238106Sdes		/* ce and wc must match */
267238106Sdes		if(query_dname_compare(wc, ce) != 0)
268238106Sdes			valid_nsec = 0;
269238106Sdes		else if(!wc_nsec)
270238106Sdes			valid_nsec = 0;
271238106Sdes	}
272238106Sdes	if(valid_nsec) {
273238106Sdes		if(wc) {
274238106Sdes			/* check if this is a delegation */
275238106Sdes			*reason = "NSEC for wildcard does not prove absence of DS";
276238106Sdes			return val_nsec_proves_no_ds(wc_nsec, qinfo);
277238106Sdes		}
278238106Sdes		/* valid nsec proves empty nonterminal */
279238106Sdes		return sec_status_insecure;
280238106Sdes	}
281238106Sdes
282238106Sdes	/* NSEC proof did not conlusively point to DS or no DS */
283238106Sdes	return sec_status_unchecked;
284238106Sdes}
285238106Sdes
286238106Sdesint nsec_proves_nodata(struct ub_packed_rrset_key* nsec,
287238106Sdes	struct query_info* qinfo, uint8_t** wc)
288238106Sdes{
289238106Sdes	log_assert(wc);
290238106Sdes	if(query_dname_compare(nsec->rk.dname, qinfo->qname) != 0) {
291238106Sdes		uint8_t* nm;
292238106Sdes		size_t ln;
293238106Sdes
294238106Sdes		/* empty-non-terminal checking.
295238106Sdes		 * Done before wildcard, because this is an exact match,
296238106Sdes		 * and would prevent a wildcard from matching. */
297238106Sdes
298238106Sdes		/* If the nsec is proving that qname is an ENT, the nsec owner
299238106Sdes		 * will be less than qname, and the next name will be a child
300238106Sdes		 * domain of the qname. */
301238106Sdes		if(!nsec_get_next(nsec, &nm, &ln))
302238106Sdes			return 0; /* bad nsec */
303238106Sdes		if(dname_strict_subdomain_c(nm, qinfo->qname) &&
304238106Sdes			dname_canonical_compare(nsec->rk.dname,
305238106Sdes				qinfo->qname) < 0) {
306238106Sdes			return 1; /* proves ENT */
307238106Sdes		}
308238106Sdes
309238106Sdes		/* wildcard checking. */
310238106Sdes
311238106Sdes		/* If this is a wildcard NSEC, make sure that a) it was
312238106Sdes		 * possible to have generated qname from the wildcard and
313238106Sdes		 * b) the type map does not contain qtype. Note that this
314238106Sdes		 * does NOT prove that this wildcard was the applicable
315238106Sdes		 * wildcard. */
316238106Sdes		if(dname_is_wild(nsec->rk.dname)) {
317238106Sdes			/* the purported closest encloser. */
318238106Sdes			uint8_t* ce = nsec->rk.dname;
319238106Sdes			size_t ce_len = nsec->rk.dname_len;
320238106Sdes			dname_remove_label(&ce, &ce_len);
321238106Sdes
322238106Sdes			/* The qname must be a strict subdomain of the
323238106Sdes			 * closest encloser, for the wildcard to apply
324238106Sdes			 */
325238106Sdes			if(dname_strict_subdomain_c(qinfo->qname, ce)) {
326238106Sdes				/* here we have a matching NSEC for the qname,
327238106Sdes				 * perform matching NSEC checks */
328238106Sdes				if(nsec_has_type(nsec, LDNS_RR_TYPE_CNAME)) {
329238106Sdes				   /* should have gotten the wildcard CNAME */
330238106Sdes					return 0;
331238106Sdes				}
332238106Sdes				if(nsec_has_type(nsec, LDNS_RR_TYPE_NS) &&
333238106Sdes				   !nsec_has_type(nsec, LDNS_RR_TYPE_SOA)) {
334238106Sdes				   /* wrong parentside (wildcard) NSEC used */
335238106Sdes					return 0;
336238106Sdes				}
337238106Sdes				if(nsec_has_type(nsec, qinfo->qtype)) {
338238106Sdes					return 0;
339238106Sdes				}
340238106Sdes				*wc = ce;
341238106Sdes				return 1;
342238106Sdes			}
343238106Sdes		}
344238106Sdes
345238106Sdes		/* Otherwise, this NSEC does not prove ENT and is not a
346238106Sdes		 * wildcard, so it does not prove NODATA. */
347238106Sdes		return 0;
348238106Sdes	}
349238106Sdes
350238106Sdes	/* If the qtype exists, then we should have gotten it. */
351238106Sdes	if(nsec_has_type(nsec, qinfo->qtype)) {
352238106Sdes		return 0;
353238106Sdes	}
354238106Sdes
355238106Sdes	/* if the name is a CNAME node, then we should have gotten the CNAME*/
356238106Sdes	if(nsec_has_type(nsec, LDNS_RR_TYPE_CNAME)) {
357238106Sdes		return 0;
358238106Sdes	}
359238106Sdes
360238106Sdes	/* If an NS set exists at this name, and NOT a SOA (so this is a
361238106Sdes	 * zone cut, not a zone apex), then we should have gotten a
362238106Sdes	 * referral (or we just got the wrong NSEC).
363238106Sdes	 * The reverse of this check is used when qtype is DS, since that
364238106Sdes	 * must use the NSEC from above the zone cut. */
365238106Sdes	if(qinfo->qtype != LDNS_RR_TYPE_DS &&
366238106Sdes		nsec_has_type(nsec, LDNS_RR_TYPE_NS) &&
367238106Sdes		!nsec_has_type(nsec, LDNS_RR_TYPE_SOA)) {
368238106Sdes		return 0;
369238106Sdes	} else if(qinfo->qtype == LDNS_RR_TYPE_DS &&
370238106Sdes		nsec_has_type(nsec, LDNS_RR_TYPE_SOA) &&
371238106Sdes		!dname_is_root(qinfo->qname)) {
372238106Sdes		return 0;
373238106Sdes	}
374238106Sdes
375238106Sdes	return 1;
376238106Sdes}
377238106Sdes
378238106Sdesint
379238106Sdesval_nsec_proves_name_error(struct ub_packed_rrset_key* nsec, uint8_t* qname)
380238106Sdes{
381238106Sdes	uint8_t* owner = nsec->rk.dname;
382238106Sdes	uint8_t* next;
383238106Sdes	size_t nlen;
384238106Sdes	if(!nsec_get_next(nsec, &next, &nlen))
385238106Sdes		return 0;
386238106Sdes
387238106Sdes	/* If NSEC owner == qname, then this NSEC proves that qname exists. */
388238106Sdes	if(query_dname_compare(qname, owner) == 0) {
389238106Sdes		return 0;
390238106Sdes	}
391238106Sdes
392238106Sdes	/* If NSEC is a parent of qname, we need to check the type map
393238106Sdes	 * If the parent name has a DNAME or is a delegation point, then
394238106Sdes	 * this NSEC is being misused. */
395238106Sdes	if(dname_subdomain_c(qname, owner) &&
396238106Sdes		(nsec_has_type(nsec, LDNS_RR_TYPE_DNAME) ||
397238106Sdes		(nsec_has_type(nsec, LDNS_RR_TYPE_NS)
398238106Sdes			&& !nsec_has_type(nsec, LDNS_RR_TYPE_SOA))
399238106Sdes		)) {
400238106Sdes		return 0;
401238106Sdes	}
402238106Sdes
403238106Sdes	if(query_dname_compare(owner, next) == 0) {
404238106Sdes		/* this nsec is the only nsec */
405238106Sdes		/* zone.name NSEC zone.name, disproves everything else */
406238106Sdes		/* but only for subdomains of that zone */
407238106Sdes		if(dname_strict_subdomain_c(qname, next))
408238106Sdes			return 1;
409238106Sdes	}
410238106Sdes	else if(dname_canonical_compare(owner, next) > 0) {
411238106Sdes		/* this is the last nsec, ....(bigger) NSEC zonename(smaller) */
412238106Sdes		/* the names after the last (owner) name do not exist
413238106Sdes		 * there are no names before the zone name in the zone
414238106Sdes		 * but the qname must be a subdomain of the zone name(next). */
415238106Sdes		if(dname_canonical_compare(owner, qname) < 0 &&
416238106Sdes			dname_strict_subdomain_c(qname, next))
417238106Sdes			return 1;
418238106Sdes	} else {
419238106Sdes		/* regular NSEC, (smaller) NSEC (larger) */
420238106Sdes		if(dname_canonical_compare(owner, qname) < 0 &&
421238106Sdes		   dname_canonical_compare(qname, next) < 0) {
422238106Sdes			return 1;
423238106Sdes		}
424238106Sdes	}
425238106Sdes	return 0;
426238106Sdes}
427238106Sdes
428238106Sdesint val_nsec_proves_insecuredelegation(struct ub_packed_rrset_key* nsec,
429238106Sdes	struct query_info* qinfo)
430238106Sdes{
431238106Sdes	if(nsec_has_type(nsec, LDNS_RR_TYPE_NS) &&
432238106Sdes		!nsec_has_type(nsec, LDNS_RR_TYPE_DS) &&
433238106Sdes		!nsec_has_type(nsec, LDNS_RR_TYPE_SOA)) {
434238106Sdes		/* see if nsec signals an insecure delegation */
435238106Sdes		if(qinfo->qtype == LDNS_RR_TYPE_DS) {
436238106Sdes			/* if type is DS and qname is equal to nsec, then it
437238106Sdes			 * is an exact match nsec, result not insecure */
438238106Sdes			if(dname_strict_subdomain_c(qinfo->qname,
439238106Sdes				nsec->rk.dname))
440238106Sdes				return 1;
441238106Sdes		} else {
442238106Sdes			if(dname_subdomain_c(qinfo->qname, nsec->rk.dname))
443238106Sdes				return 1;
444238106Sdes		}
445238106Sdes	}
446238106Sdes	return 0;
447238106Sdes}
448238106Sdes
449238106Sdesuint8_t*
450238106Sdesnsec_closest_encloser(uint8_t* qname, struct ub_packed_rrset_key* nsec)
451238106Sdes{
452238106Sdes	uint8_t* next;
453238106Sdes	size_t nlen;
454238106Sdes	uint8_t* common1, *common2;
455238106Sdes	if(!nsec_get_next(nsec, &next, &nlen))
456238106Sdes		return NULL;
457238106Sdes	/* longest common with owner or next name */
458238106Sdes	common1 = dname_get_shared_topdomain(nsec->rk.dname, qname);
459238106Sdes	common2 = dname_get_shared_topdomain(next, qname);
460238106Sdes	if(dname_count_labels(common1) > dname_count_labels(common2))
461238106Sdes		return common1;
462238106Sdes	return common2;
463238106Sdes}
464238106Sdes
465238106Sdesint val_nsec_proves_positive_wildcard(struct ub_packed_rrset_key* nsec,
466238106Sdes	struct query_info* qinf, uint8_t* wc)
467238106Sdes{
468238106Sdes	uint8_t* ce;
469238106Sdes	/*  1) prove that qname doesn't exist and
470238106Sdes	 *  2) that the correct wildcard was used
471238106Sdes	 *  nsec has been verified already. */
472238106Sdes	if(!val_nsec_proves_name_error(nsec, qinf->qname))
473238106Sdes		return 0;
474238106Sdes	/* check wildcard name */
475238106Sdes	ce = nsec_closest_encloser(qinf->qname, nsec);
476238106Sdes	if(!ce)
477238106Sdes		return 0;
478238106Sdes	if(query_dname_compare(wc, ce) != 0) {
479238106Sdes		return 0;
480238106Sdes	}
481238106Sdes	return 1;
482238106Sdes}
483238106Sdes
484238106Sdesint
485238106Sdesval_nsec_proves_no_wc(struct ub_packed_rrset_key* nsec, uint8_t* qname,
486238106Sdes	size_t qnamelen)
487238106Sdes{
488238106Sdes	/* Determine if a NSEC record proves the non-existence of a
489238106Sdes	 * wildcard that could have produced qname. */
490238106Sdes	int labs;
491238106Sdes	int i;
492238106Sdes	uint8_t* ce = nsec_closest_encloser(qname, nsec);
493238106Sdes	uint8_t* strip;
494238106Sdes	size_t striplen;
495238106Sdes	uint8_t buf[LDNS_MAX_DOMAINLEN+3];
496238106Sdes	if(!ce)
497238106Sdes		return 0;
498238106Sdes	/* we can subtract the closest encloser count - since that is the
499238106Sdes	 * largest shared topdomain with owner and next NSEC name,
500238106Sdes	 * because the NSEC is no proof for names shorter than the owner
501238106Sdes	 * and next names. */
502238106Sdes	labs = dname_count_labels(qname) - dname_count_labels(ce);
503238106Sdes
504238106Sdes	for(i=labs; i>0; i--) {
505238106Sdes		/* i is number of labels to strip off qname, prepend * wild */
506238106Sdes		strip = qname;
507238106Sdes		striplen = qnamelen;
508238106Sdes		dname_remove_labels(&strip, &striplen, i);
509238106Sdes		if(striplen > LDNS_MAX_DOMAINLEN-2)
510238106Sdes			continue; /* too long to prepend wildcard */
511238106Sdes		buf[0] = 1;
512238106Sdes		buf[1] = (uint8_t)'*';
513238106Sdes		memmove(buf+2, strip, striplen);
514238106Sdes		if(val_nsec_proves_name_error(nsec, buf)) {
515238106Sdes			return 1;
516238106Sdes		}
517238106Sdes	}
518238106Sdes	return 0;
519238106Sdes}
520238106Sdes
521238106Sdes/**
522238106Sdes * Find shared topdomain that exists
523238106Sdes */
524238106Sdesstatic void
525238106Sdesdlv_topdomain(struct ub_packed_rrset_key* nsec, uint8_t* qname,
526238106Sdes	uint8_t** nm, size_t* nm_len)
527238106Sdes{
528238106Sdes	/* make sure reply is part of nm */
529238106Sdes	/* take shared topdomain with left of NSEC. */
530238106Sdes
531238106Sdes	/* because, if empty nonterminal, then right is subdomain of qname.
532238106Sdes	 * and any shared topdomain would be empty nonterminals.
533238106Sdes	 *
534238106Sdes	 * If nxdomain, then the right is bigger, and could have an
535238106Sdes	 * interesting shared topdomain, but if it does have one, it is
536238106Sdes	 * an empty nonterminal. An empty nonterminal shared with the left
537238106Sdes	 * one. */
538238106Sdes	int n;
539238106Sdes	uint8_t* common = dname_get_shared_topdomain(qname, nsec->rk.dname);
540238106Sdes	n = dname_count_labels(*nm) - dname_count_labels(common);
541238106Sdes	dname_remove_labels(nm, nm_len, n);
542238106Sdes}
543238106Sdes
544238106Sdesint val_nsec_check_dlv(struct query_info* qinfo,
545238106Sdes        struct reply_info* rep, uint8_t** nm, size_t* nm_len)
546238106Sdes{
547238106Sdes	uint8_t* next;
548238106Sdes	size_t i, nlen;
549238106Sdes	int c;
550238106Sdes	/* we should now have a NOERROR/NODATA or NXDOMAIN message */
551238106Sdes	if(rep->an_numrrsets != 0) {
552238106Sdes		return 0;
553238106Sdes	}
554238106Sdes	/* is this NOERROR ? */
555238106Sdes	if(FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NOERROR) {
556238106Sdes		/* it can be a plain NSEC match - go up one more level. */
557238106Sdes		/* or its an empty nonterminal - go up to nonempty level */
558238106Sdes		for(i=0; i<rep->ns_numrrsets; i++) {
559238106Sdes			if(htons(rep->rrsets[i]->rk.type)!=LDNS_RR_TYPE_NSEC ||
560238106Sdes				!nsec_get_next(rep->rrsets[i], &next, &nlen))
561238106Sdes				continue;
562238106Sdes			c = dname_canonical_compare(
563238106Sdes				rep->rrsets[i]->rk.dname, qinfo->qname);
564238106Sdes			if(c == 0) {
565238106Sdes				/* plain match */
566238106Sdes				if(nsec_has_type(rep->rrsets[i],
567238106Sdes					LDNS_RR_TYPE_DLV))
568238106Sdes					return 0;
569238106Sdes				dname_remove_label(nm, nm_len);
570238106Sdes				return 1;
571238106Sdes			} else if(c < 0 &&
572238106Sdes				dname_strict_subdomain_c(next, qinfo->qname)) {
573238106Sdes				/* ENT */
574238106Sdes				dlv_topdomain(rep->rrsets[i], qinfo->qname,
575238106Sdes					nm, nm_len);
576238106Sdes				return 1;
577238106Sdes			}
578238106Sdes		}
579238106Sdes		return 0;
580238106Sdes	}
581238106Sdes
582238106Sdes	/* is this NXDOMAIN ? */
583238106Sdes	if(FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NXDOMAIN) {
584238106Sdes		/* find the qname denial NSEC record. It can tell us
585238106Sdes		 * a closest encloser name; or that we not need bother */
586238106Sdes		for(i=0; i<rep->ns_numrrsets; i++) {
587238106Sdes			if(htons(rep->rrsets[i]->rk.type) != LDNS_RR_TYPE_NSEC)
588238106Sdes				continue;
589238106Sdes			if(val_nsec_proves_name_error(rep->rrsets[i],
590238106Sdes				qinfo->qname)) {
591238106Sdes				log_nametypeclass(VERB_ALGO, "topdomain on",
592238106Sdes					rep->rrsets[i]->rk.dname,
593238106Sdes					ntohs(rep->rrsets[i]->rk.type), 0);
594238106Sdes				dlv_topdomain(rep->rrsets[i], qinfo->qname,
595238106Sdes					nm, nm_len);
596238106Sdes				return 1;
597238106Sdes			}
598238106Sdes		}
599238106Sdes		return 0;
600238106Sdes	}
601238106Sdes	return 0;
602238106Sdes}
603