1/*	$NetBSD: dnssec-keygen.c,v 1.11 2024/02/21 22:51:03 christos Exp $	*/
2
3/*
4 * Portions Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5 *
6 * SPDX-License-Identifier: MPL-2.0
7 *
8 * This Source Code Form is subject to the terms of the Mozilla Public
9 * License, v. 2.0. If a copy of the MPL was not distributed with this
10 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11 *
12 * See the COPYRIGHT file distributed with this work for additional
13 * information regarding copyright ownership.
14 *
15 * Portions Copyright (C) Network Associates, Inc.
16 *
17 * Permission to use, copy, modify, and/or distribute this software for any
18 * purpose with or without fee is hereby granted, provided that the above
19 * copyright notice and this permission notice appear in all copies.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
22 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
23 * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
24 * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
25 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
26 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
27 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
28 */
29
30/*! \file */
31
32#include <ctype.h>
33#include <inttypes.h>
34#include <stdbool.h>
35#include <stdlib.h>
36#include <unistd.h>
37
38#include <isc/attributes.h>
39#include <isc/buffer.h>
40#include <isc/commandline.h>
41#include <isc/mem.h>
42#include <isc/print.h>
43#include <isc/region.h>
44#include <isc/result.h>
45#include <isc/string.h>
46#include <isc/util.h>
47
48#include <dns/dnssec.h>
49#include <dns/fixedname.h>
50#include <dns/kasp.h>
51#include <dns/keyvalues.h>
52#include <dns/log.h>
53#include <dns/name.h>
54#include <dns/rdataclass.h>
55#include <dns/secalg.h>
56
57#include <dst/dst.h>
58
59#include <isccfg/cfg.h>
60#include <isccfg/grammar.h>
61#include <isccfg/kaspconf.h>
62#include <isccfg/namedconf.h>
63
64#include "dnssectool.h"
65
66#define MAX_RSA 4096 /* should be long enough... */
67
68const char *program = "dnssec-keygen";
69
70isc_log_t *lctx = NULL;
71
72noreturn static void
73usage(void);
74
75static void
76progress(int p);
77
78struct keygen_ctx {
79	const char *predecessor;
80	const char *policy;
81	const char *configfile;
82	const char *directory;
83	char *algname;
84	char *nametype;
85	char *type;
86	int generator;
87	int protocol;
88	int size;
89	int signatory;
90	dns_rdataclass_t rdclass;
91	int options;
92	int dbits;
93	dns_ttl_t ttl;
94	uint16_t kskflag;
95	uint16_t revflag;
96	dns_secalg_t alg;
97	/* timing data */
98	int prepub;
99	isc_stdtime_t now;
100	isc_stdtime_t publish;
101	isc_stdtime_t activate;
102	isc_stdtime_t inactive;
103	isc_stdtime_t revokekey;
104	isc_stdtime_t deltime;
105	isc_stdtime_t syncadd;
106	isc_stdtime_t syncdel;
107	bool setpub;
108	bool setact;
109	bool setinact;
110	bool setrev;
111	bool setdel;
112	bool setsyncadd;
113	bool setsyncdel;
114	bool unsetpub;
115	bool unsetact;
116	bool unsetinact;
117	bool unsetrev;
118	bool unsetdel;
119	/* how to generate the key */
120	bool setttl;
121	bool use_nsec3;
122	bool genonly;
123	bool showprogress;
124	bool quiet;
125	bool oldstyle;
126	/* state */
127	time_t lifetime;
128	bool ksk;
129	bool zsk;
130};
131
132typedef struct keygen_ctx keygen_ctx_t;
133
134static void
135usage(void) {
136	fprintf(stderr, "Usage:\n");
137	fprintf(stderr, "    %s [options] name\n\n", program);
138	fprintf(stderr, "Version: %s\n", PACKAGE_VERSION);
139	fprintf(stderr, "    name: owner of the key\n");
140	fprintf(stderr, "Options:\n");
141	fprintf(stderr, "    -K <directory>: write keys into directory\n");
142	fprintf(stderr, "    -k <policy>: generate keys for dnssec-policy\n");
143	fprintf(stderr, "    -l <file>: configuration file with dnssec-policy "
144			"statement\n");
145	fprintf(stderr, "    -a <algorithm>:\n");
146	fprintf(stderr, "        RSASHA1 | NSEC3RSASHA1 |\n");
147	fprintf(stderr, "        RSASHA256 | RSASHA512 |\n");
148	fprintf(stderr, "        ECDSAP256SHA256 | ECDSAP384SHA384 |\n");
149	fprintf(stderr, "        ED25519 | ED448 | DH\n");
150	fprintf(stderr, "    -3: use NSEC3-capable algorithm\n");
151	fprintf(stderr, "    -b <key size in bits>:\n");
152	fprintf(stderr, "        RSASHA1:\t[1024..%d]\n", MAX_RSA);
153	fprintf(stderr, "        NSEC3RSASHA1:\t[1024..%d]\n", MAX_RSA);
154	fprintf(stderr, "        RSASHA256:\t[1024..%d]\n", MAX_RSA);
155	fprintf(stderr, "        RSASHA512:\t[1024..%d]\n", MAX_RSA);
156	fprintf(stderr, "        DH:\t\t[128..4096]\n");
157	fprintf(stderr, "        ECDSAP256SHA256:\tignored\n");
158	fprintf(stderr, "        ECDSAP384SHA384:\tignored\n");
159	fprintf(stderr, "        ED25519:\tignored\n");
160	fprintf(stderr, "        ED448:\tignored\n");
161	fprintf(stderr, "        (key size defaults are set according to\n"
162			"        algorithm and usage (ZSK or KSK)\n");
163	fprintf(stderr, "    -n <nametype>: ZONE | HOST | ENTITY | "
164			"USER | OTHER\n");
165	fprintf(stderr, "        (DNSKEY generation defaults to ZONE)\n");
166	fprintf(stderr, "    -c <class>: (default: IN)\n");
167	fprintf(stderr, "    -d <digest bits> (0 => max, default)\n");
168	fprintf(stderr, "    -E <engine>:\n");
169	fprintf(stderr, "        name of an OpenSSL engine to use\n");
170	fprintf(stderr, "    -f <keyflag>: KSK | REVOKE\n");
171	fprintf(stderr, "    -g <generator>: use specified generator "
172			"(DH only)\n");
173	fprintf(stderr, "    -L <ttl>: default key TTL\n");
174	fprintf(stderr, "    -p <protocol>: (default: 3 [dnssec])\n");
175	fprintf(stderr, "    -s <strength>: strength value this key signs DNS "
176			"records with (default: 0)\n");
177	fprintf(stderr, "    -T <rrtype>: DNSKEY | KEY (default: DNSKEY; "
178			"use KEY for SIG(0))\n");
179	fprintf(stderr, "    -t <type>: "
180			"AUTHCONF | NOAUTHCONF | NOAUTH | NOCONF "
181			"(default: AUTHCONF)\n");
182	fprintf(stderr, "    -h: print usage and exit\n");
183	fprintf(stderr, "    -m <memory debugging mode>:\n");
184	fprintf(stderr, "       usage | trace | record | size | mctx\n");
185	fprintf(stderr, "    -v <level>: set verbosity level (0 - 10)\n");
186	fprintf(stderr, "    -V: print version information\n");
187	fprintf(stderr, "Timing options:\n");
188	fprintf(stderr, "    -P date/[+-]offset/none: set key publication date "
189			"(default: now)\n");
190	fprintf(stderr, "    -P sync date/[+-]offset/none: set CDS and CDNSKEY "
191			"publication date\n");
192	fprintf(stderr, "    -A date/[+-]offset/none: set key activation date "
193			"(default: now)\n");
194	fprintf(stderr, "    -R date/[+-]offset/none: set key "
195			"revocation date\n");
196	fprintf(stderr, "    -I date/[+-]offset/none: set key "
197			"inactivation date\n");
198	fprintf(stderr, "    -D date/[+-]offset/none: set key deletion date\n");
199	fprintf(stderr, "    -D sync date/[+-]offset/none: set CDS and CDNSKEY "
200			"deletion date\n");
201
202	fprintf(stderr, "    -G: generate key only; do not set -P or -A\n");
203	fprintf(stderr, "    -C: generate a backward-compatible key, omitting "
204			"all dates\n");
205	fprintf(stderr, "    -S <key>: generate a successor to an existing "
206			"key\n");
207	fprintf(stderr, "    -i <interval>: prepublication interval for "
208			"successor key "
209			"(default: 30 days)\n");
210	fprintf(stderr, "Output:\n");
211	fprintf(stderr, "     K<name>+<alg>+<id>.key, "
212			"K<name>+<alg>+<id>.private\n");
213
214	exit(-1);
215}
216
217static void
218progress(int p) {
219	char c = '*';
220
221	switch (p) {
222	case 0:
223		c = '.';
224		break;
225	case 1:
226		c = '+';
227		break;
228	case 2:
229		c = '*';
230		break;
231	case 3:
232		c = ' ';
233		break;
234	default:
235		break;
236	}
237	(void)putc(c, stderr);
238	(void)fflush(stderr);
239}
240
241static void
242kasp_from_conf(cfg_obj_t *config, isc_mem_t *mctx, const char *name,
243	       dns_kasp_t **kaspp) {
244	const cfg_listelt_t *element;
245	const cfg_obj_t *kasps = NULL;
246	dns_kasp_t *kasp = NULL, *kasp_next;
247	isc_result_t result = ISC_R_NOTFOUND;
248	dns_kasplist_t kasplist;
249
250	ISC_LIST_INIT(kasplist);
251
252	(void)cfg_map_get(config, "dnssec-policy", &kasps);
253	for (element = cfg_list_first(kasps); element != NULL;
254	     element = cfg_list_next(element))
255	{
256		cfg_obj_t *kconfig = cfg_listelt_value(element);
257		kasp = NULL;
258		if (strcmp(cfg_obj_asstring(cfg_tuple_get(kconfig, "name")),
259			   name) != 0)
260		{
261			continue;
262		}
263
264		result = cfg_kasp_fromconfig(kconfig, NULL, mctx, lctx,
265					     &kasplist, &kasp);
266		if (result != ISC_R_SUCCESS) {
267			fatal("failed to configure dnssec-policy '%s': %s",
268			      cfg_obj_asstring(cfg_tuple_get(kconfig, "name")),
269			      isc_result_totext(result));
270		}
271		INSIST(kasp != NULL);
272		dns_kasp_freeze(kasp);
273		break;
274	}
275
276	*kaspp = kasp;
277
278	/*
279	 * Cleanup kasp list.
280	 */
281	for (kasp = ISC_LIST_HEAD(kasplist); kasp != NULL; kasp = kasp_next) {
282		kasp_next = ISC_LIST_NEXT(kasp, link);
283		ISC_LIST_UNLINK(kasplist, kasp, link);
284		dns_kasp_detach(&kasp);
285	}
286}
287
288static void
289keygen(keygen_ctx_t *ctx, isc_mem_t *mctx, int argc, char **argv) {
290	char filename[255];
291	char algstr[DNS_SECALG_FORMATSIZE];
292	uint16_t flags = 0;
293	int param = 0;
294	bool null_key = false;
295	bool conflict = false;
296	bool show_progress = false;
297	isc_buffer_t buf;
298	dns_name_t *name;
299	dns_fixedname_t fname;
300	isc_result_t ret;
301	dst_key_t *key = NULL;
302	dst_key_t *prevkey = NULL;
303
304	UNUSED(argc);
305
306	dns_secalg_format(ctx->alg, algstr, sizeof(algstr));
307
308	if (ctx->predecessor == NULL) {
309		if (ctx->prepub == -1) {
310			ctx->prepub = 0;
311		}
312
313		name = dns_fixedname_initname(&fname);
314		isc_buffer_init(&buf, argv[isc_commandline_index],
315				strlen(argv[isc_commandline_index]));
316		isc_buffer_add(&buf, strlen(argv[isc_commandline_index]));
317		ret = dns_name_fromtext(name, &buf, dns_rootname, 0, NULL);
318		if (ret != ISC_R_SUCCESS) {
319			fatal("invalid key name %s: %s",
320			      argv[isc_commandline_index],
321			      isc_result_totext(ret));
322		}
323
324		if (!dst_algorithm_supported(ctx->alg)) {
325			fatal("unsupported algorithm: %s", algstr);
326		}
327
328		if (ctx->alg == DST_ALG_DH) {
329			ctx->options |= DST_TYPE_KEY;
330		}
331
332		if (ctx->use_nsec3) {
333			switch (ctx->alg) {
334			case DST_ALG_RSASHA1:
335				ctx->alg = DST_ALG_NSEC3RSASHA1;
336				break;
337			case DST_ALG_NSEC3RSASHA1:
338			case DST_ALG_RSASHA256:
339			case DST_ALG_RSASHA512:
340			case DST_ALG_ECDSA256:
341			case DST_ALG_ECDSA384:
342			case DST_ALG_ED25519:
343			case DST_ALG_ED448:
344				break;
345			default:
346				fatal("algorithm %s is incompatible with NSEC3"
347				      ", do not use the -3 option",
348				      algstr);
349			}
350		}
351
352		if (ctx->type != NULL && (ctx->options & DST_TYPE_KEY) != 0) {
353			if (strcasecmp(ctx->type, "NOAUTH") == 0) {
354				flags |= DNS_KEYTYPE_NOAUTH;
355			} else if (strcasecmp(ctx->type, "NOCONF") == 0) {
356				flags |= DNS_KEYTYPE_NOCONF;
357			} else if (strcasecmp(ctx->type, "NOAUTHCONF") == 0) {
358				flags |= (DNS_KEYTYPE_NOAUTH |
359					  DNS_KEYTYPE_NOCONF);
360				if (ctx->size < 0) {
361					ctx->size = 0;
362				}
363			} else if (strcasecmp(ctx->type, "AUTHCONF") == 0) {
364				/* nothing */
365			} else {
366				fatal("invalid type %s", ctx->type);
367			}
368		}
369
370		if (ctx->size < 0) {
371			switch (ctx->alg) {
372			case DST_ALG_RSASHA1:
373			case DST_ALG_NSEC3RSASHA1:
374			case DST_ALG_RSASHA256:
375			case DST_ALG_RSASHA512:
376				ctx->size = 2048;
377				if (verbose > 0) {
378					fprintf(stderr,
379						"key size not "
380						"specified; defaulting"
381						" to %d\n",
382						ctx->size);
383				}
384				break;
385			case DST_ALG_ECDSA256:
386			case DST_ALG_ECDSA384:
387			case DST_ALG_ED25519:
388			case DST_ALG_ED448:
389				break;
390			default:
391				fatal("key size not specified (-b option)");
392			}
393		}
394
395		if (!ctx->oldstyle && ctx->prepub > 0) {
396			if (ctx->setpub && ctx->setact &&
397			    (ctx->activate - ctx->prepub) < ctx->publish)
398			{
399				fatal("Activation and publication dates "
400				      "are closer together than the\n\t"
401				      "prepublication interval.");
402			}
403
404			if (!ctx->setpub && !ctx->setact) {
405				ctx->setpub = ctx->setact = true;
406				ctx->publish = ctx->now;
407				ctx->activate = ctx->now + ctx->prepub;
408			} else if (ctx->setpub && !ctx->setact) {
409				ctx->setact = true;
410				ctx->activate = ctx->publish + ctx->prepub;
411			} else if (ctx->setact && !ctx->setpub) {
412				ctx->setpub = true;
413				ctx->publish = ctx->activate - ctx->prepub;
414			}
415
416			if ((ctx->activate - ctx->prepub) < ctx->now) {
417				fatal("Time until activation is shorter "
418				      "than the\n\tprepublication interval.");
419			}
420		}
421	} else {
422		char keystr[DST_KEY_FORMATSIZE];
423		isc_stdtime_t when;
424		int major, minor;
425
426		if (ctx->prepub == -1) {
427			ctx->prepub = (30 * 86400);
428		}
429
430		if (ctx->alg != 0) {
431			fatal("-S and -a cannot be used together");
432		}
433		if (ctx->size >= 0) {
434			fatal("-S and -b cannot be used together");
435		}
436		if (ctx->nametype != NULL) {
437			fatal("-S and -n cannot be used together");
438		}
439		if (ctx->type != NULL) {
440			fatal("-S and -t cannot be used together");
441		}
442		if (ctx->setpub || ctx->unsetpub) {
443			fatal("-S and -P cannot be used together");
444		}
445		if (ctx->setact || ctx->unsetact) {
446			fatal("-S and -A cannot be used together");
447		}
448		if (ctx->use_nsec3) {
449			fatal("-S and -3 cannot be used together");
450		}
451		if (ctx->oldstyle) {
452			fatal("-S and -C cannot be used together");
453		}
454		if (ctx->genonly) {
455			fatal("-S and -G cannot be used together");
456		}
457
458		ret = dst_key_fromnamedfile(
459			ctx->predecessor, ctx->directory,
460			(DST_TYPE_PUBLIC | DST_TYPE_PRIVATE | DST_TYPE_STATE),
461			mctx, &prevkey);
462		if (ret != ISC_R_SUCCESS) {
463			fatal("Invalid keyfile %s: %s", ctx->predecessor,
464			      isc_result_totext(ret));
465		}
466		if (!dst_key_isprivate(prevkey)) {
467			fatal("%s is not a private key", ctx->predecessor);
468		}
469
470		name = dst_key_name(prevkey);
471		ctx->alg = dst_key_alg(prevkey);
472		ctx->size = dst_key_size(prevkey);
473		flags = dst_key_flags(prevkey);
474
475		dst_key_format(prevkey, keystr, sizeof(keystr));
476		dst_key_getprivateformat(prevkey, &major, &minor);
477		if (major != DST_MAJOR_VERSION || minor < DST_MINOR_VERSION) {
478			fatal("Key %s has incompatible format version %d.%d\n\t"
479			      "It is not possible to generate a successor key.",
480			      keystr, major, minor);
481		}
482
483		ret = dst_key_gettime(prevkey, DST_TIME_ACTIVATE, &when);
484		if (ret != ISC_R_SUCCESS) {
485			fatal("Key %s has no activation date.\n\t"
486			      "You must use dnssec-settime -A to set one "
487			      "before generating a successor.",
488			      keystr);
489		}
490
491		ret = dst_key_gettime(prevkey, DST_TIME_INACTIVE,
492				      &ctx->activate);
493		if (ret != ISC_R_SUCCESS) {
494			fatal("Key %s has no inactivation date.\n\t"
495			      "You must use dnssec-settime -I to set one "
496			      "before generating a successor.",
497			      keystr);
498		}
499
500		ctx->publish = ctx->activate - ctx->prepub;
501		if (ctx->publish < ctx->now) {
502			fatal("Key %s becomes inactive\n\t"
503			      "sooner than the prepublication period "
504			      "for the new key ends.\n\t"
505			      "Either change the inactivation date with "
506			      "dnssec-settime -I,\n\t"
507			      "or use the -i option to set a shorter "
508			      "prepublication interval.",
509			      keystr);
510		}
511
512		ret = dst_key_gettime(prevkey, DST_TIME_DELETE, &when);
513		if (ret != ISC_R_SUCCESS) {
514			fprintf(stderr,
515				"%s: WARNING: Key %s has no removal "
516				"date;\n\t it will remain in the zone "
517				"indefinitely after rollover.\n\t "
518				"You can use dnssec-settime -D to "
519				"change this.\n",
520				program, keystr);
521		}
522
523		ctx->setpub = ctx->setact = true;
524	}
525
526	switch (ctx->alg) {
527	case DNS_KEYALG_RSASHA1:
528	case DNS_KEYALG_NSEC3RSASHA1:
529	case DNS_KEYALG_RSASHA256:
530		if (ctx->size != 0 && (ctx->size < 1024 || ctx->size > MAX_RSA))
531		{
532			fatal("RSA key size %d out of range", ctx->size);
533		}
534		break;
535	case DNS_KEYALG_RSASHA512:
536		if (ctx->size != 0 && (ctx->size < 1024 || ctx->size > MAX_RSA))
537		{
538			fatal("RSA key size %d out of range", ctx->size);
539		}
540		break;
541	case DNS_KEYALG_DH:
542		if (ctx->size != 0 && (ctx->size < 128 || ctx->size > 4096)) {
543			fatal("DH key size %d out of range", ctx->size);
544		}
545		break;
546	case DST_ALG_ECDSA256:
547		ctx->size = 256;
548		break;
549	case DST_ALG_ECDSA384:
550		ctx->size = 384;
551		break;
552	case DST_ALG_ED25519:
553		ctx->size = 256;
554		break;
555	case DST_ALG_ED448:
556		ctx->size = 456;
557		break;
558	}
559
560	if (ctx->alg != DNS_KEYALG_DH && ctx->generator != 0) {
561		fatal("specified DH generator for a non-DH key");
562	}
563
564	if (ctx->nametype == NULL) {
565		if ((ctx->options & DST_TYPE_KEY) != 0) { /* KEY */
566			fatal("no nametype specified");
567		}
568		flags |= DNS_KEYOWNER_ZONE; /* DNSKEY */
569	} else if (strcasecmp(ctx->nametype, "zone") == 0) {
570		flags |= DNS_KEYOWNER_ZONE;
571	} else if ((ctx->options & DST_TYPE_KEY) != 0) { /* KEY */
572		if (strcasecmp(ctx->nametype, "host") == 0 ||
573		    strcasecmp(ctx->nametype, "entity") == 0)
574		{
575			flags |= DNS_KEYOWNER_ENTITY;
576		} else if (strcasecmp(ctx->nametype, "user") == 0) {
577			flags |= DNS_KEYOWNER_USER;
578		} else {
579			fatal("invalid KEY nametype %s", ctx->nametype);
580		}
581	} else if (strcasecmp(ctx->nametype, "other") != 0) { /* DNSKEY */
582		fatal("invalid DNSKEY nametype %s", ctx->nametype);
583	}
584
585	if (ctx->directory == NULL) {
586		ctx->directory = ".";
587	}
588
589	if ((ctx->options & DST_TYPE_KEY) != 0) { /* KEY */
590		flags |= ctx->signatory;
591	} else if ((flags & DNS_KEYOWNER_ZONE) != 0) { /* DNSKEY */
592		flags |= ctx->kskflag;
593		flags |= ctx->revflag;
594	}
595
596	if (ctx->protocol == -1) {
597		ctx->protocol = DNS_KEYPROTO_DNSSEC;
598	} else if ((ctx->options & DST_TYPE_KEY) == 0 &&
599		   ctx->protocol != DNS_KEYPROTO_DNSSEC)
600	{
601		fatal("invalid DNSKEY protocol: %d", ctx->protocol);
602	}
603
604	if ((flags & DNS_KEYFLAG_TYPEMASK) == DNS_KEYTYPE_NOKEY) {
605		if (ctx->size > 0) {
606			fatal("specified null key with non-zero size");
607		}
608		if ((flags & DNS_KEYFLAG_SIGNATORYMASK) != 0) {
609			fatal("specified null key with signing authority");
610		}
611	}
612
613	if ((flags & DNS_KEYFLAG_OWNERMASK) == DNS_KEYOWNER_ZONE &&
614	    ctx->alg == DNS_KEYALG_DH)
615	{
616		fatal("a key with algorithm %s cannot be a zone key", algstr);
617	}
618
619	switch (ctx->alg) {
620	case DNS_KEYALG_RSASHA1:
621	case DNS_KEYALG_NSEC3RSASHA1:
622	case DNS_KEYALG_RSASHA256:
623	case DNS_KEYALG_RSASHA512:
624		show_progress = true;
625		break;
626
627	case DNS_KEYALG_DH:
628		param = ctx->generator;
629		break;
630
631	case DST_ALG_ECDSA256:
632	case DST_ALG_ECDSA384:
633	case DST_ALG_ED25519:
634	case DST_ALG_ED448:
635		show_progress = true;
636		break;
637	}
638
639	if ((flags & DNS_KEYFLAG_TYPEMASK) == DNS_KEYTYPE_NOKEY) {
640		null_key = true;
641	}
642
643	isc_buffer_init(&buf, filename, sizeof(filename) - 1);
644
645	do {
646		conflict = false;
647
648		if (!ctx->quiet && show_progress) {
649			fprintf(stderr, "Generating key pair.");
650			ret = dst_key_generate(name, ctx->alg, ctx->size, param,
651					       flags, ctx->protocol,
652					       ctx->rdclass, mctx, &key,
653					       &progress);
654			putc('\n', stderr);
655			fflush(stderr);
656		} else {
657			ret = dst_key_generate(name, ctx->alg, ctx->size, param,
658					       flags, ctx->protocol,
659					       ctx->rdclass, mctx, &key, NULL);
660		}
661
662		if (ret != ISC_R_SUCCESS) {
663			char namestr[DNS_NAME_FORMATSIZE];
664			dns_name_format(name, namestr, sizeof(namestr));
665			fatal("failed to generate key %s/%s: %s\n", namestr,
666			      algstr, isc_result_totext(ret));
667		}
668
669		dst_key_setbits(key, ctx->dbits);
670
671		/*
672		 * Set key timing metadata (unless using -C)
673		 *
674		 * Creation date is always set to "now".
675		 *
676		 * For a new key without an explicit predecessor, publish
677		 * and activation dates are set to "now" by default, but
678		 * can both be overridden.
679		 *
680		 * For a successor key, activation is set to match the
681		 * predecessor's inactivation date.  Publish is set to 30
682		 * days earlier than that (XXX: this should be configurable).
683		 * If either of the resulting dates are in the past, that's
684		 * an error; the inactivation date of the predecessor key
685		 * must be updated before a successor key can be created.
686		 */
687		if (!ctx->oldstyle) {
688			dst_key_settime(key, DST_TIME_CREATED, ctx->now);
689
690			if (ctx->genonly && (ctx->setpub || ctx->setact)) {
691				fatal("cannot use -G together with "
692				      "-P or -A options");
693			}
694
695			if (ctx->setpub) {
696				dst_key_settime(key, DST_TIME_PUBLISH,
697						ctx->publish);
698			} else if (ctx->setact && !ctx->unsetpub) {
699				dst_key_settime(key, DST_TIME_PUBLISH,
700						ctx->activate - ctx->prepub);
701			} else if (!ctx->genonly && !ctx->unsetpub) {
702				dst_key_settime(key, DST_TIME_PUBLISH,
703						ctx->now);
704			}
705
706			if (ctx->setact) {
707				dst_key_settime(key, DST_TIME_ACTIVATE,
708						ctx->activate);
709			} else if (!ctx->genonly && !ctx->unsetact) {
710				dst_key_settime(key, DST_TIME_ACTIVATE,
711						ctx->now);
712			}
713
714			if (ctx->setrev) {
715				if (ctx->kskflag == 0) {
716					fprintf(stderr,
717						"%s: warning: Key is "
718						"not flagged as a KSK, but -R "
719						"was used. Revoking a ZSK is "
720						"legal, but undefined.\n",
721						program);
722				}
723				dst_key_settime(key, DST_TIME_REVOKE,
724						ctx->revokekey);
725			}
726
727			if (ctx->setinact) {
728				dst_key_settime(key, DST_TIME_INACTIVE,
729						ctx->inactive);
730			}
731
732			if (ctx->setdel) {
733				if (ctx->setinact &&
734				    ctx->deltime < ctx->inactive)
735				{
736					fprintf(stderr,
737						"%s: warning: Key is "
738						"scheduled to be deleted "
739						"before it is scheduled to be "
740						"made inactive.\n",
741						program);
742				}
743				dst_key_settime(key, DST_TIME_DELETE,
744						ctx->deltime);
745			}
746
747			if (ctx->setsyncadd) {
748				dst_key_settime(key, DST_TIME_SYNCPUBLISH,
749						ctx->syncadd);
750			}
751
752			if (ctx->setsyncdel) {
753				dst_key_settime(key, DST_TIME_SYNCDELETE,
754						ctx->syncdel);
755			}
756		} else {
757			if (ctx->setpub || ctx->setact || ctx->setrev ||
758			    ctx->setinact || ctx->setdel || ctx->unsetpub ||
759			    ctx->unsetact || ctx->unsetrev || ctx->unsetinact ||
760			    ctx->unsetdel || ctx->genonly || ctx->setsyncadd ||
761			    ctx->setsyncdel)
762			{
763				fatal("cannot use -C together with "
764				      "-P, -A, -R, -I, -D, or -G options");
765			}
766			/*
767			 * Compatibility mode: Private-key-format
768			 * should be set to 1.2.
769			 */
770			dst_key_setprivateformat(key, 1, 2);
771		}
772
773		/* Set the default key TTL */
774		if (ctx->setttl) {
775			dst_key_setttl(key, ctx->ttl);
776		}
777
778		/* Set dnssec-policy related metadata */
779		if (ctx->policy != NULL) {
780			dst_key_setnum(key, DST_NUM_LIFETIME, ctx->lifetime);
781			dst_key_setbool(key, DST_BOOL_KSK, ctx->ksk);
782			dst_key_setbool(key, DST_BOOL_ZSK, ctx->zsk);
783		}
784
785		/*
786		 * Do not overwrite an existing key, or create a key
787		 * if there is a risk of ID collision due to this key
788		 * or another key being revoked.
789		 */
790		if (key_collision(key, name, ctx->directory, mctx, NULL)) {
791			conflict = true;
792			if (null_key) {
793				dst_key_free(&key);
794				break;
795			}
796
797			if (verbose > 0) {
798				isc_buffer_clear(&buf);
799				ret = dst_key_buildfilename(
800					key, 0, ctx->directory, &buf);
801				if (ret == ISC_R_SUCCESS) {
802					fprintf(stderr,
803						"%s: %s already exists, or "
804						"might collide with another "
805						"key upon revokation.  "
806						"Generating a new key\n",
807						program, filename);
808				}
809			}
810
811			dst_key_free(&key);
812		}
813	} while (conflict);
814
815	if (conflict) {
816		fatal("cannot generate a null key due to possible key ID "
817		      "collision");
818	}
819
820	if (ctx->predecessor != NULL && prevkey != NULL) {
821		dst_key_setnum(prevkey, DST_NUM_SUCCESSOR, dst_key_id(key));
822		dst_key_setnum(key, DST_NUM_PREDECESSOR, dst_key_id(prevkey));
823
824		ret = dst_key_tofile(prevkey, ctx->options, ctx->directory);
825		if (ret != ISC_R_SUCCESS) {
826			char keystr[DST_KEY_FORMATSIZE];
827			dst_key_format(prevkey, keystr, sizeof(keystr));
828			fatal("failed to update predecessor %s: %s\n", keystr,
829			      isc_result_totext(ret));
830		}
831	}
832
833	ret = dst_key_tofile(key, ctx->options, ctx->directory);
834	if (ret != ISC_R_SUCCESS) {
835		char keystr[DST_KEY_FORMATSIZE];
836		dst_key_format(key, keystr, sizeof(keystr));
837		fatal("failed to write key %s: %s\n", keystr,
838		      isc_result_totext(ret));
839	}
840
841	isc_buffer_clear(&buf);
842	ret = dst_key_buildfilename(key, 0, NULL, &buf);
843	if (ret != ISC_R_SUCCESS) {
844		fatal("dst_key_buildfilename returned: %s\n",
845		      isc_result_totext(ret));
846	}
847	printf("%s\n", filename);
848
849	dst_key_free(&key);
850	if (prevkey != NULL) {
851		dst_key_free(&prevkey);
852	}
853}
854
855int
856main(int argc, char **argv) {
857	char *algname = NULL, *freeit = NULL;
858	char *classname = NULL;
859	char *endp;
860	isc_mem_t *mctx = NULL;
861	isc_result_t ret;
862	isc_textregion_t r;
863	const char *engine = NULL;
864	unsigned char c;
865	int ch;
866
867	keygen_ctx_t ctx = {
868		.options = DST_TYPE_PRIVATE | DST_TYPE_PUBLIC,
869		.prepub = -1,
870		.protocol = -1,
871		.size = -1,
872	};
873
874	if (argc == 1) {
875		usage();
876	}
877
878	isc_commandline_errprint = false;
879
880	/*
881	 * Process memory debugging argument first.
882	 */
883#define CMDLINE_FLAGS                                           \
884	"3A:a:b:Cc:D:d:E:eFf:Gg:hI:i:K:k:L:l:m:n:P:p:qR:r:S:s:" \
885	"T:t:v:V"
886	while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
887		switch (ch) {
888		case 'm':
889			if (strcasecmp(isc_commandline_argument, "record") == 0)
890			{
891				isc_mem_debugging |= ISC_MEM_DEBUGRECORD;
892			}
893			if (strcasecmp(isc_commandline_argument, "trace") == 0)
894			{
895				isc_mem_debugging |= ISC_MEM_DEBUGTRACE;
896			}
897			if (strcasecmp(isc_commandline_argument, "usage") == 0)
898			{
899				isc_mem_debugging |= ISC_MEM_DEBUGUSAGE;
900			}
901			break;
902		default:
903			break;
904		}
905	}
906	isc_commandline_reset = true;
907
908	isc_mem_create(&mctx);
909	isc_stdtime_get(&ctx.now);
910
911	while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
912		switch (ch) {
913		case '3':
914			ctx.use_nsec3 = true;
915			break;
916		case 'a':
917			algname = isc_commandline_argument;
918			break;
919		case 'b':
920			ctx.size = strtol(isc_commandline_argument, &endp, 10);
921			if (*endp != '\0' || ctx.size < 0) {
922				fatal("-b requires a non-negative number");
923			}
924			break;
925		case 'C':
926			ctx.oldstyle = true;
927			break;
928		case 'c':
929			classname = isc_commandline_argument;
930			break;
931		case 'd':
932			ctx.dbits = strtol(isc_commandline_argument, &endp, 10);
933			if (*endp != '\0' || ctx.dbits < 0) {
934				fatal("-d requires a non-negative number");
935			}
936			break;
937		case 'E':
938			engine = isc_commandline_argument;
939			break;
940		case 'e':
941			fprintf(stderr, "phased-out option -e "
942					"(was 'use (RSA) large exponent')\n");
943			break;
944		case 'f':
945			c = (unsigned char)(isc_commandline_argument[0]);
946			if (toupper(c) == 'K') {
947				ctx.kskflag = DNS_KEYFLAG_KSK;
948			} else if (toupper(c) == 'R') {
949				ctx.revflag = DNS_KEYFLAG_REVOKE;
950			} else {
951				fatal("unknown flag '%s'",
952				      isc_commandline_argument);
953			}
954			break;
955		case 'g':
956			ctx.generator = strtol(isc_commandline_argument, &endp,
957					       10);
958			if (*endp != '\0' || ctx.generator <= 0) {
959				fatal("-g requires a positive number");
960			}
961			break;
962		case 'K':
963			ctx.directory = isc_commandline_argument;
964			ret = try_dir(ctx.directory);
965			if (ret != ISC_R_SUCCESS) {
966				fatal("cannot open directory %s: %s",
967				      ctx.directory, isc_result_totext(ret));
968			}
969			break;
970		case 'k':
971			ctx.policy = isc_commandline_argument;
972			break;
973		case 'L':
974			ctx.ttl = strtottl(isc_commandline_argument);
975			ctx.setttl = true;
976			break;
977		case 'l':
978			ctx.configfile = isc_commandline_argument;
979			break;
980		case 'n':
981			ctx.nametype = isc_commandline_argument;
982			break;
983		case 'm':
984			break;
985		case 'p':
986			ctx.protocol = strtol(isc_commandline_argument, &endp,
987					      10);
988			if (*endp != '\0' || ctx.protocol < 0 ||
989			    ctx.protocol > 255)
990			{
991				fatal("-p must be followed by a number "
992				      "[0..255]");
993			}
994			break;
995		case 'q':
996			ctx.quiet = true;
997			break;
998		case 'r':
999			fatal("The -r option has been deprecated.\n"
1000			      "System random data is always used.\n");
1001			break;
1002		case 's':
1003			ctx.signatory = strtol(isc_commandline_argument, &endp,
1004					       10);
1005			if (*endp != '\0' || ctx.signatory < 0 ||
1006			    ctx.signatory > 15)
1007			{
1008				fatal("-s must be followed by a number "
1009				      "[0..15]");
1010			}
1011			break;
1012		case 'T':
1013			if (strcasecmp(isc_commandline_argument, "KEY") == 0) {
1014				ctx.options |= DST_TYPE_KEY;
1015			} else if (strcasecmp(isc_commandline_argument,
1016					      "DNSKE"
1017					      "Y") == 0)
1018			{
1019				/* default behavior */
1020			} else {
1021				fatal("unknown type '%s'",
1022				      isc_commandline_argument);
1023			}
1024			break;
1025		case 't':
1026			ctx.type = isc_commandline_argument;
1027			break;
1028		case 'v':
1029			endp = NULL;
1030			verbose = strtol(isc_commandline_argument, &endp, 0);
1031			if (*endp != '\0') {
1032				fatal("-v must be followed by a number");
1033			}
1034			break;
1035		case 'G':
1036			ctx.genonly = true;
1037			break;
1038		case 'P':
1039			/* -Psync ? */
1040			if (isoptarg("sync", argv, usage)) {
1041				if (ctx.setsyncadd) {
1042					fatal("-P sync specified more than "
1043					      "once");
1044				}
1045
1046				ctx.syncadd = strtotime(
1047					isc_commandline_argument, ctx.now,
1048					ctx.now, &ctx.setsyncadd);
1049				break;
1050			}
1051			(void)isoptarg("dnskey", argv, usage);
1052			if (ctx.setpub || ctx.unsetpub) {
1053				fatal("-P specified more than once");
1054			}
1055
1056			ctx.publish = strtotime(isc_commandline_argument,
1057						ctx.now, ctx.now, &ctx.setpub);
1058			ctx.unsetpub = !ctx.setpub;
1059			break;
1060		case 'A':
1061			if (ctx.setact || ctx.unsetact) {
1062				fatal("-A specified more than once");
1063			}
1064
1065			ctx.activate = strtotime(isc_commandline_argument,
1066						 ctx.now, ctx.now, &ctx.setact);
1067			ctx.unsetact = !ctx.setact;
1068			break;
1069		case 'R':
1070			if (ctx.setrev || ctx.unsetrev) {
1071				fatal("-R specified more than once");
1072			}
1073
1074			ctx.revokekey = strtotime(isc_commandline_argument,
1075						  ctx.now, ctx.now,
1076						  &ctx.setrev);
1077			ctx.unsetrev = !ctx.setrev;
1078			break;
1079		case 'I':
1080			if (ctx.setinact || ctx.unsetinact) {
1081				fatal("-I specified more than once");
1082			}
1083
1084			ctx.inactive = strtotime(isc_commandline_argument,
1085						 ctx.now, ctx.now,
1086						 &ctx.setinact);
1087			ctx.unsetinact = !ctx.setinact;
1088			break;
1089		case 'D':
1090			/* -Dsync ? */
1091			if (isoptarg("sync", argv, usage)) {
1092				if (ctx.setsyncdel) {
1093					fatal("-D sync specified more than "
1094					      "once");
1095				}
1096
1097				ctx.syncdel = strtotime(
1098					isc_commandline_argument, ctx.now,
1099					ctx.now, &ctx.setsyncdel);
1100				break;
1101			}
1102			(void)isoptarg("dnskey", argv, usage);
1103			if (ctx.setdel || ctx.unsetdel) {
1104				fatal("-D specified more than once");
1105			}
1106
1107			ctx.deltime = strtotime(isc_commandline_argument,
1108						ctx.now, ctx.now, &ctx.setdel);
1109			ctx.unsetdel = !ctx.setdel;
1110			break;
1111		case 'S':
1112			ctx.predecessor = isc_commandline_argument;
1113			break;
1114		case 'i':
1115			ctx.prepub = strtottl(isc_commandline_argument);
1116			break;
1117		case 'F':
1118			/* Reserved for FIPS mode */
1119			FALLTHROUGH;
1120		case '?':
1121			if (isc_commandline_option != '?') {
1122				fprintf(stderr, "%s: invalid argument -%c\n",
1123					program, isc_commandline_option);
1124			}
1125			FALLTHROUGH;
1126		case 'h':
1127			/* Does not return. */
1128			usage();
1129
1130		case 'V':
1131			/* Does not return. */
1132			version(program);
1133
1134		default:
1135			fprintf(stderr, "%s: unhandled option -%c\n", program,
1136				isc_commandline_option);
1137			exit(1);
1138		}
1139	}
1140
1141	if (!isatty(0)) {
1142		ctx.quiet = true;
1143	}
1144
1145	ret = dst_lib_init(mctx, engine);
1146	if (ret != ISC_R_SUCCESS) {
1147		fatal("could not initialize dst: %s", isc_result_totext(ret));
1148	}
1149
1150	setup_logging(mctx, &lctx);
1151
1152	ctx.rdclass = strtoclass(classname);
1153
1154	if (ctx.configfile == NULL || ctx.configfile[0] == '\0') {
1155		ctx.configfile = NAMED_CONFFILE;
1156	}
1157
1158	if (ctx.predecessor == NULL) {
1159		if (argc < isc_commandline_index + 1) {
1160			fatal("the key name was not specified");
1161		}
1162		if (argc > isc_commandline_index + 1) {
1163			fatal("extraneous arguments");
1164		}
1165	}
1166
1167	if (ctx.predecessor == NULL && ctx.policy == NULL) {
1168		if (algname == NULL) {
1169			fatal("no algorithm specified");
1170		}
1171		r.base = algname;
1172		r.length = strlen(algname);
1173		ret = dns_secalg_fromtext(&ctx.alg, &r);
1174		if (ret != ISC_R_SUCCESS) {
1175			fatal("unknown algorithm %s", algname);
1176		}
1177		if (!dst_algorithm_supported(ctx.alg)) {
1178			fatal("unsupported algorithm: %s", algname);
1179		}
1180	}
1181
1182	if (ctx.policy != NULL) {
1183		if (ctx.nametype != NULL) {
1184			fatal("-k and -n cannot be used together");
1185		}
1186		if (ctx.predecessor != NULL) {
1187			fatal("-k and -S cannot be used together");
1188		}
1189		if (ctx.oldstyle) {
1190			fatal("-k and -C cannot be used together");
1191		}
1192		if (ctx.setttl) {
1193			fatal("-k and -L cannot be used together");
1194		}
1195		if (ctx.prepub > 0) {
1196			fatal("-k and -i cannot be used together");
1197		}
1198		if (ctx.size != -1) {
1199			fatal("-k and -b cannot be used together");
1200		}
1201		if (ctx.kskflag || ctx.revflag) {
1202			fatal("-k and -f cannot be used together");
1203		}
1204		if (ctx.options & DST_TYPE_KEY) {
1205			fatal("-k and -T KEY cannot be used together");
1206		}
1207		if (ctx.use_nsec3) {
1208			fatal("-k and -3 cannot be used together");
1209		}
1210
1211		ctx.options |= DST_TYPE_STATE;
1212
1213		if (strcmp(ctx.policy, "default") == 0) {
1214			ctx.use_nsec3 = false;
1215			ctx.alg = DST_ALG_ECDSA256;
1216			ctx.size = 0;
1217			ctx.kskflag = DNS_KEYFLAG_KSK;
1218			ctx.ttl = 3600;
1219			ctx.setttl = true;
1220			ctx.ksk = true;
1221			ctx.zsk = true;
1222			ctx.lifetime = 0;
1223
1224			keygen(&ctx, mctx, argc, argv);
1225		} else {
1226			cfg_parser_t *parser = NULL;
1227			cfg_obj_t *config = NULL;
1228			dns_kasp_t *kasp = NULL;
1229			dns_kasp_key_t *kaspkey = NULL;
1230
1231			RUNTIME_CHECK(cfg_parser_create(mctx, lctx, &parser) ==
1232				      ISC_R_SUCCESS);
1233			if (cfg_parse_file(parser, ctx.configfile,
1234					   &cfg_type_namedconf,
1235					   &config) != ISC_R_SUCCESS)
1236			{
1237				fatal("unable to load dnssec-policy '%s' from "
1238				      "'%s'",
1239				      ctx.policy, ctx.configfile);
1240			}
1241
1242			kasp_from_conf(config, mctx, ctx.policy, &kasp);
1243			if (kasp == NULL) {
1244				fatal("failed to load dnssec-policy '%s'",
1245				      ctx.policy);
1246			}
1247			if (ISC_LIST_EMPTY(dns_kasp_keys(kasp))) {
1248				fatal("dnssec-policy '%s' has no keys "
1249				      "configured",
1250				      ctx.policy);
1251			}
1252
1253			ctx.ttl = dns_kasp_dnskeyttl(kasp);
1254			ctx.setttl = true;
1255
1256			kaspkey = ISC_LIST_HEAD(dns_kasp_keys(kasp));
1257
1258			while (kaspkey != NULL) {
1259				ctx.use_nsec3 = false;
1260				ctx.alg = dns_kasp_key_algorithm(kaspkey);
1261				ctx.size = dns_kasp_key_size(kaspkey);
1262				ctx.kskflag = dns_kasp_key_ksk(kaspkey)
1263						      ? DNS_KEYFLAG_KSK
1264						      : 0;
1265				ctx.ksk = dns_kasp_key_ksk(kaspkey);
1266				ctx.zsk = dns_kasp_key_zsk(kaspkey);
1267				ctx.lifetime = dns_kasp_key_lifetime(kaspkey);
1268
1269				keygen(&ctx, mctx, argc, argv);
1270
1271				kaspkey = ISC_LIST_NEXT(kaspkey, link);
1272			}
1273
1274			dns_kasp_detach(&kasp);
1275			cfg_obj_destroy(parser, &config);
1276			cfg_parser_destroy(&parser);
1277		}
1278	} else {
1279		keygen(&ctx, mctx, argc, argv);
1280	}
1281
1282	cleanup_logging(&lctx);
1283	dst_lib_destroy();
1284	if (verbose > 10) {
1285		isc_mem_stats(mctx, stdout);
1286	}
1287	isc_mem_destroy(&mctx);
1288
1289	if (freeit != NULL) {
1290		free(freeit);
1291	}
1292
1293	return (0);
1294}
1295