1135446Strhodes/*
2262706Serwin * Copyright (C) 2004, 2005, 2007, 2009-2014  Internet Systems Consortium, Inc. ("ISC")
3135446Strhodes * Copyright (C) 2000, 2001, 2003  Internet Software Consortium.
4135446Strhodes *
5193149Sdougb * 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
18254897Serwin/* $Id: dnssectool.c,v 1.63 2011/10/21 03:55:33 marka Exp $ */
19135446Strhodes
20170222Sdougb/*! \file */
21170222Sdougb
22170222Sdougb/*%
23170222Sdougb * DNSSEC Support Routines.
24170222Sdougb */
25170222Sdougb
26135446Strhodes#include <config.h>
27135446Strhodes
28135446Strhodes#include <stdlib.h>
29135446Strhodes
30254897Serwin#include <isc/base32.h>
31135446Strhodes#include <isc/buffer.h>
32224092Sdougb#include <isc/dir.h>
33135446Strhodes#include <isc/entropy.h>
34254897Serwin#include <isc/heap.h>
35135446Strhodes#include <isc/list.h>
36135446Strhodes#include <isc/mem.h>
37135446Strhodes#include <isc/string.h>
38135446Strhodes#include <isc/time.h>
39135446Strhodes#include <isc/util.h>
40135446Strhodes#include <isc/print.h>
41135446Strhodes
42254897Serwin#include <dns/db.h>
43254897Serwin#include <dns/dbiterator.h>
44224092Sdougb#include <dns/dnssec.h>
45254897Serwin#include <dns/fixedname.h>
46224092Sdougb#include <dns/keyvalues.h>
47135446Strhodes#include <dns/log.h>
48135446Strhodes#include <dns/name.h>
49254897Serwin#include <dns/nsec.h>
50254897Serwin#include <dns/nsec3.h>
51135446Strhodes#include <dns/rdatastruct.h>
52135446Strhodes#include <dns/rdataclass.h>
53254897Serwin#include <dns/rdataset.h>
54254897Serwin#include <dns/rdatasetiter.h>
55135446Strhodes#include <dns/rdatatype.h>
56135446Strhodes#include <dns/result.h>
57135446Strhodes#include <dns/secalg.h>
58135446Strhodes#include <dns/time.h>
59135446Strhodes
60135446Strhodes#include "dnssectool.h"
61135446Strhodes
62254897Serwinstatic isc_heap_t *expected_chains, *found_chains;
63254897Serwin
64254897Serwinstruct nsec3_chain_fixed {
65254897Serwin	isc_uint8_t	hash;
66254897Serwin	isc_uint8_t	salt_length;
67254897Serwin	isc_uint8_t	next_length;
68254897Serwin	isc_uint16_t	iterations;
69254897Serwin	/* unsigned char salt[0]; */
70254897Serwin	/* unsigned char owner[0]; */
71254897Serwin	/* unsigned char next[0]; */
72254897Serwin};
73254897Serwin
74135446Strhodesextern int verbose;
75135446Strhodesextern const char *program;
76135446Strhodes
77135446Strhodestypedef struct entropysource entropysource_t;
78135446Strhodes
79135446Strhodesstruct entropysource {
80135446Strhodes	isc_entropysource_t *source;
81135446Strhodes	isc_mem_t *mctx;
82135446Strhodes	ISC_LINK(entropysource_t) link;
83135446Strhodes};
84135446Strhodes
85135446Strhodesstatic ISC_LIST(entropysource_t) sources;
86135446Strhodesstatic fatalcallback_t *fatalcallback = NULL;
87135446Strhodes
88135446Strhodesvoid
89135446Strhodesfatal(const char *format, ...) {
90135446Strhodes	va_list args;
91135446Strhodes
92204619Sdougb	fprintf(stderr, "%s: fatal: ", program);
93135446Strhodes	va_start(args, format);
94135446Strhodes	vfprintf(stderr, format, args);
95135446Strhodes	va_end(args);
96135446Strhodes	fprintf(stderr, "\n");
97135446Strhodes	if (fatalcallback != NULL)
98135446Strhodes		(*fatalcallback)();
99135446Strhodes	exit(1);
100135446Strhodes}
101135446Strhodes
102135446Strhodesvoid
103135446Strhodessetfatalcallback(fatalcallback_t *callback) {
104135446Strhodes	fatalcallback = callback;
105135446Strhodes}
106135446Strhodes
107135446Strhodesvoid
108135446Strhodescheck_result(isc_result_t result, const char *message) {
109135446Strhodes	if (result != ISC_R_SUCCESS)
110135446Strhodes		fatal("%s: %s", message, isc_result_totext(result));
111135446Strhodes}
112135446Strhodes
113135446Strhodesvoid
114135446Strhodesvbprintf(int level, const char *fmt, ...) {
115135446Strhodes	va_list ap;
116135446Strhodes	if (level > verbose)
117135446Strhodes		return;
118135446Strhodes	va_start(ap, fmt);
119135446Strhodes	fprintf(stderr, "%s: ", program);
120135446Strhodes	vfprintf(stderr, fmt, ap);
121135446Strhodes	va_end(ap);
122135446Strhodes}
123135446Strhodes
124135446Strhodesvoid
125135446Strhodestype_format(const dns_rdatatype_t type, char *cp, unsigned int size) {
126135446Strhodes	isc_buffer_t b;
127135446Strhodes	isc_region_t r;
128135446Strhodes	isc_result_t result;
129135446Strhodes
130135446Strhodes	isc_buffer_init(&b, cp, size - 1);
131135446Strhodes	result = dns_rdatatype_totext(type, &b);
132135446Strhodes	check_result(result, "dns_rdatatype_totext()");
133135446Strhodes	isc_buffer_usedregion(&b, &r);
134135446Strhodes	r.base[r.length] = 0;
135135446Strhodes}
136135446Strhodes
137135446Strhodesvoid
138135446Strhodessig_format(dns_rdata_rrsig_t *sig, char *cp, unsigned int size) {
139135446Strhodes	char namestr[DNS_NAME_FORMATSIZE];
140135446Strhodes	char algstr[DNS_NAME_FORMATSIZE];
141135446Strhodes
142135446Strhodes	dns_name_format(&sig->signer, namestr, sizeof(namestr));
143224092Sdougb	dns_secalg_format(sig->algorithm, algstr, sizeof(algstr));
144135446Strhodes	snprintf(cp, size, "%s/%s/%d", namestr, algstr, sig->keyid);
145135446Strhodes}
146135446Strhodes
147135446Strhodesvoid
148135446Strhodessetup_logging(int verbose, isc_mem_t *mctx, isc_log_t **logp) {
149135446Strhodes	isc_result_t result;
150135446Strhodes	isc_logdestination_t destination;
151135446Strhodes	isc_logconfig_t *logconfig = NULL;
152135446Strhodes	isc_log_t *log = NULL;
153135446Strhodes	int level;
154135446Strhodes
155153816Sdougb	if (verbose < 0)
156153816Sdougb		verbose = 0;
157135446Strhodes	switch (verbose) {
158135446Strhodes	case 0:
159135446Strhodes		/*
160135446Strhodes		 * We want to see warnings about things like out-of-zone
161135446Strhodes		 * data in the master file even when not verbose.
162135446Strhodes		 */
163135446Strhodes		level = ISC_LOG_WARNING;
164135446Strhodes		break;
165135446Strhodes	case 1:
166135446Strhodes		level = ISC_LOG_INFO;
167135446Strhodes		break;
168135446Strhodes	default:
169135446Strhodes		level = ISC_LOG_DEBUG(verbose - 2 + 1);
170135446Strhodes		break;
171135446Strhodes	}
172135446Strhodes
173135446Strhodes	RUNTIME_CHECK(isc_log_create(mctx, &log, &logconfig) == ISC_R_SUCCESS);
174135446Strhodes	isc_log_setcontext(log);
175135446Strhodes	dns_log_init(log);
176135446Strhodes	dns_log_setcontext(log);
177135446Strhodes
178135446Strhodes	RUNTIME_CHECK(isc_log_settag(logconfig, program) == ISC_R_SUCCESS);
179135446Strhodes
180135446Strhodes	/*
181135446Strhodes	 * Set up a channel similar to default_stderr except:
182135446Strhodes	 *  - the logging level is passed in
183135446Strhodes	 *  - the program name and logging level are printed
184135446Strhodes	 *  - no time stamp is printed
185135446Strhodes	 */
186135446Strhodes	destination.file.stream = stderr;
187135446Strhodes	destination.file.name = NULL;
188135446Strhodes	destination.file.versions = ISC_LOG_ROLLNEVER;
189135446Strhodes	destination.file.maximum_size = 0;
190135446Strhodes	result = isc_log_createchannel(logconfig, "stderr",
191135446Strhodes				       ISC_LOG_TOFILEDESC,
192135446Strhodes				       level,
193135446Strhodes				       &destination,
194135446Strhodes				       ISC_LOG_PRINTTAG|ISC_LOG_PRINTLEVEL);
195135446Strhodes	check_result(result, "isc_log_createchannel()");
196135446Strhodes
197135446Strhodes	RUNTIME_CHECK(isc_log_usechannel(logconfig, "stderr",
198135446Strhodes					 NULL, NULL) == ISC_R_SUCCESS);
199135446Strhodes
200135446Strhodes	*logp = log;
201135446Strhodes}
202135446Strhodes
203135446Strhodesvoid
204135446Strhodescleanup_logging(isc_log_t **logp) {
205135446Strhodes	isc_log_t *log;
206135446Strhodes
207135446Strhodes	REQUIRE(logp != NULL);
208135446Strhodes
209135446Strhodes	log = *logp;
210135446Strhodes	if (log == NULL)
211135446Strhodes		return;
212135446Strhodes	isc_log_destroy(&log);
213135446Strhodes	isc_log_setcontext(NULL);
214135446Strhodes	dns_log_setcontext(NULL);
215135446Strhodes	logp = NULL;
216135446Strhodes}
217135446Strhodes
218135446Strhodesvoid
219135446Strhodessetup_entropy(isc_mem_t *mctx, const char *randomfile, isc_entropy_t **ectx) {
220135446Strhodes	isc_result_t result;
221135446Strhodes	isc_entropysource_t *source = NULL;
222135446Strhodes	entropysource_t *elt;
223135446Strhodes	int usekeyboard = ISC_ENTROPY_KEYBOARDMAYBE;
224135446Strhodes
225135446Strhodes	REQUIRE(ectx != NULL);
226194995Sdougb
227135446Strhodes	if (*ectx == NULL) {
228135446Strhodes		result = isc_entropy_create(mctx, ectx);
229135446Strhodes		if (result != ISC_R_SUCCESS)
230135446Strhodes			fatal("could not create entropy object");
231135446Strhodes		ISC_LIST_INIT(sources);
232135446Strhodes	}
233135446Strhodes
234135446Strhodes	if (randomfile != NULL && strcmp(randomfile, "keyboard") == 0) {
235135446Strhodes		usekeyboard = ISC_ENTROPY_KEYBOARDYES;
236135446Strhodes		randomfile = NULL;
237135446Strhodes	}
238135446Strhodes
239135446Strhodes	result = isc_entropy_usebestsource(*ectx, &source, randomfile,
240135446Strhodes					   usekeyboard);
241135446Strhodes
242135446Strhodes	if (result != ISC_R_SUCCESS)
243135446Strhodes		fatal("could not initialize entropy source: %s",
244135446Strhodes		      isc_result_totext(result));
245135446Strhodes
246135446Strhodes	if (source != NULL) {
247135446Strhodes		elt = isc_mem_get(mctx, sizeof(*elt));
248135446Strhodes		if (elt == NULL)
249135446Strhodes			fatal("out of memory");
250135446Strhodes		elt->source = source;
251135446Strhodes		elt->mctx = mctx;
252135446Strhodes		ISC_LINK_INIT(elt, link);
253135446Strhodes		ISC_LIST_APPEND(sources, elt, link);
254135446Strhodes	}
255135446Strhodes}
256135446Strhodes
257135446Strhodesvoid
258135446Strhodescleanup_entropy(isc_entropy_t **ectx) {
259135446Strhodes	entropysource_t *source;
260135446Strhodes	while (!ISC_LIST_EMPTY(sources)) {
261135446Strhodes		source = ISC_LIST_HEAD(sources);
262135446Strhodes		ISC_LIST_UNLINK(sources, source, link);
263135446Strhodes		isc_entropy_destroysource(&source->source);
264135446Strhodes		isc_mem_put(source->mctx, source, sizeof(*source));
265135446Strhodes	}
266135446Strhodes	isc_entropy_detach(ectx);
267135446Strhodes}
268135446Strhodes
269224092Sdougbstatic isc_stdtime_t
270224092Sdougbtime_units(isc_stdtime_t offset, char *suffix, const char *str) {
271224092Sdougb	switch (suffix[0]) {
272224092Sdougb	    case 'Y': case 'y':
273224092Sdougb		return (offset * (365 * 24 * 3600));
274224092Sdougb	    case 'M': case 'm':
275224092Sdougb		switch (suffix[1]) {
276224092Sdougb		    case 'O': case 'o':
277224092Sdougb			return (offset * (30 * 24 * 3600));
278224092Sdougb		    case 'I': case 'i':
279224092Sdougb			return (offset * 60);
280224092Sdougb		    case '\0':
281224092Sdougb			fatal("'%s' ambiguous: use 'mi' for minutes "
282224092Sdougb			      "or 'mo' for months", str);
283224092Sdougb		    default:
284224092Sdougb			fatal("time value %s is invalid", str);
285224092Sdougb		}
286224092Sdougb		/* NOTREACHED */
287224092Sdougb		break;
288224092Sdougb	    case 'W': case 'w':
289224092Sdougb		return (offset * (7 * 24 * 3600));
290224092Sdougb	    case 'D': case 'd':
291224092Sdougb		return (offset * (24 * 3600));
292224092Sdougb	    case 'H': case 'h':
293224092Sdougb		return (offset * 3600);
294224092Sdougb	    case 'S': case 's': case '\0':
295224092Sdougb		return (offset);
296224092Sdougb	    default:
297224092Sdougb		fatal("time value %s is invalid", str);
298224092Sdougb	}
299224092Sdougb	/* NOTREACHED */
300224092Sdougb	return(0); /* silence compiler warning */
301224092Sdougb}
302224092Sdougb
303224092Sdougbdns_ttl_t
304224092Sdougbstrtottl(const char *str) {
305224092Sdougb	const char *orig = str;
306224092Sdougb	dns_ttl_t ttl;
307224092Sdougb	char *endp;
308224092Sdougb
309224092Sdougb	ttl = strtol(str, &endp, 0);
310224092Sdougb	if (ttl == 0 && endp == str)
311224092Sdougb		fatal("TTL must be numeric");
312224092Sdougb	ttl = time_units(ttl, endp, orig);
313224092Sdougb	return (ttl);
314224092Sdougb}
315224092Sdougb
316135446Strhodesisc_stdtime_t
317135446Strhodesstrtotime(const char *str, isc_int64_t now, isc_int64_t base) {
318135446Strhodes	isc_int64_t val, offset;
319135446Strhodes	isc_result_t result;
320224092Sdougb	const char *orig = str;
321135446Strhodes	char *endp;
322262706Serwin	int n;
323135446Strhodes
324224092Sdougb	if ((str[0] == '0' || str[0] == '-') && str[1] == '\0')
325224092Sdougb		return ((isc_stdtime_t) 0);
326224092Sdougb
327262706Serwin	/*
328262706Serwin	 * We accept times in the following formats:
329262706Serwin	 *   now([+-]offset)
330262706Serwin	 *   YYYYMMDD([+-]offset)
331262706Serwin	 *   YYYYMMDDhhmmss([+-]offset)
332262706Serwin	 *   [+-]offset
333262706Serwin	 */
334262706Serwin	n = strspn(str, "0123456789");
335262706Serwin	if ((n == 8 || n == 14) &&
336262706Serwin	    (str[n] == '\0' || str[n] == '-' || str[n] == '+'))
337262706Serwin	{
338262706Serwin		char timestr[15];
339262706Serwin
340262706Serwin		strlcpy(timestr, str, sizeof(timestr));
341262706Serwin		timestr[n] = 0;
342262706Serwin		if (n == 8)
343262706Serwin			strlcat(timestr, "000000", sizeof(timestr));
344262706Serwin		result = dns_time64_fromtext(timestr, &val);
345262706Serwin		if (result != ISC_R_SUCCESS)
346262706Serwin			fatal("time value %s is invalid: %s", orig,
347262706Serwin			      isc_result_totext(result));
348262706Serwin		base = val;
349262706Serwin		str += n;
350262706Serwin	} else if (strncmp(str, "now", 3) == 0) {
351224092Sdougb		base = now;
352224092Sdougb		str += 3;
353224092Sdougb	}
354224092Sdougb
355224092Sdougb	if (str[0] == '\0')
356224092Sdougb		return ((isc_stdtime_t) base);
357224092Sdougb	else if (str[0] == '+') {
358135446Strhodes		offset = strtol(str + 1, &endp, 0);
359224092Sdougb		offset = time_units((isc_stdtime_t) offset, endp, orig);
360135446Strhodes		val = base + offset;
361224092Sdougb	} else if (str[0] == '-') {
362224092Sdougb		offset = strtol(str + 1, &endp, 0);
363224092Sdougb		offset = time_units((isc_stdtime_t) offset, endp, orig);
364224092Sdougb		val = base - offset;
365262706Serwin	} else
366224092Sdougb		fatal("time value %s is invalid", orig);
367135446Strhodes
368135446Strhodes	return ((isc_stdtime_t) val);
369135446Strhodes}
370135446Strhodes
371135446Strhodesdns_rdataclass_t
372135446Strhodesstrtoclass(const char *str) {
373135446Strhodes	isc_textregion_t r;
374135446Strhodes	dns_rdataclass_t rdclass;
375135446Strhodes	isc_result_t ret;
376135446Strhodes
377135446Strhodes	if (str == NULL)
378135446Strhodes		return dns_rdataclass_in;
379135446Strhodes	DE_CONST(str, r.base);
380135446Strhodes	r.length = strlen(str);
381135446Strhodes	ret = dns_rdataclass_fromtext(&rdclass, &r);
382135446Strhodes	if (ret != ISC_R_SUCCESS)
383135446Strhodes		fatal("unknown class %s", str);
384135446Strhodes	return (rdclass);
385135446Strhodes}
386224092Sdougb
387224092Sdougbisc_result_t
388224092Sdougbtry_dir(const char *dirname) {
389224092Sdougb	isc_result_t result;
390224092Sdougb	isc_dir_t d;
391224092Sdougb
392224092Sdougb	isc_dir_init(&d);
393224092Sdougb	result = isc_dir_open(&d, dirname);
394224092Sdougb	if (result == ISC_R_SUCCESS) {
395224092Sdougb		isc_dir_close(&d);
396224092Sdougb	}
397224092Sdougb	return (result);
398224092Sdougb}
399224092Sdougb
400224092Sdougb/*
401224092Sdougb * Check private key version compatibility.
402224092Sdougb */
403224092Sdougbvoid
404224092Sdougbcheck_keyversion(dst_key_t *key, char *keystr) {
405224092Sdougb	int major, minor;
406224092Sdougb	dst_key_getprivateformat(key, &major, &minor);
407224092Sdougb	INSIST(major <= DST_MAJOR_VERSION); /* invalid private key */
408224092Sdougb
409224092Sdougb	if (major < DST_MAJOR_VERSION || minor < DST_MINOR_VERSION)
410224092Sdougb		fatal("Key %s has incompatible format version %d.%d, "
411224092Sdougb		      "use -f to force upgrade to new version.",
412224092Sdougb		      keystr, major, minor);
413224092Sdougb	if (minor > DST_MINOR_VERSION)
414224092Sdougb		fatal("Key %s has incompatible format version %d.%d, "
415224092Sdougb		      "use -f to force downgrade to current version.",
416224092Sdougb		      keystr, major, minor);
417224092Sdougb}
418224092Sdougb
419224092Sdougbvoid
420224092Sdougbset_keyversion(dst_key_t *key) {
421224092Sdougb	int major, minor;
422224092Sdougb	dst_key_getprivateformat(key, &major, &minor);
423224092Sdougb	INSIST(major <= DST_MAJOR_VERSION);
424224092Sdougb
425224092Sdougb	if (major != DST_MAJOR_VERSION || minor != DST_MINOR_VERSION)
426224092Sdougb		dst_key_setprivateformat(key, DST_MAJOR_VERSION,
427224092Sdougb					 DST_MINOR_VERSION);
428224092Sdougb
429224092Sdougb	/*
430224092Sdougb	 * If the key is from a version older than 1.3, set
431224092Sdougb	 * set the creation date
432224092Sdougb	 */
433224092Sdougb	if (major < 1 || (major == 1 && minor <= 2)) {
434224092Sdougb		isc_stdtime_t now;
435224092Sdougb		isc_stdtime_get(&now);
436224092Sdougb		dst_key_settime(key, DST_TIME_CREATED, now);
437224092Sdougb	}
438224092Sdougb}
439224092Sdougb
440224092Sdougbisc_boolean_t
441234010Sdougbkey_collision(dst_key_t *dstkey, dns_name_t *name, const char *dir,
442234010Sdougb	      isc_mem_t *mctx, isc_boolean_t *exact)
443224092Sdougb{
444224092Sdougb	isc_result_t result;
445224092Sdougb	isc_boolean_t conflict = ISC_FALSE;
446224092Sdougb	dns_dnsseckeylist_t matchkeys;
447224092Sdougb	dns_dnsseckey_t *key = NULL;
448234010Sdougb	isc_uint16_t id, oldid;
449234010Sdougb	isc_uint32_t rid, roldid;
450234010Sdougb	dns_secalg_t alg;
451224092Sdougb
452224092Sdougb	if (exact != NULL)
453224092Sdougb		*exact = ISC_FALSE;
454224092Sdougb
455234010Sdougb	id = dst_key_id(dstkey);
456234010Sdougb	rid = dst_key_rid(dstkey);
457234010Sdougb	alg = dst_key_alg(dstkey);
458234010Sdougb
459224092Sdougb	ISC_LIST_INIT(matchkeys);
460224092Sdougb	result = dns_dnssec_findmatchingkeys(name, dir, mctx, &matchkeys);
461224092Sdougb	if (result == ISC_R_NOTFOUND)
462224092Sdougb		return (ISC_FALSE);
463224092Sdougb
464224092Sdougb	while (!ISC_LIST_EMPTY(matchkeys) && !conflict) {
465224092Sdougb		key = ISC_LIST_HEAD(matchkeys);
466224092Sdougb		if (dst_key_alg(key->key) != alg)
467224092Sdougb			goto next;
468224092Sdougb
469224092Sdougb		oldid = dst_key_id(key->key);
470234010Sdougb		roldid = dst_key_rid(key->key);
471234010Sdougb
472234010Sdougb		if (oldid == rid || roldid == id || id == oldid) {
473224092Sdougb			conflict = ISC_TRUE;
474234010Sdougb			if (id != oldid) {
475224092Sdougb				if (verbose > 1)
476224092Sdougb					fprintf(stderr, "Key ID %d could "
477224092Sdougb						"collide with %d\n",
478224092Sdougb						id, oldid);
479224092Sdougb			} else {
480224092Sdougb				if (exact != NULL)
481224092Sdougb					*exact = ISC_TRUE;
482224092Sdougb				if (verbose > 1)
483224092Sdougb					fprintf(stderr, "Key ID %d exists\n",
484224092Sdougb						id);
485224092Sdougb			}
486224092Sdougb		}
487224092Sdougb
488224092Sdougb next:
489224092Sdougb		ISC_LIST_UNLINK(matchkeys, key, link);
490224092Sdougb		dns_dnsseckey_destroy(mctx, &key);
491224092Sdougb	}
492224092Sdougb
493224092Sdougb	/* Finish freeing the list */
494224092Sdougb	while (!ISC_LIST_EMPTY(matchkeys)) {
495224092Sdougb		key = ISC_LIST_HEAD(matchkeys);
496224092Sdougb		ISC_LIST_UNLINK(matchkeys, key, link);
497224092Sdougb		dns_dnsseckey_destroy(mctx, &key);
498224092Sdougb	}
499224092Sdougb
500224092Sdougb	return (conflict);
501224092Sdougb}
502254897Serwin
503254897Serwinisc_boolean_t
504254897Serwinis_delegation(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin,
505254897Serwin	      dns_name_t *name, dns_dbnode_t *node, isc_uint32_t *ttlp)
506254897Serwin{
507254897Serwin	dns_rdataset_t nsset;
508254897Serwin	isc_result_t result;
509254897Serwin
510254897Serwin	if (dns_name_equal(name, origin))
511254897Serwin		return (ISC_FALSE);
512254897Serwin
513254897Serwin	dns_rdataset_init(&nsset);
514254897Serwin	result = dns_db_findrdataset(db, node, ver, dns_rdatatype_ns,
515254897Serwin				     0, 0, &nsset, NULL);
516254897Serwin	if (dns_rdataset_isassociated(&nsset)) {
517254897Serwin		if (ttlp != NULL)
518254897Serwin			*ttlp = nsset.ttl;
519254897Serwin		dns_rdataset_disassociate(&nsset);
520254897Serwin	}
521254897Serwin
522254897Serwin	return (ISC_TF(result == ISC_R_SUCCESS));
523254897Serwin}
524254897Serwin
525254897Serwinstatic isc_boolean_t
526254897Serwingoodsig(dns_name_t *origin, dns_rdata_t *sigrdata, dns_name_t *name,
527254897Serwin	dns_rdataset_t *keyrdataset, dns_rdataset_t *rdataset, isc_mem_t *mctx)
528254897Serwin{
529254897Serwin	dns_rdata_dnskey_t key;
530254897Serwin	dns_rdata_rrsig_t sig;
531254897Serwin	dst_key_t *dstkey = NULL;
532254897Serwin	isc_result_t result;
533254897Serwin
534254897Serwin	result = dns_rdata_tostruct(sigrdata, &sig, NULL);
535254897Serwin	check_result(result, "dns_rdata_tostruct()");
536254897Serwin
537254897Serwin	for (result = dns_rdataset_first(keyrdataset);
538254897Serwin	     result == ISC_R_SUCCESS;
539254897Serwin	     result = dns_rdataset_next(keyrdataset)) {
540254897Serwin		dns_rdata_t rdata = DNS_RDATA_INIT;
541254897Serwin		dns_rdataset_current(keyrdataset, &rdata);
542254897Serwin		result = dns_rdata_tostruct(&rdata, &key, NULL);
543254897Serwin		check_result(result, "dns_rdata_tostruct()");
544254897Serwin		result = dns_dnssec_keyfromrdata(origin, &rdata, mctx,
545254897Serwin						 &dstkey);
546254897Serwin		if (result != ISC_R_SUCCESS)
547254897Serwin			return (ISC_FALSE);
548254897Serwin		if (sig.algorithm != key.algorithm ||
549254897Serwin		    sig.keyid != dst_key_id(dstkey) ||
550254897Serwin		    !dns_name_equal(&sig.signer, origin)) {
551254897Serwin			dst_key_free(&dstkey);
552254897Serwin			continue;
553254897Serwin		}
554254897Serwin		result = dns_dnssec_verify(name, rdataset, dstkey, ISC_FALSE,
555254897Serwin					   mctx, sigrdata);
556254897Serwin		dst_key_free(&dstkey);
557254897Serwin		if (result == ISC_R_SUCCESS)
558254897Serwin			return(ISC_TRUE);
559254897Serwin	}
560254897Serwin	return (ISC_FALSE);
561254897Serwin}
562254897Serwin
563254897Serwinstatic isc_result_t
564254897Serwinverifynsec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
565254897Serwin	   dns_dbnode_t *node, dns_name_t *nextname)
566254897Serwin{
567254897Serwin	unsigned char buffer[DNS_NSEC_BUFFERSIZE];
568254897Serwin	char namebuf[DNS_NAME_FORMATSIZE];
569254897Serwin	char nextbuf[DNS_NAME_FORMATSIZE];
570254897Serwin	char found[DNS_NAME_FORMATSIZE];
571254897Serwin	dns_rdataset_t rdataset;
572254897Serwin	dns_rdata_t rdata = DNS_RDATA_INIT;
573254897Serwin	dns_rdata_t tmprdata = DNS_RDATA_INIT;
574254897Serwin	dns_rdata_nsec_t nsec;
575254897Serwin	isc_result_t result;
576254897Serwin
577254897Serwin	dns_rdataset_init(&rdataset);
578254897Serwin	result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec,
579254897Serwin				     0, 0, &rdataset, NULL);
580254897Serwin	if (result != ISC_R_SUCCESS) {
581254897Serwin		dns_name_format(name, namebuf, sizeof(namebuf));
582254897Serwin		fprintf(stderr, "Missing NSEC record for %s\n", namebuf);
583254897Serwin		goto failure;
584254897Serwin	}
585254897Serwin
586254897Serwin	result = dns_rdataset_first(&rdataset);
587254897Serwin	check_result(result, "dns_rdataset_first()");
588254897Serwin
589254897Serwin	dns_rdataset_current(&rdataset, &rdata);
590254897Serwin	result = dns_rdata_tostruct(&rdata, &nsec, NULL);
591254897Serwin	check_result(result, "dns_rdata_tostruct()");
592254897Serwin	/* Check bit next name is consistent */
593254897Serwin	if (!dns_name_equal(&nsec.next, nextname)) {
594254897Serwin		dns_name_format(name, namebuf, sizeof(namebuf));
595254897Serwin		dns_name_format(nextname, nextbuf, sizeof(nextbuf));
596254897Serwin		dns_name_format(&nsec.next, found, sizeof(found));
597254897Serwin		fprintf(stderr, "Bad NSEC record for %s, next name "
598254897Serwin				"mismatch (expected:%s, found:%s)\n", namebuf,
599254897Serwin				nextbuf, found);
600254897Serwin		goto failure;
601254897Serwin	}
602254897Serwin	/* Check bit map is consistent */
603254897Serwin	result = dns_nsec_buildrdata(db, ver, node, nextname, buffer,
604254897Serwin				     &tmprdata);
605254897Serwin	check_result(result, "dns_nsec_buildrdata()");
606254897Serwin	if (dns_rdata_compare(&rdata, &tmprdata) != 0) {
607254897Serwin		dns_name_format(name, namebuf, sizeof(namebuf));
608254897Serwin		fprintf(stderr, "Bad NSEC record for %s, bit map "
609254897Serwin				"mismatch\n", namebuf);
610254897Serwin		goto failure;
611254897Serwin	}
612254897Serwin	result = dns_rdataset_next(&rdataset);
613254897Serwin	if (result != ISC_R_NOMORE) {
614254897Serwin		dns_name_format(name, namebuf, sizeof(namebuf));
615254897Serwin		fprintf(stderr, "Multipe NSEC records for %s\n", namebuf);
616254897Serwin		goto failure;
617254897Serwin
618254897Serwin	}
619254897Serwin	dns_rdataset_disassociate(&rdataset);
620254897Serwin	return (ISC_R_SUCCESS);
621254897Serwin failure:
622254897Serwin	if (dns_rdataset_isassociated(&rdataset))
623254897Serwin		dns_rdataset_disassociate(&rdataset);
624254897Serwin	return (ISC_R_FAILURE);
625254897Serwin}
626254897Serwin
627254897Serwinstatic void
628254897Serwincheck_no_rrsig(dns_db_t *db, dns_dbversion_t *ver, dns_rdataset_t *rdataset,
629254897Serwin	       dns_name_t *name, dns_dbnode_t *node)
630254897Serwin{
631254897Serwin	char namebuf[DNS_NAME_FORMATSIZE];
632254897Serwin	char typebuf[80];
633254897Serwin	dns_rdataset_t sigrdataset;
634254897Serwin	dns_rdatasetiter_t *rdsiter = NULL;
635254897Serwin	isc_result_t result;
636254897Serwin
637254897Serwin	dns_rdataset_init(&sigrdataset);
638254897Serwin	result = dns_db_allrdatasets(db, node, ver, 0, &rdsiter);
639254897Serwin	check_result(result, "dns_db_allrdatasets()");
640254897Serwin	for (result = dns_rdatasetiter_first(rdsiter);
641254897Serwin	     result == ISC_R_SUCCESS;
642254897Serwin	     result = dns_rdatasetiter_next(rdsiter)) {
643254897Serwin		dns_rdatasetiter_current(rdsiter, &sigrdataset);
644254897Serwin		if (sigrdataset.type == dns_rdatatype_rrsig &&
645254897Serwin		    sigrdataset.covers == rdataset->type)
646254897Serwin			break;
647254897Serwin		dns_rdataset_disassociate(&sigrdataset);
648254897Serwin	}
649254897Serwin	if (result == ISC_R_SUCCESS) {
650254897Serwin		dns_name_format(name, namebuf, sizeof(namebuf));
651254897Serwin		type_format(rdataset->type, typebuf, sizeof(typebuf));
652254897Serwin		fprintf(stderr, "Warning: Found unexpected signatures for "
653254897Serwin			"%s/%s\n", namebuf, typebuf);
654254897Serwin	}
655254897Serwin	if (dns_rdataset_isassociated(&sigrdataset))
656254897Serwin		dns_rdataset_disassociate(&sigrdataset);
657254897Serwin	dns_rdatasetiter_destroy(&rdsiter);
658254897Serwin}
659254897Serwin
660254897Serwinstatic isc_boolean_t
661254897Serwinchain_compare(void *arg1, void *arg2) {
662254897Serwin	struct nsec3_chain_fixed *e1 = arg1, *e2 = arg2;
663254897Serwin	size_t len;
664254897Serwin
665254897Serwin	/*
666254897Serwin	 * Do each element in turn to get a stable sort.
667254897Serwin	 */
668254897Serwin	if (e1->hash < e2->hash)
669254897Serwin		return (ISC_TRUE);
670254897Serwin	if (e1->hash > e2->hash)
671254897Serwin		return (ISC_FALSE);
672254897Serwin	if (e1->iterations < e2->iterations)
673254897Serwin		return (ISC_TRUE);
674254897Serwin	if (e1->iterations > e2->iterations)
675254897Serwin		return (ISC_FALSE);
676254897Serwin	if (e1->salt_length < e2->salt_length)
677254897Serwin		return (ISC_TRUE);
678254897Serwin	if (e1->salt_length > e2->salt_length)
679254897Serwin		return (ISC_FALSE);
680254897Serwin	if (e1->next_length < e2->next_length)
681254897Serwin		return (ISC_TRUE);
682254897Serwin	if (e1->next_length > e2->next_length)
683254897Serwin		return (ISC_FALSE);
684254897Serwin	len = e1->salt_length + 2 * e1->next_length;
685254897Serwin	if (memcmp(e1 + 1, e2 + 1, len) < 0)
686254897Serwin		return (ISC_TRUE);
687254897Serwin	return (ISC_FALSE);
688254897Serwin}
689254897Serwin
690254897Serwinstatic isc_boolean_t
691254897Serwinchain_equal(struct nsec3_chain_fixed *e1, struct nsec3_chain_fixed *e2) {
692254897Serwin	size_t len;
693254897Serwin
694254897Serwin	if (e1->hash != e2->hash)
695254897Serwin		return (ISC_FALSE);
696254897Serwin	if (e1->iterations != e2->iterations)
697254897Serwin		return (ISC_FALSE);
698254897Serwin	if (e1->salt_length != e2->salt_length)
699254897Serwin		return (ISC_FALSE);
700254897Serwin	if (e1->next_length != e2->next_length)
701254897Serwin		return (ISC_FALSE);
702254897Serwin	len = e1->salt_length + 2 * e1->next_length;
703254897Serwin	if (memcmp(e1 + 1, e2 + 1, len) != 0)
704254897Serwin		return (ISC_FALSE);
705254897Serwin	return (ISC_TRUE);
706254897Serwin}
707254897Serwin
708254897Serwinstatic isc_result_t
709254897Serwinrecord_nsec3(const unsigned char *rawhash, const dns_rdata_nsec3_t *nsec3,
710254897Serwin	     isc_mem_t *mctx, isc_heap_t *chains)
711254897Serwin{
712254897Serwin	struct nsec3_chain_fixed *element;
713254897Serwin	size_t len;
714254897Serwin	unsigned char *cp;
715254897Serwin	isc_result_t result;
716254897Serwin
717254897Serwin	len = sizeof(*element) + nsec3->next_length * 2 + nsec3->salt_length;
718254897Serwin
719254897Serwin	element = isc_mem_get(mctx, len);
720254897Serwin	if (element == NULL)
721254897Serwin		return (ISC_R_NOMEMORY);
722254897Serwin	memset(element, 0, len);
723254897Serwin	element->hash = nsec3->hash;
724254897Serwin	element->salt_length = nsec3->salt_length;
725254897Serwin	element->next_length = nsec3->next_length;
726254897Serwin	element->iterations = nsec3->iterations;
727254897Serwin	cp = (unsigned char *)(element + 1);
728262706Serwin	memmove(cp, nsec3->salt, nsec3->salt_length);
729254897Serwin	cp += nsec3->salt_length;
730262706Serwin	memmove(cp, rawhash, nsec3->next_length);
731254897Serwin	cp += nsec3->next_length;
732262706Serwin	memmove(cp, nsec3->next, nsec3->next_length);
733254897Serwin	result = isc_heap_insert(chains, element);
734254897Serwin	if (result != ISC_R_SUCCESS) {
735254897Serwin		fprintf(stderr, "isc_heap_insert failed: %s\n",
736254897Serwin			isc_result_totext(result));
737254897Serwin		isc_mem_put(mctx, element, len);
738254897Serwin	}
739254897Serwin	return (result);
740254897Serwin}
741254897Serwin
742254897Serwinstatic isc_result_t
743254897Serwinmatch_nsec3(dns_name_t *name, isc_mem_t *mctx,
744254897Serwin	    dns_rdata_nsec3param_t *nsec3param, dns_rdataset_t *rdataset,
745254897Serwin	    unsigned char types[8192], unsigned int maxtype,
746254897Serwin	    unsigned char *rawhash, size_t rhsize)
747254897Serwin{
748254897Serwin	unsigned char cbm[8244];
749254897Serwin	char namebuf[DNS_NAME_FORMATSIZE];
750254897Serwin	dns_rdata_nsec3_t nsec3;
751254897Serwin	isc_result_t result;
752254897Serwin	unsigned int len;
753254897Serwin
754254897Serwin	/*
755254897Serwin	 * Find matching NSEC3 record.
756254897Serwin	 */
757254897Serwin	for (result = dns_rdataset_first(rdataset);
758254897Serwin	     result == ISC_R_SUCCESS;
759254897Serwin	     result = dns_rdataset_next(rdataset)) {
760254897Serwin		dns_rdata_t rdata = DNS_RDATA_INIT;
761254897Serwin		dns_rdataset_current(rdataset, &rdata);
762254897Serwin		result = dns_rdata_tostruct(&rdata, &nsec3, NULL);
763254897Serwin		check_result(result, "dns_rdata_tostruct()");
764254897Serwin		if (nsec3.hash == nsec3param->hash &&
765254897Serwin		    nsec3.next_length == rhsize &&
766254897Serwin		    nsec3.iterations == nsec3param->iterations &&
767254897Serwin		    nsec3.salt_length == nsec3param->salt_length &&
768254897Serwin		    memcmp(nsec3.salt, nsec3param->salt,
769254897Serwin			   nsec3param->salt_length) == 0)
770254897Serwin			break;
771254897Serwin	}
772254897Serwin	if (result != ISC_R_SUCCESS) {
773254897Serwin		dns_name_format(name, namebuf, sizeof(namebuf));
774254897Serwin		fprintf(stderr, "Missing NSEC3 record for %s\n", namebuf);
775254897Serwin		return (result);
776254897Serwin	}
777254897Serwin
778254897Serwin	/*
779254897Serwin	 * Check the type list.
780254897Serwin	 */
781254897Serwin	len = dns_nsec_compressbitmap(cbm, types, maxtype);
782254897Serwin	if (nsec3.len != len || memcmp(cbm, nsec3.typebits, len) != 0) {
783254897Serwin		dns_name_format(name, namebuf, sizeof(namebuf));
784254897Serwin		fprintf(stderr, "Bad NSEC3 record for %s, bit map "
785254897Serwin				"mismatch\n", namebuf);
786254897Serwin		return (ISC_R_FAILURE);
787254897Serwin	}
788254897Serwin
789254897Serwin	/*
790254897Serwin	 * Record chain.
791254897Serwin	 */
792254897Serwin	result = record_nsec3(rawhash, &nsec3, mctx, expected_chains);
793254897Serwin	check_result(result, "record_nsec3()");
794254897Serwin
795254897Serwin	/*
796254897Serwin	 * Make sure there is only one NSEC3 record with this set of
797254897Serwin	 * parameters.
798254897Serwin	 */
799254897Serwin	for (result = dns_rdataset_next(rdataset);
800254897Serwin	     result == ISC_R_SUCCESS;
801254897Serwin	     result = dns_rdataset_next(rdataset)) {
802254897Serwin		dns_rdata_t rdata = DNS_RDATA_INIT;
803254897Serwin		dns_rdataset_current(rdataset, &rdata);
804254897Serwin		result = dns_rdata_tostruct(&rdata, &nsec3, NULL);
805254897Serwin		check_result(result, "dns_rdata_tostruct()");
806254897Serwin		if (nsec3.hash == nsec3param->hash &&
807254897Serwin		    nsec3.iterations == nsec3param->iterations &&
808254897Serwin		    nsec3.salt_length == nsec3param->salt_length &&
809254897Serwin		    memcmp(nsec3.salt, nsec3param->salt,
810254897Serwin			   nsec3.salt_length) == 0) {
811254897Serwin			dns_name_format(name, namebuf, sizeof(namebuf));
812254897Serwin			fprintf(stderr, "Multiple NSEC3 records with the "
813254897Serwin				"same parameter set for %s", namebuf);
814254897Serwin			result = DNS_R_DUPLICATE;
815254897Serwin			break;
816254897Serwin		}
817254897Serwin	}
818254897Serwin	if (result != ISC_R_NOMORE)
819254897Serwin		return (result);
820254897Serwin
821254897Serwin	result = ISC_R_SUCCESS;
822254897Serwin	return (result);
823254897Serwin}
824254897Serwin
825254897Serwinstatic isc_boolean_t
826254897Serwininnsec3params(dns_rdata_nsec3_t *nsec3, dns_rdataset_t *nsec3paramset) {
827254897Serwin	dns_rdata_nsec3param_t nsec3param;
828254897Serwin	isc_result_t result;
829254897Serwin
830254897Serwin	for (result = dns_rdataset_first(nsec3paramset);
831254897Serwin	     result == ISC_R_SUCCESS;
832254897Serwin	     result = dns_rdataset_next(nsec3paramset)) {
833254897Serwin		dns_rdata_t rdata = DNS_RDATA_INIT;
834254897Serwin
835254897Serwin		dns_rdataset_current(nsec3paramset, &rdata);
836254897Serwin		result = dns_rdata_tostruct(&rdata, &nsec3param, NULL);
837254897Serwin		check_result(result, "dns_rdata_tostruct()");
838254897Serwin		if (nsec3param.flags == 0 &&
839254897Serwin		    nsec3param.hash == nsec3->hash &&
840254897Serwin		    nsec3param.iterations == nsec3->iterations &&
841254897Serwin		    nsec3param.salt_length == nsec3->salt_length &&
842254897Serwin		    memcmp(nsec3param.salt, nsec3->salt,
843254897Serwin			   nsec3->salt_length) == 0)
844254897Serwin			return (ISC_TRUE);
845254897Serwin	}
846254897Serwin	return (ISC_FALSE);
847254897Serwin}
848254897Serwin
849254897Serwinstatic isc_result_t
850254897Serwinrecord_found(dns_db_t *db, dns_dbversion_t *ver, isc_mem_t *mctx,
851254897Serwin	     dns_name_t *name, dns_dbnode_t *node,
852254897Serwin	     dns_rdataset_t *nsec3paramset)
853254897Serwin{
854254897Serwin	unsigned char owner[NSEC3_MAX_HASH_LENGTH];
855254897Serwin	dns_rdata_nsec3_t nsec3;
856254897Serwin	dns_rdataset_t rdataset;
857254897Serwin	dns_label_t hashlabel;
858254897Serwin	isc_buffer_t b;
859254897Serwin	isc_result_t result;
860254897Serwin
861254897Serwin	if (nsec3paramset == NULL || !dns_rdataset_isassociated(nsec3paramset))
862254897Serwin		return (ISC_R_SUCCESS);
863254897Serwin
864254897Serwin	dns_rdataset_init(&rdataset);
865254897Serwin	result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3,
866254897Serwin				     0, 0, &rdataset, NULL);
867254897Serwin	if (result != ISC_R_SUCCESS)
868254897Serwin		return (ISC_R_SUCCESS);
869254897Serwin
870254897Serwin	dns_name_getlabel(name, 0, &hashlabel);
871254897Serwin	isc_region_consume(&hashlabel, 1);
872254897Serwin	isc_buffer_init(&b, owner, sizeof(owner));
873254897Serwin	result = isc_base32hex_decoderegion(&hashlabel, &b);
874254897Serwin	if (result != ISC_R_SUCCESS)
875254897Serwin		goto cleanup;
876254897Serwin
877254897Serwin	for (result = dns_rdataset_first(&rdataset);
878254897Serwin	     result == ISC_R_SUCCESS;
879254897Serwin	     result = dns_rdataset_next(&rdataset)) {
880254897Serwin		dns_rdata_t rdata = DNS_RDATA_INIT;
881254897Serwin		dns_rdataset_current(&rdataset, &rdata);
882254897Serwin		result = dns_rdata_tostruct(&rdata, &nsec3, NULL);
883254897Serwin		check_result(result, "dns_rdata_tostruct()");
884254897Serwin		if (nsec3.next_length != isc_buffer_usedlength(&b))
885254897Serwin			continue;
886254897Serwin		/*
887254897Serwin		 * We only care about NSEC3 records that match a NSEC3PARAM
888254897Serwin		 * record.
889254897Serwin		 */
890254897Serwin		if (!innsec3params(&nsec3, nsec3paramset))
891254897Serwin			continue;
892254897Serwin
893254897Serwin		/*
894254897Serwin		 * Record chain.
895254897Serwin		 */
896254897Serwin		result = record_nsec3(owner, &nsec3, mctx, found_chains);
897254897Serwin		check_result(result, "record_nsec3()");
898254897Serwin	}
899254897Serwin
900254897Serwin cleanup:
901254897Serwin	dns_rdataset_disassociate(&rdataset);
902254897Serwin	return (ISC_R_SUCCESS);
903254897Serwin}
904254897Serwin
905254897Serwinstatic isc_boolean_t
906254897Serwinisoptout(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin,
907254897Serwin	 dns_rdata_t *nsec3rdata)
908254897Serwin{
909254897Serwin	dns_rdataset_t rdataset;
910254897Serwin	dns_rdata_t rdata = DNS_RDATA_INIT;
911254897Serwin	dns_rdata_nsec3_t nsec3;
912254897Serwin	dns_rdata_nsec3param_t nsec3param;
913254897Serwin	dns_fixedname_t fixed;
914254897Serwin	dns_name_t *hashname;
915254897Serwin	isc_result_t result;
916254897Serwin	dns_dbnode_t *node = NULL;
917254897Serwin	unsigned char rawhash[NSEC3_MAX_HASH_LENGTH];
918254897Serwin	size_t rhsize = sizeof(rawhash);
919254897Serwin	isc_boolean_t ret;
920254897Serwin
921254897Serwin	result = dns_rdata_tostruct(nsec3rdata, &nsec3param, NULL);
922254897Serwin	check_result(result, "dns_rdata_tostruct()");
923254897Serwin
924254897Serwin	dns_fixedname_init(&fixed);
925254897Serwin	result = dns_nsec3_hashname(&fixed, rawhash, &rhsize, origin, origin,
926254897Serwin				    nsec3param.hash, nsec3param.iterations,
927254897Serwin				    nsec3param.salt, nsec3param.salt_length);
928254897Serwin	check_result(result, "dns_nsec3_hashname()");
929254897Serwin
930254897Serwin	dns_rdataset_init(&rdataset);
931254897Serwin	hashname = dns_fixedname_name(&fixed);
932254897Serwin	result = dns_db_findnsec3node(db, hashname, ISC_FALSE, &node);
933254897Serwin	if (result == ISC_R_SUCCESS)
934254897Serwin		result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3,
935254897Serwin					     0, 0, &rdataset, NULL);
936254897Serwin	if (result != ISC_R_SUCCESS)
937254897Serwin		return (ISC_FALSE);
938254897Serwin
939254897Serwin	result = dns_rdataset_first(&rdataset);
940254897Serwin	check_result(result, "dns_rdataset_first()");
941254897Serwin
942254897Serwin	dns_rdataset_current(&rdataset, &rdata);
943254897Serwin
944254897Serwin	result = dns_rdata_tostruct(&rdata, &nsec3, NULL);
945254897Serwin	if (result != ISC_R_SUCCESS)
946254897Serwin		ret = ISC_FALSE;
947254897Serwin	else
948254897Serwin		ret = ISC_TF((nsec3.flags & DNS_NSEC3FLAG_OPTOUT) != 0);
949254897Serwin
950254897Serwin	if (dns_rdataset_isassociated(&rdataset))
951254897Serwin		dns_rdataset_disassociate(&rdataset);
952254897Serwin	if (node != NULL)
953254897Serwin		dns_db_detachnode(db, &node);
954254897Serwin
955254897Serwin	return (ret);
956254897Serwin}
957254897Serwin
958254897Serwinstatic isc_result_t
959254897Serwinverifynsec3(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin,
960254897Serwin	    isc_mem_t *mctx, dns_name_t *name, dns_rdata_t *rdata,
961254897Serwin	    isc_boolean_t delegation, isc_boolean_t empty,
962254897Serwin	    unsigned char types[8192], unsigned int maxtype)
963254897Serwin{
964254897Serwin	char namebuf[DNS_NAME_FORMATSIZE];
965254897Serwin	char hashbuf[DNS_NAME_FORMATSIZE];
966254897Serwin	dns_rdataset_t rdataset;
967254897Serwin	dns_rdata_nsec3param_t nsec3param;
968254897Serwin	dns_fixedname_t fixed;
969254897Serwin	dns_name_t *hashname;
970254897Serwin	isc_result_t result;
971254897Serwin	dns_dbnode_t *node = NULL;
972254897Serwin	unsigned char rawhash[NSEC3_MAX_HASH_LENGTH];
973254897Serwin	size_t rhsize = sizeof(rawhash);
974254897Serwin	isc_boolean_t optout;
975254897Serwin
976254897Serwin	result = dns_rdata_tostruct(rdata, &nsec3param, NULL);
977254897Serwin	check_result(result, "dns_rdata_tostruct()");
978254897Serwin
979254897Serwin	if (nsec3param.flags != 0)
980254897Serwin		return (ISC_R_SUCCESS);
981254897Serwin
982254897Serwin	if (!dns_nsec3_supportedhash(nsec3param.hash))
983254897Serwin		return (ISC_R_SUCCESS);
984254897Serwin
985254897Serwin	optout = isoptout(db, ver, origin, rdata);
986254897Serwin
987254897Serwin	dns_fixedname_init(&fixed);
988254897Serwin	result = dns_nsec3_hashname(&fixed, rawhash, &rhsize, name, origin,
989254897Serwin				    nsec3param.hash, nsec3param.iterations,
990254897Serwin				    nsec3param.salt, nsec3param.salt_length);
991254897Serwin	check_result(result, "dns_nsec3_hashname()");
992254897Serwin
993254897Serwin	/*
994254897Serwin	 * We don't use dns_db_find() here as it works with the choosen
995254897Serwin	 * nsec3 chain and we may also be called with uncommitted data
996254897Serwin	 * from dnssec-signzone so the secure status of the zone may not
997254897Serwin	 * be up to date.
998254897Serwin	 */
999254897Serwin	dns_rdataset_init(&rdataset);
1000254897Serwin	hashname = dns_fixedname_name(&fixed);
1001254897Serwin	result = dns_db_findnsec3node(db, hashname, ISC_FALSE, &node);
1002254897Serwin	if (result == ISC_R_SUCCESS)
1003254897Serwin		result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3,
1004254897Serwin					     0, 0, &rdataset, NULL);
1005254897Serwin	if (result != ISC_R_SUCCESS &&
1006254897Serwin	    (!delegation || (empty && !optout) ||
1007254897Serwin	     (!empty && dns_nsec_isset(types, dns_rdatatype_ds))))
1008254897Serwin	{
1009254897Serwin		dns_name_format(name, namebuf, sizeof(namebuf));
1010254897Serwin		dns_name_format(hashname, hashbuf, sizeof(hashbuf));
1011254897Serwin		fprintf(stderr, "Missing NSEC3 record for %s (%s)\n",
1012254897Serwin			namebuf, hashbuf);
1013254897Serwin	} else if (result == ISC_R_NOTFOUND &&
1014254897Serwin		   delegation && (!empty || optout))
1015254897Serwin	{
1016254897Serwin		result = ISC_R_SUCCESS;
1017254897Serwin	} else if (result == ISC_R_SUCCESS) {
1018254897Serwin		result = match_nsec3(name, mctx, &nsec3param, &rdataset,
1019254897Serwin				     types, maxtype, rawhash, rhsize);
1020254897Serwin	}
1021254897Serwin
1022254897Serwin	if (dns_rdataset_isassociated(&rdataset))
1023254897Serwin		dns_rdataset_disassociate(&rdataset);
1024254897Serwin	if (node != NULL)
1025254897Serwin		dns_db_detachnode(db, &node);
1026254897Serwin
1027254897Serwin	return (result);
1028254897Serwin}
1029254897Serwin
1030254897Serwinstatic isc_result_t
1031254897Serwinverifynsec3s(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin,
1032254897Serwin	     isc_mem_t *mctx, dns_name_t *name, dns_rdataset_t *nsec3paramset,
1033254897Serwin	     isc_boolean_t delegation, isc_boolean_t empty,
1034254897Serwin	     unsigned char types[8192], unsigned int maxtype)
1035254897Serwin{
1036254897Serwin	isc_result_t result;
1037254897Serwin
1038254897Serwin	for (result = dns_rdataset_first(nsec3paramset);
1039254897Serwin	     result == ISC_R_SUCCESS;
1040254897Serwin	     result = dns_rdataset_next(nsec3paramset)) {
1041254897Serwin		dns_rdata_t rdata = DNS_RDATA_INIT;
1042254897Serwin
1043254897Serwin		dns_rdataset_current(nsec3paramset, &rdata);
1044254897Serwin		result = verifynsec3(db, ver, origin, mctx, name, &rdata,
1045254897Serwin				     delegation, empty, types, maxtype);
1046254897Serwin		if (result != ISC_R_SUCCESS)
1047254897Serwin			break;
1048254897Serwin	}
1049254897Serwin	if (result == ISC_R_NOMORE)
1050254897Serwin		result = ISC_R_SUCCESS;
1051254897Serwin	return (result);
1052254897Serwin}
1053254897Serwin
1054254897Serwinstatic void
1055254897Serwinverifyset(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin,
1056254897Serwin	  isc_mem_t *mctx, dns_rdataset_t *rdataset, dns_name_t *name,
1057254897Serwin	  dns_dbnode_t *node, dns_rdataset_t *keyrdataset,
1058254897Serwin	  unsigned char *act_algorithms, unsigned char *bad_algorithms)
1059254897Serwin{
1060254897Serwin	unsigned char set_algorithms[256];
1061254897Serwin	char namebuf[DNS_NAME_FORMATSIZE];
1062254897Serwin	char algbuf[80];
1063254897Serwin	char typebuf[80];
1064254897Serwin	dns_rdataset_t sigrdataset;
1065254897Serwin	dns_rdatasetiter_t *rdsiter = NULL;
1066254897Serwin	isc_result_t result;
1067254897Serwin	int i;
1068254897Serwin
1069254897Serwin	dns_rdataset_init(&sigrdataset);
1070254897Serwin	result = dns_db_allrdatasets(db, node, ver, 0, &rdsiter);
1071254897Serwin	check_result(result, "dns_db_allrdatasets()");
1072254897Serwin	for (result = dns_rdatasetiter_first(rdsiter);
1073254897Serwin	     result == ISC_R_SUCCESS;
1074254897Serwin	     result = dns_rdatasetiter_next(rdsiter)) {
1075254897Serwin		dns_rdatasetiter_current(rdsiter, &sigrdataset);
1076254897Serwin		if (sigrdataset.type == dns_rdatatype_rrsig &&
1077254897Serwin		    sigrdataset.covers == rdataset->type)
1078254897Serwin			break;
1079254897Serwin		dns_rdataset_disassociate(&sigrdataset);
1080254897Serwin	}
1081254897Serwin	if (result != ISC_R_SUCCESS) {
1082254897Serwin		dns_name_format(name, namebuf, sizeof(namebuf));
1083254897Serwin		type_format(rdataset->type, typebuf, sizeof(typebuf));
1084254897Serwin		fprintf(stderr, "No signatures for %s/%s\n", namebuf, typebuf);
1085254897Serwin		for (i = 0; i < 256; i++)
1086254897Serwin			if (act_algorithms[i] != 0)
1087254897Serwin				bad_algorithms[i] = 1;
1088254897Serwin		dns_rdatasetiter_destroy(&rdsiter);
1089254897Serwin		return;
1090254897Serwin	}
1091254897Serwin
1092254897Serwin	memset(set_algorithms, 0, sizeof(set_algorithms));
1093254897Serwin	for (result = dns_rdataset_first(&sigrdataset);
1094254897Serwin	     result == ISC_R_SUCCESS;
1095254897Serwin	     result = dns_rdataset_next(&sigrdataset)) {
1096254897Serwin		dns_rdata_t rdata = DNS_RDATA_INIT;
1097254897Serwin		dns_rdata_rrsig_t sig;
1098254897Serwin
1099254897Serwin		dns_rdataset_current(&sigrdataset, &rdata);
1100254897Serwin		result = dns_rdata_tostruct(&rdata, &sig, NULL);
1101254897Serwin		check_result(result, "dns_rdata_tostruct()");
1102254897Serwin		if (rdataset->ttl != sig.originalttl) {
1103254897Serwin			dns_name_format(name, namebuf, sizeof(namebuf));
1104254897Serwin			type_format(rdataset->type, typebuf, sizeof(typebuf));
1105254897Serwin			fprintf(stderr, "TTL mismatch for %s %s keytag %u\n",
1106254897Serwin				namebuf, typebuf, sig.keyid);
1107254897Serwin			continue;
1108254897Serwin		}
1109254897Serwin		if ((set_algorithms[sig.algorithm] != 0) ||
1110254897Serwin		    (act_algorithms[sig.algorithm] == 0))
1111254897Serwin			continue;
1112254897Serwin		if (goodsig(origin, &rdata, name, keyrdataset, rdataset, mctx))
1113254897Serwin			set_algorithms[sig.algorithm] = 1;
1114254897Serwin	}
1115254897Serwin	dns_rdatasetiter_destroy(&rdsiter);
1116254897Serwin	if (memcmp(set_algorithms, act_algorithms, sizeof(set_algorithms))) {
1117254897Serwin		dns_name_format(name, namebuf, sizeof(namebuf));
1118254897Serwin		type_format(rdataset->type, typebuf, sizeof(typebuf));
1119254897Serwin		for (i = 0; i < 256; i++)
1120254897Serwin			if ((act_algorithms[i] != 0) &&
1121254897Serwin			    (set_algorithms[i] == 0)) {
1122254897Serwin				dns_secalg_format(i, algbuf, sizeof(algbuf));
1123254897Serwin				fprintf(stderr, "No correct %s signature for "
1124254897Serwin					"%s %s\n", algbuf, namebuf, typebuf);
1125254897Serwin				bad_algorithms[i] = 1;
1126254897Serwin			}
1127254897Serwin	}
1128254897Serwin	dns_rdataset_disassociate(&sigrdataset);
1129254897Serwin}
1130254897Serwin
1131254897Serwinstatic isc_result_t
1132254897Serwinverifynode(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin,
1133254897Serwin	   isc_mem_t *mctx, dns_name_t *name, dns_dbnode_t *node,
1134254897Serwin	   isc_boolean_t delegation, dns_rdataset_t *keyrdataset,
1135254897Serwin	   unsigned char *act_algorithms, unsigned char *bad_algorithms,
1136254897Serwin	   dns_rdataset_t *nsecset, dns_rdataset_t *nsec3paramset,
1137254897Serwin	   dns_name_t *nextname)
1138254897Serwin{
1139254897Serwin	unsigned char types[8192];
1140254897Serwin	unsigned int maxtype = 0;
1141254897Serwin	dns_rdataset_t rdataset; dns_rdatasetiter_t *rdsiter = NULL;
1142254897Serwin	isc_result_t result, tresult;
1143254897Serwin
1144254897Serwin	memset(types, 0, sizeof(types));
1145254897Serwin	result = dns_db_allrdatasets(db, node, ver, 0, &rdsiter);
1146254897Serwin	check_result(result, "dns_db_allrdatasets()");
1147254897Serwin	result = dns_rdatasetiter_first(rdsiter);
1148254897Serwin	dns_rdataset_init(&rdataset);
1149254897Serwin	while (result == ISC_R_SUCCESS) {
1150254897Serwin		dns_rdatasetiter_current(rdsiter, &rdataset);
1151254897Serwin		/*
1152254897Serwin		 * If we are not at a delegation then everything should be
1153254897Serwin		 * signed.  If we are at a delegation then only the DS set
1154254897Serwin		 * is signed.  The NS set is not signed at a delegation but
1155254897Serwin		 * its existance is recorded in the bit map.  Anything else
1156254897Serwin		 * other than NSEC and DS is not signed at a delegation.
1157254897Serwin		 */
1158254897Serwin		if (rdataset.type != dns_rdatatype_rrsig &&
1159254897Serwin		    rdataset.type != dns_rdatatype_dnskey &&
1160254897Serwin		    (!delegation || rdataset.type == dns_rdatatype_ds ||
1161254897Serwin		     rdataset.type == dns_rdatatype_nsec)) {
1162254897Serwin			verifyset(db, ver, origin, mctx, &rdataset,
1163254897Serwin				  name, node, keyrdataset,
1164254897Serwin				  act_algorithms, bad_algorithms);
1165254897Serwin			dns_nsec_setbit(types, rdataset.type, 1);
1166254897Serwin			if (rdataset.type > maxtype)
1167254897Serwin				maxtype = rdataset.type;
1168254897Serwin		} else if (rdataset.type != dns_rdatatype_rrsig &&
1169254897Serwin			   rdataset.type != dns_rdatatype_dnskey) {
1170254897Serwin			if (rdataset.type == dns_rdatatype_ns)
1171254897Serwin				dns_nsec_setbit(types, rdataset.type, 1);
1172254897Serwin			check_no_rrsig(db, ver, &rdataset, name, node);
1173254897Serwin		} else
1174254897Serwin			dns_nsec_setbit(types, rdataset.type, 1);
1175254897Serwin		dns_rdataset_disassociate(&rdataset);
1176254897Serwin		result = dns_rdatasetiter_next(rdsiter);
1177254897Serwin	}
1178254897Serwin	if (result != ISC_R_NOMORE)
1179254897Serwin		fatal("rdataset iteration failed: %s",
1180254897Serwin		      isc_result_totext(result));
1181254897Serwin	dns_rdatasetiter_destroy(&rdsiter);
1182254897Serwin
1183254897Serwin	result = ISC_R_SUCCESS;
1184254897Serwin
1185254897Serwin	if (nsecset != NULL && dns_rdataset_isassociated(nsecset))
1186254897Serwin		result = verifynsec(db, ver, name, node, nextname);
1187254897Serwin
1188254897Serwin	if (nsec3paramset != NULL && dns_rdataset_isassociated(nsec3paramset)) {
1189254897Serwin		tresult = verifynsec3s(db, ver, origin, mctx, name,
1190254897Serwin				       nsec3paramset, delegation, ISC_FALSE,
1191254897Serwin				       types, maxtype);
1192254897Serwin		if (result == ISC_R_SUCCESS && tresult != ISC_R_SUCCESS)
1193254897Serwin			result = tresult;
1194254897Serwin	}
1195254897Serwin	return (result);
1196254897Serwin}
1197254897Serwin
1198254897Serwinstatic isc_boolean_t
1199254897Serwinis_empty(dns_db_t *db, dns_dbversion_t *ver, dns_dbnode_t *node) {
1200254897Serwin	dns_rdatasetiter_t *rdsiter = NULL;
1201254897Serwin	isc_result_t result;
1202254897Serwin
1203254897Serwin	result = dns_db_allrdatasets(db, node, ver, 0, &rdsiter);
1204254897Serwin	check_result(result, "dns_db_allrdatasets()");
1205254897Serwin	result = dns_rdatasetiter_first(rdsiter);
1206254897Serwin	dns_rdatasetiter_destroy(&rdsiter);
1207254897Serwin	if (result == ISC_R_NOMORE)
1208254897Serwin		return (ISC_TRUE);
1209254897Serwin	return (ISC_FALSE);
1210254897Serwin}
1211254897Serwin
1212254897Serwinstatic void
1213254897Serwincheck_no_nsec(dns_name_t *name, dns_dbnode_t *node, dns_db_t *db,
1214254897Serwin	      dns_dbversion_t *ver)
1215254897Serwin{
1216254897Serwin	dns_rdataset_t rdataset;
1217254897Serwin	isc_result_t result;
1218254897Serwin
1219254897Serwin	dns_rdataset_init(&rdataset);
1220254897Serwin	result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec,
1221254897Serwin				     0, 0, &rdataset, NULL);
1222254897Serwin	if (result != ISC_R_NOTFOUND) {
1223254897Serwin		char namebuf[DNS_NAME_FORMATSIZE];
1224254897Serwin		dns_name_format(name, namebuf, sizeof(namebuf));
1225254897Serwin		fatal("unexpected NSEC RRset at %s\n", namebuf);
1226254897Serwin	}
1227254897Serwin
1228254897Serwin	if (dns_rdataset_isassociated(&rdataset))
1229254897Serwin		dns_rdataset_disassociate(&rdataset);
1230254897Serwin}
1231254897Serwin
1232254897Serwinstatic isc_boolean_t
1233254897Serwinnewchain(const struct nsec3_chain_fixed *first,
1234254897Serwin	 const struct nsec3_chain_fixed *e)
1235254897Serwin{
1236254897Serwin	if (first->hash != e->hash ||
1237254897Serwin	    first->iterations != e->iterations ||
1238254897Serwin	    first->salt_length != e->salt_length ||
1239254897Serwin	    first->next_length != e->next_length ||
1240254897Serwin	    memcmp(first + 1, e + 1, first->salt_length) != 0)
1241254897Serwin		return (ISC_TRUE);
1242254897Serwin	return (ISC_FALSE);
1243254897Serwin}
1244254897Serwin
1245254897Serwinstatic void
1246254897Serwinfree_element(isc_mem_t *mctx, struct nsec3_chain_fixed *e) {
1247254897Serwin	size_t len;
1248254897Serwin
1249254897Serwin	len = sizeof(*e) + e->salt_length + 2 * e->next_length;
1250254897Serwin	isc_mem_put(mctx, e, len);
1251254897Serwin}
1252254897Serwin
1253254897Serwinstatic isc_boolean_t
1254254897Serwinchecknext(const struct nsec3_chain_fixed *first,
1255254897Serwin	  const struct nsec3_chain_fixed *e)
1256254897Serwin{
1257254897Serwin	char buf[512];
1258254897Serwin	const unsigned char *d1 = (const unsigned char *)(first + 1);
1259254897Serwin	const unsigned char *d2 = (const unsigned char *)(e + 1);
1260254897Serwin	isc_buffer_t b;
1261254897Serwin	isc_region_t sr;
1262254897Serwin
1263254897Serwin	d1 += first->salt_length + first->next_length;
1264254897Serwin	d2 += e->salt_length;
1265254897Serwin
1266254897Serwin	if (memcmp(d1, d2, first->next_length) == 0)
1267254897Serwin		return (ISC_TRUE);
1268254897Serwin
1269254897Serwin	DE_CONST(d1 - first->next_length, sr.base);
1270254897Serwin	sr.length = first->next_length;
1271254897Serwin	isc_buffer_init(&b, buf, sizeof(buf));
1272254897Serwin	isc_base32hex_totext(&sr, 1, "", &b);
1273254897Serwin	fprintf(stderr, "Break in NSEC3 chain at: %.*s\n",
1274254897Serwin		(int) isc_buffer_usedlength(&b), buf);
1275254897Serwin
1276254897Serwin	DE_CONST(d1, sr.base);
1277254897Serwin	sr.length = first->next_length;
1278254897Serwin	isc_buffer_init(&b, buf, sizeof(buf));
1279254897Serwin	isc_base32hex_totext(&sr, 1, "", &b);
1280254897Serwin	fprintf(stderr, "Expected: %.*s\n", (int) isc_buffer_usedlength(&b),
1281254897Serwin		buf);
1282254897Serwin
1283254897Serwin	DE_CONST(d2, sr.base);
1284254897Serwin	sr.length = first->next_length;
1285254897Serwin	isc_buffer_init(&b, buf, sizeof(buf));
1286254897Serwin	isc_base32hex_totext(&sr, 1, "", &b);
1287254897Serwin	fprintf(stderr, "Found: %.*s\n", (int) isc_buffer_usedlength(&b), buf);
1288254897Serwin
1289254897Serwin	return (ISC_FALSE);
1290254897Serwin}
1291254897Serwin
1292254897Serwin#define EXPECTEDANDFOUND "Expected and found NSEC3 chains not equal\n"
1293254897Serwin
1294254897Serwinstatic isc_result_t
1295254897Serwinverify_nsec3_chains(isc_mem_t *mctx) {
1296254897Serwin	isc_result_t result = ISC_R_SUCCESS;
1297254897Serwin	struct nsec3_chain_fixed *e, *f = NULL;
1298254897Serwin	struct nsec3_chain_fixed *first = NULL, *prev = NULL;
1299254897Serwin
1300254897Serwin	while ((e = isc_heap_element(expected_chains, 1)) != NULL) {
1301254897Serwin		isc_heap_delete(expected_chains, 1);
1302254897Serwin		if (f == NULL)
1303254897Serwin			f = isc_heap_element(found_chains, 1);
1304254897Serwin		if (f != NULL) {
1305254897Serwin			isc_heap_delete(found_chains, 1);
1306254897Serwin
1307254897Serwin			/*
1308254897Serwin			 * Check that they match.
1309254897Serwin			 */
1310254897Serwin			if (chain_equal(e, f)) {
1311254897Serwin				free_element(mctx, f);
1312254897Serwin				f = NULL;
1313254897Serwin			} else {
1314254897Serwin				if (result == ISC_R_SUCCESS)
1315254897Serwin					fprintf(stderr, EXPECTEDANDFOUND);
1316254897Serwin				result = ISC_R_FAILURE;
1317254897Serwin				/*
1318254897Serwin				 * Attempt to resync found_chain.
1319254897Serwin				 */
1320254897Serwin				while (f != NULL && !chain_compare(e, f)) {
1321254897Serwin					free_element(mctx, f);
1322254897Serwin					f = isc_heap_element(found_chains, 1);
1323254897Serwin					if (f != NULL)
1324254897Serwin						isc_heap_delete(found_chains, 1);
1325254897Serwin					if (f != NULL && chain_equal(e, f)) {
1326254897Serwin						free_element(mctx, f);
1327254897Serwin						f = NULL;
1328254897Serwin						break;
1329254897Serwin					}
1330254897Serwin				}
1331254897Serwin			}
1332254897Serwin		} else if (result == ISC_R_SUCCESS) {
1333254897Serwin			fprintf(stderr, EXPECTEDANDFOUND);
1334254897Serwin			result = ISC_R_FAILURE;
1335254897Serwin		}
1336254897Serwin		if (first == NULL || newchain(first, e)) {
1337254897Serwin			if (prev != NULL) {
1338254897Serwin				if (!checknext(prev, first))
1339254897Serwin					result = ISC_R_FAILURE;
1340254897Serwin				if (prev != first)
1341254897Serwin					free_element(mctx, prev);
1342254897Serwin			}
1343254897Serwin			if (first != NULL)
1344254897Serwin				free_element(mctx, first);
1345254897Serwin			prev = first = e;
1346254897Serwin			continue;
1347254897Serwin		}
1348254897Serwin		if (!checknext(prev, e))
1349254897Serwin			result = ISC_R_FAILURE;
1350254897Serwin		if (prev != first)
1351254897Serwin			free_element(mctx, prev);
1352254897Serwin		prev = e;
1353254897Serwin	}
1354254897Serwin	if (prev != NULL) {
1355254897Serwin		if (!checknext(prev, first))
1356254897Serwin			result = ISC_R_FAILURE;
1357254897Serwin		if (prev != first)
1358254897Serwin			free_element(mctx, prev);
1359254897Serwin	}
1360254897Serwin	if (first != NULL)
1361254897Serwin		free_element(mctx, first);
1362254897Serwin	do {
1363254897Serwin		if (f != NULL) {
1364254897Serwin			if (result == ISC_R_SUCCESS) {
1365254897Serwin				fprintf(stderr, EXPECTEDANDFOUND);
1366254897Serwin				result = ISC_R_FAILURE;
1367254897Serwin			}
1368254897Serwin			free_element(mctx, f);
1369254897Serwin		}
1370254897Serwin		f = isc_heap_element(found_chains, 1);
1371254897Serwin		if (f != NULL)
1372254897Serwin			isc_heap_delete(found_chains, 1);
1373254897Serwin	} while (f != NULL);
1374254897Serwin
1375254897Serwin	return (result);
1376254897Serwin}
1377254897Serwin
1378254897Serwinstatic isc_result_t
1379254897Serwinverifyemptynodes(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin,
1380254897Serwin		 isc_mem_t *mctx, dns_name_t *name, dns_name_t *prevname,
1381254897Serwin		 isc_boolean_t isdelegation, dns_rdataset_t *nsec3paramset)
1382254897Serwin{
1383254897Serwin	dns_namereln_t reln;
1384254897Serwin	int order;
1385254897Serwin	unsigned int labels, nlabels, i;
1386254897Serwin	dns_name_t suffix;
1387254897Serwin	isc_result_t result = ISC_R_SUCCESS, tresult;
1388254897Serwin
1389254897Serwin	reln = dns_name_fullcompare(prevname, name, &order, &labels);
1390254897Serwin	if (order >= 0)
1391254897Serwin		return (result);
1392254897Serwin
1393254897Serwin	nlabels = dns_name_countlabels(name);
1394254897Serwin
1395254897Serwin	if (reln == dns_namereln_commonancestor ||
1396254897Serwin	    reln == dns_namereln_contains) {
1397254897Serwin		dns_name_init(&suffix, NULL);
1398254897Serwin		for (i = labels + 1; i < nlabels; i++) {
1399254897Serwin			dns_name_getlabelsequence(name, nlabels - i, i,
1400254897Serwin						  &suffix);
1401254897Serwin			if (nsec3paramset != NULL &&
1402254897Serwin			     dns_rdataset_isassociated(nsec3paramset)) {
1403254897Serwin				tresult = verifynsec3s(db, ver, origin, mctx,
1404254897Serwin						       &suffix, nsec3paramset,
1405254897Serwin						       isdelegation, ISC_TRUE,
1406254897Serwin						       NULL, 0);
1407254897Serwin				if (result == ISC_R_SUCCESS &&
1408254897Serwin				    tresult != ISC_R_SUCCESS)
1409254897Serwin					result = tresult;
1410254897Serwin			}
1411254897Serwin		}
1412254897Serwin	}
1413254897Serwin	return (result);
1414254897Serwin}
1415254897Serwin
1416254897Serwin/*%
1417254897Serwin * Verify that certain things are sane:
1418254897Serwin *
1419254897Serwin *   The apex has a DNSKEY record with at least one KSK, and at least
1420254897Serwin *   one ZSK if the -x flag was not used.
1421254897Serwin *
1422254897Serwin *   The DNSKEY record was signed with at least one of the KSKs in this
1423254897Serwin *   set.
1424254897Serwin *
1425254897Serwin *   The rest of the zone was signed with at least one of the ZSKs
1426254897Serwin *   present in the DNSKEY RRSET.
1427254897Serwin */
1428254897Serwinvoid
1429254897Serwinverifyzone(dns_db_t *db, dns_dbversion_t *ver,
1430254897Serwin	   dns_name_t *origin, isc_mem_t *mctx,
1431254897Serwin	   isc_boolean_t ignore_kskflag, isc_boolean_t keyset_kskonly)
1432254897Serwin{
1433254897Serwin	char algbuf[80];
1434254897Serwin	dns_dbiterator_t *dbiter = NULL;
1435254897Serwin	dns_dbnode_t *node = NULL, *nextnode = NULL;
1436254897Serwin	dns_fixedname_t fname, fnextname, fprevname, fzonecut;
1437254897Serwin	dns_name_t *name, *nextname, *prevname, *zonecut;
1438254897Serwin	dns_rdata_dnskey_t dnskey;
1439254897Serwin	dns_rdata_t rdata = DNS_RDATA_INIT;
1440254897Serwin	dns_rdataset_t keyset, soaset;
1441254897Serwin	dns_rdataset_t keysigs, soasigs;
1442254897Serwin	dns_rdataset_t nsecset, nsecsigs;
1443254897Serwin	dns_rdataset_t nsec3paramset, nsec3paramsigs;
1444254897Serwin	int i;
1445254897Serwin	isc_boolean_t done = ISC_FALSE;
1446254897Serwin	isc_boolean_t first = ISC_TRUE;
1447254897Serwin	isc_boolean_t goodksk = ISC_FALSE;
1448254897Serwin	isc_boolean_t goodzsk = ISC_FALSE;
1449254897Serwin	isc_result_t result, vresult = ISC_R_UNSET;
1450254897Serwin	unsigned char revoked_ksk[256];
1451254897Serwin	unsigned char revoked_zsk[256];
1452254897Serwin	unsigned char standby_ksk[256];
1453254897Serwin	unsigned char standby_zsk[256];
1454254897Serwin	unsigned char ksk_algorithms[256];
1455254897Serwin	unsigned char zsk_algorithms[256];
1456254897Serwin	unsigned char bad_algorithms[256];
1457254897Serwin	unsigned char act_algorithms[256];
1458254897Serwin
1459254897Serwin	result = isc_heap_create(mctx, chain_compare, NULL, 1024,
1460254897Serwin				 &expected_chains);
1461254897Serwin	check_result(result, "isc_heap_create()");
1462254897Serwin	result = isc_heap_create(mctx, chain_compare, NULL, 1024,
1463254897Serwin				 &found_chains);
1464254897Serwin	check_result(result, "isc_heap_create()");
1465254897Serwin
1466254897Serwin	result = dns_db_findnode(db, origin, ISC_FALSE, &node);
1467254897Serwin	if (result != ISC_R_SUCCESS)
1468254897Serwin		fatal("failed to find the zone's origin: %s",
1469254897Serwin		      isc_result_totext(result));
1470254897Serwin
1471254897Serwin	dns_rdataset_init(&keyset);
1472254897Serwin	dns_rdataset_init(&keysigs);
1473254897Serwin	dns_rdataset_init(&soaset);
1474254897Serwin	dns_rdataset_init(&soasigs);
1475254897Serwin	dns_rdataset_init(&nsecset);
1476254897Serwin	dns_rdataset_init(&nsecsigs);
1477254897Serwin	dns_rdataset_init(&nsec3paramset);
1478254897Serwin	dns_rdataset_init(&nsec3paramsigs);
1479254897Serwin	result = dns_db_findrdataset(db, node, ver, dns_rdatatype_dnskey,
1480254897Serwin				     0, 0, &keyset, &keysigs);
1481254897Serwin	if (result != ISC_R_SUCCESS)
1482254897Serwin		fatal("Zone contains no DNSSEC keys\n");
1483254897Serwin
1484254897Serwin	result = dns_db_findrdataset(db, node, ver, dns_rdatatype_soa,
1485254897Serwin				     0, 0, &soaset, &soasigs);
1486254897Serwin	if (result != ISC_R_SUCCESS)
1487254897Serwin		fatal("Zone contains no SOA record\n");
1488254897Serwin
1489254897Serwin	result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec,
1490254897Serwin				     0, 0, &nsecset, &nsecsigs);
1491254897Serwin	if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND)
1492254897Serwin		fatal("NSEC lookup failed\n");
1493254897Serwin
1494254897Serwin	result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3param,
1495254897Serwin				     0, 0, &nsec3paramset, &nsec3paramsigs);
1496254897Serwin	if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND)
1497254897Serwin		fatal("NSEC3PARAM lookup failed\n");
1498254897Serwin
1499254897Serwin	if (!dns_rdataset_isassociated(&keysigs))
1500254897Serwin		fatal("DNSKEY is not signed (keys offline or inactive?)\n");
1501254897Serwin
1502254897Serwin	if (!dns_rdataset_isassociated(&soasigs))
1503254897Serwin		fatal("SOA is not signed (keys offline or inactive?)\n");
1504254897Serwin
1505254897Serwin	if (dns_rdataset_isassociated(&nsecset) &&
1506254897Serwin	    !dns_rdataset_isassociated(&nsecsigs))
1507254897Serwin		fatal("NSEC is not signed (keys offline or inactive?)\n");
1508254897Serwin
1509254897Serwin	if (dns_rdataset_isassociated(&nsec3paramset) &&
1510254897Serwin	    !dns_rdataset_isassociated(&nsec3paramsigs))
1511254897Serwin		fatal("NSEC3PARAM is not signed (keys offline or inactive?)\n");
1512254897Serwin
1513254897Serwin	if (!dns_rdataset_isassociated(&nsecset) &&
1514254897Serwin	    !dns_rdataset_isassociated(&nsec3paramset))
1515254897Serwin		fatal("No valid NSEC/NSEC3 chain for testing\n");
1516254897Serwin
1517254897Serwin	dns_db_detachnode(db, &node);
1518254897Serwin
1519254897Serwin	memset(revoked_ksk, 0, sizeof(revoked_ksk));
1520254897Serwin	memset(revoked_zsk, 0, sizeof(revoked_zsk));
1521254897Serwin	memset(standby_ksk, 0, sizeof(standby_ksk));
1522254897Serwin	memset(standby_zsk, 0, sizeof(standby_zsk));
1523254897Serwin	memset(ksk_algorithms, 0, sizeof(ksk_algorithms));
1524254897Serwin	memset(zsk_algorithms, 0, sizeof(zsk_algorithms));
1525254897Serwin	memset(bad_algorithms, 0, sizeof(bad_algorithms));
1526254897Serwin	memset(act_algorithms, 0, sizeof(act_algorithms));
1527254897Serwin
1528254897Serwin	/*
1529254897Serwin	 * Check that the DNSKEY RR has at least one self signing KSK
1530254897Serwin	 * and one ZSK per algorithm in it (or, if -x was used, one
1531254897Serwin	 * self-signing KSK).
1532254897Serwin	 */
1533254897Serwin	for (result = dns_rdataset_first(&keyset);
1534254897Serwin	     result == ISC_R_SUCCESS;
1535254897Serwin	     result = dns_rdataset_next(&keyset)) {
1536254897Serwin		dns_rdataset_current(&keyset, &rdata);
1537254897Serwin		result = dns_rdata_tostruct(&rdata, &dnskey, NULL);
1538254897Serwin		check_result(result, "dns_rdata_tostruct");
1539254897Serwin
1540254897Serwin		if ((dnskey.flags & DNS_KEYOWNER_ZONE) == 0)
1541254897Serwin			;
1542254897Serwin		else if ((dnskey.flags & DNS_KEYFLAG_REVOKE) != 0) {
1543254897Serwin			if ((dnskey.flags & DNS_KEYFLAG_KSK) != 0 &&
1544254897Serwin			    !dns_dnssec_selfsigns(&rdata, origin, &keyset,
1545254897Serwin						  &keysigs, ISC_FALSE,
1546254897Serwin						  mctx)) {
1547254897Serwin				char namebuf[DNS_NAME_FORMATSIZE];
1548254897Serwin				char buffer[1024];
1549254897Serwin				isc_buffer_t buf;
1550254897Serwin
1551254897Serwin				dns_name_format(origin, namebuf,
1552254897Serwin						sizeof(namebuf));
1553254897Serwin				isc_buffer_init(&buf, buffer, sizeof(buffer));
1554254897Serwin				result = dns_rdata_totext(&rdata, NULL, &buf);
1555254897Serwin				check_result(result, "dns_rdata_totext");
1556254897Serwin				fatal("revoked KSK is not self signed:\n"
1557254897Serwin				      "%s DNSKEY %.*s", namebuf,
1558254897Serwin				      (int)isc_buffer_usedlength(&buf), buffer);
1559254897Serwin			}
1560254897Serwin			if ((dnskey.flags & DNS_KEYFLAG_KSK) != 0 &&
1561254897Serwin			     revoked_ksk[dnskey.algorithm] != 255)
1562254897Serwin				revoked_ksk[dnskey.algorithm]++;
1563254897Serwin			else if ((dnskey.flags & DNS_KEYFLAG_KSK) == 0 &&
1564254897Serwin				 revoked_zsk[dnskey.algorithm] != 255)
1565254897Serwin				revoked_zsk[dnskey.algorithm]++;
1566254897Serwin		} else if ((dnskey.flags & DNS_KEYFLAG_KSK) != 0) {
1567254897Serwin			if (dns_dnssec_selfsigns(&rdata, origin, &keyset,
1568254897Serwin						 &keysigs, ISC_FALSE, mctx)) {
1569254897Serwin				if (ksk_algorithms[dnskey.algorithm] != 255)
1570254897Serwin					ksk_algorithms[dnskey.algorithm]++;
1571254897Serwin				goodksk = ISC_TRUE;
1572254897Serwin			} else {
1573254897Serwin				if (standby_ksk[dnskey.algorithm] != 255)
1574254897Serwin					standby_ksk[dnskey.algorithm]++;
1575254897Serwin			}
1576254897Serwin		} else if (dns_dnssec_selfsigns(&rdata, origin, &keyset,
1577254897Serwin						&keysigs, ISC_FALSE, mctx)) {
1578254897Serwin			if (zsk_algorithms[dnskey.algorithm] != 255)
1579254897Serwin				zsk_algorithms[dnskey.algorithm]++;
1580254897Serwin			goodzsk = ISC_TRUE;
1581254897Serwin		} else if (dns_dnssec_signs(&rdata, origin, &soaset,
1582254897Serwin					    &soasigs, ISC_FALSE, mctx)) {
1583254897Serwin			if (zsk_algorithms[dnskey.algorithm] != 255)
1584254897Serwin				zsk_algorithms[dnskey.algorithm]++;
1585254897Serwin		} else {
1586254897Serwin			if (standby_zsk[dnskey.algorithm] != 255)
1587254897Serwin				standby_zsk[dnskey.algorithm]++;
1588254897Serwin		}
1589254897Serwin		dns_rdata_freestruct(&dnskey);
1590254897Serwin		dns_rdata_reset(&rdata);
1591254897Serwin	}
1592254897Serwin	dns_rdataset_disassociate(&keysigs);
1593254897Serwin	dns_rdataset_disassociate(&soaset);
1594254897Serwin	dns_rdataset_disassociate(&soasigs);
1595254897Serwin	if (dns_rdataset_isassociated(&nsecsigs))
1596254897Serwin		dns_rdataset_disassociate(&nsecsigs);
1597254897Serwin	if (dns_rdataset_isassociated(&nsec3paramsigs))
1598254897Serwin		dns_rdataset_disassociate(&nsec3paramsigs);
1599254897Serwin
1600254897Serwin	if (ignore_kskflag ) {
1601254897Serwin		if (!goodksk && !goodzsk)
1602254897Serwin			fatal("No self-signed DNSKEY found.");
1603254897Serwin	} else if (!goodksk)
1604254897Serwin		fatal("No self-signed KSK DNSKEY found.  Supply an active\n"
1605254897Serwin		      "key with the KSK flag set, or use '-P'.");
1606254897Serwin
1607254897Serwin	fprintf(stderr, "Verifying the zone using the following algorithms:");
1608254897Serwin	for (i = 0; i < 256; i++) {
1609254897Serwin		if (ignore_kskflag)
1610254897Serwin			act_algorithms[i] = (ksk_algorithms[i] != 0 ||
1611254897Serwin					     zsk_algorithms[i] != 0) ? 1 : 0;
1612254897Serwin		else
1613254897Serwin			act_algorithms[i] = ksk_algorithms[i] != 0 ? 1 : 0;
1614254897Serwin		if (act_algorithms[i] != 0) {
1615254897Serwin			dns_secalg_format(i, algbuf, sizeof(algbuf));
1616254897Serwin			fprintf(stderr, " %s", algbuf);
1617254897Serwin		}
1618254897Serwin	}
1619254897Serwin	fprintf(stderr, ".\n");
1620254897Serwin
1621254897Serwin	if (!ignore_kskflag && !keyset_kskonly) {
1622254897Serwin		for (i = 0; i < 256; i++) {
1623254897Serwin			/*
1624254897Serwin			 * The counts should both be zero or both be non-zero.
1625254897Serwin			 * Mark the algorithm as bad if this is not met.
1626254897Serwin			 */
1627254897Serwin			if ((ksk_algorithms[i] != 0) ==
1628254897Serwin			    (zsk_algorithms[i] != 0))
1629254897Serwin				continue;
1630254897Serwin			dns_secalg_format(i, algbuf, sizeof(algbuf));
1631254897Serwin			fprintf(stderr, "Missing %s for algorithm %s\n",
1632254897Serwin				(ksk_algorithms[i] != 0)
1633254897Serwin				   ? "ZSK"
1634254897Serwin				   : "self-signed KSK",
1635254897Serwin				algbuf);
1636254897Serwin			bad_algorithms[i] = 1;
1637254897Serwin		}
1638254897Serwin	}
1639254897Serwin
1640254897Serwin	/*
1641254897Serwin	 * Check that all the other records were signed by keys that are
1642254897Serwin	 * present in the DNSKEY RRSET.
1643254897Serwin	 */
1644254897Serwin
1645254897Serwin	dns_fixedname_init(&fname);
1646254897Serwin	name = dns_fixedname_name(&fname);
1647254897Serwin	dns_fixedname_init(&fnextname);
1648254897Serwin	nextname = dns_fixedname_name(&fnextname);
1649254897Serwin	dns_fixedname_init(&fprevname);
1650254897Serwin	prevname = NULL;
1651254897Serwin	dns_fixedname_init(&fzonecut);
1652254897Serwin	zonecut = NULL;
1653254897Serwin
1654254897Serwin	result = dns_db_createiterator(db, DNS_DB_NONSEC3, &dbiter);
1655254897Serwin	check_result(result, "dns_db_createiterator()");
1656254897Serwin
1657254897Serwin	result = dns_dbiterator_first(dbiter);
1658254897Serwin	check_result(result, "dns_dbiterator_first()");
1659254897Serwin
1660254897Serwin	while (!done) {
1661254897Serwin		isc_boolean_t isdelegation = ISC_FALSE;
1662254897Serwin
1663254897Serwin		result = dns_dbiterator_current(dbiter, &node, name);
1664254897Serwin		check_dns_dbiterator_current(result);
1665254897Serwin		if (!dns_name_issubdomain(name, origin)) {
1666254897Serwin			check_no_nsec(name, node, db, ver);
1667254897Serwin			dns_db_detachnode(db, &node);
1668254897Serwin			result = dns_dbiterator_next(dbiter);
1669254897Serwin			if (result == ISC_R_NOMORE)
1670254897Serwin				done = ISC_TRUE;
1671254897Serwin			else
1672254897Serwin				check_result(result, "dns_dbiterator_next()");
1673254897Serwin			continue;
1674254897Serwin		}
1675254897Serwin		if (is_delegation(db, ver, origin, name, node, NULL)) {
1676254897Serwin			zonecut = dns_fixedname_name(&fzonecut);
1677254897Serwin			dns_name_copy(name, zonecut, NULL);
1678254897Serwin			isdelegation = ISC_TRUE;
1679254897Serwin		}
1680254897Serwin		nextnode = NULL;
1681254897Serwin		result = dns_dbiterator_next(dbiter);
1682254897Serwin		while (result == ISC_R_SUCCESS) {
1683254897Serwin			result = dns_dbiterator_current(dbiter, &nextnode,
1684254897Serwin							nextname);
1685254897Serwin			check_dns_dbiterator_current(result);
1686254897Serwin			if (!dns_name_issubdomain(nextname, origin) ||
1687254897Serwin			    (zonecut != NULL &&
1688254897Serwin			     dns_name_issubdomain(nextname, zonecut)))
1689254897Serwin			{
1690254897Serwin				check_no_nsec(nextname, nextnode, db, ver);
1691254897Serwin				dns_db_detachnode(db, &nextnode);
1692254897Serwin				result = dns_dbiterator_next(dbiter);
1693254897Serwin				continue;
1694254897Serwin			}
1695254897Serwin			if (is_empty(db, ver, nextnode)) {
1696254897Serwin				dns_db_detachnode(db, &nextnode);
1697254897Serwin				result = dns_dbiterator_next(dbiter);
1698254897Serwin				continue;
1699254897Serwin			}
1700254897Serwin			dns_db_detachnode(db, &nextnode);
1701254897Serwin			break;
1702254897Serwin		}
1703254897Serwin		if (result == ISC_R_NOMORE) {
1704254897Serwin			done = ISC_TRUE;
1705254897Serwin			nextname = origin;
1706254897Serwin		} else if (result != ISC_R_SUCCESS)
1707254897Serwin			fatal("iterating through the database failed: %s",
1708254897Serwin			      isc_result_totext(result));
1709254897Serwin		result = verifynode(db, ver, origin, mctx, name, node,
1710254897Serwin				    isdelegation, &keyset, act_algorithms,
1711254897Serwin				    bad_algorithms, &nsecset, &nsec3paramset,
1712254897Serwin				    nextname);
1713254897Serwin		if (vresult == ISC_R_UNSET)
1714254897Serwin			vresult = ISC_R_SUCCESS;
1715254897Serwin		if (vresult == ISC_R_SUCCESS && result != ISC_R_SUCCESS)
1716254897Serwin			vresult = result;
1717254897Serwin		if (prevname != NULL) {
1718254897Serwin			result = verifyemptynodes(db, ver, origin, mctx, name,
1719254897Serwin						  prevname, isdelegation,
1720254897Serwin						  &nsec3paramset);
1721254897Serwin		} else
1722254897Serwin			prevname = dns_fixedname_name(&fprevname);
1723254897Serwin		dns_name_copy(name, prevname, NULL);
1724254897Serwin		if (vresult == ISC_R_SUCCESS && result != ISC_R_SUCCESS)
1725254897Serwin			vresult = result;
1726254897Serwin		dns_db_detachnode(db, &node);
1727254897Serwin	}
1728254897Serwin
1729254897Serwin	dns_dbiterator_destroy(&dbiter);
1730254897Serwin
1731254897Serwin	result = dns_db_createiterator(db, DNS_DB_NSEC3ONLY, &dbiter);
1732254897Serwin	check_result(result, "dns_db_createiterator()");
1733254897Serwin
1734254897Serwin	for (result = dns_dbiterator_first(dbiter);
1735254897Serwin	     result == ISC_R_SUCCESS;
1736254897Serwin	     result = dns_dbiterator_next(dbiter) ) {
1737254897Serwin		result = dns_dbiterator_current(dbiter, &node, name);
1738254897Serwin		check_dns_dbiterator_current(result);
1739254897Serwin		result = verifynode(db, ver, origin, mctx, name, node,
1740254897Serwin				    ISC_FALSE, &keyset, act_algorithms,
1741254897Serwin				    bad_algorithms, NULL, NULL, NULL);
1742254897Serwin		check_result(result, "verifynode");
1743254897Serwin		record_found(db, ver, mctx, name, node, &nsec3paramset);
1744254897Serwin		dns_db_detachnode(db, &node);
1745254897Serwin	}
1746254897Serwin	dns_dbiterator_destroy(&dbiter);
1747254897Serwin
1748254897Serwin	dns_rdataset_disassociate(&keyset);
1749254897Serwin	if (dns_rdataset_isassociated(&nsecset))
1750254897Serwin		dns_rdataset_disassociate(&nsecset);
1751254897Serwin	if (dns_rdataset_isassociated(&nsec3paramset))
1752254897Serwin		dns_rdataset_disassociate(&nsec3paramset);
1753254897Serwin
1754254897Serwin	result = verify_nsec3_chains(mctx);
1755254897Serwin	if (vresult == ISC_R_UNSET)
1756254897Serwin		vresult = ISC_R_SUCCESS;
1757254897Serwin	if (result != ISC_R_SUCCESS && vresult == ISC_R_SUCCESS)
1758254897Serwin		vresult = result;
1759254897Serwin	isc_heap_destroy(&expected_chains);
1760254897Serwin	isc_heap_destroy(&found_chains);
1761254897Serwin
1762254897Serwin	/*
1763254897Serwin	 * If we made it this far, we have what we consider a properly signed
1764254897Serwin	 * zone.  Set the good flag.
1765254897Serwin	 */
1766254897Serwin	for (i = 0; i < 256; i++) {
1767254897Serwin		if (bad_algorithms[i] != 0) {
1768254897Serwin			if (first)
1769254897Serwin				fprintf(stderr, "The zone is not fully signed "
1770254897Serwin					"for the following algorithms:");
1771254897Serwin			dns_secalg_format(i, algbuf, sizeof(algbuf));
1772254897Serwin			fprintf(stderr, " %s", algbuf);
1773254897Serwin			first = ISC_FALSE;
1774254897Serwin		}
1775254897Serwin	}
1776254897Serwin	if (!first) {
1777254897Serwin		fprintf(stderr, ".\n");
1778254897Serwin		fatal("DNSSEC completeness test failed.");
1779254897Serwin	}
1780254897Serwin
1781254897Serwin	if (vresult != ISC_R_SUCCESS)
1782254897Serwin		fatal("DNSSEC completeness test failed (%s).",
1783254897Serwin		      dns_result_totext(vresult));
1784254897Serwin
1785254897Serwin	if (goodksk || ignore_kskflag) {
1786254897Serwin		/*
1787254897Serwin		 * Print the success summary.
1788254897Serwin		 */
1789254897Serwin		fprintf(stderr, "Zone fully signed:\n");
1790254897Serwin		for (i = 0; i < 256; i++) {
1791254897Serwin			if ((ksk_algorithms[i] != 0) ||
1792254897Serwin			    (standby_ksk[i] != 0) ||
1793254897Serwin			    (revoked_zsk[i] != 0) ||
1794254897Serwin			    (zsk_algorithms[i] != 0) ||
1795254897Serwin			    (standby_zsk[i] != 0) ||
1796254897Serwin			    (revoked_zsk[i] != 0)) {
1797254897Serwin				dns_secalg_format(i, algbuf, sizeof(algbuf));
1798254897Serwin				fprintf(stderr, "Algorithm: %s: KSKs: "
1799254897Serwin					"%u active, %u stand-by, %u revoked\n",
1800254897Serwin					algbuf, ksk_algorithms[i],
1801254897Serwin					standby_ksk[i], revoked_ksk[i]);
1802254897Serwin				fprintf(stderr, "%*sZSKs: "
1803254897Serwin					"%u active, %u %s, %u revoked\n",
1804254897Serwin					(int) strlen(algbuf) + 13, "",
1805254897Serwin					zsk_algorithms[i],
1806254897Serwin					standby_zsk[i],
1807254897Serwin					keyset_kskonly ? "present" : "stand-by",
1808254897Serwin					revoked_zsk[i]);
1809254897Serwin			}
1810254897Serwin		}
1811254897Serwin	}
1812254897Serwin}
1813