xref: /minix3/external/bsd/bind/dist/bin/dnssec/dnssectool.c (revision 00b67f09dd46474d133c95011a48590a8e8f94c7)
1*00b67f09SDavid van Moolenbroek /*	$NetBSD: dnssectool.c,v 1.9 2015/07/08 17:28:55 christos Exp $	*/
2*00b67f09SDavid van Moolenbroek 
3*00b67f09SDavid van Moolenbroek /*
4*00b67f09SDavid van Moolenbroek  * Copyright (C) 2004, 2005, 2007, 2009-2015  Internet Systems Consortium, Inc. ("ISC")
5*00b67f09SDavid van Moolenbroek  * Copyright (C) 2000, 2001, 2003  Internet Software Consortium.
6*00b67f09SDavid van Moolenbroek  *
7*00b67f09SDavid van Moolenbroek  * Permission to use, copy, modify, and/or distribute this software for any
8*00b67f09SDavid van Moolenbroek  * purpose with or without fee is hereby granted, provided that the above
9*00b67f09SDavid van Moolenbroek  * copyright notice and this permission notice appear in all copies.
10*00b67f09SDavid van Moolenbroek  *
11*00b67f09SDavid van Moolenbroek  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12*00b67f09SDavid van Moolenbroek  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13*00b67f09SDavid van Moolenbroek  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14*00b67f09SDavid van Moolenbroek  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15*00b67f09SDavid van Moolenbroek  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16*00b67f09SDavid van Moolenbroek  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17*00b67f09SDavid van Moolenbroek  * PERFORMANCE OF THIS SOFTWARE.
18*00b67f09SDavid van Moolenbroek  */
19*00b67f09SDavid van Moolenbroek 
20*00b67f09SDavid van Moolenbroek /*! \file */
21*00b67f09SDavid van Moolenbroek 
22*00b67f09SDavid van Moolenbroek /*%
23*00b67f09SDavid van Moolenbroek  * DNSSEC Support Routines.
24*00b67f09SDavid van Moolenbroek  */
25*00b67f09SDavid van Moolenbroek 
26*00b67f09SDavid van Moolenbroek #include <config.h>
27*00b67f09SDavid van Moolenbroek 
28*00b67f09SDavid van Moolenbroek #include <stdlib.h>
29*00b67f09SDavid van Moolenbroek 
30*00b67f09SDavid van Moolenbroek #include <isc/base32.h>
31*00b67f09SDavid van Moolenbroek #include <isc/buffer.h>
32*00b67f09SDavid van Moolenbroek #include <isc/dir.h>
33*00b67f09SDavid van Moolenbroek #include <isc/entropy.h>
34*00b67f09SDavid van Moolenbroek #include <isc/heap.h>
35*00b67f09SDavid van Moolenbroek #include <isc/list.h>
36*00b67f09SDavid van Moolenbroek #include <isc/mem.h>
37*00b67f09SDavid van Moolenbroek #include <isc/string.h>
38*00b67f09SDavid van Moolenbroek #include <isc/time.h>
39*00b67f09SDavid van Moolenbroek #include <isc/util.h>
40*00b67f09SDavid van Moolenbroek #include <isc/print.h>
41*00b67f09SDavid van Moolenbroek 
42*00b67f09SDavid van Moolenbroek #include <dns/db.h>
43*00b67f09SDavid van Moolenbroek #include <dns/dbiterator.h>
44*00b67f09SDavid van Moolenbroek #include <dns/dnssec.h>
45*00b67f09SDavid van Moolenbroek #include <dns/fixedname.h>
46*00b67f09SDavid van Moolenbroek #include <dns/keyvalues.h>
47*00b67f09SDavid van Moolenbroek #include <dns/log.h>
48*00b67f09SDavid van Moolenbroek #include <dns/name.h>
49*00b67f09SDavid van Moolenbroek #include <dns/nsec.h>
50*00b67f09SDavid van Moolenbroek #include <dns/nsec3.h>
51*00b67f09SDavid van Moolenbroek #include <dns/rdatastruct.h>
52*00b67f09SDavid van Moolenbroek #include <dns/rdataclass.h>
53*00b67f09SDavid van Moolenbroek #include <dns/rdataset.h>
54*00b67f09SDavid van Moolenbroek #include <dns/rdatasetiter.h>
55*00b67f09SDavid van Moolenbroek #include <dns/rdatatype.h>
56*00b67f09SDavid van Moolenbroek #include <dns/result.h>
57*00b67f09SDavid van Moolenbroek #include <dns/secalg.h>
58*00b67f09SDavid van Moolenbroek #include <dns/time.h>
59*00b67f09SDavid van Moolenbroek 
60*00b67f09SDavid van Moolenbroek #include "dnssectool.h"
61*00b67f09SDavid van Moolenbroek 
62*00b67f09SDavid van Moolenbroek static isc_heap_t *expected_chains, *found_chains;
63*00b67f09SDavid van Moolenbroek 
64*00b67f09SDavid van Moolenbroek struct nsec3_chain_fixed {
65*00b67f09SDavid van Moolenbroek 	isc_uint8_t	hash;
66*00b67f09SDavid van Moolenbroek 	isc_uint8_t	salt_length;
67*00b67f09SDavid van Moolenbroek 	isc_uint8_t	next_length;
68*00b67f09SDavid van Moolenbroek 	isc_uint16_t	iterations;
69*00b67f09SDavid van Moolenbroek 	/* unsigned char salt[0]; */
70*00b67f09SDavid van Moolenbroek 	/* unsigned char owner[0]; */
71*00b67f09SDavid van Moolenbroek 	/* unsigned char next[0]; */
72*00b67f09SDavid van Moolenbroek };
73*00b67f09SDavid van Moolenbroek 
74*00b67f09SDavid van Moolenbroek extern int verbose;
75*00b67f09SDavid van Moolenbroek extern const char *program;
76*00b67f09SDavid van Moolenbroek 
77*00b67f09SDavid van Moolenbroek typedef struct entropysource entropysource_t;
78*00b67f09SDavid van Moolenbroek 
79*00b67f09SDavid van Moolenbroek struct entropysource {
80*00b67f09SDavid van Moolenbroek 	isc_entropysource_t *source;
81*00b67f09SDavid van Moolenbroek 	isc_mem_t *mctx;
82*00b67f09SDavid van Moolenbroek 	ISC_LINK(entropysource_t) link;
83*00b67f09SDavid van Moolenbroek };
84*00b67f09SDavid van Moolenbroek 
85*00b67f09SDavid van Moolenbroek static ISC_LIST(entropysource_t) sources;
86*00b67f09SDavid van Moolenbroek static fatalcallback_t *fatalcallback = NULL;
87*00b67f09SDavid van Moolenbroek 
88*00b67f09SDavid van Moolenbroek void
fatal(const char * format,...)89*00b67f09SDavid van Moolenbroek fatal(const char *format, ...) {
90*00b67f09SDavid van Moolenbroek 	va_list args;
91*00b67f09SDavid van Moolenbroek 
92*00b67f09SDavid van Moolenbroek 	fprintf(stderr, "%s: fatal: ", program);
93*00b67f09SDavid van Moolenbroek 	va_start(args, format);
94*00b67f09SDavid van Moolenbroek 	vfprintf(stderr, format, args);
95*00b67f09SDavid van Moolenbroek 	va_end(args);
96*00b67f09SDavid van Moolenbroek 	fprintf(stderr, "\n");
97*00b67f09SDavid van Moolenbroek 	if (fatalcallback != NULL)
98*00b67f09SDavid van Moolenbroek 		(*fatalcallback)();
99*00b67f09SDavid van Moolenbroek 	exit(1);
100*00b67f09SDavid van Moolenbroek }
101*00b67f09SDavid van Moolenbroek 
102*00b67f09SDavid van Moolenbroek void
setfatalcallback(fatalcallback_t * callback)103*00b67f09SDavid van Moolenbroek setfatalcallback(fatalcallback_t *callback) {
104*00b67f09SDavid van Moolenbroek 	fatalcallback = callback;
105*00b67f09SDavid van Moolenbroek }
106*00b67f09SDavid van Moolenbroek 
107*00b67f09SDavid van Moolenbroek void
check_result(isc_result_t result,const char * message)108*00b67f09SDavid van Moolenbroek check_result(isc_result_t result, const char *message) {
109*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
110*00b67f09SDavid van Moolenbroek 		fatal("%s: %s", message, isc_result_totext(result));
111*00b67f09SDavid van Moolenbroek }
112*00b67f09SDavid van Moolenbroek 
113*00b67f09SDavid van Moolenbroek void
vbprintf(int level,const char * fmt,...)114*00b67f09SDavid van Moolenbroek vbprintf(int level, const char *fmt, ...) {
115*00b67f09SDavid van Moolenbroek 	va_list ap;
116*00b67f09SDavid van Moolenbroek 	if (level > verbose)
117*00b67f09SDavid van Moolenbroek 		return;
118*00b67f09SDavid van Moolenbroek 	va_start(ap, fmt);
119*00b67f09SDavid van Moolenbroek 	fprintf(stderr, "%s: ", program);
120*00b67f09SDavid van Moolenbroek 	vfprintf(stderr, fmt, ap);
121*00b67f09SDavid van Moolenbroek 	va_end(ap);
122*00b67f09SDavid van Moolenbroek }
123*00b67f09SDavid van Moolenbroek 
124*00b67f09SDavid van Moolenbroek void
version(const char * name)125*00b67f09SDavid van Moolenbroek version(const char *name) {
126*00b67f09SDavid van Moolenbroek 	fprintf(stderr, "%s %s\n", name, VERSION);
127*00b67f09SDavid van Moolenbroek 	exit(0);
128*00b67f09SDavid van Moolenbroek }
129*00b67f09SDavid van Moolenbroek 
130*00b67f09SDavid van Moolenbroek void
type_format(const dns_rdatatype_t type,char * cp,unsigned int size)131*00b67f09SDavid van Moolenbroek type_format(const dns_rdatatype_t type, char *cp, unsigned int size) {
132*00b67f09SDavid van Moolenbroek 	isc_buffer_t b;
133*00b67f09SDavid van Moolenbroek 	isc_region_t r;
134*00b67f09SDavid van Moolenbroek 	isc_result_t result;
135*00b67f09SDavid van Moolenbroek 
136*00b67f09SDavid van Moolenbroek 	isc_buffer_init(&b, cp, size - 1);
137*00b67f09SDavid van Moolenbroek 	result = dns_rdatatype_totext(type, &b);
138*00b67f09SDavid van Moolenbroek 	check_result(result, "dns_rdatatype_totext()");
139*00b67f09SDavid van Moolenbroek 	isc_buffer_usedregion(&b, &r);
140*00b67f09SDavid van Moolenbroek 	r.base[r.length] = 0;
141*00b67f09SDavid van Moolenbroek }
142*00b67f09SDavid van Moolenbroek 
143*00b67f09SDavid van Moolenbroek void
sig_format(dns_rdata_rrsig_t * sig,char * cp,unsigned int size)144*00b67f09SDavid van Moolenbroek sig_format(dns_rdata_rrsig_t *sig, char *cp, unsigned int size) {
145*00b67f09SDavid van Moolenbroek 	char namestr[DNS_NAME_FORMATSIZE];
146*00b67f09SDavid van Moolenbroek 	char algstr[DNS_NAME_FORMATSIZE];
147*00b67f09SDavid van Moolenbroek 
148*00b67f09SDavid van Moolenbroek 	dns_name_format(&sig->signer, namestr, sizeof(namestr));
149*00b67f09SDavid van Moolenbroek 	dns_secalg_format(sig->algorithm, algstr, sizeof(algstr));
150*00b67f09SDavid van Moolenbroek 	snprintf(cp, size, "%s/%s/%d", namestr, algstr, sig->keyid);
151*00b67f09SDavid van Moolenbroek }
152*00b67f09SDavid van Moolenbroek 
153*00b67f09SDavid van Moolenbroek void
setup_logging(isc_mem_t * mctx,isc_log_t ** logp)154*00b67f09SDavid van Moolenbroek setup_logging(isc_mem_t *mctx, isc_log_t **logp) {
155*00b67f09SDavid van Moolenbroek 	isc_result_t result;
156*00b67f09SDavid van Moolenbroek 	isc_logdestination_t destination;
157*00b67f09SDavid van Moolenbroek 	isc_logconfig_t *logconfig = NULL;
158*00b67f09SDavid van Moolenbroek 	isc_log_t *log = NULL;
159*00b67f09SDavid van Moolenbroek 	int level;
160*00b67f09SDavid van Moolenbroek 
161*00b67f09SDavid van Moolenbroek 	if (verbose < 0)
162*00b67f09SDavid van Moolenbroek 		verbose = 0;
163*00b67f09SDavid van Moolenbroek 	switch (verbose) {
164*00b67f09SDavid van Moolenbroek 	case 0:
165*00b67f09SDavid van Moolenbroek 		/*
166*00b67f09SDavid van Moolenbroek 		 * We want to see warnings about things like out-of-zone
167*00b67f09SDavid van Moolenbroek 		 * data in the master file even when not verbose.
168*00b67f09SDavid van Moolenbroek 		 */
169*00b67f09SDavid van Moolenbroek 		level = ISC_LOG_WARNING;
170*00b67f09SDavid van Moolenbroek 		break;
171*00b67f09SDavid van Moolenbroek 	case 1:
172*00b67f09SDavid van Moolenbroek 		level = ISC_LOG_INFO;
173*00b67f09SDavid van Moolenbroek 		break;
174*00b67f09SDavid van Moolenbroek 	default:
175*00b67f09SDavid van Moolenbroek 		level = ISC_LOG_DEBUG(verbose - 2 + 1);
176*00b67f09SDavid van Moolenbroek 		break;
177*00b67f09SDavid van Moolenbroek 	}
178*00b67f09SDavid van Moolenbroek 
179*00b67f09SDavid van Moolenbroek 	RUNTIME_CHECK(isc_log_create(mctx, &log, &logconfig) == ISC_R_SUCCESS);
180*00b67f09SDavid van Moolenbroek 	isc_log_setcontext(log);
181*00b67f09SDavid van Moolenbroek 	dns_log_init(log);
182*00b67f09SDavid van Moolenbroek 	dns_log_setcontext(log);
183*00b67f09SDavid van Moolenbroek 
184*00b67f09SDavid van Moolenbroek 	RUNTIME_CHECK(isc_log_settag(logconfig, program) == ISC_R_SUCCESS);
185*00b67f09SDavid van Moolenbroek 
186*00b67f09SDavid van Moolenbroek 	/*
187*00b67f09SDavid van Moolenbroek 	 * Set up a channel similar to default_stderr except:
188*00b67f09SDavid van Moolenbroek 	 *  - the logging level is passed in
189*00b67f09SDavid van Moolenbroek 	 *  - the program name and logging level are printed
190*00b67f09SDavid van Moolenbroek 	 *  - no time stamp is printed
191*00b67f09SDavid van Moolenbroek 	 */
192*00b67f09SDavid van Moolenbroek 	destination.file.stream = stderr;
193*00b67f09SDavid van Moolenbroek 	destination.file.name = NULL;
194*00b67f09SDavid van Moolenbroek 	destination.file.versions = ISC_LOG_ROLLNEVER;
195*00b67f09SDavid van Moolenbroek 	destination.file.maximum_size = 0;
196*00b67f09SDavid van Moolenbroek 	result = isc_log_createchannel(logconfig, "stderr",
197*00b67f09SDavid van Moolenbroek 				       ISC_LOG_TOFILEDESC,
198*00b67f09SDavid van Moolenbroek 				       level,
199*00b67f09SDavid van Moolenbroek 				       &destination,
200*00b67f09SDavid van Moolenbroek 				       ISC_LOG_PRINTTAG|ISC_LOG_PRINTLEVEL);
201*00b67f09SDavid van Moolenbroek 	check_result(result, "isc_log_createchannel()");
202*00b67f09SDavid van Moolenbroek 
203*00b67f09SDavid van Moolenbroek 	RUNTIME_CHECK(isc_log_usechannel(logconfig, "stderr",
204*00b67f09SDavid van Moolenbroek 					 NULL, NULL) == ISC_R_SUCCESS);
205*00b67f09SDavid van Moolenbroek 
206*00b67f09SDavid van Moolenbroek 	*logp = log;
207*00b67f09SDavid van Moolenbroek }
208*00b67f09SDavid van Moolenbroek 
209*00b67f09SDavid van Moolenbroek void
cleanup_logging(isc_log_t ** logp)210*00b67f09SDavid van Moolenbroek cleanup_logging(isc_log_t **logp) {
211*00b67f09SDavid van Moolenbroek 	isc_log_t *log;
212*00b67f09SDavid van Moolenbroek 
213*00b67f09SDavid van Moolenbroek 	REQUIRE(logp != NULL);
214*00b67f09SDavid van Moolenbroek 
215*00b67f09SDavid van Moolenbroek 	log = *logp;
216*00b67f09SDavid van Moolenbroek 	if (log == NULL)
217*00b67f09SDavid van Moolenbroek 		return;
218*00b67f09SDavid van Moolenbroek 	isc_log_destroy(&log);
219*00b67f09SDavid van Moolenbroek 	isc_log_setcontext(NULL);
220*00b67f09SDavid van Moolenbroek 	dns_log_setcontext(NULL);
221*00b67f09SDavid van Moolenbroek 	logp = NULL;
222*00b67f09SDavid van Moolenbroek }
223*00b67f09SDavid van Moolenbroek 
224*00b67f09SDavid van Moolenbroek void
setup_entropy(isc_mem_t * mctx,const char * randomfile,isc_entropy_t ** ectx)225*00b67f09SDavid van Moolenbroek setup_entropy(isc_mem_t *mctx, const char *randomfile, isc_entropy_t **ectx) {
226*00b67f09SDavid van Moolenbroek 	isc_result_t result;
227*00b67f09SDavid van Moolenbroek 	isc_entropysource_t *source = NULL;
228*00b67f09SDavid van Moolenbroek 	entropysource_t *elt;
229*00b67f09SDavid van Moolenbroek 	int usekeyboard = ISC_ENTROPY_KEYBOARDMAYBE;
230*00b67f09SDavid van Moolenbroek 
231*00b67f09SDavid van Moolenbroek 	REQUIRE(ectx != NULL);
232*00b67f09SDavid van Moolenbroek 
233*00b67f09SDavid van Moolenbroek 	if (*ectx == NULL) {
234*00b67f09SDavid van Moolenbroek 		result = isc_entropy_create(mctx, ectx);
235*00b67f09SDavid van Moolenbroek 		if (result != ISC_R_SUCCESS)
236*00b67f09SDavid van Moolenbroek 			fatal("could not create entropy object");
237*00b67f09SDavid van Moolenbroek 		ISC_LIST_INIT(sources);
238*00b67f09SDavid van Moolenbroek 	}
239*00b67f09SDavid van Moolenbroek 
240*00b67f09SDavid van Moolenbroek 	if (randomfile != NULL && strcmp(randomfile, "keyboard") == 0) {
241*00b67f09SDavid van Moolenbroek 		usekeyboard = ISC_ENTROPY_KEYBOARDYES;
242*00b67f09SDavid van Moolenbroek 		randomfile = NULL;
243*00b67f09SDavid van Moolenbroek 	}
244*00b67f09SDavid van Moolenbroek 
245*00b67f09SDavid van Moolenbroek 	result = isc_entropy_usebestsource(*ectx, &source, randomfile,
246*00b67f09SDavid van Moolenbroek 					   usekeyboard);
247*00b67f09SDavid van Moolenbroek 
248*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
249*00b67f09SDavid van Moolenbroek 		fatal("could not initialize entropy source: %s",
250*00b67f09SDavid van Moolenbroek 		      isc_result_totext(result));
251*00b67f09SDavid van Moolenbroek 
252*00b67f09SDavid van Moolenbroek 	if (source != NULL) {
253*00b67f09SDavid van Moolenbroek 		elt = isc_mem_get(mctx, sizeof(*elt));
254*00b67f09SDavid van Moolenbroek 		if (elt == NULL)
255*00b67f09SDavid van Moolenbroek 			fatal("out of memory");
256*00b67f09SDavid van Moolenbroek 		elt->source = source;
257*00b67f09SDavid van Moolenbroek 		elt->mctx = mctx;
258*00b67f09SDavid van Moolenbroek 		ISC_LINK_INIT(elt, link);
259*00b67f09SDavid van Moolenbroek 		ISC_LIST_APPEND(sources, elt, link);
260*00b67f09SDavid van Moolenbroek 	}
261*00b67f09SDavid van Moolenbroek }
262*00b67f09SDavid van Moolenbroek 
263*00b67f09SDavid van Moolenbroek void
cleanup_entropy(isc_entropy_t ** ectx)264*00b67f09SDavid van Moolenbroek cleanup_entropy(isc_entropy_t **ectx) {
265*00b67f09SDavid van Moolenbroek 	entropysource_t *source;
266*00b67f09SDavid van Moolenbroek 	while (!ISC_LIST_EMPTY(sources)) {
267*00b67f09SDavid van Moolenbroek 		source = ISC_LIST_HEAD(sources);
268*00b67f09SDavid van Moolenbroek 		ISC_LIST_UNLINK(sources, source, link);
269*00b67f09SDavid van Moolenbroek 		isc_entropy_destroysource(&source->source);
270*00b67f09SDavid van Moolenbroek 		isc_mem_put(source->mctx, source, sizeof(*source));
271*00b67f09SDavid van Moolenbroek 	}
272*00b67f09SDavid van Moolenbroek 	isc_entropy_detach(ectx);
273*00b67f09SDavid van Moolenbroek }
274*00b67f09SDavid van Moolenbroek 
275*00b67f09SDavid van Moolenbroek static isc_stdtime_t
time_units(isc_stdtime_t offset,char * suffix,const char * str)276*00b67f09SDavid van Moolenbroek time_units(isc_stdtime_t offset, char *suffix, const char *str) {
277*00b67f09SDavid van Moolenbroek 	switch (suffix[0]) {
278*00b67f09SDavid van Moolenbroek 	    case 'Y': case 'y':
279*00b67f09SDavid van Moolenbroek 		return (offset * (365 * 24 * 3600));
280*00b67f09SDavid van Moolenbroek 	    case 'M': case 'm':
281*00b67f09SDavid van Moolenbroek 		switch (suffix[1]) {
282*00b67f09SDavid van Moolenbroek 		    case 'O': case 'o':
283*00b67f09SDavid van Moolenbroek 			return (offset * (30 * 24 * 3600));
284*00b67f09SDavid van Moolenbroek 		    case 'I': case 'i':
285*00b67f09SDavid van Moolenbroek 			return (offset * 60);
286*00b67f09SDavid van Moolenbroek 		    case '\0':
287*00b67f09SDavid van Moolenbroek 			fatal("'%s' ambiguous: use 'mi' for minutes "
288*00b67f09SDavid van Moolenbroek 			      "or 'mo' for months", str);
289*00b67f09SDavid van Moolenbroek 		    default:
290*00b67f09SDavid van Moolenbroek 			fatal("time value %s is invalid", str);
291*00b67f09SDavid van Moolenbroek 		}
292*00b67f09SDavid van Moolenbroek 		/* NOTREACHED */
293*00b67f09SDavid van Moolenbroek 		break;
294*00b67f09SDavid van Moolenbroek 	    case 'W': case 'w':
295*00b67f09SDavid van Moolenbroek 		return (offset * (7 * 24 * 3600));
296*00b67f09SDavid van Moolenbroek 	    case 'D': case 'd':
297*00b67f09SDavid van Moolenbroek 		return (offset * (24 * 3600));
298*00b67f09SDavid van Moolenbroek 	    case 'H': case 'h':
299*00b67f09SDavid van Moolenbroek 		return (offset * 3600);
300*00b67f09SDavid van Moolenbroek 	    case 'S': case 's': case '\0':
301*00b67f09SDavid van Moolenbroek 		return (offset);
302*00b67f09SDavid van Moolenbroek 	    default:
303*00b67f09SDavid van Moolenbroek 		fatal("time value %s is invalid", str);
304*00b67f09SDavid van Moolenbroek 	}
305*00b67f09SDavid van Moolenbroek 	/* NOTREACHED */
306*00b67f09SDavid van Moolenbroek 	return(0); /* silence compiler warning */
307*00b67f09SDavid van Moolenbroek }
308*00b67f09SDavid van Moolenbroek 
309*00b67f09SDavid van Moolenbroek static inline isc_boolean_t
isnone(const char * str)310*00b67f09SDavid van Moolenbroek isnone(const char *str) {
311*00b67f09SDavid van Moolenbroek 	return (ISC_TF((strcasecmp(str, "none") == 0) ||
312*00b67f09SDavid van Moolenbroek 		       (strcasecmp(str, "never") == 0)));
313*00b67f09SDavid van Moolenbroek }
314*00b67f09SDavid van Moolenbroek 
315*00b67f09SDavid van Moolenbroek dns_ttl_t
strtottl(const char * str)316*00b67f09SDavid van Moolenbroek strtottl(const char *str) {
317*00b67f09SDavid van Moolenbroek 	const char *orig = str;
318*00b67f09SDavid van Moolenbroek 	dns_ttl_t ttl;
319*00b67f09SDavid van Moolenbroek 	char *endp;
320*00b67f09SDavid van Moolenbroek 
321*00b67f09SDavid van Moolenbroek 	if (isnone(str))
322*00b67f09SDavid van Moolenbroek 		return ((dns_ttl_t) 0);
323*00b67f09SDavid van Moolenbroek 
324*00b67f09SDavid van Moolenbroek 	ttl = strtol(str, &endp, 0);
325*00b67f09SDavid van Moolenbroek 	if (ttl == 0 && endp == str)
326*00b67f09SDavid van Moolenbroek 		fatal("TTL must be numeric");
327*00b67f09SDavid van Moolenbroek 	ttl = time_units(ttl, endp, orig);
328*00b67f09SDavid van Moolenbroek 	return (ttl);
329*00b67f09SDavid van Moolenbroek }
330*00b67f09SDavid van Moolenbroek 
331*00b67f09SDavid van Moolenbroek isc_stdtime_t
strtotime(const char * str,isc_int64_t now,isc_int64_t base,isc_boolean_t * setp)332*00b67f09SDavid van Moolenbroek strtotime(const char *str, isc_int64_t now, isc_int64_t base,
333*00b67f09SDavid van Moolenbroek 	  isc_boolean_t *setp)
334*00b67f09SDavid van Moolenbroek {
335*00b67f09SDavid van Moolenbroek 	isc_int64_t val, offset;
336*00b67f09SDavid van Moolenbroek 	isc_result_t result;
337*00b67f09SDavid van Moolenbroek 	const char *orig = str;
338*00b67f09SDavid van Moolenbroek 	char *endp;
339*00b67f09SDavid van Moolenbroek 	size_t n;
340*00b67f09SDavid van Moolenbroek 
341*00b67f09SDavid van Moolenbroek 	if (isnone(str)) {
342*00b67f09SDavid van Moolenbroek 		if (setp != NULL)
343*00b67f09SDavid van Moolenbroek 			*setp = ISC_FALSE;
344*00b67f09SDavid van Moolenbroek 		return ((isc_stdtime_t) 0);
345*00b67f09SDavid van Moolenbroek 	}
346*00b67f09SDavid van Moolenbroek 
347*00b67f09SDavid van Moolenbroek 	if (setp != NULL)
348*00b67f09SDavid van Moolenbroek 		*setp = ISC_TRUE;
349*00b67f09SDavid van Moolenbroek 
350*00b67f09SDavid van Moolenbroek 	if ((str[0] == '0' || str[0] == '-') && str[1] == '\0')
351*00b67f09SDavid van Moolenbroek 		return ((isc_stdtime_t) 0);
352*00b67f09SDavid van Moolenbroek 
353*00b67f09SDavid van Moolenbroek 	/*
354*00b67f09SDavid van Moolenbroek 	 * We accept times in the following formats:
355*00b67f09SDavid van Moolenbroek 	 *   now([+-]offset)
356*00b67f09SDavid van Moolenbroek 	 *   YYYYMMDD([+-]offset)
357*00b67f09SDavid van Moolenbroek 	 *   YYYYMMDDhhmmss([+-]offset)
358*00b67f09SDavid van Moolenbroek 	 *   [+-]offset
359*00b67f09SDavid van Moolenbroek 	 */
360*00b67f09SDavid van Moolenbroek 	n = strspn(str, "0123456789");
361*00b67f09SDavid van Moolenbroek 	if ((n == 8u || n == 14u) &&
362*00b67f09SDavid van Moolenbroek 	    (str[n] == '\0' || str[n] == '-' || str[n] == '+'))
363*00b67f09SDavid van Moolenbroek 	{
364*00b67f09SDavid van Moolenbroek 		char timestr[15];
365*00b67f09SDavid van Moolenbroek 
366*00b67f09SDavid van Moolenbroek 		strlcpy(timestr, str, sizeof(timestr));
367*00b67f09SDavid van Moolenbroek 		timestr[n] = 0;
368*00b67f09SDavid van Moolenbroek 		if (n == 8u)
369*00b67f09SDavid van Moolenbroek 			strlcat(timestr, "000000", sizeof(timestr));
370*00b67f09SDavid van Moolenbroek 		result = dns_time64_fromtext(timestr, &val);
371*00b67f09SDavid van Moolenbroek 		if (result != ISC_R_SUCCESS)
372*00b67f09SDavid van Moolenbroek 			fatal("time value %s is invalid: %s", orig,
373*00b67f09SDavid van Moolenbroek 			      isc_result_totext(result));
374*00b67f09SDavid van Moolenbroek 		base = val;
375*00b67f09SDavid van Moolenbroek 		str += n;
376*00b67f09SDavid van Moolenbroek 	} else if (strncmp(str, "now", 3) == 0) {
377*00b67f09SDavid van Moolenbroek 		base = now;
378*00b67f09SDavid van Moolenbroek 		str += 3;
379*00b67f09SDavid van Moolenbroek 	}
380*00b67f09SDavid van Moolenbroek 
381*00b67f09SDavid van Moolenbroek 	if (str[0] == '\0')
382*00b67f09SDavid van Moolenbroek 		return ((isc_stdtime_t) base);
383*00b67f09SDavid van Moolenbroek 	else if (str[0] == '+') {
384*00b67f09SDavid van Moolenbroek 		offset = strtol(str + 1, &endp, 0);
385*00b67f09SDavid van Moolenbroek 		offset = time_units((isc_stdtime_t) offset, endp, orig);
386*00b67f09SDavid van Moolenbroek 		val = base + offset;
387*00b67f09SDavid van Moolenbroek 	} else if (str[0] == '-') {
388*00b67f09SDavid van Moolenbroek 		offset = strtol(str + 1, &endp, 0);
389*00b67f09SDavid van Moolenbroek 		offset = time_units((isc_stdtime_t) offset, endp, orig);
390*00b67f09SDavid van Moolenbroek 		val = base - offset;
391*00b67f09SDavid van Moolenbroek 	} else
392*00b67f09SDavid van Moolenbroek 		fatal("time value %s is invalid", orig);
393*00b67f09SDavid van Moolenbroek 
394*00b67f09SDavid van Moolenbroek 	return ((isc_stdtime_t) val);
395*00b67f09SDavid van Moolenbroek }
396*00b67f09SDavid van Moolenbroek 
397*00b67f09SDavid van Moolenbroek dns_rdataclass_t
strtoclass(const char * str)398*00b67f09SDavid van Moolenbroek strtoclass(const char *str) {
399*00b67f09SDavid van Moolenbroek 	isc_textregion_t r;
400*00b67f09SDavid van Moolenbroek 	dns_rdataclass_t rdclass;
401*00b67f09SDavid van Moolenbroek 	isc_result_t ret;
402*00b67f09SDavid van Moolenbroek 
403*00b67f09SDavid van Moolenbroek 	if (str == NULL)
404*00b67f09SDavid van Moolenbroek 		return dns_rdataclass_in;
405*00b67f09SDavid van Moolenbroek 	DE_CONST(str, r.base);
406*00b67f09SDavid van Moolenbroek 	r.length = strlen(str);
407*00b67f09SDavid van Moolenbroek 	ret = dns_rdataclass_fromtext(&rdclass, &r);
408*00b67f09SDavid van Moolenbroek 	if (ret != ISC_R_SUCCESS)
409*00b67f09SDavid van Moolenbroek 		fatal("unknown class %s", str);
410*00b67f09SDavid van Moolenbroek 	return (rdclass);
411*00b67f09SDavid van Moolenbroek }
412*00b67f09SDavid van Moolenbroek 
413*00b67f09SDavid van Moolenbroek isc_result_t
try_dir(const char * dirname)414*00b67f09SDavid van Moolenbroek try_dir(const char *dirname) {
415*00b67f09SDavid van Moolenbroek 	isc_result_t result;
416*00b67f09SDavid van Moolenbroek 	isc_dir_t d;
417*00b67f09SDavid van Moolenbroek 
418*00b67f09SDavid van Moolenbroek 	isc_dir_init(&d);
419*00b67f09SDavid van Moolenbroek 	result = isc_dir_open(&d, dirname);
420*00b67f09SDavid van Moolenbroek 	if (result == ISC_R_SUCCESS) {
421*00b67f09SDavid van Moolenbroek 		isc_dir_close(&d);
422*00b67f09SDavid van Moolenbroek 	}
423*00b67f09SDavid van Moolenbroek 	return (result);
424*00b67f09SDavid van Moolenbroek }
425*00b67f09SDavid van Moolenbroek 
426*00b67f09SDavid van Moolenbroek /*
427*00b67f09SDavid van Moolenbroek  * Check private key version compatibility.
428*00b67f09SDavid van Moolenbroek  */
429*00b67f09SDavid van Moolenbroek void
check_keyversion(dst_key_t * key,char * keystr)430*00b67f09SDavid van Moolenbroek check_keyversion(dst_key_t *key, char *keystr) {
431*00b67f09SDavid van Moolenbroek 	int major, minor;
432*00b67f09SDavid van Moolenbroek 	dst_key_getprivateformat(key, &major, &minor);
433*00b67f09SDavid van Moolenbroek 	INSIST(major <= DST_MAJOR_VERSION); /* invalid private key */
434*00b67f09SDavid van Moolenbroek 
435*00b67f09SDavid van Moolenbroek 	if (major < DST_MAJOR_VERSION || minor < DST_MINOR_VERSION)
436*00b67f09SDavid van Moolenbroek 		fatal("Key %s has incompatible format version %d.%d, "
437*00b67f09SDavid van Moolenbroek 		      "use -f to force upgrade to new version.",
438*00b67f09SDavid van Moolenbroek 		      keystr, major, minor);
439*00b67f09SDavid van Moolenbroek 	if (minor > DST_MINOR_VERSION)
440*00b67f09SDavid van Moolenbroek 		fatal("Key %s has incompatible format version %d.%d, "
441*00b67f09SDavid van Moolenbroek 		      "use -f to force downgrade to current version.",
442*00b67f09SDavid van Moolenbroek 		      keystr, major, minor);
443*00b67f09SDavid van Moolenbroek }
444*00b67f09SDavid van Moolenbroek 
445*00b67f09SDavid van Moolenbroek void
set_keyversion(dst_key_t * key)446*00b67f09SDavid van Moolenbroek set_keyversion(dst_key_t *key) {
447*00b67f09SDavid van Moolenbroek 	int major, minor;
448*00b67f09SDavid van Moolenbroek 	dst_key_getprivateformat(key, &major, &minor);
449*00b67f09SDavid van Moolenbroek 	INSIST(major <= DST_MAJOR_VERSION);
450*00b67f09SDavid van Moolenbroek 
451*00b67f09SDavid van Moolenbroek 	if (major != DST_MAJOR_VERSION || minor != DST_MINOR_VERSION)
452*00b67f09SDavid van Moolenbroek 		dst_key_setprivateformat(key, DST_MAJOR_VERSION,
453*00b67f09SDavid van Moolenbroek 					 DST_MINOR_VERSION);
454*00b67f09SDavid van Moolenbroek 
455*00b67f09SDavid van Moolenbroek 	/*
456*00b67f09SDavid van Moolenbroek 	 * If the key is from a version older than 1.3, set
457*00b67f09SDavid van Moolenbroek 	 * set the creation date
458*00b67f09SDavid van Moolenbroek 	 */
459*00b67f09SDavid van Moolenbroek 	if (major < 1 || (major == 1 && minor <= 2)) {
460*00b67f09SDavid van Moolenbroek 		isc_stdtime_t now;
461*00b67f09SDavid van Moolenbroek 		isc_stdtime_get(&now);
462*00b67f09SDavid van Moolenbroek 		dst_key_settime(key, DST_TIME_CREATED, now);
463*00b67f09SDavid van Moolenbroek 	}
464*00b67f09SDavid van Moolenbroek }
465*00b67f09SDavid van Moolenbroek 
466*00b67f09SDavid van Moolenbroek isc_boolean_t
key_collision(dst_key_t * dstkey,dns_name_t * name,const char * dir,isc_mem_t * mctx,isc_boolean_t * exact)467*00b67f09SDavid van Moolenbroek key_collision(dst_key_t *dstkey, dns_name_t *name, const char *dir,
468*00b67f09SDavid van Moolenbroek 	      isc_mem_t *mctx, isc_boolean_t *exact)
469*00b67f09SDavid van Moolenbroek {
470*00b67f09SDavid van Moolenbroek 	isc_result_t result;
471*00b67f09SDavid van Moolenbroek 	isc_boolean_t conflict = ISC_FALSE;
472*00b67f09SDavid van Moolenbroek 	dns_dnsseckeylist_t matchkeys;
473*00b67f09SDavid van Moolenbroek 	dns_dnsseckey_t *key = NULL;
474*00b67f09SDavid van Moolenbroek 	isc_uint16_t id, oldid;
475*00b67f09SDavid van Moolenbroek 	isc_uint32_t rid, roldid;
476*00b67f09SDavid van Moolenbroek 	dns_secalg_t alg;
477*00b67f09SDavid van Moolenbroek 
478*00b67f09SDavid van Moolenbroek 	if (exact != NULL)
479*00b67f09SDavid van Moolenbroek 		*exact = ISC_FALSE;
480*00b67f09SDavid van Moolenbroek 
481*00b67f09SDavid van Moolenbroek 	id = dst_key_id(dstkey);
482*00b67f09SDavid van Moolenbroek 	rid = dst_key_rid(dstkey);
483*00b67f09SDavid van Moolenbroek 	alg = dst_key_alg(dstkey);
484*00b67f09SDavid van Moolenbroek 
485*00b67f09SDavid van Moolenbroek 	ISC_LIST_INIT(matchkeys);
486*00b67f09SDavid van Moolenbroek 	result = dns_dnssec_findmatchingkeys(name, dir, mctx, &matchkeys);
487*00b67f09SDavid van Moolenbroek 	if (result == ISC_R_NOTFOUND)
488*00b67f09SDavid van Moolenbroek 		return (ISC_FALSE);
489*00b67f09SDavid van Moolenbroek 
490*00b67f09SDavid van Moolenbroek 	while (!ISC_LIST_EMPTY(matchkeys) && !conflict) {
491*00b67f09SDavid van Moolenbroek 		key = ISC_LIST_HEAD(matchkeys);
492*00b67f09SDavid van Moolenbroek 		if (dst_key_alg(key->key) != alg)
493*00b67f09SDavid van Moolenbroek 			goto next;
494*00b67f09SDavid van Moolenbroek 
495*00b67f09SDavid van Moolenbroek 		oldid = dst_key_id(key->key);
496*00b67f09SDavid van Moolenbroek 		roldid = dst_key_rid(key->key);
497*00b67f09SDavid van Moolenbroek 
498*00b67f09SDavid van Moolenbroek 		if (oldid == rid || roldid == id || id == oldid) {
499*00b67f09SDavid van Moolenbroek 			conflict = ISC_TRUE;
500*00b67f09SDavid van Moolenbroek 			if (id != oldid) {
501*00b67f09SDavid van Moolenbroek 				if (verbose > 1)
502*00b67f09SDavid van Moolenbroek 					fprintf(stderr, "Key ID %d could "
503*00b67f09SDavid van Moolenbroek 						"collide with %d\n",
504*00b67f09SDavid van Moolenbroek 						id, oldid);
505*00b67f09SDavid van Moolenbroek 			} else {
506*00b67f09SDavid van Moolenbroek 				if (exact != NULL)
507*00b67f09SDavid van Moolenbroek 					*exact = ISC_TRUE;
508*00b67f09SDavid van Moolenbroek 				if (verbose > 1)
509*00b67f09SDavid van Moolenbroek 					fprintf(stderr, "Key ID %d exists\n",
510*00b67f09SDavid van Moolenbroek 						id);
511*00b67f09SDavid van Moolenbroek 			}
512*00b67f09SDavid van Moolenbroek 		}
513*00b67f09SDavid van Moolenbroek 
514*00b67f09SDavid van Moolenbroek  next:
515*00b67f09SDavid van Moolenbroek 		ISC_LIST_UNLINK(matchkeys, key, link);
516*00b67f09SDavid van Moolenbroek 		dns_dnsseckey_destroy(mctx, &key);
517*00b67f09SDavid van Moolenbroek 	}
518*00b67f09SDavid van Moolenbroek 
519*00b67f09SDavid van Moolenbroek 	/* Finish freeing the list */
520*00b67f09SDavid van Moolenbroek 	while (!ISC_LIST_EMPTY(matchkeys)) {
521*00b67f09SDavid van Moolenbroek 		key = ISC_LIST_HEAD(matchkeys);
522*00b67f09SDavid van Moolenbroek 		ISC_LIST_UNLINK(matchkeys, key, link);
523*00b67f09SDavid van Moolenbroek 		dns_dnsseckey_destroy(mctx, &key);
524*00b67f09SDavid van Moolenbroek 	}
525*00b67f09SDavid van Moolenbroek 
526*00b67f09SDavid van Moolenbroek 	return (conflict);
527*00b67f09SDavid van Moolenbroek }
528*00b67f09SDavid van Moolenbroek 
529*00b67f09SDavid van Moolenbroek isc_boolean_t
is_delegation(dns_db_t * db,dns_dbversion_t * ver,dns_name_t * origin,dns_name_t * name,dns_dbnode_t * node,isc_uint32_t * ttlp)530*00b67f09SDavid van Moolenbroek is_delegation(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin,
531*00b67f09SDavid van Moolenbroek 	      dns_name_t *name, dns_dbnode_t *node, isc_uint32_t *ttlp)
532*00b67f09SDavid van Moolenbroek {
533*00b67f09SDavid van Moolenbroek 	dns_rdataset_t nsset;
534*00b67f09SDavid van Moolenbroek 	isc_result_t result;
535*00b67f09SDavid van Moolenbroek 
536*00b67f09SDavid van Moolenbroek 	if (dns_name_equal(name, origin))
537*00b67f09SDavid van Moolenbroek 		return (ISC_FALSE);
538*00b67f09SDavid van Moolenbroek 
539*00b67f09SDavid van Moolenbroek 	dns_rdataset_init(&nsset);
540*00b67f09SDavid van Moolenbroek 	result = dns_db_findrdataset(db, node, ver, dns_rdatatype_ns,
541*00b67f09SDavid van Moolenbroek 				     0, 0, &nsset, NULL);
542*00b67f09SDavid van Moolenbroek 	if (dns_rdataset_isassociated(&nsset)) {
543*00b67f09SDavid van Moolenbroek 		if (ttlp != NULL)
544*00b67f09SDavid van Moolenbroek 			*ttlp = nsset.ttl;
545*00b67f09SDavid van Moolenbroek 		dns_rdataset_disassociate(&nsset);
546*00b67f09SDavid van Moolenbroek 	}
547*00b67f09SDavid van Moolenbroek 
548*00b67f09SDavid van Moolenbroek 	return (ISC_TF(result == ISC_R_SUCCESS));
549*00b67f09SDavid van Moolenbroek }
550*00b67f09SDavid van Moolenbroek 
551*00b67f09SDavid van Moolenbroek static isc_boolean_t
goodsig(dns_name_t * origin,dns_rdata_t * sigrdata,dns_name_t * name,dns_rdataset_t * keyrdataset,dns_rdataset_t * rdataset,isc_mem_t * mctx)552*00b67f09SDavid van Moolenbroek goodsig(dns_name_t *origin, dns_rdata_t *sigrdata, dns_name_t *name,
553*00b67f09SDavid van Moolenbroek 	dns_rdataset_t *keyrdataset, dns_rdataset_t *rdataset, isc_mem_t *mctx)
554*00b67f09SDavid van Moolenbroek {
555*00b67f09SDavid van Moolenbroek 	dns_rdata_dnskey_t key;
556*00b67f09SDavid van Moolenbroek 	dns_rdata_rrsig_t sig;
557*00b67f09SDavid van Moolenbroek 	dst_key_t *dstkey = NULL;
558*00b67f09SDavid van Moolenbroek 	isc_result_t result;
559*00b67f09SDavid van Moolenbroek 
560*00b67f09SDavid van Moolenbroek 	result = dns_rdata_tostruct(sigrdata, &sig, NULL);
561*00b67f09SDavid van Moolenbroek 	check_result(result, "dns_rdata_tostruct()");
562*00b67f09SDavid van Moolenbroek 
563*00b67f09SDavid van Moolenbroek 	for (result = dns_rdataset_first(keyrdataset);
564*00b67f09SDavid van Moolenbroek 	     result == ISC_R_SUCCESS;
565*00b67f09SDavid van Moolenbroek 	     result = dns_rdataset_next(keyrdataset)) {
566*00b67f09SDavid van Moolenbroek 		dns_rdata_t rdata = DNS_RDATA_INIT;
567*00b67f09SDavid van Moolenbroek 		dns_rdataset_current(keyrdataset, &rdata);
568*00b67f09SDavid van Moolenbroek 		result = dns_rdata_tostruct(&rdata, &key, NULL);
569*00b67f09SDavid van Moolenbroek 		check_result(result, "dns_rdata_tostruct()");
570*00b67f09SDavid van Moolenbroek 		result = dns_dnssec_keyfromrdata(origin, &rdata, mctx,
571*00b67f09SDavid van Moolenbroek 						 &dstkey);
572*00b67f09SDavid van Moolenbroek 		if (result != ISC_R_SUCCESS)
573*00b67f09SDavid van Moolenbroek 			return (ISC_FALSE);
574*00b67f09SDavid van Moolenbroek 		if (sig.algorithm != key.algorithm ||
575*00b67f09SDavid van Moolenbroek 		    sig.keyid != dst_key_id(dstkey) ||
576*00b67f09SDavid van Moolenbroek 		    !dns_name_equal(&sig.signer, origin)) {
577*00b67f09SDavid van Moolenbroek 			dst_key_free(&dstkey);
578*00b67f09SDavid van Moolenbroek 			continue;
579*00b67f09SDavid van Moolenbroek 		}
580*00b67f09SDavid van Moolenbroek 		result = dns_dnssec_verify(name, rdataset, dstkey, ISC_FALSE,
581*00b67f09SDavid van Moolenbroek 					   mctx, sigrdata);
582*00b67f09SDavid van Moolenbroek 		dst_key_free(&dstkey);
583*00b67f09SDavid van Moolenbroek 		if (result == ISC_R_SUCCESS)
584*00b67f09SDavid van Moolenbroek 			return(ISC_TRUE);
585*00b67f09SDavid van Moolenbroek 	}
586*00b67f09SDavid van Moolenbroek 	return (ISC_FALSE);
587*00b67f09SDavid van Moolenbroek }
588*00b67f09SDavid van Moolenbroek 
589*00b67f09SDavid van Moolenbroek static isc_result_t
verifynsec(dns_db_t * db,dns_dbversion_t * ver,dns_name_t * name,dns_dbnode_t * node,dns_name_t * nextname)590*00b67f09SDavid van Moolenbroek verifynsec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
591*00b67f09SDavid van Moolenbroek 	   dns_dbnode_t *node, dns_name_t *nextname)
592*00b67f09SDavid van Moolenbroek {
593*00b67f09SDavid van Moolenbroek 	unsigned char buffer[DNS_NSEC_BUFFERSIZE];
594*00b67f09SDavid van Moolenbroek 	char namebuf[DNS_NAME_FORMATSIZE];
595*00b67f09SDavid van Moolenbroek 	char nextbuf[DNS_NAME_FORMATSIZE];
596*00b67f09SDavid van Moolenbroek 	char found[DNS_NAME_FORMATSIZE];
597*00b67f09SDavid van Moolenbroek 	dns_rdataset_t rdataset;
598*00b67f09SDavid van Moolenbroek 	dns_rdata_t rdata = DNS_RDATA_INIT;
599*00b67f09SDavid van Moolenbroek 	dns_rdata_t tmprdata = DNS_RDATA_INIT;
600*00b67f09SDavid van Moolenbroek 	dns_rdata_nsec_t nsec;
601*00b67f09SDavid van Moolenbroek 	isc_result_t result;
602*00b67f09SDavid van Moolenbroek 
603*00b67f09SDavid van Moolenbroek 	dns_rdataset_init(&rdataset);
604*00b67f09SDavid van Moolenbroek 	result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec,
605*00b67f09SDavid van Moolenbroek 				     0, 0, &rdataset, NULL);
606*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS) {
607*00b67f09SDavid van Moolenbroek 		dns_name_format(name, namebuf, sizeof(namebuf));
608*00b67f09SDavid van Moolenbroek 		fprintf(stderr, "Missing NSEC record for %s\n", namebuf);
609*00b67f09SDavid van Moolenbroek 		goto failure;
610*00b67f09SDavid van Moolenbroek 	}
611*00b67f09SDavid van Moolenbroek 
612*00b67f09SDavid van Moolenbroek 	result = dns_rdataset_first(&rdataset);
613*00b67f09SDavid van Moolenbroek 	check_result(result, "dns_rdataset_first()");
614*00b67f09SDavid van Moolenbroek 
615*00b67f09SDavid van Moolenbroek 	dns_rdataset_current(&rdataset, &rdata);
616*00b67f09SDavid van Moolenbroek 	result = dns_rdata_tostruct(&rdata, &nsec, NULL);
617*00b67f09SDavid van Moolenbroek 	check_result(result, "dns_rdata_tostruct()");
618*00b67f09SDavid van Moolenbroek 	/* Check bit next name is consistent */
619*00b67f09SDavid van Moolenbroek 	if (!dns_name_equal(&nsec.next, nextname)) {
620*00b67f09SDavid van Moolenbroek 		dns_name_format(name, namebuf, sizeof(namebuf));
621*00b67f09SDavid van Moolenbroek 		dns_name_format(nextname, nextbuf, sizeof(nextbuf));
622*00b67f09SDavid van Moolenbroek 		dns_name_format(&nsec.next, found, sizeof(found));
623*00b67f09SDavid van Moolenbroek 		fprintf(stderr, "Bad NSEC record for %s, next name "
624*00b67f09SDavid van Moolenbroek 				"mismatch (expected:%s, found:%s)\n", namebuf,
625*00b67f09SDavid van Moolenbroek 				nextbuf, found);
626*00b67f09SDavid van Moolenbroek 		goto failure;
627*00b67f09SDavid van Moolenbroek 	}
628*00b67f09SDavid van Moolenbroek 	/* Check bit map is consistent */
629*00b67f09SDavid van Moolenbroek 	result = dns_nsec_buildrdata(db, ver, node, nextname, buffer,
630*00b67f09SDavid van Moolenbroek 				     &tmprdata);
631*00b67f09SDavid van Moolenbroek 	check_result(result, "dns_nsec_buildrdata()");
632*00b67f09SDavid van Moolenbroek 	if (dns_rdata_compare(&rdata, &tmprdata) != 0) {
633*00b67f09SDavid van Moolenbroek 		dns_name_format(name, namebuf, sizeof(namebuf));
634*00b67f09SDavid van Moolenbroek 		fprintf(stderr, "Bad NSEC record for %s, bit map "
635*00b67f09SDavid van Moolenbroek 				"mismatch\n", namebuf);
636*00b67f09SDavid van Moolenbroek 		goto failure;
637*00b67f09SDavid van Moolenbroek 	}
638*00b67f09SDavid van Moolenbroek 	result = dns_rdataset_next(&rdataset);
639*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_NOMORE) {
640*00b67f09SDavid van Moolenbroek 		dns_name_format(name, namebuf, sizeof(namebuf));
641*00b67f09SDavid van Moolenbroek 		fprintf(stderr, "Multipe NSEC records for %s\n", namebuf);
642*00b67f09SDavid van Moolenbroek 		goto failure;
643*00b67f09SDavid van Moolenbroek 
644*00b67f09SDavid van Moolenbroek 	}
645*00b67f09SDavid van Moolenbroek 	dns_rdataset_disassociate(&rdataset);
646*00b67f09SDavid van Moolenbroek 	return (ISC_R_SUCCESS);
647*00b67f09SDavid van Moolenbroek  failure:
648*00b67f09SDavid van Moolenbroek 	if (dns_rdataset_isassociated(&rdataset))
649*00b67f09SDavid van Moolenbroek 		dns_rdataset_disassociate(&rdataset);
650*00b67f09SDavid van Moolenbroek 	return (ISC_R_FAILURE);
651*00b67f09SDavid van Moolenbroek }
652*00b67f09SDavid van Moolenbroek 
653*00b67f09SDavid van Moolenbroek static void
check_no_rrsig(dns_db_t * db,dns_dbversion_t * ver,dns_rdataset_t * rdataset,dns_name_t * name,dns_dbnode_t * node)654*00b67f09SDavid van Moolenbroek check_no_rrsig(dns_db_t *db, dns_dbversion_t *ver, dns_rdataset_t *rdataset,
655*00b67f09SDavid van Moolenbroek 	       dns_name_t *name, dns_dbnode_t *node)
656*00b67f09SDavid van Moolenbroek {
657*00b67f09SDavid van Moolenbroek 	char namebuf[DNS_NAME_FORMATSIZE];
658*00b67f09SDavid van Moolenbroek 	char typebuf[80];
659*00b67f09SDavid van Moolenbroek 	dns_rdataset_t sigrdataset;
660*00b67f09SDavid van Moolenbroek 	dns_rdatasetiter_t *rdsiter = NULL;
661*00b67f09SDavid van Moolenbroek 	isc_result_t result;
662*00b67f09SDavid van Moolenbroek 
663*00b67f09SDavid van Moolenbroek 	dns_rdataset_init(&sigrdataset);
664*00b67f09SDavid van Moolenbroek 	result = dns_db_allrdatasets(db, node, ver, 0, &rdsiter);
665*00b67f09SDavid van Moolenbroek 	check_result(result, "dns_db_allrdatasets()");
666*00b67f09SDavid van Moolenbroek 	for (result = dns_rdatasetiter_first(rdsiter);
667*00b67f09SDavid van Moolenbroek 	     result == ISC_R_SUCCESS;
668*00b67f09SDavid van Moolenbroek 	     result = dns_rdatasetiter_next(rdsiter)) {
669*00b67f09SDavid van Moolenbroek 		dns_rdatasetiter_current(rdsiter, &sigrdataset);
670*00b67f09SDavid van Moolenbroek 		if (sigrdataset.type == dns_rdatatype_rrsig &&
671*00b67f09SDavid van Moolenbroek 		    sigrdataset.covers == rdataset->type)
672*00b67f09SDavid van Moolenbroek 			break;
673*00b67f09SDavid van Moolenbroek 		dns_rdataset_disassociate(&sigrdataset);
674*00b67f09SDavid van Moolenbroek 	}
675*00b67f09SDavid van Moolenbroek 	if (result == ISC_R_SUCCESS) {
676*00b67f09SDavid van Moolenbroek 		dns_name_format(name, namebuf, sizeof(namebuf));
677*00b67f09SDavid van Moolenbroek 		type_format(rdataset->type, typebuf, sizeof(typebuf));
678*00b67f09SDavid van Moolenbroek 		fprintf(stderr, "Warning: Found unexpected signatures for "
679*00b67f09SDavid van Moolenbroek 			"%s/%s\n", namebuf, typebuf);
680*00b67f09SDavid van Moolenbroek 	}
681*00b67f09SDavid van Moolenbroek 	if (dns_rdataset_isassociated(&sigrdataset))
682*00b67f09SDavid van Moolenbroek 		dns_rdataset_disassociate(&sigrdataset);
683*00b67f09SDavid van Moolenbroek 	dns_rdatasetiter_destroy(&rdsiter);
684*00b67f09SDavid van Moolenbroek }
685*00b67f09SDavid van Moolenbroek 
686*00b67f09SDavid van Moolenbroek static isc_boolean_t
chain_compare(void * arg1,void * arg2)687*00b67f09SDavid van Moolenbroek chain_compare(void *arg1, void *arg2) {
688*00b67f09SDavid van Moolenbroek 	struct nsec3_chain_fixed *e1 = arg1, *e2 = arg2;
689*00b67f09SDavid van Moolenbroek 	size_t len;
690*00b67f09SDavid van Moolenbroek 
691*00b67f09SDavid van Moolenbroek 	/*
692*00b67f09SDavid van Moolenbroek 	 * Do each element in turn to get a stable sort.
693*00b67f09SDavid van Moolenbroek 	 */
694*00b67f09SDavid van Moolenbroek 	if (e1->hash < e2->hash)
695*00b67f09SDavid van Moolenbroek 		return (ISC_TRUE);
696*00b67f09SDavid van Moolenbroek 	if (e1->hash > e2->hash)
697*00b67f09SDavid van Moolenbroek 		return (ISC_FALSE);
698*00b67f09SDavid van Moolenbroek 	if (e1->iterations < e2->iterations)
699*00b67f09SDavid van Moolenbroek 		return (ISC_TRUE);
700*00b67f09SDavid van Moolenbroek 	if (e1->iterations > e2->iterations)
701*00b67f09SDavid van Moolenbroek 		return (ISC_FALSE);
702*00b67f09SDavid van Moolenbroek 	if (e1->salt_length < e2->salt_length)
703*00b67f09SDavid van Moolenbroek 		return (ISC_TRUE);
704*00b67f09SDavid van Moolenbroek 	if (e1->salt_length > e2->salt_length)
705*00b67f09SDavid van Moolenbroek 		return (ISC_FALSE);
706*00b67f09SDavid van Moolenbroek 	if (e1->next_length < e2->next_length)
707*00b67f09SDavid van Moolenbroek 		return (ISC_TRUE);
708*00b67f09SDavid van Moolenbroek 	if (e1->next_length > e2->next_length)
709*00b67f09SDavid van Moolenbroek 		return (ISC_FALSE);
710*00b67f09SDavid van Moolenbroek 	len = e1->salt_length + 2 * e1->next_length;
711*00b67f09SDavid van Moolenbroek 	if (memcmp(e1 + 1, e2 + 1, len) < 0)
712*00b67f09SDavid van Moolenbroek 		return (ISC_TRUE);
713*00b67f09SDavid van Moolenbroek 	return (ISC_FALSE);
714*00b67f09SDavid van Moolenbroek }
715*00b67f09SDavid van Moolenbroek 
716*00b67f09SDavid van Moolenbroek static isc_boolean_t
chain_equal(struct nsec3_chain_fixed * e1,struct nsec3_chain_fixed * e2)717*00b67f09SDavid van Moolenbroek chain_equal(struct nsec3_chain_fixed *e1, struct nsec3_chain_fixed *e2) {
718*00b67f09SDavid van Moolenbroek 	size_t len;
719*00b67f09SDavid van Moolenbroek 
720*00b67f09SDavid van Moolenbroek 	if (e1->hash != e2->hash)
721*00b67f09SDavid van Moolenbroek 		return (ISC_FALSE);
722*00b67f09SDavid van Moolenbroek 	if (e1->iterations != e2->iterations)
723*00b67f09SDavid van Moolenbroek 		return (ISC_FALSE);
724*00b67f09SDavid van Moolenbroek 	if (e1->salt_length != e2->salt_length)
725*00b67f09SDavid van Moolenbroek 		return (ISC_FALSE);
726*00b67f09SDavid van Moolenbroek 	if (e1->next_length != e2->next_length)
727*00b67f09SDavid van Moolenbroek 		return (ISC_FALSE);
728*00b67f09SDavid van Moolenbroek 	len = e1->salt_length + 2 * e1->next_length;
729*00b67f09SDavid van Moolenbroek 	if (memcmp(e1 + 1, e2 + 1, len) != 0)
730*00b67f09SDavid van Moolenbroek 		return (ISC_FALSE);
731*00b67f09SDavid van Moolenbroek 	return (ISC_TRUE);
732*00b67f09SDavid van Moolenbroek }
733*00b67f09SDavid van Moolenbroek 
734*00b67f09SDavid van Moolenbroek static isc_result_t
record_nsec3(const unsigned char * rawhash,const dns_rdata_nsec3_t * nsec3,isc_mem_t * mctx,isc_heap_t * chains)735*00b67f09SDavid van Moolenbroek record_nsec3(const unsigned char *rawhash, const dns_rdata_nsec3_t *nsec3,
736*00b67f09SDavid van Moolenbroek 	     isc_mem_t *mctx, isc_heap_t *chains)
737*00b67f09SDavid van Moolenbroek {
738*00b67f09SDavid van Moolenbroek 	struct nsec3_chain_fixed *element;
739*00b67f09SDavid van Moolenbroek 	size_t len;
740*00b67f09SDavid van Moolenbroek 	unsigned char *cp;
741*00b67f09SDavid van Moolenbroek 	isc_result_t result;
742*00b67f09SDavid van Moolenbroek 
743*00b67f09SDavid van Moolenbroek 	len = sizeof(*element) + nsec3->next_length * 2 + nsec3->salt_length;
744*00b67f09SDavid van Moolenbroek 
745*00b67f09SDavid van Moolenbroek 	element = isc_mem_get(mctx, len);
746*00b67f09SDavid van Moolenbroek 	if (element == NULL)
747*00b67f09SDavid van Moolenbroek 		return (ISC_R_NOMEMORY);
748*00b67f09SDavid van Moolenbroek 	memset(element, 0, len);
749*00b67f09SDavid van Moolenbroek 	element->hash = nsec3->hash;
750*00b67f09SDavid van Moolenbroek 	element->salt_length = nsec3->salt_length;
751*00b67f09SDavid van Moolenbroek 	element->next_length = nsec3->next_length;
752*00b67f09SDavid van Moolenbroek 	element->iterations = nsec3->iterations;
753*00b67f09SDavid van Moolenbroek 	cp = (unsigned char *)(element + 1);
754*00b67f09SDavid van Moolenbroek 	memmove(cp, nsec3->salt, nsec3->salt_length);
755*00b67f09SDavid van Moolenbroek 	cp += nsec3->salt_length;
756*00b67f09SDavid van Moolenbroek 	memmove(cp, rawhash, nsec3->next_length);
757*00b67f09SDavid van Moolenbroek 	cp += nsec3->next_length;
758*00b67f09SDavid van Moolenbroek 	memmove(cp, nsec3->next, nsec3->next_length);
759*00b67f09SDavid van Moolenbroek 	result = isc_heap_insert(chains, element);
760*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS) {
761*00b67f09SDavid van Moolenbroek 		fprintf(stderr, "isc_heap_insert failed: %s\n",
762*00b67f09SDavid van Moolenbroek 			isc_result_totext(result));
763*00b67f09SDavid van Moolenbroek 		isc_mem_put(mctx, element, len);
764*00b67f09SDavid van Moolenbroek 	}
765*00b67f09SDavid van Moolenbroek 	return (result);
766*00b67f09SDavid van Moolenbroek }
767*00b67f09SDavid van Moolenbroek 
768*00b67f09SDavid van Moolenbroek static isc_result_t
match_nsec3(dns_name_t * name,isc_mem_t * mctx,dns_rdata_nsec3param_t * nsec3param,dns_rdataset_t * rdataset,unsigned char types[8192],unsigned int maxtype,unsigned char * rawhash,size_t rhsize)769*00b67f09SDavid van Moolenbroek match_nsec3(dns_name_t *name, isc_mem_t *mctx,
770*00b67f09SDavid van Moolenbroek 	    dns_rdata_nsec3param_t *nsec3param, dns_rdataset_t *rdataset,
771*00b67f09SDavid van Moolenbroek 	    unsigned char types[8192], unsigned int maxtype,
772*00b67f09SDavid van Moolenbroek 	    unsigned char *rawhash, size_t rhsize)
773*00b67f09SDavid van Moolenbroek {
774*00b67f09SDavid van Moolenbroek 	unsigned char cbm[8244];
775*00b67f09SDavid van Moolenbroek 	char namebuf[DNS_NAME_FORMATSIZE];
776*00b67f09SDavid van Moolenbroek 	dns_rdata_nsec3_t nsec3;
777*00b67f09SDavid van Moolenbroek 	isc_result_t result;
778*00b67f09SDavid van Moolenbroek 	unsigned int len;
779*00b67f09SDavid van Moolenbroek 
780*00b67f09SDavid van Moolenbroek 	/*
781*00b67f09SDavid van Moolenbroek 	 * Find matching NSEC3 record.
782*00b67f09SDavid van Moolenbroek 	 */
783*00b67f09SDavid van Moolenbroek 	for (result = dns_rdataset_first(rdataset);
784*00b67f09SDavid van Moolenbroek 	     result == ISC_R_SUCCESS;
785*00b67f09SDavid van Moolenbroek 	     result = dns_rdataset_next(rdataset)) {
786*00b67f09SDavid van Moolenbroek 		dns_rdata_t rdata = DNS_RDATA_INIT;
787*00b67f09SDavid van Moolenbroek 		dns_rdataset_current(rdataset, &rdata);
788*00b67f09SDavid van Moolenbroek 		result = dns_rdata_tostruct(&rdata, &nsec3, NULL);
789*00b67f09SDavid van Moolenbroek 		check_result(result, "dns_rdata_tostruct()");
790*00b67f09SDavid van Moolenbroek 		if (nsec3.hash == nsec3param->hash &&
791*00b67f09SDavid van Moolenbroek 		    nsec3.next_length == rhsize &&
792*00b67f09SDavid van Moolenbroek 		    nsec3.iterations == nsec3param->iterations &&
793*00b67f09SDavid van Moolenbroek 		    nsec3.salt_length == nsec3param->salt_length &&
794*00b67f09SDavid van Moolenbroek 		    memcmp(nsec3.salt, nsec3param->salt,
795*00b67f09SDavid van Moolenbroek 			   nsec3param->salt_length) == 0)
796*00b67f09SDavid van Moolenbroek 			break;
797*00b67f09SDavid van Moolenbroek 	}
798*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS) {
799*00b67f09SDavid van Moolenbroek 		dns_name_format(name, namebuf, sizeof(namebuf));
800*00b67f09SDavid van Moolenbroek 		fprintf(stderr, "Missing NSEC3 record for %s\n", namebuf);
801*00b67f09SDavid van Moolenbroek 		return (result);
802*00b67f09SDavid van Moolenbroek 	}
803*00b67f09SDavid van Moolenbroek 
804*00b67f09SDavid van Moolenbroek 	/*
805*00b67f09SDavid van Moolenbroek 	 * Check the type list.
806*00b67f09SDavid van Moolenbroek 	 */
807*00b67f09SDavid van Moolenbroek 	len = dns_nsec_compressbitmap(cbm, types, maxtype);
808*00b67f09SDavid van Moolenbroek 	if (nsec3.len != len || memcmp(cbm, nsec3.typebits, len) != 0) {
809*00b67f09SDavid van Moolenbroek 		dns_name_format(name, namebuf, sizeof(namebuf));
810*00b67f09SDavid van Moolenbroek 		fprintf(stderr, "Bad NSEC3 record for %s, bit map "
811*00b67f09SDavid van Moolenbroek 				"mismatch\n", namebuf);
812*00b67f09SDavid van Moolenbroek 		return (ISC_R_FAILURE);
813*00b67f09SDavid van Moolenbroek 	}
814*00b67f09SDavid van Moolenbroek 
815*00b67f09SDavid van Moolenbroek 	/*
816*00b67f09SDavid van Moolenbroek 	 * Record chain.
817*00b67f09SDavid van Moolenbroek 	 */
818*00b67f09SDavid van Moolenbroek 	result = record_nsec3(rawhash, &nsec3, mctx, expected_chains);
819*00b67f09SDavid van Moolenbroek 	check_result(result, "record_nsec3()");
820*00b67f09SDavid van Moolenbroek 
821*00b67f09SDavid van Moolenbroek 	/*
822*00b67f09SDavid van Moolenbroek 	 * Make sure there is only one NSEC3 record with this set of
823*00b67f09SDavid van Moolenbroek 	 * parameters.
824*00b67f09SDavid van Moolenbroek 	 */
825*00b67f09SDavid van Moolenbroek 	for (result = dns_rdataset_next(rdataset);
826*00b67f09SDavid van Moolenbroek 	     result == ISC_R_SUCCESS;
827*00b67f09SDavid van Moolenbroek 	     result = dns_rdataset_next(rdataset)) {
828*00b67f09SDavid van Moolenbroek 		dns_rdata_t rdata = DNS_RDATA_INIT;
829*00b67f09SDavid van Moolenbroek 		dns_rdataset_current(rdataset, &rdata);
830*00b67f09SDavid van Moolenbroek 		result = dns_rdata_tostruct(&rdata, &nsec3, NULL);
831*00b67f09SDavid van Moolenbroek 		check_result(result, "dns_rdata_tostruct()");
832*00b67f09SDavid van Moolenbroek 		if (nsec3.hash == nsec3param->hash &&
833*00b67f09SDavid van Moolenbroek 		    nsec3.iterations == nsec3param->iterations &&
834*00b67f09SDavid van Moolenbroek 		    nsec3.salt_length == nsec3param->salt_length &&
835*00b67f09SDavid van Moolenbroek 		    memcmp(nsec3.salt, nsec3param->salt,
836*00b67f09SDavid van Moolenbroek 			   nsec3.salt_length) == 0) {
837*00b67f09SDavid van Moolenbroek 			dns_name_format(name, namebuf, sizeof(namebuf));
838*00b67f09SDavid van Moolenbroek 			fprintf(stderr, "Multiple NSEC3 records with the "
839*00b67f09SDavid van Moolenbroek 				"same parameter set for %s", namebuf);
840*00b67f09SDavid van Moolenbroek 			result = DNS_R_DUPLICATE;
841*00b67f09SDavid van Moolenbroek 			break;
842*00b67f09SDavid van Moolenbroek 		}
843*00b67f09SDavid van Moolenbroek 	}
844*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_NOMORE)
845*00b67f09SDavid van Moolenbroek 		return (result);
846*00b67f09SDavid van Moolenbroek 
847*00b67f09SDavid van Moolenbroek 	result = ISC_R_SUCCESS;
848*00b67f09SDavid van Moolenbroek 	return (result);
849*00b67f09SDavid van Moolenbroek }
850*00b67f09SDavid van Moolenbroek 
851*00b67f09SDavid van Moolenbroek static isc_boolean_t
innsec3params(dns_rdata_nsec3_t * nsec3,dns_rdataset_t * nsec3paramset)852*00b67f09SDavid van Moolenbroek innsec3params(dns_rdata_nsec3_t *nsec3, dns_rdataset_t *nsec3paramset) {
853*00b67f09SDavid van Moolenbroek 	dns_rdata_nsec3param_t nsec3param;
854*00b67f09SDavid van Moolenbroek 	isc_result_t result;
855*00b67f09SDavid van Moolenbroek 
856*00b67f09SDavid van Moolenbroek 	for (result = dns_rdataset_first(nsec3paramset);
857*00b67f09SDavid van Moolenbroek 	     result == ISC_R_SUCCESS;
858*00b67f09SDavid van Moolenbroek 	     result = dns_rdataset_next(nsec3paramset)) {
859*00b67f09SDavid van Moolenbroek 		dns_rdata_t rdata = DNS_RDATA_INIT;
860*00b67f09SDavid van Moolenbroek 
861*00b67f09SDavid van Moolenbroek 		dns_rdataset_current(nsec3paramset, &rdata);
862*00b67f09SDavid van Moolenbroek 		result = dns_rdata_tostruct(&rdata, &nsec3param, NULL);
863*00b67f09SDavid van Moolenbroek 		check_result(result, "dns_rdata_tostruct()");
864*00b67f09SDavid van Moolenbroek 		if (nsec3param.flags == 0 &&
865*00b67f09SDavid van Moolenbroek 		    nsec3param.hash == nsec3->hash &&
866*00b67f09SDavid van Moolenbroek 		    nsec3param.iterations == nsec3->iterations &&
867*00b67f09SDavid van Moolenbroek 		    nsec3param.salt_length == nsec3->salt_length &&
868*00b67f09SDavid van Moolenbroek 		    memcmp(nsec3param.salt, nsec3->salt,
869*00b67f09SDavid van Moolenbroek 			   nsec3->salt_length) == 0)
870*00b67f09SDavid van Moolenbroek 			return (ISC_TRUE);
871*00b67f09SDavid van Moolenbroek 	}
872*00b67f09SDavid van Moolenbroek 	return (ISC_FALSE);
873*00b67f09SDavid van Moolenbroek }
874*00b67f09SDavid van Moolenbroek 
875*00b67f09SDavid van Moolenbroek static isc_result_t
record_found(dns_db_t * db,dns_dbversion_t * ver,isc_mem_t * mctx,dns_name_t * name,dns_dbnode_t * node,dns_rdataset_t * nsec3paramset)876*00b67f09SDavid van Moolenbroek record_found(dns_db_t *db, dns_dbversion_t *ver, isc_mem_t *mctx,
877*00b67f09SDavid van Moolenbroek 	     dns_name_t *name, dns_dbnode_t *node,
878*00b67f09SDavid van Moolenbroek 	     dns_rdataset_t *nsec3paramset)
879*00b67f09SDavid van Moolenbroek {
880*00b67f09SDavid van Moolenbroek 	unsigned char owner[NSEC3_MAX_HASH_LENGTH];
881*00b67f09SDavid van Moolenbroek 	dns_rdata_nsec3_t nsec3;
882*00b67f09SDavid van Moolenbroek 	dns_rdataset_t rdataset;
883*00b67f09SDavid van Moolenbroek 	dns_label_t hashlabel;
884*00b67f09SDavid van Moolenbroek 	isc_buffer_t b;
885*00b67f09SDavid van Moolenbroek 	isc_result_t result;
886*00b67f09SDavid van Moolenbroek 
887*00b67f09SDavid van Moolenbroek 	if (nsec3paramset == NULL || !dns_rdataset_isassociated(nsec3paramset))
888*00b67f09SDavid van Moolenbroek 		return (ISC_R_SUCCESS);
889*00b67f09SDavid van Moolenbroek 
890*00b67f09SDavid van Moolenbroek 	dns_rdataset_init(&rdataset);
891*00b67f09SDavid van Moolenbroek 	result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3,
892*00b67f09SDavid van Moolenbroek 				     0, 0, &rdataset, NULL);
893*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
894*00b67f09SDavid van Moolenbroek 		return (ISC_R_SUCCESS);
895*00b67f09SDavid van Moolenbroek 
896*00b67f09SDavid van Moolenbroek 	dns_name_getlabel(name, 0, &hashlabel);
897*00b67f09SDavid van Moolenbroek 	isc_region_consume(&hashlabel, 1);
898*00b67f09SDavid van Moolenbroek 	isc_buffer_init(&b, owner, sizeof(owner));
899*00b67f09SDavid van Moolenbroek 	result = isc_base32hex_decoderegion(&hashlabel, &b);
900*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
901*00b67f09SDavid van Moolenbroek 		goto cleanup;
902*00b67f09SDavid van Moolenbroek 
903*00b67f09SDavid van Moolenbroek 	for (result = dns_rdataset_first(&rdataset);
904*00b67f09SDavid van Moolenbroek 	     result == ISC_R_SUCCESS;
905*00b67f09SDavid van Moolenbroek 	     result = dns_rdataset_next(&rdataset)) {
906*00b67f09SDavid van Moolenbroek 		dns_rdata_t rdata = DNS_RDATA_INIT;
907*00b67f09SDavid van Moolenbroek 		dns_rdataset_current(&rdataset, &rdata);
908*00b67f09SDavid van Moolenbroek 		result = dns_rdata_tostruct(&rdata, &nsec3, NULL);
909*00b67f09SDavid van Moolenbroek 		check_result(result, "dns_rdata_tostruct()");
910*00b67f09SDavid van Moolenbroek 		if (nsec3.next_length != isc_buffer_usedlength(&b))
911*00b67f09SDavid van Moolenbroek 			continue;
912*00b67f09SDavid van Moolenbroek 		/*
913*00b67f09SDavid van Moolenbroek 		 * We only care about NSEC3 records that match a NSEC3PARAM
914*00b67f09SDavid van Moolenbroek 		 * record.
915*00b67f09SDavid van Moolenbroek 		 */
916*00b67f09SDavid van Moolenbroek 		if (!innsec3params(&nsec3, nsec3paramset))
917*00b67f09SDavid van Moolenbroek 			continue;
918*00b67f09SDavid van Moolenbroek 
919*00b67f09SDavid van Moolenbroek 		/*
920*00b67f09SDavid van Moolenbroek 		 * Record chain.
921*00b67f09SDavid van Moolenbroek 		 */
922*00b67f09SDavid van Moolenbroek 		result = record_nsec3(owner, &nsec3, mctx, found_chains);
923*00b67f09SDavid van Moolenbroek 		check_result(result, "record_nsec3()");
924*00b67f09SDavid van Moolenbroek 	}
925*00b67f09SDavid van Moolenbroek 
926*00b67f09SDavid van Moolenbroek  cleanup:
927*00b67f09SDavid van Moolenbroek 	dns_rdataset_disassociate(&rdataset);
928*00b67f09SDavid van Moolenbroek 	return (ISC_R_SUCCESS);
929*00b67f09SDavid van Moolenbroek }
930*00b67f09SDavid van Moolenbroek 
931*00b67f09SDavid van Moolenbroek static isc_boolean_t
isoptout(dns_db_t * db,dns_dbversion_t * ver,dns_name_t * origin,dns_rdata_t * nsec3rdata)932*00b67f09SDavid van Moolenbroek isoptout(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin,
933*00b67f09SDavid van Moolenbroek 	 dns_rdata_t *nsec3rdata)
934*00b67f09SDavid van Moolenbroek {
935*00b67f09SDavid van Moolenbroek 	dns_rdataset_t rdataset;
936*00b67f09SDavid van Moolenbroek 	dns_rdata_t rdata = DNS_RDATA_INIT;
937*00b67f09SDavid van Moolenbroek 	dns_rdata_nsec3_t nsec3;
938*00b67f09SDavid van Moolenbroek 	dns_rdata_nsec3param_t nsec3param;
939*00b67f09SDavid van Moolenbroek 	dns_fixedname_t fixed;
940*00b67f09SDavid van Moolenbroek 	dns_name_t *hashname;
941*00b67f09SDavid van Moolenbroek 	isc_result_t result;
942*00b67f09SDavid van Moolenbroek 	dns_dbnode_t *node = NULL;
943*00b67f09SDavid van Moolenbroek 	unsigned char rawhash[NSEC3_MAX_HASH_LENGTH];
944*00b67f09SDavid van Moolenbroek 	size_t rhsize = sizeof(rawhash);
945*00b67f09SDavid van Moolenbroek 	isc_boolean_t ret;
946*00b67f09SDavid van Moolenbroek 
947*00b67f09SDavid van Moolenbroek 	result = dns_rdata_tostruct(nsec3rdata, &nsec3param, NULL);
948*00b67f09SDavid van Moolenbroek 	check_result(result, "dns_rdata_tostruct()");
949*00b67f09SDavid van Moolenbroek 
950*00b67f09SDavid van Moolenbroek 	dns_fixedname_init(&fixed);
951*00b67f09SDavid van Moolenbroek 	result = dns_nsec3_hashname(&fixed, rawhash, &rhsize, origin, origin,
952*00b67f09SDavid van Moolenbroek 				    nsec3param.hash, nsec3param.iterations,
953*00b67f09SDavid van Moolenbroek 				    nsec3param.salt, nsec3param.salt_length);
954*00b67f09SDavid van Moolenbroek 	check_result(result, "dns_nsec3_hashname()");
955*00b67f09SDavid van Moolenbroek 
956*00b67f09SDavid van Moolenbroek 	dns_rdataset_init(&rdataset);
957*00b67f09SDavid van Moolenbroek 	hashname = dns_fixedname_name(&fixed);
958*00b67f09SDavid van Moolenbroek 	result = dns_db_findnsec3node(db, hashname, ISC_FALSE, &node);
959*00b67f09SDavid van Moolenbroek 	if (result == ISC_R_SUCCESS)
960*00b67f09SDavid van Moolenbroek 		result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3,
961*00b67f09SDavid van Moolenbroek 					     0, 0, &rdataset, NULL);
962*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
963*00b67f09SDavid van Moolenbroek 		return (ISC_FALSE);
964*00b67f09SDavid van Moolenbroek 
965*00b67f09SDavid van Moolenbroek 	result = dns_rdataset_first(&rdataset);
966*00b67f09SDavid van Moolenbroek 	check_result(result, "dns_rdataset_first()");
967*00b67f09SDavid van Moolenbroek 
968*00b67f09SDavid van Moolenbroek 	dns_rdataset_current(&rdataset, &rdata);
969*00b67f09SDavid van Moolenbroek 
970*00b67f09SDavid van Moolenbroek 	result = dns_rdata_tostruct(&rdata, &nsec3, NULL);
971*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
972*00b67f09SDavid van Moolenbroek 		ret = ISC_FALSE;
973*00b67f09SDavid van Moolenbroek 	else
974*00b67f09SDavid van Moolenbroek 		ret = ISC_TF((nsec3.flags & DNS_NSEC3FLAG_OPTOUT) != 0);
975*00b67f09SDavid van Moolenbroek 
976*00b67f09SDavid van Moolenbroek 	if (dns_rdataset_isassociated(&rdataset))
977*00b67f09SDavid van Moolenbroek 		dns_rdataset_disassociate(&rdataset);
978*00b67f09SDavid van Moolenbroek 	if (node != NULL)
979*00b67f09SDavid van Moolenbroek 		dns_db_detachnode(db, &node);
980*00b67f09SDavid van Moolenbroek 
981*00b67f09SDavid van Moolenbroek 	return (ret);
982*00b67f09SDavid van Moolenbroek }
983*00b67f09SDavid van Moolenbroek 
984*00b67f09SDavid van Moolenbroek static isc_result_t
verifynsec3(dns_db_t * db,dns_dbversion_t * ver,dns_name_t * origin,isc_mem_t * mctx,dns_name_t * name,dns_rdata_t * rdata,isc_boolean_t delegation,isc_boolean_t empty,unsigned char types[8192],unsigned int maxtype)985*00b67f09SDavid van Moolenbroek verifynsec3(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin,
986*00b67f09SDavid van Moolenbroek 	    isc_mem_t *mctx, dns_name_t *name, dns_rdata_t *rdata,
987*00b67f09SDavid van Moolenbroek 	    isc_boolean_t delegation, isc_boolean_t empty,
988*00b67f09SDavid van Moolenbroek 	    unsigned char types[8192], unsigned int maxtype)
989*00b67f09SDavid van Moolenbroek {
990*00b67f09SDavid van Moolenbroek 	char namebuf[DNS_NAME_FORMATSIZE];
991*00b67f09SDavid van Moolenbroek 	char hashbuf[DNS_NAME_FORMATSIZE];
992*00b67f09SDavid van Moolenbroek 	dns_rdataset_t rdataset;
993*00b67f09SDavid van Moolenbroek 	dns_rdata_nsec3param_t nsec3param;
994*00b67f09SDavid van Moolenbroek 	dns_fixedname_t fixed;
995*00b67f09SDavid van Moolenbroek 	dns_name_t *hashname;
996*00b67f09SDavid van Moolenbroek 	isc_result_t result;
997*00b67f09SDavid van Moolenbroek 	dns_dbnode_t *node = NULL;
998*00b67f09SDavid van Moolenbroek 	unsigned char rawhash[NSEC3_MAX_HASH_LENGTH];
999*00b67f09SDavid van Moolenbroek 	size_t rhsize = sizeof(rawhash);
1000*00b67f09SDavid van Moolenbroek 	isc_boolean_t optout;
1001*00b67f09SDavid van Moolenbroek 
1002*00b67f09SDavid van Moolenbroek 	result = dns_rdata_tostruct(rdata, &nsec3param, NULL);
1003*00b67f09SDavid van Moolenbroek 	check_result(result, "dns_rdata_tostruct()");
1004*00b67f09SDavid van Moolenbroek 
1005*00b67f09SDavid van Moolenbroek 	if (nsec3param.flags != 0)
1006*00b67f09SDavid van Moolenbroek 		return (ISC_R_SUCCESS);
1007*00b67f09SDavid van Moolenbroek 
1008*00b67f09SDavid van Moolenbroek 	if (!dns_nsec3_supportedhash(nsec3param.hash))
1009*00b67f09SDavid van Moolenbroek 		return (ISC_R_SUCCESS);
1010*00b67f09SDavid van Moolenbroek 
1011*00b67f09SDavid van Moolenbroek 	optout = isoptout(db, ver, origin, rdata);
1012*00b67f09SDavid van Moolenbroek 
1013*00b67f09SDavid van Moolenbroek 	dns_fixedname_init(&fixed);
1014*00b67f09SDavid van Moolenbroek 	result = dns_nsec3_hashname(&fixed, rawhash, &rhsize, name, origin,
1015*00b67f09SDavid van Moolenbroek 				    nsec3param.hash, nsec3param.iterations,
1016*00b67f09SDavid van Moolenbroek 				    nsec3param.salt, nsec3param.salt_length);
1017*00b67f09SDavid van Moolenbroek 	check_result(result, "dns_nsec3_hashname()");
1018*00b67f09SDavid van Moolenbroek 
1019*00b67f09SDavid van Moolenbroek 	/*
1020*00b67f09SDavid van Moolenbroek 	 * We don't use dns_db_find() here as it works with the choosen
1021*00b67f09SDavid van Moolenbroek 	 * nsec3 chain and we may also be called with uncommitted data
1022*00b67f09SDavid van Moolenbroek 	 * from dnssec-signzone so the secure status of the zone may not
1023*00b67f09SDavid van Moolenbroek 	 * be up to date.
1024*00b67f09SDavid van Moolenbroek 	 */
1025*00b67f09SDavid van Moolenbroek 	dns_rdataset_init(&rdataset);
1026*00b67f09SDavid van Moolenbroek 	hashname = dns_fixedname_name(&fixed);
1027*00b67f09SDavid van Moolenbroek 	result = dns_db_findnsec3node(db, hashname, ISC_FALSE, &node);
1028*00b67f09SDavid van Moolenbroek 	if (result == ISC_R_SUCCESS)
1029*00b67f09SDavid van Moolenbroek 		result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3,
1030*00b67f09SDavid van Moolenbroek 					     0, 0, &rdataset, NULL);
1031*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS &&
1032*00b67f09SDavid van Moolenbroek 	    (!delegation || (empty && !optout) ||
1033*00b67f09SDavid van Moolenbroek 	     (!empty && dns_nsec_isset(types, dns_rdatatype_ds))))
1034*00b67f09SDavid van Moolenbroek 	{
1035*00b67f09SDavid van Moolenbroek 		dns_name_format(name, namebuf, sizeof(namebuf));
1036*00b67f09SDavid van Moolenbroek 		dns_name_format(hashname, hashbuf, sizeof(hashbuf));
1037*00b67f09SDavid van Moolenbroek 		fprintf(stderr, "Missing NSEC3 record for %s (%s)\n",
1038*00b67f09SDavid van Moolenbroek 			namebuf, hashbuf);
1039*00b67f09SDavid van Moolenbroek 	} else if (result == ISC_R_NOTFOUND &&
1040*00b67f09SDavid van Moolenbroek 		   delegation && (!empty || optout))
1041*00b67f09SDavid van Moolenbroek 	{
1042*00b67f09SDavid van Moolenbroek 		result = ISC_R_SUCCESS;
1043*00b67f09SDavid van Moolenbroek 	} else if (result == ISC_R_SUCCESS) {
1044*00b67f09SDavid van Moolenbroek 		result = match_nsec3(name, mctx, &nsec3param, &rdataset,
1045*00b67f09SDavid van Moolenbroek 				     types, maxtype, rawhash, rhsize);
1046*00b67f09SDavid van Moolenbroek 	}
1047*00b67f09SDavid van Moolenbroek 
1048*00b67f09SDavid van Moolenbroek 	if (dns_rdataset_isassociated(&rdataset))
1049*00b67f09SDavid van Moolenbroek 		dns_rdataset_disassociate(&rdataset);
1050*00b67f09SDavid van Moolenbroek 	if (node != NULL)
1051*00b67f09SDavid van Moolenbroek 		dns_db_detachnode(db, &node);
1052*00b67f09SDavid van Moolenbroek 
1053*00b67f09SDavid van Moolenbroek 	return (result);
1054*00b67f09SDavid van Moolenbroek }
1055*00b67f09SDavid van Moolenbroek 
1056*00b67f09SDavid van Moolenbroek static isc_result_t
verifynsec3s(dns_db_t * db,dns_dbversion_t * ver,dns_name_t * origin,isc_mem_t * mctx,dns_name_t * name,dns_rdataset_t * nsec3paramset,isc_boolean_t delegation,isc_boolean_t empty,unsigned char types[8192],unsigned int maxtype)1057*00b67f09SDavid van Moolenbroek verifynsec3s(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin,
1058*00b67f09SDavid van Moolenbroek 	     isc_mem_t *mctx, dns_name_t *name, dns_rdataset_t *nsec3paramset,
1059*00b67f09SDavid van Moolenbroek 	     isc_boolean_t delegation, isc_boolean_t empty,
1060*00b67f09SDavid van Moolenbroek 	     unsigned char types[8192], unsigned int maxtype)
1061*00b67f09SDavid van Moolenbroek {
1062*00b67f09SDavid van Moolenbroek 	isc_result_t result;
1063*00b67f09SDavid van Moolenbroek 
1064*00b67f09SDavid van Moolenbroek 	for (result = dns_rdataset_first(nsec3paramset);
1065*00b67f09SDavid van Moolenbroek 	     result == ISC_R_SUCCESS;
1066*00b67f09SDavid van Moolenbroek 	     result = dns_rdataset_next(nsec3paramset)) {
1067*00b67f09SDavid van Moolenbroek 		dns_rdata_t rdata = DNS_RDATA_INIT;
1068*00b67f09SDavid van Moolenbroek 
1069*00b67f09SDavid van Moolenbroek 		dns_rdataset_current(nsec3paramset, &rdata);
1070*00b67f09SDavid van Moolenbroek 		result = verifynsec3(db, ver, origin, mctx, name, &rdata,
1071*00b67f09SDavid van Moolenbroek 				     delegation, empty, types, maxtype);
1072*00b67f09SDavid van Moolenbroek 		if (result != ISC_R_SUCCESS)
1073*00b67f09SDavid van Moolenbroek 			break;
1074*00b67f09SDavid van Moolenbroek 	}
1075*00b67f09SDavid van Moolenbroek 	if (result == ISC_R_NOMORE)
1076*00b67f09SDavid van Moolenbroek 		result = ISC_R_SUCCESS;
1077*00b67f09SDavid van Moolenbroek 	return (result);
1078*00b67f09SDavid van Moolenbroek }
1079*00b67f09SDavid van Moolenbroek 
1080*00b67f09SDavid van Moolenbroek static void
verifyset(dns_db_t * db,dns_dbversion_t * ver,dns_name_t * origin,isc_mem_t * mctx,dns_rdataset_t * rdataset,dns_name_t * name,dns_dbnode_t * node,dns_rdataset_t * keyrdataset,unsigned char * act_algorithms,unsigned char * bad_algorithms)1081*00b67f09SDavid van Moolenbroek verifyset(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin,
1082*00b67f09SDavid van Moolenbroek 	  isc_mem_t *mctx, dns_rdataset_t *rdataset, dns_name_t *name,
1083*00b67f09SDavid van Moolenbroek 	  dns_dbnode_t *node, dns_rdataset_t *keyrdataset,
1084*00b67f09SDavid van Moolenbroek 	  unsigned char *act_algorithms, unsigned char *bad_algorithms)
1085*00b67f09SDavid van Moolenbroek {
1086*00b67f09SDavid van Moolenbroek 	unsigned char set_algorithms[256];
1087*00b67f09SDavid van Moolenbroek 	char namebuf[DNS_NAME_FORMATSIZE];
1088*00b67f09SDavid van Moolenbroek 	char algbuf[80];
1089*00b67f09SDavid van Moolenbroek 	char typebuf[80];
1090*00b67f09SDavid van Moolenbroek 	dns_rdataset_t sigrdataset;
1091*00b67f09SDavid van Moolenbroek 	dns_rdatasetiter_t *rdsiter = NULL;
1092*00b67f09SDavid van Moolenbroek 	isc_result_t result;
1093*00b67f09SDavid van Moolenbroek 	int i;
1094*00b67f09SDavid van Moolenbroek 
1095*00b67f09SDavid van Moolenbroek 	dns_rdataset_init(&sigrdataset);
1096*00b67f09SDavid van Moolenbroek 	result = dns_db_allrdatasets(db, node, ver, 0, &rdsiter);
1097*00b67f09SDavid van Moolenbroek 	check_result(result, "dns_db_allrdatasets()");
1098*00b67f09SDavid van Moolenbroek 	for (result = dns_rdatasetiter_first(rdsiter);
1099*00b67f09SDavid van Moolenbroek 	     result == ISC_R_SUCCESS;
1100*00b67f09SDavid van Moolenbroek 	     result = dns_rdatasetiter_next(rdsiter)) {
1101*00b67f09SDavid van Moolenbroek 		dns_rdatasetiter_current(rdsiter, &sigrdataset);
1102*00b67f09SDavid van Moolenbroek 		if (sigrdataset.type == dns_rdatatype_rrsig &&
1103*00b67f09SDavid van Moolenbroek 		    sigrdataset.covers == rdataset->type)
1104*00b67f09SDavid van Moolenbroek 			break;
1105*00b67f09SDavid van Moolenbroek 		dns_rdataset_disassociate(&sigrdataset);
1106*00b67f09SDavid van Moolenbroek 	}
1107*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS) {
1108*00b67f09SDavid van Moolenbroek 		dns_name_format(name, namebuf, sizeof(namebuf));
1109*00b67f09SDavid van Moolenbroek 		type_format(rdataset->type, typebuf, sizeof(typebuf));
1110*00b67f09SDavid van Moolenbroek 		fprintf(stderr, "No signatures for %s/%s\n", namebuf, typebuf);
1111*00b67f09SDavid van Moolenbroek 		for (i = 0; i < 256; i++)
1112*00b67f09SDavid van Moolenbroek 			if (act_algorithms[i] != 0)
1113*00b67f09SDavid van Moolenbroek 				bad_algorithms[i] = 1;
1114*00b67f09SDavid van Moolenbroek 		dns_rdatasetiter_destroy(&rdsiter);
1115*00b67f09SDavid van Moolenbroek 		return;
1116*00b67f09SDavid van Moolenbroek 	}
1117*00b67f09SDavid van Moolenbroek 
1118*00b67f09SDavid van Moolenbroek 	memset(set_algorithms, 0, sizeof(set_algorithms));
1119*00b67f09SDavid van Moolenbroek 	for (result = dns_rdataset_first(&sigrdataset);
1120*00b67f09SDavid van Moolenbroek 	     result == ISC_R_SUCCESS;
1121*00b67f09SDavid van Moolenbroek 	     result = dns_rdataset_next(&sigrdataset)) {
1122*00b67f09SDavid van Moolenbroek 		dns_rdata_t rdata = DNS_RDATA_INIT;
1123*00b67f09SDavid van Moolenbroek 		dns_rdata_rrsig_t sig;
1124*00b67f09SDavid van Moolenbroek 
1125*00b67f09SDavid van Moolenbroek 		dns_rdataset_current(&sigrdataset, &rdata);
1126*00b67f09SDavid van Moolenbroek 		result = dns_rdata_tostruct(&rdata, &sig, NULL);
1127*00b67f09SDavid van Moolenbroek 		check_result(result, "dns_rdata_tostruct()");
1128*00b67f09SDavid van Moolenbroek 		if (rdataset->ttl != sig.originalttl) {
1129*00b67f09SDavid van Moolenbroek 			dns_name_format(name, namebuf, sizeof(namebuf));
1130*00b67f09SDavid van Moolenbroek 			type_format(rdataset->type, typebuf, sizeof(typebuf));
1131*00b67f09SDavid van Moolenbroek 			fprintf(stderr, "TTL mismatch for %s %s keytag %u\n",
1132*00b67f09SDavid van Moolenbroek 				namebuf, typebuf, sig.keyid);
1133*00b67f09SDavid van Moolenbroek 			continue;
1134*00b67f09SDavid van Moolenbroek 		}
1135*00b67f09SDavid van Moolenbroek 		if ((set_algorithms[sig.algorithm] != 0) ||
1136*00b67f09SDavid van Moolenbroek 		    (act_algorithms[sig.algorithm] == 0))
1137*00b67f09SDavid van Moolenbroek 			continue;
1138*00b67f09SDavid van Moolenbroek 		if (goodsig(origin, &rdata, name, keyrdataset, rdataset, mctx))
1139*00b67f09SDavid van Moolenbroek 			set_algorithms[sig.algorithm] = 1;
1140*00b67f09SDavid van Moolenbroek 	}
1141*00b67f09SDavid van Moolenbroek 	dns_rdatasetiter_destroy(&rdsiter);
1142*00b67f09SDavid van Moolenbroek 	if (memcmp(set_algorithms, act_algorithms, sizeof(set_algorithms))) {
1143*00b67f09SDavid van Moolenbroek 		dns_name_format(name, namebuf, sizeof(namebuf));
1144*00b67f09SDavid van Moolenbroek 		type_format(rdataset->type, typebuf, sizeof(typebuf));
1145*00b67f09SDavid van Moolenbroek 		for (i = 0; i < 256; i++)
1146*00b67f09SDavid van Moolenbroek 			if ((act_algorithms[i] != 0) &&
1147*00b67f09SDavid van Moolenbroek 			    (set_algorithms[i] == 0)) {
1148*00b67f09SDavid van Moolenbroek 				dns_secalg_format(i, algbuf, sizeof(algbuf));
1149*00b67f09SDavid van Moolenbroek 				fprintf(stderr, "No correct %s signature for "
1150*00b67f09SDavid van Moolenbroek 					"%s %s\n", algbuf, namebuf, typebuf);
1151*00b67f09SDavid van Moolenbroek 				bad_algorithms[i] = 1;
1152*00b67f09SDavid van Moolenbroek 			}
1153*00b67f09SDavid van Moolenbroek 	}
1154*00b67f09SDavid van Moolenbroek 	dns_rdataset_disassociate(&sigrdataset);
1155*00b67f09SDavid van Moolenbroek }
1156*00b67f09SDavid van Moolenbroek 
1157*00b67f09SDavid van Moolenbroek static isc_result_t
verifynode(dns_db_t * db,dns_dbversion_t * ver,dns_name_t * origin,isc_mem_t * mctx,dns_name_t * name,dns_dbnode_t * node,isc_boolean_t delegation,dns_rdataset_t * keyrdataset,unsigned char * act_algorithms,unsigned char * bad_algorithms,dns_rdataset_t * nsecset,dns_rdataset_t * nsec3paramset,dns_name_t * nextname)1158*00b67f09SDavid van Moolenbroek verifynode(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin,
1159*00b67f09SDavid van Moolenbroek 	   isc_mem_t *mctx, dns_name_t *name, dns_dbnode_t *node,
1160*00b67f09SDavid van Moolenbroek 	   isc_boolean_t delegation, dns_rdataset_t *keyrdataset,
1161*00b67f09SDavid van Moolenbroek 	   unsigned char *act_algorithms, unsigned char *bad_algorithms,
1162*00b67f09SDavid van Moolenbroek 	   dns_rdataset_t *nsecset, dns_rdataset_t *nsec3paramset,
1163*00b67f09SDavid van Moolenbroek 	   dns_name_t *nextname)
1164*00b67f09SDavid van Moolenbroek {
1165*00b67f09SDavid van Moolenbroek 	unsigned char types[8192];
1166*00b67f09SDavid van Moolenbroek 	unsigned int maxtype = 0;
1167*00b67f09SDavid van Moolenbroek 	dns_rdataset_t rdataset; dns_rdatasetiter_t *rdsiter = NULL;
1168*00b67f09SDavid van Moolenbroek 	isc_result_t result, tresult;
1169*00b67f09SDavid van Moolenbroek 
1170*00b67f09SDavid van Moolenbroek 	memset(types, 0, sizeof(types));
1171*00b67f09SDavid van Moolenbroek 	result = dns_db_allrdatasets(db, node, ver, 0, &rdsiter);
1172*00b67f09SDavid van Moolenbroek 	check_result(result, "dns_db_allrdatasets()");
1173*00b67f09SDavid van Moolenbroek 	result = dns_rdatasetiter_first(rdsiter);
1174*00b67f09SDavid van Moolenbroek 	dns_rdataset_init(&rdataset);
1175*00b67f09SDavid van Moolenbroek 	while (result == ISC_R_SUCCESS) {
1176*00b67f09SDavid van Moolenbroek 		dns_rdatasetiter_current(rdsiter, &rdataset);
1177*00b67f09SDavid van Moolenbroek 		/*
1178*00b67f09SDavid van Moolenbroek 		 * If we are not at a delegation then everything should be
1179*00b67f09SDavid van Moolenbroek 		 * signed.  If we are at a delegation then only the DS set
1180*00b67f09SDavid van Moolenbroek 		 * is signed.  The NS set is not signed at a delegation but
1181*00b67f09SDavid van Moolenbroek 		 * its existance is recorded in the bit map.  Anything else
1182*00b67f09SDavid van Moolenbroek 		 * other than NSEC and DS is not signed at a delegation.
1183*00b67f09SDavid van Moolenbroek 		 */
1184*00b67f09SDavid van Moolenbroek 		if (rdataset.type != dns_rdatatype_rrsig &&
1185*00b67f09SDavid van Moolenbroek 		    rdataset.type != dns_rdatatype_dnskey &&
1186*00b67f09SDavid van Moolenbroek 		    (!delegation || rdataset.type == dns_rdatatype_ds ||
1187*00b67f09SDavid van Moolenbroek 		     rdataset.type == dns_rdatatype_nsec)) {
1188*00b67f09SDavid van Moolenbroek 			verifyset(db, ver, origin, mctx, &rdataset,
1189*00b67f09SDavid van Moolenbroek 				  name, node, keyrdataset,
1190*00b67f09SDavid van Moolenbroek 				  act_algorithms, bad_algorithms);
1191*00b67f09SDavid van Moolenbroek 			dns_nsec_setbit(types, rdataset.type, 1);
1192*00b67f09SDavid van Moolenbroek 			if (rdataset.type > maxtype)
1193*00b67f09SDavid van Moolenbroek 				maxtype = rdataset.type;
1194*00b67f09SDavid van Moolenbroek 		} else if (rdataset.type != dns_rdatatype_rrsig &&
1195*00b67f09SDavid van Moolenbroek 			   rdataset.type != dns_rdatatype_dnskey) {
1196*00b67f09SDavid van Moolenbroek 			if (rdataset.type == dns_rdatatype_ns)
1197*00b67f09SDavid van Moolenbroek 				dns_nsec_setbit(types, rdataset.type, 1);
1198*00b67f09SDavid van Moolenbroek 			check_no_rrsig(db, ver, &rdataset, name, node);
1199*00b67f09SDavid van Moolenbroek 		} else
1200*00b67f09SDavid van Moolenbroek 			dns_nsec_setbit(types, rdataset.type, 1);
1201*00b67f09SDavid van Moolenbroek 		dns_rdataset_disassociate(&rdataset);
1202*00b67f09SDavid van Moolenbroek 		result = dns_rdatasetiter_next(rdsiter);
1203*00b67f09SDavid van Moolenbroek 	}
1204*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_NOMORE)
1205*00b67f09SDavid van Moolenbroek 		fatal("rdataset iteration failed: %s",
1206*00b67f09SDavid van Moolenbroek 		      isc_result_totext(result));
1207*00b67f09SDavid van Moolenbroek 	dns_rdatasetiter_destroy(&rdsiter);
1208*00b67f09SDavid van Moolenbroek 
1209*00b67f09SDavid van Moolenbroek 	result = ISC_R_SUCCESS;
1210*00b67f09SDavid van Moolenbroek 
1211*00b67f09SDavid van Moolenbroek 	if (nsecset != NULL && dns_rdataset_isassociated(nsecset))
1212*00b67f09SDavid van Moolenbroek 		result = verifynsec(db, ver, name, node, nextname);
1213*00b67f09SDavid van Moolenbroek 
1214*00b67f09SDavid van Moolenbroek 	if (nsec3paramset != NULL && dns_rdataset_isassociated(nsec3paramset)) {
1215*00b67f09SDavid van Moolenbroek 		tresult = verifynsec3s(db, ver, origin, mctx, name,
1216*00b67f09SDavid van Moolenbroek 				       nsec3paramset, delegation, ISC_FALSE,
1217*00b67f09SDavid van Moolenbroek 				       types, maxtype);
1218*00b67f09SDavid van Moolenbroek 		if (result == ISC_R_SUCCESS && tresult != ISC_R_SUCCESS)
1219*00b67f09SDavid van Moolenbroek 			result = tresult;
1220*00b67f09SDavid van Moolenbroek 	}
1221*00b67f09SDavid van Moolenbroek 	return (result);
1222*00b67f09SDavid van Moolenbroek }
1223*00b67f09SDavid van Moolenbroek 
1224*00b67f09SDavid van Moolenbroek static isc_boolean_t
is_empty(dns_db_t * db,dns_dbversion_t * ver,dns_dbnode_t * node)1225*00b67f09SDavid van Moolenbroek is_empty(dns_db_t *db, dns_dbversion_t *ver, dns_dbnode_t *node) {
1226*00b67f09SDavid van Moolenbroek 	dns_rdatasetiter_t *rdsiter = NULL;
1227*00b67f09SDavid van Moolenbroek 	isc_result_t result;
1228*00b67f09SDavid van Moolenbroek 
1229*00b67f09SDavid van Moolenbroek 	result = dns_db_allrdatasets(db, node, ver, 0, &rdsiter);
1230*00b67f09SDavid van Moolenbroek 	check_result(result, "dns_db_allrdatasets()");
1231*00b67f09SDavid van Moolenbroek 	result = dns_rdatasetiter_first(rdsiter);
1232*00b67f09SDavid van Moolenbroek 	dns_rdatasetiter_destroy(&rdsiter);
1233*00b67f09SDavid van Moolenbroek 	if (result == ISC_R_NOMORE)
1234*00b67f09SDavid van Moolenbroek 		return (ISC_TRUE);
1235*00b67f09SDavid van Moolenbroek 	return (ISC_FALSE);
1236*00b67f09SDavid van Moolenbroek }
1237*00b67f09SDavid van Moolenbroek 
1238*00b67f09SDavid van Moolenbroek static void
check_no_nsec(dns_name_t * name,dns_dbnode_t * node,dns_db_t * db,dns_dbversion_t * ver)1239*00b67f09SDavid van Moolenbroek check_no_nsec(dns_name_t *name, dns_dbnode_t *node, dns_db_t *db,
1240*00b67f09SDavid van Moolenbroek 	      dns_dbversion_t *ver)
1241*00b67f09SDavid van Moolenbroek {
1242*00b67f09SDavid van Moolenbroek 	dns_rdataset_t rdataset;
1243*00b67f09SDavid van Moolenbroek 	isc_result_t result;
1244*00b67f09SDavid van Moolenbroek 
1245*00b67f09SDavid van Moolenbroek 	dns_rdataset_init(&rdataset);
1246*00b67f09SDavid van Moolenbroek 	result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec,
1247*00b67f09SDavid van Moolenbroek 				     0, 0, &rdataset, NULL);
1248*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_NOTFOUND) {
1249*00b67f09SDavid van Moolenbroek 		char namebuf[DNS_NAME_FORMATSIZE];
1250*00b67f09SDavid van Moolenbroek 		dns_name_format(name, namebuf, sizeof(namebuf));
1251*00b67f09SDavid van Moolenbroek 		fatal("unexpected NSEC RRset at %s\n", namebuf);
1252*00b67f09SDavid van Moolenbroek 	}
1253*00b67f09SDavid van Moolenbroek 
1254*00b67f09SDavid van Moolenbroek 	if (dns_rdataset_isassociated(&rdataset))
1255*00b67f09SDavid van Moolenbroek 		dns_rdataset_disassociate(&rdataset);
1256*00b67f09SDavid van Moolenbroek }
1257*00b67f09SDavid van Moolenbroek 
1258*00b67f09SDavid van Moolenbroek static isc_boolean_t
newchain(const struct nsec3_chain_fixed * first,const struct nsec3_chain_fixed * e)1259*00b67f09SDavid van Moolenbroek newchain(const struct nsec3_chain_fixed *first,
1260*00b67f09SDavid van Moolenbroek 	 const struct nsec3_chain_fixed *e)
1261*00b67f09SDavid van Moolenbroek {
1262*00b67f09SDavid van Moolenbroek 	if (first->hash != e->hash ||
1263*00b67f09SDavid van Moolenbroek 	    first->iterations != e->iterations ||
1264*00b67f09SDavid van Moolenbroek 	    first->salt_length != e->salt_length ||
1265*00b67f09SDavid van Moolenbroek 	    first->next_length != e->next_length ||
1266*00b67f09SDavid van Moolenbroek 	    memcmp(first + 1, e + 1, first->salt_length) != 0)
1267*00b67f09SDavid van Moolenbroek 		return (ISC_TRUE);
1268*00b67f09SDavid van Moolenbroek 	return (ISC_FALSE);
1269*00b67f09SDavid van Moolenbroek }
1270*00b67f09SDavid van Moolenbroek 
1271*00b67f09SDavid van Moolenbroek static void
free_element(isc_mem_t * mctx,struct nsec3_chain_fixed * e)1272*00b67f09SDavid van Moolenbroek free_element(isc_mem_t *mctx, struct nsec3_chain_fixed *e) {
1273*00b67f09SDavid van Moolenbroek 	size_t len;
1274*00b67f09SDavid van Moolenbroek 
1275*00b67f09SDavid van Moolenbroek 	len = sizeof(*e) + e->salt_length + 2 * e->next_length;
1276*00b67f09SDavid van Moolenbroek 	isc_mem_put(mctx, e, len);
1277*00b67f09SDavid van Moolenbroek }
1278*00b67f09SDavid van Moolenbroek 
1279*00b67f09SDavid van Moolenbroek static isc_boolean_t
checknext(const struct nsec3_chain_fixed * first,const struct nsec3_chain_fixed * e)1280*00b67f09SDavid van Moolenbroek checknext(const struct nsec3_chain_fixed *first,
1281*00b67f09SDavid van Moolenbroek 	  const struct nsec3_chain_fixed *e)
1282*00b67f09SDavid van Moolenbroek {
1283*00b67f09SDavid van Moolenbroek 	char buf[512];
1284*00b67f09SDavid van Moolenbroek 	const unsigned char *d1 = (const unsigned char *)(first + 1);
1285*00b67f09SDavid van Moolenbroek 	const unsigned char *d2 = (const unsigned char *)(e + 1);
1286*00b67f09SDavid van Moolenbroek 	isc_buffer_t b;
1287*00b67f09SDavid van Moolenbroek 	isc_region_t sr;
1288*00b67f09SDavid van Moolenbroek 
1289*00b67f09SDavid van Moolenbroek 	d1 += first->salt_length + first->next_length;
1290*00b67f09SDavid van Moolenbroek 	d2 += e->salt_length;
1291*00b67f09SDavid van Moolenbroek 
1292*00b67f09SDavid van Moolenbroek 	if (memcmp(d1, d2, first->next_length) == 0)
1293*00b67f09SDavid van Moolenbroek 		return (ISC_TRUE);
1294*00b67f09SDavid van Moolenbroek 
1295*00b67f09SDavid van Moolenbroek 	DE_CONST(d1 - first->next_length, sr.base);
1296*00b67f09SDavid van Moolenbroek 	sr.length = first->next_length;
1297*00b67f09SDavid van Moolenbroek 	isc_buffer_init(&b, buf, sizeof(buf));
1298*00b67f09SDavid van Moolenbroek 	isc_base32hex_totext(&sr, 1, "", &b);
1299*00b67f09SDavid van Moolenbroek 	fprintf(stderr, "Break in NSEC3 chain at: %.*s\n",
1300*00b67f09SDavid van Moolenbroek 		(int) isc_buffer_usedlength(&b), buf);
1301*00b67f09SDavid van Moolenbroek 
1302*00b67f09SDavid van Moolenbroek 	DE_CONST(d1, sr.base);
1303*00b67f09SDavid van Moolenbroek 	sr.length = first->next_length;
1304*00b67f09SDavid van Moolenbroek 	isc_buffer_init(&b, buf, sizeof(buf));
1305*00b67f09SDavid van Moolenbroek 	isc_base32hex_totext(&sr, 1, "", &b);
1306*00b67f09SDavid van Moolenbroek 	fprintf(stderr, "Expected: %.*s\n", (int) isc_buffer_usedlength(&b),
1307*00b67f09SDavid van Moolenbroek 		buf);
1308*00b67f09SDavid van Moolenbroek 
1309*00b67f09SDavid van Moolenbroek 	DE_CONST(d2, sr.base);
1310*00b67f09SDavid van Moolenbroek 	sr.length = first->next_length;
1311*00b67f09SDavid van Moolenbroek 	isc_buffer_init(&b, buf, sizeof(buf));
1312*00b67f09SDavid van Moolenbroek 	isc_base32hex_totext(&sr, 1, "", &b);
1313*00b67f09SDavid van Moolenbroek 	fprintf(stderr, "Found: %.*s\n", (int) isc_buffer_usedlength(&b), buf);
1314*00b67f09SDavid van Moolenbroek 
1315*00b67f09SDavid van Moolenbroek 	return (ISC_FALSE);
1316*00b67f09SDavid van Moolenbroek }
1317*00b67f09SDavid van Moolenbroek 
1318*00b67f09SDavid van Moolenbroek #define EXPECTEDANDFOUND "Expected and found NSEC3 chains not equal\n"
1319*00b67f09SDavid van Moolenbroek 
1320*00b67f09SDavid van Moolenbroek static isc_result_t
verify_nsec3_chains(isc_mem_t * mctx)1321*00b67f09SDavid van Moolenbroek verify_nsec3_chains(isc_mem_t *mctx) {
1322*00b67f09SDavid van Moolenbroek 	isc_result_t result = ISC_R_SUCCESS;
1323*00b67f09SDavid van Moolenbroek 	struct nsec3_chain_fixed *e, *f = NULL;
1324*00b67f09SDavid van Moolenbroek 	struct nsec3_chain_fixed *first = NULL, *prev = NULL;
1325*00b67f09SDavid van Moolenbroek 
1326*00b67f09SDavid van Moolenbroek 	while ((e = isc_heap_element(expected_chains, 1)) != NULL) {
1327*00b67f09SDavid van Moolenbroek 		isc_heap_delete(expected_chains, 1);
1328*00b67f09SDavid van Moolenbroek 		if (f == NULL)
1329*00b67f09SDavid van Moolenbroek 			f = isc_heap_element(found_chains, 1);
1330*00b67f09SDavid van Moolenbroek 		if (f != NULL) {
1331*00b67f09SDavid van Moolenbroek 			isc_heap_delete(found_chains, 1);
1332*00b67f09SDavid van Moolenbroek 
1333*00b67f09SDavid van Moolenbroek 			/*
1334*00b67f09SDavid van Moolenbroek 			 * Check that they match.
1335*00b67f09SDavid van Moolenbroek 			 */
1336*00b67f09SDavid van Moolenbroek 			if (chain_equal(e, f)) {
1337*00b67f09SDavid van Moolenbroek 				free_element(mctx, f);
1338*00b67f09SDavid van Moolenbroek 				f = NULL;
1339*00b67f09SDavid van Moolenbroek 			} else {
1340*00b67f09SDavid van Moolenbroek 				if (result == ISC_R_SUCCESS)
1341*00b67f09SDavid van Moolenbroek 					fprintf(stderr, EXPECTEDANDFOUND);
1342*00b67f09SDavid van Moolenbroek 				result = ISC_R_FAILURE;
1343*00b67f09SDavid van Moolenbroek 				/*
1344*00b67f09SDavid van Moolenbroek 				 * Attempt to resync found_chain.
1345*00b67f09SDavid van Moolenbroek 				 */
1346*00b67f09SDavid van Moolenbroek 				while (f != NULL && !chain_compare(e, f)) {
1347*00b67f09SDavid van Moolenbroek 					free_element(mctx, f);
1348*00b67f09SDavid van Moolenbroek 					f = isc_heap_element(found_chains, 1);
1349*00b67f09SDavid van Moolenbroek 					if (f != NULL)
1350*00b67f09SDavid van Moolenbroek 						isc_heap_delete(found_chains, 1);
1351*00b67f09SDavid van Moolenbroek 					if (f != NULL && chain_equal(e, f)) {
1352*00b67f09SDavid van Moolenbroek 						free_element(mctx, f);
1353*00b67f09SDavid van Moolenbroek 						f = NULL;
1354*00b67f09SDavid van Moolenbroek 						break;
1355*00b67f09SDavid van Moolenbroek 					}
1356*00b67f09SDavid van Moolenbroek 				}
1357*00b67f09SDavid van Moolenbroek 			}
1358*00b67f09SDavid van Moolenbroek 		} else if (result == ISC_R_SUCCESS) {
1359*00b67f09SDavid van Moolenbroek 			fprintf(stderr, EXPECTEDANDFOUND);
1360*00b67f09SDavid van Moolenbroek 			result = ISC_R_FAILURE;
1361*00b67f09SDavid van Moolenbroek 		}
1362*00b67f09SDavid van Moolenbroek 		if (first == NULL || newchain(first, e)) {
1363*00b67f09SDavid van Moolenbroek 			if (prev != NULL) {
1364*00b67f09SDavid van Moolenbroek 				if (!checknext(prev, first))
1365*00b67f09SDavid van Moolenbroek 					result = ISC_R_FAILURE;
1366*00b67f09SDavid van Moolenbroek 				if (prev != first)
1367*00b67f09SDavid van Moolenbroek 					free_element(mctx, prev);
1368*00b67f09SDavid van Moolenbroek 			}
1369*00b67f09SDavid van Moolenbroek 			if (first != NULL)
1370*00b67f09SDavid van Moolenbroek 				free_element(mctx, first);
1371*00b67f09SDavid van Moolenbroek 			prev = first = e;
1372*00b67f09SDavid van Moolenbroek 			continue;
1373*00b67f09SDavid van Moolenbroek 		}
1374*00b67f09SDavid van Moolenbroek 		if (!checknext(prev, e))
1375*00b67f09SDavid van Moolenbroek 			result = ISC_R_FAILURE;
1376*00b67f09SDavid van Moolenbroek 		if (prev != first)
1377*00b67f09SDavid van Moolenbroek 			free_element(mctx, prev);
1378*00b67f09SDavid van Moolenbroek 		prev = e;
1379*00b67f09SDavid van Moolenbroek 	}
1380*00b67f09SDavid van Moolenbroek 	if (prev != NULL) {
1381*00b67f09SDavid van Moolenbroek 		if (!checknext(prev, first))
1382*00b67f09SDavid van Moolenbroek 			result = ISC_R_FAILURE;
1383*00b67f09SDavid van Moolenbroek 		if (prev != first)
1384*00b67f09SDavid van Moolenbroek 			free_element(mctx, prev);
1385*00b67f09SDavid van Moolenbroek 	}
1386*00b67f09SDavid van Moolenbroek 	if (first != NULL)
1387*00b67f09SDavid van Moolenbroek 		free_element(mctx, first);
1388*00b67f09SDavid van Moolenbroek 	do {
1389*00b67f09SDavid van Moolenbroek 		if (f != NULL) {
1390*00b67f09SDavid van Moolenbroek 			if (result == ISC_R_SUCCESS) {
1391*00b67f09SDavid van Moolenbroek 				fprintf(stderr, EXPECTEDANDFOUND);
1392*00b67f09SDavid van Moolenbroek 				result = ISC_R_FAILURE;
1393*00b67f09SDavid van Moolenbroek 			}
1394*00b67f09SDavid van Moolenbroek 			free_element(mctx, f);
1395*00b67f09SDavid van Moolenbroek 		}
1396*00b67f09SDavid van Moolenbroek 		f = isc_heap_element(found_chains, 1);
1397*00b67f09SDavid van Moolenbroek 		if (f != NULL)
1398*00b67f09SDavid van Moolenbroek 			isc_heap_delete(found_chains, 1);
1399*00b67f09SDavid van Moolenbroek 	} while (f != NULL);
1400*00b67f09SDavid van Moolenbroek 
1401*00b67f09SDavid van Moolenbroek 	return (result);
1402*00b67f09SDavid van Moolenbroek }
1403*00b67f09SDavid van Moolenbroek 
1404*00b67f09SDavid van Moolenbroek static isc_result_t
verifyemptynodes(dns_db_t * db,dns_dbversion_t * ver,dns_name_t * origin,isc_mem_t * mctx,dns_name_t * name,dns_name_t * prevname,isc_boolean_t isdelegation,dns_rdataset_t * nsec3paramset)1405*00b67f09SDavid van Moolenbroek verifyemptynodes(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin,
1406*00b67f09SDavid van Moolenbroek 		 isc_mem_t *mctx, dns_name_t *name, dns_name_t *prevname,
1407*00b67f09SDavid van Moolenbroek 		 isc_boolean_t isdelegation, dns_rdataset_t *nsec3paramset)
1408*00b67f09SDavid van Moolenbroek {
1409*00b67f09SDavid van Moolenbroek 	dns_namereln_t reln;
1410*00b67f09SDavid van Moolenbroek 	int order;
1411*00b67f09SDavid van Moolenbroek 	unsigned int labels, nlabels, i;
1412*00b67f09SDavid van Moolenbroek 	dns_name_t suffix;
1413*00b67f09SDavid van Moolenbroek 	isc_result_t result = ISC_R_SUCCESS, tresult;
1414*00b67f09SDavid van Moolenbroek 
1415*00b67f09SDavid van Moolenbroek 	reln = dns_name_fullcompare(prevname, name, &order, &labels);
1416*00b67f09SDavid van Moolenbroek 	if (order >= 0)
1417*00b67f09SDavid van Moolenbroek 		return (result);
1418*00b67f09SDavid van Moolenbroek 
1419*00b67f09SDavid van Moolenbroek 	nlabels = dns_name_countlabels(name);
1420*00b67f09SDavid van Moolenbroek 
1421*00b67f09SDavid van Moolenbroek 	if (reln == dns_namereln_commonancestor ||
1422*00b67f09SDavid van Moolenbroek 	    reln == dns_namereln_contains) {
1423*00b67f09SDavid van Moolenbroek 		dns_name_init(&suffix, NULL);
1424*00b67f09SDavid van Moolenbroek 		for (i = labels + 1; i < nlabels; i++) {
1425*00b67f09SDavid van Moolenbroek 			dns_name_getlabelsequence(name, nlabels - i, i,
1426*00b67f09SDavid van Moolenbroek 						  &suffix);
1427*00b67f09SDavid van Moolenbroek 			if (nsec3paramset != NULL &&
1428*00b67f09SDavid van Moolenbroek 			     dns_rdataset_isassociated(nsec3paramset)) {
1429*00b67f09SDavid van Moolenbroek 				tresult = verifynsec3s(db, ver, origin, mctx,
1430*00b67f09SDavid van Moolenbroek 						       &suffix, nsec3paramset,
1431*00b67f09SDavid van Moolenbroek 						       isdelegation, ISC_TRUE,
1432*00b67f09SDavid van Moolenbroek 						       NULL, 0);
1433*00b67f09SDavid van Moolenbroek 				if (result == ISC_R_SUCCESS &&
1434*00b67f09SDavid van Moolenbroek 				    tresult != ISC_R_SUCCESS)
1435*00b67f09SDavid van Moolenbroek 					result = tresult;
1436*00b67f09SDavid van Moolenbroek 			}
1437*00b67f09SDavid van Moolenbroek 		}
1438*00b67f09SDavid van Moolenbroek 	}
1439*00b67f09SDavid van Moolenbroek 	return (result);
1440*00b67f09SDavid van Moolenbroek }
1441*00b67f09SDavid van Moolenbroek 
1442*00b67f09SDavid van Moolenbroek /*%
1443*00b67f09SDavid van Moolenbroek  * Verify that certain things are sane:
1444*00b67f09SDavid van Moolenbroek  *
1445*00b67f09SDavid van Moolenbroek  *   The apex has a DNSKEY record with at least one KSK, and at least
1446*00b67f09SDavid van Moolenbroek  *   one ZSK if the -x flag was not used.
1447*00b67f09SDavid van Moolenbroek  *
1448*00b67f09SDavid van Moolenbroek  *   The DNSKEY record was signed with at least one of the KSKs in this
1449*00b67f09SDavid van Moolenbroek  *   set.
1450*00b67f09SDavid van Moolenbroek  *
1451*00b67f09SDavid van Moolenbroek  *   The rest of the zone was signed with at least one of the ZSKs
1452*00b67f09SDavid van Moolenbroek  *   present in the DNSKEY RRSET.
1453*00b67f09SDavid van Moolenbroek  */
1454*00b67f09SDavid van Moolenbroek void
verifyzone(dns_db_t * db,dns_dbversion_t * ver,dns_name_t * origin,isc_mem_t * mctx,isc_boolean_t ignore_kskflag,isc_boolean_t keyset_kskonly)1455*00b67f09SDavid van Moolenbroek verifyzone(dns_db_t *db, dns_dbversion_t *ver,
1456*00b67f09SDavid van Moolenbroek 	   dns_name_t *origin, isc_mem_t *mctx,
1457*00b67f09SDavid van Moolenbroek 	   isc_boolean_t ignore_kskflag, isc_boolean_t keyset_kskonly)
1458*00b67f09SDavid van Moolenbroek {
1459*00b67f09SDavid van Moolenbroek 	char algbuf[80];
1460*00b67f09SDavid van Moolenbroek 	dns_dbiterator_t *dbiter = NULL;
1461*00b67f09SDavid van Moolenbroek 	dns_dbnode_t *node = NULL, *nextnode = NULL;
1462*00b67f09SDavid van Moolenbroek 	dns_fixedname_t fname, fnextname, fprevname, fzonecut;
1463*00b67f09SDavid van Moolenbroek 	dns_name_t *name, *nextname, *prevname, *zonecut;
1464*00b67f09SDavid van Moolenbroek 	dns_rdata_dnskey_t dnskey;
1465*00b67f09SDavid van Moolenbroek 	dns_rdata_t rdata = DNS_RDATA_INIT;
1466*00b67f09SDavid van Moolenbroek 	dns_rdataset_t keyset, soaset;
1467*00b67f09SDavid van Moolenbroek 	dns_rdataset_t keysigs, soasigs;
1468*00b67f09SDavid van Moolenbroek 	dns_rdataset_t nsecset, nsecsigs;
1469*00b67f09SDavid van Moolenbroek 	dns_rdataset_t nsec3paramset, nsec3paramsigs;
1470*00b67f09SDavid van Moolenbroek 	int i;
1471*00b67f09SDavid van Moolenbroek 	isc_boolean_t done = ISC_FALSE;
1472*00b67f09SDavid van Moolenbroek 	isc_boolean_t first = ISC_TRUE;
1473*00b67f09SDavid van Moolenbroek 	isc_boolean_t goodksk = ISC_FALSE;
1474*00b67f09SDavid van Moolenbroek 	isc_boolean_t goodzsk = ISC_FALSE;
1475*00b67f09SDavid van Moolenbroek 	isc_result_t result, vresult = ISC_R_UNSET;
1476*00b67f09SDavid van Moolenbroek 	unsigned char revoked_ksk[256];
1477*00b67f09SDavid van Moolenbroek 	unsigned char revoked_zsk[256];
1478*00b67f09SDavid van Moolenbroek 	unsigned char standby_ksk[256];
1479*00b67f09SDavid van Moolenbroek 	unsigned char standby_zsk[256];
1480*00b67f09SDavid van Moolenbroek 	unsigned char ksk_algorithms[256];
1481*00b67f09SDavid van Moolenbroek 	unsigned char zsk_algorithms[256];
1482*00b67f09SDavid van Moolenbroek 	unsigned char bad_algorithms[256];
1483*00b67f09SDavid van Moolenbroek 	unsigned char act_algorithms[256];
1484*00b67f09SDavid van Moolenbroek 
1485*00b67f09SDavid van Moolenbroek 	result = isc_heap_create(mctx, chain_compare, NULL, 1024,
1486*00b67f09SDavid van Moolenbroek 				 &expected_chains);
1487*00b67f09SDavid van Moolenbroek 	check_result(result, "isc_heap_create()");
1488*00b67f09SDavid van Moolenbroek 	result = isc_heap_create(mctx, chain_compare, NULL, 1024,
1489*00b67f09SDavid van Moolenbroek 				 &found_chains);
1490*00b67f09SDavid van Moolenbroek 	check_result(result, "isc_heap_create()");
1491*00b67f09SDavid van Moolenbroek 
1492*00b67f09SDavid van Moolenbroek 	result = dns_db_findnode(db, origin, ISC_FALSE, &node);
1493*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
1494*00b67f09SDavid van Moolenbroek 		fatal("failed to find the zone's origin: %s",
1495*00b67f09SDavid van Moolenbroek 		      isc_result_totext(result));
1496*00b67f09SDavid van Moolenbroek 
1497*00b67f09SDavid van Moolenbroek 	dns_rdataset_init(&keyset);
1498*00b67f09SDavid van Moolenbroek 	dns_rdataset_init(&keysigs);
1499*00b67f09SDavid van Moolenbroek 	dns_rdataset_init(&soaset);
1500*00b67f09SDavid van Moolenbroek 	dns_rdataset_init(&soasigs);
1501*00b67f09SDavid van Moolenbroek 	dns_rdataset_init(&nsecset);
1502*00b67f09SDavid van Moolenbroek 	dns_rdataset_init(&nsecsigs);
1503*00b67f09SDavid van Moolenbroek 	dns_rdataset_init(&nsec3paramset);
1504*00b67f09SDavid van Moolenbroek 	dns_rdataset_init(&nsec3paramsigs);
1505*00b67f09SDavid van Moolenbroek 	result = dns_db_findrdataset(db, node, ver, dns_rdatatype_dnskey,
1506*00b67f09SDavid van Moolenbroek 				     0, 0, &keyset, &keysigs);
1507*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
1508*00b67f09SDavid van Moolenbroek 		fatal("Zone contains no DNSSEC keys\n");
1509*00b67f09SDavid van Moolenbroek 
1510*00b67f09SDavid van Moolenbroek 	result = dns_db_findrdataset(db, node, ver, dns_rdatatype_soa,
1511*00b67f09SDavid van Moolenbroek 				     0, 0, &soaset, &soasigs);
1512*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
1513*00b67f09SDavid van Moolenbroek 		fatal("Zone contains no SOA record\n");
1514*00b67f09SDavid van Moolenbroek 
1515*00b67f09SDavid van Moolenbroek 	result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec,
1516*00b67f09SDavid van Moolenbroek 				     0, 0, &nsecset, &nsecsigs);
1517*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND)
1518*00b67f09SDavid van Moolenbroek 		fatal("NSEC lookup failed\n");
1519*00b67f09SDavid van Moolenbroek 
1520*00b67f09SDavid van Moolenbroek 	result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3param,
1521*00b67f09SDavid van Moolenbroek 				     0, 0, &nsec3paramset, &nsec3paramsigs);
1522*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND)
1523*00b67f09SDavid van Moolenbroek 		fatal("NSEC3PARAM lookup failed\n");
1524*00b67f09SDavid van Moolenbroek 
1525*00b67f09SDavid van Moolenbroek 	if (!dns_rdataset_isassociated(&keysigs))
1526*00b67f09SDavid van Moolenbroek 		fatal("DNSKEY is not signed (keys offline or inactive?)\n");
1527*00b67f09SDavid van Moolenbroek 
1528*00b67f09SDavid van Moolenbroek 	if (!dns_rdataset_isassociated(&soasigs))
1529*00b67f09SDavid van Moolenbroek 		fatal("SOA is not signed (keys offline or inactive?)\n");
1530*00b67f09SDavid van Moolenbroek 
1531*00b67f09SDavid van Moolenbroek 	if (dns_rdataset_isassociated(&nsecset) &&
1532*00b67f09SDavid van Moolenbroek 	    !dns_rdataset_isassociated(&nsecsigs))
1533*00b67f09SDavid van Moolenbroek 		fatal("NSEC is not signed (keys offline or inactive?)\n");
1534*00b67f09SDavid van Moolenbroek 
1535*00b67f09SDavid van Moolenbroek 	if (dns_rdataset_isassociated(&nsec3paramset) &&
1536*00b67f09SDavid van Moolenbroek 	    !dns_rdataset_isassociated(&nsec3paramsigs))
1537*00b67f09SDavid van Moolenbroek 		fatal("NSEC3PARAM is not signed (keys offline or inactive?)\n");
1538*00b67f09SDavid van Moolenbroek 
1539*00b67f09SDavid van Moolenbroek 	if (!dns_rdataset_isassociated(&nsecset) &&
1540*00b67f09SDavid van Moolenbroek 	    !dns_rdataset_isassociated(&nsec3paramset))
1541*00b67f09SDavid van Moolenbroek 		fatal("No valid NSEC/NSEC3 chain for testing\n");
1542*00b67f09SDavid van Moolenbroek 
1543*00b67f09SDavid van Moolenbroek 	dns_db_detachnode(db, &node);
1544*00b67f09SDavid van Moolenbroek 
1545*00b67f09SDavid van Moolenbroek 	memset(revoked_ksk, 0, sizeof(revoked_ksk));
1546*00b67f09SDavid van Moolenbroek 	memset(revoked_zsk, 0, sizeof(revoked_zsk));
1547*00b67f09SDavid van Moolenbroek 	memset(standby_ksk, 0, sizeof(standby_ksk));
1548*00b67f09SDavid van Moolenbroek 	memset(standby_zsk, 0, sizeof(standby_zsk));
1549*00b67f09SDavid van Moolenbroek 	memset(ksk_algorithms, 0, sizeof(ksk_algorithms));
1550*00b67f09SDavid van Moolenbroek 	memset(zsk_algorithms, 0, sizeof(zsk_algorithms));
1551*00b67f09SDavid van Moolenbroek 	memset(bad_algorithms, 0, sizeof(bad_algorithms));
1552*00b67f09SDavid van Moolenbroek 	memset(act_algorithms, 0, sizeof(act_algorithms));
1553*00b67f09SDavid van Moolenbroek 
1554*00b67f09SDavid van Moolenbroek 	/*
1555*00b67f09SDavid van Moolenbroek 	 * Check that the DNSKEY RR has at least one self signing KSK
1556*00b67f09SDavid van Moolenbroek 	 * and one ZSK per algorithm in it (or, if -x was used, one
1557*00b67f09SDavid van Moolenbroek 	 * self-signing KSK).
1558*00b67f09SDavid van Moolenbroek 	 */
1559*00b67f09SDavid van Moolenbroek 	for (result = dns_rdataset_first(&keyset);
1560*00b67f09SDavid van Moolenbroek 	     result == ISC_R_SUCCESS;
1561*00b67f09SDavid van Moolenbroek 	     result = dns_rdataset_next(&keyset)) {
1562*00b67f09SDavid van Moolenbroek 		dns_rdataset_current(&keyset, &rdata);
1563*00b67f09SDavid van Moolenbroek 		result = dns_rdata_tostruct(&rdata, &dnskey, NULL);
1564*00b67f09SDavid van Moolenbroek 		check_result(result, "dns_rdata_tostruct");
1565*00b67f09SDavid van Moolenbroek 
1566*00b67f09SDavid van Moolenbroek 		if ((dnskey.flags & DNS_KEYOWNER_ZONE) == 0)
1567*00b67f09SDavid van Moolenbroek 			;
1568*00b67f09SDavid van Moolenbroek 		else if ((dnskey.flags & DNS_KEYFLAG_REVOKE) != 0) {
1569*00b67f09SDavid van Moolenbroek 			if ((dnskey.flags & DNS_KEYFLAG_KSK) != 0 &&
1570*00b67f09SDavid van Moolenbroek 			    !dns_dnssec_selfsigns(&rdata, origin, &keyset,
1571*00b67f09SDavid van Moolenbroek 						  &keysigs, ISC_FALSE,
1572*00b67f09SDavid van Moolenbroek 						  mctx)) {
1573*00b67f09SDavid van Moolenbroek 				char namebuf[DNS_NAME_FORMATSIZE];
1574*00b67f09SDavid van Moolenbroek 				char buffer[1024];
1575*00b67f09SDavid van Moolenbroek 				isc_buffer_t buf;
1576*00b67f09SDavid van Moolenbroek 
1577*00b67f09SDavid van Moolenbroek 				dns_name_format(origin, namebuf,
1578*00b67f09SDavid van Moolenbroek 						sizeof(namebuf));
1579*00b67f09SDavid van Moolenbroek 				isc_buffer_init(&buf, buffer, sizeof(buffer));
1580*00b67f09SDavid van Moolenbroek 				result = dns_rdata_totext(&rdata, NULL, &buf);
1581*00b67f09SDavid van Moolenbroek 				check_result(result, "dns_rdata_totext");
1582*00b67f09SDavid van Moolenbroek 				fatal("revoked KSK is not self signed:\n"
1583*00b67f09SDavid van Moolenbroek 				      "%s DNSKEY %.*s", namebuf,
1584*00b67f09SDavid van Moolenbroek 				      (int)isc_buffer_usedlength(&buf), buffer);
1585*00b67f09SDavid van Moolenbroek 			}
1586*00b67f09SDavid van Moolenbroek 			if ((dnskey.flags & DNS_KEYFLAG_KSK) != 0 &&
1587*00b67f09SDavid van Moolenbroek 			     revoked_ksk[dnskey.algorithm] != 255)
1588*00b67f09SDavid van Moolenbroek 				revoked_ksk[dnskey.algorithm]++;
1589*00b67f09SDavid van Moolenbroek 			else if ((dnskey.flags & DNS_KEYFLAG_KSK) == 0 &&
1590*00b67f09SDavid van Moolenbroek 				 revoked_zsk[dnskey.algorithm] != 255)
1591*00b67f09SDavid van Moolenbroek 				revoked_zsk[dnskey.algorithm]++;
1592*00b67f09SDavid van Moolenbroek 		} else if ((dnskey.flags & DNS_KEYFLAG_KSK) != 0) {
1593*00b67f09SDavid van Moolenbroek 			if (dns_dnssec_selfsigns(&rdata, origin, &keyset,
1594*00b67f09SDavid van Moolenbroek 						 &keysigs, ISC_FALSE, mctx)) {
1595*00b67f09SDavid van Moolenbroek 				if (ksk_algorithms[dnskey.algorithm] != 255)
1596*00b67f09SDavid van Moolenbroek 					ksk_algorithms[dnskey.algorithm]++;
1597*00b67f09SDavid van Moolenbroek 				goodksk = ISC_TRUE;
1598*00b67f09SDavid van Moolenbroek 			} else {
1599*00b67f09SDavid van Moolenbroek 				if (standby_ksk[dnskey.algorithm] != 255)
1600*00b67f09SDavid van Moolenbroek 					standby_ksk[dnskey.algorithm]++;
1601*00b67f09SDavid van Moolenbroek 			}
1602*00b67f09SDavid van Moolenbroek 		} else if (dns_dnssec_selfsigns(&rdata, origin, &keyset,
1603*00b67f09SDavid van Moolenbroek 						&keysigs, ISC_FALSE, mctx)) {
1604*00b67f09SDavid van Moolenbroek 			if (zsk_algorithms[dnskey.algorithm] != 255)
1605*00b67f09SDavid van Moolenbroek 				zsk_algorithms[dnskey.algorithm]++;
1606*00b67f09SDavid van Moolenbroek 			goodzsk = ISC_TRUE;
1607*00b67f09SDavid van Moolenbroek 		} else if (dns_dnssec_signs(&rdata, origin, &soaset,
1608*00b67f09SDavid van Moolenbroek 					    &soasigs, ISC_FALSE, mctx)) {
1609*00b67f09SDavid van Moolenbroek 			if (zsk_algorithms[dnskey.algorithm] != 255)
1610*00b67f09SDavid van Moolenbroek 				zsk_algorithms[dnskey.algorithm]++;
1611*00b67f09SDavid van Moolenbroek 		} else {
1612*00b67f09SDavid van Moolenbroek 			if (standby_zsk[dnskey.algorithm] != 255)
1613*00b67f09SDavid van Moolenbroek 				standby_zsk[dnskey.algorithm]++;
1614*00b67f09SDavid van Moolenbroek 		}
1615*00b67f09SDavid van Moolenbroek 		dns_rdata_freestruct(&dnskey);
1616*00b67f09SDavid van Moolenbroek 		dns_rdata_reset(&rdata);
1617*00b67f09SDavid van Moolenbroek 	}
1618*00b67f09SDavid van Moolenbroek 	dns_rdataset_disassociate(&keysigs);
1619*00b67f09SDavid van Moolenbroek 	dns_rdataset_disassociate(&soaset);
1620*00b67f09SDavid van Moolenbroek 	dns_rdataset_disassociate(&soasigs);
1621*00b67f09SDavid van Moolenbroek 	if (dns_rdataset_isassociated(&nsecsigs))
1622*00b67f09SDavid van Moolenbroek 		dns_rdataset_disassociate(&nsecsigs);
1623*00b67f09SDavid van Moolenbroek 	if (dns_rdataset_isassociated(&nsec3paramsigs))
1624*00b67f09SDavid van Moolenbroek 		dns_rdataset_disassociate(&nsec3paramsigs);
1625*00b67f09SDavid van Moolenbroek 
1626*00b67f09SDavid van Moolenbroek 	if (ignore_kskflag ) {
1627*00b67f09SDavid van Moolenbroek 		if (!goodksk && !goodzsk)
1628*00b67f09SDavid van Moolenbroek 			fatal("No self-signed DNSKEY found.");
1629*00b67f09SDavid van Moolenbroek 	} else if (!goodksk)
1630*00b67f09SDavid van Moolenbroek 		fatal("No self-signed KSK DNSKEY found.  Supply an active\n"
1631*00b67f09SDavid van Moolenbroek 		      "key with the KSK flag set, or use '-P'.");
1632*00b67f09SDavid van Moolenbroek 
1633*00b67f09SDavid van Moolenbroek 	fprintf(stderr, "Verifying the zone using the following algorithms:");
1634*00b67f09SDavid van Moolenbroek 	for (i = 0; i < 256; i++) {
1635*00b67f09SDavid van Moolenbroek 		if (ignore_kskflag)
1636*00b67f09SDavid van Moolenbroek 			act_algorithms[i] = (ksk_algorithms[i] != 0 ||
1637*00b67f09SDavid van Moolenbroek 					     zsk_algorithms[i] != 0) ? 1 : 0;
1638*00b67f09SDavid van Moolenbroek 		else
1639*00b67f09SDavid van Moolenbroek 			act_algorithms[i] = ksk_algorithms[i] != 0 ? 1 : 0;
1640*00b67f09SDavid van Moolenbroek 		if (act_algorithms[i] != 0) {
1641*00b67f09SDavid van Moolenbroek 			dns_secalg_format(i, algbuf, sizeof(algbuf));
1642*00b67f09SDavid van Moolenbroek 			fprintf(stderr, " %s", algbuf);
1643*00b67f09SDavid van Moolenbroek 		}
1644*00b67f09SDavid van Moolenbroek 	}
1645*00b67f09SDavid van Moolenbroek 	fprintf(stderr, ".\n");
1646*00b67f09SDavid van Moolenbroek 
1647*00b67f09SDavid van Moolenbroek 	if (!ignore_kskflag && !keyset_kskonly) {
1648*00b67f09SDavid van Moolenbroek 		for (i = 0; i < 256; i++) {
1649*00b67f09SDavid van Moolenbroek 			/*
1650*00b67f09SDavid van Moolenbroek 			 * The counts should both be zero or both be non-zero.
1651*00b67f09SDavid van Moolenbroek 			 * Mark the algorithm as bad if this is not met.
1652*00b67f09SDavid van Moolenbroek 			 */
1653*00b67f09SDavid van Moolenbroek 			if ((ksk_algorithms[i] != 0) ==
1654*00b67f09SDavid van Moolenbroek 			    (zsk_algorithms[i] != 0))
1655*00b67f09SDavid van Moolenbroek 				continue;
1656*00b67f09SDavid van Moolenbroek 			dns_secalg_format(i, algbuf, sizeof(algbuf));
1657*00b67f09SDavid van Moolenbroek 			fprintf(stderr, "Missing %s for algorithm %s\n",
1658*00b67f09SDavid van Moolenbroek 				(ksk_algorithms[i] != 0)
1659*00b67f09SDavid van Moolenbroek 				   ? "ZSK"
1660*00b67f09SDavid van Moolenbroek 				   : "self-signed KSK",
1661*00b67f09SDavid van Moolenbroek 				algbuf);
1662*00b67f09SDavid van Moolenbroek 			bad_algorithms[i] = 1;
1663*00b67f09SDavid van Moolenbroek 		}
1664*00b67f09SDavid van Moolenbroek 	}
1665*00b67f09SDavid van Moolenbroek 
1666*00b67f09SDavid van Moolenbroek 	/*
1667*00b67f09SDavid van Moolenbroek 	 * Check that all the other records were signed by keys that are
1668*00b67f09SDavid van Moolenbroek 	 * present in the DNSKEY RRSET.
1669*00b67f09SDavid van Moolenbroek 	 */
1670*00b67f09SDavid van Moolenbroek 
1671*00b67f09SDavid van Moolenbroek 	dns_fixedname_init(&fname);
1672*00b67f09SDavid van Moolenbroek 	name = dns_fixedname_name(&fname);
1673*00b67f09SDavid van Moolenbroek 	dns_fixedname_init(&fnextname);
1674*00b67f09SDavid van Moolenbroek 	nextname = dns_fixedname_name(&fnextname);
1675*00b67f09SDavid van Moolenbroek 	dns_fixedname_init(&fprevname);
1676*00b67f09SDavid van Moolenbroek 	prevname = NULL;
1677*00b67f09SDavid van Moolenbroek 	dns_fixedname_init(&fzonecut);
1678*00b67f09SDavid van Moolenbroek 	zonecut = NULL;
1679*00b67f09SDavid van Moolenbroek 
1680*00b67f09SDavid van Moolenbroek 	result = dns_db_createiterator(db, DNS_DB_NONSEC3, &dbiter);
1681*00b67f09SDavid van Moolenbroek 	check_result(result, "dns_db_createiterator()");
1682*00b67f09SDavid van Moolenbroek 
1683*00b67f09SDavid van Moolenbroek 	result = dns_dbiterator_first(dbiter);
1684*00b67f09SDavid van Moolenbroek 	check_result(result, "dns_dbiterator_first()");
1685*00b67f09SDavid van Moolenbroek 
1686*00b67f09SDavid van Moolenbroek 	while (!done) {
1687*00b67f09SDavid van Moolenbroek 		isc_boolean_t isdelegation = ISC_FALSE;
1688*00b67f09SDavid van Moolenbroek 
1689*00b67f09SDavid van Moolenbroek 		result = dns_dbiterator_current(dbiter, &node, name);
1690*00b67f09SDavid van Moolenbroek 		check_dns_dbiterator_current(result);
1691*00b67f09SDavid van Moolenbroek 		if (!dns_name_issubdomain(name, origin)) {
1692*00b67f09SDavid van Moolenbroek 			check_no_nsec(name, node, db, ver);
1693*00b67f09SDavid van Moolenbroek 			dns_db_detachnode(db, &node);
1694*00b67f09SDavid van Moolenbroek 			result = dns_dbiterator_next(dbiter);
1695*00b67f09SDavid van Moolenbroek 			if (result == ISC_R_NOMORE)
1696*00b67f09SDavid van Moolenbroek 				done = ISC_TRUE;
1697*00b67f09SDavid van Moolenbroek 			else
1698*00b67f09SDavid van Moolenbroek 				check_result(result, "dns_dbiterator_next()");
1699*00b67f09SDavid van Moolenbroek 			continue;
1700*00b67f09SDavid van Moolenbroek 		}
1701*00b67f09SDavid van Moolenbroek 		if (is_delegation(db, ver, origin, name, node, NULL)) {
1702*00b67f09SDavid van Moolenbroek 			zonecut = dns_fixedname_name(&fzonecut);
1703*00b67f09SDavid van Moolenbroek 			dns_name_copy(name, zonecut, NULL);
1704*00b67f09SDavid van Moolenbroek 			isdelegation = ISC_TRUE;
1705*00b67f09SDavid van Moolenbroek 		}
1706*00b67f09SDavid van Moolenbroek 		nextnode = NULL;
1707*00b67f09SDavid van Moolenbroek 		result = dns_dbiterator_next(dbiter);
1708*00b67f09SDavid van Moolenbroek 		while (result == ISC_R_SUCCESS) {
1709*00b67f09SDavid van Moolenbroek 			result = dns_dbiterator_current(dbiter, &nextnode,
1710*00b67f09SDavid van Moolenbroek 							nextname);
1711*00b67f09SDavid van Moolenbroek 			check_dns_dbiterator_current(result);
1712*00b67f09SDavid van Moolenbroek 			if (!dns_name_issubdomain(nextname, origin) ||
1713*00b67f09SDavid van Moolenbroek 			    (zonecut != NULL &&
1714*00b67f09SDavid van Moolenbroek 			     dns_name_issubdomain(nextname, zonecut)))
1715*00b67f09SDavid van Moolenbroek 			{
1716*00b67f09SDavid van Moolenbroek 				check_no_nsec(nextname, nextnode, db, ver);
1717*00b67f09SDavid van Moolenbroek 				dns_db_detachnode(db, &nextnode);
1718*00b67f09SDavid van Moolenbroek 				result = dns_dbiterator_next(dbiter);
1719*00b67f09SDavid van Moolenbroek 				continue;
1720*00b67f09SDavid van Moolenbroek 			}
1721*00b67f09SDavid van Moolenbroek 			if (is_empty(db, ver, nextnode)) {
1722*00b67f09SDavid van Moolenbroek 				dns_db_detachnode(db, &nextnode);
1723*00b67f09SDavid van Moolenbroek 				result = dns_dbiterator_next(dbiter);
1724*00b67f09SDavid van Moolenbroek 				continue;
1725*00b67f09SDavid van Moolenbroek 			}
1726*00b67f09SDavid van Moolenbroek 			dns_db_detachnode(db, &nextnode);
1727*00b67f09SDavid van Moolenbroek 			break;
1728*00b67f09SDavid van Moolenbroek 		}
1729*00b67f09SDavid van Moolenbroek 		if (result == ISC_R_NOMORE) {
1730*00b67f09SDavid van Moolenbroek 			done = ISC_TRUE;
1731*00b67f09SDavid van Moolenbroek 			nextname = origin;
1732*00b67f09SDavid van Moolenbroek 		} else if (result != ISC_R_SUCCESS)
1733*00b67f09SDavid van Moolenbroek 			fatal("iterating through the database failed: %s",
1734*00b67f09SDavid van Moolenbroek 			      isc_result_totext(result));
1735*00b67f09SDavid van Moolenbroek 		result = verifynode(db, ver, origin, mctx, name, node,
1736*00b67f09SDavid van Moolenbroek 				    isdelegation, &keyset, act_algorithms,
1737*00b67f09SDavid van Moolenbroek 				    bad_algorithms, &nsecset, &nsec3paramset,
1738*00b67f09SDavid van Moolenbroek 				    nextname);
1739*00b67f09SDavid van Moolenbroek 		if (vresult == ISC_R_UNSET)
1740*00b67f09SDavid van Moolenbroek 			vresult = ISC_R_SUCCESS;
1741*00b67f09SDavid van Moolenbroek 		if (vresult == ISC_R_SUCCESS && result != ISC_R_SUCCESS)
1742*00b67f09SDavid van Moolenbroek 			vresult = result;
1743*00b67f09SDavid van Moolenbroek 		if (prevname != NULL) {
1744*00b67f09SDavid van Moolenbroek 			result = verifyemptynodes(db, ver, origin, mctx, name,
1745*00b67f09SDavid van Moolenbroek 						  prevname, isdelegation,
1746*00b67f09SDavid van Moolenbroek 						  &nsec3paramset);
1747*00b67f09SDavid van Moolenbroek 		} else
1748*00b67f09SDavid van Moolenbroek 			prevname = dns_fixedname_name(&fprevname);
1749*00b67f09SDavid van Moolenbroek 		dns_name_copy(name, prevname, NULL);
1750*00b67f09SDavid van Moolenbroek 		if (vresult == ISC_R_SUCCESS && result != ISC_R_SUCCESS)
1751*00b67f09SDavid van Moolenbroek 			vresult = result;
1752*00b67f09SDavid van Moolenbroek 		dns_db_detachnode(db, &node);
1753*00b67f09SDavid van Moolenbroek 	}
1754*00b67f09SDavid van Moolenbroek 
1755*00b67f09SDavid van Moolenbroek 	dns_dbiterator_destroy(&dbiter);
1756*00b67f09SDavid van Moolenbroek 
1757*00b67f09SDavid van Moolenbroek 	result = dns_db_createiterator(db, DNS_DB_NSEC3ONLY, &dbiter);
1758*00b67f09SDavid van Moolenbroek 	check_result(result, "dns_db_createiterator()");
1759*00b67f09SDavid van Moolenbroek 
1760*00b67f09SDavid van Moolenbroek 	for (result = dns_dbiterator_first(dbiter);
1761*00b67f09SDavid van Moolenbroek 	     result == ISC_R_SUCCESS;
1762*00b67f09SDavid van Moolenbroek 	     result = dns_dbiterator_next(dbiter) ) {
1763*00b67f09SDavid van Moolenbroek 		result = dns_dbiterator_current(dbiter, &node, name);
1764*00b67f09SDavid van Moolenbroek 		check_dns_dbiterator_current(result);
1765*00b67f09SDavid van Moolenbroek 		result = verifynode(db, ver, origin, mctx, name, node,
1766*00b67f09SDavid van Moolenbroek 				    ISC_FALSE, &keyset, act_algorithms,
1767*00b67f09SDavid van Moolenbroek 				    bad_algorithms, NULL, NULL, NULL);
1768*00b67f09SDavid van Moolenbroek 		check_result(result, "verifynode");
1769*00b67f09SDavid van Moolenbroek 		record_found(db, ver, mctx, name, node, &nsec3paramset);
1770*00b67f09SDavid van Moolenbroek 		dns_db_detachnode(db, &node);
1771*00b67f09SDavid van Moolenbroek 	}
1772*00b67f09SDavid van Moolenbroek 	dns_dbiterator_destroy(&dbiter);
1773*00b67f09SDavid van Moolenbroek 
1774*00b67f09SDavid van Moolenbroek 	dns_rdataset_disassociate(&keyset);
1775*00b67f09SDavid van Moolenbroek 	if (dns_rdataset_isassociated(&nsecset))
1776*00b67f09SDavid van Moolenbroek 		dns_rdataset_disassociate(&nsecset);
1777*00b67f09SDavid van Moolenbroek 	if (dns_rdataset_isassociated(&nsec3paramset))
1778*00b67f09SDavid van Moolenbroek 		dns_rdataset_disassociate(&nsec3paramset);
1779*00b67f09SDavid van Moolenbroek 
1780*00b67f09SDavid van Moolenbroek 	result = verify_nsec3_chains(mctx);
1781*00b67f09SDavid van Moolenbroek 	if (vresult == ISC_R_UNSET)
1782*00b67f09SDavid van Moolenbroek 		vresult = ISC_R_SUCCESS;
1783*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS && vresult == ISC_R_SUCCESS)
1784*00b67f09SDavid van Moolenbroek 		vresult = result;
1785*00b67f09SDavid van Moolenbroek 	isc_heap_destroy(&expected_chains);
1786*00b67f09SDavid van Moolenbroek 	isc_heap_destroy(&found_chains);
1787*00b67f09SDavid van Moolenbroek 
1788*00b67f09SDavid van Moolenbroek 	/*
1789*00b67f09SDavid van Moolenbroek 	 * If we made it this far, we have what we consider a properly signed
1790*00b67f09SDavid van Moolenbroek 	 * zone.  Set the good flag.
1791*00b67f09SDavid van Moolenbroek 	 */
1792*00b67f09SDavid van Moolenbroek 	for (i = 0; i < 256; i++) {
1793*00b67f09SDavid van Moolenbroek 		if (bad_algorithms[i] != 0) {
1794*00b67f09SDavid van Moolenbroek 			if (first)
1795*00b67f09SDavid van Moolenbroek 				fprintf(stderr, "The zone is not fully signed "
1796*00b67f09SDavid van Moolenbroek 					"for the following algorithms:");
1797*00b67f09SDavid van Moolenbroek 			dns_secalg_format(i, algbuf, sizeof(algbuf));
1798*00b67f09SDavid van Moolenbroek 			fprintf(stderr, " %s", algbuf);
1799*00b67f09SDavid van Moolenbroek 			first = ISC_FALSE;
1800*00b67f09SDavid van Moolenbroek 		}
1801*00b67f09SDavid van Moolenbroek 	}
1802*00b67f09SDavid van Moolenbroek 	if (!first) {
1803*00b67f09SDavid van Moolenbroek 		fprintf(stderr, ".\n");
1804*00b67f09SDavid van Moolenbroek 		fatal("DNSSEC completeness test failed.");
1805*00b67f09SDavid van Moolenbroek 	}
1806*00b67f09SDavid van Moolenbroek 
1807*00b67f09SDavid van Moolenbroek 	if (vresult != ISC_R_SUCCESS)
1808*00b67f09SDavid van Moolenbroek 		fatal("DNSSEC completeness test failed (%s).",
1809*00b67f09SDavid van Moolenbroek 		      dns_result_totext(vresult));
1810*00b67f09SDavid van Moolenbroek 
1811*00b67f09SDavid van Moolenbroek 	if (goodksk || ignore_kskflag) {
1812*00b67f09SDavid van Moolenbroek 		/*
1813*00b67f09SDavid van Moolenbroek 		 * Print the success summary.
1814*00b67f09SDavid van Moolenbroek 		 */
1815*00b67f09SDavid van Moolenbroek 		fprintf(stderr, "Zone fully signed:\n");
1816*00b67f09SDavid van Moolenbroek 		for (i = 0; i < 256; i++) {
1817*00b67f09SDavid van Moolenbroek 			if ((ksk_algorithms[i] != 0) ||
1818*00b67f09SDavid van Moolenbroek 			    (standby_ksk[i] != 0) ||
1819*00b67f09SDavid van Moolenbroek 			    (revoked_zsk[i] != 0) ||
1820*00b67f09SDavid van Moolenbroek 			    (zsk_algorithms[i] != 0) ||
1821*00b67f09SDavid van Moolenbroek 			    (standby_zsk[i] != 0) ||
1822*00b67f09SDavid van Moolenbroek 			    (revoked_zsk[i] != 0)) {
1823*00b67f09SDavid van Moolenbroek 				dns_secalg_format(i, algbuf, sizeof(algbuf));
1824*00b67f09SDavid van Moolenbroek 				fprintf(stderr, "Algorithm: %s: KSKs: "
1825*00b67f09SDavid van Moolenbroek 					"%u active, %u stand-by, %u revoked\n",
1826*00b67f09SDavid van Moolenbroek 					algbuf, ksk_algorithms[i],
1827*00b67f09SDavid van Moolenbroek 					standby_ksk[i], revoked_ksk[i]);
1828*00b67f09SDavid van Moolenbroek 				fprintf(stderr, "%*sZSKs: "
1829*00b67f09SDavid van Moolenbroek 					"%u active, %u %s, %u revoked\n",
1830*00b67f09SDavid van Moolenbroek 					(int) strlen(algbuf) + 13, "",
1831*00b67f09SDavid van Moolenbroek 					zsk_algorithms[i],
1832*00b67f09SDavid van Moolenbroek 					standby_zsk[i],
1833*00b67f09SDavid van Moolenbroek 					keyset_kskonly ? "present" : "stand-by",
1834*00b67f09SDavid van Moolenbroek 					revoked_zsk[i]);
1835*00b67f09SDavid van Moolenbroek 			}
1836*00b67f09SDavid van Moolenbroek 		}
1837*00b67f09SDavid van Moolenbroek 	}
1838*00b67f09SDavid van Moolenbroek }
1839