xref: /netbsd-src/external/mpl/bind/dist/bin/dnssec/dnssec-verify.c (revision 0a3071956a3a9fdebdbf7f338cf2d439b45fc728)
1 /*	$NetBSD: dnssec-verify.c,v 1.7 2024/02/21 22:51:03 christos Exp $	*/
2 
3 /*
4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5  *
6  * SPDX-License-Identifier: MPL-2.0
7  *
8  * This Source Code Form is subject to the terms of the Mozilla Public
9  * License, v. 2.0. If a copy of the MPL was not distributed with this
10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11  *
12  * See the COPYRIGHT file distributed with this work for additional
13  * information regarding copyright ownership.
14  */
15 
16 /*! \file */
17 
18 #include <stdbool.h>
19 #include <stdlib.h>
20 #include <time.h>
21 
22 #include <isc/app.h>
23 #include <isc/attributes.h>
24 #include <isc/base32.h>
25 #include <isc/commandline.h>
26 #include <isc/event.h>
27 #include <isc/file.h>
28 #include <isc/hash.h>
29 #include <isc/hex.h>
30 #include <isc/mem.h>
31 #include <isc/mutex.h>
32 #include <isc/os.h>
33 #include <isc/print.h>
34 #include <isc/random.h>
35 #include <isc/result.h>
36 #include <isc/rwlock.h>
37 #include <isc/serial.h>
38 #include <isc/stdio.h>
39 #include <isc/string.h>
40 #include <isc/time.h>
41 #include <isc/util.h>
42 
43 #include <dns/db.h>
44 #include <dns/dbiterator.h>
45 #include <dns/diff.h>
46 #include <dns/dnssec.h>
47 #include <dns/ds.h>
48 #include <dns/fixedname.h>
49 #include <dns/keyvalues.h>
50 #include <dns/log.h>
51 #include <dns/master.h>
52 #include <dns/masterdump.h>
53 #include <dns/nsec.h>
54 #include <dns/nsec3.h>
55 #include <dns/rdata.h>
56 #include <dns/rdataclass.h>
57 #include <dns/rdatalist.h>
58 #include <dns/rdataset.h>
59 #include <dns/rdatasetiter.h>
60 #include <dns/rdatastruct.h>
61 #include <dns/rdatatype.h>
62 #include <dns/soa.h>
63 #include <dns/time.h>
64 #include <dns/zoneverify.h>
65 
66 #include <dst/dst.h>
67 
68 #include "dnssectool.h"
69 
70 const char *program = "dnssec-verify";
71 
72 static isc_stdtime_t now;
73 static isc_mem_t *mctx = NULL;
74 static dns_masterformat_t inputformat = dns_masterformat_text;
75 static dns_db_t *gdb;		  /* The database */
76 static dns_dbversion_t *gversion; /* The database version */
77 static dns_rdataclass_t gclass;	  /* The class */
78 static dns_name_t *gorigin;	  /* The database origin */
79 static bool ignore_kskflag = false;
80 static bool keyset_kskonly = false;
81 
82 static void
83 report(const char *format, ...) {
84 	if (!quiet) {
85 		char buf[4096];
86 		va_list args;
87 
88 		va_start(args, format);
89 		vsnprintf(buf, sizeof(buf), format, args);
90 		va_end(args);
91 		fprintf(stdout, "%s\n", buf);
92 	}
93 }
94 
95 /*%
96  * Load the zone file from disk
97  */
98 static void
99 loadzone(char *file, char *origin, dns_rdataclass_t rdclass, dns_db_t **db) {
100 	isc_buffer_t b;
101 	int len;
102 	dns_fixedname_t fname;
103 	dns_name_t *name;
104 	isc_result_t result;
105 
106 	len = strlen(origin);
107 	isc_buffer_init(&b, origin, len);
108 	isc_buffer_add(&b, len);
109 
110 	name = dns_fixedname_initname(&fname);
111 	result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL);
112 	if (result != ISC_R_SUCCESS) {
113 		fatal("failed converting name '%s' to dns format: %s", origin,
114 		      isc_result_totext(result));
115 	}
116 
117 	result = dns_db_create(mctx, "rbt", name, dns_dbtype_zone, rdclass, 0,
118 			       NULL, db);
119 	check_result(result, "dns_db_create()");
120 
121 	result = dns_db_load(*db, file, inputformat, 0);
122 	switch (result) {
123 	case DNS_R_SEENINCLUDE:
124 	case ISC_R_SUCCESS:
125 		break;
126 	case DNS_R_NOTZONETOP:
127 		/*
128 		 * Comparing pointers (vs. using strcmp()) is intentional: we
129 		 * want to check whether -o was supplied on the command line,
130 		 * not whether origin and file contain the same string.
131 		 */
132 		if (origin == file) {
133 			fatal("failed loading zone '%s' from file '%s': "
134 			      "use -o to specify a different zone origin",
135 			      origin, file);
136 		}
137 		FALLTHROUGH;
138 	default:
139 		fatal("failed loading zone from '%s': %s", file,
140 		      isc_result_totext(result));
141 	}
142 }
143 
144 noreturn static void
145 usage(void);
146 
147 static void
148 usage(void) {
149 	fprintf(stderr, "Usage:\n");
150 	fprintf(stderr, "\t%s [options] zonefile [keys]\n", program);
151 
152 	fprintf(stderr, "\n");
153 
154 	fprintf(stderr, "Version: %s\n", PACKAGE_VERSION);
155 
156 	fprintf(stderr, "Options: (default value in parenthesis) \n");
157 	fprintf(stderr, "\t-v debuglevel (0)\n");
158 	fprintf(stderr, "\t-q quiet\n");
159 	fprintf(stderr, "\t-V:\tprint version information\n");
160 	fprintf(stderr, "\t-o origin:\n");
161 	fprintf(stderr, "\t\tzone origin (name of zonefile)\n");
162 	fprintf(stderr, "\t-I format:\n");
163 	fprintf(stderr, "\t\tfile format of input zonefile (text)\n");
164 	fprintf(stderr, "\t-c class (IN)\n");
165 	fprintf(stderr, "\t-E engine:\n");
166 	fprintf(stderr, "\t\tname of an OpenSSL engine to use\n");
167 	fprintf(stderr, "\t-x:\tDNSKEY record signed with KSKs only, "
168 			"not ZSKs\n");
169 	fprintf(stderr, "\t-z:\tAll records signed with KSKs\n");
170 	exit(0);
171 }
172 
173 int
174 main(int argc, char *argv[]) {
175 	char *origin = NULL, *file = NULL;
176 	char *inputformatstr = NULL;
177 	isc_result_t result;
178 	isc_log_t *log = NULL;
179 	const char *engine = NULL;
180 	char *classname = NULL;
181 	dns_rdataclass_t rdclass;
182 	char *endp;
183 	int ch;
184 
185 #define CMDLINE_FLAGS "c:E:hm:o:I:qv:Vxz"
186 
187 	/*
188 	 * Process memory debugging argument first.
189 	 */
190 	while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
191 		switch (ch) {
192 		case 'm':
193 			if (strcasecmp(isc_commandline_argument, "record") == 0)
194 			{
195 				isc_mem_debugging |= ISC_MEM_DEBUGRECORD;
196 			}
197 			if (strcasecmp(isc_commandline_argument, "trace") == 0)
198 			{
199 				isc_mem_debugging |= ISC_MEM_DEBUGTRACE;
200 			}
201 			if (strcasecmp(isc_commandline_argument, "usage") == 0)
202 			{
203 				isc_mem_debugging |= ISC_MEM_DEBUGUSAGE;
204 			}
205 			break;
206 		default:
207 			break;
208 		}
209 	}
210 	isc_commandline_reset = true;
211 	check_result(isc_app_start(), "isc_app_start");
212 
213 	isc_mem_create(&mctx);
214 
215 	isc_commandline_errprint = false;
216 
217 	while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
218 		switch (ch) {
219 		case 'c':
220 			classname = isc_commandline_argument;
221 			break;
222 
223 		case 'E':
224 			engine = isc_commandline_argument;
225 			break;
226 
227 		case 'I':
228 			inputformatstr = isc_commandline_argument;
229 			break;
230 
231 		case 'm':
232 			break;
233 
234 		case 'o':
235 			origin = isc_commandline_argument;
236 			break;
237 
238 		case 'v':
239 			endp = NULL;
240 			verbose = strtol(isc_commandline_argument, &endp, 0);
241 			if (*endp != '\0') {
242 				fatal("verbose level must be numeric");
243 			}
244 			break;
245 
246 		case 'q':
247 			quiet = true;
248 			break;
249 
250 		case 'x':
251 			keyset_kskonly = true;
252 			break;
253 
254 		case 'z':
255 			ignore_kskflag = true;
256 			break;
257 
258 		case '?':
259 			if (isc_commandline_option != '?') {
260 				fprintf(stderr, "%s: invalid argument -%c\n",
261 					program, isc_commandline_option);
262 			}
263 			FALLTHROUGH;
264 
265 		case 'h':
266 			/* Does not return. */
267 			usage();
268 
269 		case 'V':
270 			/* Does not return. */
271 			version(program);
272 
273 		default:
274 			fprintf(stderr, "%s: unhandled option -%c\n", program,
275 				isc_commandline_option);
276 			exit(1);
277 		}
278 	}
279 
280 	result = dst_lib_init(mctx, engine);
281 	if (result != ISC_R_SUCCESS) {
282 		fatal("could not initialize dst: %s",
283 		      isc_result_totext(result));
284 	}
285 
286 	isc_stdtime_get(&now);
287 
288 	rdclass = strtoclass(classname);
289 
290 	setup_logging(mctx, &log);
291 
292 	argc -= isc_commandline_index;
293 	argv += isc_commandline_index;
294 
295 	if (argc < 1) {
296 		usage();
297 	}
298 
299 	file = argv[0];
300 
301 	argc -= 1;
302 	argv += 1;
303 
304 	POST(argc);
305 	POST(argv);
306 
307 	if (origin == NULL) {
308 		origin = file;
309 	}
310 
311 	if (inputformatstr != NULL) {
312 		if (strcasecmp(inputformatstr, "text") == 0) {
313 			inputformat = dns_masterformat_text;
314 		} else if (strcasecmp(inputformatstr, "raw") == 0) {
315 			inputformat = dns_masterformat_raw;
316 		} else {
317 			fatal("unknown file format: %s\n", inputformatstr);
318 		}
319 	}
320 
321 	gdb = NULL;
322 	report("Loading zone '%s' from file '%s'\n", origin, file);
323 	loadzone(file, origin, rdclass, &gdb);
324 	gorigin = dns_db_origin(gdb);
325 	gclass = dns_db_class(gdb);
326 
327 	gversion = NULL;
328 	result = dns_db_newversion(gdb, &gversion);
329 	check_result(result, "dns_db_newversion()");
330 
331 	result = dns_zoneverify_dnssec(NULL, gdb, gversion, gorigin, NULL, mctx,
332 				       ignore_kskflag, keyset_kskonly, report);
333 
334 	dns_db_closeversion(gdb, &gversion, false);
335 	dns_db_detach(&gdb);
336 
337 	cleanup_logging(&log);
338 	dst_lib_destroy();
339 	if (verbose > 10) {
340 		isc_mem_stats(mctx, stdout);
341 	}
342 	isc_mem_destroy(&mctx);
343 
344 	(void)isc_app_finish();
345 
346 	return (result == ISC_R_SUCCESS ? 0 : 1);
347 }
348