dnssec-keygen.c 22.7 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
 */

Evan Hunt's avatar
Evan Hunt committed
32
/* $Id: dnssec-keygen.c,v 1.93 2009/09/04 16:57:22 each 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 40
#include <stdlib.h>

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

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

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

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

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

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

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

69 70
#define DEFAULT_ALGORITHM "RSASHA1"

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

static void
77
usage(void) {
78
	fprintf(stderr, "Usage:\n");
79
	fprintf(stderr, "    %s [options] name\n\n", program);
80
	fprintf(stderr, "Version: %s\n", VERSION);
81 82
	fprintf(stderr, "    name: owner of the key\n");
	fprintf(stderr, "Options:\n");
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
	fprintf(stderr, "    -K <directory>: write keys into directory\n");
	fprintf(stderr, "    -a <algorithm>:\n");
	fprintf(stderr, "        RSA | RSAMD5 | DSA | RSASHA1 | "
				"NSEC3RSASHA1 | NSEC3DSA |\n");
	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);
	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");
114
	fprintf(stderr, "    -d <digest bits> (0 => max, default)\n");
115 116 117 118 119 120 121 122 123
	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
124
	fprintf(stderr, "    -t <type>: "
125 126
			"AUTHCONF | NOAUTHCONF | NOAUTH | NOCONF "
			"(default: AUTHCONF)\n");
Brian Wellington's avatar
Brian Wellington committed
127
	fprintf(stderr, "    -r <randomdev>: a file containing random data\n");
128 129 130 131 132 133 134 135 136 137 138 139 140

	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");
	fprintf(stderr, "    -P date/[+-]offset: set key publication date\n");
	fprintf(stderr, "    -A date/[+-]offset: set key activation date\n");
	fprintf(stderr, "    -R date/[+-]offset: set key revocation date\n");
	fprintf(stderr, "    -U date/[+-]offset: set key unpublication date\n");
	fprintf(stderr, "    -D date/[+-]offset: set key deletion date\n");
	fprintf(stderr, "    -C: generate a backward-compatible key, omitting "
			"dates\n");
141 142
	fprintf(stderr, "Output:\n");
	fprintf(stderr, "     K<name>+<alg>+<id>.key, "
143
			"K<name>+<alg>+<id>.private\n");
144 145 146 147

	exit (-1);
}

Brian Wellington's avatar
Brian Wellington committed
148 149
int
main(int argc, char **argv) {
150
	char	        *algname = NULL, *nametype = NULL, *type = NULL;
151
	char		*classname = NULL;
Brian Wellington's avatar
Brian Wellington committed
152
	char		*endp;
153
	dst_key_t	*key = NULL, *oldkey;
154 155
	dns_fixedname_t	fname;
	dns_name_t	*name;
156
	isc_uint16_t	flags = 0, kskflag = 0, revflag = 0;
157
	dns_secalg_t	alg;
Brian Wellington's avatar
Brian Wellington committed
158
	isc_boolean_t	conflict = ISC_FALSE, null_key = ISC_FALSE;
159
	isc_boolean_t	oldstyle = ISC_FALSE;
160 161 162 163 164
	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;
165
	char		filename[255];
166
	const char	*directory = NULL;
167
	isc_buffer_t	buf;
168
	isc_log_t	*log = NULL;
Brian Wellington's avatar
Brian Wellington committed
169
	isc_entropy_t	*ectx = NULL;
170
	dns_rdataclass_t rdclass;
171
	int		options = DST_TYPE_PRIVATE | DST_TYPE_PUBLIC;
172
	int		dbits = 0;
173 174 175 176
	isc_boolean_t	use_default = ISC_FALSE, use_nsec3 = ISC_FALSE;
	isc_stdtime_t	publish = 0, activate = 0, revoke = 0;
	isc_stdtime_t	unpublish = 0, delete = 0;
	isc_stdtime_t	now;
177 178 179 180 181 182
	isc_boolean_t	setpub = ISC_FALSE, setact = ISC_FALSE;
	isc_boolean_t	setrev = ISC_FALSE, setunpub = ISC_FALSE;
	isc_boolean_t	setdel = ISC_FALSE;
	isc_boolean_t	unsetpub = ISC_FALSE, unsetact = ISC_FALSE;
	isc_boolean_t	unsetrev = ISC_FALSE, unsetunpub = ISC_FALSE;
	isc_boolean_t	unsetdel = ISC_FALSE;
Brian Wellington's avatar
Brian Wellington committed
183

Brian Wellington's avatar
Brian Wellington committed
184
	if (argc == 1)
185
		usage();
Brian Wellington's avatar
Brian Wellington committed
186

187 188
	dns_result_register();

189 190
	isc_commandline_errprint = ISC_FALSE;

191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
	/*
	 * Process memory debugging argument first.
	 */
#define CMDLINE_FLAGS "3a:b:Cc:d:eFf:g:K:km:n:p:r:s:T:t:v:hP:A:R:U:D:"
	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
220
	    switch (ch) {
221 222 223
		case '3':
			use_nsec3 = ISC_TRUE;
			break;
Brian Wellington's avatar
Brian Wellington committed
224
		case 'a':
225
			algname = isc_commandline_argument;
Brian Wellington's avatar
Brian Wellington committed
226
			break;
Brian Wellington's avatar
Brian Wellington committed
227 228 229
		case 'b':
			size = strtol(isc_commandline_argument, &endp, 10);
			if (*endp != '\0' || size < 0)
230
				fatal("-b requires a non-negative number");
Brian Wellington's avatar
Brian Wellington committed
231
			break;
232 233 234
		case 'C':
			oldstyle = ISC_TRUE;
			break;
235 236 237
		case 'c':
			classname = isc_commandline_argument;
			break;
238 239 240 241 242
		case 'd':
			dbits = strtol(isc_commandline_argument, &endp, 10);
			if (*endp != '\0' || dbits < 0)
				fatal("-d requires a non-negative number");
			break;
Brian Wellington's avatar
Brian Wellington committed
243 244
		case 'e':
			rsa_exp = 1;
Brian Wellington's avatar
Brian Wellington committed
245
			break;
246
		case 'f':
247 248 249 250
			if (toupper(isc_commandline_argument[0]) == 'K')
				kskflag = DNS_KEYFLAG_KSK;
			else if (toupper(isc_commandline_argument[0]) == 'R')
				revflag = DNS_KEYFLAG_REVOKE;
251 252 253 254
			else
				fatal("unknown flag '%s'",
				      isc_commandline_argument);
			break;
255
		case 'g':
256 257
			generator = strtol(isc_commandline_argument,
					   &endp, 10);
Brian Wellington's avatar
Brian Wellington committed
258
			if (*endp != '\0' || generator <= 0)
259
				fatal("-g requires a positive number");
Brian Wellington's avatar
Brian Wellington committed
260
			break;
261 262 263
		case 'K':
			directory = isc_commandline_argument;
			break;
264
		case 'k':
265 266 267
			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");
268
			break;
Brian Wellington's avatar
Brian Wellington committed
269
		case 'n':
270
			nametype = isc_commandline_argument;
Brian Wellington's avatar
Brian Wellington committed
271
			break;
272
		case 'm':
273
			break;
Brian Wellington's avatar
Brian Wellington committed
274
		case 'p':
Brian Wellington's avatar
Brian Wellington committed
275 276
			protocol = strtol(isc_commandline_argument, &endp, 10);
			if (*endp != '\0' || protocol < 0 || protocol > 255)
277 278
				fatal("-p must be followed by a number "
				      "[0..255]");
Brian Wellington's avatar
Brian Wellington committed
279
			break;
280 281 282
		case 'r':
			setup_entropy(mctx, isc_commandline_argument, &ectx);
			break;
Brian Wellington's avatar
Brian Wellington committed
283
		case 's':
284 285
			signatory = strtol(isc_commandline_argument,
					   &endp, 10);
Brian Wellington's avatar
Brian Wellington committed
286
			if (*endp != '\0' || signatory < 0 || signatory > 15)
287 288
				fatal("-s must be followed by a number "
				      "[0..15]");
Brian Wellington's avatar
Brian Wellington committed
289
			break;
290 291 292 293 294 295 296 297 298 299 300 301 302
		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;
303
			break;
304 305 306 307
		case 'v':
			endp = NULL;
			verbose = strtol(isc_commandline_argument, &endp, 0);
			if (*endp != '\0')
308
				fatal("-v must be followed by a number");
309
			break;
310 311 312 313
		case 'z':
			/* already the default */
			break;
		case 'P':
Automatic Updater's avatar
Automatic Updater committed
314 315
			if (setpub || unsetpub)
				fatal("-P specified more than once");
316 317 318 319 320 321

			if (strcasecmp(isc_commandline_argument, "none")) {
				setpub = ISC_TRUE;
				publish = strtotime(isc_commandline_argument,
						    now, now);
			} else {
Automatic Updater's avatar
Automatic Updater committed
322 323
				unsetpub = ISC_TRUE;
			}
324 325
			break;
		case 'A':
Automatic Updater's avatar
Automatic Updater committed
326 327
			if (setact || unsetact)
				fatal("-A specified more than once");
328 329 330 331 332 333

			if (strcasecmp(isc_commandline_argument, "none")) {
				setact = ISC_TRUE;
				activate = strtotime(isc_commandline_argument,
						     now, now);
			} else {
Automatic Updater's avatar
Automatic Updater committed
334
				unsetact = ISC_TRUE;
335
			}
336 337
			break;
		case 'R':
Automatic Updater's avatar
Automatic Updater committed
338 339
			if (setrev || unsetrev)
				fatal("-R specified more than once");
340 341 342 343 344 345

			if (strcasecmp(isc_commandline_argument, "none")) {
				setrev = ISC_TRUE;
				revoke = strtotime(isc_commandline_argument,
						   now, now);
			} else {
Automatic Updater's avatar
Automatic Updater committed
346
				unsetrev = ISC_TRUE;
347
			}
348 349
			break;
		case 'U':
Automatic Updater's avatar
Automatic Updater committed
350 351
			if (setunpub || unsetunpub)
				fatal("-U specified more than once");
352 353 354 355 356 357

			if (strcasecmp(isc_commandline_argument, "none")) {
				setunpub = ISC_TRUE;
				unpublish = strtotime(isc_commandline_argument,
						      now, now);
			} else {
Automatic Updater's avatar
Automatic Updater committed
358
				unsetunpub = ISC_TRUE;
359
			}
360 361
			break;
		case 'D':
Automatic Updater's avatar
Automatic Updater committed
362 363
			if (setdel || unsetdel)
				fatal("-D specified more than once");
364 365 366 367 368 369

			if (strcasecmp(isc_commandline_argument, "none")) {
				setdel = ISC_TRUE;
				delete = strtotime(isc_commandline_argument,
						   now, now);
			} else {
Automatic Updater's avatar
Automatic Updater committed
370
				unsetdel = ISC_TRUE;
371
			}
372
			break;
Francis Dupont's avatar
Francis Dupont committed
373 374 375
		case 'F':
			/* Reserved for FIPS mode */
			/* FALLTHROUGH */
376 377 378 379
		case '?':
			if (isc_commandline_option != '?')
				fprintf(stderr, "%s: invalid argument -%c\n",
					program, isc_commandline_option);
Francis Dupont's avatar
Francis Dupont committed
380
			/* FALLTHROUGH */
Brian Wellington's avatar
Brian Wellington committed
381
		case 'h':
382
			usage();
383

Brian Wellington's avatar
Brian Wellington committed
384
		default:
385 386 387
			fprintf(stderr, "%s: unhandled option -%c\n",
				program, isc_commandline_option);
			exit(1);
388
		}
Brian Wellington's avatar
Brian Wellington committed
389 390
	}

391 392
	if (ectx == NULL)
		setup_entropy(mctx, NULL, &ectx);
Brian Wellington's avatar
Brian Wellington committed
393 394 395 396 397
	ret = dst_lib_init(mctx, ectx,
			   ISC_ENTROPY_BLOCKING | ISC_ENTROPY_GOODONLY);
	if (ret != ISC_R_SUCCESS)
		fatal("could not initialize dst");

398 399
	setup_logging(verbose, mctx, &log);

400
	if (argc < isc_commandline_index + 1)
401
		fatal("the key name was not specified");
402
	if (argc > isc_commandline_index + 1)
403
		fatal("extraneous arguments");
Brian Wellington's avatar
Brian Wellington committed
404

405 406
	if (algname == NULL) {
		use_default = ISC_TRUE;
407 408 409 410
		if (use_nsec3)
			algname = strdup(DEFAULT_NSEC3_ALGORITHM);
		else
			algname = strdup(DEFAULT_ALGORITHM);
411 412 413 414 415
		if (verbose > 0)
			fprintf(stderr, "no algorithm specified; "
				"defaulting to %s\n", algname);
	}

Automatic Updater's avatar
Automatic Updater committed
416
	if (strcasecmp(algname, "RSA") == 0) {
417 418 419 420 421
		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) {
422
		options |= DST_TYPE_KEY;
Brian Wellington's avatar
Brian Wellington committed
423
		alg = DST_ALG_HMACMD5;
424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
	} 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;
439
	} else {
Brian Wellington's avatar
Brian Wellington committed
440 441 442 443
		r.base = algname;
		r.length = strlen(algname);
		ret = dns_secalg_fromtext(&alg, &r);
		if (ret != ISC_R_SUCCESS)
444
			fatal("unknown algorithm %s", algname);
445 446
		if (alg == DST_ALG_DH)
			options |= DST_TYPE_KEY;
Brian Wellington's avatar
Brian Wellington committed
447
	}
Brian Wellington's avatar
Brian Wellington committed
448

449 450 451 452 453 454
	if (use_nsec3 &&
	    alg != DST_ALG_NSEC3DSA && alg != DST_ALG_NSEC3RSASHA1) {
		fatal("%s is incompatible with NSEC3; "
		      "do not use the -3 option", algname);
	}

455
	if (type != NULL && (options & DST_TYPE_KEY) != 0) {
Brian Wellington's avatar
Brian Wellington committed
456 457 458 459
		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
460
		else if (strcasecmp(type, "NOAUTHCONF") == 0) {
Brian Wellington's avatar
Brian Wellington committed
461
			flags |= (DNS_KEYTYPE_NOAUTH | DNS_KEYTYPE_NOCONF);
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
462 463 464
			if (size < 0)
				size = 0;
		}
Brian Wellington's avatar
Brian Wellington committed
465 466 467
		else if (strcasecmp(type, "AUTHCONF") == 0)
			/* nothing */;
		else
468
			fatal("invalid type %s", type);
Brian Wellington's avatar
Brian Wellington committed
469 470
	}

471 472
	if (size < 0) {
		if (use_default) {
473
			size = ((kskflag & DNS_KEYFLAG_KSK) != 0) ? 2048 : 1024;
Automatic Updater's avatar
Automatic Updater committed
474 475
			if (verbose > 0)
				fprintf(stderr, "key size not specified; "
476 477 478 479 480
					"defaulting to %d\n", size);
		} else {
			fatal("key size not specified (-b option)");
		}
	}
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
481

Brian Wellington's avatar
Brian Wellington committed
482
	switch (alg) {
Brian Wellington's avatar
Brian Wellington committed
483 484
	case DNS_KEYALG_RSAMD5:
	case DNS_KEYALG_RSASHA1:
485
	case DNS_KEYALG_NSEC3RSASHA1:
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
486
		if (size != 0 && (size < 512 || size > MAX_RSA))
487
			fatal("RSA key size %d out of range", size);
Brian Wellington's avatar
Brian Wellington committed
488 489 490
		break;
	case DNS_KEYALG_DH:
		if (size != 0 && (size < 128 || size > 4096))
491
			fatal("DH key size %d out of range", size);
Brian Wellington's avatar
Brian Wellington committed
492 493
		break;
	case DNS_KEYALG_DSA:
494
	case DNS_KEYALG_NSEC3DSA:
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
495
		if (size != 0 && !dsa_size_ok(size))
496
			fatal("invalid DSS key size: %d", size);
Brian Wellington's avatar
Brian Wellington committed
497 498 499
		break;
	case DST_ALG_HMACMD5:
		if (size < 1 || size > 512)
500
			fatal("HMAC-MD5 key size %d out of range", size);
501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550
		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
551 552 553
		break;
	}

554 555
	if (!(alg == DNS_KEYALG_RSAMD5 || alg == DNS_KEYALG_RSASHA1 ||
	      alg == DNS_KEYALG_NSEC3RSASHA1) && rsa_exp != 0)
Brian Wellington's avatar
Brian Wellington committed
556
		fatal("specified RSA exponent for a non-RSA key");
Brian Wellington's avatar
Brian Wellington committed
557 558

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

561 562 563 564 565
	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
566
		flags |= DNS_KEYOWNER_ZONE;
567
	else if ((options & DST_TYPE_KEY) != 0)	{ /* KEY / HMAC */
568 569 570 571 572 573 574 575 576
		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
577

578
	rdclass = strtoclass(classname);
579

580 581 582
	if (directory == NULL)
		directory = ".";

583
	if ((options & DST_TYPE_KEY) != 0)  /* KEY / HMAC */
584
		flags |= signatory;
585
	else if ((flags & DNS_KEYOWNER_ZONE) != 0) { /* DNSKEY */
586 587
		flags |= kskflag;
		flags |= revflag;
Automatic Updater's avatar
Automatic Updater committed
588
	}
Brian Wellington's avatar
Brian Wellington committed
589

590 591
	if (protocol == -1)
		protocol = DNS_KEYPROTO_DNSSEC;
592 593 594
	else if ((options & DST_TYPE_KEY) == 0 &&
		 protocol != DNS_KEYPROTO_DNSSEC)
		fatal("invalid DNSKEY protocol: %d", protocol);
Brian Wellington's avatar
Brian Wellington committed
595 596 597

	if ((flags & DNS_KEYFLAG_TYPEMASK) == DNS_KEYTYPE_NOKEY) {
		if (size > 0)
598
			fatal("specified null key with non-zero size");
Brian Wellington's avatar
Brian Wellington committed
599
		if ((flags & DNS_KEYFLAG_SIGNATORYMASK) != 0)
600
			fatal("specified null key with signing authority");
Brian Wellington's avatar
Brian Wellington committed
601
	}
Brian Wellington's avatar
Brian Wellington committed
602

603
	if ((flags & DNS_KEYFLAG_OWNERMASK) == DNS_KEYOWNER_ZONE &&
604 605 606 607
	    (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))
608
		fatal("a key with algorithm '%s' cannot be a zone key",
609 610
		      algname);

611 612 613 614 615
	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]));
616
	ret = dns_name_fromtext(name, &buf, dns_rootname, 0, NULL);
617
	if (ret != ISC_R_SUCCESS)
618
		fatal("invalid key name %s: %s", argv[isc_commandline_index],
619
		      isc_result_totext(ret));
Brian Wellington's avatar
Brian Wellington committed
620 621

	switch(alg) {
Brian Wellington's avatar
Brian Wellington committed
622 623
	case DNS_KEYALG_RSAMD5:
	case DNS_KEYALG_RSASHA1:
Brian Wellington's avatar
Brian Wellington committed
624 625 626 627 628 629 630
		param = rsa_exp;
		break;
	case DNS_KEYALG_DH:
		param = generator;
		break;
	case DNS_KEYALG_DSA:
	case DST_ALG_HMACMD5:
631 632 633 634 635
	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
636 637
		param = 0;
		break;
Brian Wellington's avatar
Brian Wellington committed
638 639
	}

640 641 642 643 644
	if ((flags & DNS_KEYFLAG_TYPEMASK) == DNS_KEYTYPE_NOKEY)
		null_key = ISC_TRUE;

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

645 646
	do {
		conflict = ISC_FALSE;
647
		oldkey = NULL;
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
648 649 650

		/* generate the key */
		ret = dst_key_generate(name, alg, size, param, flags, protocol,
651
				       rdclass, mctx, &key);
652
		isc_entropy_stopcallbacksources(ectx);
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
653 654

		if (ret != ISC_R_SUCCESS) {
655 656
			char namestr[DNS_NAME_FORMATSIZE];
			char algstr[ALG_FORMATSIZE];
Andreas Gustafsson's avatar
Andreas Gustafsson committed
657 658
			dns_name_format(name, namestr, sizeof(namestr));
			alg_format(alg, algstr, sizeof(algstr));
659
			fatal("failed to generate key %s/%s: %s\n",
660
			      namestr, algstr, isc_result_totext(ret));
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
661 662
			exit(-1);
		}
663

664 665
		dst_key_setbits(key, dbits);

666
		/*
667
		 * Set key timing metadata (unless using -C)
668 669 670
		 */
		if (!oldstyle) {
			dst_key_settime(key, DST_TIME_CREATED, now);
671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693

			if (setpub)
				dst_key_settime(key, DST_TIME_PUBLISH,
						publish);
			if (setact)
				dst_key_settime(key, DST_TIME_ACTIVATE,
						activate);
			if (setrev)
				dst_key_settime(key, DST_TIME_REVOKE,
						revoke);
			if (setunpub)
				dst_key_settime(key, DST_TIME_UNPUBLISH,
						unpublish);
			if (setdel)
				dst_key_settime(key, DST_TIME_DELETE,
						delete);
		} else {
			if (setpub || setact || setrev || setunpub ||
			    setdel || unsetpub || unsetact ||
			    unsetrev || unsetunpub || unsetdel)
				fatal("cannot use -C together with "
				      "-P, -A, -R, -U, or -D options");
			/*
Automatic Updater's avatar
Automatic Updater committed
694
			 * Compatibility mode: Private-key-format
695 696 697
			 * should be set to 1.2.
			 */
			dst_key_setprivateformat(key, 1, 2);
698 699
		}

700 701
		/*
		 * Try to read a key with the same name, alg and id from disk.
702
		 * If there is one we must continue generating a new one
703 704
		 * unless we were asked to generate a null key, in which
		 * case we return failure.
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
705
		 */
706
		ret = dst_key_fromfile(name, dst_key_id(key), alg,
Evan Hunt's avatar
Evan Hunt committed
707 708
				       DST_TYPE_PRIVATE, directory,
				       mctx, &oldkey);
709 710
		/* do not overwrite an existing key  */
		if (ret == ISC_R_SUCCESS) {
711
			dst_key_free(&oldkey);
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
712
			conflict = ISC_TRUE;
713 714
			if (null_key)
				break;
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
715
		}
716 717 718
		if (conflict == ISC_TRUE) {
			if (verbose > 0) {
				isc_buffer_clear(&buf);
Evan Hunt's avatar
Evan Hunt committed
719 720
				ret = dst_key_buildfilename(key, 0, directory,
							    &buf);
721 722 723
				fprintf(stderr,
					"%s: %s already exists, "
					"generating a new key\n",
724
					program, filename);
725
			}
726
			dst_key_free(&key);
727
		}
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
728

729
	} while (conflict == ISC_TRUE);
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
730

731
	if (conflict)
732 733
		fatal("cannot generate a null key when a key with id 0 "
		      "already exists");
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
734

735
	ret = dst_key_tofile(key, options, directory);
736 737
	if (ret != ISC_R_SUCCESS) {
		char keystr[KEY_FORMATSIZE];
Andreas Gustafsson's avatar
Andreas Gustafsson committed
738
		key_format(key, keystr, sizeof(keystr));
739 740 741
		fatal("failed to write key %s: %s\n", keystr,
		      isc_result_totext(ret));
	}
742 743

	isc_buffer_clear(&buf);
Brian Wellington's avatar
Brian Wellington committed
744
	ret = dst_key_buildfilename(key, 0, NULL, &buf);
745
	printf("%s\n", filename);
746
	dst_key_free(&key);
747

Brian Wellington's avatar
Brian Wellington committed
748
	cleanup_logging(&log);
Brian Wellington's avatar
Brian Wellington committed
749
	cleanup_entropy(&ectx);
Brian Wellington's avatar
Brian Wellington committed
750
	dst_lib_destroy();
751
	dns_name_destroy();
752 753
	if (verbose > 10)
		isc_mem_stats(mctx, stdout);
Brian Wellington's avatar
Brian Wellington committed
754
	isc_mem_destroy(&mctx);
755 756

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