dnssec-makekeyset.c 11.3 KB
Newer Older
1
/*
Mark Andrews's avatar
Mark Andrews committed
2
 * Portions Copyright (C) 2004, 2005  Internet Systems Consortium, Inc. ("ISC")
Mark Andrews's avatar
Mark Andrews committed
3
 * Portions Copyright (C) 2000-2003  Internet Software Consortium.
4
 * Portions Copyright (C) 1995-2000 by Network Associates, Inc.
5
 *
6
7
8
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
9
 *
Mark Andrews's avatar
Mark Andrews committed
10
11
12
13
14
15
16
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
 * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
18
 */

Mark Andrews's avatar
Mark Andrews committed
19
/* $Id: dnssec-makekeyset.c,v 1.65 2005/04/29 00:22:25 marka Exp $ */
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

/*! \file */

/**
 * \brief   dnssec-makekeyset - DNSSEC zone signing tool
 * 
 * \section dnssec-makekeyset-synopsis SYNOPSIS
 * \par
 * 	dnssec-makekeyset [ -a ]  [ -s start-time ]  [ -e end-time ]  [ -h ]  [
 *        -p ]  [ -r randomdev ]  [ -tttl ]  [ -v level ]  key...
 * 
 * \section dnssec-makekeyset-description DESCRIPTION
 * \par
 *        dnssec-makekeyset generates a key set from one or more keys created  by
 *        dnssec-keygen.  It creates a file containing a KEY record for each key,
 *        and self-signs the key set with each zone key. The output  file  is  of
 *        the form keyset-nnnn., where nnnn is the zone name.
 * 
 * \link org.isc.doc.0037 More ... \endlink
 */

/** \page org.isc.doc.0037 dnssec-makekeyset
 *  \htmlinclude org.isc.doc.0037.html
 */
David Lawrence's avatar
David Lawrence committed
44

45
46
47
48
49
#include <config.h>

#include <stdlib.h>

#include <isc/commandline.h>
Brian Wellington's avatar
Brian Wellington committed
50
#include <isc/entropy.h>
51
#include <isc/mem.h>
52
#include <isc/print.h>
53
#include <isc/string.h>
Brian Wellington's avatar
Brian Wellington committed
54
#include <isc/util.h>
55
56

#include <dns/db.h>
57
#include <dns/diff.h>
58
59
60
#include <dns/dnssec.h>
#include <dns/fixedname.h>
#include <dns/log.h>
61
62
63
#include <dns/rdata.h>
#include <dns/rdataset.h>
#include <dns/result.h>
64
#include <dns/secalg.h>
65
66
#include <dns/time.h>

67
68
69
#include <dst/dst.h>

#include "dnssectool.h"
70

David Lawrence's avatar
David Lawrence committed
71
const char *program = "dnssec-makekeyset";
72
73
int verbose;

74
75
76
77
78
79
80
81
82
83
84
typedef struct keynode keynode_t;
struct keynode {
	dst_key_t *key;
	ISC_LINK(keynode_t) link;
};
typedef ISC_LIST(keynode_t) keylist_t;

static isc_stdtime_t starttime = 0, endtime = 0, now;
static int ttl = -1;

static isc_mem_t *mctx = NULL;
Brian Wellington's avatar
Brian Wellington committed
85
static isc_entropy_t *ectx = NULL;
86
87

static keylist_t keylist;
88

89
static void
90
usage(void) {
91
	fprintf(stderr, "Usage:\n");
92
	fprintf(stderr, "\t%s [options] keys\n", program);
93
94
95

	fprintf(stderr, "\n");

96
97
	fprintf(stderr, "Version: %s\n", VERSION);

98
	fprintf(stderr, "Options: (default value in parenthesis) \n");
99
100
	fprintf(stderr, "\t-a\n");
	fprintf(stderr, "\t\tverify generated signatures\n");
101
102
103
	fprintf(stderr, "\t-s YYYYMMDDHHMMSS|+offset:\n");
	fprintf(stderr, "\t\tSIG start time - absolute|offset (now)\n");
	fprintf(stderr, "\t-e YYYYMMDDHHMMSS|+offset|\"now\"+offset]:\n");
104
105
	fprintf(stderr, "\t\tSIG end time  - "
			     "absolute|from start|from now (now + 30 days)\n");
106
	fprintf(stderr, "\t-t ttl\n");
107
108
	fprintf(stderr, "\t-p\n");
	fprintf(stderr, "\t\tuse pseudorandom data (faster but less secure)\n");
109
110
	fprintf(stderr, "\t-r randomdev:\n");
	fprintf(stderr, "\t\ta file containing random data\n");
111
112
113
114
115
	fprintf(stderr, "\t-v level:\n");
	fprintf(stderr, "\t\tverbose level (0)\n");

	fprintf(stderr, "\n");

116
	fprintf(stderr, "keys:\n");
117
	fprintf(stderr, "\tkeyfile (Kname+alg+tag)\n");
118
119
120
121
122

	fprintf(stderr, "\n");

	fprintf(stderr, "Output:\n");
	fprintf(stderr, "\tkeyset (keyset-<name>)\n");
123
124
125
	exit(0);
}

126
127
128
129
130
131
132
133
134
135
136
137
138
static isc_boolean_t
zonekey_on_list(dst_key_t *key) {
	keynode_t *keynode;
	for (keynode = ISC_LIST_HEAD(keylist);
	     keynode != NULL;
	     keynode = ISC_LIST_NEXT(keynode, link))
	{
		if (dst_key_compare(keynode->key, key))
			return (ISC_TRUE);
	}
	return (ISC_FALSE);
}

139
140
141
142
143
int
main(int argc, char *argv[]) {
	int i, ch;
	char *startstr = NULL, *endstr = NULL;
	dns_fixedname_t fdomain;
144
	dns_name_t *domain = NULL;
145
146
	char *output = NULL;
	char *endp;
147
	unsigned char data[65536];
148
149
	dns_db_t *db;
	dns_dbversion_t *version;
150
151
152
	dns_diff_t diff;
	dns_difftuple_t *tuple;
	dns_fixedname_t tname;
153
	dst_key_t *key = NULL;
154
155
156
	dns_rdata_t rdata = DNS_RDATA_INIT;
	dns_rdataset_t rdataset;
	dns_rdataclass_t rdclass;
157
158
159
160
161
	isc_result_t result;
	isc_buffer_t b;
	isc_region_t r;
	isc_log_t *log = NULL;
	keynode_t *keynode;
162
163
	unsigned int eflags;
	isc_boolean_t pseudorandom = ISC_FALSE;
164
	isc_boolean_t tryverify = ISC_FALSE;
165
166

	result = isc_mem_create(0, 0, &mctx);
167
168
169
	if (result != ISC_R_SUCCESS)
		fatal("failed to create memory context: %s",
		      isc_result_totext(result));
170

Brian Wellington's avatar
Brian Wellington committed
171
172
	dns_result_register();

173
	while ((ch = isc_commandline_parse(argc, argv, "as:e:t:r:v:ph")) != -1)
174
175
	{
		switch (ch) {
176
177
178
		case 'a':
			tryverify = ISC_TRUE;
			break;
179
		case 's':
180
			startstr = isc_commandline_argument;
181
182
183
			break;

		case 'e':
184
			endstr = isc_commandline_argument;
185
186
187
188
189
190
			break;

		case 't':
			endp = NULL;
			ttl = strtol(isc_commandline_argument, &endp, 0);
			if (*endp != '\0')
191
				fatal("TTL must be numeric");
192
193
			break;

194
		case 'r':
195
			setup_entropy(mctx, isc_commandline_argument, &ectx);
196
197
			break;

198
199
200
201
		case 'v':
			endp = NULL;
			verbose = strtol(isc_commandline_argument, &endp, 0);
			if (*endp != '\0')
202
				fatal("verbose level must be numeric");
203
204
			break;

205
206
207
208
		case 'p':
			pseudorandom = ISC_TRUE;
			break;

Brian Wellington's avatar
Brian Wellington committed
209
		case 'h':
210
211
212
213
214
215
216
217
218
		default:
			usage();

		}
	}

	argc -= isc_commandline_index;
	argv += isc_commandline_index;

219
	if (argc < 1)
220
221
		usage();

222
223
	if (ectx == NULL)
		setup_entropy(mctx, NULL, &ectx);
224
225
226
227
	eflags = ISC_ENTROPY_BLOCKING;
	if (!pseudorandom)
		eflags |= ISC_ENTROPY_GOODONLY;
	result = dst_lib_init(mctx, ectx, eflags);
Brian Wellington's avatar
Brian Wellington committed
228
	if (result != ISC_R_SUCCESS)
229
230
		fatal("could not initialize dst: %s", 
		      isc_result_totext(result));
Brian Wellington's avatar
Brian Wellington committed
231

232
233
	isc_stdtime_get(&now);

234
	if (startstr != NULL)
235
236
237
238
		starttime = strtotime(startstr, now, now);
	else
		starttime = now;

239
	if (endstr != NULL)
240
241
242
243
244
245
		endtime = strtotime(endstr, now, starttime);
	else
		endtime = starttime + (30 * 24 * 60 * 60);

	if (ttl == -1) {
		ttl = 3600;
246
		fprintf(stderr, "%s: TTL not specified, assuming 3600\n",
247
			program);
248
249
	}

250
	setup_logging(verbose, mctx, &log);
251

252
253
	dns_diff_init(mctx, &diff);
	rdclass = 0;
254
255
256
257

	ISC_LIST_INIT(keylist);

	for (i = 0; i < argc; i++) {
258
259
260
		char namestr[DNS_NAME_FORMATSIZE];
		isc_buffer_t namebuf;

Brian Wellington's avatar
Brian Wellington committed
261
262
263
		key = NULL;
		result = dst_key_fromnamedfile(argv[i], DST_TYPE_PUBLIC,
					       mctx, &key);
264
		if (result != ISC_R_SUCCESS)
265
266
			fatal("error loading key from %s: %s", argv[i],
			      isc_result_totext(result));
267
268
		if (rdclass == 0)
			rdclass = dst_key_class(key);
Brian Wellington's avatar
Brian Wellington committed
269

Andreas Gustafsson's avatar
Andreas Gustafsson committed
270
		isc_buffer_init(&namebuf, namestr, sizeof(namestr));
271
		result = dns_name_tofilenametext(dst_key_name(key),
272
273
274
						 ISC_FALSE,
						 &namebuf);
		check_result(result, "dns_name_tofilenametext");
275
		isc_buffer_putuint8(&namebuf, 0);
276
277
278
279
280

		if (domain == NULL) {
			dns_fixedname_init(&fdomain);
			domain = dns_fixedname_name(&fdomain);
			dns_name_copy(dst_key_name(key), domain, NULL);
Brian Wellington's avatar
typo    
Brian Wellington committed
281
		} else if (!dns_name_equal(domain, dst_key_name(key))) {
282
			char str[DNS_NAME_FORMATSIZE];
Andreas Gustafsson's avatar
Andreas Gustafsson committed
283
			dns_name_format(domain, str, sizeof(str));
284
285
			fatal("all keys must have the same owner - %s "
			      "and %s do not match", str, namestr);
286
		}
287

288
289
		if (output == NULL) {
			output = isc_mem_allocate(mctx,
290
						  strlen("keyset-") +
291
						  strlen(namestr) + 1);
292
			if (output == NULL)
293
				fatal("out of memory");
294
			sprintf(output, "keyset-%s", namestr);
295
		}
296

297
298
		if (dst_key_iszonekey(key)) {
			dst_key_t *zonekey = NULL;
Brian Wellington's avatar
Brian Wellington committed
299
			result = dst_key_fromnamedfile(argv[i],
300
						       DST_TYPE_PUBLIC |
Brian Wellington's avatar
Brian Wellington committed
301
302
						       DST_TYPE_PRIVATE,
						       mctx, &zonekey);
303
			if (result != ISC_R_SUCCESS)
Brian Wellington's avatar
Brian Wellington committed
304
				fatal("failed to read private key %s: %s",
Brian Wellington's avatar
Brian Wellington committed
305
				      argv[i], isc_result_totext(result));
306
			if (!zonekey_on_list(zonekey)) {
Andreas Gustafsson's avatar
Andreas Gustafsson committed
307
				keynode = isc_mem_get(mctx, sizeof(keynode_t));
308
309
310
311
312
313
				if (keynode == NULL)
					fatal("out of memory");
				keynode->key = zonekey;
				ISC_LIST_INITANDAPPEND(keylist, keynode, link);
			} else
				dst_key_free(&zonekey);
314
		}
315
316
		dns_rdata_reset(&rdata);
		isc_buffer_init(&b, data, sizeof(data));
317
		result = dst_key_todns(key, &b);
318
		dst_key_free(&key);
319
		if (result != ISC_R_SUCCESS)
Brian Wellington's avatar
Brian Wellington committed
320
321
			fatal("failed to convert key %s to a DNS KEY: %s",
			      argv[i], isc_result_totext(result));
Bob Halley's avatar
Bob Halley committed
322
		isc_buffer_usedregion(&b, &r);
323
		dns_rdata_fromregion(&rdata, rdclass, dns_rdatatype_dnskey, &r);
324
325
326
327
328
		tuple = NULL;
		result = dns_difftuple_create(mctx, DNS_DIFFOP_ADD,
					      domain, ttl, &rdata, &tuple);
		check_result(result, "dns_difftuple_create");
		dns_diff_append(&diff, &tuple);
329
330
	}

331
332
333
334
335
336
337
338
	db = NULL;
	result = dns_db_create(mctx, "rbt", dns_rootname, dns_dbtype_zone,
			       rdclass, 0, NULL, &db);
	if (result != ISC_R_SUCCESS)
		fatal("failed to create a database");

	version = NULL;
	dns_db_newversion(db, &version);
339

340
341
342
343
344
345
	result = dns_diff_apply(&diff, db, version);
	check_result(result, "dns_diff_apply");
	dns_diff_clear(&diff);

	dns_fixedname_init(&tname);
	dns_rdataset_init(&rdataset);
346
	result = dns_db_find(db, domain, version, dns_rdatatype_dnskey, 0, 0,
347
348
349
			     NULL, dns_fixedname_name(&tname), &rdataset,
			     NULL);
	check_result(result, "dns_db_find");
350
351
352

	if (ISC_LIST_EMPTY(keylist))
		fprintf(stderr,
353
			"%s: no private zone key found; not self-signing\n",
354
			program);
355
356
357
358
	for (keynode = ISC_LIST_HEAD(keylist);
	     keynode != NULL;
	     keynode = ISC_LIST_NEXT(keynode, link))
	{
359
360
		dns_rdata_reset(&rdata);
		isc_buffer_init(&b, data, sizeof(data));
361
		result = dns_dnssec_sign(domain, &rdataset, keynode->key,
362
					 &starttime, &endtime, mctx, &b,
363
					 &rdata);
364
		isc_entropy_stopcallbacksources(ectx);
365
366
		if (result != ISC_R_SUCCESS) {
			char keystr[KEY_FORMATSIZE];
Andreas Gustafsson's avatar
Andreas Gustafsson committed
367
			key_format(keynode->key, keystr, sizeof(keystr));
368
369
370
			fatal("failed to sign keyset with key %s: %s",
			      keystr, isc_result_totext(result));
		}
371
372
373
		if (tryverify) {
			result = dns_dnssec_verify(domain, &rdataset,
						   keynode->key, ISC_TRUE,
374
						   mctx, &rdata);
375
376
			if (result != ISC_R_SUCCESS) {
				char keystr[KEY_FORMATSIZE];
Andreas Gustafsson's avatar
Andreas Gustafsson committed
377
				key_format(keynode->key, keystr, sizeof(keystr));
378
379
380
381
382
				fatal("signature from key '%s' failed to "
				      "verify: %s",
				      keystr, isc_result_totext(result));
			}
		}
383
384
385
386
387
		tuple = NULL;
		result = dns_difftuple_create(mctx, DNS_DIFFOP_ADD,
					      domain, ttl, &rdata, &tuple);
		check_result(result, "dns_difftuple_create");
		dns_diff_append(&diff, &tuple);
388
	}
389

390
391
392
	result = dns_diff_apply(&diff, db, version);
	check_result(result, "dns_diff_apply");
	dns_diff_clear(&diff);
393

394
	dns_rdataset_disassociate(&rdataset);
395
396
397

	dns_db_closeversion(db, &version, ISC_TRUE);
	result = dns_db_dump(db, version, output);
398
399
	if (result != ISC_R_SUCCESS) {
		char domainstr[DNS_NAME_FORMATSIZE];
Andreas Gustafsson's avatar
Andreas Gustafsson committed
400
		dns_name_format(domain, domainstr, sizeof(domainstr));
401
		fatal("failed to write database for %s to %s",
402
403
		      domainstr, output);
	}
404

405
406
	printf("%s\n", output);

407
408
409
410
411
	dns_db_detach(&db);

	while (!ISC_LIST_EMPTY(keylist)) {
		keynode = ISC_LIST_HEAD(keylist);
		ISC_LIST_UNLINK(keylist, keynode, link);
412
		dst_key_free(&keynode->key);
413
414
415
		isc_mem_put(mctx, keynode, sizeof(keynode_t));
	}

Brian Wellington's avatar
Brian Wellington committed
416
	cleanup_logging(&log);
Brian Wellington's avatar
Brian Wellington committed
417
	cleanup_entropy(&ectx);
418
419

	isc_mem_free(mctx, output);
Brian Wellington's avatar
Brian Wellington committed
420
	dst_lib_destroy();
421
422
	if (verbose > 10)
		isc_mem_stats(mctx, stdout);
423
424
425
	isc_mem_destroy(&mctx);
	return (0);
}