xref: /netbsd-src/external/mpl/bind/dist/bin/dnssec/dnssec-verify.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
1 /*	$NetBSD: dnssec-verify.c,v 1.9 2025/01/26 16:24:33 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/attributes.h>
23 #include <isc/base32.h>
24 #include <isc/commandline.h>
25 #include <isc/file.h>
26 #include <isc/hash.h>
27 #include <isc/hex.h>
28 #include <isc/mem.h>
29 #include <isc/mutex.h>
30 #include <isc/os.h>
31 #include <isc/random.h>
32 #include <isc/result.h>
33 #include <isc/rwlock.h>
34 #include <isc/serial.h>
35 #include <isc/stdio.h>
36 #include <isc/string.h>
37 #include <isc/time.h>
38 #include <isc/util.h>
39 
40 #include <dns/db.h>
41 #include <dns/dbiterator.h>
42 #include <dns/diff.h>
43 #include <dns/dnssec.h>
44 #include <dns/ds.h>
45 #include <dns/fixedname.h>
46 #include <dns/keyvalues.h>
47 #include <dns/log.h>
48 #include <dns/master.h>
49 #include <dns/masterdump.h>
50 #include <dns/nsec.h>
51 #include <dns/nsec3.h>
52 #include <dns/rdata.h>
53 #include <dns/rdataclass.h>
54 #include <dns/rdatalist.h>
55 #include <dns/rdataset.h>
56 #include <dns/rdatasetiter.h>
57 #include <dns/rdatastruct.h>
58 #include <dns/rdatatype.h>
59 #include <dns/soa.h>
60 #include <dns/time.h>
61 #include <dns/zoneverify.h>
62 
63 #include <dst/dst.h>
64 
65 #include "dnssectool.h"
66 
67 const char *program = "dnssec-verify";
68 
69 static isc_stdtime_t now;
70 static isc_mem_t *mctx = NULL;
71 static dns_masterformat_t inputformat = dns_masterformat_text;
72 static dns_db_t *gdb = NULL;		 /* The database */
73 static dns_dbversion_t *gversion = NULL; /* The database version */
74 static dns_rdataclass_t gclass;		 /* The class */
75 static dns_name_t *gorigin = NULL;	 /* The database origin */
76 static bool ignore_kskflag = false;
77 static bool keyset_kskonly = false;
78 
79 static void
80 report(const char *format, ...) {
81 	if (!quiet) {
82 		char buf[4096];
83 		va_list args;
84 
85 		va_start(args, format);
86 		vsnprintf(buf, sizeof(buf), format, args);
87 		va_end(args);
88 		fprintf(stdout, "%s\n", buf);
89 	}
90 }
91 
92 /*%
93  * Load the zone file from disk
94  */
95 static void
96 loadzone(char *file, char *origin, dns_rdataclass_t rdclass, dns_db_t **db) {
97 	isc_buffer_t b;
98 	int len;
99 	dns_fixedname_t fname;
100 	dns_name_t *name;
101 	isc_result_t result;
102 
103 	len = strlen(origin);
104 	isc_buffer_init(&b, origin, len);
105 	isc_buffer_add(&b, len);
106 
107 	name = dns_fixedname_initname(&fname);
108 	result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL);
109 	if (result != ISC_R_SUCCESS) {
110 		fatal("failed converting name '%s' to dns format: %s", origin,
111 		      isc_result_totext(result));
112 	}
113 
114 	result = dns_db_create(mctx, ZONEDB_DEFAULT, name, dns_dbtype_zone,
115 			       rdclass, 0, NULL, db);
116 	check_result(result, "dns_db_create()");
117 
118 	result = dns_db_load(*db, file, inputformat, 0);
119 	switch (result) {
120 	case DNS_R_SEENINCLUDE:
121 	case ISC_R_SUCCESS:
122 		break;
123 	case DNS_R_NOTZONETOP:
124 		/*
125 		 * Comparing pointers (vs. using strcmp()) is intentional: we
126 		 * want to check whether -o was supplied on the command line,
127 		 * not whether origin and file contain the same string.
128 		 */
129 		if (origin == file) {
130 			fatal("failed loading zone '%s' from file '%s': "
131 			      "use -o to specify a different zone origin",
132 			      origin, file);
133 		}
134 		FALLTHROUGH;
135 	default:
136 		fatal("failed loading zone from '%s': %s", file,
137 		      isc_result_totext(result));
138 	}
139 }
140 
141 noreturn static void
142 usage(void);
143 
144 static void
145 usage(void) {
146 	fprintf(stderr, "Usage:\n");
147 	fprintf(stderr, "\t%s [options] zonefile [keys]\n", program);
148 
149 	fprintf(stderr, "\n");
150 
151 	fprintf(stderr, "Version: %s\n", PACKAGE_VERSION);
152 
153 	fprintf(stderr, "Options: (default value in parenthesis) \n");
154 	fprintf(stderr, "\t-v debuglevel (0)\n");
155 	fprintf(stderr, "\t-q quiet\n");
156 	fprintf(stderr, "\t-V:\tprint version information\n");
157 	fprintf(stderr, "\t-o origin:\n");
158 	fprintf(stderr, "\t\tzone origin (name of zonefile)\n");
159 	fprintf(stderr, "\t-I format:\n");
160 	fprintf(stderr, "\t\tfile format of input zonefile (text)\n");
161 	fprintf(stderr, "\t-c class (IN)\n");
162 	fprintf(stderr, "\t-E engine:\n");
163 	fprintf(stderr, "\t\tname of an OpenSSL engine to use\n");
164 	fprintf(stderr, "\t-x:\tDNSKEY record signed with KSKs only, "
165 			"not ZSKs\n");
166 	fprintf(stderr, "\t-z:\tAll records signed with KSKs\n");
167 	exit(EXIT_SUCCESS);
168 }
169 
170 int
171 main(int argc, char *argv[]) {
172 	char *origin = NULL, *file = NULL;
173 	char *inputformatstr = NULL;
174 	isc_result_t result;
175 	isc_log_t *log = NULL;
176 	const char *engine = NULL;
177 	char *classname = NULL;
178 	dns_rdataclass_t rdclass;
179 	char *endp;
180 	int ch;
181 
182 #define CMDLINE_FLAGS "c:E:hJ:m:o:I:qv:Vxz"
183 
184 	/*
185 	 * Process memory debugging argument first.
186 	 */
187 	while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
188 		switch (ch) {
189 		case 'm':
190 			if (strcasecmp(isc_commandline_argument, "record") == 0)
191 			{
192 				isc_mem_debugging |= ISC_MEM_DEBUGRECORD;
193 			}
194 			if (strcasecmp(isc_commandline_argument, "trace") == 0)
195 			{
196 				isc_mem_debugging |= ISC_MEM_DEBUGTRACE;
197 			}
198 			if (strcasecmp(isc_commandline_argument, "usage") == 0)
199 			{
200 				isc_mem_debugging |= ISC_MEM_DEBUGUSAGE;
201 			}
202 			break;
203 		default:
204 			break;
205 		}
206 	}
207 	isc_commandline_reset = true;
208 
209 	isc_mem_create(&mctx);
210 
211 	isc_commandline_errprint = false;
212 
213 	while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
214 		switch (ch) {
215 		case 'c':
216 			classname = isc_commandline_argument;
217 			break;
218 
219 		case 'E':
220 			engine = isc_commandline_argument;
221 			break;
222 
223 		case 'I':
224 			inputformatstr = isc_commandline_argument;
225 			break;
226 
227 		case 'J':
228 			journal = 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(EXIT_FAILURE);
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 	now = isc_stdtime_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 	if (journal != NULL) {
325 		loadjournal(mctx, gdb, journal);
326 	}
327 	gorigin = dns_db_origin(gdb);
328 	gclass = dns_db_class(gdb);
329 
330 	gversion = NULL;
331 	result = dns_db_newversion(gdb, &gversion);
332 	check_result(result, "dns_db_newversion()");
333 
334 	result = dns_zoneverify_dnssec(NULL, gdb, gversion, gorigin, NULL, mctx,
335 				       ignore_kskflag, keyset_kskonly, report);
336 
337 	dns_db_closeversion(gdb, &gversion, false);
338 	dns_db_detach(&gdb);
339 
340 	cleanup_logging(&log);
341 	dst_lib_destroy();
342 	if (verbose > 10) {
343 		isc_mem_stats(mctx, stdout);
344 	}
345 	isc_mem_destroy(&mctx);
346 
347 	return result == ISC_R_SUCCESS ? 0 : 1;
348 }
349