1/*****************************************************************
2**
3**	@(#) dnssec-zkt.c (c) Jan 2005  Holger Zuleger  hznet.de
4**
5**	Secure DNS zone key tool
6**	A wrapper command around the BIND dnssec-keygen utility
7**
8**	Copyright (c) 2005 - 2008, Holger Zuleger HZnet. All rights reserved.
9**
10**	This software is open source.
11**
12**	Redistribution and use in source and binary forms, with or without
13**	modification, are permitted provided that the following conditions
14**	are met:
15**
16**	Redistributions of source code must retain the above copyright notice,
17**	this list of conditions and the following disclaimer.
18**
19**	Redistributions in binary form must reproduce the above copyright notice,
20**	this list of conditions and the following disclaimer in the documentation
21**	and/or other materials provided with the distribution.
22**
23**	Neither the name of Holger Zuleger HZnet nor the names of its contributors may
24**	be used to endorse or promote products derived from this software without
25**	specific prior written permission.
26**
27**	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28**	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29**	TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30**	PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
31**	LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32**	CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33**	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34**	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35**	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36**	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37**	POSSIBILITY OF SUCH DAMAGE.
38**
39*****************************************************************/
40
41# include <stdio.h>
42# include <stdlib.h>	/* abort(), exit(), ... */
43# include <string.h>
44# include <dirent.h>
45# include <assert.h>
46# include <unistd.h>
47# include <ctype.h>
48
49#ifdef HAVE_CONFIG_H
50# include <config.h>
51#endif
52# include "config_zkt.h"
53#if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG
54# include <getopt.h>
55#endif
56
57# include "debug.h"
58# include "misc.h"
59# include "strlist.h"
60# include "zconf.h"
61# include "dki.h"
62# include "zkt.h"
63
64extern  int	optopt;
65extern  int	opterr;
66extern  int	optind;
67extern  char	*optarg;
68const	char	*progname;
69
70char	*labellist = NULL;
71
72int	headerflag = 1;
73int	ageflag = 0;
74int	lifetime = 0;
75int	lifetimeflag = 0;
76int	timeflag = 1;
77int	exptimeflag = 0;
78int	pathflag = 0;
79int	kskflag = 1;
80int	zskflag = 1;
81int	ljustflag = 0;
82
83static	int	dirflag = 0;
84static	int	recflag = RECURSIVE;
85static	int	trustedkeyflag = 0;
86static	char	*kskdomain = "";
87static	const	char	*view = "";
88
89# define	short_options	":0:1:2:3:9A:C:D:P:S:R:HKTs:ZV:afF:c:O:dhkLl:prtez"
90#if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG
91static struct option long_options[] = {
92	{"ksk-rollover",	no_argument, NULL, '9'},
93	{"ksk-status",		required_argument, NULL, '0'},
94	{"ksk-roll-status",	required_argument, NULL, '0'},
95	{"ksk-newkey",		required_argument, NULL, '1'},
96	{"ksk-publish",		required_argument, NULL, '2'},
97	{"ksk-delkey",		required_argument, NULL, '3'},
98	{"ksk-roll-phase1",	required_argument, NULL, '1'},
99	{"ksk-roll-phase2",	required_argument, NULL, '2'},
100	{"ksk-roll-phase3",	required_argument, NULL, '3'},
101	{"list-dnskeys",	no_argument, NULL, 'K'},
102	{"list-trustedkeys",	no_argument, NULL, 'T'},
103	{"ksk",			no_argument, NULL, 'k'},
104	{"zsk",			no_argument, NULL, 'z'},
105	{"age",			no_argument, NULL, 'a'},
106	{"lifetime",		no_argument, NULL, 'f'},
107	{"time",		no_argument, NULL, 't'},
108	{"expire",		no_argument, NULL, 'e'},
109	{"recursive",		no_argument, NULL, 'r'},
110	{"zone-config",		no_argument, NULL, 'Z'},
111	{"leftjust",		no_argument, NULL, 'L'},
112	{"path",		no_argument, NULL, 'p'},
113	{"nohead",		no_argument, NULL, 'h'},
114	{"directory",		no_argument, NULL, 'd'},
115	{"config",		required_argument, NULL, 'c'},
116	{"option",		required_argument, NULL, 'O'},
117	{"config-option",	required_argument, NULL, 'O'},
118	{"published",		required_argument, NULL, 'P'},
119	{"standby",		required_argument, NULL, 'S'},
120	{"active",		required_argument, NULL, 'A'},
121	{"depreciated",		required_argument, NULL, 'D'},
122	{"create",		required_argument, NULL, 'C'},
123	{"revoke",		required_argument, NULL, 'R'},
124	{"remove",		required_argument, NULL, 19 },
125	{"destroy",		required_argument, NULL, 20 },
126	{"setlifetime",		required_argument, NULL, 'F' },
127	{"view",		required_argument, NULL, 'V' },
128	{"help",		no_argument, NULL, 'H'},
129	{0, 0, 0, 0}
130};
131#endif
132
133static	int	parsedirectory (const char *dir, dki_t **listp);
134static	void	parsefile (const char *file, dki_t **listp);
135static	void	createkey (const char *keyname, const dki_t *list, const zconf_t *conf);
136static	void	ksk_roll (const char *keyname, int phase, const dki_t *list, const zconf_t *conf);
137static	int	create_parent_file (const char *fname, int phase, int ttl, const dki_t *dkp);
138static	void    usage (char *mesg, zconf_t *cp);
139static	const char *parsetag (const char *str, int *tagp);
140
141static	void	setglobalflags (zconf_t *config)
142{
143	recflag = config->recursive;
144	ageflag = config->printage;
145	timeflag = config->printtime;
146	ljustflag = config->ljust;
147}
148
149int	main (int argc, char *argv[])
150{
151	dki_t	*data = NULL;
152	dki_t	*dkp;
153	int	c;
154	int	opt_index;
155	int	action;
156	const	char	*file;
157	const	char	*defconfname = NULL;
158	char	*p;
159	char	str[254+1];
160	const char	*keyname = NULL;
161	int		searchtag;
162	zconf_t	*config;
163
164	progname = *argv;
165	if ( (p = strrchr (progname, '/')) )
166		progname = ++p;
167	view = getnameappendix (progname, "dnssec-zkt");
168
169	defconfname = getdefconfname (view);
170	config = loadconfig ("", (zconf_t *)NULL);	/* load built in config */
171	if ( fileexist (defconfname) )			/* load default config file */
172		config = loadconfig (defconfname, config);
173	if ( config == NULL )
174		fatal ("Out of memory\n");
175	setglobalflags (config);
176
177        opterr = 0;
178	opt_index = 0;
179	action = 0;
180#if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG
181	while ( (c = getopt_long (argc, argv, short_options, long_options, &opt_index)) != -1 )
182#else
183	while ( (c = getopt (argc, argv, short_options)) != -1 )
184#endif
185	{
186		switch ( c )
187		{
188		case '9':		/* ksk rollover help */
189			ksk_roll ("help", c - '0', NULL, NULL);
190			exit (1);
191		case '1':		/* ksk rollover: create new key */
192		case '2':		/* ksk rollover: publish DS */
193		case '3':		/* ksk rollover: delete old key */
194		case '0':		/* ksk rollover: show current status */
195			action = c;
196			if ( !optarg )
197				usage ("ksk rollover requires an domain argument", config);
198			kskdomain = domain_canonicdup (optarg);
199			break;
200		case 'T':
201			trustedkeyflag = 1;
202			zskflag = pathflag = 0;
203			/* fall through */
204		case 'H':
205		case 'K':
206		case 'Z':
207			action = c;
208			break;
209		case 'C':
210			pathflag = !pathflag;
211			/* fall through */
212		case 'P':
213		case 'S':
214		case 'A':
215		case 'D':
216		case 'R':
217		case 's':
218		case 19:
219		case 20:
220			if ( (keyname = parsetag (optarg, &searchtag)) != NULL )
221				keyname = domain_canonicdup (keyname);
222			action = c;
223			break;
224		case 'a':		/* age */
225			ageflag = !ageflag;
226			break;
227		case 'f':		/* key lifetime */
228			lifetimeflag = !lifetimeflag;
229			break;
230		case 'F':		/* set key lifetime */
231			lifetime = atoi (optarg);
232			lifetimeflag = 1;	/* set some flags for more informative output */
233			exptimeflag = 1;
234			timeflag = 1;
235			action = c;
236			break;
237		case 'V':		/* view name */
238			view = optarg;
239			defconfname = getdefconfname (view);
240			if ( fileexist (defconfname) )		/* load default config file */
241				config = loadconfig (defconfname, config);
242			if ( config == NULL )
243				fatal ("Out of memory\n");
244			setglobalflags (config);
245			break;
246		case 'c':
247			config = loadconfig (optarg, config);
248			setglobalflags (config);
249			checkconfig (config);
250			break;
251		case 'O':		/* read option from commandline */
252			config = loadconfig_fromstr (optarg, config);
253			setglobalflags (config);
254			checkconfig (config);
255			break;
256		case 'd':		/* ignore directory arg */
257			dirflag = 1;
258			break;
259		case 'h':		/* print no headline */
260			headerflag = 0;
261			break;
262		case 'k':		/* ksk only */
263			zskflag = 0;
264			break;
265		case 'L':		/* ljust */
266			ljustflag = !ljustflag;
267			break;
268		case 'l':		/* label list */
269			labellist = prepstrlist (optarg, LISTDELIM);
270			if ( labellist == NULL )
271				fatal ("Out of memory\n");
272			break;
273		case 'p':		/* print path */
274			pathflag = 1;
275			break;
276		case 'r':		/* switch recursive flag */
277			recflag = !recflag;
278			break;
279		case 't':		/* time */
280			timeflag = !timeflag;
281			break;
282		case 'e':		/* expire time */
283			exptimeflag = !exptimeflag;
284			break;
285		case 'z':		/* zsk only */
286			kskflag = 0;
287			break;
288		case ':':
289			snprintf (str, sizeof(str), "option \"-%c\" requires an argument.\n",
290										optopt);
291			usage (str, config);
292			break;
293		case '?':
294			if ( isprint (optopt) )
295				snprintf (str, sizeof(str), "Unknown option \"-%c\".\n",
296										optopt);
297			else
298				snprintf (str, sizeof (str), "Unknown option char \\x%x.\n",
299										optopt);
300			usage (str, config);
301			break;
302		default:
303			abort();
304		}
305	}
306
307	/* it's better to do this before we read the whole directory tree */
308	if ( action == 'Z' )
309	{
310		fprintf (stderr, "The use of -Z is deprecated. Please use zkt-conf instead\n");
311		printconfig ("stdout", config);
312		return 0;
313	}
314
315	if ( kskflag == 0 && zskflag == 0 )
316		kskflag = zskflag = 1;
317
318	c = optind;
319	do {
320		if ( c >= argc )		/* no args left */
321			file = config->zonedir;	/* use default directory */
322		else
323			file = argv[c++];
324
325		if ( is_directory (file) )
326			parsedirectory (file, &data);
327		else
328			parsefile (file, &data);
329
330	}  while ( c < argc );	/* for all arguments */
331
332	switch ( action )
333	{
334	case 'H':
335		usage ("", config);
336	case 'C':
337		createkey (keyname, data, config);
338		break;
339	case 'P':
340	case 'S':
341	case 'A':
342	case 'D':
343		if ( (dkp = (dki_t*)zkt_search (data, searchtag, keyname)) == NULL )
344			fatal ("Key with tag %u not found\n", searchtag);
345		else if ( dkp == (void *) 01 )
346			fatal ("Key with tag %u found multiple times\n", searchtag);
347		if ( (c = dki_setstatus_preservetime (dkp, action)) != 0 )
348			fatal ("Couldn't change status of key %u: %d\n", searchtag, c);
349		break;
350	case 19:	/* remove (rename) key file */
351		if ( (dkp = (dki_t *)zkt_search (data, searchtag, keyname)) == NULL )
352			fatal ("Key with tag %u not found\n", searchtag);
353		else if ( dkp == (void *) 01 )
354			fatal ("Key with tag %u found multiple times\n", searchtag);
355		dki_remove (dkp);
356		break;
357	case 20:	/* destroy the key (remove the files!) */
358		if ( (dkp = (dki_t *)zkt_search (data, searchtag, keyname)) == NULL )
359			fatal ("Key with tag %u not found\n", searchtag);
360		else if ( dkp == (void *) 01 )
361			fatal ("Key with tag %u found multiple times\n", searchtag);
362		dki_destroy (dkp);
363		break;
364	case 'R':
365		if ( (dkp = (dki_t *)zkt_search (data, searchtag, keyname)) == NULL )
366			fatal ("Key with tag %u not found\n", searchtag);
367		else if ( dkp == (void *) 01 )
368			fatal ("Key with tag %u found multiple times\n", searchtag);
369		if ( (c = dki_setstatus (dkp, action)) != 0 )
370			fatal ("Couldn't change status of key %u: %d\n", searchtag, c);
371		break;
372	case 's':
373		if ( (dkp = (dki_t *)zkt_search (data, searchtag, keyname)) == NULL )
374			fatal ("Key with tag %u not found\n", searchtag);
375		else if ( dkp == (void *) 01 )
376			fatal ("Key with tag %u found multiple times\n", searchtag);
377		dki_prt_dnskey (dkp, stdout);
378		break;
379	case 'K':
380		zkt_list_dnskeys (data);
381		break;
382	case 'T':
383		zkt_list_trustedkeys (data);
384		break;
385	case '1':	/* ksk rollover new key */
386	case '2':	/* ksk rollover publish DS */
387	case '3':	/* ksk rollover delete old key */
388	case '0':	/* ksk rollover status */
389		ksk_roll (kskdomain, action - '0', data, config);
390		break;
391	case 'F':
392		zkt_setkeylifetime (data);
393		/* fall through */
394	default:
395		zkt_list_keys (data);
396	}
397
398	return 0;
399}
400
401# define	sopt_usage(mesg, value)	fprintf (stderr, mesg, value)
402#if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG
403# define	lopt_usage(mesg, value)	fprintf (stderr, mesg, value)
404# define	loptstr(lstr, sstr)	lstr
405#else
406# define	lopt_usage(mesg, value)
407# define	loptstr(lstr, sstr)	sstr
408#endif
409static	void    usage (char *mesg, zconf_t *cp)
410{
411        fprintf (stderr, "Secure DNS Zone Key Tool %s\n", ZKT_VERSION);
412        fprintf (stderr, "\n");
413        fprintf (stderr, "Show zone config parameter as %s file\n", LOCALCONF_FILE);
414        sopt_usage ("\tusage: %s -Z\n", progname);
415        lopt_usage ("\tusage: %s --zone-config\n", progname);
416        fprintf (stderr, "\n");
417        fprintf (stderr, "List keys in current or given directory (-r for recursive mode)\n");
418        sopt_usage ("\tusage: %s [-dhatkzpr] [-c config] [file|dir ...]\n", progname);
419        fprintf (stderr, "\n");
420        fprintf (stderr, "List public part of keys in DNSKEY RR format\n");
421        sopt_usage ("\tusage: %s -K [-dhkzr] [-c config] [file|dir ...]\n", progname);
422        lopt_usage ("\tusage: %s --list-dnskeys [-dhkzr] [-c config] [file|dir ...]\n", progname);
423        fprintf (stderr, "\n");
424        fprintf (stderr, "List keys (output is suitable for trusted-keys section)\n");
425        sopt_usage ("\tusage: %s -T [-dhzr] [-c config] [file|dir ...]\n", progname);
426        lopt_usage ("\tusage: %s --list-trustedkeys [-dhzr] [-c config] [file|dir ...]\n", progname);
427        fprintf (stderr, "\n");
428        fprintf (stderr, "Create a new key \n");
429        sopt_usage ("\tusage: %s -C <name> [-k] [-dpr] [-c config] [dir ...]\n", progname);
430        lopt_usage ("\tusage: %s --create=<name> [-k] [-dpr] [-c config] [dir ...]\n", progname);
431        fprintf (stderr, "\t\tKSK (use -k):  %s %d bits\n", dki_algo2str (cp->k_algo), cp->k_bits);
432        fprintf (stderr, "\t\tZSK (default): %s %d bits\n", dki_algo2str (cp->k_algo), cp->z_bits);
433        fprintf (stderr, "\n");
434        fprintf (stderr, "Change key status of specified key to published, active or depreciated\n");
435        fprintf (stderr, "\t(<keyspec> := tag | tag:name) \n");
436        sopt_usage ("\tusage: %s -P|-A|-D <keyspec> [-dr] [-c config] [dir ...]\n", progname);
437        lopt_usage ("\tusage: %s --published=<keyspec> [-dr] [-c config] [dir ...]\n", progname);
438        lopt_usage ("\tusage: %s --active=<keyspec> [-dr] [-c config] [dir ...]\n", progname);
439        lopt_usage ("\tusage: %s --depreciated=<keyspec> [-dr] [-c config] [dir ...]\n", progname);
440        fprintf (stderr, "\n");
441        fprintf (stderr, "Revoke specified key (<keyspec> := tag | tag:name) \n");
442        sopt_usage ("\tusage: %s -R <keyspec> [-dr] [-c config] [dir ...]\n", progname);
443        lopt_usage ("\tusage: %s --revoke=<keyspec> [-dr] [-c config] [dir ...]\n", progname);
444        fprintf (stderr, "\n");
445        fprintf (stderr, "Remove (rename) or destroy (delete) specified key (<keyspec> := tag | tag:name) \n");
446        lopt_usage ("\tusage: %s --remove=<keyspec> [-dr] [-c config] [dir ...]\n", progname);
447        lopt_usage ("\tusage: %s --destroy=<keyspec> [-dr] [-c config] [dir ...]\n", progname);
448        fprintf (stderr, "\n");
449        fprintf (stderr, "Initiate a semi-automated KSK rollover");
450        fprintf (stderr, "('%s -9%s' prints out a short description)\n", progname, loptstr ("|--ksk-rollover", ""));
451        sopt_usage ("\tusage: %s {-1} do.ma.in.\n", progname);
452        lopt_usage ("\tusage: %s {--ksk-roll-phase1|--ksk-newkey} do.ma.in.\n", progname);
453        sopt_usage ("\tusage: %s {-2} do.ma.in.\n", progname);
454        lopt_usage ("\tusage: %s {--ksk-roll-phase2|--ksk-publish} do.ma.in.\n", progname);
455        sopt_usage ("\tusage: %s {-3} do.ma.in.\n", progname);
456        lopt_usage ("\tusage: %s {--ksk-roll-phase3|--ksk-delkey} do.ma.in.\n", progname);
457        sopt_usage ("\tusage: %s {-0} do.ma.in.\n", progname);
458        lopt_usage ("\tusage: %s {--ksk-roll-status|--ksk-status} do.ma.in.\n", progname);
459        fprintf (stderr, "\n");
460
461        fprintf (stderr, "\n");
462        fprintf (stderr, "General options \n");
463        fprintf (stderr, "\t-c file%s", loptstr (", --config=file\n", ""));
464	fprintf (stderr, "\t\t read config from <file> instead of %s\n", CONFIG_FILE);
465        fprintf (stderr, "\t-O optstr%s", loptstr (", --config-option=\"optstr\"\n", ""));
466	fprintf (stderr, "\t\t read config options from commandline\n");
467        fprintf (stderr, "\t-h%s\t no headline or trusted-key section header/trailer in -T mode\n", loptstr (", --nohead", "\t"));
468        fprintf (stderr, "\t-d%s\t skip directory arguments\n", loptstr (", --directory", "\t"));
469        fprintf (stderr, "\t-L%s\t print the domain name left justified (default: %s)\n", loptstr (", --leftjust", "\t"), ljustflag ? "on": "off");
470        fprintf (stderr, "\t-l list\t\t print out only zone keys out of the given domain list\n");
471        fprintf (stderr, "\t-p%s\t show path of keyfile / create key in current directory\n", loptstr (", --path", "\t"));
472        fprintf (stderr, "\t-r%s\t recursive mode on/off (default: %s)\n", loptstr(", --recursive", "\t"), recflag ? "on": "off");
473        fprintf (stderr, "\t-a%s\t print age of key (default: %s)\n", loptstr (", --age", "\t"), ageflag ? "on": "off");
474        fprintf (stderr, "\t-t%s\t print key generation time (default: %s)\n", loptstr (", --time", "\t"),
475								timeflag ? "on": "off");
476        fprintf (stderr, "\t-e%s\t print key expiration time\n", loptstr (", --expire", "\t"));
477        fprintf (stderr, "\t-f%s\t print key lifetime\n", loptstr (", --lifetime", "\t"));
478        fprintf (stderr, "\t-F days%s=days\t set key lifetime\n", loptstr (", --setlifetime", "\t"));
479        fprintf (stderr, "\t-k%s\t key signing keys only\n", loptstr (", --ksk", "\t"));
480        fprintf (stderr, "\t-z%s\t zone signing keys only\n", loptstr (", --zsk", "\t"));
481        if ( mesg && *mesg )
482                fprintf (stderr, "%s\n", mesg);
483        exit (1);
484}
485
486static	void	createkey (const char *keyname, const dki_t *list, const zconf_t *conf)
487{
488	const char *dir = "";
489	dki_t	*dkp;
490
491	if ( keyname == NULL || *keyname == '\0' )
492		fatal ("Create key: no keyname!");
493
494	dbg_val2 ("createkey: keyname %s, pathflag = %d\n", keyname, pathflag);
495	/* search for already existent key to get the directory name */
496	if ( pathflag && (dkp = (dki_t *)zkt_search (list, 0, keyname)) != NULL )
497	{
498		char    path[MAX_PATHSIZE+1];
499		zconf_t localconf;
500
501		dir = dkp->dname;
502		pathname (path, sizeof (path), dir, LOCALCONF_FILE, NULL);
503		if ( fileexist (path) )                 /* load local config file */
504		{
505			dbg_val ("Load local config file \"%s\"\n", path);
506			memcpy (&localconf, conf, sizeof (zconf_t));
507			conf = loadconfig (path, &localconf);
508		}
509	}
510
511	if  ( zskflag )
512		dkp = dki_new (dir, keyname, DKI_ZSK, conf->k_algo, conf->z_bits, conf->z_random, conf->z_life / DAYSEC);
513	else
514		dkp = dki_new (dir, keyname, DKI_KSK, conf->k_algo, conf->k_bits, conf->k_random, conf->k_life / DAYSEC);
515	if ( dkp == NULL )
516		fatal ("Can't create key %s: %s!\n", keyname, dki_geterrstr ());
517
518	/* create a new key always in state published, which means "standby" for ksk */
519	dki_setstatus (dkp, DKI_PUB);
520}
521
522static	int	get_parent_phase (const char *file)
523{
524	FILE	*fp;
525	int	phase;
526
527	if ( (fp = fopen (file, "r")) == NULL )
528		return -1;
529
530	phase = 0;
531	if ( fscanf (fp, "; KSK rollover phase%d", &phase) != 1 )
532		phase = 0;
533
534	fclose (fp);
535	return phase;
536}
537
538static	void	ksk_roll (const char *keyname, int phase, const dki_t *list, const zconf_t *conf)
539{
540	char    path[MAX_PATHSIZE+1];
541	zconf_t localconf;
542	const char *dir;
543	dki_t	*keylist;
544	dki_t	*dkp;
545	dki_t	*standby;
546	int	parent_exist;
547	int	parent_age;
548	int	parent_phase;
549	int	parent_propagation;
550	int	key_ttl;
551	int	ksk;
552
553	if ( phase == 9 )	/* usage */
554	{
555		fprintf (stderr, "A KSK rollover requires three consecutive steps:\n");
556		fprintf (stderr, "\n");
557		fprintf (stderr, "-1%s", loptstr ("|--ksk-roll-phase1 (--ksk-newkey)\n", ""));
558		fprintf (stderr, "\t Create a new KSK.\n");
559		fprintf (stderr, "\t This step also creates a parent-<domain> file which contains only\n");
560		fprintf (stderr, "\t the _old_ key.  This file will be copied in hierarchical mode\n");
561		fprintf (stderr, "\t by dnssec-signer to the parent directory as keyset-<domain> file.\n");
562		fprintf (stderr, "\t Wait until the new keyset is propagated, before going to the next step.\n");
563		fprintf (stderr, "\n");
564		fprintf (stderr, "-2%s", loptstr ("|--ksk-roll-phase2 (--ksk-publish)\n", ""));
565		fprintf (stderr, "\t This step creates a parent-<domain> file with the _new_ key only.\n");
566		fprintf (stderr, "\t Please send this file immediately to the parent (In hierarchical\n");
567		fprintf (stderr, "\t mode this will be done automatically by the dnssec-signer command).\n");
568		fprintf (stderr, "\t Then wait until the new DS is generated by the parent and propagated\n");
569		fprintf (stderr, "\t to all the parent name server, plus the old DS TTL before going to step three.\n");
570		fprintf (stderr, "\n");
571		fprintf (stderr, "-3%s", loptstr ("|--ksk-roll-phase3 (--ksk-delkey)\n", ""));
572		fprintf (stderr, "\t Remove (rename) the old KSK and the parent-<domain> file.\n");
573		fprintf (stderr, "\t You have to manually delete the old KSK (look at file names beginning\n");
574		fprintf (stderr, "\t with an lower 'k').\n");
575		fprintf (stderr, "\n");
576		fprintf (stderr, "-0%s", loptstr ("|--ksk-roll-stat (--ksk-status)\n", ""));
577		fprintf (stderr, "\t Show the current KSK rollover state of a domain.\n");
578
579		fprintf (stderr, "\n");
580
581		return;
582	}
583
584	if ( keyname == NULL || *keyname == '\0' )
585		fatal ("ksk rollover: no domain!");
586
587	dbg_val2 ("ksk_roll: keyname %s, phase = %d\n", keyname, phase);
588
589	/* search for already existent key to get the directory name */
590	if ( (keylist = (dki_t *)zkt_search (list, 0, keyname)) == NULL )
591		fatal ("ksk rollover: domain %s not found!\n", keyname);
592	dkp = keylist;
593
594	/* try to read local config file */
595	dir = dkp->dname;
596	pathname (path, sizeof (path), dir, LOCALCONF_FILE, NULL);
597	if ( fileexist (path) )                 /* load local config file */
598	{
599		dbg_val ("Load local config file \"%s\"\n", path);
600		memcpy (&localconf, conf, sizeof (zconf_t));
601		conf = loadconfig (path, &localconf);
602	}
603	key_ttl = conf->key_ttl;
604
605	/* check if parent-file already exist */
606	pathname (path, sizeof (path), dir, "parent-", keyname);
607	parent_phase = parent_age = 0;
608	if ( (parent_exist = fileexist (path)) != 0 )
609	{
610		parent_phase = get_parent_phase (path);
611		parent_age = file_age (path);
612	}
613	// parent_propagation = 2 * DAYSEC;
614	parent_propagation = 5 * MINSEC;
615
616	ksk = 0;	/* count active(!) key signing keys */
617	standby = NULL;	/* find standby key if available */
618	for ( dkp = keylist; dkp; dkp = dkp->next )
619		if ( dki_isksk (dkp) )
620		 {
621			if ( dki_status (dkp) == DKI_ACT )
622				ksk++;
623			else if ( dki_status (dkp) == DKI_PUB )
624				standby = dkp;
625		}
626
627	switch ( phase )
628	{
629	case 0:	/* print status (debug) */
630		fprintf (stdout, "ksk_rollover:\n");
631		fprintf (stdout, "\t domain = %s\n", keyname);
632		fprintf (stdout, "\t phase = %d\n", parent_phase);
633		fprintf (stdout, "\t parent_file %s %s\n", path, parent_exist ? "exist": "not exist");
634		if ( parent_exist )
635			fprintf (stdout, "\t age of parent_file %d %s\n", parent_age, str_delspace (age2str (parent_age)));
636		fprintf (stdout, "\t # of active key signing keys %d\n", ksk);
637		fprintf (stdout, "\t parent_propagation %d %s\n", parent_propagation, str_delspace (age2str (parent_propagation)));
638		fprintf (stdout, "\t keys ttl %d %s\n", key_ttl, age2str (key_ttl));
639
640		for ( dkp = keylist; dkp; dkp = dkp->next )
641		{
642			/* TODO: Nur zum testen */
643			dki_prt_dnskey (dkp, stdout);
644		}
645		break;
646	case 1:
647		if ( parent_exist || ksk > 1 )
648			fatal ("Can\'t create new ksk because there is already an ksk rollover in progress\n");
649
650		fprintf (stdout, "create new ksk \n");
651		dkp = dki_new (dir, keyname, DKI_KSK, conf->k_algo, conf->k_bits, conf->k_random, conf->k_life / DAYSEC);
652		if ( dkp == NULL )
653			fatal ("Can't create key %s: %s!\n", keyname, dki_geterrstr ());
654		if ( standby )
655		{
656			dki_setstatus (standby, DKI_ACT);	/* activate standby key */
657			dki_setstatus (dkp, DKI_PUB);	/* new key will be the new standby */
658		}
659
660		// dkp = keylist;	/* use old key to create the parent file */
661		if ( (dkp = (dki_t *)dki_findalgo (keylist, 1, conf->k_algo, 'a', 1)) == NULL )	/* find the oldest active ksk to create the parent file */
662			fatal ("ksk_rollover phase1: Couldn't find the old active key\n");
663		if ( !create_parent_file (path, phase, key_ttl, dkp) )
664			fatal ("Couldn't create parentfile %s\n", path);
665		break;
666
667	case 2:
668		if ( ksk < 2 )
669			fatal ("Can\'t publish new key because no one exist\n");
670		if ( !parent_exist )
671			fatal ("More than one KSK but no parent file found!\n");
672		if ( parent_phase != 1 )
673			fatal ("Parent file exists but is in wrong state (phase = %d)\n", parent_phase);
674		if ( parent_age < conf->proptime + key_ttl )
675			fatal ("ksk_rollover (phase2): you have to wait for the propagation of the new KSK (at least %dsec or %s)\n",
676				conf->proptime + key_ttl - parent_age,
677				str_delspace (age2str (conf->proptime + key_ttl - parent_age)));
678
679		fprintf (stdout, "save new ksk in parent file\n");
680		dkp = keylist->next;	/* set dkp to new ksk */
681		if ( !create_parent_file (path, phase, key_ttl, dkp) )
682			fatal ("Couldn't create parentfile %s\n", path);
683		break;
684	case 3:
685		if ( !parent_exist || ksk < 2 )
686			fatal ("ksk-delkey only allowed after ksk-publish\n");
687		if ( parent_phase != 2 )
688			fatal ("Parent file exists but is in wrong state (phase = %d)\n", parent_phase);
689		if ( parent_age < parent_propagation + key_ttl )
690			fatal ("ksk_rollover (phase3): you have to wait for DS propagation (at least %dsec or %s)\n",
691				parent_propagation + key_ttl - parent_age,
692				str_delspace (age2str (parent_propagation + key_ttl - parent_age)));
693		/* remove the parentfile */
694		fprintf (stdout, "remove parentfile \n");
695		unlink (path);
696		/* remove or rename the old key */
697		fprintf (stdout, "old ksk renamed \n");
698		dkp = keylist;	/* set dkp to old ksk */
699		dki_remove (dkp);
700		break;
701	default:	assert (phase == 1 || phase == 2 || phase == 3);
702	}
703}
704
705/*****************************************************************
706**	create_parent_file ()
707*****************************************************************/
708static	int	create_parent_file (const char *fname, int phase, int ttl, const dki_t *dkp)
709{
710	FILE	*fp;
711
712	assert ( fname != NULL );
713
714	if ( dkp == NULL || (phase != 1 && phase != 2) )
715		return 0;
716
717	if ( (fp = fopen (fname, "w")) == NULL )
718		fatal ("can\'t create new parentfile \"%s\"\n", fname);
719
720	if ( phase == 1 )
721		fprintf (fp, "; KSK rollover phase1 (old key)\n");
722	else
723		fprintf (fp, "; KSK rollover phase2 (new key)\n");
724
725	dki_prt_dnskeyttl (dkp, fp, ttl);
726	fclose (fp);
727
728	return phase;
729}
730
731static	int	parsedirectory (const char *dir, dki_t **listp)
732{
733	dki_t	*dkp;
734	DIR	*dirp;
735	struct  dirent  *dentp;
736	char	path[MAX_PATHSIZE+1];
737
738	if ( dirflag )
739		return 0;
740
741	dbg_val ("directory: opendir(%s)\n", dir);
742	if ( (dirp = opendir (dir)) == NULL )
743		return 0;
744
745	while ( (dentp = readdir (dirp)) != NULL )
746	{
747		if ( is_dotfilename (dentp->d_name) )
748			continue;
749
750		dbg_val ("directory: check %s\n", dentp->d_name);
751		pathname (path, sizeof (path), dir, dentp->d_name, NULL);
752		if ( is_directory (path) && recflag )
753		{
754			dbg_val ("directory: recursive %s\n", path);
755			parsedirectory (path, listp);
756		}
757		else if ( is_keyfilename (dentp->d_name) )
758			if ( (dkp = dki_read (dir, dentp->d_name)) )
759			{
760				// fprintf (stderr, "parsedir: tssearch (%d %s)\n", dkp, dkp->name);
761#if defined (USE_TREE) && USE_TREE
762				dki_tadd (listp, dkp, 1);
763#else
764				dki_add (listp, dkp);
765#endif
766			}
767	}
768	closedir (dirp);
769	return 1;
770}
771
772static	void	parsefile (const char *file, dki_t **listp)
773{
774	char	path[MAX_PATHSIZE+1];
775	dki_t	*dkp;
776
777	/* file arg contains path ? ... */
778	file = splitpath (path, sizeof (path), file);	/* ... then split of */
779
780	if ( is_keyfilename (file) )	/* plain file name looks like DNS key file ? */
781	{
782		if ( (dkp = dki_read (path, file)) )	/* read DNS key file ... */
783#if defined (USE_TREE) && USE_TREE
784			dki_tadd (listp, dkp, 1);		/* ... and add to tree */
785#else
786			dki_add (listp, dkp);		/* ... and add to list */
787#endif
788		else
789			error ("error parsing %s: (%s)\n", file, dki_geterrstr());
790	}
791}
792
793static	const char *parsetag (const char *str, int *tagp)
794{
795	const	char	*p;
796
797	*tagp = 0;
798	while ( isspace (*str) )	/* skip leading ws */
799		str++;
800
801	p = str;
802	if ( isdigit (*p) )		/* keytag starts with digit */
803	{
804		sscanf (p, "%u", tagp);	/* read keytag as number */
805		do			/* eat up to the end of the number */
806			p++;
807		while ( isdigit (*p) );
808
809		if ( *p == ':' )	/* label follows ? */
810			return p+1;	/* return that */
811		if ( *p == '\0' )
812			return NULL;	/* no label */
813	}
814	return str;	/* return as label string if not a numeric keytag */
815}
816
817