1135446Strhodes/*
2254402Serwin * Copyright (C) 2004-2013  Internet Systems Consortium, Inc. ("ISC")
3135446Strhodes * Copyright (C) 2000-2003  Internet Software Consortium.
4135446Strhodes *
5174187Sdougb * Permission to use, copy, modify, and/or distribute this software for any
6135446Strhodes * purpose with or without fee is hereby granted, provided that the above
7135446Strhodes * copyright notice and this permission notice appear in all copies.
8135446Strhodes *
9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11135446Strhodes * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15135446Strhodes * PERFORMANCE OF THIS SOFTWARE.
16135446Strhodes */
17135446Strhodes
18234010Sdougb/* $Id$ */
19135446Strhodes
20135446Strhodes#include <config.h>
21135446Strhodes
22193149Sdougb#include <isc/base32.h>
23135446Strhodes#include <isc/mem.h>
24135446Strhodes#include <isc/print.h>
25193149Sdougb#include <isc/sha2.h>
26135446Strhodes#include <isc/string.h>
27135446Strhodes#include <isc/task.h>
28135446Strhodes#include <isc/util.h>
29135446Strhodes
30135446Strhodes#include <dns/db.h>
31224092Sdougb#include <dns/dnssec.h>
32135446Strhodes#include <dns/ds.h>
33135446Strhodes#include <dns/events.h>
34135446Strhodes#include <dns/keytable.h>
35224092Sdougb#include <dns/keyvalues.h>
36135446Strhodes#include <dns/log.h>
37135446Strhodes#include <dns/message.h>
38135446Strhodes#include <dns/ncache.h>
39135446Strhodes#include <dns/nsec.h>
40193149Sdougb#include <dns/nsec3.h>
41135446Strhodes#include <dns/rdata.h>
42135446Strhodes#include <dns/rdataset.h>
43135446Strhodes#include <dns/rdatatype.h>
44135446Strhodes#include <dns/resolver.h>
45135446Strhodes#include <dns/result.h>
46135446Strhodes#include <dns/validator.h>
47135446Strhodes#include <dns/view.h>
48135446Strhodes
49165071Sdougb/*! \file
50165071Sdougb * \brief
51165071Sdougb * Basic processing sequences.
52165071Sdougb *
53165071Sdougb * \li When called with rdataset and sigrdataset:
54165071Sdougb * validator_start -> validate -> proveunsecure -> startfinddlvsep ->
55165071Sdougb *	dlv_validator_start -> validator_start -> validate -> proveunsecure
56165071Sdougb *
57165071Sdougb * validator_start -> validate -> nsecvalidate	(secure wildcard answer)
58186462Sdougb *
59165071Sdougb * \li When called with rdataset, sigrdataset and with DNS_VALIDATOR_DLV:
60165071Sdougb * validator_start -> startfinddlvsep -> dlv_validator_start ->
61165071Sdougb *	validator_start -> validate -> proveunsecure
62165071Sdougb *
63165071Sdougb * \li When called with rdataset:
64165071Sdougb * validator_start -> proveunsecure -> startfinddlvsep ->
65165071Sdougb *	dlv_validator_start -> validator_start -> proveunsecure
66165071Sdougb *
67165071Sdougb * \li When called with rdataset and with DNS_VALIDATOR_DLV:
68165071Sdougb * validator_start -> startfinddlvsep -> dlv_validator_start ->
69165071Sdougb *	validator_start -> proveunsecure
70165071Sdougb *
71165071Sdougb * \li When called without a rdataset:
72165071Sdougb * validator_start -> nsecvalidate -> proveunsecure -> startfinddlvsep ->
73165071Sdougb *	dlv_validator_start -> validator_start -> nsecvalidate -> proveunsecure
74165071Sdougb *
75170222Sdougb * Note: there isn't a case for DNS_VALIDATOR_DLV here as we want nsecvalidate()
76170222Sdougb * to always validate the authority section even when it does not contain
77170222Sdougb * signatures.
78165071Sdougb *
79165071Sdougb * validator_start: determines what type of validation to do.
80165071Sdougb * validate: attempts to perform a positive validation.
81165071Sdougb * proveunsecure: attempts to prove the answer comes from a unsecure zone.
82165071Sdougb * nsecvalidate: attempts to prove a negative response.
83165071Sdougb * startfinddlvsep: starts the DLV record lookup.
84165071Sdougb * dlv_validator_start: resets state and restarts the lookup using the
85165071Sdougb *	DLV RRset found by startfinddlvsep.
86165071Sdougb */
87165071Sdougb
88135446Strhodes#define VALIDATOR_MAGIC			ISC_MAGIC('V', 'a', 'l', '?')
89135446Strhodes#define VALID_VALIDATOR(v)		ISC_MAGIC_VALID(v, VALIDATOR_MAGIC)
90135446Strhodes
91165071Sdougb#define VALATTR_SHUTDOWN		0x0001	/*%< Shutting down. */
92193149Sdougb#define VALATTR_CANCELED		0x0002	/*%< Canceled. */
93165071Sdougb#define VALATTR_TRIEDVERIFY		0x0004  /*%< We have found a key and
94165071Sdougb						 * have attempted a verify. */
95165071Sdougb#define VALATTR_INSECURITY		0x0010	/*%< Attempting proveunsecure. */
96165071Sdougb#define VALATTR_DLVTRIED		0x0020	/*%< Looked for a DLV record. */
97135446Strhodes
98165071Sdougb/*!
99165071Sdougb * NSEC proofs to be looked for.
100165071Sdougb */
101193149Sdougb#define VALATTR_NEEDNOQNAME		0x00000100
102193149Sdougb#define VALATTR_NEEDNOWILDCARD		0x00000200
103193149Sdougb#define VALATTR_NEEDNODATA		0x00000400
104135446Strhodes
105165071Sdougb/*!
106165071Sdougb * NSEC proofs that have been found.
107165071Sdougb */
108193149Sdougb#define VALATTR_FOUNDNOQNAME		0x00001000
109193149Sdougb#define VALATTR_FOUNDNOWILDCARD		0x00002000
110193149Sdougb#define VALATTR_FOUNDNODATA		0x00004000
111193149Sdougb#define VALATTR_FOUNDCLOSEST		0x00008000
112135446Strhodes
113193149Sdougb/*
114193149Sdougb *
115193149Sdougb */
116193149Sdougb#define VALATTR_FOUNDOPTOUT		0x00010000
117193149Sdougb#define VALATTR_FOUNDUNKNOWN		0x00020000
118193149Sdougb
119135446Strhodes#define NEEDNODATA(val) ((val->attributes & VALATTR_NEEDNODATA) != 0)
120135446Strhodes#define NEEDNOQNAME(val) ((val->attributes & VALATTR_NEEDNOQNAME) != 0)
121135446Strhodes#define NEEDNOWILDCARD(val) ((val->attributes & VALATTR_NEEDNOWILDCARD) != 0)
122135446Strhodes#define DLVTRIED(val) ((val->attributes & VALATTR_DLVTRIED) != 0)
123214586Sdougb#define FOUNDNODATA(val) ((val->attributes & VALATTR_FOUNDNODATA) != 0)
124214586Sdougb#define FOUNDNOQNAME(val) ((val->attributes & VALATTR_FOUNDNOQNAME) != 0)
125214586Sdougb#define FOUNDNOWILDCARD(val) ((val->attributes & VALATTR_FOUNDNOWILDCARD) != 0)
126214586Sdougb#define FOUNDCLOSEST(val) ((val->attributes & VALATTR_FOUNDCLOSEST) != 0)
127214586Sdougb#define FOUNDOPTOUT(val) ((val->attributes & VALATTR_FOUNDOPTOUT) != 0)
128135446Strhodes
129135446Strhodes#define SHUTDOWN(v)		(((v)->attributes & VALATTR_SHUTDOWN) != 0)
130174187Sdougb#define CANCELED(v)		(((v)->attributes & VALATTR_CANCELED) != 0)
131135446Strhodes
132223812Sdougb#define NEGATIVE(r)	(((r)->attributes & DNS_RDATASETATTR_NEGATIVE) != 0)
133223812Sdougb
134135446Strhodesstatic void
135135446Strhodesdestroy(dns_validator_t *val);
136135446Strhodes
137135446Strhodesstatic isc_result_t
138135446Strhodesget_dst_key(dns_validator_t *val, dns_rdata_rrsig_t *siginfo,
139135446Strhodes	    dns_rdataset_t *rdataset);
140135446Strhodes
141135446Strhodesstatic isc_result_t
142135446Strhodesvalidate(dns_validator_t *val, isc_boolean_t resume);
143135446Strhodes
144135446Strhodesstatic isc_result_t
145135446Strhodesvalidatezonekey(dns_validator_t *val);
146135446Strhodes
147135446Strhodesstatic isc_result_t
148135446Strhodesnsecvalidate(dns_validator_t *val, isc_boolean_t resume);
149135446Strhodes
150135446Strhodesstatic isc_result_t
151186462Sdougbproveunsecure(dns_validator_t *val, isc_boolean_t have_ds,
152186462Sdougb	      isc_boolean_t resume);
153135446Strhodes
154135446Strhodesstatic void
155135446Strhodesvalidator_logv(dns_validator_t *val, isc_logcategory_t *category,
156135446Strhodes	       isc_logmodule_t *module, int level, const char *fmt, va_list ap)
157135446Strhodes     ISC_FORMAT_PRINTF(5, 0);
158135446Strhodes
159135446Strhodesstatic void
160254402Serwinvalidator_log(void *val, int level, const char *fmt, ...)
161135446Strhodes     ISC_FORMAT_PRINTF(3, 4);
162135446Strhodes
163135446Strhodesstatic void
164135446Strhodesvalidator_logcreate(dns_validator_t *val,
165135446Strhodes		    dns_name_t *name, dns_rdatatype_t type,
166135446Strhodes		    const char *caller, const char *operation);
167135446Strhodes
168135446Strhodesstatic isc_result_t
169135446Strhodesdlv_validatezonekey(dns_validator_t *val);
170135446Strhodes
171165071Sdougbstatic void
172153816Sdougbdlv_validator_start(dns_validator_t *val);
173153816Sdougb
174153816Sdougbstatic isc_result_t
175135446Strhodesfinddlvsep(dns_validator_t *val, isc_boolean_t resume);
176135446Strhodes
177165071Sdougbstatic isc_result_t
178165071Sdougbstartfinddlvsep(dns_validator_t *val, dns_name_t *unsecure);
179165071Sdougb
180165071Sdougb/*%
181165071Sdougb * Mark the RRsets as a answer.
182165071Sdougb */
183153816Sdougbstatic inline void
184214586Sdougbmarkanswer(dns_validator_t *val, const char *where) {
185214586Sdougb	validator_log(val, ISC_LOG_DEBUG(3), "marking as answer (%s)", where);
186165071Sdougb	if (val->event->rdataset != NULL)
187205292Sdougb		dns_rdataset_settrust(val->event->rdataset, dns_trust_answer);
188165071Sdougb	if (val->event->sigrdataset != NULL)
189205292Sdougb		dns_rdataset_settrust(val->event->sigrdataset,
190205292Sdougb				      dns_trust_answer);
191153816Sdougb}
192153816Sdougb
193205292Sdougbstatic inline void
194205292Sdougbmarksecure(dns_validatorevent_t *event) {
195205292Sdougb	dns_rdataset_settrust(event->rdataset, dns_trust_secure);
196214586Sdougb	if (event->sigrdataset != NULL)
197214586Sdougb		dns_rdataset_settrust(event->sigrdataset, dns_trust_secure);
198254402Serwin	event->secure = ISC_TRUE;
199205292Sdougb}
200205292Sdougb
201135446Strhodesstatic void
202135446Strhodesvalidator_done(dns_validator_t *val, isc_result_t result) {
203135446Strhodes	isc_task_t *task;
204135446Strhodes
205135446Strhodes	if (val->event == NULL)
206135446Strhodes		return;
207135446Strhodes
208135446Strhodes	/*
209135446Strhodes	 * Caller must be holding the lock.
210135446Strhodes	 */
211135446Strhodes
212135446Strhodes	val->event->result = result;
213135446Strhodes	task = val->event->ev_sender;
214135446Strhodes	val->event->ev_sender = val;
215135446Strhodes	val->event->ev_type = DNS_EVENT_VALIDATORDONE;
216135446Strhodes	val->event->ev_action = val->action;
217135446Strhodes	val->event->ev_arg = val->arg;
218135446Strhodes	isc_task_sendanddetach(&task, (isc_event_t **)&val->event);
219135446Strhodes}
220135446Strhodes
221135446Strhodesstatic inline isc_boolean_t
222135446Strhodesexit_check(dns_validator_t *val) {
223135446Strhodes	/*
224135446Strhodes	 * Caller must be holding the lock.
225135446Strhodes	 */
226135446Strhodes	if (!SHUTDOWN(val))
227135446Strhodes		return (ISC_FALSE);
228135446Strhodes
229135446Strhodes	INSIST(val->event == NULL);
230135446Strhodes
231135446Strhodes	if (val->fetch != NULL || val->subvalidator != NULL)
232135446Strhodes		return (ISC_FALSE);
233135446Strhodes
234135446Strhodes	return (ISC_TRUE);
235135446Strhodes}
236135446Strhodes
237190227Sdougb/*
238190227Sdougb * Check that we have atleast one supported algorithm in the DLV RRset.
239190227Sdougb */
240190227Sdougbstatic inline isc_boolean_t
241190227Sdougbdlv_algorithm_supported(dns_validator_t *val) {
242190227Sdougb	dns_rdata_t rdata = DNS_RDATA_INIT;
243190227Sdougb	dns_rdata_dlv_t dlv;
244190227Sdougb	isc_result_t result;
245190227Sdougb
246190227Sdougb	for (result = dns_rdataset_first(&val->dlv);
247190227Sdougb	     result == ISC_R_SUCCESS;
248190227Sdougb	     result = dns_rdataset_next(&val->dlv)) {
249190227Sdougb		dns_rdata_reset(&rdata);
250190227Sdougb		dns_rdataset_current(&val->dlv, &rdata);
251190227Sdougb		result = dns_rdata_tostruct(&rdata, &dlv, NULL);
252190227Sdougb		RUNTIME_CHECK(result == ISC_R_SUCCESS);
253190227Sdougb
254190227Sdougb		if (!dns_resolver_algorithm_supported(val->view->resolver,
255190227Sdougb						      val->event->name,
256190227Sdougb						      dlv.algorithm))
257190227Sdougb			continue;
258190227Sdougb
259224092Sdougb#ifdef HAVE_OPENSSL_GOST
260190227Sdougb		if (dlv.digest_type != DNS_DSDIGEST_SHA256 &&
261224092Sdougb		    dlv.digest_type != DNS_DSDIGEST_SHA1 &&
262224092Sdougb		    dlv.digest_type != DNS_DSDIGEST_GOST)
263224092Sdougb			continue;
264224092Sdougb#else
265224092Sdougb		if (dlv.digest_type != DNS_DSDIGEST_SHA256 &&
266190227Sdougb		    dlv.digest_type != DNS_DSDIGEST_SHA1)
267190227Sdougb			continue;
268224092Sdougb#endif
269190227Sdougb
270224092Sdougb
271190227Sdougb		return (ISC_TRUE);
272190227Sdougb	}
273190227Sdougb	return (ISC_FALSE);
274190227Sdougb}
275190227Sdougb
276165071Sdougb/*%
277165071Sdougb * Look in the NSEC record returned from a DS query to see if there is
278165071Sdougb * a NS RRset at this name.  If it is found we are at a delegation point.
279165071Sdougb */
280135446Strhodesstatic isc_boolean_t
281135446Strhodesisdelegation(dns_name_t *name, dns_rdataset_t *rdataset,
282135446Strhodes	     isc_result_t dbresult)
283135446Strhodes{
284193149Sdougb	dns_fixedname_t fixed;
285193149Sdougb	dns_label_t hashlabel;
286193149Sdougb	dns_name_t nsec3name;
287193149Sdougb	dns_rdata_nsec3_t nsec3;
288193149Sdougb	dns_rdata_t rdata = DNS_RDATA_INIT;
289135446Strhodes	dns_rdataset_t set;
290193149Sdougb	int order;
291193149Sdougb	int scope;
292135446Strhodes	isc_boolean_t found;
293193149Sdougb	isc_buffer_t buffer;
294135446Strhodes	isc_result_t result;
295193149Sdougb	unsigned char hash[NSEC3_MAX_HASH_LENGTH];
296193149Sdougb	unsigned char owner[NSEC3_MAX_HASH_LENGTH];
297193149Sdougb	unsigned int length;
298135446Strhodes
299135446Strhodes	REQUIRE(dbresult == DNS_R_NXRRSET || dbresult == DNS_R_NCACHENXRRSET);
300135446Strhodes
301135446Strhodes	dns_rdataset_init(&set);
302135446Strhodes	if (dbresult == DNS_R_NXRRSET)
303135446Strhodes		dns_rdataset_clone(rdataset, &set);
304135446Strhodes	else {
305135446Strhodes		result = dns_ncache_getrdataset(rdataset, name,
306135446Strhodes						dns_rdatatype_nsec, &set);
307193149Sdougb		if (result == ISC_R_NOTFOUND)
308193149Sdougb			goto trynsec3;
309135446Strhodes		if (result != ISC_R_SUCCESS)
310135446Strhodes			return (ISC_FALSE);
311135446Strhodes	}
312135446Strhodes
313135446Strhodes	INSIST(set.type == dns_rdatatype_nsec);
314135446Strhodes
315135446Strhodes	found = ISC_FALSE;
316135446Strhodes	result = dns_rdataset_first(&set);
317135446Strhodes	if (result == ISC_R_SUCCESS) {
318135446Strhodes		dns_rdataset_current(&set, &rdata);
319135446Strhodes		found = dns_nsec_typepresent(&rdata, dns_rdatatype_ns);
320193149Sdougb		dns_rdata_reset(&rdata);
321135446Strhodes	}
322135446Strhodes	dns_rdataset_disassociate(&set);
323135446Strhodes	return (found);
324193149Sdougb
325193149Sdougb trynsec3:
326193149Sdougb	/*
327193149Sdougb	 * Iterate over the ncache entry.
328193149Sdougb	 */
329193149Sdougb	found = ISC_FALSE;
330193149Sdougb	dns_name_init(&nsec3name, NULL);
331193149Sdougb	dns_fixedname_init(&fixed);
332193149Sdougb	dns_name_downcase(name, dns_fixedname_name(&fixed), NULL);
333193149Sdougb	name = dns_fixedname_name(&fixed);
334193149Sdougb	for (result = dns_rdataset_first(rdataset);
335193149Sdougb	     result == ISC_R_SUCCESS;
336193149Sdougb	     result = dns_rdataset_next(rdataset))
337193149Sdougb	{
338193149Sdougb		dns_ncache_current(rdataset, &nsec3name, &set);
339193149Sdougb		if (set.type != dns_rdatatype_nsec3) {
340193149Sdougb			dns_rdataset_disassociate(&set);
341193149Sdougb			continue;
342193149Sdougb		}
343193149Sdougb		dns_name_getlabel(&nsec3name, 0, &hashlabel);
344193149Sdougb		isc_region_consume(&hashlabel, 1);
345193149Sdougb		isc_buffer_init(&buffer, owner, sizeof(owner));
346193149Sdougb		result = isc_base32hex_decoderegion(&hashlabel, &buffer);
347193149Sdougb		if (result != ISC_R_SUCCESS) {
348193149Sdougb			dns_rdataset_disassociate(&set);
349193149Sdougb			continue;
350193149Sdougb		}
351193149Sdougb		for (result = dns_rdataset_first(&set);
352193149Sdougb		     result == ISC_R_SUCCESS;
353193149Sdougb		     result = dns_rdataset_next(&set))
354193149Sdougb		{
355193149Sdougb			dns_rdata_reset(&rdata);
356193149Sdougb			dns_rdataset_current(&set, &rdata);
357193149Sdougb			(void)dns_rdata_tostruct(&rdata, &nsec3, NULL);
358193149Sdougb			if (nsec3.hash != 1)
359193149Sdougb				continue;
360193149Sdougb			length = isc_iterated_hash(hash, nsec3.hash,
361193149Sdougb						   nsec3.iterations, nsec3.salt,
362193149Sdougb						   nsec3.salt_length,
363193149Sdougb						   name->ndata, name->length);
364193149Sdougb			if (length != isc_buffer_usedlength(&buffer))
365193149Sdougb				continue;
366193149Sdougb			order = memcmp(hash, owner, length);
367193149Sdougb			if (order == 0) {
368193149Sdougb				found = dns_nsec3_typepresent(&rdata,
369193149Sdougb							      dns_rdatatype_ns);
370193149Sdougb				dns_rdataset_disassociate(&set);
371193149Sdougb				return (found);
372193149Sdougb			}
373193149Sdougb			if ((nsec3.flags & DNS_NSEC3FLAG_OPTOUT) == 0)
374193149Sdougb				continue;
375193149Sdougb			/*
376193149Sdougb			 * Does this optout span cover the name?
377193149Sdougb			 */
378193149Sdougb			scope = memcmp(owner, nsec3.next, nsec3.next_length);
379193149Sdougb			if ((scope < 0 && order > 0 &&
380193149Sdougb			     memcmp(hash, nsec3.next, length) < 0) ||
381193149Sdougb			    (scope >= 0 && (order > 0 ||
382193149Sdougb					memcmp(hash, nsec3.next, length) < 0)))
383193149Sdougb			{
384193149Sdougb				dns_rdataset_disassociate(&set);
385193149Sdougb				return (ISC_TRUE);
386193149Sdougb			}
387193149Sdougb		}
388193149Sdougb		dns_rdataset_disassociate(&set);
389193149Sdougb	}
390193149Sdougb	return (found);
391135446Strhodes}
392135446Strhodes
393165071Sdougb/*%
394224092Sdougb * We have been asked to look for a key.
395165071Sdougb * If found resume the validation process.
396165071Sdougb * If not found fail the validation process.
397165071Sdougb */
398135446Strhodesstatic void
399135446Strhodesfetch_callback_validator(isc_task_t *task, isc_event_t *event) {
400135446Strhodes	dns_fetchevent_t *devent;
401135446Strhodes	dns_validator_t *val;
402135446Strhodes	dns_rdataset_t *rdataset;
403135446Strhodes	isc_boolean_t want_destroy;
404135446Strhodes	isc_result_t result;
405135446Strhodes	isc_result_t eresult;
406216175Sdougb	isc_result_t saved_result;
407254402Serwin	dns_fetch_t *fetch;
408135446Strhodes
409135446Strhodes	UNUSED(task);
410135446Strhodes	INSIST(event->ev_type == DNS_EVENT_FETCHDONE);
411135446Strhodes	devent = (dns_fetchevent_t *)event;
412135446Strhodes	val = devent->ev_arg;
413135446Strhodes	rdataset = &val->frdataset;
414135446Strhodes	eresult = devent->result;
415135446Strhodes
416153816Sdougb	/* Free resources which are not of interest. */
417153816Sdougb	if (devent->node != NULL)
418153816Sdougb		dns_db_detachnode(devent->db, &devent->node);
419153816Sdougb	if (devent->db != NULL)
420153816Sdougb		dns_db_detach(&devent->db);
421153816Sdougb	if (dns_rdataset_isassociated(&val->fsigrdataset))
422153816Sdougb		dns_rdataset_disassociate(&val->fsigrdataset);
423135446Strhodes	isc_event_free(&event);
424135446Strhodes
425135446Strhodes	INSIST(val->event != NULL);
426135446Strhodes
427135446Strhodes	validator_log(val, ISC_LOG_DEBUG(3), "in fetch_callback_validator");
428135446Strhodes	LOCK(&val->lock);
429254402Serwin	fetch = val->fetch;
430254402Serwin	val->fetch = NULL;
431174187Sdougb	if (CANCELED(val)) {
432174187Sdougb		validator_done(val, ISC_R_CANCELED);
433174187Sdougb	} else if (eresult == ISC_R_SUCCESS) {
434135446Strhodes		validator_log(val, ISC_LOG_DEBUG(3),
435222395Sdougb			      "keyset with trust %s",
436222395Sdougb			      dns_trust_totext(rdataset->trust));
437135446Strhodes		/*
438135446Strhodes		 * Only extract the dst key if the keyset is secure.
439135446Strhodes		 */
440135446Strhodes		if (rdataset->trust >= dns_trust_secure) {
441135446Strhodes			result = get_dst_key(val, val->siginfo, rdataset);
442135446Strhodes			if (result == ISC_R_SUCCESS)
443135446Strhodes				val->keyset = &val->frdataset;
444135446Strhodes		}
445135446Strhodes		result = validate(val, ISC_TRUE);
446216175Sdougb		if (result == DNS_R_NOVALIDSIG &&
447216175Sdougb		    (val->attributes & VALATTR_TRIEDVERIFY) == 0)
448216175Sdougb		{
449216175Sdougb			saved_result = result;
450216175Sdougb			validator_log(val, ISC_LOG_DEBUG(3),
451216175Sdougb				      "falling back to insecurity proof");
452216175Sdougb			val->attributes |= VALATTR_INSECURITY;
453216175Sdougb			result = proveunsecure(val, ISC_FALSE, ISC_FALSE);
454216175Sdougb			if (result == DNS_R_NOTINSECURE)
455216175Sdougb				result = saved_result;
456216175Sdougb		}
457135446Strhodes		if (result != DNS_R_WAIT)
458135446Strhodes			validator_done(val, result);
459135446Strhodes	} else {
460135446Strhodes		validator_log(val, ISC_LOG_DEBUG(3),
461135446Strhodes			      "fetch_callback_validator: got %s",
462135446Strhodes			      isc_result_totext(eresult));
463135446Strhodes		if (eresult == ISC_R_CANCELED)
464135446Strhodes			validator_done(val, eresult);
465135446Strhodes		else
466205292Sdougb			validator_done(val, DNS_R_BROKENCHAIN);
467135446Strhodes	}
468135446Strhodes	want_destroy = exit_check(val);
469135446Strhodes	UNLOCK(&val->lock);
470254402Serwin	if (fetch != NULL)
471254402Serwin		dns_resolver_destroyfetch(&fetch);
472135446Strhodes	if (want_destroy)
473135446Strhodes		destroy(val);
474135446Strhodes}
475135446Strhodes
476165071Sdougb/*%
477165071Sdougb * We were asked to look for a DS record as part of following a key chain
478165071Sdougb * upwards.  If found resume the validation process.  If not found fail the
479165071Sdougb * validation process.
480165071Sdougb */
481135446Strhodesstatic void
482135446Strhodesdsfetched(isc_task_t *task, isc_event_t *event) {
483135446Strhodes	dns_fetchevent_t *devent;
484135446Strhodes	dns_validator_t *val;
485135446Strhodes	dns_rdataset_t *rdataset;
486135446Strhodes	isc_boolean_t want_destroy;
487135446Strhodes	isc_result_t result;
488135446Strhodes	isc_result_t eresult;
489254402Serwin	dns_fetch_t *fetch;
490135446Strhodes
491135446Strhodes	UNUSED(task);
492135446Strhodes	INSIST(event->ev_type == DNS_EVENT_FETCHDONE);
493135446Strhodes	devent = (dns_fetchevent_t *)event;
494135446Strhodes	val = devent->ev_arg;
495135446Strhodes	rdataset = &val->frdataset;
496135446Strhodes	eresult = devent->result;
497135446Strhodes
498153816Sdougb	/* Free resources which are not of interest. */
499153816Sdougb	if (devent->node != NULL)
500153816Sdougb		dns_db_detachnode(devent->db, &devent->node);
501153816Sdougb	if (devent->db != NULL)
502153816Sdougb		dns_db_detach(&devent->db);
503153816Sdougb	if (dns_rdataset_isassociated(&val->fsigrdataset))
504153816Sdougb		dns_rdataset_disassociate(&val->fsigrdataset);
505135446Strhodes	isc_event_free(&event);
506135446Strhodes
507135446Strhodes	INSIST(val->event != NULL);
508135446Strhodes
509135446Strhodes	validator_log(val, ISC_LOG_DEBUG(3), "in dsfetched");
510135446Strhodes	LOCK(&val->lock);
511254402Serwin	fetch = val->fetch;
512254402Serwin	val->fetch = NULL;
513174187Sdougb	if (CANCELED(val)) {
514174187Sdougb		validator_done(val, ISC_R_CANCELED);
515174187Sdougb	} else if (eresult == ISC_R_SUCCESS) {
516135446Strhodes		validator_log(val, ISC_LOG_DEBUG(3),
517222395Sdougb			      "dsset with trust %s",
518222395Sdougb			       dns_trust_totext(rdataset->trust));
519135446Strhodes		val->dsset = &val->frdataset;
520135446Strhodes		result = validatezonekey(val);
521135446Strhodes		if (result != DNS_R_WAIT)
522135446Strhodes			validator_done(val, result);
523225361Sdougb	} else if (eresult == DNS_R_CNAME ||
524225361Sdougb		   eresult == DNS_R_NXRRSET ||
525174187Sdougb		   eresult == DNS_R_NCACHENXRRSET ||
526174187Sdougb		   eresult == DNS_R_SERVFAIL)	/* RFC 1034 parent? */
527135446Strhodes	{
528135446Strhodes		validator_log(val, ISC_LOG_DEBUG(3),
529174187Sdougb			      "falling back to insecurity proof (%s)",
530174187Sdougb			      dns_result_totext(eresult));
531135446Strhodes		val->attributes |= VALATTR_INSECURITY;
532186462Sdougb		result = proveunsecure(val, ISC_FALSE, ISC_FALSE);
533135446Strhodes		if (result != DNS_R_WAIT)
534135446Strhodes			validator_done(val, result);
535135446Strhodes	} else {
536135446Strhodes		validator_log(val, ISC_LOG_DEBUG(3),
537135446Strhodes			      "dsfetched: got %s",
538135446Strhodes			      isc_result_totext(eresult));
539135446Strhodes		if (eresult == ISC_R_CANCELED)
540135446Strhodes			validator_done(val, eresult);
541135446Strhodes		else
542205292Sdougb			validator_done(val, DNS_R_BROKENCHAIN);
543135446Strhodes	}
544135446Strhodes	want_destroy = exit_check(val);
545135446Strhodes	UNLOCK(&val->lock);
546254402Serwin	if (fetch != NULL)
547254402Serwin		dns_resolver_destroyfetch(&fetch);
548135446Strhodes	if (want_destroy)
549135446Strhodes		destroy(val);
550135446Strhodes}
551135446Strhodes
552165071Sdougb/*%
553165071Sdougb * We were asked to look for the DS record as part of proving that a
554165071Sdougb * name is unsecure.
555165071Sdougb *
556165071Sdougb * If the DS record doesn't exist and the query name corresponds to
557165071Sdougb * a delegation point we are transitioning from a secure zone to a
558165071Sdougb * unsecure zone.
559165071Sdougb *
560165071Sdougb * If the DS record exists it will be secure.  We can continue looking
561165071Sdougb * for the break point in the chain of trust.
562135446Strhodes */
563135446Strhodesstatic void
564135446Strhodesdsfetched2(isc_task_t *task, isc_event_t *event) {
565135446Strhodes	dns_fetchevent_t *devent;
566135446Strhodes	dns_validator_t *val;
567135446Strhodes	dns_name_t *tname;
568135446Strhodes	isc_boolean_t want_destroy;
569135446Strhodes	isc_result_t result;
570135446Strhodes	isc_result_t eresult;
571254402Serwin	dns_fetch_t *fetch;
572135446Strhodes
573135446Strhodes	UNUSED(task);
574135446Strhodes	INSIST(event->ev_type == DNS_EVENT_FETCHDONE);
575135446Strhodes	devent = (dns_fetchevent_t *)event;
576135446Strhodes	val = devent->ev_arg;
577135446Strhodes	eresult = devent->result;
578135446Strhodes
579153816Sdougb	/* Free resources which are not of interest. */
580153816Sdougb	if (devent->node != NULL)
581153816Sdougb		dns_db_detachnode(devent->db, &devent->node);
582153816Sdougb	if (devent->db != NULL)
583153816Sdougb		dns_db_detach(&devent->db);
584153816Sdougb	if (dns_rdataset_isassociated(&val->fsigrdataset))
585153816Sdougb		dns_rdataset_disassociate(&val->fsigrdataset);
586135446Strhodes
587135446Strhodes	INSIST(val->event != NULL);
588135446Strhodes
589165071Sdougb	validator_log(val, ISC_LOG_DEBUG(3), "in dsfetched2: %s",
590165071Sdougb		      dns_result_totext(eresult));
591135446Strhodes	LOCK(&val->lock);
592254402Serwin	fetch = val->fetch;
593254402Serwin	val->fetch = NULL;
594174187Sdougb	if (CANCELED(val)) {
595174187Sdougb		validator_done(val, ISC_R_CANCELED);
596225361Sdougb	} else if (eresult == DNS_R_CNAME ||
597225361Sdougb		   eresult == DNS_R_NXRRSET ||
598225361Sdougb		   eresult == DNS_R_NCACHENXRRSET)
599225361Sdougb	{
600135446Strhodes		/*
601135446Strhodes		 * There is no DS.  If this is a delegation, we're done.
602135446Strhodes		 */
603135446Strhodes		tname = dns_fixedname_name(&devent->foundname);
604225361Sdougb		if (eresult != DNS_R_CNAME &&
605225361Sdougb		    isdelegation(tname, &val->frdataset, eresult)) {
606135446Strhodes			if (val->mustbesecure) {
607135446Strhodes				validator_log(val, ISC_LOG_WARNING,
608224092Sdougb					      "must be secure failure, no DS"
609224092Sdougb					      " and this is a delegation");
610135446Strhodes				validator_done(val, DNS_R_MUSTBESECURE);
611165071Sdougb			} else if (val->view->dlv == NULL || DLVTRIED(val)) {
612214586Sdougb				markanswer(val, "dsfetched2");
613135446Strhodes				validator_done(val, ISC_R_SUCCESS);
614165071Sdougb			} else {
615165071Sdougb				result = startfinddlvsep(val, tname);
616165071Sdougb				if (result != DNS_R_WAIT)
617165071Sdougb					validator_done(val, result);
618135446Strhodes			}
619135446Strhodes		} else {
620186462Sdougb			result = proveunsecure(val, ISC_FALSE, ISC_TRUE);
621135446Strhodes			if (result != DNS_R_WAIT)
622135446Strhodes				validator_done(val, result);
623135446Strhodes		}
624135446Strhodes	} else if (eresult == ISC_R_SUCCESS ||
625135446Strhodes		   eresult == DNS_R_NXDOMAIN ||
626135446Strhodes		   eresult == DNS_R_NCACHENXDOMAIN)
627135446Strhodes	{
628135446Strhodes		/*
629186462Sdougb		 * There is a DS which may or may not be a zone cut.
630165071Sdougb		 * In either case we are still in a secure zone resume
631165071Sdougb		 * validation.
632135446Strhodes		 */
633186462Sdougb		result = proveunsecure(val, ISC_TF(eresult == ISC_R_SUCCESS),
634186462Sdougb				       ISC_TRUE);
635135446Strhodes		if (result != DNS_R_WAIT)
636135446Strhodes			validator_done(val, result);
637135446Strhodes	} else {
638135446Strhodes		if (eresult == ISC_R_CANCELED)
639135446Strhodes			validator_done(val, eresult);
640135446Strhodes		else
641135446Strhodes			validator_done(val, DNS_R_NOVALIDDS);
642135446Strhodes	}
643135446Strhodes	isc_event_free(&event);
644135446Strhodes	want_destroy = exit_check(val);
645135446Strhodes	UNLOCK(&val->lock);
646254402Serwin	if (fetch != NULL)
647254402Serwin		dns_resolver_destroyfetch(&fetch);
648135446Strhodes	if (want_destroy)
649135446Strhodes		destroy(val);
650135446Strhodes}
651135446Strhodes
652165071Sdougb/*%
653165071Sdougb * Callback from when a DNSKEY RRset has been validated.
654165071Sdougb *
655165071Sdougb * Resumes the stalled validation process.
656165071Sdougb */
657135446Strhodesstatic void
658135446Strhodeskeyvalidated(isc_task_t *task, isc_event_t *event) {
659135446Strhodes	dns_validatorevent_t *devent;
660135446Strhodes	dns_validator_t *val;
661135446Strhodes	isc_boolean_t want_destroy;
662135446Strhodes	isc_result_t result;
663135446Strhodes	isc_result_t eresult;
664216175Sdougb	isc_result_t saved_result;
665135446Strhodes
666135446Strhodes	UNUSED(task);
667135446Strhodes	INSIST(event->ev_type == DNS_EVENT_VALIDATORDONE);
668135446Strhodes
669135446Strhodes	devent = (dns_validatorevent_t *)event;
670135446Strhodes	val = devent->ev_arg;
671135446Strhodes	eresult = devent->result;
672135446Strhodes
673135446Strhodes	isc_event_free(&event);
674135446Strhodes	dns_validator_destroy(&val->subvalidator);
675135446Strhodes
676135446Strhodes	INSIST(val->event != NULL);
677135446Strhodes
678135446Strhodes	validator_log(val, ISC_LOG_DEBUG(3), "in keyvalidated");
679135446Strhodes	LOCK(&val->lock);
680174187Sdougb	if (CANCELED(val)) {
681174187Sdougb		validator_done(val, ISC_R_CANCELED);
682174187Sdougb	} else if (eresult == ISC_R_SUCCESS) {
683135446Strhodes		validator_log(val, ISC_LOG_DEBUG(3),
684222395Sdougb			      "keyset with trust %s",
685222395Sdougb			      dns_trust_totext(val->frdataset.trust));
686135446Strhodes		/*
687135446Strhodes		 * Only extract the dst key if the keyset is secure.
688135446Strhodes		 */
689135446Strhodes		if (val->frdataset.trust >= dns_trust_secure)
690135446Strhodes			(void) get_dst_key(val, val->siginfo, &val->frdataset);
691135446Strhodes		result = validate(val, ISC_TRUE);
692216175Sdougb		if (result == DNS_R_NOVALIDSIG &&
693216175Sdougb		    (val->attributes & VALATTR_TRIEDVERIFY) == 0)
694216175Sdougb		{
695216175Sdougb			saved_result = result;
696216175Sdougb			validator_log(val, ISC_LOG_DEBUG(3),
697216175Sdougb				      "falling back to insecurity proof");
698216175Sdougb			val->attributes |= VALATTR_INSECURITY;
699216175Sdougb			result = proveunsecure(val, ISC_FALSE, ISC_FALSE);
700216175Sdougb			if (result == DNS_R_NOTINSECURE)
701216175Sdougb				result = saved_result;
702216175Sdougb		}
703135446Strhodes		if (result != DNS_R_WAIT)
704135446Strhodes			validator_done(val, result);
705135446Strhodes	} else {
706205292Sdougb		if (eresult != DNS_R_BROKENCHAIN) {
707205292Sdougb			if (dns_rdataset_isassociated(&val->frdataset))
708205292Sdougb				dns_rdataset_expire(&val->frdataset);
709205292Sdougb			if (dns_rdataset_isassociated(&val->fsigrdataset))
710205292Sdougb				dns_rdataset_expire(&val->fsigrdataset);
711205292Sdougb		}
712135446Strhodes		validator_log(val, ISC_LOG_DEBUG(3),
713135446Strhodes			      "keyvalidated: got %s",
714135446Strhodes			      isc_result_totext(eresult));
715205292Sdougb		validator_done(val, DNS_R_BROKENCHAIN);
716135446Strhodes	}
717135446Strhodes	want_destroy = exit_check(val);
718135446Strhodes	UNLOCK(&val->lock);
719135446Strhodes	if (want_destroy)
720135446Strhodes		destroy(val);
721135446Strhodes}
722135446Strhodes
723165071Sdougb/*%
724165071Sdougb * Callback when the DS record has been validated.
725165071Sdougb *
726165071Sdougb * Resumes validation of the zone key or the unsecure zone proof.
727165071Sdougb */
728135446Strhodesstatic void
729135446Strhodesdsvalidated(isc_task_t *task, isc_event_t *event) {
730135446Strhodes	dns_validatorevent_t *devent;
731135446Strhodes	dns_validator_t *val;
732135446Strhodes	isc_boolean_t want_destroy;
733135446Strhodes	isc_result_t result;
734135446Strhodes	isc_result_t eresult;
735135446Strhodes
736135446Strhodes	UNUSED(task);
737135446Strhodes	INSIST(event->ev_type == DNS_EVENT_VALIDATORDONE);
738135446Strhodes
739135446Strhodes	devent = (dns_validatorevent_t *)event;
740135446Strhodes	val = devent->ev_arg;
741135446Strhodes	eresult = devent->result;
742135446Strhodes
743135446Strhodes	isc_event_free(&event);
744135446Strhodes	dns_validator_destroy(&val->subvalidator);
745135446Strhodes
746135446Strhodes	INSIST(val->event != NULL);
747135446Strhodes
748135446Strhodes	validator_log(val, ISC_LOG_DEBUG(3), "in dsvalidated");
749135446Strhodes	LOCK(&val->lock);
750174187Sdougb	if (CANCELED(val)) {
751174187Sdougb		validator_done(val, ISC_R_CANCELED);
752174187Sdougb	} else if (eresult == ISC_R_SUCCESS) {
753214586Sdougb		isc_boolean_t have_dsset;
754214586Sdougb		dns_name_t *name;
755135446Strhodes		validator_log(val, ISC_LOG_DEBUG(3),
756222395Sdougb			      "%s with trust %s",
757214586Sdougb			      val->frdataset.type == dns_rdatatype_ds ?
758214586Sdougb			      "dsset" : "ds non-existance",
759222395Sdougb			      dns_trust_totext(val->frdataset.trust));
760214586Sdougb		have_dsset = ISC_TF(val->frdataset.type == dns_rdatatype_ds);
761214586Sdougb		name = dns_fixedname_name(&val->fname);
762214586Sdougb		if ((val->attributes & VALATTR_INSECURITY) != 0 &&
763214586Sdougb		    val->frdataset.covers == dns_rdatatype_ds &&
764223812Sdougb		    NEGATIVE(&val->frdataset) &&
765214586Sdougb		    isdelegation(name, &val->frdataset, DNS_R_NCACHENXRRSET)) {
766214586Sdougb			if (val->mustbesecure) {
767214586Sdougb				validator_log(val, ISC_LOG_WARNING,
768214586Sdougb					      "must be secure failure, no DS "
769214586Sdougb					      "and this is a delegation");
770214586Sdougb				result = DNS_R_MUSTBESECURE;
771214586Sdougb			} else if (val->view->dlv == NULL || DLVTRIED(val)) {
772214586Sdougb				markanswer(val, "dsvalidated");
773214586Sdougb				result = ISC_R_SUCCESS;;
774214586Sdougb			} else
775214586Sdougb				result = startfinddlvsep(val, name);
776214586Sdougb		} else if ((val->attributes & VALATTR_INSECURITY) != 0) {
777214586Sdougb			result = proveunsecure(val, have_dsset, ISC_TRUE);
778214586Sdougb		} else
779135446Strhodes			result = validatezonekey(val);
780135446Strhodes		if (result != DNS_R_WAIT)
781135446Strhodes			validator_done(val, result);
782135446Strhodes	} else {
783205292Sdougb		if (eresult != DNS_R_BROKENCHAIN) {
784205292Sdougb			if (dns_rdataset_isassociated(&val->frdataset))
785205292Sdougb				dns_rdataset_expire(&val->frdataset);
786205292Sdougb			if (dns_rdataset_isassociated(&val->fsigrdataset))
787205292Sdougb				dns_rdataset_expire(&val->fsigrdataset);
788205292Sdougb		}
789135446Strhodes		validator_log(val, ISC_LOG_DEBUG(3),
790135446Strhodes			      "dsvalidated: got %s",
791135446Strhodes			      isc_result_totext(eresult));
792205292Sdougb		validator_done(val, DNS_R_BROKENCHAIN);
793135446Strhodes	}
794135446Strhodes	want_destroy = exit_check(val);
795135446Strhodes	UNLOCK(&val->lock);
796135446Strhodes	if (want_destroy)
797135446Strhodes		destroy(val);
798135446Strhodes}
799135446Strhodes
800165071Sdougb/*%
801225361Sdougb * Callback when the CNAME record has been validated.
802225361Sdougb *
803225361Sdougb * Resumes validation of the unsecure zone proof.
804225361Sdougb */
805225361Sdougbstatic void
806225361Sdougbcnamevalidated(isc_task_t *task, isc_event_t *event) {
807225361Sdougb	dns_validatorevent_t *devent;
808225361Sdougb	dns_validator_t *val;
809225361Sdougb	isc_boolean_t want_destroy;
810225361Sdougb	isc_result_t result;
811225361Sdougb	isc_result_t eresult;
812225361Sdougb
813225361Sdougb	UNUSED(task);
814225361Sdougb	INSIST(event->ev_type == DNS_EVENT_VALIDATORDONE);
815225361Sdougb
816225361Sdougb	devent = (dns_validatorevent_t *)event;
817225361Sdougb	val = devent->ev_arg;
818225361Sdougb	eresult = devent->result;
819225361Sdougb
820225361Sdougb	isc_event_free(&event);
821225361Sdougb	dns_validator_destroy(&val->subvalidator);
822225361Sdougb
823225361Sdougb	INSIST(val->event != NULL);
824225361Sdougb	INSIST((val->attributes & VALATTR_INSECURITY) != 0);
825225361Sdougb
826225361Sdougb	validator_log(val, ISC_LOG_DEBUG(3), "in cnamevalidated");
827225361Sdougb	LOCK(&val->lock);
828225361Sdougb	if (CANCELED(val)) {
829225361Sdougb		validator_done(val, ISC_R_CANCELED);
830225361Sdougb	} else if (eresult == ISC_R_SUCCESS) {
831225361Sdougb		validator_log(val, ISC_LOG_DEBUG(3), "cname with trust %s",
832225361Sdougb			      dns_trust_totext(val->frdataset.trust));
833225361Sdougb		result = proveunsecure(val, ISC_FALSE, ISC_TRUE);
834225361Sdougb		if (result != DNS_R_WAIT)
835225361Sdougb			validator_done(val, result);
836225361Sdougb	} else {
837225361Sdougb		if (eresult != DNS_R_BROKENCHAIN) {
838225361Sdougb			if (dns_rdataset_isassociated(&val->frdataset))
839225361Sdougb				dns_rdataset_expire(&val->frdataset);
840225361Sdougb			if (dns_rdataset_isassociated(&val->fsigrdataset))
841225361Sdougb				dns_rdataset_expire(&val->fsigrdataset);
842225361Sdougb		}
843225361Sdougb		validator_log(val, ISC_LOG_DEBUG(3),
844225361Sdougb			      "cnamevalidated: got %s",
845225361Sdougb			      isc_result_totext(eresult));
846225361Sdougb		validator_done(val, DNS_R_BROKENCHAIN);
847225361Sdougb	}
848225361Sdougb	want_destroy = exit_check(val);
849225361Sdougb	UNLOCK(&val->lock);
850225361Sdougb	if (want_destroy)
851225361Sdougb		destroy(val);
852225361Sdougb}
853225361Sdougb
854225361Sdougb/*%
855165071Sdougb * Callback for when NSEC records have been validated.
856165071Sdougb *
857193149Sdougb * Looks for NOQNAME, NODATA and OPTOUT proofs.
858165071Sdougb *
859165071Sdougb * Resumes nsecvalidate.
860165071Sdougb */
861135446Strhodesstatic void
862135446Strhodesauthvalidated(isc_task_t *task, isc_event_t *event) {
863135446Strhodes	dns_validatorevent_t *devent;
864135446Strhodes	dns_validator_t *val;
865153816Sdougb	dns_rdataset_t *rdataset;
866135446Strhodes	isc_boolean_t want_destroy;
867135446Strhodes	isc_result_t result;
868135446Strhodes	isc_boolean_t exists, data;
869135446Strhodes
870135446Strhodes	UNUSED(task);
871135446Strhodes	INSIST(event->ev_type == DNS_EVENT_VALIDATORDONE);
872135446Strhodes
873135446Strhodes	devent = (dns_validatorevent_t *)event;
874135446Strhodes	rdataset = devent->rdataset;
875135446Strhodes	val = devent->ev_arg;
876135446Strhodes	result = devent->result;
877135446Strhodes	dns_validator_destroy(&val->subvalidator);
878135446Strhodes
879135446Strhodes	INSIST(val->event != NULL);
880135446Strhodes
881135446Strhodes	validator_log(val, ISC_LOG_DEBUG(3), "in authvalidated");
882135446Strhodes	LOCK(&val->lock);
883174187Sdougb	if (CANCELED(val)) {
884174187Sdougb		validator_done(val, ISC_R_CANCELED);
885174187Sdougb	} else if (result != ISC_R_SUCCESS) {
886135446Strhodes		validator_log(val, ISC_LOG_DEBUG(3),
887135446Strhodes			      "authvalidated: got %s",
888135446Strhodes			      isc_result_totext(result));
889205292Sdougb		if (result == DNS_R_BROKENCHAIN)
890205292Sdougb			val->authfail++;
891135446Strhodes		if (result == ISC_R_CANCELED)
892135446Strhodes			validator_done(val, result);
893135446Strhodes		else {
894135446Strhodes			result = nsecvalidate(val, ISC_TRUE);
895135446Strhodes			if (result != DNS_R_WAIT)
896135446Strhodes				validator_done(val, result);
897135446Strhodes		}
898135446Strhodes	} else {
899135446Strhodes		dns_name_t **proofs = val->event->proofs;
900170222Sdougb		dns_name_t *wild = dns_fixedname_name(&val->wild);
901186462Sdougb
902135446Strhodes		if (rdataset->trust == dns_trust_secure)
903135446Strhodes			val->seensig = ISC_TRUE;
904135446Strhodes
905143731Sdougb		if (rdataset->type == dns_rdatatype_nsec &&
906135446Strhodes		    rdataset->trust == dns_trust_secure &&
907214586Sdougb		    (NEEDNODATA(val) || NEEDNOQNAME(val)) &&
908214586Sdougb		    !FOUNDNODATA(val) && !FOUNDNOQNAME(val) &&
909254402Serwin		    dns_nsec_noexistnodata(val->event->type, val->event->name,
910254402Serwin					   devent->name, rdataset, &exists,
911254402Serwin					   &data, wild, validator_log, val)
912135446Strhodes				      == ISC_R_SUCCESS)
913170222Sdougb		{
914135446Strhodes			if (exists && !data) {
915135446Strhodes				val->attributes |= VALATTR_FOUNDNODATA;
916135446Strhodes				if (NEEDNODATA(val))
917135446Strhodes					proofs[DNS_VALIDATOR_NODATAPROOF] =
918135446Strhodes						devent->name;
919135446Strhodes			}
920135446Strhodes			if (!exists) {
921135446Strhodes				val->attributes |= VALATTR_FOUNDNOQNAME;
922193149Sdougb				val->attributes |= VALATTR_FOUNDCLOSEST;
923193149Sdougb				/*
924193149Sdougb				 * The NSEC noqname proof also contains
925193149Sdougb				 * the closest encloser.
926193149Sdougb
927193149Sdougb				 */
928135446Strhodes				if (NEEDNOQNAME(val))
929135446Strhodes					proofs[DNS_VALIDATOR_NOQNAMEPROOF] =
930135446Strhodes						devent->name;
931135446Strhodes			}
932135446Strhodes		}
933193149Sdougb
934135446Strhodes		result = nsecvalidate(val, ISC_TRUE);
935135446Strhodes		if (result != DNS_R_WAIT)
936135446Strhodes			validator_done(val, result);
937135446Strhodes	}
938135446Strhodes	want_destroy = exit_check(val);
939135446Strhodes	UNLOCK(&val->lock);
940135446Strhodes	if (want_destroy)
941135446Strhodes		destroy(val);
942135446Strhodes
943135446Strhodes	/*
944135446Strhodes	 * Free stuff from the event.
945135446Strhodes	 */
946135446Strhodes	isc_event_free(&event);
947135446Strhodes}
948135446Strhodes
949165071Sdougb/*%
950165071Sdougb * Looks for the requested name and type in the view (zones and cache).
951165071Sdougb *
952165071Sdougb * When looking for a DLV record also checks to make sure the NSEC record
953165071Sdougb * returns covers the query name as part of aggressive negative caching.
954165071Sdougb *
955165071Sdougb * Returns:
956165071Sdougb * \li	ISC_R_SUCCESS
957165071Sdougb * \li	ISC_R_NOTFOUND
958165071Sdougb * \li	DNS_R_NCACHENXDOMAIN
959165071Sdougb * \li	DNS_R_NCACHENXRRSET
960165071Sdougb * \li	DNS_R_NXRRSET
961165071Sdougb * \li	DNS_R_NXDOMAIN
962205292Sdougb * \li	DNS_R_BROKENCHAIN
963165071Sdougb */
964135446Strhodesstatic inline isc_result_t
965135446Strhodesview_find(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type) {
966135446Strhodes	dns_fixedname_t fixedname;
967135446Strhodes	dns_name_t *foundname;
968135446Strhodes	dns_rdata_nsec_t nsec;
969135446Strhodes	dns_rdata_t rdata = DNS_RDATA_INIT;
970135446Strhodes	isc_result_t result;
971135446Strhodes	unsigned int options;
972205292Sdougb	isc_time_t now;
973135446Strhodes	char buf1[DNS_NAME_FORMATSIZE];
974135446Strhodes	char buf2[DNS_NAME_FORMATSIZE];
975135446Strhodes	char buf3[DNS_NAME_FORMATSIZE];
976205292Sdougb	char namebuf[DNS_NAME_FORMATSIZE];
977205292Sdougb	char typebuf[DNS_RDATATYPE_FORMATSIZE];
978135446Strhodes
979135446Strhodes	if (dns_rdataset_isassociated(&val->frdataset))
980135446Strhodes		dns_rdataset_disassociate(&val->frdataset);
981135446Strhodes	if (dns_rdataset_isassociated(&val->fsigrdataset))
982135446Strhodes		dns_rdataset_disassociate(&val->fsigrdataset);
983135446Strhodes
984205292Sdougb	if (isc_time_now(&now) == ISC_R_SUCCESS &&
985205292Sdougb	    dns_resolver_getbadcache(val->view->resolver, name, type, &now)) {
986205292Sdougb
987205292Sdougb		dns_name_format(name, namebuf, sizeof(namebuf));
988205292Sdougb		dns_rdatatype_format(type, typebuf, sizeof(typebuf));
989205292Sdougb		validator_log(val, ISC_LOG_INFO, "bad cache hit (%s/%s)",
990205292Sdougb			      namebuf, typebuf);
991205292Sdougb		return (DNS_R_BROKENCHAIN);
992205292Sdougb	}
993205292Sdougb
994135446Strhodes	options = DNS_DBFIND_PENDINGOK;
995135446Strhodes	if (type == dns_rdatatype_dlv)
996135446Strhodes		options |= DNS_DBFIND_COVERINGNSEC;
997135446Strhodes	dns_fixedname_init(&fixedname);
998135446Strhodes	foundname = dns_fixedname_name(&fixedname);
999135446Strhodes	result = dns_view_find(val->view, name, type, 0, options,
1000135446Strhodes			       ISC_FALSE, NULL, NULL, foundname,
1001135446Strhodes			       &val->frdataset, &val->fsigrdataset);
1002205292Sdougb
1003135446Strhodes	if (result == DNS_R_NXDOMAIN) {
1004135446Strhodes		if (dns_rdataset_isassociated(&val->frdataset))
1005135446Strhodes			dns_rdataset_disassociate(&val->frdataset);
1006135446Strhodes		if (dns_rdataset_isassociated(&val->fsigrdataset))
1007135446Strhodes			dns_rdataset_disassociate(&val->fsigrdataset);
1008135446Strhodes	} else if (result == DNS_R_COVERINGNSEC) {
1009135446Strhodes		validator_log(val, ISC_LOG_DEBUG(3), "DNS_R_COVERINGNSEC");
1010135446Strhodes		/*
1011135446Strhodes		 * Check if the returned NSEC covers the name.
1012135446Strhodes		 */
1013135446Strhodes		INSIST(type == dns_rdatatype_dlv);
1014135446Strhodes		if (val->frdataset.trust != dns_trust_secure) {
1015135446Strhodes			validator_log(val, ISC_LOG_DEBUG(3),
1016222395Sdougb				      "covering nsec: trust %s",
1017222395Sdougb				      dns_trust_totext(val->frdataset.trust));
1018135446Strhodes			goto notfound;
1019135446Strhodes		}
1020135446Strhodes		result = dns_rdataset_first(&val->frdataset);
1021135446Strhodes		if (result != ISC_R_SUCCESS)
1022135446Strhodes			goto notfound;
1023135446Strhodes		dns_rdataset_current(&val->frdataset, &rdata);
1024135446Strhodes		if (dns_nsec_typepresent(&rdata, dns_rdatatype_ns) &&
1025135446Strhodes		    !dns_nsec_typepresent(&rdata, dns_rdatatype_soa)) {
1026135446Strhodes			/* Parent NSEC record. */
1027135446Strhodes			if (dns_name_issubdomain(name, foundname)) {
1028135446Strhodes				validator_log(val, ISC_LOG_DEBUG(3),
1029135446Strhodes					      "covering nsec: for parent");
1030135446Strhodes				goto notfound;
1031135446Strhodes			}
1032135446Strhodes		}
1033135446Strhodes		result = dns_rdata_tostruct(&rdata, &nsec, NULL);
1034135446Strhodes		if (result != ISC_R_SUCCESS)
1035135446Strhodes			goto notfound;
1036135446Strhodes		if (dns_name_compare(foundname, &nsec.next) >= 0) {
1037135446Strhodes			/* End of zone chain. */
1038135446Strhodes			if (!dns_name_issubdomain(name, &nsec.next)) {
1039135446Strhodes				/*
1040186462Sdougb				 * XXXMPA We could look for a parent NSEC
1041135446Strhodes				 * at nsec.next and if found retest with
1042135446Strhodes				 * this NSEC.
1043135446Strhodes				 */
1044135446Strhodes				dns_rdata_freestruct(&nsec);
1045135446Strhodes				validator_log(val, ISC_LOG_DEBUG(3),
1046135446Strhodes					      "covering nsec: not in zone");
1047135446Strhodes				goto notfound;
1048135446Strhodes			}
1049135446Strhodes		} else if (dns_name_compare(name, &nsec.next) >= 0) {
1050135446Strhodes			/*
1051135446Strhodes			 * XXXMPA We could check if this NSEC is at a zone
1052135446Strhodes			 * apex and if the qname is not below it and look for
1053135446Strhodes			 * a parent NSEC with the same name.  This requires
1054135446Strhodes			 * that we can cache both NSEC records which we
1055135446Strhodes			 * currently don't support.
1056135446Strhodes			 */
1057135446Strhodes			dns_rdata_freestruct(&nsec);
1058135446Strhodes			validator_log(val, ISC_LOG_DEBUG(3),
1059135446Strhodes				      "covering nsec: not in range");
1060135446Strhodes			goto notfound;
1061135446Strhodes		}
1062135446Strhodes		if (isc_log_wouldlog(dns_lctx,ISC_LOG_DEBUG(3))) {
1063135446Strhodes			dns_name_format(name, buf1, sizeof buf1);
1064135446Strhodes			dns_name_format(foundname, buf2, sizeof buf2);
1065135446Strhodes			dns_name_format(&nsec.next, buf3, sizeof buf3);
1066135446Strhodes			validator_log(val, ISC_LOG_DEBUG(3),
1067135446Strhodes				      "covering nsec found: '%s' '%s' '%s'",
1068135446Strhodes				      buf1, buf2, buf3);
1069135446Strhodes		}
1070135446Strhodes		if (dns_rdataset_isassociated(&val->frdataset))
1071135446Strhodes			dns_rdataset_disassociate(&val->frdataset);
1072135446Strhodes		if (dns_rdataset_isassociated(&val->fsigrdataset))
1073135446Strhodes			dns_rdataset_disassociate(&val->fsigrdataset);
1074135446Strhodes		dns_rdata_freestruct(&nsec);
1075135446Strhodes		result = DNS_R_NCACHENXDOMAIN;
1076135446Strhodes	} else if (result != ISC_R_SUCCESS &&
1077186462Sdougb		   result != DNS_R_NCACHENXDOMAIN &&
1078186462Sdougb		   result != DNS_R_NCACHENXRRSET &&
1079186462Sdougb		   result != DNS_R_EMPTYNAME &&
1080186462Sdougb		   result != DNS_R_NXRRSET &&
1081186462Sdougb		   result != ISC_R_NOTFOUND) {
1082135446Strhodes		goto  notfound;
1083135446Strhodes	}
1084135446Strhodes	return (result);
1085135446Strhodes
1086135446Strhodes notfound:
1087135446Strhodes	if (dns_rdataset_isassociated(&val->frdataset))
1088135446Strhodes		dns_rdataset_disassociate(&val->frdataset);
1089135446Strhodes	if (dns_rdataset_isassociated(&val->fsigrdataset))
1090135446Strhodes		dns_rdataset_disassociate(&val->fsigrdataset);
1091135446Strhodes	return (ISC_R_NOTFOUND);
1092135446Strhodes}
1093135446Strhodes
1094165071Sdougb/*%
1095165071Sdougb * Checks to make sure we are not going to loop.  As we use a SHARED fetch
1096165071Sdougb * the validation process will stall if looping was to occur.
1097165071Sdougb */
1098135446Strhodesstatic inline isc_boolean_t
1099193149Sdougbcheck_deadlock(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type,
1100193149Sdougb	       dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
1101193149Sdougb{
1102135446Strhodes	dns_validator_t *parent;
1103135446Strhodes
1104165071Sdougb	for (parent = val; parent != NULL; parent = parent->parent) {
1105135446Strhodes		if (parent->event != NULL &&
1106135446Strhodes		    parent->event->type == type &&
1107193149Sdougb		    dns_name_equal(parent->event->name, name) &&
1108193149Sdougb		    /*
1109193149Sdougb		     * As NSEC3 records are meta data you sometimes
1110193149Sdougb		     * need to prove a NSEC3 record which says that
1111193149Sdougb		     * itself doesn't exist.
1112193149Sdougb		     */
1113193149Sdougb		    (parent->event->type != dns_rdatatype_nsec3 ||
1114193149Sdougb		     rdataset == NULL || sigrdataset == NULL ||
1115193149Sdougb		     parent->event->message == NULL ||
1116193149Sdougb		     parent->event->rdataset != NULL ||
1117193149Sdougb		     parent->event->sigrdataset != NULL))
1118135446Strhodes		{
1119135446Strhodes			validator_log(val, ISC_LOG_DEBUG(3),
1120135446Strhodes				      "continuing validation would lead to "
1121135446Strhodes				      "deadlock: aborting validation");
1122135446Strhodes			return (ISC_TRUE);
1123135446Strhodes		}
1124135446Strhodes	}
1125135446Strhodes	return (ISC_FALSE);
1126135446Strhodes}
1127135446Strhodes
1128165071Sdougb/*%
1129165071Sdougb * Start a fetch for the requested name and type.
1130165071Sdougb */
1131135446Strhodesstatic inline isc_result_t
1132135446Strhodescreate_fetch(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type,
1133135446Strhodes	     isc_taskaction_t callback, const char *caller)
1134135446Strhodes{
1135135446Strhodes	if (dns_rdataset_isassociated(&val->frdataset))
1136135446Strhodes		dns_rdataset_disassociate(&val->frdataset);
1137135446Strhodes	if (dns_rdataset_isassociated(&val->fsigrdataset))
1138135446Strhodes		dns_rdataset_disassociate(&val->fsigrdataset);
1139135446Strhodes
1140224092Sdougb	if (check_deadlock(val, name, type, NULL, NULL)) {
1141224092Sdougb		validator_log(val, ISC_LOG_DEBUG(3),
1142224092Sdougb			      "deadlock found (create_fetch)");
1143135446Strhodes		return (DNS_R_NOVALIDSIG);
1144224092Sdougb	}
1145135446Strhodes
1146135446Strhodes	validator_logcreate(val, name, type, caller, "fetch");
1147135446Strhodes	return (dns_resolver_createfetch(val->view->resolver, name, type,
1148135446Strhodes					 NULL, NULL, NULL, 0,
1149135446Strhodes					 val->event->ev_sender,
1150135446Strhodes					 callback, val,
1151135446Strhodes					 &val->frdataset,
1152135446Strhodes					 &val->fsigrdataset,
1153135446Strhodes					 &val->fetch));
1154135446Strhodes}
1155135446Strhodes
1156165071Sdougb/*%
1157165071Sdougb * Start a subvalidation process.
1158165071Sdougb */
1159135446Strhodesstatic inline isc_result_t
1160135446Strhodescreate_validator(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type,
1161135446Strhodes		 dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset,
1162135446Strhodes		 isc_taskaction_t action, const char *caller)
1163135446Strhodes{
1164135446Strhodes	isc_result_t result;
1165135446Strhodes
1166224092Sdougb	if (check_deadlock(val, name, type, rdataset, sigrdataset)) {
1167224092Sdougb		validator_log(val, ISC_LOG_DEBUG(3),
1168224092Sdougb			      "deadlock found (create_validator)");
1169135446Strhodes		return (DNS_R_NOVALIDSIG);
1170224092Sdougb	}
1171135446Strhodes
1172135446Strhodes	validator_logcreate(val, name, type, caller, "validator");
1173135446Strhodes	result = dns_validator_create(val->view, name, type,
1174135446Strhodes				      rdataset, sigrdataset, NULL, 0,
1175135446Strhodes				      val->task, action, val,
1176135446Strhodes				      &val->subvalidator);
1177153816Sdougb	if (result == ISC_R_SUCCESS) {
1178135446Strhodes		val->subvalidator->parent = val;
1179153816Sdougb		val->subvalidator->depth = val->depth + 1;
1180153816Sdougb	}
1181135446Strhodes	return (result);
1182135446Strhodes}
1183135446Strhodes
1184165071Sdougb/*%
1185135446Strhodes * Try to find a key that could have signed 'siginfo' among those
1186135446Strhodes * in 'rdataset'.  If found, build a dst_key_t for it and point
1187135446Strhodes * val->key at it.
1188135446Strhodes *
1189135446Strhodes * If val->key is non-NULL, this returns the next matching key.
1190135446Strhodes */
1191135446Strhodesstatic isc_result_t
1192135446Strhodesget_dst_key(dns_validator_t *val, dns_rdata_rrsig_t *siginfo,
1193135446Strhodes	    dns_rdataset_t *rdataset)
1194135446Strhodes{
1195135446Strhodes	isc_result_t result;
1196135446Strhodes	isc_buffer_t b;
1197135446Strhodes	dns_rdata_t rdata = DNS_RDATA_INIT;
1198135446Strhodes	dst_key_t *oldkey = val->key;
1199135446Strhodes	isc_boolean_t foundold;
1200135446Strhodes
1201135446Strhodes	if (oldkey == NULL)
1202135446Strhodes		foundold = ISC_TRUE;
1203135446Strhodes	else {
1204135446Strhodes		foundold = ISC_FALSE;
1205135446Strhodes		val->key = NULL;
1206135446Strhodes	}
1207135446Strhodes
1208135446Strhodes	result = dns_rdataset_first(rdataset);
1209135446Strhodes	if (result != ISC_R_SUCCESS)
1210135446Strhodes		goto failure;
1211135446Strhodes	do {
1212135446Strhodes		dns_rdataset_current(rdataset, &rdata);
1213135446Strhodes
1214135446Strhodes		isc_buffer_init(&b, rdata.data, rdata.length);
1215135446Strhodes		isc_buffer_add(&b, rdata.length);
1216135446Strhodes		INSIST(val->key == NULL);
1217135446Strhodes		result = dst_key_fromdns(&siginfo->signer, rdata.rdclass, &b,
1218135446Strhodes					 val->view->mctx, &val->key);
1219135446Strhodes		if (result != ISC_R_SUCCESS)
1220135446Strhodes			goto failure;
1221135446Strhodes		if (siginfo->algorithm ==
1222135446Strhodes		    (dns_secalg_t)dst_key_alg(val->key) &&
1223135446Strhodes		    siginfo->keyid ==
1224135446Strhodes		    (dns_keytag_t)dst_key_id(val->key) &&
1225135446Strhodes		    dst_key_iszonekey(val->key))
1226135446Strhodes		{
1227135446Strhodes			if (foundold)
1228135446Strhodes				/*
1229135446Strhodes				 * This is the key we're looking for.
1230135446Strhodes				 */
1231135446Strhodes				return (ISC_R_SUCCESS);
1232135446Strhodes			else if (dst_key_compare(oldkey, val->key) == ISC_TRUE)
1233135446Strhodes			{
1234135446Strhodes				foundold = ISC_TRUE;
1235135446Strhodes				dst_key_free(&oldkey);
1236135446Strhodes			}
1237135446Strhodes		}
1238135446Strhodes		dst_key_free(&val->key);
1239135446Strhodes		dns_rdata_reset(&rdata);
1240135446Strhodes		result = dns_rdataset_next(rdataset);
1241135446Strhodes	} while (result == ISC_R_SUCCESS);
1242135446Strhodes	if (result == ISC_R_NOMORE)
1243135446Strhodes		result = ISC_R_NOTFOUND;
1244135446Strhodes
1245135446Strhodes failure:
1246135446Strhodes	if (oldkey != NULL)
1247135446Strhodes		dst_key_free(&oldkey);
1248135446Strhodes
1249135446Strhodes	return (result);
1250135446Strhodes}
1251135446Strhodes
1252165071Sdougb/*%
1253193149Sdougb * Get the key that generated this signature.
1254165071Sdougb */
1255135446Strhodesstatic isc_result_t
1256135446Strhodesget_key(dns_validator_t *val, dns_rdata_rrsig_t *siginfo) {
1257135446Strhodes	isc_result_t result;
1258135446Strhodes	unsigned int nlabels;
1259135446Strhodes	int order;
1260135446Strhodes	dns_namereln_t namereln;
1261135446Strhodes
1262135446Strhodes	/*
1263135446Strhodes	 * Is the signer name appropriate for this signature?
1264135446Strhodes	 *
1265135446Strhodes	 * The signer name must be at the same level as the owner name
1266193149Sdougb	 * or closer to the DNS root.
1267135446Strhodes	 */
1268135446Strhodes	namereln = dns_name_fullcompare(val->event->name, &siginfo->signer,
1269135446Strhodes					&order, &nlabels);
1270135446Strhodes	if (namereln != dns_namereln_subdomain &&
1271135446Strhodes	    namereln != dns_namereln_equal)
1272135446Strhodes		return (DNS_R_CONTINUE);
1273135446Strhodes
1274135446Strhodes	if (namereln == dns_namereln_equal) {
1275135446Strhodes		/*
1276135446Strhodes		 * If this is a self-signed keyset, it must not be a zone key
1277135446Strhodes		 * (since get_key is not called from validatezonekey).
1278135446Strhodes		 */
1279135446Strhodes		if (val->event->rdataset->type == dns_rdatatype_dnskey)
1280135446Strhodes			return (DNS_R_CONTINUE);
1281135446Strhodes
1282135446Strhodes		/*
1283135446Strhodes		 * Records appearing in the parent zone at delegation
1284135446Strhodes		 * points cannot be self-signed.
1285135446Strhodes		 */
1286135446Strhodes		if (dns_rdatatype_atparent(val->event->rdataset->type))
1287135446Strhodes			return (DNS_R_CONTINUE);
1288193149Sdougb	} else {
1289193149Sdougb		/*
1290193149Sdougb		 * SOA and NS RRsets can only be signed by a key with
1291193149Sdougb		 * the same name.
1292193149Sdougb		 */
1293193149Sdougb		if (val->event->rdataset->type == dns_rdatatype_soa ||
1294193149Sdougb		    val->event->rdataset->type == dns_rdatatype_ns) {
1295193149Sdougb			const char *typename;
1296193149Sdougb
1297193149Sdougb			if (val->event->rdataset->type == dns_rdatatype_soa)
1298193149Sdougb				typename = "SOA";
1299193149Sdougb			else
1300193149Sdougb				typename = "NS";
1301193149Sdougb			validator_log(val, ISC_LOG_DEBUG(3),
1302193149Sdougb				      "%s signer mismatch", typename);
1303193149Sdougb			return (DNS_R_CONTINUE);
1304193149Sdougb		}
1305135446Strhodes	}
1306135446Strhodes
1307135446Strhodes	/*
1308135446Strhodes	 * Do we know about this key?
1309135446Strhodes	 */
1310135446Strhodes	result = view_find(val, &siginfo->signer, dns_rdatatype_dnskey);
1311135446Strhodes	if (result == ISC_R_SUCCESS) {
1312135446Strhodes		/*
1313135446Strhodes		 * We have an rrset for the given keyname.
1314135446Strhodes		 */
1315135446Strhodes		val->keyset = &val->frdataset;
1316214586Sdougb		if ((DNS_TRUST_PENDING(val->frdataset.trust) ||
1317214586Sdougb		     DNS_TRUST_ANSWER(val->frdataset.trust)) &&
1318135446Strhodes		    dns_rdataset_isassociated(&val->fsigrdataset))
1319135446Strhodes		{
1320135446Strhodes			/*
1321214586Sdougb			 * We know the key but haven't validated it yet or
1322214586Sdougb			 * we have a key of trust answer but a DS/DLV
1323214586Sdougb			 * record for the zone may have been added.
1324135446Strhodes			 */
1325135446Strhodes			result = create_validator(val, &siginfo->signer,
1326135446Strhodes						  dns_rdatatype_dnskey,
1327135446Strhodes						  &val->frdataset,
1328135446Strhodes						  &val->fsigrdataset,
1329135446Strhodes						  keyvalidated,
1330135446Strhodes						  "get_key");
1331135446Strhodes			if (result != ISC_R_SUCCESS)
1332135446Strhodes				return (result);
1333135446Strhodes			return (DNS_R_WAIT);
1334199958Sdougb		} else if (DNS_TRUST_PENDING(val->frdataset.trust)) {
1335135446Strhodes			/*
1336135446Strhodes			 * Having a pending key with no signature means that
1337135446Strhodes			 * something is broken.
1338135446Strhodes			 */
1339135446Strhodes			result = DNS_R_CONTINUE;
1340135446Strhodes		} else if (val->frdataset.trust < dns_trust_secure) {
1341135446Strhodes			/*
1342135446Strhodes			 * The key is legitimately insecure.  There's no
1343135446Strhodes			 * point in even attempting verification.
1344135446Strhodes			 */
1345135446Strhodes			val->key = NULL;
1346135446Strhodes			result = ISC_R_SUCCESS;
1347135446Strhodes		} else {
1348135446Strhodes			/*
1349135446Strhodes			 * See if we've got the key used in the signature.
1350135446Strhodes			 */
1351135446Strhodes			validator_log(val, ISC_LOG_DEBUG(3),
1352222395Sdougb				      "keyset with trust %s",
1353222395Sdougb				      dns_trust_totext(val->frdataset.trust));
1354135446Strhodes			result = get_dst_key(val, siginfo, val->keyset);
1355135446Strhodes			if (result != ISC_R_SUCCESS) {
1356135446Strhodes				/*
1357135446Strhodes				 * Either the key we're looking for is not
1358135446Strhodes				 * in the rrset, or something bad happened.
1359135446Strhodes				 * Give up.
1360135446Strhodes				 */
1361135446Strhodes				result = DNS_R_CONTINUE;
1362135446Strhodes			}
1363135446Strhodes		}
1364135446Strhodes	} else if (result == ISC_R_NOTFOUND) {
1365135446Strhodes		/*
1366135446Strhodes		 * We don't know anything about this key.
1367135446Strhodes		 */
1368205292Sdougb		result = create_fetch(val, &siginfo->signer,
1369205292Sdougb				      dns_rdatatype_dnskey,
1370135446Strhodes				      fetch_callback_validator, "get_key");
1371135446Strhodes		if (result != ISC_R_SUCCESS)
1372135446Strhodes			return (result);
1373135446Strhodes		return (DNS_R_WAIT);
1374135446Strhodes	} else if (result ==  DNS_R_NCACHENXDOMAIN ||
1375135446Strhodes		   result == DNS_R_NCACHENXRRSET ||
1376186462Sdougb		   result == DNS_R_EMPTYNAME ||
1377135446Strhodes		   result == DNS_R_NXDOMAIN ||
1378135446Strhodes		   result == DNS_R_NXRRSET)
1379135446Strhodes	{
1380135446Strhodes		/*
1381135446Strhodes		 * This key doesn't exist.
1382135446Strhodes		 */
1383135446Strhodes		result = DNS_R_CONTINUE;
1384205292Sdougb	} else if (result == DNS_R_BROKENCHAIN)
1385205292Sdougb		return (result);
1386135446Strhodes
1387135446Strhodes	if (dns_rdataset_isassociated(&val->frdataset) &&
1388135446Strhodes	    val->keyset != &val->frdataset)
1389135446Strhodes		dns_rdataset_disassociate(&val->frdataset);
1390135446Strhodes	if (dns_rdataset_isassociated(&val->fsigrdataset))
1391135446Strhodes		dns_rdataset_disassociate(&val->fsigrdataset);
1392135446Strhodes
1393135446Strhodes	return (result);
1394135446Strhodes}
1395135446Strhodes
1396135446Strhodesstatic dns_keytag_t
1397135446Strhodescompute_keytag(dns_rdata_t *rdata, dns_rdata_dnskey_t *key) {
1398135446Strhodes	isc_region_t r;
1399135446Strhodes
1400135446Strhodes	dns_rdata_toregion(rdata, &r);
1401135446Strhodes	return (dst_region_computeid(&r, key->algorithm));
1402135446Strhodes}
1403135446Strhodes
1404165071Sdougb/*%
1405135446Strhodes * Is this keyset self-signed?
1406135446Strhodes */
1407135446Strhodesstatic isc_boolean_t
1408135446Strhodesisselfsigned(dns_validator_t *val) {
1409135446Strhodes	dns_rdataset_t *rdataset, *sigrdataset;
1410135446Strhodes	dns_rdata_t rdata = DNS_RDATA_INIT;
1411135446Strhodes	dns_rdata_t sigrdata = DNS_RDATA_INIT;
1412135446Strhodes	dns_rdata_dnskey_t key;
1413135446Strhodes	dns_rdata_rrsig_t sig;
1414135446Strhodes	dns_keytag_t keytag;
1415224092Sdougb	dns_name_t *name;
1416135446Strhodes	isc_result_t result;
1417224092Sdougb	dst_key_t *dstkey;
1418224092Sdougb	isc_mem_t *mctx;
1419224092Sdougb	isc_boolean_t answer = ISC_FALSE;
1420135446Strhodes
1421135446Strhodes	rdataset = val->event->rdataset;
1422135446Strhodes	sigrdataset = val->event->sigrdataset;
1423224092Sdougb	name = val->event->name;
1424224092Sdougb	mctx = val->view->mctx;
1425135446Strhodes
1426254402Serwin	if (rdataset->type == dns_rdatatype_cname ||
1427254402Serwin	    rdataset->type == dns_rdatatype_dname)
1428254402Serwin		return (answer);
1429254402Serwin
1430135446Strhodes	INSIST(rdataset->type == dns_rdatatype_dnskey);
1431135446Strhodes
1432135446Strhodes	for (result = dns_rdataset_first(rdataset);
1433135446Strhodes	     result == ISC_R_SUCCESS;
1434135446Strhodes	     result = dns_rdataset_next(rdataset))
1435135446Strhodes	{
1436135446Strhodes		dns_rdata_reset(&rdata);
1437135446Strhodes		dns_rdataset_current(rdataset, &rdata);
1438186462Sdougb		result = dns_rdata_tostruct(&rdata, &key, NULL);
1439186462Sdougb		RUNTIME_CHECK(result == ISC_R_SUCCESS);
1440135446Strhodes		keytag = compute_keytag(&rdata, &key);
1441135446Strhodes		for (result = dns_rdataset_first(sigrdataset);
1442135446Strhodes		     result == ISC_R_SUCCESS;
1443135446Strhodes		     result = dns_rdataset_next(sigrdataset))
1444135446Strhodes		{
1445135446Strhodes			dns_rdata_reset(&sigrdata);
1446135446Strhodes			dns_rdataset_current(sigrdataset, &sigrdata);
1447186462Sdougb			result = dns_rdata_tostruct(&sigrdata, &sig, NULL);
1448186462Sdougb			RUNTIME_CHECK(result == ISC_R_SUCCESS);
1449135446Strhodes
1450224092Sdougb			if (sig.algorithm != key.algorithm ||
1451224092Sdougb			    sig.keyid != keytag ||
1452224092Sdougb			    !dns_name_equal(name, &sig.signer))
1453224092Sdougb				continue;
1454224092Sdougb
1455224092Sdougb			dstkey = NULL;
1456224092Sdougb			result = dns_dnssec_keyfromrdata(name, &rdata, mctx,
1457224092Sdougb							 &dstkey);
1458224092Sdougb			if (result != ISC_R_SUCCESS)
1459224092Sdougb				continue;
1460224092Sdougb
1461254897Serwin			result = dns_dnssec_verify3(name, rdataset, dstkey,
1462254897Serwin						    ISC_TRUE,
1463254897Serwin						    val->view->maxbits,
1464285258Sdelphij						    mctx, &sigrdata, NULL);
1465224092Sdougb			dst_key_free(&dstkey);
1466224092Sdougb			if (result != ISC_R_SUCCESS)
1467224092Sdougb				continue;
1468224092Sdougb			if ((key.flags & DNS_KEYFLAG_REVOKE) == 0) {
1469224092Sdougb				answer = ISC_TRUE;
1470224092Sdougb				continue;
1471224092Sdougb			}
1472224092Sdougb			dns_view_untrust(val->view, name, &key, mctx);
1473135446Strhodes		}
1474135446Strhodes	}
1475224092Sdougb	return (answer);
1476135446Strhodes}
1477135446Strhodes
1478165071Sdougb/*%
1479165071Sdougb * Attempt to verify the rdataset using the given key and rdata (RRSIG).
1480165071Sdougb * The signature was good and from a wildcard record and the QNAME does
1481165071Sdougb * not match the wildcard we need to look for a NOQNAME proof.
1482165071Sdougb *
1483165071Sdougb * Returns:
1484165071Sdougb * \li	ISC_R_SUCCESS if the verification succeeds.
1485165071Sdougb * \li	Others if the verification fails.
1486165071Sdougb */
1487135446Strhodesstatic isc_result_t
1488165071Sdougbverify(dns_validator_t *val, dst_key_t *key, dns_rdata_t *rdata,
1489165071Sdougb       isc_uint16_t keyid)
1490165071Sdougb{
1491135446Strhodes	isc_result_t result;
1492135446Strhodes	dns_fixedname_t fixed;
1493170222Sdougb	isc_boolean_t ignore = ISC_FALSE;
1494234010Sdougb	dns_name_t *wild;
1495135446Strhodes
1496135446Strhodes	val->attributes |= VALATTR_TRIEDVERIFY;
1497135446Strhodes	dns_fixedname_init(&fixed);
1498234010Sdougb	wild = dns_fixedname_name(&fixed);
1499170222Sdougb again:
1500254897Serwin	result = dns_dnssec_verify3(val->event->name, val->event->rdataset,
1501254897Serwin				    key, ignore, val->view->maxbits,
1502254897Serwin				    val->view->mctx, rdata, wild);
1503234010Sdougb	if ((result == DNS_R_SIGEXPIRED || result == DNS_R_SIGFUTURE) &&
1504234010Sdougb	    val->view->acceptexpired)
1505234010Sdougb	{
1506170222Sdougb		ignore = ISC_TRUE;
1507170222Sdougb		goto again;
1508170222Sdougb	}
1509170222Sdougb	if (ignore && (result == ISC_R_SUCCESS || result == DNS_R_FROMWILDCARD))
1510170222Sdougb		validator_log(val, ISC_LOG_INFO,
1511170222Sdougb			      "accepted expired %sRRSIG (keyid=%u)",
1512170222Sdougb			      (result == DNS_R_FROMWILDCARD) ?
1513170222Sdougb			      "wildcard " : "", keyid);
1514234010Sdougb	else if (result == DNS_R_SIGEXPIRED || result == DNS_R_SIGFUTURE)
1515234010Sdougb		validator_log(val, ISC_LOG_INFO,
1516234010Sdougb			      "verify failed due to bad signature (keyid=%u): "
1517234010Sdougb			      "%s", keyid, isc_result_totext(result));
1518170222Sdougb	else
1519170222Sdougb		validator_log(val, ISC_LOG_DEBUG(3),
1520170222Sdougb			      "verify rdataset (keyid=%u): %s",
1521170222Sdougb			      keyid, isc_result_totext(result));
1522135446Strhodes	if (result == DNS_R_FROMWILDCARD) {
1523234010Sdougb		if (!dns_name_equal(val->event->name, wild)) {
1524234010Sdougb			dns_name_t *closest;
1525234010Sdougb			unsigned int labels;
1526234010Sdougb
1527234010Sdougb			/*
1528234010Sdougb			 * Compute the closest encloser in case we need it
1529234010Sdougb			 * for the NSEC3 NOQNAME proof.
1530234010Sdougb			 */
1531234010Sdougb			closest = dns_fixedname_name(&val->closest);
1532234010Sdougb			dns_name_copy(wild, closest, NULL);
1533234010Sdougb			labels = dns_name_countlabels(closest) - 1;
1534234010Sdougb			dns_name_getlabelsequence(closest, 1, labels, closest);
1535135446Strhodes			val->attributes |= VALATTR_NEEDNOQNAME;
1536234010Sdougb		}
1537135446Strhodes		result = ISC_R_SUCCESS;
1538135446Strhodes	}
1539135446Strhodes	return (result);
1540135446Strhodes}
1541135446Strhodes
1542165071Sdougb/*%
1543135446Strhodes * Attempts positive response validation of a normal RRset.
1544135446Strhodes *
1545135446Strhodes * Returns:
1546165071Sdougb * \li	ISC_R_SUCCESS	Validation completed successfully
1547165071Sdougb * \li	DNS_R_WAIT	Validation has started but is waiting
1548135446Strhodes *			for an event.
1549165071Sdougb * \li	Other return codes are possible and all indicate failure.
1550135446Strhodes */
1551135446Strhodesstatic isc_result_t
1552135446Strhodesvalidate(dns_validator_t *val, isc_boolean_t resume) {
1553135446Strhodes	isc_result_t result;
1554135446Strhodes	dns_validatorevent_t *event;
1555135446Strhodes	dns_rdata_t rdata = DNS_RDATA_INIT;
1556135446Strhodes
1557135446Strhodes	/*
1558135446Strhodes	 * Caller must be holding the validator lock.
1559135446Strhodes	 */
1560135446Strhodes
1561135446Strhodes	event = val->event;
1562135446Strhodes
1563135446Strhodes	if (resume) {
1564135446Strhodes		/*
1565135446Strhodes		 * We already have a sigrdataset.
1566135446Strhodes		 */
1567135446Strhodes		result = ISC_R_SUCCESS;
1568135446Strhodes		validator_log(val, ISC_LOG_DEBUG(3), "resuming validate");
1569135446Strhodes	} else {
1570135446Strhodes		result = dns_rdataset_first(event->sigrdataset);
1571135446Strhodes	}
1572135446Strhodes
1573135446Strhodes	for (;
1574135446Strhodes	     result == ISC_R_SUCCESS;
1575135446Strhodes	     result = dns_rdataset_next(event->sigrdataset))
1576135446Strhodes	{
1577135446Strhodes		dns_rdata_reset(&rdata);
1578135446Strhodes		dns_rdataset_current(event->sigrdataset, &rdata);
1579135446Strhodes		if (val->siginfo == NULL) {
1580135446Strhodes			val->siginfo = isc_mem_get(val->view->mctx,
1581135446Strhodes						   sizeof(*val->siginfo));
1582135446Strhodes			if (val->siginfo == NULL)
1583135446Strhodes				return (ISC_R_NOMEMORY);
1584135446Strhodes		}
1585135446Strhodes		result = dns_rdata_tostruct(&rdata, val->siginfo, NULL);
1586135446Strhodes		if (result != ISC_R_SUCCESS)
1587135446Strhodes			return (result);
1588135446Strhodes
1589135446Strhodes		/*
1590135446Strhodes		 * At this point we could check that the signature algorithm
1591135446Strhodes		 * was known and "sufficiently good".
1592135446Strhodes		 */
1593135446Strhodes		if (!dns_resolver_algorithm_supported(val->view->resolver,
1594216175Sdougb						    event->name,
1595216175Sdougb						    val->siginfo->algorithm)) {
1596216175Sdougb			resume = ISC_FALSE;
1597135446Strhodes			continue;
1598216175Sdougb		}
1599135446Strhodes
1600135446Strhodes		if (!resume) {
1601135446Strhodes			result = get_key(val, val->siginfo);
1602135446Strhodes			if (result == DNS_R_CONTINUE)
1603135446Strhodes				continue; /* Try the next SIG RR. */
1604135446Strhodes			if (result != ISC_R_SUCCESS)
1605135446Strhodes				return (result);
1606135446Strhodes		}
1607135446Strhodes
1608135446Strhodes		/*
1609216175Sdougb		 * There isn't a secure DNSKEY for this signature so move
1610216175Sdougb		 * onto the next RRSIG.
1611135446Strhodes		 */
1612135446Strhodes		if (val->key == NULL) {
1613216175Sdougb			resume = ISC_FALSE;
1614216175Sdougb			continue;
1615135446Strhodes		}
1616135446Strhodes
1617135446Strhodes		do {
1618165071Sdougb			result = verify(val, val->key, &rdata,
1619165071Sdougb					val->siginfo->keyid);
1620135446Strhodes			if (result == ISC_R_SUCCESS)
1621135446Strhodes				break;
1622135446Strhodes			if (val->keynode != NULL) {
1623135446Strhodes				dns_keynode_t *nextnode = NULL;
1624135446Strhodes				result = dns_keytable_findnextkeynode(
1625135446Strhodes							val->keytable,
1626135446Strhodes							val->keynode,
1627135446Strhodes							&nextnode);
1628135446Strhodes				dns_keytable_detachkeynode(val->keytable,
1629135446Strhodes							   &val->keynode);
1630135446Strhodes				val->keynode = nextnode;
1631135446Strhodes				if (result != ISC_R_SUCCESS) {
1632135446Strhodes					val->key = NULL;
1633135446Strhodes					break;
1634135446Strhodes				}
1635135446Strhodes				val->key = dns_keynode_key(val->keynode);
1636224092Sdougb				if (val->key == NULL)
1637224092Sdougb					break;
1638135446Strhodes			} else {
1639135446Strhodes				if (get_dst_key(val, val->siginfo, val->keyset)
1640135446Strhodes				    != ISC_R_SUCCESS)
1641135446Strhodes					break;
1642135446Strhodes			}
1643135446Strhodes		} while (1);
1644135446Strhodes		if (result != ISC_R_SUCCESS)
1645135446Strhodes			validator_log(val, ISC_LOG_DEBUG(3),
1646135446Strhodes				      "failed to verify rdataset");
1647135446Strhodes		else {
1648135446Strhodes			isc_stdtime_t now;
1649135446Strhodes
1650135446Strhodes			isc_stdtime_get(&now);
1651245163Serwin			dns_rdataset_trimttl(event->rdataset,
1652245163Serwin					     event->sigrdataset,
1653245163Serwin					     val->siginfo, now,
1654245163Serwin					     val->view->acceptexpired);
1655135446Strhodes		}
1656135446Strhodes
1657135446Strhodes		if (val->keynode != NULL)
1658135446Strhodes			dns_keytable_detachkeynode(val->keytable,
1659135446Strhodes						   &val->keynode);
1660135446Strhodes		else {
1661135446Strhodes			if (val->key != NULL)
1662135446Strhodes				dst_key_free(&val->key);
1663135446Strhodes			if (val->keyset != NULL) {
1664135446Strhodes				dns_rdataset_disassociate(val->keyset);
1665135446Strhodes				val->keyset = NULL;
1666135446Strhodes			}
1667135446Strhodes		}
1668135446Strhodes		val->key = NULL;
1669214586Sdougb		if (NEEDNOQNAME(val)) {
1670135446Strhodes			if (val->event->message == NULL) {
1671135446Strhodes				validator_log(val, ISC_LOG_DEBUG(3),
1672135446Strhodes				      "no message available for noqname proof");
1673135446Strhodes				return (DNS_R_NOVALIDSIG);
1674135446Strhodes			}
1675135446Strhodes			validator_log(val, ISC_LOG_DEBUG(3),
1676135446Strhodes				      "looking for noqname proof");
1677135446Strhodes			return (nsecvalidate(val, ISC_FALSE));
1678135446Strhodes		} else if (result == ISC_R_SUCCESS) {
1679205292Sdougb			marksecure(event);
1680135446Strhodes			validator_log(val, ISC_LOG_DEBUG(3),
1681224092Sdougb				      "marking as secure, "
1682224092Sdougb				      "noqname proof not needed");
1683135446Strhodes			return (result);
1684135446Strhodes		} else {
1685135446Strhodes			validator_log(val, ISC_LOG_DEBUG(3),
1686135446Strhodes				      "verify failure: %s",
1687135446Strhodes				      isc_result_totext(result));
1688135446Strhodes			resume = ISC_FALSE;
1689135446Strhodes		}
1690135446Strhodes	}
1691135446Strhodes	if (result != ISC_R_NOMORE) {
1692135446Strhodes		validator_log(val, ISC_LOG_DEBUG(3),
1693135446Strhodes			      "failed to iterate signatures: %s",
1694135446Strhodes			      isc_result_totext(result));
1695135446Strhodes		return (result);
1696135446Strhodes	}
1697135446Strhodes
1698135446Strhodes	validator_log(val, ISC_LOG_INFO, "no valid signature found");
1699135446Strhodes	return (DNS_R_NOVALIDSIG);
1700135446Strhodes}
1701135446Strhodes
1702165071Sdougb/*%
1703224092Sdougb * Check whether this DNSKEY (keyrdata) signed the DNSKEY RRset
1704224092Sdougb * (val->event->rdataset).
1705224092Sdougb */
1706224092Sdougbstatic isc_result_t
1707224092Sdougbcheckkey(dns_validator_t *val, dns_rdata_t *keyrdata, isc_uint16_t keyid,
1708224092Sdougb	 dns_secalg_t algorithm)
1709224092Sdougb{
1710224092Sdougb	dns_rdata_rrsig_t sig;
1711224092Sdougb	dst_key_t *dstkey = NULL;
1712224092Sdougb	isc_result_t result;
1713224092Sdougb
1714224092Sdougb	for (result = dns_rdataset_first(val->event->sigrdataset);
1715224092Sdougb	     result == ISC_R_SUCCESS;
1716224092Sdougb	     result = dns_rdataset_next(val->event->sigrdataset))
1717224092Sdougb	{
1718224092Sdougb		dns_rdata_t rdata = DNS_RDATA_INIT;
1719224092Sdougb
1720224092Sdougb		dns_rdataset_current(val->event->sigrdataset, &rdata);
1721224092Sdougb		result = dns_rdata_tostruct(&rdata, &sig, NULL);
1722224092Sdougb		RUNTIME_CHECK(result == ISC_R_SUCCESS);
1723224092Sdougb		if (keyid != sig.keyid || algorithm != sig.algorithm)
1724224092Sdougb			continue;
1725224092Sdougb		if (dstkey == NULL) {
1726224092Sdougb			result = dns_dnssec_keyfromrdata(val->event->name,
1727224092Sdougb							 keyrdata,
1728224092Sdougb							 val->view->mctx,
1729224092Sdougb							 &dstkey);
1730224092Sdougb			if (result != ISC_R_SUCCESS)
1731224092Sdougb				/*
1732224092Sdougb				 * This really shouldn't happen, but...
1733224092Sdougb				 */
1734224092Sdougb				continue;
1735224092Sdougb		}
1736224092Sdougb		result = verify(val, dstkey, &rdata, sig.keyid);
1737224092Sdougb		if (result == ISC_R_SUCCESS)
1738224092Sdougb			break;
1739224092Sdougb	}
1740224092Sdougb	if (dstkey != NULL)
1741224092Sdougb		dst_key_free(&dstkey);
1742224092Sdougb	return (result);
1743224092Sdougb}
1744224092Sdougb
1745224092Sdougb/*%
1746224092Sdougb * Find the DNSKEY that corresponds to the DS.
1747224092Sdougb */
1748224092Sdougbstatic isc_result_t
1749224092Sdougbkeyfromds(dns_validator_t *val, dns_rdataset_t *rdataset, dns_rdata_t *dsrdata,
1750224092Sdougb	  isc_uint8_t digest, isc_uint16_t keyid, dns_secalg_t algorithm,
1751224092Sdougb	  dns_rdata_t *keyrdata)
1752224092Sdougb{
1753224092Sdougb	dns_keytag_t keytag;
1754224092Sdougb	dns_rdata_dnskey_t key;
1755224092Sdougb	isc_result_t result;
1756224092Sdougb	unsigned char dsbuf[DNS_DS_BUFFERSIZE];
1757224092Sdougb
1758224092Sdougb	for (result = dns_rdataset_first(rdataset);
1759224092Sdougb	     result == ISC_R_SUCCESS;
1760224092Sdougb	     result = dns_rdataset_next(rdataset))
1761224092Sdougb	{
1762224092Sdougb		dns_rdata_t newdsrdata = DNS_RDATA_INIT;
1763224092Sdougb
1764224092Sdougb		dns_rdata_reset(keyrdata);
1765224092Sdougb		dns_rdataset_current(rdataset, keyrdata);
1766224092Sdougb		result = dns_rdata_tostruct(keyrdata, &key, NULL);
1767224092Sdougb		RUNTIME_CHECK(result == ISC_R_SUCCESS);
1768224092Sdougb		keytag = compute_keytag(keyrdata, &key);
1769224092Sdougb		if (keyid != keytag || algorithm != key.algorithm)
1770224092Sdougb			continue;
1771224092Sdougb		dns_rdata_reset(&newdsrdata);
1772224092Sdougb		result = dns_ds_buildrdata(val->event->name, keyrdata, digest,
1773224092Sdougb					   dsbuf, &newdsrdata);
1774224092Sdougb		if (result != ISC_R_SUCCESS) {
1775224092Sdougb			validator_log(val, ISC_LOG_DEBUG(3),
1776224092Sdougb				      "dns_ds_buildrdata() -> %s",
1777224092Sdougb				      dns_result_totext(result));
1778224092Sdougb			continue;
1779224092Sdougb		}
1780224092Sdougb		if (dns_rdata_compare(dsrdata, &newdsrdata) == 0)
1781224092Sdougb			break;
1782224092Sdougb	}
1783224092Sdougb	return (result);
1784224092Sdougb}
1785224092Sdougb
1786224092Sdougb/*%
1787165071Sdougb * Validate the DNSKEY RRset by looking for a DNSKEY that matches a
1788165071Sdougb * DLV record and that also verifies the DNSKEY RRset.
1789165071Sdougb */
1790135446Strhodesstatic isc_result_t
1791135446Strhodesdlv_validatezonekey(dns_validator_t *val) {
1792135446Strhodes	dns_rdata_dlv_t dlv;
1793135446Strhodes	dns_rdata_t dlvrdata = DNS_RDATA_INIT;
1794135446Strhodes	dns_rdata_t keyrdata = DNS_RDATA_INIT;
1795135446Strhodes	dns_rdataset_t trdataset;
1796135446Strhodes	isc_boolean_t supported_algorithm;
1797135446Strhodes	isc_result_t result;
1798224092Sdougb	char digest_types[256];
1799135446Strhodes
1800153816Sdougb	validator_log(val, ISC_LOG_DEBUG(3), "dlv_validatezonekey");
1801165071Sdougb
1802135446Strhodes	/*
1803135446Strhodes	 * Look through the DLV record and find the keys that can sign the
1804135446Strhodes	 * key set and the matching signature.  For each such key, attempt
1805135446Strhodes	 * verification.
1806135446Strhodes	 */
1807135446Strhodes	supported_algorithm = ISC_FALSE;
1808135446Strhodes
1809170222Sdougb	/*
1810170222Sdougb	 * If DNS_DSDIGEST_SHA256 is present we are required to prefer
1811170222Sdougb	 * it over DNS_DSDIGEST_SHA1.  This in practice means that we
1812170222Sdougb	 * need to ignore DNS_DSDIGEST_SHA1 if a DNS_DSDIGEST_SHA256
1813170222Sdougb	 * is present.
1814170222Sdougb	 */
1815224092Sdougb	memset(digest_types, 1, sizeof(digest_types));
1816153816Sdougb	for (result = dns_rdataset_first(&val->dlv);
1817135446Strhodes	     result == ISC_R_SUCCESS;
1818170222Sdougb	     result = dns_rdataset_next(&val->dlv)) {
1819170222Sdougb		dns_rdata_reset(&dlvrdata);
1820170222Sdougb		dns_rdataset_current(&val->dlv, &dlvrdata);
1821186462Sdougb		result = dns_rdata_tostruct(&dlvrdata, &dlv, NULL);
1822186462Sdougb		RUNTIME_CHECK(result == ISC_R_SUCCESS);
1823170222Sdougb
1824170222Sdougb		if (!dns_resolver_algorithm_supported(val->view->resolver,
1825170222Sdougb						      val->event->name,
1826170222Sdougb						      dlv.algorithm))
1827170222Sdougb			continue;
1828170222Sdougb
1829174187Sdougb		if (dlv.digest_type == DNS_DSDIGEST_SHA256 &&
1830174187Sdougb		    dlv.length == ISC_SHA256_DIGESTLENGTH) {
1831224092Sdougb			digest_types[DNS_DSDIGEST_SHA1] = 0;
1832170222Sdougb			break;
1833170222Sdougb		}
1834170222Sdougb	}
1835170222Sdougb
1836170222Sdougb	for (result = dns_rdataset_first(&val->dlv);
1837170222Sdougb	     result == ISC_R_SUCCESS;
1838153816Sdougb	     result = dns_rdataset_next(&val->dlv))
1839135446Strhodes	{
1840135446Strhodes		dns_rdata_reset(&dlvrdata);
1841153816Sdougb		dns_rdataset_current(&val->dlv, &dlvrdata);
1842186462Sdougb		result = dns_rdata_tostruct(&dlvrdata, &dlv, NULL);
1843186462Sdougb		RUNTIME_CHECK(result == ISC_R_SUCCESS);
1844135446Strhodes
1845170222Sdougb		if (!dns_resolver_digest_supported(val->view->resolver,
1846170222Sdougb						   dlv.digest_type))
1847170222Sdougb			continue;
1848186462Sdougb
1849224092Sdougb		if (digest_types[dlv.digest_type] == 0)
1850170222Sdougb			continue;
1851170222Sdougb
1852170222Sdougb		if (!dns_resolver_algorithm_supported(val->view->resolver,
1853135446Strhodes						      val->event->name,
1854135446Strhodes						      dlv.algorithm))
1855135446Strhodes			continue;
1856135446Strhodes
1857135446Strhodes		supported_algorithm = ISC_TRUE;
1858135446Strhodes
1859135446Strhodes		dns_rdataset_init(&trdataset);
1860135446Strhodes		dns_rdataset_clone(val->event->rdataset, &trdataset);
1861135446Strhodes
1862224092Sdougb		/*
1863224092Sdougb		 * Convert to DLV to DS and find matching DNSKEY.
1864224092Sdougb		 */
1865224092Sdougb		dlvrdata.type = dns_rdatatype_ds;
1866224092Sdougb		result = keyfromds(val, &trdataset, &dlvrdata,
1867224092Sdougb				   dlv.digest_type, dlv.key_tag,
1868224092Sdougb				   dlv.algorithm, &keyrdata);
1869135446Strhodes		if (result != ISC_R_SUCCESS) {
1870193149Sdougb			dns_rdataset_disassociate(&trdataset);
1871135446Strhodes			validator_log(val, ISC_LOG_DEBUG(3),
1872135446Strhodes				      "no DNSKEY matching DLV");
1873135446Strhodes			continue;
1874135446Strhodes		}
1875224092Sdougb
1876153816Sdougb		validator_log(val, ISC_LOG_DEBUG(3),
1877153816Sdougb		      "Found matching DLV record: checking for signature");
1878224092Sdougb		/*
1879224092Sdougb		 * Check that this DNSKEY signed the DNSKEY rrset.
1880224092Sdougb		 */
1881224092Sdougb		result = checkkey(val, &keyrdata, dlv.key_tag, dlv.algorithm);
1882153816Sdougb
1883135446Strhodes		dns_rdataset_disassociate(&trdataset);
1884135446Strhodes		if (result == ISC_R_SUCCESS)
1885135446Strhodes			break;
1886135446Strhodes		validator_log(val, ISC_LOG_DEBUG(3),
1887135446Strhodes			      "no RRSIG matching DLV key");
1888135446Strhodes	}
1889135446Strhodes	if (result == ISC_R_SUCCESS) {
1890205292Sdougb		marksecure(val->event);
1891224092Sdougb		validator_log(val, ISC_LOG_DEBUG(3), "marking as secure (dlv)");
1892135446Strhodes		return (result);
1893135446Strhodes	} else if (result == ISC_R_NOMORE && !supported_algorithm) {
1894135446Strhodes		if (val->mustbesecure) {
1895135446Strhodes			validator_log(val, ISC_LOG_WARNING,
1896224092Sdougb				      "must be secure failure,"
1897224092Sdougb				      "no supported algorithm/digest (dlv)");
1898135446Strhodes			return (DNS_R_MUSTBESECURE);
1899135446Strhodes		}
1900135446Strhodes		validator_log(val, ISC_LOG_DEBUG(3),
1901153816Sdougb			      "no supported algorithm/digest (dlv)");
1902214586Sdougb		markanswer(val, "dlv_validatezonekey (2)");
1903135446Strhodes		return (ISC_R_SUCCESS);
1904135446Strhodes	} else
1905135446Strhodes		return (DNS_R_NOVALIDSIG);
1906135446Strhodes}
1907135446Strhodes
1908165071Sdougb/*%
1909224092Sdougb * Attempts positive response validation of an RRset containing zone keys
1910224092Sdougb * (i.e. a DNSKEY rrset).
1911135446Strhodes *
1912135446Strhodes * Returns:
1913165071Sdougb * \li	ISC_R_SUCCESS	Validation completed successfully
1914165071Sdougb * \li	DNS_R_WAIT	Validation has started but is waiting
1915135446Strhodes *			for an event.
1916165071Sdougb * \li	Other return codes are possible and all indicate failure.
1917135446Strhodes */
1918135446Strhodesstatic isc_result_t
1919135446Strhodesvalidatezonekey(dns_validator_t *val) {
1920135446Strhodes	isc_result_t result;
1921135446Strhodes	dns_validatorevent_t *event;
1922135446Strhodes	dns_rdataset_t trdataset;
1923135446Strhodes	dns_rdata_t dsrdata = DNS_RDATA_INIT;
1924135446Strhodes	dns_rdata_t keyrdata = DNS_RDATA_INIT;
1925135446Strhodes	dns_rdata_t sigrdata = DNS_RDATA_INIT;
1926165071Sdougb	char namebuf[DNS_NAME_FORMATSIZE];
1927135446Strhodes	dns_rdata_ds_t ds;
1928135446Strhodes	dns_rdata_rrsig_t sig;
1929135446Strhodes	dst_key_t *dstkey;
1930135446Strhodes	isc_boolean_t supported_algorithm;
1931165071Sdougb	isc_boolean_t atsep = ISC_FALSE;
1932224092Sdougb	char digest_types[256];
1933135446Strhodes
1934135446Strhodes	/*
1935135446Strhodes	 * Caller must be holding the validator lock.
1936135446Strhodes	 */
1937135446Strhodes
1938135446Strhodes	event = val->event;
1939135446Strhodes
1940153816Sdougb	if (val->havedlvsep && val->dlv.trust >= dns_trust_secure &&
1941153816Sdougb	    dns_name_equal(event->name, dns_fixedname_name(&val->dlvsep)))
1942153816Sdougb		return (dlv_validatezonekey(val));
1943153816Sdougb
1944135446Strhodes	if (val->dsset == NULL) {
1945218384Sdougb
1946135446Strhodes		/*
1947218384Sdougb		 * We have a dlv sep.  Skip looking up the SEP from
1948218384Sdougb		 * {trusted,managed}-keys.  If the dlv sep is for the
1949218384Sdougb		 * root then it will have been handled above so we don't
1950218384Sdougb		 * need to check whether val->event->name is "." prior to
1951218384Sdougb		 * looking up the DS.
1952218384Sdougb		 */
1953218384Sdougb		if (val->havedlvsep)
1954218384Sdougb			goto find_ds;
1955218384Sdougb
1956218384Sdougb		/*
1957135446Strhodes		 * First, see if this key was signed by a trusted key.
1958135446Strhodes		 */
1959135446Strhodes		for (result = dns_rdataset_first(val->event->sigrdataset);
1960135446Strhodes		     result == ISC_R_SUCCESS;
1961135446Strhodes		     result = dns_rdataset_next(val->event->sigrdataset))
1962135446Strhodes		{
1963214586Sdougb			dns_keynode_t *keynode = NULL;
1964214586Sdougb			dns_fixedname_t fixed;
1965214586Sdougb			dns_name_t *found;
1966135446Strhodes
1967214586Sdougb			dns_fixedname_init(&fixed);
1968214586Sdougb			found = dns_fixedname_name(&fixed);
1969135446Strhodes			dns_rdata_reset(&sigrdata);
1970135446Strhodes			dns_rdataset_current(val->event->sigrdataset,
1971135446Strhodes					     &sigrdata);
1972186462Sdougb			result = dns_rdata_tostruct(&sigrdata, &sig, NULL);
1973186462Sdougb			RUNTIME_CHECK(result == ISC_R_SUCCESS);
1974193149Sdougb
1975193149Sdougb			if (!dns_name_equal(val->event->name, &sig.signer))
1976193149Sdougb				continue;
1977193149Sdougb
1978135446Strhodes			result = dns_keytable_findkeynode(val->keytable,
1979135446Strhodes							  val->event->name,
1980135446Strhodes							  sig.algorithm,
1981224092Sdougb							  sig.keyid, &keynode);
1982214586Sdougb			if (result == ISC_R_NOTFOUND &&
1983214586Sdougb			    dns_keytable_finddeepestmatch(val->keytable,
1984214586Sdougb				  val->event->name, found) != ISC_R_SUCCESS) {
1985214586Sdougb				if (val->mustbesecure) {
1986214586Sdougb					validator_log(val, ISC_LOG_WARNING,
1987218384Sdougb						     "must be secure failure, "
1988218384Sdougb						     "not beneath secure root");
1989214586Sdougb					return (DNS_R_MUSTBESECURE);
1990214586Sdougb				} else
1991214586Sdougb					validator_log(val, ISC_LOG_DEBUG(3),
1992218384Sdougb						     "not beneath secure root");
1993218384Sdougb				if (val->view->dlv == NULL) {
1994214586Sdougb					markanswer(val, "validatezonekey (1)");
1995214586Sdougb					return (ISC_R_SUCCESS);
1996214586Sdougb				}
1997214586Sdougb				return (startfinddlvsep(val, dns_rootname));
1998214586Sdougb			}
1999165071Sdougb			if (result == DNS_R_PARTIALMATCH ||
2000165071Sdougb			    result == ISC_R_SUCCESS)
2001165071Sdougb				atsep = ISC_TRUE;
2002135446Strhodes			while (result == ISC_R_SUCCESS) {
2003214586Sdougb				dns_keynode_t *nextnode = NULL;
2004135446Strhodes				dstkey = dns_keynode_key(keynode);
2005224092Sdougb				if (dstkey == NULL) {
2006224092Sdougb					dns_keytable_detachkeynode(
2007224092Sdougb								val->keytable,
2008224092Sdougb								&keynode);
2009224092Sdougb					break;
2010224092Sdougb				}
2011165071Sdougb				result = verify(val, dstkey, &sigrdata,
2012165071Sdougb						sig.keyid);
2013135446Strhodes				if (result == ISC_R_SUCCESS) {
2014224092Sdougb					dns_keytable_detachkeynode(
2015224092Sdougb								val->keytable,
2016224092Sdougb								&keynode);
2017135446Strhodes					break;
2018135446Strhodes				}
2019135446Strhodes				result = dns_keytable_findnextkeynode(
2020135446Strhodes								val->keytable,
2021135446Strhodes								keynode,
2022135446Strhodes								&nextnode);
2023135446Strhodes				dns_keytable_detachkeynode(val->keytable,
2024135446Strhodes							   &keynode);
2025135446Strhodes				keynode = nextnode;
2026135446Strhodes			}
2027135446Strhodes			if (result == ISC_R_SUCCESS) {
2028205292Sdougb				marksecure(event);
2029135446Strhodes				validator_log(val, ISC_LOG_DEBUG(3),
2030135446Strhodes					      "signed by trusted key; "
2031135446Strhodes					      "marking as secure");
2032135446Strhodes				return (result);
2033135446Strhodes			}
2034135446Strhodes		}
2035135446Strhodes
2036165071Sdougb		if (atsep) {
2037165071Sdougb			/*
2038165071Sdougb			 * We have not found a key to verify this DNSKEY
2039165071Sdougb			 * RRset.  As this is a SEP we have to assume that
2040165071Sdougb			 * the RRset is invalid.
2041165071Sdougb			 */
2042165071Sdougb			dns_name_format(val->event->name, namebuf,
2043186462Sdougb					sizeof(namebuf));
2044205292Sdougb			validator_log(val, ISC_LOG_NOTICE,
2045165071Sdougb				      "unable to find a DNSKEY which verifies "
2046224092Sdougb				      "the DNSKEY RRset and also matches a "
2047224092Sdougb				      "trusted key for '%s'",
2048165071Sdougb				      namebuf);
2049205292Sdougb			validator_log(val, ISC_LOG_NOTICE,
2050205292Sdougb				      "please check the 'trusted-keys' for "
2051205292Sdougb				      "'%s' in named.conf.", namebuf);
2052165071Sdougb			return (DNS_R_NOVALIDKEY);
2053165071Sdougb		}
2054165071Sdougb
2055135446Strhodes		/*
2056218384Sdougb		 * If this is the root name and there was no trusted key,
2057218384Sdougb		 * give up, since there's no DS at the root.
2058218384Sdougb		 */
2059218384Sdougb		if (dns_name_equal(event->name, dns_rootname)) {
2060218384Sdougb			if ((val->attributes & VALATTR_TRIEDVERIFY) != 0) {
2061218384Sdougb				validator_log(val, ISC_LOG_DEBUG(3),
2062218384Sdougb					      "root key failed to validate");
2063218384Sdougb				return (DNS_R_NOVALIDSIG);
2064218384Sdougb			} else {
2065218384Sdougb				validator_log(val, ISC_LOG_DEBUG(3),
2066218384Sdougb					      "no trusted root key");
2067218384Sdougb				return (DNS_R_NOVALIDDS);
2068218384Sdougb			}
2069218384Sdougb		}
2070218384Sdougb find_ds:
2071218384Sdougb		/*
2072135446Strhodes		 * Otherwise, try to find the DS record.
2073135446Strhodes		 */
2074135446Strhodes		result = view_find(val, val->event->name, dns_rdatatype_ds);
2075135446Strhodes		if (result == ISC_R_SUCCESS) {
2076135446Strhodes			/*
2077135446Strhodes			 * We have DS records.
2078135446Strhodes			 */
2079135446Strhodes			val->dsset = &val->frdataset;
2080214586Sdougb			if ((DNS_TRUST_PENDING(val->frdataset.trust) ||
2081214586Sdougb			     DNS_TRUST_ANSWER(val->frdataset.trust)) &&
2082135446Strhodes			    dns_rdataset_isassociated(&val->fsigrdataset))
2083135446Strhodes			{
2084135446Strhodes				result = create_validator(val,
2085135446Strhodes							  val->event->name,
2086135446Strhodes							  dns_rdatatype_ds,
2087135446Strhodes							  &val->frdataset,
2088135446Strhodes							  &val->fsigrdataset,
2089135446Strhodes							  dsvalidated,
2090135446Strhodes							  "validatezonekey");
2091135446Strhodes				if (result != ISC_R_SUCCESS)
2092135446Strhodes					return (result);
2093135446Strhodes				return (DNS_R_WAIT);
2094199958Sdougb			} else if (DNS_TRUST_PENDING(val->frdataset.trust)) {
2095135446Strhodes				/*
2096135446Strhodes				 * There should never be an unsigned DS.
2097135446Strhodes				 */
2098135446Strhodes				dns_rdataset_disassociate(&val->frdataset);
2099135446Strhodes				validator_log(val, ISC_LOG_DEBUG(2),
2100135446Strhodes					      "unsigned DS record");
2101135446Strhodes				return (DNS_R_NOVALIDSIG);
2102225361Sdougb			} else {
2103135446Strhodes				result = ISC_R_SUCCESS;
2104225361Sdougb				POST(result);
2105225361Sdougb			}
2106135446Strhodes		} else if (result == ISC_R_NOTFOUND) {
2107135446Strhodes			/*
2108135446Strhodes			 * We don't have the DS.  Find it.
2109135446Strhodes			 */
2110135446Strhodes			result = create_fetch(val, val->event->name,
2111135446Strhodes					      dns_rdatatype_ds, dsfetched,
2112135446Strhodes					      "validatezonekey");
2113135446Strhodes			if (result != ISC_R_SUCCESS)
2114135446Strhodes				return (result);
2115135446Strhodes			return (DNS_R_WAIT);
2116225361Sdougb		} else if (result == DNS_R_NCACHENXDOMAIN ||
2117135446Strhodes			   result == DNS_R_NCACHENXRRSET ||
2118186462Sdougb			   result == DNS_R_EMPTYNAME ||
2119135446Strhodes			   result == DNS_R_NXDOMAIN ||
2120225361Sdougb			   result == DNS_R_NXRRSET ||
2121225361Sdougb			   result == DNS_R_CNAME)
2122135446Strhodes		{
2123135446Strhodes			/*
2124135446Strhodes			 * The DS does not exist.
2125135446Strhodes			 */
2126135446Strhodes			if (dns_rdataset_isassociated(&val->frdataset))
2127135446Strhodes				dns_rdataset_disassociate(&val->frdataset);
2128135446Strhodes			if (dns_rdataset_isassociated(&val->fsigrdataset))
2129135446Strhodes				dns_rdataset_disassociate(&val->fsigrdataset);
2130135446Strhodes			validator_log(val, ISC_LOG_DEBUG(2), "no DS record");
2131135446Strhodes			return (DNS_R_NOVALIDSIG);
2132205292Sdougb		} else if (result == DNS_R_BROKENCHAIN)
2133205292Sdougb			return (result);
2134135446Strhodes	}
2135135446Strhodes
2136135446Strhodes	/*
2137135446Strhodes	 * We have a DS set.
2138135446Strhodes	 */
2139135446Strhodes	INSIST(val->dsset != NULL);
2140135446Strhodes
2141135446Strhodes	if (val->dsset->trust < dns_trust_secure) {
2142135446Strhodes		if (val->mustbesecure) {
2143135446Strhodes			validator_log(val, ISC_LOG_WARNING,
2144224092Sdougb				      "must be secure failure,"
2145224092Sdougb				      " insecure DS");
2146135446Strhodes			return (DNS_R_MUSTBESECURE);
2147135446Strhodes		}
2148222395Sdougb		if (val->view->dlv == NULL || DLVTRIED(val)) {
2149222395Sdougb			markanswer(val, "validatezonekey (2)");
2150222395Sdougb			return (ISC_R_SUCCESS);
2151222395Sdougb		}
2152222395Sdougb		return (startfinddlvsep(val, val->event->name));
2153135446Strhodes	}
2154135446Strhodes
2155135446Strhodes	/*
2156135446Strhodes	 * Look through the DS record and find the keys that can sign the
2157135446Strhodes	 * key set and the matching signature.  For each such key, attempt
2158135446Strhodes	 * verification.
2159135446Strhodes	 */
2160135446Strhodes
2161135446Strhodes	supported_algorithm = ISC_FALSE;
2162135446Strhodes
2163170222Sdougb	/*
2164170222Sdougb	 * If DNS_DSDIGEST_SHA256 is present we are required to prefer
2165170222Sdougb	 * it over DNS_DSDIGEST_SHA1.  This in practice means that we
2166170222Sdougb	 * need to ignore DNS_DSDIGEST_SHA1 if a DNS_DSDIGEST_SHA256
2167170222Sdougb	 * is present.
2168170222Sdougb	 */
2169224092Sdougb	memset(digest_types, 1, sizeof(digest_types));
2170135446Strhodes	for (result = dns_rdataset_first(val->dsset);
2171135446Strhodes	     result == ISC_R_SUCCESS;
2172170222Sdougb	     result = dns_rdataset_next(val->dsset)) {
2173170222Sdougb		dns_rdata_reset(&dsrdata);
2174170222Sdougb		dns_rdataset_current(val->dsset, &dsrdata);
2175186462Sdougb		result = dns_rdata_tostruct(&dsrdata, &ds, NULL);
2176186462Sdougb		RUNTIME_CHECK(result == ISC_R_SUCCESS);
2177170222Sdougb
2178170222Sdougb		if (!dns_resolver_algorithm_supported(val->view->resolver,
2179170222Sdougb						      val->event->name,
2180170222Sdougb						      ds.algorithm))
2181170222Sdougb			continue;
2182170222Sdougb
2183174187Sdougb		if (ds.digest_type == DNS_DSDIGEST_SHA256 &&
2184174187Sdougb		    ds.length == ISC_SHA256_DIGESTLENGTH) {
2185224092Sdougb			digest_types[DNS_DSDIGEST_SHA1] = 0;
2186170222Sdougb			break;
2187170222Sdougb		}
2188170222Sdougb	}
2189170222Sdougb
2190170222Sdougb	for (result = dns_rdataset_first(val->dsset);
2191170222Sdougb	     result == ISC_R_SUCCESS;
2192135446Strhodes	     result = dns_rdataset_next(val->dsset))
2193135446Strhodes	{
2194135446Strhodes		dns_rdata_reset(&dsrdata);
2195135446Strhodes		dns_rdataset_current(val->dsset, &dsrdata);
2196186462Sdougb		result = dns_rdata_tostruct(&dsrdata, &ds, NULL);
2197186462Sdougb		RUNTIME_CHECK(result == ISC_R_SUCCESS);
2198135446Strhodes
2199170222Sdougb		if (!dns_resolver_digest_supported(val->view->resolver,
2200170222Sdougb						   ds.digest_type))
2201153816Sdougb			continue;
2202170222Sdougb
2203224092Sdougb		if (digest_types[ds.digest_type] == 0)
2204170222Sdougb			continue;
2205170222Sdougb
2206135446Strhodes		if (!dns_resolver_algorithm_supported(val->view->resolver,
2207135446Strhodes						      val->event->name,
2208135446Strhodes						      ds.algorithm))
2209135446Strhodes			continue;
2210135446Strhodes
2211135446Strhodes		supported_algorithm = ISC_TRUE;
2212135446Strhodes
2213135446Strhodes		dns_rdataset_init(&trdataset);
2214135446Strhodes		dns_rdataset_clone(val->event->rdataset, &trdataset);
2215135446Strhodes
2216165071Sdougb		/*
2217224092Sdougb		 * Find matching DNSKEY from DS.
2218165071Sdougb		 */
2219224092Sdougb		result = keyfromds(val, &trdataset, &dsrdata, ds.digest_type,
2220224092Sdougb				   ds.key_tag, ds.algorithm, &keyrdata);
2221135446Strhodes		if (result != ISC_R_SUCCESS) {
2222193149Sdougb			dns_rdataset_disassociate(&trdataset);
2223135446Strhodes			validator_log(val, ISC_LOG_DEBUG(3),
2224135446Strhodes				      "no DNSKEY matching DS");
2225135446Strhodes			continue;
2226135446Strhodes		}
2227186462Sdougb
2228224092Sdougb		/*
2229224092Sdougb		 * Check that this DNSKEY signed the DNSKEY rrset.
2230224092Sdougb		 */
2231224092Sdougb		result = checkkey(val, &keyrdata, ds.key_tag, ds.algorithm);
2232224092Sdougb
2233135446Strhodes		dns_rdataset_disassociate(&trdataset);
2234135446Strhodes		if (result == ISC_R_SUCCESS)
2235135446Strhodes			break;
2236135446Strhodes		validator_log(val, ISC_LOG_DEBUG(3),
2237135446Strhodes			      "no RRSIG matching DS key");
2238135446Strhodes	}
2239135446Strhodes	if (result == ISC_R_SUCCESS) {
2240205292Sdougb		marksecure(event);
2241224092Sdougb		validator_log(val, ISC_LOG_DEBUG(3), "marking as secure (DS)");
2242135446Strhodes		return (result);
2243135446Strhodes	} else if (result == ISC_R_NOMORE && !supported_algorithm) {
2244135446Strhodes		if (val->mustbesecure) {
2245135446Strhodes			validator_log(val, ISC_LOG_WARNING,
2246224092Sdougb				      "must be secure failure, "
2247224092Sdougb				      "no supported algorithm/digest (DS)");
2248135446Strhodes			return (DNS_R_MUSTBESECURE);
2249135446Strhodes		}
2250135446Strhodes		validator_log(val, ISC_LOG_DEBUG(3),
2251153816Sdougb			      "no supported algorithm/digest (DS)");
2252214586Sdougb		markanswer(val, "validatezonekey (3)");
2253135446Strhodes		return (ISC_R_SUCCESS);
2254224092Sdougb	} else {
2255224092Sdougb		validator_log(val, ISC_LOG_INFO,
2256224092Sdougb			      "no valid signature found (DS)");
2257135446Strhodes		return (DNS_R_NOVALIDSIG);
2258224092Sdougb	}
2259135446Strhodes}
2260135446Strhodes
2261165071Sdougb/*%
2262135446Strhodes * Starts a positive response validation.
2263135446Strhodes *
2264135446Strhodes * Returns:
2265165071Sdougb * \li	ISC_R_SUCCESS	Validation completed successfully
2266165071Sdougb * \li	DNS_R_WAIT	Validation has started but is waiting
2267135446Strhodes *			for an event.
2268165071Sdougb * \li	Other return codes are possible and all indicate failure.
2269135446Strhodes */
2270135446Strhodesstatic isc_result_t
2271135446Strhodesstart_positive_validation(dns_validator_t *val) {
2272135446Strhodes	/*
2273135446Strhodes	 * If this is not a key, go straight into validate().
2274135446Strhodes	 */
2275135446Strhodes	if (val->event->type != dns_rdatatype_dnskey || !isselfsigned(val))
2276135446Strhodes		return (validate(val, ISC_FALSE));
2277135446Strhodes
2278135446Strhodes	return (validatezonekey(val));
2279135446Strhodes}
2280135446Strhodes
2281165071Sdougb/*%
2282214586Sdougb * val_rdataset_first and val_rdataset_next provide iteration methods
2283214586Sdougb * that hide whether we are iterating across a message or a  negative
2284214586Sdougb * cache rdataset.
2285214586Sdougb */
2286214586Sdougbstatic isc_result_t
2287214586Sdougbval_rdataset_first(dns_validator_t *val, dns_name_t **namep,
2288214586Sdougb		   dns_rdataset_t **rdatasetp)
2289214586Sdougb{
2290214586Sdougb	dns_message_t *message = val->event->message;
2291214586Sdougb	isc_result_t result;
2292214586Sdougb
2293214586Sdougb	REQUIRE(rdatasetp != NULL);
2294214586Sdougb	REQUIRE(namep != NULL);
2295214586Sdougb	if (message == NULL) {
2296214586Sdougb		REQUIRE(*rdatasetp != NULL);
2297214586Sdougb		REQUIRE(*namep != NULL);
2298214586Sdougb	} else {
2299214586Sdougb		REQUIRE(*rdatasetp == NULL);
2300214586Sdougb		REQUIRE(*namep == NULL);
2301214586Sdougb	}
2302214586Sdougb
2303214586Sdougb	if (message != NULL) {
2304214586Sdougb		result = dns_message_firstname(message, DNS_SECTION_AUTHORITY);
2305214586Sdougb		if (result != ISC_R_SUCCESS)
2306214586Sdougb			return (result);
2307214586Sdougb		dns_message_currentname(message, DNS_SECTION_AUTHORITY, namep);
2308214586Sdougb		*rdatasetp = ISC_LIST_HEAD((*namep)->list);
2309214586Sdougb		INSIST(*rdatasetp != NULL);
2310214586Sdougb	} else {
2311214586Sdougb		result = dns_rdataset_first(val->event->rdataset);
2312214586Sdougb		if (result == ISC_R_SUCCESS)
2313214586Sdougb			dns_ncache_current(val->event->rdataset, *namep,
2314214586Sdougb					   *rdatasetp);
2315214586Sdougb	}
2316214586Sdougb	return (result);
2317214586Sdougb}
2318214586Sdougb
2319214586Sdougbstatic isc_result_t
2320214586Sdougbval_rdataset_next(dns_validator_t *val, dns_name_t **namep,
2321214586Sdougb		  dns_rdataset_t **rdatasetp)
2322214586Sdougb{
2323214586Sdougb	dns_message_t *message = val->event->message;
2324214586Sdougb	isc_result_t result = ISC_R_SUCCESS;
2325214586Sdougb
2326214586Sdougb	REQUIRE(rdatasetp != NULL && *rdatasetp != NULL);
2327214586Sdougb	REQUIRE(namep != NULL && *namep != NULL);
2328214586Sdougb
2329214586Sdougb	if (message != NULL) {
2330214586Sdougb		dns_rdataset_t *rdataset = *rdatasetp;
2331214586Sdougb		rdataset = ISC_LIST_NEXT(rdataset, link);
2332214586Sdougb		if (rdataset == NULL) {
2333214586Sdougb			*namep = NULL;
2334214586Sdougb			result = dns_message_nextname(message,
2335214586Sdougb						      DNS_SECTION_AUTHORITY);
2336214586Sdougb			if (result == ISC_R_SUCCESS) {
2337214586Sdougb				dns_message_currentname(message,
2338214586Sdougb							DNS_SECTION_AUTHORITY,
2339214586Sdougb							namep);
2340214586Sdougb				rdataset = ISC_LIST_HEAD((*namep)->list);
2341214586Sdougb				INSIST(rdataset != NULL);
2342214586Sdougb			}
2343214586Sdougb		}
2344214586Sdougb		*rdatasetp = rdataset;
2345214586Sdougb	} else {
2346214586Sdougb		dns_rdataset_disassociate(*rdatasetp);
2347214586Sdougb		result = dns_rdataset_next(val->event->rdataset);
2348214586Sdougb		if (result == ISC_R_SUCCESS)
2349214586Sdougb			dns_ncache_current(val->event->rdataset, *namep,
2350214586Sdougb					   *rdatasetp);
2351214586Sdougb	}
2352214586Sdougb	return (result);
2353214586Sdougb}
2354214586Sdougb
2355214586Sdougb/*%
2356165071Sdougb * Look for NODATA at the wildcard and NOWILDCARD proofs in the
2357165071Sdougb * previously validated NSEC records.  As these proofs are mutually
2358165071Sdougb * exclusive we stop when one is found.
2359165071Sdougb *
2360165071Sdougb * Returns
2361186462Sdougb * \li	ISC_R_SUCCESS
2362165071Sdougb */
2363135446Strhodesstatic isc_result_t
2364193149Sdougbcheckwildcard(dns_validator_t *val, dns_rdatatype_t type, dns_name_t *zonename)
2365193149Sdougb{
2366214586Sdougb	dns_name_t *name, *wild, tname;
2367135446Strhodes	isc_result_t result;
2368135446Strhodes	isc_boolean_t exists, data;
2369135446Strhodes	char namebuf[DNS_NAME_FORMATSIZE];
2370214586Sdougb	dns_rdataset_t *rdataset, trdataset;
2371135446Strhodes
2372214586Sdougb	dns_name_init(&tname, NULL);
2373214586Sdougb	dns_rdataset_init(&trdataset);
2374135446Strhodes	wild = dns_fixedname_name(&val->wild);
2375193149Sdougb
2376193149Sdougb	if (dns_name_countlabels(wild) == 0) {
2377193149Sdougb		validator_log(val, ISC_LOG_DEBUG(3),
2378193149Sdougb			      "in checkwildcard: no wildcard to check");
2379193149Sdougb		return (ISC_R_SUCCESS);
2380193149Sdougb	}
2381193149Sdougb
2382135446Strhodes	dns_name_format(wild, namebuf, sizeof(namebuf));
2383135446Strhodes	validator_log(val, ISC_LOG_DEBUG(3), "in checkwildcard: %s", namebuf);
2384135446Strhodes
2385214586Sdougb	if (val->event->message == NULL) {
2386214586Sdougb		name = &tname;
2387214586Sdougb		rdataset = &trdataset;
2388214586Sdougb	} else {
2389214586Sdougb		name = NULL;
2390214586Sdougb		rdataset = NULL;
2391214586Sdougb	}
2392214586Sdougb
2393214586Sdougb	for (result = val_rdataset_first(val, &name, &rdataset);
2394135446Strhodes	     result == ISC_R_SUCCESS;
2395214586Sdougb	     result = val_rdataset_next(val, &name, &rdataset))
2396135446Strhodes	{
2397214586Sdougb		if (rdataset->type != type ||
2398214586Sdougb		    rdataset->trust != dns_trust_secure)
2399214586Sdougb			continue;
2400135446Strhodes
2401214586Sdougb		if (rdataset->type == dns_rdatatype_nsec &&
2402214586Sdougb		    (NEEDNODATA(val) || NEEDNOWILDCARD(val)) &&
2403214586Sdougb		    !FOUNDNODATA(val) && !FOUNDNOWILDCARD(val) &&
2404254402Serwin		    dns_nsec_noexistnodata(val->event->type, wild, name,
2405254402Serwin					   rdataset, &exists, &data, NULL,
2406254402Serwin					   validator_log, val)
2407214586Sdougb				       == ISC_R_SUCCESS)
2408214586Sdougb		{
2409214586Sdougb			dns_name_t **proofs = val->event->proofs;
2410214586Sdougb			if (exists && !data)
2411214586Sdougb				val->attributes |= VALATTR_FOUNDNODATA;
2412214586Sdougb			if (exists && !data && NEEDNODATA(val))
2413214586Sdougb				proofs[DNS_VALIDATOR_NODATAPROOF] =
2414214586Sdougb						 name;
2415214586Sdougb			if (!exists)
2416214586Sdougb				val->attributes |=
2417214586Sdougb					 VALATTR_FOUNDNOWILDCARD;
2418214586Sdougb			if (!exists && NEEDNOQNAME(val))
2419214586Sdougb				proofs[DNS_VALIDATOR_NOWILDCARDPROOF] =
2420214586Sdougb						 name;
2421214586Sdougb			if (dns_rdataset_isassociated(&trdataset))
2422214586Sdougb				dns_rdataset_disassociate(&trdataset);
2423214586Sdougb			return (ISC_R_SUCCESS);
2424214586Sdougb		}
2425135446Strhodes
2426214586Sdougb		if (rdataset->type == dns_rdatatype_nsec3 &&
2427214586Sdougb		    (NEEDNODATA(val) || NEEDNOWILDCARD(val)) &&
2428214586Sdougb		    !FOUNDNODATA(val) && !FOUNDNOWILDCARD(val) &&
2429254402Serwin		    dns_nsec3_noexistnodata(val->event->type, wild, name,
2430254402Serwin					    rdataset, zonename, &exists, &data,
2431254402Serwin					    NULL, NULL, NULL, NULL, NULL, NULL,
2432254402Serwin					    validator_log, val)
2433254402Serwin					    == ISC_R_SUCCESS)
2434135446Strhodes		{
2435214586Sdougb			dns_name_t **proofs = val->event->proofs;
2436214586Sdougb			if (exists && !data)
2437214586Sdougb				val->attributes |= VALATTR_FOUNDNODATA;
2438214586Sdougb			if (exists && !data && NEEDNODATA(val))
2439214586Sdougb				proofs[DNS_VALIDATOR_NODATAPROOF] =
2440214586Sdougb						 name;
2441214586Sdougb			if (!exists)
2442214586Sdougb				val->attributes |=
2443214586Sdougb					 VALATTR_FOUNDNOWILDCARD;
2444214586Sdougb			if (!exists && NEEDNOQNAME(val))
2445214586Sdougb				proofs[DNS_VALIDATOR_NOWILDCARDPROOF] =
2446214586Sdougb						 name;
2447214586Sdougb			if (dns_rdataset_isassociated(&trdataset))
2448214586Sdougb				dns_rdataset_disassociate(&trdataset);
2449214586Sdougb			return (ISC_R_SUCCESS);
2450135446Strhodes		}
2451135446Strhodes	}
2452135446Strhodes	if (result == ISC_R_NOMORE)
2453135446Strhodes		result = ISC_R_SUCCESS;
2454214586Sdougb	if (dns_rdataset_isassociated(&trdataset))
2455214586Sdougb		dns_rdataset_disassociate(&trdataset);
2456135446Strhodes	return (result);
2457135446Strhodes}
2458135446Strhodes
2459193149Sdougbstatic isc_result_t
2460193149Sdougbfindnsec3proofs(dns_validator_t *val) {
2461214586Sdougb	dns_name_t *name, tname;
2462193149Sdougb	isc_result_t result;
2463193149Sdougb	isc_boolean_t exists, data, optout, unknown;
2464234010Sdougb	isc_boolean_t setclosest, setnearest, *setclosestp;
2465193149Sdougb	dns_fixedname_t fclosest, fnearest, fzonename;
2466234010Sdougb	dns_name_t *closest, *nearest, *zonename, *closestp;
2467193149Sdougb	dns_name_t **proofs = val->event->proofs;
2468214586Sdougb	dns_rdataset_t *rdataset, trdataset;
2469193149Sdougb
2470214586Sdougb	dns_name_init(&tname, NULL);
2471214586Sdougb	dns_rdataset_init(&trdataset);
2472193149Sdougb	dns_fixedname_init(&fclosest);
2473193149Sdougb	dns_fixedname_init(&fnearest);
2474193149Sdougb	dns_fixedname_init(&fzonename);
2475193149Sdougb	closest = dns_fixedname_name(&fclosest);
2476193149Sdougb	nearest = dns_fixedname_name(&fnearest);
2477193149Sdougb	zonename = dns_fixedname_name(&fzonename);
2478193149Sdougb
2479214586Sdougb	if (val->event->message == NULL) {
2480214586Sdougb		name = &tname;
2481214586Sdougb		rdataset = &trdataset;
2482214586Sdougb	} else {
2483214586Sdougb		name = NULL;
2484214586Sdougb		rdataset = NULL;
2485214586Sdougb	}
2486214586Sdougb
2487214586Sdougb	for (result = val_rdataset_first(val, &name, &rdataset);
2488193149Sdougb	     result == ISC_R_SUCCESS;
2489214586Sdougb	     result = val_rdataset_next(val, &name, &rdataset))
2490193149Sdougb	{
2491214586Sdougb		if (rdataset->type != dns_rdatatype_nsec3 ||
2492214586Sdougb		    rdataset->trust != dns_trust_secure)
2493214586Sdougb			continue;
2494193149Sdougb
2495254402Serwin		result = dns_nsec3_noexistnodata(val->event->type,
2496254402Serwin						 val->event->name, name,
2497254402Serwin						 rdataset, zonename, NULL,
2498254402Serwin						 NULL, NULL, NULL, NULL, NULL,
2499254402Serwin						 NULL, NULL, validator_log,
2500254402Serwin						 val);
2501214586Sdougb		if (result != ISC_R_IGNORE && result != ISC_R_SUCCESS) {
2502214586Sdougb			if (dns_rdataset_isassociated(&trdataset))
2503214586Sdougb				dns_rdataset_disassociate(&trdataset);
2504214586Sdougb			return (result);
2505193149Sdougb		}
2506193149Sdougb	}
2507193149Sdougb	if (result != ISC_R_NOMORE)
2508193149Sdougb		result = ISC_R_SUCCESS;
2509225361Sdougb	POST(result);
2510193149Sdougb
2511193149Sdougb	if (dns_name_countlabels(zonename) == 0)
2512193149Sdougb		return (ISC_R_SUCCESS);
2513193149Sdougb
2514234010Sdougb	/*
2515234010Sdougb	 * If the val->closest is set then we want to use it otherwise
2516234010Sdougb	 * we need to discover it.
2517234010Sdougb	 */
2518234010Sdougb	if (dns_name_countlabels(dns_fixedname_name(&val->closest)) != 0) {
2519234010Sdougb		char namebuf[DNS_NAME_FORMATSIZE];
2520234010Sdougb
2521234010Sdougb		dns_name_format(dns_fixedname_name(&val->closest),
2522234010Sdougb				 namebuf, sizeof(namebuf));
2523234010Sdougb		validator_log(val, ISC_LOG_DEBUG(3), "closest encloser from "
2524234010Sdougb			      "wildcard signature '%s'", namebuf);
2525234010Sdougb		dns_name_copy(dns_fixedname_name(&val->closest), closest, NULL);
2526234010Sdougb		closestp = NULL;
2527234010Sdougb		setclosestp = NULL;
2528234010Sdougb	} else {
2529234010Sdougb		closestp = closest;
2530234010Sdougb		setclosestp = &setclosest;
2531234010Sdougb	}
2532234010Sdougb
2533214586Sdougb	for (result = val_rdataset_first(val, &name, &rdataset);
2534193149Sdougb	     result == ISC_R_SUCCESS;
2535214586Sdougb	     result = val_rdataset_next(val, &name, &rdataset))
2536193149Sdougb	{
2537214586Sdougb		if (rdataset->type != dns_rdatatype_nsec3 ||
2538214586Sdougb		    rdataset->trust != dns_trust_secure)
2539214586Sdougb			continue;
2540193149Sdougb
2541214586Sdougb		/*
2542214586Sdougb		 * We process all NSEC3 records to find the closest
2543214586Sdougb		 * encloser and nearest name to the closest encloser.
2544214586Sdougb		 */
2545214586Sdougb		setclosest = setnearest = ISC_FALSE;
2546214586Sdougb		optout = ISC_FALSE;
2547214586Sdougb		unknown = ISC_FALSE;
2548254402Serwin		result = dns_nsec3_noexistnodata(val->event->type,
2549254402Serwin						 val->event->name,
2550254402Serwin						 name, rdataset, zonename,
2551254402Serwin						 &exists, &data, &optout,
2552254402Serwin						 &unknown, setclosestp,
2553254402Serwin						 &setnearest, closestp,
2554254402Serwin						 nearest, validator_log, val);
2555214586Sdougb		if (unknown)
2556214586Sdougb			val->attributes |= VALATTR_FOUNDUNKNOWN;
2557214586Sdougb		if (result != ISC_R_SUCCESS)
2558214586Sdougb			continue;
2559254402Serwin		if (setclosest)
2560254402Serwin			proofs[DNS_VALIDATOR_CLOSESTENCLOSER] = name;
2561214586Sdougb		if (exists && !data && NEEDNODATA(val)) {
2562214586Sdougb			val->attributes |= VALATTR_FOUNDNODATA;
2563214586Sdougb			proofs[DNS_VALIDATOR_NODATAPROOF] = name;
2564193149Sdougb		}
2565214586Sdougb		if (!exists && setnearest) {
2566214586Sdougb			val->attributes |= VALATTR_FOUNDNOQNAME;
2567214586Sdougb			proofs[DNS_VALIDATOR_NOQNAMEPROOF] = name;
2568214586Sdougb			if (optout)
2569214586Sdougb				val->attributes |= VALATTR_FOUNDOPTOUT;
2570214586Sdougb		}
2571193149Sdougb	}
2572214586Sdougb	if (result == ISC_R_NOMORE)
2573193149Sdougb		result = ISC_R_SUCCESS;
2574193149Sdougb
2575193149Sdougb	/*
2576193149Sdougb	 * To know we have a valid noqname and optout proofs we need to also
2577193149Sdougb	 * have a valid closest encloser.  Otherwise we could still be looking
2578193149Sdougb	 * at proofs from the parent zone.
2579193149Sdougb	 */
2580193149Sdougb	if (dns_name_countlabels(closest) > 0 &&
2581193149Sdougb	    dns_name_countlabels(nearest) ==
2582193149Sdougb	    dns_name_countlabels(closest) + 1 &&
2583193149Sdougb	    dns_name_issubdomain(nearest, closest))
2584193149Sdougb	{
2585193149Sdougb		val->attributes |= VALATTR_FOUNDCLOSEST;
2586193149Sdougb		result = dns_name_concatenate(dns_wildcardname, closest,
2587193149Sdougb					      dns_fixedname_name(&val->wild),
2588193149Sdougb					      NULL);
2589193149Sdougb		RUNTIME_CHECK(result == ISC_R_SUCCESS);
2590193149Sdougb	} else {
2591193149Sdougb		val->attributes &= ~VALATTR_FOUNDNOQNAME;
2592193149Sdougb		val->attributes &= ~VALATTR_FOUNDOPTOUT;
2593193149Sdougb		proofs[DNS_VALIDATOR_NOQNAMEPROOF] = NULL;
2594193149Sdougb	}
2595193149Sdougb
2596193149Sdougb	/*
2597193149Sdougb	 * Do we need to check for the wildcard?
2598193149Sdougb	 */
2599214586Sdougb	if (FOUNDNOQNAME(val) && FOUNDCLOSEST(val) &&
2600214586Sdougb	    ((NEEDNODATA(val) && !FOUNDNODATA(val)) || NEEDNOWILDCARD(val))) {
2601193149Sdougb		result = checkwildcard(val, dns_rdatatype_nsec3, zonename);
2602193149Sdougb		if (result != ISC_R_SUCCESS)
2603193149Sdougb			return (result);
2604193149Sdougb	}
2605193149Sdougb	return (result);
2606193149Sdougb}
2607193149Sdougb
2608165071Sdougb/*%
2609214586Sdougb * Validate the authority section records.
2610165071Sdougb */
2611135446Strhodesstatic isc_result_t
2612214586Sdougbvalidate_authority(dns_validator_t *val, isc_boolean_t resume) {
2613135446Strhodes	dns_name_t *name;
2614135446Strhodes	dns_message_t *message = val->event->message;
2615135446Strhodes	isc_result_t result;
2616135446Strhodes
2617135446Strhodes	if (!resume)
2618135446Strhodes		result = dns_message_firstname(message, DNS_SECTION_AUTHORITY);
2619214586Sdougb	else
2620135446Strhodes		result = ISC_R_SUCCESS;
2621135446Strhodes
2622135446Strhodes	for (;
2623135446Strhodes	     result == ISC_R_SUCCESS;
2624135446Strhodes	     result = dns_message_nextname(message, DNS_SECTION_AUTHORITY))
2625135446Strhodes	{
2626135446Strhodes		dns_rdataset_t *rdataset = NULL, *sigrdataset = NULL;
2627135446Strhodes
2628135446Strhodes		name = NULL;
2629135446Strhodes		dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name);
2630135446Strhodes		if (resume) {
2631135446Strhodes			rdataset = ISC_LIST_NEXT(val->currentset, link);
2632135446Strhodes			val->currentset = NULL;
2633135446Strhodes			resume = ISC_FALSE;
2634135446Strhodes		} else
2635135446Strhodes			rdataset = ISC_LIST_HEAD(name->list);
2636135446Strhodes
2637135446Strhodes		for (;
2638135446Strhodes		     rdataset != NULL;
2639135446Strhodes		     rdataset = ISC_LIST_NEXT(rdataset, link))
2640135446Strhodes		{
2641135446Strhodes			if (rdataset->type == dns_rdatatype_rrsig)
2642135446Strhodes				continue;
2643135446Strhodes
2644135446Strhodes			for (sigrdataset = ISC_LIST_HEAD(name->list);
2645135446Strhodes			     sigrdataset != NULL;
2646135446Strhodes			     sigrdataset = ISC_LIST_NEXT(sigrdataset,
2647135446Strhodes							 link))
2648135446Strhodes			{
2649135446Strhodes				if (sigrdataset->type == dns_rdatatype_rrsig &&
2650135446Strhodes				    sigrdataset->covers == rdataset->type)
2651135446Strhodes					break;
2652135446Strhodes			}
2653135446Strhodes			/*
2654135446Strhodes			 * If a signed zone is missing the zone key, bad
2655135446Strhodes			 * things could happen.  A query for data in the zone
2656135446Strhodes			 * would lead to a query for the zone key, which
2657135446Strhodes			 * would return a negative answer, which would contain
2658135446Strhodes			 * an SOA and an NSEC signed by the missing key, which
2659135446Strhodes			 * would trigger another query for the DNSKEY (since
2660135446Strhodes			 * the first one is still in progress), and go into an
2661135446Strhodes			 * infinite loop.  Avoid that.
2662135446Strhodes			 */
2663135446Strhodes			if (val->event->type == dns_rdatatype_dnskey &&
2664225361Sdougb			    rdataset->type == dns_rdatatype_nsec &&
2665135446Strhodes			    dns_name_equal(name, val->event->name))
2666135446Strhodes			{
2667135446Strhodes				dns_rdata_t nsec = DNS_RDATA_INIT;
2668135446Strhodes
2669135446Strhodes				result = dns_rdataset_first(rdataset);
2670135446Strhodes				if (result != ISC_R_SUCCESS)
2671135446Strhodes					return (result);
2672135446Strhodes				dns_rdataset_current(rdataset, &nsec);
2673135446Strhodes				if (dns_nsec_typepresent(&nsec,
2674135446Strhodes							dns_rdatatype_soa))
2675135446Strhodes					continue;
2676135446Strhodes			}
2677135446Strhodes			val->currentset = rdataset;
2678135446Strhodes			result = create_validator(val, name, rdataset->type,
2679135446Strhodes						  rdataset, sigrdataset,
2680135446Strhodes						  authvalidated,
2681214586Sdougb						  "validate_authority");
2682135446Strhodes			if (result != ISC_R_SUCCESS)
2683135446Strhodes				return (result);
2684205292Sdougb			val->authcount++;
2685135446Strhodes			return (DNS_R_WAIT);
2686214586Sdougb		}
2687214586Sdougb	}
2688214586Sdougb	if (result == ISC_R_NOMORE)
2689214586Sdougb		result = ISC_R_SUCCESS;
2690214586Sdougb	return (result);
2691214586Sdougb}
2692135446Strhodes
2693214586Sdougb/*%
2694214586Sdougb * Validate the ncache elements.
2695214586Sdougb */
2696214586Sdougbstatic isc_result_t
2697214586Sdougbvalidate_ncache(dns_validator_t *val, isc_boolean_t resume) {
2698214586Sdougb	dns_name_t *name;
2699214586Sdougb	isc_result_t result;
2700214586Sdougb
2701214586Sdougb	if (!resume)
2702214586Sdougb		result = dns_rdataset_first(val->event->rdataset);
2703214586Sdougb	else
2704214586Sdougb		result = dns_rdataset_next(val->event->rdataset);
2705214586Sdougb
2706214586Sdougb	for (;
2707214586Sdougb	     result == ISC_R_SUCCESS;
2708214586Sdougb	     result = dns_rdataset_next(val->event->rdataset))
2709214586Sdougb	{
2710214586Sdougb		dns_rdataset_t *rdataset, *sigrdataset = NULL;
2711214586Sdougb
2712214586Sdougb		if (dns_rdataset_isassociated(&val->frdataset))
2713214586Sdougb			dns_rdataset_disassociate(&val->frdataset);
2714214586Sdougb		if (dns_rdataset_isassociated(&val->fsigrdataset))
2715214586Sdougb			dns_rdataset_disassociate(&val->fsigrdataset);
2716214586Sdougb
2717214586Sdougb		dns_fixedname_init(&val->fname);
2718214586Sdougb		name = dns_fixedname_name(&val->fname);
2719214586Sdougb		rdataset = &val->frdataset;
2720214586Sdougb		dns_ncache_current(val->event->rdataset, name, rdataset);
2721214586Sdougb
2722214586Sdougb		if (val->frdataset.type == dns_rdatatype_rrsig)
2723214586Sdougb			continue;
2724214586Sdougb
2725214586Sdougb		result = dns_ncache_getsigrdataset(val->event->rdataset, name,
2726214586Sdougb						   rdataset->type,
2727214586Sdougb						   &val->fsigrdataset);
2728214586Sdougb		if (result == ISC_R_SUCCESS)
2729214586Sdougb			sigrdataset = &val->fsigrdataset;
2730214586Sdougb
2731214586Sdougb		/*
2732214586Sdougb		 * If a signed zone is missing the zone key, bad
2733214586Sdougb		 * things could happen.  A query for data in the zone
2734214586Sdougb		 * would lead to a query for the zone key, which
2735214586Sdougb		 * would return a negative answer, which would contain
2736214586Sdougb		 * an SOA and an NSEC signed by the missing key, which
2737214586Sdougb		 * would trigger another query for the DNSKEY (since
2738214586Sdougb		 * the first one is still in progress), and go into an
2739214586Sdougb		 * infinite loop.  Avoid that.
2740214586Sdougb		 */
2741214586Sdougb		if (val->event->type == dns_rdatatype_dnskey &&
2742225361Sdougb		    rdataset->type == dns_rdatatype_nsec &&
2743214586Sdougb		    dns_name_equal(name, val->event->name))
2744214586Sdougb		{
2745214586Sdougb			dns_rdata_t nsec = DNS_RDATA_INIT;
2746214586Sdougb
2747214586Sdougb			result = dns_rdataset_first(rdataset);
2748214586Sdougb			if (result != ISC_R_SUCCESS)
2749214586Sdougb				return (result);
2750214586Sdougb			dns_rdataset_current(rdataset, &nsec);
2751214586Sdougb			if (dns_nsec_typepresent(&nsec,
2752214586Sdougb						dns_rdatatype_soa))
2753214586Sdougb				continue;
2754135446Strhodes		}
2755214586Sdougb		val->currentset = rdataset;
2756214586Sdougb		result = create_validator(val, name, rdataset->type,
2757214586Sdougb					  rdataset, sigrdataset,
2758214586Sdougb					  authvalidated,
2759214586Sdougb					  "validate_ncache");
2760214586Sdougb		if (result != ISC_R_SUCCESS)
2761214586Sdougb			return (result);
2762214586Sdougb		val->authcount++;
2763214586Sdougb		return (DNS_R_WAIT);
2764135446Strhodes	}
2765135446Strhodes	if (result == ISC_R_NOMORE)
2766135446Strhodes		result = ISC_R_SUCCESS;
2767214586Sdougb	return (result);
2768214586Sdougb}
2769214586Sdougb
2770214586Sdougb/*%
2771214586Sdougb * Prove a negative answer is good or that there is a NOQNAME when the
2772214586Sdougb * answer is from a wildcard.
2773214586Sdougb *
2774214586Sdougb * Loop through the authority section looking for NODATA, NOWILDCARD
2775214586Sdougb * and NOQNAME proofs in the NSEC records by calling authvalidated().
2776214586Sdougb *
2777214586Sdougb * If the required proofs are found we are done.
2778214586Sdougb *
2779214586Sdougb * If the proofs are not found attempt to prove this is a unsecure
2780214586Sdougb * response.
2781214586Sdougb */
2782214586Sdougbstatic isc_result_t
2783214586Sdougbnsecvalidate(dns_validator_t *val, isc_boolean_t resume) {
2784214586Sdougb	isc_result_t result;
2785214586Sdougb
2786214586Sdougb	if (resume)
2787214586Sdougb		validator_log(val, ISC_LOG_DEBUG(3), "resuming nsecvalidate");
2788214586Sdougb
2789214586Sdougb	if (val->event->message == NULL)
2790214586Sdougb		result = validate_ncache(val, resume);
2791214586Sdougb	else
2792214586Sdougb		result = validate_authority(val, resume);
2793214586Sdougb
2794135446Strhodes	if (result != ISC_R_SUCCESS)
2795135446Strhodes		return (result);
2796135446Strhodes
2797135446Strhodes	/*
2798165071Sdougb	 * Do we only need to check for NOQNAME?  To get here we must have
2799165071Sdougb	 * had a secure wildcard answer.
2800135446Strhodes	 */
2801214586Sdougb	if (!NEEDNODATA(val) && !NEEDNOWILDCARD(val) && NEEDNOQNAME(val)) {
2802214586Sdougb		if (!FOUNDNOQNAME(val))
2803193149Sdougb			findnsec3proofs(val);
2804214586Sdougb		if (FOUNDNOQNAME(val) && FOUNDCLOSEST(val)) {
2805135446Strhodes			validator_log(val, ISC_LOG_DEBUG(3),
2806224092Sdougb				      "marking as secure, noqname proof found");
2807205292Sdougb			marksecure(val->event);
2808135446Strhodes			return (ISC_R_SUCCESS);
2809214586Sdougb		} else if (FOUNDOPTOUT(val) &&
2810193149Sdougb			   dns_name_countlabels(dns_fixedname_name(&val->wild))
2811193149Sdougb					 != 0) {
2812193149Sdougb			validator_log(val, ISC_LOG_DEBUG(3),
2813193149Sdougb				      "optout proof found");
2814193149Sdougb			val->event->optout = ISC_TRUE;
2815214586Sdougb			markanswer(val, "nsecvalidate (1)");
2816193149Sdougb			return (ISC_R_SUCCESS);
2817193149Sdougb		} else if ((val->attributes & VALATTR_FOUNDUNKNOWN) != 0) {
2818193149Sdougb			validator_log(val, ISC_LOG_DEBUG(3),
2819193149Sdougb				      "unknown NSEC3 hash algorithm found");
2820214586Sdougb			markanswer(val, "nsecvalidate (2)");
2821193149Sdougb			return (ISC_R_SUCCESS);
2822135446Strhodes		}
2823135446Strhodes		validator_log(val, ISC_LOG_DEBUG(3),
2824135446Strhodes			      "noqname proof not found");
2825135446Strhodes		return (DNS_R_NOVALIDNSEC);
2826135446Strhodes	}
2827135446Strhodes
2828214586Sdougb	if (!FOUNDNOQNAME(val) && !FOUNDNODATA(val))
2829193149Sdougb		findnsec3proofs(val);
2830193149Sdougb
2831135446Strhodes	/*
2832135446Strhodes	 * Do we need to check for the wildcard?
2833135446Strhodes	 */
2834214586Sdougb	if (FOUNDNOQNAME(val) && FOUNDCLOSEST(val) &&
2835214586Sdougb	    ((NEEDNODATA(val) && !FOUNDNODATA(val)) || NEEDNOWILDCARD(val))) {
2836193149Sdougb		result = checkwildcard(val, dns_rdatatype_nsec, NULL);
2837135446Strhodes		if (result != ISC_R_SUCCESS)
2838135446Strhodes			return (result);
2839135446Strhodes	}
2840135446Strhodes
2841214586Sdougb	if ((NEEDNODATA(val) && (FOUNDNODATA(val) || FOUNDOPTOUT(val))) ||
2842214586Sdougb	    (NEEDNOQNAME(val) && FOUNDNOQNAME(val) &&
2843214586Sdougb	     NEEDNOWILDCARD(val) && FOUNDNOWILDCARD(val) &&
2844214586Sdougb	     FOUNDCLOSEST(val))) {
2845193149Sdougb		if ((val->attributes & VALATTR_FOUNDOPTOUT) != 0)
2846193149Sdougb			val->event->optout = ISC_TRUE;
2847135446Strhodes		validator_log(val, ISC_LOG_DEBUG(3),
2848165071Sdougb			      "nonexistence proof(s) found");
2849214586Sdougb		if (val->event->message == NULL)
2850214586Sdougb			marksecure(val->event);
2851254402Serwin		else
2852254402Serwin			val->event->secure = ISC_TRUE;
2853135446Strhodes		return (ISC_R_SUCCESS);
2854135446Strhodes	}
2855165071Sdougb
2856208337Sdougb	if (val->authfail != 0 && val->authcount == val->authfail)
2857205292Sdougb		return (DNS_R_BROKENCHAIN);
2858165071Sdougb	validator_log(val, ISC_LOG_DEBUG(3),
2859165071Sdougb		      "nonexistence proof(s) not found");
2860165071Sdougb	val->attributes |= VALATTR_INSECURITY;
2861186462Sdougb	return (proveunsecure(val, ISC_FALSE, ISC_FALSE));
2862135446Strhodes}
2863135446Strhodes
2864135446Strhodesstatic isc_boolean_t
2865153816Sdougbcheck_ds(dns_validator_t *val, dns_name_t *name, dns_rdataset_t *rdataset) {
2866135446Strhodes	dns_rdata_t dsrdata = DNS_RDATA_INIT;
2867135446Strhodes	dns_rdata_ds_t ds;
2868135446Strhodes	isc_result_t result;
2869135446Strhodes
2870135446Strhodes	for (result = dns_rdataset_first(rdataset);
2871135446Strhodes	     result == ISC_R_SUCCESS;
2872135446Strhodes	     result = dns_rdataset_next(rdataset)) {
2873135446Strhodes		dns_rdataset_current(rdataset, &dsrdata);
2874186462Sdougb		result = dns_rdata_tostruct(&dsrdata, &ds, NULL);
2875186462Sdougb		RUNTIME_CHECK(result == ISC_R_SUCCESS);
2876135446Strhodes
2877170222Sdougb		if (dns_resolver_digest_supported(val->view->resolver,
2878170222Sdougb						  ds.digest_type) &&
2879153816Sdougb		    dns_resolver_algorithm_supported(val->view->resolver,
2880153816Sdougb						     name, ds.algorithm)) {
2881153816Sdougb			dns_rdata_reset(&dsrdata);
2882135446Strhodes			return (ISC_TRUE);
2883153816Sdougb		}
2884135446Strhodes		dns_rdata_reset(&dsrdata);
2885135446Strhodes	}
2886135446Strhodes	return (ISC_FALSE);
2887135446Strhodes}
2888135446Strhodes
2889205292Sdougbstatic void
2890205292Sdougbdlvvalidated(isc_task_t *task, isc_event_t *event) {
2891205292Sdougb	dns_validatorevent_t *devent;
2892205292Sdougb	dns_validator_t *val;
2893205292Sdougb	isc_result_t eresult;
2894205292Sdougb	isc_boolean_t want_destroy;
2895205292Sdougb
2896205292Sdougb	UNUSED(task);
2897205292Sdougb	INSIST(event->ev_type == DNS_EVENT_VALIDATORDONE);
2898205292Sdougb
2899205292Sdougb	devent = (dns_validatorevent_t *)event;
2900205292Sdougb	val = devent->ev_arg;
2901205292Sdougb	eresult = devent->result;
2902205292Sdougb
2903205292Sdougb	isc_event_free(&event);
2904205292Sdougb	dns_validator_destroy(&val->subvalidator);
2905205292Sdougb
2906205292Sdougb	INSIST(val->event != NULL);
2907205292Sdougb
2908205292Sdougb	validator_log(val, ISC_LOG_DEBUG(3), "in dlvvalidated");
2909205292Sdougb	LOCK(&val->lock);
2910205292Sdougb	if (CANCELED(val)) {
2911205292Sdougb		validator_done(val, ISC_R_CANCELED);
2912205292Sdougb	} else if (eresult == ISC_R_SUCCESS) {
2913205292Sdougb		validator_log(val, ISC_LOG_DEBUG(3),
2914222395Sdougb			      "dlvset with trust %s",
2915222395Sdougb			      dns_trust_totext(val->frdataset.trust));
2916205292Sdougb		dns_rdataset_clone(&val->frdataset, &val->dlv);
2917205292Sdougb		val->havedlvsep = ISC_TRUE;
2918205292Sdougb		if (dlv_algorithm_supported(val))
2919205292Sdougb			dlv_validator_start(val);
2920205292Sdougb		else {
2921214586Sdougb			markanswer(val, "dlvvalidated");
2922205292Sdougb			validator_done(val, ISC_R_SUCCESS);
2923205292Sdougb		}
2924205292Sdougb	} else {
2925205292Sdougb		if (eresult != DNS_R_BROKENCHAIN) {
2926205292Sdougb			if (dns_rdataset_isassociated(&val->frdataset))
2927205292Sdougb				dns_rdataset_expire(&val->frdataset);
2928205292Sdougb			if (dns_rdataset_isassociated(&val->fsigrdataset))
2929205292Sdougb				dns_rdataset_expire(&val->fsigrdataset);
2930205292Sdougb		}
2931205292Sdougb		validator_log(val, ISC_LOG_DEBUG(3),
2932205292Sdougb			      "dlvvalidated: got %s",
2933205292Sdougb			      isc_result_totext(eresult));
2934205292Sdougb		validator_done(val, DNS_R_BROKENCHAIN);
2935205292Sdougb	}
2936205292Sdougb	want_destroy = exit_check(val);
2937205292Sdougb	UNLOCK(&val->lock);
2938205292Sdougb	if (want_destroy)
2939205292Sdougb		destroy(val);
2940205292Sdougb}
2941205292Sdougb
2942165071Sdougb/*%
2943165071Sdougb * Callback from fetching a DLV record.
2944186462Sdougb *
2945165071Sdougb * Resumes the DLV lookup process.
2946165071Sdougb */
2947135446Strhodesstatic void
2948153816Sdougbdlvfetched(isc_task_t *task, isc_event_t *event) {
2949153816Sdougb	char namebuf[DNS_NAME_FORMATSIZE];
2950135446Strhodes	dns_fetchevent_t *devent;
2951135446Strhodes	dns_validator_t *val;
2952135446Strhodes	isc_boolean_t want_destroy;
2953135446Strhodes	isc_result_t eresult;
2954135446Strhodes	isc_result_t result;
2955254402Serwin	dns_fetch_t *fetch;
2956135446Strhodes
2957135446Strhodes	UNUSED(task);
2958135446Strhodes	INSIST(event->ev_type == DNS_EVENT_FETCHDONE);
2959135446Strhodes	devent = (dns_fetchevent_t *)event;
2960135446Strhodes	val = devent->ev_arg;
2961135446Strhodes	eresult = devent->result;
2962153816Sdougb
2963153816Sdougb	/* Free resources which are not of interest. */
2964153816Sdougb	if (devent->node != NULL)
2965153816Sdougb		dns_db_detachnode(devent->db, &devent->node);
2966153816Sdougb	if (devent->db != NULL)
2967153816Sdougb		dns_db_detach(&devent->db);
2968153816Sdougb	if (dns_rdataset_isassociated(&val->fsigrdataset))
2969153816Sdougb		dns_rdataset_disassociate(&val->fsigrdataset);
2970135446Strhodes	isc_event_free(&event);
2971153816Sdougb
2972135446Strhodes	INSIST(val->event != NULL);
2973153816Sdougb	validator_log(val, ISC_LOG_DEBUG(3), "in dlvfetched: %s",
2974135446Strhodes		      dns_result_totext(eresult));
2975135446Strhodes
2976135446Strhodes	LOCK(&val->lock);
2977254402Serwin	fetch = val->fetch;
2978254402Serwin	val->fetch = NULL;
2979135446Strhodes	if (eresult == ISC_R_SUCCESS) {
2980153816Sdougb		dns_name_format(dns_fixedname_name(&val->dlvsep), namebuf,
2981153816Sdougb				sizeof(namebuf));
2982153816Sdougb		dns_rdataset_clone(&val->frdataset, &val->dlv);
2983135446Strhodes		val->havedlvsep = ISC_TRUE;
2984190227Sdougb		if (dlv_algorithm_supported(val)) {
2985190227Sdougb			validator_log(val, ISC_LOG_DEBUG(3), "DLV %s found",
2986190227Sdougb				      namebuf);
2987190227Sdougb			dlv_validator_start(val);
2988190227Sdougb		} else {
2989190227Sdougb			validator_log(val, ISC_LOG_DEBUG(3),
2990190227Sdougb				      "DLV %s found with no supported algorithms",
2991190227Sdougb				      namebuf);
2992214586Sdougb			markanswer(val, "dlvfetched (1)");
2993190227Sdougb			validator_done(val, ISC_R_SUCCESS);
2994190227Sdougb		}
2995135446Strhodes	} else if (eresult == DNS_R_NXRRSET ||
2996135446Strhodes		   eresult == DNS_R_NXDOMAIN ||
2997135446Strhodes		   eresult == DNS_R_NCACHENXRRSET ||
2998135446Strhodes		   eresult == DNS_R_NCACHENXDOMAIN) {
2999190227Sdougb		result = finddlvsep(val, ISC_TRUE);
3000135446Strhodes		if (result == ISC_R_SUCCESS) {
3001190227Sdougb			if (dlv_algorithm_supported(val)) {
3002190227Sdougb				dns_name_format(dns_fixedname_name(&val->dlvsep),
3003190227Sdougb						namebuf, sizeof(namebuf));
3004190227Sdougb				validator_log(val, ISC_LOG_DEBUG(3),
3005190227Sdougb					      "DLV %s found", namebuf);
3006190227Sdougb				dlv_validator_start(val);
3007190227Sdougb			} else {
3008190227Sdougb				validator_log(val, ISC_LOG_DEBUG(3),
3009190227Sdougb					      "DLV %s found with no supported "
3010190227Sdougb					      "algorithms", namebuf);
3011214586Sdougb				markanswer(val, "dlvfetched (2)");
3012190227Sdougb				validator_done(val, ISC_R_SUCCESS);
3013190227Sdougb			}
3014135446Strhodes		} else if (result == ISC_R_NOTFOUND) {
3015153816Sdougb			validator_log(val, ISC_LOG_DEBUG(3), "DLV not found");
3016214586Sdougb			markanswer(val, "dlvfetched (3)");
3017135446Strhodes			validator_done(val, ISC_R_SUCCESS);
3018153816Sdougb		} else {
3019153816Sdougb			validator_log(val, ISC_LOG_DEBUG(3), "DLV lookup: %s",
3020153816Sdougb				      dns_result_totext(result));
3021153816Sdougb			if (result != DNS_R_WAIT)
3022153816Sdougb				validator_done(val, result);
3023153816Sdougb		}
3024153816Sdougb	} else {
3025153816Sdougb		validator_log(val, ISC_LOG_DEBUG(3), "DLV lookup: %s",
3026153816Sdougb			      dns_result_totext(eresult));
3027165071Sdougb		validator_done(val, eresult);
3028135446Strhodes	}
3029135446Strhodes	want_destroy = exit_check(val);
3030135446Strhodes	UNLOCK(&val->lock);
3031254402Serwin	if (fetch != NULL)
3032254402Serwin		dns_resolver_destroyfetch(&fetch);
3033135446Strhodes	if (want_destroy)
3034135446Strhodes		destroy(val);
3035135446Strhodes}
3036135446Strhodes
3037165071Sdougb/*%
3038193149Sdougb * Start the DLV lookup process.
3039186462Sdougb *
3040165071Sdougb * Returns
3041165071Sdougb * \li	ISC_R_SUCCESS
3042165071Sdougb * \li	DNS_R_WAIT
3043165071Sdougb * \li	Others on validation failures.
3044165071Sdougb */
3045135446Strhodesstatic isc_result_t
3046153816Sdougbstartfinddlvsep(dns_validator_t *val, dns_name_t *unsecure) {
3047153816Sdougb	char namebuf[DNS_NAME_FORMATSIZE];
3048153816Sdougb	isc_result_t result;
3049153816Sdougb
3050153816Sdougb	INSIST(!DLVTRIED(val));
3051153816Sdougb
3052153816Sdougb	val->attributes |= VALATTR_DLVTRIED;
3053153816Sdougb
3054153816Sdougb	dns_name_format(unsecure, namebuf, sizeof(namebuf));
3055153816Sdougb	validator_log(val, ISC_LOG_DEBUG(3),
3056153816Sdougb		      "plain DNSSEC returns unsecure (%s): looking for DLV",
3057153816Sdougb		      namebuf);
3058153816Sdougb
3059153816Sdougb	if (dns_name_issubdomain(val->event->name, val->view->dlv)) {
3060224092Sdougb		validator_log(val, ISC_LOG_WARNING, "must be secure failure, "
3061224092Sdougb			      " %s is under DLV (startfinddlvsep)", namebuf);
3062153816Sdougb		return (DNS_R_MUSTBESECURE);
3063153816Sdougb	}
3064153816Sdougb
3065153816Sdougb	val->dlvlabels = dns_name_countlabels(unsecure) - 1;
3066153816Sdougb	result = finddlvsep(val, ISC_FALSE);
3067153816Sdougb	if (result == ISC_R_NOTFOUND) {
3068153816Sdougb		validator_log(val, ISC_LOG_DEBUG(3), "DLV not found");
3069214586Sdougb		markanswer(val, "startfinddlvsep (1)");
3070153816Sdougb		return (ISC_R_SUCCESS);
3071153816Sdougb	}
3072153816Sdougb	if (result != ISC_R_SUCCESS) {
3073153816Sdougb		validator_log(val, ISC_LOG_DEBUG(3), "DLV lookup: %s",
3074153816Sdougb			      dns_result_totext(result));
3075153816Sdougb		return (result);
3076153816Sdougb	}
3077153816Sdougb	dns_name_format(dns_fixedname_name(&val->dlvsep), namebuf,
3078153816Sdougb			sizeof(namebuf));
3079190227Sdougb	if (dlv_algorithm_supported(val)) {
3080190227Sdougb		validator_log(val, ISC_LOG_DEBUG(3), "DLV %s found", namebuf);
3081190227Sdougb		dlv_validator_start(val);
3082190227Sdougb		return (DNS_R_WAIT);
3083193149Sdougb	}
3084190227Sdougb	validator_log(val, ISC_LOG_DEBUG(3), "DLV %s found with no supported "
3085190227Sdougb		      "algorithms", namebuf);
3086214586Sdougb	markanswer(val, "startfinddlvsep (2)");
3087190227Sdougb	validator_done(val, ISC_R_SUCCESS);
3088190227Sdougb	return (ISC_R_SUCCESS);
3089153816Sdougb}
3090153816Sdougb
3091165071Sdougb/*%
3092165071Sdougb * Continue the DLV lookup process.
3093165071Sdougb *
3094165071Sdougb * Returns
3095165071Sdougb * \li	ISC_R_SUCCESS
3096165071Sdougb * \li	ISC_R_NOTFOUND
3097165071Sdougb * \li	DNS_R_WAIT
3098165071Sdougb * \li	Others on validation failure.
3099165071Sdougb */
3100153816Sdougbstatic isc_result_t
3101135446Strhodesfinddlvsep(dns_validator_t *val, isc_boolean_t resume) {
3102153816Sdougb	char namebuf[DNS_NAME_FORMATSIZE];
3103135446Strhodes	dns_fixedname_t dlvfixed;
3104135446Strhodes	dns_name_t *dlvname;
3105135446Strhodes	dns_name_t *dlvsep;
3106135446Strhodes	dns_name_t noroot;
3107135446Strhodes	isc_result_t result;
3108135446Strhodes	unsigned int labels;
3109165071Sdougb
3110153816Sdougb	INSIST(val->view->dlv != NULL);
3111135446Strhodes
3112135446Strhodes	if (!resume) {
3113153816Sdougb		if (dns_name_issubdomain(val->event->name, val->view->dlv)) {
3114224092Sdougb			dns_name_format(val->event->name, namebuf,
3115224092Sdougb					sizeof(namebuf));
3116153816Sdougb			validator_log(val, ISC_LOG_WARNING,
3117224092Sdougb				      "must be secure failure, "
3118224092Sdougb				      "%s is under DLV (finddlvsep)", namebuf);
3119153816Sdougb			return (DNS_R_MUSTBESECURE);
3120153816Sdougb		}
3121153816Sdougb
3122135446Strhodes		dns_fixedname_init(&val->dlvsep);
3123135446Strhodes		dlvsep = dns_fixedname_name(&val->dlvsep);
3124135446Strhodes		dns_name_copy(val->event->name, dlvsep, NULL);
3125174187Sdougb		/*
3126174187Sdougb		 * If this is a response to a DS query, we need to look in
3127174187Sdougb		 * the parent zone for the trust anchor.
3128174187Sdougb		 */
3129153816Sdougb		if (val->event->type == dns_rdatatype_ds) {
3130153816Sdougb			labels = dns_name_countlabels(dlvsep);
3131153816Sdougb			if (labels == 0)
3132153816Sdougb				return (ISC_R_NOTFOUND);
3133153816Sdougb			dns_name_getlabelsequence(dlvsep, 1, labels - 1,
3134153816Sdougb						  dlvsep);
3135153816Sdougb		}
3136135446Strhodes	} else {
3137135446Strhodes		dlvsep = dns_fixedname_name(&val->dlvsep);
3138135446Strhodes		labels = dns_name_countlabels(dlvsep);
3139135446Strhodes		dns_name_getlabelsequence(dlvsep, 1, labels - 1, dlvsep);
3140135446Strhodes	}
3141135446Strhodes	dns_name_init(&noroot, NULL);
3142135446Strhodes	dns_fixedname_init(&dlvfixed);
3143135446Strhodes	dlvname = dns_fixedname_name(&dlvfixed);
3144135446Strhodes	labels = dns_name_countlabels(dlvsep);
3145153816Sdougb	if (labels == 0)
3146153816Sdougb		return (ISC_R_NOTFOUND);
3147135446Strhodes	dns_name_getlabelsequence(dlvsep, 0, labels - 1, &noroot);
3148135446Strhodes	result = dns_name_concatenate(&noroot, val->view->dlv, dlvname, NULL);
3149135446Strhodes	while (result == ISC_R_NOSPACE) {
3150135446Strhodes		labels = dns_name_countlabels(dlvsep);
3151135446Strhodes		dns_name_getlabelsequence(dlvsep, 1, labels - 1, dlvsep);
3152135446Strhodes		dns_name_getlabelsequence(dlvsep, 0, labels - 2, &noroot);
3153135446Strhodes		result = dns_name_concatenate(&noroot, val->view->dlv,
3154135446Strhodes					      dlvname, NULL);
3155135446Strhodes	}
3156135446Strhodes	if (result != ISC_R_SUCCESS) {
3157135446Strhodes		validator_log(val, ISC_LOG_DEBUG(2), "DLV concatenate failed");
3158135446Strhodes		return (DNS_R_NOVALIDSIG);
3159135446Strhodes	}
3160135446Strhodes
3161153816Sdougb	while (dns_name_countlabels(dlvname) >=
3162153816Sdougb	       dns_name_countlabels(val->view->dlv) + val->dlvlabels) {
3163153816Sdougb		dns_name_format(dlvname, namebuf, sizeof(namebuf));
3164153816Sdougb		validator_log(val, ISC_LOG_DEBUG(3), "looking for DLV %s",
3165153816Sdougb			      namebuf);
3166135446Strhodes		result = view_find(val, dlvname, dns_rdatatype_dlv);
3167135446Strhodes		if (result == ISC_R_SUCCESS) {
3168205292Sdougb			if (DNS_TRUST_PENDING(val->frdataset.trust) &&
3169205292Sdougb			    dns_rdataset_isassociated(&val->fsigrdataset))
3170205292Sdougb			{
3171205292Sdougb				dns_fixedname_init(&val->fname);
3172205292Sdougb				dns_name_copy(dlvname,
3173205292Sdougb					      dns_fixedname_name(&val->fname),
3174205292Sdougb					      NULL);
3175205292Sdougb				result = create_validator(val,
3176205292Sdougb						dns_fixedname_name(&val->fname),
3177205292Sdougb							  dns_rdatatype_dlv,
3178205292Sdougb							  &val->frdataset,
3179205292Sdougb							  &val->fsigrdataset,
3180205292Sdougb							  dlvvalidated,
3181205292Sdougb							  "finddlvsep");
3182205292Sdougb				if (result != ISC_R_SUCCESS)
3183205292Sdougb					return (result);
3184205292Sdougb				return (DNS_R_WAIT);
3185205292Sdougb			}
3186224092Sdougb			if (val->frdataset.trust < dns_trust_secure) {
3187224092Sdougb				validator_log(val, ISC_LOG_DEBUG(3),
3188224092Sdougb					      "DLV not validated");
3189135446Strhodes				return (DNS_R_NOVALIDSIG);
3190224092Sdougb			}
3191135446Strhodes			val->havedlvsep = ISC_TRUE;
3192153816Sdougb			dns_rdataset_clone(&val->frdataset, &val->dlv);
3193135446Strhodes			return (ISC_R_SUCCESS);
3194135446Strhodes		}
3195135446Strhodes		if (result == ISC_R_NOTFOUND) {
3196153816Sdougb			result = create_fetch(val, dlvname, dns_rdatatype_dlv,
3197153816Sdougb					      dlvfetched, "finddlvsep");
3198153816Sdougb			if (result != ISC_R_SUCCESS)
3199153816Sdougb				return (result);
3200153816Sdougb			return (DNS_R_WAIT);
3201135446Strhodes		}
3202135446Strhodes		if (result != DNS_R_NXRRSET &&
3203135446Strhodes		    result != DNS_R_NXDOMAIN &&
3204186462Sdougb		    result != DNS_R_EMPTYNAME &&
3205135446Strhodes		    result != DNS_R_NCACHENXRRSET &&
3206153816Sdougb		    result != DNS_R_NCACHENXDOMAIN)
3207135446Strhodes			return (result);
3208135446Strhodes		/*
3209135446Strhodes		 * Strip first labels from both dlvsep and dlvname.
3210135446Strhodes		 */
3211135446Strhodes		labels = dns_name_countlabels(dlvsep);
3212153816Sdougb		if (labels == 0)
3213153816Sdougb			break;
3214135446Strhodes		dns_name_getlabelsequence(dlvsep, 1, labels - 1, dlvsep);
3215135446Strhodes		labels = dns_name_countlabels(dlvname);
3216135446Strhodes		dns_name_getlabelsequence(dlvname, 1, labels - 1, dlvname);
3217135446Strhodes	}
3218135446Strhodes	return (ISC_R_NOTFOUND);
3219135446Strhodes}
3220135446Strhodes
3221165071Sdougb/*%
3222153816Sdougb * proveunsecure walks down from the SEP looking for a break in the
3223165071Sdougb * chain of trust.  That occurs when we can prove the DS record does
3224153816Sdougb * not exist at a delegation point or the DS exists at a delegation
3225153816Sdougb * but we don't support the algorithm/digest.
3226165071Sdougb *
3227165071Sdougb * If DLV is active and we look for a DLV record at or below the
3228165071Sdougb * point we go insecure.  If found we restart the validation process.
3229165071Sdougb * If not found or DLV isn't active we mark the response as a answer.
3230165071Sdougb *
3231165071Sdougb * Returns:
3232165071Sdougb * \li	ISC_R_SUCCESS		val->event->name is in a unsecure zone
3233165071Sdougb * \li	DNS_R_WAIT		validation is in progress.
3234165071Sdougb * \li	DNS_R_MUSTBESECURE	val->event->name is supposed to be secure
3235165071Sdougb *				(policy) but we proved that it is unsecure.
3236165071Sdougb * \li	DNS_R_NOVALIDSIG
3237165071Sdougb * \li	DNS_R_NOVALIDNSEC
3238165071Sdougb * \li	DNS_R_NOTINSECURE
3239205292Sdougb * \li	DNS_R_BROKENCHAIN
3240153816Sdougb */
3241135446Strhodesstatic isc_result_t
3242186462Sdougbproveunsecure(dns_validator_t *val, isc_boolean_t have_ds, isc_boolean_t resume)
3243186462Sdougb{
3244135446Strhodes	isc_result_t result;
3245153816Sdougb	dns_fixedname_t fixedsecroot;
3246153816Sdougb	dns_name_t *secroot;
3247135446Strhodes	dns_name_t *tname;
3248153816Sdougb	char namebuf[DNS_NAME_FORMATSIZE];
3249193149Sdougb	dns_name_t *found;
3250193149Sdougb	dns_fixedname_t fixedfound;
3251135446Strhodes
3252153816Sdougb	dns_fixedname_init(&fixedsecroot);
3253153816Sdougb	secroot = dns_fixedname_name(&fixedsecroot);
3254193149Sdougb	dns_fixedname_init(&fixedfound);
3255193149Sdougb	found = dns_fixedname_name(&fixedfound);
3256153816Sdougb	if (val->havedlvsep)
3257153816Sdougb		dns_name_copy(dns_fixedname_name(&val->dlvsep), secroot, NULL);
3258153816Sdougb	else {
3259202961Sdougb		unsigned int labels;
3260174187Sdougb		dns_name_copy(val->event->name, secroot, NULL);
3261174187Sdougb		/*
3262174187Sdougb		 * If this is a response to a DS query, we need to look in
3263174187Sdougb		 * the parent zone for the trust anchor.
3264174187Sdougb		 */
3265202961Sdougb
3266202961Sdougb		labels = dns_name_countlabels(secroot);
3267202961Sdougb		if (val->event->type == dns_rdatatype_ds && labels > 1U)
3268202961Sdougb			dns_name_getlabelsequence(secroot, 1, labels - 1,
3269202961Sdougb						  secroot);
3270153816Sdougb		result = dns_keytable_finddeepestmatch(val->keytable,
3271174187Sdougb						       secroot, secroot);
3272153816Sdougb		if (result == ISC_R_NOTFOUND) {
3273143731Sdougb			if (val->mustbesecure) {
3274143731Sdougb				validator_log(val, ISC_LOG_WARNING,
3275224092Sdougb					      "must be secure failure, "
3276224092Sdougb					      "not beneath secure root");
3277143731Sdougb				result = DNS_R_MUSTBESECURE;
3278143731Sdougb				goto out;
3279224092Sdougb			} else
3280224092Sdougb				validator_log(val, ISC_LOG_DEBUG(3),
3281224092Sdougb					      "not beneath secure root");
3282153816Sdougb			if (val->view->dlv == NULL || DLVTRIED(val)) {
3283214586Sdougb				markanswer(val, "proveunsecure (1)");
3284153816Sdougb				return (ISC_R_SUCCESS);
3285153816Sdougb			}
3286153816Sdougb			return (startfinddlvsep(val, dns_rootname));
3287153816Sdougb		} else if (result != ISC_R_SUCCESS)
3288153816Sdougb			return (result);
3289135446Strhodes	}
3290135446Strhodes
3291135446Strhodes	if (!resume) {
3292153816Sdougb		/*
3293153816Sdougb		 * We are looking for breaks below the SEP so add a label.
3294153816Sdougb		 */
3295153816Sdougb		val->labels = dns_name_countlabels(secroot) + 1;
3296135446Strhodes	} else {
3297135446Strhodes		validator_log(val, ISC_LOG_DEBUG(3), "resuming proveunsecure");
3298186462Sdougb		/*
3299186462Sdougb		 * If we have a DS rdataset and it is secure then check if
3300186462Sdougb		 * the DS rdataset has a supported algorithm combination.
3301224092Sdougb		 * If not this is an insecure delegation as far as this
3302186462Sdougb		 * resolver is concerned.  Fall back to DLV if available.
3303186462Sdougb		 */
3304186462Sdougb		if (have_ds && val->frdataset.trust >= dns_trust_secure &&
3305153816Sdougb		    !check_ds(val, dns_fixedname_name(&val->fname),
3306153816Sdougb			      &val->frdataset)) {
3307153816Sdougb			dns_name_format(dns_fixedname_name(&val->fname),
3308153816Sdougb					namebuf, sizeof(namebuf));
3309186462Sdougb			if ((val->view->dlv == NULL || DLVTRIED(val)) &&
3310186462Sdougb			    val->mustbesecure) {
3311135446Strhodes				validator_log(val, ISC_LOG_WARNING,
3312224092Sdougb					      "must be secure failure at '%s', "
3313224092Sdougb					      "can't fall back to DLV",
3314153816Sdougb					      namebuf);
3315135446Strhodes				result = DNS_R_MUSTBESECURE;
3316135446Strhodes				goto out;
3317135446Strhodes			}
3318135446Strhodes			validator_log(val, ISC_LOG_DEBUG(3),
3319153816Sdougb				      "no supported algorithm/digest (%s/DS)",
3320153816Sdougb				      namebuf);
3321153816Sdougb			if (val->view->dlv == NULL || DLVTRIED(val)) {
3322214586Sdougb				markanswer(val, "proveunsecure (2)");
3323153816Sdougb				result = ISC_R_SUCCESS;
3324153816Sdougb				goto out;
3325153816Sdougb			}
3326234010Sdougb			return(startfinddlvsep(val,
3327234010Sdougb					      dns_fixedname_name(&val->fname)));
3328135446Strhodes		}
3329135446Strhodes		val->labels++;
3330135446Strhodes	}
3331135446Strhodes
3332135446Strhodes	for (;
3333135446Strhodes	     val->labels <= dns_name_countlabels(val->event->name);
3334135446Strhodes	     val->labels++)
3335135446Strhodes	{
3336135446Strhodes
3337135446Strhodes		dns_fixedname_init(&val->fname);
3338135446Strhodes		tname = dns_fixedname_name(&val->fname);
3339135446Strhodes		if (val->labels == dns_name_countlabels(val->event->name))
3340135446Strhodes			dns_name_copy(val->event->name, tname, NULL);
3341135446Strhodes		else
3342135446Strhodes			dns_name_split(val->event->name, val->labels,
3343135446Strhodes				       NULL, tname);
3344135446Strhodes
3345135446Strhodes		dns_name_format(tname, namebuf, sizeof(namebuf));
3346135446Strhodes		validator_log(val, ISC_LOG_DEBUG(3),
3347135446Strhodes			      "checking existence of DS at '%s'",
3348135446Strhodes			      namebuf);
3349135446Strhodes
3350135446Strhodes		result = view_find(val, tname, dns_rdatatype_ds);
3351135446Strhodes		if (result == DNS_R_NXRRSET || result == DNS_R_NCACHENXRRSET) {
3352135446Strhodes			/*
3353135446Strhodes			 * There is no DS.  If this is a delegation,
3354224092Sdougb			 * we may be done.
3355135446Strhodes			 */
3356214586Sdougb			/*
3357214586Sdougb			 * If we have "trust == answer" then this namespace
3358214586Sdougb			 * has switched from insecure to should be secure.
3359214586Sdougb			 */
3360214586Sdougb			if (DNS_TRUST_PENDING(val->frdataset.trust) ||
3361214586Sdougb			    DNS_TRUST_ANSWER(val->frdataset.trust)) {
3362214586Sdougb				result = create_validator(val, tname,
3363214586Sdougb							  dns_rdatatype_ds,
3364214586Sdougb							  &val->frdataset,
3365214586Sdougb							  NULL, dsvalidated,
3366214586Sdougb							  "proveunsecure");
3367224092Sdougb				if (result != ISC_R_SUCCESS)
3368170222Sdougb					goto out;
3369170222Sdougb				return (DNS_R_WAIT);
3370170222Sdougb			}
3371193149Sdougb			/*
3372193149Sdougb			 * Zones using NSEC3 don't return a NSEC RRset so
3373193149Sdougb			 * we need to use dns_view_findzonecut2 to find
3374193149Sdougb			 * the zone cut.
3375193149Sdougb			 */
3376193149Sdougb			if (result == DNS_R_NXRRSET &&
3377193149Sdougb			    !dns_rdataset_isassociated(&val->frdataset) &&
3378193149Sdougb			    dns_view_findzonecut2(val->view, tname, found,
3379224092Sdougb						 0, 0, ISC_FALSE, ISC_FALSE,
3380224092Sdougb						 NULL, NULL) == ISC_R_SUCCESS &&
3381193149Sdougb			    dns_name_equal(tname, found)) {
3382193149Sdougb				if (val->mustbesecure) {
3383193149Sdougb					validator_log(val, ISC_LOG_WARNING,
3384224092Sdougb						      "must be secure failure, "
3385224092Sdougb						      "no DS at zone cut");
3386193149Sdougb					return (DNS_R_MUSTBESECURE);
3387193149Sdougb				}
3388193149Sdougb				if (val->view->dlv == NULL || DLVTRIED(val)) {
3389214586Sdougb					markanswer(val, "proveunsecure (3)");
3390193149Sdougb					return (ISC_R_SUCCESS);
3391193149Sdougb				}
3392193149Sdougb				return (startfinddlvsep(val, tname));
3393193149Sdougb			}
3394135446Strhodes			if (val->frdataset.trust < dns_trust_secure) {
3395135446Strhodes				/*
3396135446Strhodes				 * This shouldn't happen, since the negative
3397135446Strhodes				 * response should have been validated.  Since
3398135446Strhodes				 * there's no way of validating existing
3399135446Strhodes				 * negative response blobs, give up.
3400135446Strhodes				 */
3401224092Sdougb				validator_log(val, ISC_LOG_WARNING,
3402224092Sdougb					      "can't validate existing "
3403224092Sdougb					      "negative responses (no DS)");
3404135446Strhodes				result = DNS_R_NOVALIDSIG;
3405135446Strhodes				goto out;
3406135446Strhodes			}
3407135446Strhodes			if (isdelegation(tname, &val->frdataset, result)) {
3408135446Strhodes				if (val->mustbesecure) {
3409135446Strhodes					validator_log(val, ISC_LOG_WARNING,
3410224092Sdougb						      "must be secure failure, "
3411224092Sdougb						      "%s is a delegation",
3412224092Sdougb						      namebuf);
3413135446Strhodes					return (DNS_R_MUSTBESECURE);
3414135446Strhodes				}
3415153816Sdougb				if (val->view->dlv == NULL || DLVTRIED(val)) {
3416214586Sdougb					markanswer(val, "proveunsecure (4)");
3417153816Sdougb					return (ISC_R_SUCCESS);
3418153816Sdougb				}
3419153816Sdougb				return (startfinddlvsep(val, tname));
3420135446Strhodes			}
3421135446Strhodes			continue;
3422225361Sdougb		} else if (result == DNS_R_CNAME) {
3423225361Sdougb			if (DNS_TRUST_PENDING(val->frdataset.trust) ||
3424225361Sdougb			    DNS_TRUST_ANSWER(val->frdataset.trust)) {
3425225361Sdougb				result = create_validator(val, tname,
3426225361Sdougb							  dns_rdatatype_cname,
3427225361Sdougb							  &val->frdataset,
3428225361Sdougb							  NULL, cnamevalidated,
3429225361Sdougb							  "proveunsecure "
3430225361Sdougb							  "(cname)");
3431225361Sdougb				if (result != ISC_R_SUCCESS)
3432225361Sdougb					goto out;
3433225361Sdougb				return (DNS_R_WAIT);
3434225361Sdougb			}
3435225361Sdougb			continue;
3436135446Strhodes		} else if (result == ISC_R_SUCCESS) {
3437135446Strhodes			/*
3438135446Strhodes			 * There is a DS here.  Verify that it's secure and
3439135446Strhodes			 * continue.
3440135446Strhodes			 */
3441135446Strhodes			if (val->frdataset.trust >= dns_trust_secure) {
3442153816Sdougb				if (!check_ds(val, tname, &val->frdataset)) {
3443135446Strhodes					validator_log(val, ISC_LOG_DEBUG(3),
3444153816Sdougb						     "no supported algorithm/"
3445153816Sdougb						     "digest (%s/DS)", namebuf);
3446135446Strhodes					if (val->mustbesecure) {
3447135446Strhodes						validator_log(val,
3448135446Strhodes							      ISC_LOG_WARNING,
3449224092Sdougb						      "must be secure failure, "
3450224092Sdougb						      "no supported algorithm/"
3451224092Sdougb						      "digest (%s/DS)",
3452224092Sdougb						      namebuf);
3453135446Strhodes						result = DNS_R_MUSTBESECURE;
3454135446Strhodes						goto out;
3455135446Strhodes					}
3456153816Sdougb					if (val->view->dlv == NULL ||
3457153816Sdougb					    DLVTRIED(val)) {
3458214586Sdougb						markanswer(val,
3459214586Sdougb							   "proveunsecure (5)");
3460153816Sdougb						result = ISC_R_SUCCESS;
3461153816Sdougb						goto out;
3462153816Sdougb					}
3463234010Sdougb					return(startfinddlvsep(val, tname));
3464135446Strhodes				}
3465135446Strhodes				continue;
3466135446Strhodes			}
3467135446Strhodes			else if (!dns_rdataset_isassociated(&val->fsigrdataset))
3468135446Strhodes			{
3469224092Sdougb				validator_log(val, ISC_LOG_DEBUG(3),
3470224092Sdougb					      "DS is unsigned");
3471135446Strhodes				result = DNS_R_NOVALIDSIG;
3472135446Strhodes				goto out;
3473135446Strhodes			}
3474214586Sdougb			/*
3475214586Sdougb			 * Validate / re-validate answer.
3476214586Sdougb			 */
3477135446Strhodes			result = create_validator(val, tname, dns_rdatatype_ds,
3478135446Strhodes						  &val->frdataset,
3479135446Strhodes						  &val->fsigrdataset,
3480135446Strhodes						  dsvalidated,
3481135446Strhodes						  "proveunsecure");
3482135446Strhodes			if (result != ISC_R_SUCCESS)
3483135446Strhodes				goto out;
3484135446Strhodes			return (DNS_R_WAIT);
3485135446Strhodes		} else if (result == DNS_R_NXDOMAIN ||
3486165071Sdougb			   result == DNS_R_NCACHENXDOMAIN) {
3487135446Strhodes			/*
3488135446Strhodes			 * This is not a zone cut.  Assuming things are
3489135446Strhodes			 * as expected, continue.
3490135446Strhodes			 */
3491135446Strhodes			if (!dns_rdataset_isassociated(&val->frdataset)) {
3492135446Strhodes				/*
3493135446Strhodes				 * There should be an NSEC here, since we
3494135446Strhodes				 * are still in a secure zone.
3495135446Strhodes				 */
3496135446Strhodes				result = DNS_R_NOVALIDNSEC;
3497135446Strhodes				goto out;
3498216175Sdougb			} else if (DNS_TRUST_PENDING(val->frdataset.trust) ||
3499216175Sdougb				   DNS_TRUST_ANSWER(val->frdataset.trust)) {
3500216175Sdougb				/*
3501216175Sdougb				 * If we have "trust == answer" then this namespace
3502216175Sdougb				 * has switched from insecure to should be secure.
3503216175Sdougb				 */
3504216175Sdougb				result = create_validator(val, tname,
3505216175Sdougb							  dns_rdatatype_ds,
3506216175Sdougb							  &val->frdataset,
3507216175Sdougb							  NULL, dsvalidated,
3508216175Sdougb							  "proveunsecure");
3509216175Sdougb				if (result != ISC_R_SUCCESS)
3510216175Sdougb					goto out;
3511216175Sdougb				return (DNS_R_WAIT);
3512135446Strhodes			} else if (val->frdataset.trust < dns_trust_secure) {
3513135446Strhodes				/*
3514135446Strhodes				 * This shouldn't happen, since the negative
3515135446Strhodes				 * response should have been validated.  Since
3516135446Strhodes				 * there's no way of validating existing
3517135446Strhodes				 * negative response blobs, give up.
3518135446Strhodes				 */
3519224092Sdougb				validator_log(val, ISC_LOG_WARNING,
3520224092Sdougb					      "can't validate existing "
3521224092Sdougb					      "negative responses "
3522224092Sdougb					      "(not a zone cut)");
3523135446Strhodes				result = DNS_R_NOVALIDSIG;
3524135446Strhodes				goto out;
3525135446Strhodes			}
3526135446Strhodes			continue;
3527135446Strhodes		} else if (result == ISC_R_NOTFOUND) {
3528135446Strhodes			/*
3529135446Strhodes			 * We don't know anything about the DS.  Find it.
3530135446Strhodes			 */
3531135446Strhodes			result = create_fetch(val, tname, dns_rdatatype_ds,
3532135446Strhodes					      dsfetched2, "proveunsecure");
3533135446Strhodes			if (result != ISC_R_SUCCESS)
3534135446Strhodes				goto out;
3535135446Strhodes			return (DNS_R_WAIT);
3536205292Sdougb		} else if (result == DNS_R_BROKENCHAIN)
3537205292Sdougb			return (result);
3538135446Strhodes	}
3539193149Sdougb
3540214586Sdougb	/* Couldn't complete insecurity proof */
3541135446Strhodes	validator_log(val, ISC_LOG_DEBUG(3), "insecurity proof failed");
3542224092Sdougb	return (DNS_R_NOTINSECURE);
3543135446Strhodes
3544135446Strhodes out:
3545135446Strhodes	if (dns_rdataset_isassociated(&val->frdataset))
3546135446Strhodes		dns_rdataset_disassociate(&val->frdataset);
3547135446Strhodes	if (dns_rdataset_isassociated(&val->fsigrdataset))
3548135446Strhodes		dns_rdataset_disassociate(&val->fsigrdataset);
3549135446Strhodes	return (result);
3550135446Strhodes}
3551135446Strhodes
3552165071Sdougb/*%
3553165071Sdougb * Reset state and revalidate the answer using DLV.
3554165071Sdougb */
3555165071Sdougbstatic void
3556153816Sdougbdlv_validator_start(dns_validator_t *val) {
3557153816Sdougb	isc_event_t *event;
3558153816Sdougb
3559153816Sdougb	validator_log(val, ISC_LOG_DEBUG(3), "dlv_validator_start");
3560153816Sdougb
3561153816Sdougb	/*
3562153816Sdougb	 * Reset state and try again.
3563153816Sdougb	 */
3564153816Sdougb	val->attributes &= VALATTR_DLVTRIED;
3565153816Sdougb	val->options &= ~DNS_VALIDATOR_DLV;
3566153816Sdougb
3567153816Sdougb	event = (isc_event_t *)val->event;
3568153816Sdougb	isc_task_send(val->task, &event);
3569153816Sdougb}
3570153816Sdougb
3571165071Sdougb/*%
3572165071Sdougb * Start the validation process.
3573165071Sdougb *
3574193149Sdougb * Attempt to validate the answer based on the category it appears to
3575165071Sdougb * fall in.
3576165071Sdougb * \li	1. secure positive answer.
3577165071Sdougb * \li	2. unsecure positive answer.
3578165071Sdougb * \li	3. a negative answer (secure or unsecure).
3579165071Sdougb *
3580165071Sdougb * Note a answer that appears to be a secure positive answer may actually
3581224092Sdougb * be an unsecure positive answer.
3582165071Sdougb */
3583135446Strhodesstatic void
3584135446Strhodesvalidator_start(isc_task_t *task, isc_event_t *event) {
3585135446Strhodes	dns_validator_t *val;
3586135446Strhodes	dns_validatorevent_t *vevent;
3587135446Strhodes	isc_boolean_t want_destroy = ISC_FALSE;
3588135446Strhodes	isc_result_t result = ISC_R_FAILURE;
3589135446Strhodes
3590135446Strhodes	UNUSED(task);
3591135446Strhodes	REQUIRE(event->ev_type == DNS_EVENT_VALIDATORSTART);
3592135446Strhodes	vevent = (dns_validatorevent_t *)event;
3593135446Strhodes	val = vevent->validator;
3594135446Strhodes
3595193149Sdougb	/* If the validator has been canceled, val->event == NULL */
3596135446Strhodes	if (val->event == NULL)
3597135446Strhodes		return;
3598135446Strhodes
3599153816Sdougb	if (DLVTRIED(val))
3600153816Sdougb		validator_log(val, ISC_LOG_DEBUG(3), "restarting using DLV");
3601153816Sdougb	else
3602153816Sdougb		validator_log(val, ISC_LOG_DEBUG(3), "starting");
3603135446Strhodes
3604135446Strhodes	LOCK(&val->lock);
3605135446Strhodes
3606170222Sdougb	if ((val->options & DNS_VALIDATOR_DLV) != 0 &&
3607170222Sdougb	     val->event->rdataset != NULL) {
3608153816Sdougb		validator_log(val, ISC_LOG_DEBUG(3), "looking for DLV");
3609153816Sdougb		result = startfinddlvsep(val, dns_rootname);
3610153816Sdougb	} else if (val->event->rdataset != NULL &&
3611153816Sdougb		   val->event->sigrdataset != NULL) {
3612135446Strhodes		isc_result_t saved_result;
3613135446Strhodes
3614135446Strhodes		/*
3615135446Strhodes		 * This looks like a simple validation.  We say "looks like"
3616135446Strhodes		 * because it might end up requiring an insecurity proof.
3617135446Strhodes		 */
3618135446Strhodes		validator_log(val, ISC_LOG_DEBUG(3),
3619135446Strhodes			      "attempting positive response validation");
3620135446Strhodes
3621135446Strhodes		INSIST(dns_rdataset_isassociated(val->event->rdataset));
3622135446Strhodes		INSIST(dns_rdataset_isassociated(val->event->sigrdataset));
3623135446Strhodes		result = start_positive_validation(val);
3624135446Strhodes		if (result == DNS_R_NOVALIDSIG &&
3625135446Strhodes		    (val->attributes & VALATTR_TRIEDVERIFY) == 0)
3626135446Strhodes		{
3627135446Strhodes			saved_result = result;
3628135446Strhodes			validator_log(val, ISC_LOG_DEBUG(3),
3629135446Strhodes				      "falling back to insecurity proof");
3630135446Strhodes			val->attributes |= VALATTR_INSECURITY;
3631186462Sdougb			result = proveunsecure(val, ISC_FALSE, ISC_FALSE);
3632135446Strhodes			if (result == DNS_R_NOTINSECURE)
3633135446Strhodes				result = saved_result;
3634135446Strhodes		}
3635214586Sdougb	} else if (val->event->rdataset != NULL &&
3636214586Sdougb		   val->event->rdataset->type != 0) {
3637135446Strhodes		/*
3638135446Strhodes		 * This is either an unsecure subdomain or a response from
3639135446Strhodes		 * a broken server.
3640135446Strhodes		 */
3641135446Strhodes		INSIST(dns_rdataset_isassociated(val->event->rdataset));
3642135446Strhodes		validator_log(val, ISC_LOG_DEBUG(3),
3643135446Strhodes			      "attempting insecurity proof");
3644135446Strhodes
3645135446Strhodes		val->attributes |= VALATTR_INSECURITY;
3646186462Sdougb		result = proveunsecure(val, ISC_FALSE, ISC_FALSE);
3647224092Sdougb		if (result == DNS_R_NOTINSECURE)
3648224092Sdougb			validator_log(val, ISC_LOG_INFO,
3649224092Sdougb				      "got insecure response; "
3650224092Sdougb				      "parent indicates it should be secure");
3651135446Strhodes	} else if (val->event->rdataset == NULL &&
3652135446Strhodes		   val->event->sigrdataset == NULL)
3653135446Strhodes	{
3654135446Strhodes		/*
3655135446Strhodes		 * This is a nonexistence validation.
3656135446Strhodes		 */
3657135446Strhodes		validator_log(val, ISC_LOG_DEBUG(3),
3658135446Strhodes			      "attempting negative response validation");
3659135446Strhodes
3660135446Strhodes		if (val->event->message->rcode == dns_rcode_nxdomain) {
3661135446Strhodes			val->attributes |= VALATTR_NEEDNOQNAME;
3662135446Strhodes			val->attributes |= VALATTR_NEEDNOWILDCARD;
3663135446Strhodes		} else
3664135446Strhodes			val->attributes |= VALATTR_NEEDNODATA;
3665135446Strhodes		result = nsecvalidate(val, ISC_FALSE);
3666214586Sdougb	} else if (val->event->rdataset != NULL &&
3667223812Sdougb		    NEGATIVE(val->event->rdataset))
3668214586Sdougb	{
3669214586Sdougb		/*
3670214586Sdougb		 * This is a nonexistence validation.
3671214586Sdougb		 */
3672214586Sdougb		validator_log(val, ISC_LOG_DEBUG(3),
3673214586Sdougb			      "attempting negative response validation");
3674214586Sdougb
3675214586Sdougb		if (val->event->rdataset->covers == dns_rdatatype_any) {
3676214586Sdougb			val->attributes |= VALATTR_NEEDNOQNAME;
3677214586Sdougb			val->attributes |= VALATTR_NEEDNOWILDCARD;
3678214586Sdougb		} else
3679214586Sdougb			val->attributes |= VALATTR_NEEDNODATA;
3680214586Sdougb		result = nsecvalidate(val, ISC_FALSE);
3681135446Strhodes	} else {
3682135446Strhodes		/*
3683135446Strhodes		 * This shouldn't happen.
3684135446Strhodes		 */
3685135446Strhodes		INSIST(0);
3686135446Strhodes	}
3687135446Strhodes
3688135446Strhodes	if (result != DNS_R_WAIT) {
3689135446Strhodes		want_destroy = exit_check(val);
3690135446Strhodes		validator_done(val, result);
3691135446Strhodes	}
3692135446Strhodes
3693135446Strhodes	UNLOCK(&val->lock);
3694135446Strhodes	if (want_destroy)
3695135446Strhodes		destroy(val);
3696135446Strhodes}
3697135446Strhodes
3698135446Strhodesisc_result_t
3699135446Strhodesdns_validator_create(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type,
3700135446Strhodes		     dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset,
3701135446Strhodes		     dns_message_t *message, unsigned int options,
3702135446Strhodes		     isc_task_t *task, isc_taskaction_t action, void *arg,
3703135446Strhodes		     dns_validator_t **validatorp)
3704135446Strhodes{
3705225361Sdougb	isc_result_t result = ISC_R_FAILURE;
3706135446Strhodes	dns_validator_t *val;
3707225361Sdougb	isc_task_t *tclone = NULL;
3708135446Strhodes	dns_validatorevent_t *event;
3709135446Strhodes
3710135446Strhodes	REQUIRE(name != NULL);
3711135446Strhodes	REQUIRE(rdataset != NULL ||
3712135446Strhodes		(rdataset == NULL && sigrdataset == NULL && message != NULL));
3713135446Strhodes	REQUIRE(validatorp != NULL && *validatorp == NULL);
3714135446Strhodes
3715135446Strhodes	val = isc_mem_get(view->mctx, sizeof(*val));
3716135446Strhodes	if (val == NULL)
3717135446Strhodes		return (ISC_R_NOMEMORY);
3718135446Strhodes	val->view = NULL;
3719135446Strhodes	dns_view_weakattach(view, &val->view);
3720224092Sdougb
3721135446Strhodes	event = (dns_validatorevent_t *)
3722135446Strhodes		isc_event_allocate(view->mctx, task,
3723135446Strhodes				   DNS_EVENT_VALIDATORSTART,
3724135446Strhodes				   validator_start, NULL,
3725135446Strhodes				   sizeof(dns_validatorevent_t));
3726135446Strhodes	if (event == NULL) {
3727135446Strhodes		result = ISC_R_NOMEMORY;
3728135446Strhodes		goto cleanup_val;
3729135446Strhodes	}
3730135446Strhodes	isc_task_attach(task, &tclone);
3731135446Strhodes	event->validator = val;
3732135446Strhodes	event->result = ISC_R_FAILURE;
3733135446Strhodes	event->name = name;
3734135446Strhodes	event->type = type;
3735135446Strhodes	event->rdataset = rdataset;
3736135446Strhodes	event->sigrdataset = sigrdataset;
3737135446Strhodes	event->message = message;
3738135446Strhodes	memset(event->proofs, 0, sizeof(event->proofs));
3739193149Sdougb	event->optout = ISC_FALSE;
3740254402Serwin	event->secure = ISC_FALSE;
3741135446Strhodes	result = isc_mutex_init(&val->lock);
3742135446Strhodes	if (result != ISC_R_SUCCESS)
3743135446Strhodes		goto cleanup_event;
3744135446Strhodes	val->event = event;
3745135446Strhodes	val->options = options;
3746135446Strhodes	val->attributes = 0;
3747135446Strhodes	val->fetch = NULL;
3748135446Strhodes	val->subvalidator = NULL;
3749135446Strhodes	val->parent = NULL;
3750224092Sdougb
3751135446Strhodes	val->keytable = NULL;
3752224092Sdougb	result = dns_view_getsecroots(val->view, &val->keytable);
3753224092Sdougb	if (result != ISC_R_SUCCESS)
3754262706Serwin		goto cleanup_mutex;
3755135446Strhodes	val->keynode = NULL;
3756135446Strhodes	val->key = NULL;
3757135446Strhodes	val->siginfo = NULL;
3758135446Strhodes	val->task = task;
3759135446Strhodes	val->action = action;
3760135446Strhodes	val->arg = arg;
3761135446Strhodes	val->labels = 0;
3762135446Strhodes	val->currentset = NULL;
3763135446Strhodes	val->keyset = NULL;
3764135446Strhodes	val->dsset = NULL;
3765153816Sdougb	dns_rdataset_init(&val->dlv);
3766135446Strhodes	val->seensig = ISC_FALSE;
3767135446Strhodes	val->havedlvsep = ISC_FALSE;
3768153816Sdougb	val->depth = 0;
3769205292Sdougb	val->authcount = 0;
3770205292Sdougb	val->authfail = 0;
3771135446Strhodes	val->mustbesecure = dns_resolver_getmustbesecure(view->resolver, name);
3772135446Strhodes	dns_rdataset_init(&val->frdataset);
3773135446Strhodes	dns_rdataset_init(&val->fsigrdataset);
3774135446Strhodes	dns_fixedname_init(&val->wild);
3775193149Sdougb	dns_fixedname_init(&val->nearest);
3776193149Sdougb	dns_fixedname_init(&val->closest);
3777135446Strhodes	ISC_LINK_INIT(val, link);
3778135446Strhodes	val->magic = VALIDATOR_MAGIC;
3779135446Strhodes
3780166332Sdougb	if ((options & DNS_VALIDATOR_DEFER) == 0)
3781166332Sdougb		isc_task_send(task, ISC_EVENT_PTR(&event));
3782135446Strhodes
3783135446Strhodes	*validatorp = val;
3784135446Strhodes
3785135446Strhodes	return (ISC_R_SUCCESS);
3786135446Strhodes
3787262706Serwin cleanup_mutex:
3788262706Serwin	DESTROYLOCK(&val->lock);
3789262706Serwin
3790135446Strhodes cleanup_event:
3791135446Strhodes	isc_task_detach(&tclone);
3792165071Sdougb	isc_event_free(ISC_EVENT_PTR(&event));
3793135446Strhodes
3794135446Strhodes cleanup_val:
3795135446Strhodes	dns_view_weakdetach(&val->view);
3796135446Strhodes	isc_mem_put(view->mctx, val, sizeof(*val));
3797135446Strhodes
3798135446Strhodes	return (result);
3799135446Strhodes}
3800135446Strhodes
3801135446Strhodesvoid
3802166332Sdougbdns_validator_send(dns_validator_t *validator) {
3803166332Sdougb	isc_event_t *event;
3804166332Sdougb	REQUIRE(VALID_VALIDATOR(validator));
3805166332Sdougb
3806166332Sdougb	LOCK(&validator->lock);
3807166332Sdougb
3808166332Sdougb	INSIST((validator->options & DNS_VALIDATOR_DEFER) != 0);
3809166332Sdougb	event = (isc_event_t *)validator->event;
3810166332Sdougb	validator->options &= ~DNS_VALIDATOR_DEFER;
3811166332Sdougb	UNLOCK(&validator->lock);
3812166332Sdougb
3813166332Sdougb	isc_task_send(validator->task, ISC_EVENT_PTR(&event));
3814166332Sdougb}
3815166332Sdougb
3816166332Sdougbvoid
3817135446Strhodesdns_validator_cancel(dns_validator_t *validator) {
3818254402Serwin	dns_fetch_t *fetch = NULL;
3819254402Serwin
3820135446Strhodes	REQUIRE(VALID_VALIDATOR(validator));
3821135446Strhodes
3822135446Strhodes	LOCK(&validator->lock);
3823135446Strhodes
3824135446Strhodes	validator_log(validator, ISC_LOG_DEBUG(3), "dns_validator_cancel");
3825135446Strhodes
3826218384Sdougb	if ((validator->attributes & VALATTR_CANCELED) == 0) {
3827218384Sdougb		validator->attributes |= VALATTR_CANCELED;
3828218384Sdougb		if (validator->event != NULL) {
3829254402Serwin			fetch = validator->fetch;
3830254402Serwin			validator->fetch = NULL;
3831135446Strhodes
3832218384Sdougb			if (validator->subvalidator != NULL)
3833218384Sdougb				dns_validator_cancel(validator->subvalidator);
3834218384Sdougb			if ((validator->options & DNS_VALIDATOR_DEFER) != 0) {
3835218384Sdougb				validator->options &= ~DNS_VALIDATOR_DEFER;
3836218384Sdougb				validator_done(validator, ISC_R_CANCELED);
3837218384Sdougb			}
3838166332Sdougb		}
3839135446Strhodes	}
3840135446Strhodes	UNLOCK(&validator->lock);
3841254402Serwin
3842254402Serwin	/* Need to cancel and destroy the fetch outside validator lock */
3843254402Serwin	if (fetch != NULL) {
3844254402Serwin		dns_resolver_cancelfetch(fetch);
3845254402Serwin		dns_resolver_destroyfetch(&fetch);
3846254402Serwin	}
3847135446Strhodes}
3848135446Strhodes
3849135446Strhodesstatic void
3850135446Strhodesdestroy(dns_validator_t *val) {
3851135446Strhodes	isc_mem_t *mctx;
3852135446Strhodes
3853135446Strhodes	REQUIRE(SHUTDOWN(val));
3854135446Strhodes	REQUIRE(val->event == NULL);
3855135446Strhodes	REQUIRE(val->fetch == NULL);
3856135446Strhodes
3857135446Strhodes	if (val->keynode != NULL)
3858135446Strhodes		dns_keytable_detachkeynode(val->keytable, &val->keynode);
3859135446Strhodes	else if (val->key != NULL)
3860135446Strhodes		dst_key_free(&val->key);
3861135446Strhodes	if (val->keytable != NULL)
3862135446Strhodes		dns_keytable_detach(&val->keytable);
3863135446Strhodes	if (val->subvalidator != NULL)
3864135446Strhodes		dns_validator_destroy(&val->subvalidator);
3865153816Sdougb	if (val->havedlvsep)
3866153816Sdougb		dns_rdataset_disassociate(&val->dlv);
3867153816Sdougb	if (dns_rdataset_isassociated(&val->frdataset))
3868153816Sdougb		dns_rdataset_disassociate(&val->frdataset);
3869153816Sdougb	if (dns_rdataset_isassociated(&val->fsigrdataset))
3870153816Sdougb		dns_rdataset_disassociate(&val->fsigrdataset);
3871135446Strhodes	mctx = val->view->mctx;
3872135446Strhodes	if (val->siginfo != NULL)
3873135446Strhodes		isc_mem_put(mctx, val->siginfo, sizeof(*val->siginfo));
3874135446Strhodes	DESTROYLOCK(&val->lock);
3875135446Strhodes	dns_view_weakdetach(&val->view);
3876135446Strhodes	val->magic = 0;
3877135446Strhodes	isc_mem_put(mctx, val, sizeof(*val));
3878135446Strhodes}
3879135446Strhodes
3880135446Strhodesvoid
3881135446Strhodesdns_validator_destroy(dns_validator_t **validatorp) {
3882135446Strhodes	dns_validator_t *val;
3883135446Strhodes	isc_boolean_t want_destroy = ISC_FALSE;
3884135446Strhodes
3885135446Strhodes	REQUIRE(validatorp != NULL);
3886135446Strhodes	val = *validatorp;
3887135446Strhodes	REQUIRE(VALID_VALIDATOR(val));
3888135446Strhodes
3889135446Strhodes	LOCK(&val->lock);
3890135446Strhodes
3891135446Strhodes	val->attributes |= VALATTR_SHUTDOWN;
3892135446Strhodes	validator_log(val, ISC_LOG_DEBUG(3), "dns_validator_destroy");
3893135446Strhodes
3894135446Strhodes	want_destroy = exit_check(val);
3895135446Strhodes
3896135446Strhodes	UNLOCK(&val->lock);
3897135446Strhodes
3898135446Strhodes	if (want_destroy)
3899135446Strhodes		destroy(val);
3900135446Strhodes
3901135446Strhodes	*validatorp = NULL;
3902135446Strhodes}
3903135446Strhodes
3904135446Strhodesstatic void
3905135446Strhodesvalidator_logv(dns_validator_t *val, isc_logcategory_t *category,
3906135446Strhodes	       isc_logmodule_t *module, int level, const char *fmt, va_list ap)
3907135446Strhodes{
3908135446Strhodes	char msgbuf[2048];
3909153816Sdougb	static const char spaces[] = "        *";
3910153816Sdougb	int depth = val->depth * 2;
3911135446Strhodes
3912135446Strhodes	vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
3913135446Strhodes
3914153816Sdougb	if ((unsigned int) depth >= sizeof spaces)
3915153816Sdougb		depth = sizeof spaces - 1;
3916153816Sdougb
3917135446Strhodes	if (val->event != NULL && val->event->name != NULL) {
3918135446Strhodes		char namebuf[DNS_NAME_FORMATSIZE];
3919135446Strhodes		char typebuf[DNS_RDATATYPE_FORMATSIZE];
3920135446Strhodes
3921135446Strhodes		dns_name_format(val->event->name, namebuf, sizeof(namebuf));
3922135446Strhodes		dns_rdatatype_format(val->event->type, typebuf,
3923135446Strhodes				     sizeof(typebuf));
3924135446Strhodes		isc_log_write(dns_lctx, category, module, level,
3925153816Sdougb			      "%.*svalidating @%p: %s %s: %s", depth, spaces,
3926153816Sdougb			      val, namebuf, typebuf, msgbuf);
3927135446Strhodes	} else {
3928135446Strhodes		isc_log_write(dns_lctx, category, module, level,
3929153816Sdougb			      "%.*svalidator @%p: %s", depth, spaces,
3930153816Sdougb			       val, msgbuf);
3931135446Strhodes	}
3932135446Strhodes}
3933135446Strhodes
3934135446Strhodesstatic void
3935254402Serwinvalidator_log(void *val, int level, const char *fmt, ...) {
3936135446Strhodes	va_list ap;
3937135446Strhodes
3938135446Strhodes	if (! isc_log_wouldlog(dns_lctx, level))
3939135446Strhodes		return;
3940135446Strhodes
3941135446Strhodes	va_start(ap, fmt);
3942135446Strhodes
3943135446Strhodes	validator_logv(val, DNS_LOGCATEGORY_DNSSEC,
3944135446Strhodes		       DNS_LOGMODULE_VALIDATOR, level, fmt, ap);
3945135446Strhodes	va_end(ap);
3946135446Strhodes}
3947135446Strhodes
3948135446Strhodesstatic void
3949135446Strhodesvalidator_logcreate(dns_validator_t *val,
3950135446Strhodes		    dns_name_t *name, dns_rdatatype_t type,
3951135446Strhodes		    const char *caller, const char *operation)
3952135446Strhodes{
3953135446Strhodes	char namestr[DNS_NAME_FORMATSIZE];
3954135446Strhodes	char typestr[DNS_RDATATYPE_FORMATSIZE];
3955135446Strhodes
3956135446Strhodes	dns_name_format(name, namestr, sizeof(namestr));
3957135446Strhodes	dns_rdatatype_format(type, typestr, sizeof(typestr));
3958135446Strhodes	validator_log(val, ISC_LOG_DEBUG(9), "%s: creating %s for %s %s",
3959135446Strhodes		      caller, operation, namestr, typestr);
3960135446Strhodes}
3961