1/*
2 * Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14 * PERFORMANCE OF THIS SOFTWARE.
15 */
16
17/* $Id: dnssec-verify.c,v 1.1.2.1 2011/03/16 06:37:51 each Exp $ */
18
19/*! \file */
20
21#include <config.h>
22
23#include <stdlib.h>
24#include <time.h>
25
26#include <isc/app.h>
27#include <isc/base32.h>
28#include <isc/commandline.h>
29#include <isc/entropy.h>
30#include <isc/event.h>
31#include <isc/file.h>
32#include <isc/hash.h>
33#include <isc/hex.h>
34#include <isc/mem.h>
35#include <isc/mutex.h>
36#include <isc/os.h>
37#include <isc/print.h>
38#include <isc/random.h>
39#include <isc/rwlock.h>
40#include <isc/serial.h>
41#include <isc/stdio.h>
42#include <isc/stdlib.h>
43#include <isc/string.h>
44#include <isc/time.h>
45#include <isc/util.h>
46
47#include <dns/db.h>
48#include <dns/dbiterator.h>
49#include <dns/diff.h>
50#include <dns/dnssec.h>
51#include <dns/ds.h>
52#include <dns/fixedname.h>
53#include <dns/keyvalues.h>
54#include <dns/log.h>
55#include <dns/master.h>
56#include <dns/masterdump.h>
57#include <dns/nsec.h>
58#include <dns/nsec3.h>
59#include <dns/rdata.h>
60#include <dns/rdatalist.h>
61#include <dns/rdataset.h>
62#include <dns/rdataclass.h>
63#include <dns/rdatasetiter.h>
64#include <dns/rdatastruct.h>
65#include <dns/rdatatype.h>
66#include <dns/result.h>
67#include <dns/soa.h>
68#include <dns/time.h>
69
70#include <dst/dst.h>
71
72#include "dnssectool.h"
73
74const char *program = "dnssec-verify";
75int verbose;
76
77static isc_stdtime_t now;
78static isc_mem_t *mctx = NULL;
79static isc_entropy_t *ectx = NULL;
80static dns_masterformat_t inputformat = dns_masterformat_text;
81static dns_db_t *gdb;			/* The database */
82static dns_dbversion_t *gversion;	/* The database version */
83static dns_rdataclass_t gclass;		/* The class */
84static dns_name_t *gorigin;		/* The database origin */
85static isc_boolean_t ignore_kskflag = ISC_FALSE;
86static isc_boolean_t keyset_kskonly = ISC_FALSE;
87
88/*%
89 * Load the zone file from disk
90 */
91static void
92loadzone(char *file, char *origin, dns_rdataclass_t rdclass, dns_db_t **db) {
93	isc_buffer_t b;
94	int len;
95	dns_fixedname_t fname;
96	dns_name_t *name;
97	isc_result_t result;
98
99	len = strlen(origin);
100	isc_buffer_init(&b, origin, len);
101	isc_buffer_add(&b, len);
102
103	dns_fixedname_init(&fname);
104	name = dns_fixedname_name(&fname);
105	result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL);
106	if (result != ISC_R_SUCCESS)
107		fatal("failed converting name '%s' to dns format: %s",
108		      origin, isc_result_totext(result));
109
110	result = dns_db_create(mctx, "rbt", name, dns_dbtype_zone,
111			       rdclass, 0, NULL, db);
112	check_result(result, "dns_db_create()");
113
114	result = dns_db_load2(*db, file, inputformat);
115	if (result != ISC_R_SUCCESS && result != DNS_R_SEENINCLUDE)
116		fatal("failed loading zone from '%s': %s",
117		      file, isc_result_totext(result));
118}
119
120ISC_PLATFORM_NORETURN_PRE static void
121usage(void) ISC_PLATFORM_NORETURN_POST;
122
123static void
124usage(void) {
125	fprintf(stderr, "Usage:\n");
126	fprintf(stderr, "\t%s [options] zonefile [keys]\n", program);
127
128	fprintf(stderr, "\n");
129
130	fprintf(stderr, "Version: %s\n", VERSION);
131
132	fprintf(stderr, "Options: (default value in parenthesis) \n");
133	fprintf(stderr, "\t-v debuglevel (0)\n");
134	fprintf(stderr, "\t-o origin:\n");
135	fprintf(stderr, "\t\tzone origin (name of zonefile)\n");
136	fprintf(stderr, "\t-I format:\n");
137	fprintf(stderr, "\t\tfile format of input zonefile (text)\n");
138	fprintf(stderr, "\t-c class (IN)\n");
139	fprintf(stderr, "\t-E engine:\n");
140#ifdef USE_PKCS11
141	fprintf(stderr, "\t\tname of an OpenSSL engine to use "
142				"(default is \"pkcs11\")\n");
143#else
144	fprintf(stderr, "\t\tname of an OpenSSL engine to use\n");
145#endif
146	fprintf(stderr, "\t-x:\tDNSKEY record signed with KSKs only, "
147			"not ZSKs\n");
148	fprintf(stderr, "\t-z:\tAll records signed with KSKs\n");
149	exit(0);
150}
151
152int
153main(int argc, char *argv[]) {
154	char *origin = NULL, *file = NULL;
155	char *inputformatstr = NULL;
156	isc_result_t result;
157	isc_log_t *log = NULL;
158#ifdef USE_PKCS11
159	const char *engine = "pkcs11";
160#else
161	const char *engine = NULL;
162#endif
163	char *classname = NULL;
164	dns_rdataclass_t rdclass;
165	char ch, *endp;
166
167#define CMDLINE_FLAGS \
168	"m:o:I:c:E:v:xz"
169
170	/*
171	 * Process memory debugging argument first.
172	 */
173	while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
174		switch (ch) {
175		case 'm':
176			if (strcasecmp(isc_commandline_argument, "record") == 0)
177				isc_mem_debugging |= ISC_MEM_DEBUGRECORD;
178			if (strcasecmp(isc_commandline_argument, "trace") == 0)
179				isc_mem_debugging |= ISC_MEM_DEBUGTRACE;
180			if (strcasecmp(isc_commandline_argument, "usage") == 0)
181				isc_mem_debugging |= ISC_MEM_DEBUGUSAGE;
182			if (strcasecmp(isc_commandline_argument, "size") == 0)
183				isc_mem_debugging |= ISC_MEM_DEBUGSIZE;
184			if (strcasecmp(isc_commandline_argument, "mctx") == 0)
185				isc_mem_debugging |= ISC_MEM_DEBUGCTX;
186			break;
187		default:
188			break;
189		}
190	}
191	isc_commandline_reset = ISC_TRUE;
192	check_result(isc_app_start(), "isc_app_start");
193
194	result = isc_mem_create(0, 0, &mctx);
195	if (result != ISC_R_SUCCESS)
196		fatal("out of memory");
197
198	dns_result_register();
199
200	isc_commandline_errprint = ISC_FALSE;
201
202	while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
203		switch (ch) {
204		case 'c':
205			classname = isc_commandline_argument;
206			break;
207
208		case 'E':
209			engine = isc_commandline_argument;
210			break;
211
212		case 'h':
213			usage();
214			break;
215
216		case 'I':
217			inputformatstr = isc_commandline_argument;
218			break;
219
220		case 'm':
221			break;
222
223		case 'o':
224			origin = isc_commandline_argument;
225			break;
226
227		case 'v':
228			endp = NULL;
229			verbose = strtol(isc_commandline_argument, &endp, 0);
230			if (*endp != '\0')
231				fatal("verbose level must be numeric");
232			break;
233
234		case 'x':
235			keyset_kskonly = ISC_TRUE;
236			break;
237
238		case 'z':
239			ignore_kskflag = ISC_TRUE;
240			break;
241
242		case '?':
243			if (isc_commandline_option != '?')
244				fprintf(stderr, "%s: invalid argument -%c\n",
245					program, isc_commandline_option);
246			usage();
247			break;
248
249		default:
250			fprintf(stderr, "%s: unhandled option -%c\n",
251				program, isc_commandline_option);
252			exit(1);
253		}
254	}
255
256	if (ectx == NULL)
257		setup_entropy(mctx, NULL, &ectx);
258
259	result = isc_hash_create(mctx, ectx, DNS_NAME_MAXWIRE);
260	if (result != ISC_R_SUCCESS)
261		fatal("could not create hash context");
262
263	result = dst_lib_init2(mctx, ectx, engine, ISC_ENTROPY_BLOCKING);
264	if (result != ISC_R_SUCCESS)
265		fatal("could not initialize dst: %s",
266		      isc_result_totext(result));
267
268	isc_stdtime_get(&now);
269
270	rdclass = strtoclass(classname);
271
272	setup_logging(verbose, mctx, &log);
273
274	argc -= isc_commandline_index;
275	argv += isc_commandline_index;
276
277	if (argc < 1)
278		usage();
279
280	file = argv[0];
281
282	argc -= 1;
283	argv += 1;
284
285	POST(argc);
286	POST(argv);
287
288	if (origin == NULL)
289		origin = file;
290
291	if (inputformatstr != NULL) {
292		if (strcasecmp(inputformatstr, "text") == 0)
293			inputformat = dns_masterformat_text;
294		else if (strcasecmp(inputformatstr, "raw") == 0)
295			inputformat = dns_masterformat_raw;
296		else
297			fatal("unknown file format: %s\n", inputformatstr);
298	}
299
300	gdb = NULL;
301	fprintf(stderr, "Loading zone '%s' from file '%s'\n", origin, file);
302	loadzone(file, origin, rdclass, &gdb);
303	gorigin = dns_db_origin(gdb);
304	gclass = dns_db_class(gdb);
305
306	gversion = NULL;
307	result = dns_db_newversion(gdb, &gversion);
308	check_result(result, "dns_db_newversion()");
309
310	verifyzone(gdb, gversion, gorigin, mctx,
311		   ignore_kskflag, keyset_kskonly);
312
313	dns_db_closeversion(gdb, &gversion, ISC_FALSE);
314	dns_db_detach(&gdb);
315
316	cleanup_logging(&log);
317	dst_lib_destroy();
318	isc_hash_destroy();
319	cleanup_entropy(&ectx);
320	dns_name_destroy();
321	if (verbose > 10)
322		isc_mem_stats(mctx, stdout);
323	isc_mem_destroy(&mctx);
324
325	(void) isc_app_finish();
326
327	return (0);
328}
329