dnssec-keygen.c 25.4 KB
Newer Older
Brian Wellington's avatar
Brian Wellington committed
1
/*
Automatic Updater's avatar
Automatic Updater committed
2
 * Portions Copyright (C) 2004-2009  Internet Systems Consortium, Inc. ("ISC")
Mark Andrews's avatar
Mark Andrews committed
3
 * Portions Copyright (C) 1999-2003  Internet Software Consortium.
Automatic Updater's avatar
Automatic Updater committed
4
5
6
7
8
9
10
11
12
13
14
15
16
 *
 * Permission to use, copy, modify, and/or 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.
 *
 * 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
 * Portions Copyright (C) 1995-2000 by Network Associates, Inc.
18
 *
Automatic Updater's avatar
Automatic Updater committed
19
 * Permission to use, copy, modify, and/or distribute this software for any
Brian Wellington's avatar
Brian Wellington committed
20
21
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
22
 *
Mark Andrews's avatar
Mark Andrews committed
23
24
25
26
27
28
29
 * 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.
Brian Wellington's avatar
Brian Wellington committed
30
31
 */

32
/* $Id: dnssec-keygen.c,v 1.106 2009/10/28 00:27:10 marka Exp $ */
33
34

/*! \file */
Brian Wellington's avatar
Brian Wellington committed
35
36
37

#include <config.h>

38
#include <ctype.h>
Brian Wellington's avatar
Brian Wellington committed
39
#include <stdlib.h>
40
#include <unistd.h>
Brian Wellington's avatar
Brian Wellington committed
41

42
#include <isc/buffer.h>
43
#include <isc/commandline.h>
Brian Wellington's avatar
Brian Wellington committed
44
#include <isc/entropy.h>
Brian Wellington's avatar
Brian Wellington committed
45
#include <isc/mem.h>
46
47
#include <isc/region.h>
#include <isc/string.h>
Bob Halley's avatar
Bob Halley committed
48
49
#include <isc/util.h>

50
#include <dns/fixedname.h>
Brian Wellington's avatar
Brian Wellington committed
51
#include <dns/keyvalues.h>
52
#include <dns/log.h>
53
#include <dns/name.h>
54
#include <dns/rdataclass.h>
55
#include <dns/result.h>
Brian Wellington's avatar
Brian Wellington committed
56
#include <dns/secalg.h>
57

Brian Wellington's avatar
Brian Wellington committed
58
59
#include <dst/dst.h>

60
#include "dnssectool.h"
Brian Wellington's avatar
Brian Wellington committed
61

62
#define MAX_RSA 4096 /* should be long enough... */
63

David Lawrence's avatar
David Lawrence committed
64
const char *program = "dnssec-keygen";
65
int verbose;
66

67
68
#define DEFAULT_ALGORITHM "RSASHA1"
#define DEFAULT_NSEC3_ALGORITHM "NSEC3RSASHA1"
69

70
71
72
73
74
static isc_boolean_t
dsa_size_ok(int size) {
	return (ISC_TF(size >= 512 && size <= 1024 && size % 64 == 0));
}

Francis Dupont's avatar
Francis Dupont committed
75
76
77
ISC_PLATFORM_NORETURN_PRE static void
usage(void) ISC_PLATFORM_NORETURN_POST;

78
79
static void progress(int p);

80
static void
81
usage(void) {
82
	fprintf(stderr, "Usage:\n");
83
	fprintf(stderr, "    %s [options] name\n\n", program);
84
	fprintf(stderr, "Version: %s\n", VERSION);
85
86
	fprintf(stderr, "    name: owner of the key\n");
	fprintf(stderr, "Options:\n");
87
88
	fprintf(stderr, "    -K <directory>: write keys into directory\n");
	fprintf(stderr, "    -a <algorithm>:\n");
89
90
91
	fprintf(stderr, "        RSA | RSAMD5 | DSA | RSASHA1 | NSEC3RSASHA1"
				" | NSEC3DSA |\n");
	fprintf(stderr, "        RSASHA256 | RSASHA512 |\n");
92
93
94
95
96
97
98
99
100
101
	fprintf(stderr, "        DH | HMAC-MD5 | HMAC-SHA1 | HMAC-SHA224 | "
				"HMAC-SHA256 | \n");
	fprintf(stderr, "        HMAC-SHA384 | HMAC-SHA512\n");
	fprintf(stderr, "       (default: RSASHA1, or "
			       "NSEC3RSASHA1 if using -3)\n");
	fprintf(stderr, "    -3: use NSEC3-capable algorithm\n");
	fprintf(stderr, "    -b <key size in bits>:\n");
	fprintf(stderr, "	 RSAMD5:\t[512..%d]\n", MAX_RSA);
	fprintf(stderr, "	 RSASHA1:\t[512..%d]\n", MAX_RSA);
	fprintf(stderr, "	 NSEC3RSASHA1:\t[512..%d]\n", MAX_RSA);
102
103
	fprintf(stderr, "	 RSASHA256:\t[512..%d]\n", MAX_RSA);
	fprintf(stderr, "	 RSASHA512:\t[1024..%d]\n", MAX_RSA);
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
	fprintf(stderr, "	 DH:\t\t[128..4096]\n");
	fprintf(stderr, "	 DSA:\t\t[512..1024] and divisible by 64\n");
	fprintf(stderr, "	 NSEC3DSA:\t[512..1024] and divisible "
				"by 64\n");
	fprintf(stderr, "	 HMAC-MD5:\t[1..512]\n");
	fprintf(stderr, "	 HMAC-SHA1:\t[1..160]\n");
	fprintf(stderr, "	 HMAC-SHA224:\t[1..224]\n");
	fprintf(stderr, "	 HMAC-SHA256:\t[1..256]\n");
	fprintf(stderr, "	 HMAC-SHA384:\t[1..384]\n");
	fprintf(stderr, "	 HMAC-SHA512:\t[1..512]\n");
	fprintf(stderr, "        (if using the default algorithm, key size\n"
			"        defaults to 2048 for KSK, or 1024 for all "
			"others)\n");
	fprintf(stderr, "    -n <nametype>: ZONE | HOST | ENTITY | "
					    "USER | OTHER\n");
	fprintf(stderr, "	 (DNSKEY generation defaults to ZONE)\n");
	fprintf(stderr, "    -c <class>: (default: IN)\n");
121
	fprintf(stderr, "    -d <digest bits> (0 => max, default)\n");
Francis Dupont's avatar
Francis Dupont committed
122
123
124
125
126
#ifdef USE_PKCS11
	fprintf(stderr, "    -E <engine name> (default \"pkcs11\")\n");
#else
	fprintf(stderr, "    -E <engine name>\n");
#endif
127
128
129
130
131
132
133
134
135
	fprintf(stderr, "    -e: use large exponent (RSAMD5/RSASHA1 only)\n");
	fprintf(stderr, "    -f <keyflag>: KSK | REVOKE\n");
	fprintf(stderr, "    -g <generator>: use specified generator "
			"(DH only)\n");
	fprintf(stderr, "    -p <protocol>: (default: 3 [dnssec])\n");
	fprintf(stderr, "    -s <strength>: strength value this key signs DNS "
			"records with (default: 0)\n");
	fprintf(stderr, "    -T <rrtype>: DNSKEY | KEY (default: DNSKEY; "
			"use KEY for SIG(0))\n");
Brian Wellington's avatar
Brian Wellington committed
136
	fprintf(stderr, "    -t <type>: "
137
138
			"AUTHCONF | NOAUTHCONF | NOAUTH | NOCONF "
			"(default: AUTHCONF)\n");
Brian Wellington's avatar
Brian Wellington committed
139
	fprintf(stderr, "    -r <randomdev>: a file containing random data\n");
140
141
142
143
144
145

	fprintf(stderr, "    -h: print usage and exit\n");
	fprintf(stderr, "    -m <memory debugging mode>:\n");
	fprintf(stderr, "	usage | trace | record | size | mctx\n");
	fprintf(stderr, "    -v <level>: set verbosity level (0 - 10)\n");
	fprintf(stderr, "Date options:\n");
146
147
148
149
	fprintf(stderr, "    -P date/[+-]offset: set key publication date "
						"(default: now)\n");
	fprintf(stderr, "    -A date/[+-]offset: set key activation date "
						"(default: now)\n");
150
	fprintf(stderr, "    -R date/[+-]offset: set key revocation date\n");
151
	fprintf(stderr, "    -I date/[+-]offset: set key inactivation date\n");
152
	fprintf(stderr, "    -D date/[+-]offset: set key deletion date\n");
153
	fprintf(stderr, "    -G: generate key only; do not set -P or -A\n");
154
	fprintf(stderr, "    -C: generate a backward-compatible key, omitting "
155
			"all dates\n");
156
157
	fprintf(stderr, "Output:\n");
	fprintf(stderr, "     K<name>+<alg>+<id>.key, "
158
			"K<name>+<alg>+<id>.private\n");
159
160
161
162

	exit (-1);
}

163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
static void
progress(int p)
{
	char c = '*';

	switch (p) {
	case 0:
		c = '.';
		break;
	case 1:
		c = '+';
		break;
	case 2:
		c = '*';
		break;
	case 3:
179
		c = ' ';
180
181
182
183
184
185
186
187
		break;
	default:
		break;
	}
	(void) putc(c, stderr);
	(void) fflush(stderr);
}

Brian Wellington's avatar
Brian Wellington committed
188
189
int
main(int argc, char **argv) {
190
	char	        *algname = NULL, *nametype = NULL, *type = NULL;
191
	char		*classname = NULL;
Brian Wellington's avatar
Brian Wellington committed
192
	char		*endp;
193
	dst_key_t	*key = NULL, *oldkey;
194
195
	dns_fixedname_t	fname;
	dns_name_t	*name;
196
	isc_uint16_t	flags = 0, kskflag = 0, revflag = 0;
197
	dns_secalg_t	alg;
Brian Wellington's avatar
Brian Wellington committed
198
	isc_boolean_t	conflict = ISC_FALSE, null_key = ISC_FALSE;
199
	isc_boolean_t	oldstyle = ISC_FALSE;
200
201
202
203
204
	isc_mem_t	*mctx = NULL;
	int		ch, rsa_exp = 0, generator = 0, param = 0;
	int		protocol = -1, size = -1, signatory = 0;
	isc_result_t	ret;
	isc_textregion_t r;
205
	char		filename[255];
206
	const char	*directory = NULL;
207
	isc_buffer_t	buf;
208
	isc_log_t	*log = NULL;
Brian Wellington's avatar
Brian Wellington committed
209
	isc_entropy_t	*ectx = NULL;
Francis Dupont's avatar
Francis Dupont committed
210
211
212
213
214
#ifdef USE_PKCS11
	const char	*engine = "pkcs11";
#else
	const char	*engine = NULL;
#endif
215
	dns_rdataclass_t rdclass;
216
	int		options = DST_TYPE_PRIVATE | DST_TYPE_PUBLIC;
217
	int		dbits = 0;
218
219
	isc_boolean_t	use_default = ISC_FALSE, use_nsec3 = ISC_FALSE;
	isc_stdtime_t	publish = 0, activate = 0, revoke = 0;
220
	isc_stdtime_t	inactive = 0, delete = 0;
221
	isc_stdtime_t	now;
222
	isc_boolean_t	setpub = ISC_FALSE, setact = ISC_FALSE;
223
	isc_boolean_t	setrev = ISC_FALSE, setinact = ISC_FALSE;
224
225
	isc_boolean_t	setdel = ISC_FALSE;
	isc_boolean_t	unsetpub = ISC_FALSE, unsetact = ISC_FALSE;
226
	isc_boolean_t	unsetrev = ISC_FALSE, unsetinact = ISC_FALSE;
227
	isc_boolean_t	unsetdel = ISC_FALSE;
228
	isc_boolean_t	genonly = ISC_FALSE;
229
230
	isc_boolean_t	quiet = ISC_FALSE;
	isc_boolean_t	show_progress = ISC_FALSE;
Brian Wellington's avatar
Brian Wellington committed
231

Brian Wellington's avatar
Brian Wellington committed
232
	if (argc == 1)
233
		usage();
Brian Wellington's avatar
Brian Wellington committed
234

235
236
	dns_result_register();

237
238
	isc_commandline_errprint = ISC_FALSE;

239
240
241
	/*
	 * Process memory debugging argument first.
	 */
242
#define CMDLINE_FLAGS "3a:b:Cc:d:E:eFf:g:K:km:n:p:qr:s:T:t:v:hGP:A:R:I:D:"
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
	while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
		switch (ch) {
		case 'm':
			if (strcasecmp(isc_commandline_argument, "record") == 0)
				isc_mem_debugging |= ISC_MEM_DEBUGRECORD;
			if (strcasecmp(isc_commandline_argument, "trace") == 0)
				isc_mem_debugging |= ISC_MEM_DEBUGTRACE;
			if (strcasecmp(isc_commandline_argument, "usage") == 0)
				isc_mem_debugging |= ISC_MEM_DEBUGUSAGE;
			if (strcasecmp(isc_commandline_argument, "size") == 0)
				isc_mem_debugging |= ISC_MEM_DEBUGSIZE;
			if (strcasecmp(isc_commandline_argument, "mctx") == 0)
				isc_mem_debugging |= ISC_MEM_DEBUGCTX;
			break;
		default:
			break;
		}
	}
	isc_commandline_reset = ISC_TRUE;

	RUNTIME_CHECK(isc_mem_create(0, 0, &mctx) == ISC_R_SUCCESS);

	isc_stdtime_get(&now);

	while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
Brian Wellington's avatar
Brian Wellington committed
268
	    switch (ch) {
269
270
271
		case '3':
			use_nsec3 = ISC_TRUE;
			break;
Brian Wellington's avatar
Brian Wellington committed
272
		case 'a':
273
			algname = isc_commandline_argument;
Brian Wellington's avatar
Brian Wellington committed
274
			break;
Brian Wellington's avatar
Brian Wellington committed
275
276
277
		case 'b':
			size = strtol(isc_commandline_argument, &endp, 10);
			if (*endp != '\0' || size < 0)
278
				fatal("-b requires a non-negative number");
Brian Wellington's avatar
Brian Wellington committed
279
			break;
280
281
282
		case 'C':
			oldstyle = ISC_TRUE;
			break;
283
284
285
		case 'c':
			classname = isc_commandline_argument;
			break;
286
287
288
289
290
		case 'd':
			dbits = strtol(isc_commandline_argument, &endp, 10);
			if (*endp != '\0' || dbits < 0)
				fatal("-d requires a non-negative number");
			break;
Francis Dupont's avatar
Francis Dupont committed
291
292
293
		case 'E':
			engine = isc_commandline_argument;
			break;
Brian Wellington's avatar
Brian Wellington committed
294
295
		case 'e':
			rsa_exp = 1;
Brian Wellington's avatar
Brian Wellington committed
296
			break;
297
		case 'f':
298
299
300
301
			if (toupper(isc_commandline_argument[0]) == 'K')
				kskflag = DNS_KEYFLAG_KSK;
			else if (toupper(isc_commandline_argument[0]) == 'R')
				revflag = DNS_KEYFLAG_REVOKE;
302
303
304
305
			else
				fatal("unknown flag '%s'",
				      isc_commandline_argument);
			break;
306
		case 'g':
307
308
			generator = strtol(isc_commandline_argument,
					   &endp, 10);
Brian Wellington's avatar
Brian Wellington committed
309
			if (*endp != '\0' || generator <= 0)
310
				fatal("-g requires a positive number");
Brian Wellington's avatar
Brian Wellington committed
311
			break;
312
313
		case 'K':
			directory = isc_commandline_argument;
314
315
			ret = try_dir(directory);
			if (ret != ISC_R_SUCCESS)
Evan Hunt's avatar
Evan Hunt committed
316
				fatal("cannot open directory %s: %s",
317
				      directory, isc_result_totext(ret));
318
			break;
319
		case 'k':
320
321
322
			fatal("The -k option has been deprecated.\n"
			      "To generate a key-signing key, use -f KSK.\n"
			      "To generate a key with TYPE=KEY, use -T KEY.\n");
323
			break;
Brian Wellington's avatar
Brian Wellington committed
324
		case 'n':
325
			nametype = isc_commandline_argument;
Brian Wellington's avatar
Brian Wellington committed
326
			break;
327
		case 'm':
328
			break;
Brian Wellington's avatar
Brian Wellington committed
329
		case 'p':
Brian Wellington's avatar
Brian Wellington committed
330
331
			protocol = strtol(isc_commandline_argument, &endp, 10);
			if (*endp != '\0' || protocol < 0 || protocol > 255)
332
333
				fatal("-p must be followed by a number "
				      "[0..255]");
Brian Wellington's avatar
Brian Wellington committed
334
			break;
335
336
337
		case 'q':
			quiet = ISC_TRUE;
			break;
338
339
340
		case 'r':
			setup_entropy(mctx, isc_commandline_argument, &ectx);
			break;
Brian Wellington's avatar
Brian Wellington committed
341
		case 's':
342
343
			signatory = strtol(isc_commandline_argument,
					   &endp, 10);
Brian Wellington's avatar
Brian Wellington committed
344
			if (*endp != '\0' || signatory < 0 || signatory > 15)
345
346
				fatal("-s must be followed by a number "
				      "[0..15]");
Brian Wellington's avatar
Brian Wellington committed
347
			break;
348
349
350
351
352
353
354
355
356
357
358
359
360
		case 'T':
			if (strcasecmp(isc_commandline_argument, "KEY") == 0)
				options |= DST_TYPE_KEY;
			else if (strcasecmp(isc_commandline_argument,
				 "DNSKEY") == 0)
				/* default behavior */
				;
			else
				fatal("unknown type '%s'",
				      isc_commandline_argument);
			break;
		case 't':
			type = isc_commandline_argument;
361
			break;
362
363
364
365
		case 'v':
			endp = NULL;
			verbose = strtol(isc_commandline_argument, &endp, 0);
			if (*endp != '\0')
366
				fatal("-v must be followed by a number");
367
			break;
368
369
370
		case 'z':
			/* already the default */
			break;
371
372
373
		case 'G':
			genonly = ISC_TRUE;
			break;
374
		case 'P':
Automatic Updater's avatar
Automatic Updater committed
375
376
			if (setpub || unsetpub)
				fatal("-P specified more than once");
377
378
379
380
381
382

			if (strcasecmp(isc_commandline_argument, "none")) {
				setpub = ISC_TRUE;
				publish = strtotime(isc_commandline_argument,
						    now, now);
			} else {
Automatic Updater's avatar
Automatic Updater committed
383
384
				unsetpub = ISC_TRUE;
			}
385
386
			break;
		case 'A':
Automatic Updater's avatar
Automatic Updater committed
387
388
			if (setact || unsetact)
				fatal("-A specified more than once");
389
390
391
392
393
394

			if (strcasecmp(isc_commandline_argument, "none")) {
				setact = ISC_TRUE;
				activate = strtotime(isc_commandline_argument,
						     now, now);
			} else {
Automatic Updater's avatar
Automatic Updater committed
395
				unsetact = ISC_TRUE;
396
			}
397
398
			break;
		case 'R':
Automatic Updater's avatar
Automatic Updater committed
399
400
			if (setrev || unsetrev)
				fatal("-R specified more than once");
401
402
403
404
405
406

			if (strcasecmp(isc_commandline_argument, "none")) {
				setrev = ISC_TRUE;
				revoke = strtotime(isc_commandline_argument,
						   now, now);
			} else {
Automatic Updater's avatar
Automatic Updater committed
407
				unsetrev = ISC_TRUE;
408
			}
409
			break;
410
411
412
		case 'I':
			if (setinact || unsetinact)
				fatal("-I specified more than once");
413
414

			if (strcasecmp(isc_commandline_argument, "none")) {
415
416
417
				setinact = ISC_TRUE;
				inactive = strtotime(isc_commandline_argument,
						     now, now);
418
			} else {
419
				unsetinact = ISC_TRUE;
420
			}
421
422
			break;
		case 'D':
Automatic Updater's avatar
Automatic Updater committed
423
424
			if (setdel || unsetdel)
				fatal("-D specified more than once");
425
426
427
428
429
430

			if (strcasecmp(isc_commandline_argument, "none")) {
				setdel = ISC_TRUE;
				delete = strtotime(isc_commandline_argument,
						   now, now);
			} else {
Automatic Updater's avatar
Automatic Updater committed
431
				unsetdel = ISC_TRUE;
432
			}
433
			break;
Francis Dupont's avatar
Francis Dupont committed
434
435
436
		case 'F':
			/* Reserved for FIPS mode */
			/* FALLTHROUGH */
437
438
439
440
		case '?':
			if (isc_commandline_option != '?')
				fprintf(stderr, "%s: invalid argument -%c\n",
					program, isc_commandline_option);
Francis Dupont's avatar
Francis Dupont committed
441
			/* FALLTHROUGH */
Brian Wellington's avatar
Brian Wellington committed
442
		case 'h':
443
			usage();
444

Brian Wellington's avatar
Brian Wellington committed
445
		default:
446
447
448
			fprintf(stderr, "%s: unhandled option -%c\n",
				program, isc_commandline_option);
			exit(1);
449
		}
Brian Wellington's avatar
Brian Wellington committed
450
451
	}

452
453
454
	if (!isatty(0))
		quiet = ISC_TRUE;

455
456
	if (ectx == NULL)
		setup_entropy(mctx, NULL, &ectx);
Francis Dupont's avatar
Francis Dupont committed
457
458
	ret = dst_lib_init2(mctx, ectx, engine,
			    ISC_ENTROPY_BLOCKING | ISC_ENTROPY_GOODONLY);
Brian Wellington's avatar
Brian Wellington committed
459
	if (ret != ISC_R_SUCCESS)
Francis Dupont's avatar
Francis Dupont committed
460
461
		fatal("could not initialize dst: %s",
		      isc_result_totext(ret));
Brian Wellington's avatar
Brian Wellington committed
462

463
464
	setup_logging(verbose, mctx, &log);

465
	if (argc < isc_commandline_index + 1)
466
		fatal("the key name was not specified");
467
	if (argc > isc_commandline_index + 1)
468
		fatal("extraneous arguments");
Brian Wellington's avatar
Brian Wellington committed
469

470
471
	if (algname == NULL) {
		use_default = ISC_TRUE;
472
473
474
475
		if (use_nsec3)
			algname = strdup(DEFAULT_NSEC3_ALGORITHM);
		else
			algname = strdup(DEFAULT_ALGORITHM);
476
477
478
479
480
		if (verbose > 0)
			fprintf(stderr, "no algorithm specified; "
				"defaulting to %s\n", algname);
	}

Automatic Updater's avatar
Automatic Updater committed
481
	if (strcasecmp(algname, "RSA") == 0) {
482
483
484
485
486
		fprintf(stderr, "The use of RSA (RSAMD5) is not recommended.\n"
				"If you still wish to use RSA (RSAMD5) please "
				"specify \"-a RSAMD5\"\n");
		return (1);
	} else if (strcasecmp(algname, "HMAC-MD5") == 0) {
487
		options |= DST_TYPE_KEY;
Brian Wellington's avatar
Brian Wellington committed
488
		alg = DST_ALG_HMACMD5;
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
	} else if (strcasecmp(algname, "HMAC-SHA1") == 0) {
		options |= DST_TYPE_KEY;
		alg = DST_ALG_HMACSHA1;
	} else if (strcasecmp(algname, "HMAC-SHA224") == 0) {
		options |= DST_TYPE_KEY;
		alg = DST_ALG_HMACSHA224;
	} else if (strcasecmp(algname, "HMAC-SHA256") == 0) {
		options |= DST_TYPE_KEY;
		alg = DST_ALG_HMACSHA256;
	} else if (strcasecmp(algname, "HMAC-SHA384") == 0) {
		options |= DST_TYPE_KEY;
		alg = DST_ALG_HMACSHA384;
	} else if (strcasecmp(algname, "HMAC-SHA512") == 0) {
		options |= DST_TYPE_KEY;
		alg = DST_ALG_HMACSHA512;
504
	} else {
Brian Wellington's avatar
Brian Wellington committed
505
506
507
508
		r.base = algname;
		r.length = strlen(algname);
		ret = dns_secalg_fromtext(&alg, &r);
		if (ret != ISC_R_SUCCESS)
509
			fatal("unknown algorithm %s", algname);
510
511
		if (alg == DST_ALG_DH)
			options |= DST_TYPE_KEY;
Brian Wellington's avatar
Brian Wellington committed
512
	}
Brian Wellington's avatar
Brian Wellington committed
513

514
	if (use_nsec3 &&
515
516
	    alg != DST_ALG_NSEC3DSA && alg != DST_ALG_NSEC3RSASHA1 &&
	    alg != DST_ALG_RSASHA256 && alg!= DST_ALG_RSASHA512) {
517
518
519
520
		fatal("%s is incompatible with NSEC3; "
		      "do not use the -3 option", algname);
	}

521
	if (type != NULL && (options & DST_TYPE_KEY) != 0) {
Brian Wellington's avatar
Brian Wellington committed
522
523
524
525
		if (strcasecmp(type, "NOAUTH") == 0)
			flags |= DNS_KEYTYPE_NOAUTH;
		else if (strcasecmp(type, "NOCONF") == 0)
			flags |= DNS_KEYTYPE_NOCONF;
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
526
		else if (strcasecmp(type, "NOAUTHCONF") == 0) {
Brian Wellington's avatar
Brian Wellington committed
527
			flags |= (DNS_KEYTYPE_NOAUTH | DNS_KEYTYPE_NOCONF);
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
528
529
530
			if (size < 0)
				size = 0;
		}
Brian Wellington's avatar
Brian Wellington committed
531
532
533
		else if (strcasecmp(type, "AUTHCONF") == 0)
			/* nothing */;
		else
534
			fatal("invalid type %s", type);
Brian Wellington's avatar
Brian Wellington committed
535
536
	}

537
538
	if (size < 0) {
		if (use_default) {
539
			size = ((kskflag & DNS_KEYFLAG_KSK) != 0) ? 2048 : 1024;
Automatic Updater's avatar
Automatic Updater committed
540
541
			if (verbose > 0)
				fprintf(stderr, "key size not specified; "
542
543
544
545
546
					"defaulting to %d\n", size);
		} else {
			fatal("key size not specified (-b option)");
		}
	}
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
547

Brian Wellington's avatar
Brian Wellington committed
548
	switch (alg) {
Brian Wellington's avatar
Brian Wellington committed
549
550
	case DNS_KEYALG_RSAMD5:
	case DNS_KEYALG_RSASHA1:
551
	case DNS_KEYALG_NSEC3RSASHA1:
552
	case DNS_KEYALG_RSASHA256:
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
553
		if (size != 0 && (size < 512 || size > MAX_RSA))
554
			fatal("RSA key size %d out of range", size);
Brian Wellington's avatar
Brian Wellington committed
555
		break;
556
557
558
559
	case DNS_KEYALG_RSASHA512:
		if (size != 0 && (size < 1024 || size > MAX_RSA))
			fatal("RSA key size %d out of range", size);
		break;
Brian Wellington's avatar
Brian Wellington committed
560
561
	case DNS_KEYALG_DH:
		if (size != 0 && (size < 128 || size > 4096))
562
			fatal("DH key size %d out of range", size);
Brian Wellington's avatar
Brian Wellington committed
563
564
		break;
	case DNS_KEYALG_DSA:
565
	case DNS_KEYALG_NSEC3DSA:
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
566
		if (size != 0 && !dsa_size_ok(size))
567
			fatal("invalid DSS key size: %d", size);
Brian Wellington's avatar
Brian Wellington committed
568
569
570
		break;
	case DST_ALG_HMACMD5:
		if (size < 1 || size > 512)
571
			fatal("HMAC-MD5 key size %d out of range", size);
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
		if (dbits != 0 && (dbits < 80 || dbits > 128))
			fatal("HMAC-MD5 digest bits %d out of range", dbits);
		if ((dbits % 8) != 0)
			fatal("HMAC-MD5 digest bits %d not divisible by 8",
			      dbits);
		break;
	case DST_ALG_HMACSHA1:
		if (size < 1 || size > 160)
			fatal("HMAC-SHA1 key size %d out of range", size);
		if (dbits != 0 && (dbits < 80 || dbits > 160))
			fatal("HMAC-SHA1 digest bits %d out of range", dbits);
		if ((dbits % 8) != 0)
			fatal("HMAC-SHA1 digest bits %d not divisible by 8",
			      dbits);
		break;
	case DST_ALG_HMACSHA224:
		if (size < 1 || size > 224)
			fatal("HMAC-SHA224 key size %d out of range", size);
		if (dbits != 0 && (dbits < 112 || dbits > 224))
			fatal("HMAC-SHA224 digest bits %d out of range", dbits);
		if ((dbits % 8) != 0)
			fatal("HMAC-SHA224 digest bits %d not divisible by 8",
			      dbits);
		break;
	case DST_ALG_HMACSHA256:
		if (size < 1 || size > 256)
			fatal("HMAC-SHA256 key size %d out of range", size);
		if (dbits != 0 && (dbits < 128 || dbits > 256))
			fatal("HMAC-SHA256 digest bits %d out of range", dbits);
		if ((dbits % 8) != 0)
			fatal("HMAC-SHA256 digest bits %d not divisible by 8",
			      dbits);
		break;
	case DST_ALG_HMACSHA384:
		if (size < 1 || size > 384)
			fatal("HMAC-384 key size %d out of range", size);
		if (dbits != 0 && (dbits < 192 || dbits > 384))
			fatal("HMAC-SHA384 digest bits %d out of range", dbits);
		if ((dbits % 8) != 0)
			fatal("HMAC-SHA384 digest bits %d not divisible by 8",
			      dbits);
		break;
	case DST_ALG_HMACSHA512:
		if (size < 1 || size > 512)
			fatal("HMAC-SHA512 key size %d out of range", size);
		if (dbits != 0 && (dbits < 256 || dbits > 512))
			fatal("HMAC-SHA512 digest bits %d out of range", dbits);
		if ((dbits % 8) != 0)
			fatal("HMAC-SHA512 digest bits %d not divisible by 8",
			      dbits);
Brian Wellington's avatar
Brian Wellington committed
622
623
624
		break;
	}

625
	if (!(alg == DNS_KEYALG_RSAMD5 || alg == DNS_KEYALG_RSASHA1 ||
626
627
	      alg == DNS_KEYALG_NSEC3RSASHA1 || alg == DNS_KEYALG_RSASHA256 ||
	      alg == DNS_KEYALG_RSASHA512) && rsa_exp != 0)
Brian Wellington's avatar
Brian Wellington committed
628
		fatal("specified RSA exponent for a non-RSA key");
Brian Wellington's avatar
Brian Wellington committed
629
630

	if (alg != DNS_KEYALG_DH && generator != 0)
Brian Wellington's avatar
Brian Wellington committed
631
		fatal("specified DH generator for a non-DH key");
Brian Wellington's avatar
Brian Wellington committed
632

633
634
635
636
637
	if (nametype == NULL) {
		if ((options & DST_TYPE_KEY) != 0) /* KEY / HMAC */
			fatal("no nametype specified");
		flags |= DNS_KEYOWNER_ZONE;	/* DNSKEY */
	} else if (strcasecmp(nametype, "zone") == 0)
Brian Wellington's avatar
Brian Wellington committed
638
		flags |= DNS_KEYOWNER_ZONE;
639
	else if ((options & DST_TYPE_KEY) != 0)	{ /* KEY / HMAC */
640
641
642
643
644
645
646
647
648
		if (strcasecmp(nametype, "host") == 0 ||
			 strcasecmp(nametype, "entity") == 0)
			flags |= DNS_KEYOWNER_ENTITY;
		else if (strcasecmp(nametype, "user") == 0)
			flags |= DNS_KEYOWNER_USER;
		else
			fatal("invalid KEY nametype %s", nametype);
	} else if (strcasecmp(nametype, "other") != 0) /* DNSKEY */
		fatal("invalid DNSKEY nametype %s", nametype);
Brian Wellington's avatar
Brian Wellington committed
649

650
	rdclass = strtoclass(classname);
651

652
653
654
	if (directory == NULL)
		directory = ".";

655
	if ((options & DST_TYPE_KEY) != 0)  /* KEY / HMAC */
656
		flags |= signatory;
657
	else if ((flags & DNS_KEYOWNER_ZONE) != 0) { /* DNSKEY */
658
659
		flags |= kskflag;
		flags |= revflag;
Automatic Updater's avatar
Automatic Updater committed
660
	}
Brian Wellington's avatar
Brian Wellington committed
661

662
663
	if (protocol == -1)
		protocol = DNS_KEYPROTO_DNSSEC;
664
665
666
	else if ((options & DST_TYPE_KEY) == 0 &&
		 protocol != DNS_KEYPROTO_DNSSEC)
		fatal("invalid DNSKEY protocol: %d", protocol);
Brian Wellington's avatar
Brian Wellington committed
667
668
669

	if ((flags & DNS_KEYFLAG_TYPEMASK) == DNS_KEYTYPE_NOKEY) {
		if (size > 0)
670
			fatal("specified null key with non-zero size");
Brian Wellington's avatar
Brian Wellington committed
671
		if ((flags & DNS_KEYFLAG_SIGNATORYMASK) != 0)
672
			fatal("specified null key with signing authority");
Brian Wellington's avatar
Brian Wellington committed
673
	}
Brian Wellington's avatar
Brian Wellington committed
674

675
	if ((flags & DNS_KEYFLAG_OWNERMASK) == DNS_KEYOWNER_ZONE &&
676
677
678
679
	    (alg == DNS_KEYALG_DH || alg == DST_ALG_HMACMD5 ||
	     alg == DST_ALG_HMACSHA1 || alg == DST_ALG_HMACSHA224 ||
	     alg == DST_ALG_HMACSHA256 || alg == DST_ALG_HMACSHA384 ||
	     alg == DST_ALG_HMACSHA512))
680
		fatal("a key with algorithm '%s' cannot be a zone key",
681
682
		      algname);

683
684
685
686
687
	dns_fixedname_init(&fname);
	name = dns_fixedname_name(&fname);
	isc_buffer_init(&buf, argv[isc_commandline_index],
			strlen(argv[isc_commandline_index]));
	isc_buffer_add(&buf, strlen(argv[isc_commandline_index]));
688
	ret = dns_name_fromtext(name, &buf, dns_rootname, 0, NULL);
689
	if (ret != ISC_R_SUCCESS)
690
		fatal("invalid key name %s: %s", argv[isc_commandline_index],
691
		      isc_result_totext(ret));
Brian Wellington's avatar
Brian Wellington committed
692
693

	switch(alg) {
Brian Wellington's avatar
Brian Wellington committed
694
695
	case DNS_KEYALG_RSAMD5:
	case DNS_KEYALG_RSASHA1:
696
697
698
	case DNS_KEYALG_NSEC3RSASHA1:
	case DNS_KEYALG_RSASHA256:
	case DNS_KEYALG_RSASHA512:
Brian Wellington's avatar
Brian Wellington committed
699
		param = rsa_exp;
700
		show_progress = ISC_TRUE;
Brian Wellington's avatar
Brian Wellington committed
701
		break;
702

Brian Wellington's avatar
Brian Wellington committed
703
704
705
	case DNS_KEYALG_DH:
		param = generator;
		break;
706

Brian Wellington's avatar
Brian Wellington committed
707
	case DNS_KEYALG_DSA:
708
	case DNS_KEYALG_NSEC3DSA:
709
710
711
		show_progress = ISC_TRUE;
		/* fall through */

Brian Wellington's avatar
Brian Wellington committed
712
	case DST_ALG_HMACMD5:
713
714
715
716
717
	case DST_ALG_HMACSHA1:
	case DST_ALG_HMACSHA224:
	case DST_ALG_HMACSHA256:
	case DST_ALG_HMACSHA384:
	case DST_ALG_HMACSHA512:
Brian Wellington's avatar
Brian Wellington committed
718
719
		param = 0;
		break;
Brian Wellington's avatar
Brian Wellington committed
720
721
	}

722
723
724
725
726
	if ((flags & DNS_KEYFLAG_TYPEMASK) == DNS_KEYTYPE_NOKEY)
		null_key = ISC_TRUE;

	isc_buffer_init(&buf, filename, sizeof(filename) - 1);

727
728
	do {
		conflict = ISC_FALSE;
729
		oldkey = NULL;
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
730

731
732
733
734
735
736
737
738
739
740
741
742
743
		if (!quiet && show_progress) {
			fprintf(stderr, "Generating key pair.");
			ret = dst_key_generate2(name, alg, size, param, flags,
						protocol, rdclass, mctx, &key,
						&progress);
			putc('\n', stderr);
			fflush(stderr);
		} else {
			ret = dst_key_generate2(name, alg, size, param, flags,
						protocol, rdclass, mctx, &key,
						NULL);
		}

744
		isc_entropy_stopcallbacksources(ectx);
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
745
746

		if (ret != ISC_R_SUCCESS) {
747
			char namestr[DNS_NAME_FORMATSIZE];
748
			char algstr[DNS_SECALG_FORMATSIZE];
Andreas Gustafsson's avatar
Andreas Gustafsson committed
749
			dns_name_format(name, namestr, sizeof(namestr));
750
			dns_secalg_format(alg, algstr, sizeof(algstr));
751
			fatal("failed to generate key %s/%s: %s\n",
752
			      namestr, algstr, isc_result_totext(ret));
Evan Hunt's avatar
Evan Hunt committed
753
			/* NOTREACHED */
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
754
755
			exit(-1);
		}
756

757
758
		dst_key_setbits(key, dbits);

759
		/*
760
		 * Set key timing metadata (unless using -C)
761
762
763
764
		 *
		 * Publish and activation dates are set to "now" by default,
		 * but can be overridden.  Creation date is always set to
		 * "now".
765
766
767
		 */
		if (!oldstyle) {
			dst_key_settime(key, DST_TIME_CREATED, now);
768

769
770
771
772
			if (genonly && (setpub || setact))
				fatal("cannot use -G together with "
				      "-P or -A options");

773
			if (setpub)
774
				dst_key_settime(key, DST_TIME_PUBLISH, publish);
775
			else if (!genonly && !setact)
776
777
				dst_key_settime(key, DST_TIME_PUBLISH, now);

778
779
780
			if (setact)
				dst_key_settime(key, DST_TIME_ACTIVATE,
						activate);
781
			else if (!genonly && !setpub)
782
783
				dst_key_settime(key, DST_TIME_ACTIVATE, now);

784
785
786
787
788
789
790
			if (setrev) {
				if (kskflag == 0)
					fprintf(stderr, "%s: warning: Key is "
						"not flagged as a KSK, but -R "
						"was used. Revoking a ZSK is "
						"legal, but undefined.\n",
						program);
791
				dst_key_settime(key, DST_TIME_REVOKE, revoke);
792
			}
793
794
795
796
797

			if (setinact)
				dst_key_settime(key, DST_TIME_INACTIVE,
						inactive);

798
			if (setdel)
799
				dst_key_settime(key, DST_TIME_DELETE, delete);
800
		} else {
801
			if (setpub || setact || setrev || setinact ||
802
			    setdel || unsetpub || unsetact ||
803
			    unsetrev || unsetinact || unsetdel || genonly)
804
				fatal("cannot use -C together with "
805
				      "-P, -A, -R, -I, -D, or -G options");
806
			/*
Automatic Updater's avatar
Automatic Updater committed
807
			 * Compatibility mode: Private-key-format
808
809
810
			 * should be set to 1.2.
			 */
			dst_key_setprivateformat(key, 1, 2);
811
812
		}

813
814
		/*
		 * Try to read a key with the same name, alg and id from disk.
Francis Dupont's avatar
Francis Dupont committed
815
816
		 * If there is one we must continue generating a different
		 * key unless we were asked to generate a null key, in which
817
		 * case we return failure.
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
818
		 */
819
		ret = dst_key_fromfile(name, dst_key_id(key), alg,
Evan Hunt's avatar
Evan Hunt committed
820
821
				       DST_TYPE_PRIVATE, directory,
				       mctx, &oldkey);
822
823
		/* do not overwrite an existing key  */
		if (ret == ISC_R_SUCCESS) {
824
			dst_key_free(&oldkey);
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
825
			conflict = ISC_TRUE;
826
827
			if (null_key)
				break;
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
828
		}
829
830
831
		if (conflict == ISC_TRUE) {
			if (verbose > 0) {
				isc_buffer_clear(&buf);
832
				dst_key_buildfilename(key, 0, directory, &buf);
833
834
835
				fprintf(stderr,
					"%s: %s already exists, "
					"generating a new key\n",
836
					program, filename);
837
			}
838
			dst_key_free(&key);
839
		}
840
	} while (conflict == ISC_TRUE);
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
841

842
	if (conflict)
843
844
		fatal("cannot generate a null key when a key with id 0 "
		      "already exists");
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
845

846
	ret = dst_key_tofile(key, options, directory);
847
	if (ret != ISC_R_SUCCESS) {
848
849
		char keystr[DST_KEY_FORMATSIZE];
		dst_key_format(key, keystr, sizeof(keystr));
850
851
852
		fatal("failed to write key %s: %s\n", keystr,
		      isc_result_totext(ret));
	}
853
854

	isc_buffer_clear(&buf);
Brian Wellington's avatar
Brian Wellington committed
855
	ret = dst_key_buildfilename(key, 0, NULL, &buf);
856
	printf("%s\n", filename);
857
	dst_key_free(&key);
858

Brian Wellington's avatar
Brian Wellington committed
859
	cleanup_logging(&log);
Brian Wellington's avatar
Brian Wellington committed
860
	cleanup_entropy(&ectx);
Brian Wellington's avatar
Brian Wellington committed
861
	dst_lib_destroy();
862
	dns_name_destroy();
863
864
	if (verbose > 10)
		isc_mem_stats(mctx, stdout);
Brian Wellington's avatar
Brian Wellington committed
865
	isc_mem_destroy(&mctx);
866
867

	return (0);
Brian Wellington's avatar
Brian Wellington committed
868
}