1/*****************************************************************
2**
3**	@(#) zkt-keyman.c (c) Jan 2005 - Apr 2010  Holger Zuleger  hznet.de
4**
5**	ZKT key managing tool (formely knon as dnsses-zkt)
6**	A wrapper command around the BIND dnssec-keygen utility
7**
8**	Copyright (c) 2005 - 2010, 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	char	*kskdomain = "";
86static	const	char	*view = "";
87
88# define	short_options	":0:1:2:3:9A:C:D:P:S:R:h:ZV:F:c:O:krz"
89#if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG
90static struct option long_options[] = {
91	{"ksk-rollover",	no_argument, NULL, '9'},
92	{"ksk-status",		required_argument, NULL, '0'},
93	{"ksk-roll-status",	required_argument, NULL, '0'},
94	{"ksk-newkey",		required_argument, NULL, '1'},
95	{"ksk-publish",		required_argument, NULL, '2'},
96	{"ksk-delkey",		required_argument, NULL, '3'},
97	{"ksk-roll-phase1",	required_argument, NULL, '1'},
98	{"ksk-roll-phase2",	required_argument, NULL, '2'},
99	{"ksk-roll-phase3",	required_argument, NULL, '3'},
100	{"ksk",			no_argument, NULL, 'k'},
101	{"zsk",			no_argument, NULL, 'z'},
102	{"recursive",		no_argument, NULL, 'r'},
103	{"config",		required_argument, NULL, 'c'},
104	{"option",		required_argument, NULL, 'O'},
105	{"config-option",	required_argument, NULL, 'O'},
106	{"published",		required_argument, NULL, 'P'},
107	{"standby",		required_argument, NULL, 'S'},
108	{"active",		required_argument, NULL, 'A'},
109	{"depreciated",		required_argument, NULL, 'D'},
110	{"create",		required_argument, NULL, 'C'},
111	{"revoke",		required_argument, NULL, 'R'},
112	{"remove",		required_argument, NULL, 19 },
113	{"destroy",		required_argument, NULL, 20 },
114	{"setlifetime",		required_argument, NULL, 'F' },
115	{"view",		required_argument, NULL, 'V' },
116	{"help",		no_argument, NULL, 'h'},
117	{0, 0, 0, 0}
118};
119#endif
120
121static	int	parsedirectory (const char *dir, dki_t **listp);
122static	void	parsefile (const char *file, dki_t **listp);
123static	void	createkey (const char *keyname, const dki_t *list, const zconf_t *conf);
124static	void	ksk_roll (const char *keyname, int phase, const dki_t *list, const zconf_t *conf);
125static	int	create_parent_file (const char *fname, int phase, int ttl, const dki_t *dkp);
126static	void    usage (char *mesg, zconf_t *cp);
127static	const char *parsetag (const char *str, int *tagp);
128
129static	void	setglobalflags (zconf_t *config)
130{
131	recflag = config->recursive;
132}
133
134int	main (int argc, char *argv[])
135{
136	dki_t	*data = NULL;
137	dki_t	*dkp;
138	int	c;
139	int	opt_index;
140	int	action;
141	const	char	*file;
142	const	char	*defconfname = NULL;
143	char	*p;
144	char	str[254+1];
145	const char	*keyname = NULL;
146	int		searchtag;
147	zconf_t	*config;
148
149	progname = *argv;
150	if ( (p = strrchr (progname, '/')) )
151		progname = ++p;
152	view = getnameappendix (progname, "dnssec-zkt");
153
154	defconfname = getdefconfname (view);
155	config = loadconfig ("", (zconf_t *)NULL);	/* load built in config */
156	if ( fileexist (defconfname) )			/* load default config file */
157		config = loadconfig (defconfname, config);
158	if ( config == NULL )
159		fatal ("Out of memory\n");
160	setglobalflags (config);
161
162        opterr = 0;
163	opt_index = 0;
164	action = 0;
165#if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG
166	while ( (c = getopt_long (argc, argv, short_options, long_options, &opt_index)) != -1 )
167#else
168	while ( (c = getopt (argc, argv, short_options)) != -1 )
169#endif
170	{
171		switch ( c )
172		{
173		case '9':		/* ksk rollover help */
174			ksk_roll ("help", c - '0', NULL, NULL);
175			exit (1);
176		case '1':		/* ksk rollover: create new key */
177		case '2':		/* ksk rollover: publish DS */
178		case '3':		/* ksk rollover: delete old key */
179		case '0':		/* ksk rollover: show current status */
180			action = c;
181			if ( !optarg )
182				usage ("ksk rollover requires an domain argument", config);
183			kskdomain = domain_canonicdup (optarg);
184			break;
185		case 'h':
186		case 'K':
187		case 'Z':
188			action = c;
189			break;
190		case 'C':
191			pathflag = !pathflag;
192			/* fall through */
193		case 'P':
194		case 'S':
195		case 'A':
196		case 'D':
197		case 'R':
198		case 's':
199		case 19:
200		case 20:
201			if ( (keyname = parsetag (optarg, &searchtag)) != NULL )
202				keyname = domain_canonicdup (keyname);
203			action = c;
204			break;
205		case 'F':		/* set key lifetime */
206			lifetime = atoi (optarg);
207			action = c;
208			break;
209		case 'V':		/* view name */
210			view = optarg;
211			defconfname = getdefconfname (view);
212			if ( fileexist (defconfname) )		/* load default config file */
213				config = loadconfig (defconfname, config);
214			if ( config == NULL )
215				fatal ("Out of memory\n");
216			setglobalflags (config);
217			break;
218		case 'c':
219			config = loadconfig (optarg, config);
220			setglobalflags (config);
221			checkconfig (config);
222			break;
223		case 'O':		/* read option from commandline */
224			config = loadconfig_fromstr (optarg, config);
225			setglobalflags (config);
226			checkconfig (config);
227			break;
228		case 'd':		/* ignore directory arg */
229			dirflag = 1;
230			break;
231		case 'k':		/* ksk only */
232			zskflag = 0;
233			break;
234		case 'r':		/* switch recursive flag */
235			recflag = !recflag;
236			break;
237		case 'z':		/* zsk only */
238			kskflag = 0;
239			break;
240		case ':':
241			snprintf (str, sizeof(str), "option \"-%c\" requires an argument.\n",
242										optopt);
243			usage (str, config);
244			break;
245		case '?':
246			if ( isprint (optopt) )
247				snprintf (str, sizeof(str), "Unknown option \"-%c\".\n",
248										optopt);
249			else
250				snprintf (str, sizeof (str), "Unknown option char \\x%x.\n",
251										optopt);
252			usage (str, config);
253			break;
254		default:
255			abort();
256		}
257	}
258
259	if ( kskflag == 0 && zskflag == 0 )
260		kskflag = zskflag = 1;
261
262	c = optind;
263	do {
264		if ( c >= argc )		/* no args left */
265			file = config->zonedir;	/* use default directory */
266		else
267			file = argv[c++];
268
269		if ( is_directory (file) )
270			parsedirectory (file, &data);
271		else
272			parsefile (file, &data);
273
274	}  while ( c < argc );	/* for all arguments */
275
276	switch ( action )
277	{
278	case 'h':
279		usage ("", config);
280	case 'C':
281		createkey (keyname, data, config);
282		break;
283	case 'P':
284	case 'S':
285	case 'A':
286	case 'D':
287		if ( (dkp = (dki_t*)zkt_search (data, searchtag, keyname)) == NULL )
288			fatal ("Key with tag %u not found\n", searchtag);
289		else if ( dkp == (void *) 01 )
290			fatal ("Key with tag %u found multiple times\n", searchtag);
291		if ( (c = dki_setstatus_preservetime (dkp, action)) != 0 )
292			fatal ("Couldn't change status of key %u: %d\n", searchtag, c);
293		break;
294	case 19:	/* remove (rename) key file */
295		if ( (dkp = (dki_t *)zkt_search (data, searchtag, keyname)) == NULL )
296			fatal ("Key with tag %u not found\n", searchtag);
297		else if ( dkp == (void *) 01 )
298			fatal ("Key with tag %u found multiple times\n", searchtag);
299		dki_remove (dkp);
300		break;
301	case 20:	/* destroy the key (remove the files!) */
302		if ( (dkp = (dki_t *)zkt_search (data, searchtag, keyname)) == NULL )
303			fatal ("Key with tag %u not found\n", searchtag);
304		else if ( dkp == (void *) 01 )
305			fatal ("Key with tag %u found multiple times\n", searchtag);
306		dki_destroy (dkp);
307		break;
308	case 'R':
309		if ( (dkp = (dki_t *)zkt_search (data, searchtag, keyname)) == NULL )
310			fatal ("Key with tag %u not found\n", searchtag);
311		else if ( dkp == (void *) 01 )
312			fatal ("Key with tag %u found multiple times\n", searchtag);
313		if ( (c = dki_setstatus (dkp, action)) != 0 )
314			fatal ("Couldn't change status of key %u: %d\n", searchtag, c);
315		break;
316	case '1':	/* ksk rollover new key */
317	case '2':	/* ksk rollover publish DS */
318	case '3':	/* ksk rollover delete old key */
319	case '0':	/* ksk rollover status */
320		ksk_roll (kskdomain, action - '0', data, config);
321		break;
322	case 'F':
323		zkt_setkeylifetime (data);
324		/* fall through */
325	default:
326		zkt_list_keys (data);
327	}
328
329	return 0;
330}
331
332# define	sopt_usage(mesg, value)	fprintf (stderr, mesg, value)
333#if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG
334# define	lopt_usage(mesg, value)	fprintf (stderr, mesg, value)
335# define	loptstr(lstr, sstr)	lstr
336#else
337# define	lopt_usage(mesg, value)
338# define	loptstr(lstr, sstr)	sstr
339#endif
340static	void    usage (char *mesg, zconf_t *cp)
341{
342        fprintf (stderr, "DNS Zone Key Management Tool %s\n", ZKT_VERSION);
343        fprintf (stderr, "\n");
344        fprintf (stderr, "Create a new key \n");
345        sopt_usage ("\tusage: %s -C <name> [-k] [-dpr] [-c config] [dir ...]\n", progname);
346        lopt_usage ("\tusage: %s --create=<name> [-k] [-dpr] [-c config] [dir ...]\n", progname);
347        fprintf (stderr, "\t\tKSK (use -k):  %s %d bits\n", dki_algo2str (cp->k_algo), cp->k_bits);
348        fprintf (stderr, "\t\tZSK (default): %s %d bits\n", dki_algo2str (cp->k_algo), cp->z_bits);
349        fprintf (stderr, "\n");
350        fprintf (stderr, "Change key status of specified key to published, active or depreciated\n");
351        fprintf (stderr, "\t(<keyspec> := tag | tag:name) \n");
352        sopt_usage ("\tusage: %s -P|-A|-D <keyspec> [-dr] [-c config] [dir ...]\n", progname);
353        lopt_usage ("\tusage: %s --published=<keyspec> [-dr] [-c config] [dir ...]\n", progname);
354        lopt_usage ("\tusage: %s --active=<keyspec> [-dr] [-c config] [dir ...]\n", progname);
355        lopt_usage ("\tusage: %s --depreciated=<keyspec> [-dr] [-c config] [dir ...]\n", progname);
356        fprintf (stderr, "\n");
357        fprintf (stderr, "Revoke specified key (<keyspec> := tag | tag:name) \n");
358        sopt_usage ("\tusage: %s -R <keyspec> [-dr] [-c config] [dir ...]\n", progname);
359        lopt_usage ("\tusage: %s --revoke=<keyspec> [-dr] [-c config] [dir ...]\n", progname);
360        fprintf (stderr, "\n");
361        fprintf (stderr, "Remove (rename) or destroy (delete) specified key (<keyspec> := tag | tag:name) \n");
362        lopt_usage ("\tusage: %s --remove=<keyspec> [-dr] [-c config] [dir ...]\n", progname);
363        lopt_usage ("\tusage: %s --destroy=<keyspec> [-dr] [-c config] [dir ...]\n", progname);
364        fprintf (stderr, "\n");
365        fprintf (stderr, "Initiate a semi-automated KSK rollover");
366        fprintf (stderr, "('%s -9%s' prints out a brief description)\n", progname, loptstr ("|--ksk-rollover", ""));
367        sopt_usage ("\tusage: %s {-1} do.ma.in.\n", progname);
368        lopt_usage ("\tusage: %s {--ksk-roll-phase1|--ksk-newkey} do.ma.in.\n", progname);
369        sopt_usage ("\tusage: %s {-2} do.ma.in.\n", progname);
370        lopt_usage ("\tusage: %s {--ksk-roll-phase2|--ksk-publish} do.ma.in.\n", progname);
371        sopt_usage ("\tusage: %s {-3} do.ma.in.\n", progname);
372        lopt_usage ("\tusage: %s {--ksk-roll-phase3|--ksk-delkey} do.ma.in.\n", progname);
373        sopt_usage ("\tusage: %s {-0} do.ma.in.\n", progname);
374        lopt_usage ("\tusage: %s {--ksk-roll-status|--ksk-status} do.ma.in.\n", progname);
375        fprintf (stderr, "\n");
376
377        fprintf (stderr, "\n");
378        fprintf (stderr, "General options \n");
379        fprintf (stderr, "\t-c file%s", loptstr (", --config=file\n", ""));
380	fprintf (stderr, "\t\t read config from <file> instead of %s\n", CONFIG_FILE);
381        fprintf (stderr, "\t-O optstr%s", loptstr (", --config-option=\"optstr\"\n", ""));
382	fprintf (stderr, "\t\t read config options from commandline\n");
383        fprintf (stderr, "\t-d%s\t skip directory arguments\n", loptstr (", --directory", "\t"));
384        fprintf (stderr, "\t-r%s\t recursive mode on/off (default: %s)\n", loptstr(", --recursive", "\t"), recflag ? "on": "off");
385        fprintf (stderr, "\t-F days%s=days\t set key lifetime\n", loptstr (", --setlifetime", "\t"));
386        fprintf (stderr, "\t-k%s\t key signing keys only\n", loptstr (", --ksk", "\t"));
387        fprintf (stderr, "\t-z%s\t zone signing keys only\n", loptstr (", --zsk", "\t"));
388        if ( mesg && *mesg )
389                fprintf (stderr, "%s\n", mesg);
390        exit (1);
391}
392
393static	void	createkey (const char *keyname, const dki_t *list, const zconf_t *conf)
394{
395	const char *dir = "";
396	dki_t	*dkp;
397
398	if ( keyname == NULL || *keyname == '\0' )
399		fatal ("Create key: no keyname!");
400
401	dbg_val2 ("createkey: keyname %s, pathflag = %d\n", keyname, pathflag);
402	/* search for already existent key to get the directory name */
403	if ( pathflag && (dkp = (dki_t *)zkt_search (list, 0, keyname)) != NULL )
404	{
405		char    path[MAX_PATHSIZE+1];
406		zconf_t localconf;
407
408		dir = dkp->dname;
409		pathname (path, sizeof (path), dir, LOCALCONF_FILE, NULL);
410		if ( fileexist (path) )                 /* load local config file */
411		{
412			dbg_val ("Load local config file \"%s\"\n", path);
413			memcpy (&localconf, conf, sizeof (zconf_t));
414			conf = loadconfig (path, &localconf);
415		}
416	}
417
418	if  ( zskflag )
419		dkp = dki_new (dir, keyname, DKI_ZSK, conf->k_algo, conf->z_bits, conf->z_random, conf->z_life / DAYSEC);
420	else
421		dkp = dki_new (dir, keyname, DKI_KSK, conf->k_algo, conf->k_bits, conf->k_random, conf->k_life / DAYSEC);
422	if ( dkp == NULL )
423		fatal ("Can't create key %s: %s!\n", keyname, dki_geterrstr ());
424
425	/* create a new key always in state published, which means "standby" for ksk */
426	dki_setstatus (dkp, DKI_PUB);
427}
428
429static	int	get_parent_phase (const char *file)
430{
431	FILE	*fp;
432	int	phase;
433
434	if ( (fp = fopen (file, "r")) == NULL )
435		return -1;
436
437	phase = 0;
438	if ( fscanf (fp, "; KSK rollover phase%d", &phase) != 1 )
439		phase = 0;
440
441	fclose (fp);
442	return phase;
443}
444
445static	void	ksk_roll (const char *keyname, int phase, const dki_t *list, const zconf_t *conf)
446{
447	char    path[MAX_PATHSIZE+1];
448	zconf_t localconf;
449	const char *dir;
450	dki_t	*keylist;
451	dki_t	*dkp;
452	dki_t	*standby;
453	int	parent_exist;
454	int	parent_age;
455	int	parent_phase;
456	int	parent_propagation;
457	int	key_ttl;
458	int	ksk;
459
460	if ( phase == 9 )	/* usage */
461	{
462		fprintf (stderr, "A KSK rollover requires three consecutive steps:\n");
463		fprintf (stderr, "\n");
464		fprintf (stderr, "-1%s", loptstr ("|--ksk-roll-phase1 (--ksk-newkey)\n", ""));
465		fprintf (stderr, "\t Create a new KSK.\n");
466		fprintf (stderr, "\t This step also creates a parent-<domain> file which contains only\n");
467		fprintf (stderr, "\t the _old_ key.  This file will be copied in hierarchical mode\n");
468		fprintf (stderr, "\t by dnssec-signer to the parent directory as keyset-<domain> file.\n");
469		fprintf (stderr, "\t Wait until the new keyset is propagated, before going to the next step.\n");
470		fprintf (stderr, "\n");
471		fprintf (stderr, "-2%s", loptstr ("|--ksk-roll-phase2 (--ksk-publish)\n", ""));
472		fprintf (stderr, "\t This step creates a parent-<domain> file with the _new_ key only.\n");
473		fprintf (stderr, "\t Please send this file immediately to the parent (In hierarchical\n");
474		fprintf (stderr, "\t mode this will be done automatically by the dnssec-signer command).\n");
475		fprintf (stderr, "\t Then wait until the new DS is generated by the parent and propagated\n");
476		fprintf (stderr, "\t to all the parent name server, plus the old DS TTL before going to step three.\n");
477		fprintf (stderr, "\n");
478		fprintf (stderr, "-3%s", loptstr ("|--ksk-roll-phase3 (--ksk-delkey)\n", ""));
479		fprintf (stderr, "\t Remove (rename) the old KSK and the parent-<domain> file.\n");
480		fprintf (stderr, "\t You have to manually delete the old KSK (look at file names beginning\n");
481		fprintf (stderr, "\t with an lower 'k').\n");
482		fprintf (stderr, "\n");
483		fprintf (stderr, "-0%s", loptstr ("|--ksk-roll-stat (--ksk-status)\n", ""));
484		fprintf (stderr, "\t Show the current KSK rollover state of a domain.\n");
485
486		fprintf (stderr, "\n");
487
488		return;
489	}
490
491	if ( keyname == NULL || *keyname == '\0' )
492		fatal ("ksk rollover: no domain!");
493
494	dbg_val2 ("ksk_roll: keyname %s, phase = %d\n", keyname, phase);
495
496	/* search for already existent key to get the directory name */
497	if ( (keylist = (dki_t *)zkt_search (list, 0, keyname)) == NULL )
498		fatal ("ksk rollover: domain %s not found!\n", keyname);
499	dkp = keylist;
500
501	/* try to read local config file */
502	dir = dkp->dname;
503	pathname (path, sizeof (path), dir, LOCALCONF_FILE, NULL);
504	if ( fileexist (path) )                 /* load local config file */
505	{
506		dbg_val ("Load local config file \"%s\"\n", path);
507		memcpy (&localconf, conf, sizeof (zconf_t));
508		conf = loadconfig (path, &localconf);
509	}
510	key_ttl = conf->key_ttl;
511
512	/* check if parent-file already exist */
513	pathname (path, sizeof (path), dir, "parent-", keyname);
514	parent_phase = parent_age = 0;
515	if ( (parent_exist = fileexist (path)) != 0 )
516	{
517		parent_phase = get_parent_phase (path);
518		parent_age = file_age (path);
519	}
520	// parent_propagation = 2 * DAYSEC;
521	parent_propagation = 5 * MINSEC;
522
523	ksk = 0;	/* count active(!) key signing keys */
524	standby = NULL;	/* find standby key if available */
525	for ( dkp = keylist; dkp; dkp = dkp->next )
526		if ( dki_isksk (dkp) )
527		 {
528			if ( dki_status (dkp) == DKI_ACT )
529				ksk++;
530			else if ( dki_status (dkp) == DKI_PUB )
531				standby = dkp;
532		}
533
534	switch ( phase )
535	{
536	case 0:	/* print status (debug) */
537		fprintf (stdout, "ksk_rollover:\n");
538		fprintf (stdout, "\t domain = %s\n", keyname);
539		fprintf (stdout, "\t phase = %d\n", parent_phase);
540		fprintf (stdout, "\t parent_file %s %s\n", path, parent_exist ? "exist": "not exist");
541		if ( parent_exist )
542			fprintf (stdout, "\t age of parent_file %d %s\n", parent_age, str_delspace (age2str (parent_age)));
543		fprintf (stdout, "\t # of active key signing keys %d\n", ksk);
544		fprintf (stdout, "\t parent_propagation %d %s\n", parent_propagation, str_delspace (age2str (parent_propagation)));
545		fprintf (stdout, "\t keys ttl %d %s\n", key_ttl, age2str (key_ttl));
546
547		for ( dkp = keylist; dkp; dkp = dkp->next )
548		{
549			/* TODO: Nur zum testen */
550			dki_prt_dnskey (dkp, stdout);
551		}
552		break;
553	case 1:
554		if ( parent_exist || ksk > 1 )
555			fatal ("Can\'t create new ksk because there is already an ksk rollover in progress\n");
556
557		fprintf (stdout, "create new ksk \n");
558		dkp = dki_new (dir, keyname, DKI_KSK, conf->k_algo, conf->k_bits, conf->k_random, conf->k_life / DAYSEC);
559		if ( dkp == NULL )
560			fatal ("Can't create key %s: %s!\n", keyname, dki_geterrstr ());
561		if ( standby )
562		{
563			dki_setstatus (standby, DKI_ACT);	/* activate standby key */
564			dki_setstatus (dkp, DKI_PUB);	/* new key will be the new standby */
565		}
566
567		// dkp = keylist;	/* use old key to create the parent file */
568		if ( (dkp = (dki_t *)dki_findalgo (keylist, 1, conf->k_algo, 'a', 1)) == NULL )	/* find the oldest active ksk to create the parent file */
569			fatal ("ksk_rollover phase1: Couldn't find the old active key\n");
570		if ( !create_parent_file (path, phase, key_ttl, dkp) )
571			fatal ("Couldn't create parentfile %s\n", path);
572		break;
573
574	case 2:
575		if ( ksk < 2 )
576			fatal ("Can\'t publish new key because no one exist\n");
577		if ( !parent_exist )
578			fatal ("More than one KSK but no parent file found!\n");
579		if ( parent_phase != 1 )
580			fatal ("Parent file exists but is in wrong state (phase = %d)\n", parent_phase);
581		if ( parent_age < conf->proptime + key_ttl )
582			fatal ("ksk_rollover (phase2): you have to wait for the propagation of the new KSK (at least %dsec or %s)\n",
583				conf->proptime + key_ttl - parent_age,
584				str_delspace (age2str (conf->proptime + key_ttl - parent_age)));
585
586		fprintf (stdout, "save new ksk in parent file\n");
587		dkp = keylist->next;	/* set dkp to new ksk */
588		if ( !create_parent_file (path, phase, key_ttl, dkp) )
589			fatal ("Couldn't create parentfile %s\n", path);
590		break;
591	case 3:
592		if ( !parent_exist || ksk < 2 )
593			fatal ("ksk-delkey only allowed after ksk-publish\n");
594		if ( parent_phase != 2 )
595			fatal ("Parent file exists but is in wrong state (phase = %d)\n", parent_phase);
596		if ( parent_age < parent_propagation + key_ttl )
597			fatal ("ksk_rollover (phase3): you have to wait for DS propagation (at least %dsec or %s)\n",
598				parent_propagation + key_ttl - parent_age,
599				str_delspace (age2str (parent_propagation + key_ttl - parent_age)));
600		/* remove the parentfile */
601		fprintf (stdout, "remove parentfile \n");
602		unlink (path);
603		/* remove or rename the old key */
604		fprintf (stdout, "old ksk renamed \n");
605		dkp = keylist;	/* set dkp to old ksk */
606		dki_remove (dkp);
607		break;
608	default:	assert (phase == 1 || phase == 2 || phase == 3);
609	}
610}
611
612/*****************************************************************
613**	create_parent_file ()
614*****************************************************************/
615static	int	create_parent_file (const char *fname, int phase, int ttl, const dki_t *dkp)
616{
617	FILE	*fp;
618
619	assert ( fname != NULL );
620
621	if ( dkp == NULL || (phase != 1 && phase != 2) )
622		return 0;
623
624	if ( (fp = fopen (fname, "w")) == NULL )
625		fatal ("can\'t create new parentfile \"%s\"\n", fname);
626
627	if ( phase == 1 )
628		fprintf (fp, "; KSK rollover phase1 (old key)\n");
629	else
630		fprintf (fp, "; KSK rollover phase2 (new key)\n");
631
632	dki_prt_dnskeyttl (dkp, fp, ttl);
633	fclose (fp);
634
635	return phase;
636}
637
638static	int	parsedirectory (const char *dir, dki_t **listp)
639{
640	dki_t	*dkp;
641	DIR	*dirp;
642	struct  dirent  *dentp;
643	char	path[MAX_PATHSIZE+1];
644
645	if ( dirflag )
646		return 0;
647
648	dbg_val ("directory: opendir(%s)\n", dir);
649	if ( (dirp = opendir (dir)) == NULL )
650		return 0;
651
652	while ( (dentp = readdir (dirp)) != NULL )
653	{
654		if ( is_dotfilename (dentp->d_name) )
655			continue;
656
657		dbg_val ("directory: check %s\n", dentp->d_name);
658		pathname (path, sizeof (path), dir, dentp->d_name, NULL);
659		if ( is_directory (path) && recflag )
660		{
661			dbg_val ("directory: recursive %s\n", path);
662			parsedirectory (path, listp);
663		}
664		else if ( is_keyfilename (dentp->d_name) )
665			if ( (dkp = dki_read (dir, dentp->d_name)) )
666			{
667				// fprintf (stderr, "parsedir: tssearch (%d %s)\n", dkp, dkp->name);
668#if defined (USE_TREE) && USE_TREE
669				dki_tadd (listp, dkp, 1);
670#else
671				dki_add (listp, dkp);
672#endif
673			}
674	}
675	closedir (dirp);
676	return 1;
677}
678
679static	void	parsefile (const char *file, dki_t **listp)
680{
681	char	path[MAX_PATHSIZE+1];
682	dki_t	*dkp;
683
684	/* file arg contains path ? ... */
685	file = splitpath (path, sizeof (path), file);	/* ... then split of */
686
687	if ( is_keyfilename (file) )	/* plain file name looks like DNS key file ? */
688	{
689		if ( (dkp = dki_read (path, file)) )	/* read DNS key file ... */
690#if defined (USE_TREE) && USE_TREE
691			dki_tadd (listp, dkp, 1);		/* ... and add to tree */
692#else
693			dki_add (listp, dkp);		/* ... and add to list */
694#endif
695		else
696			error ("error parsing %s: (%s)\n", file, dki_geterrstr());
697	}
698}
699
700static	const char *parsetag (const char *str, int *tagp)
701{
702	const	char	*p;
703
704	*tagp = 0;
705	while ( isspace (*str) )	/* skip leading ws */
706		str++;
707
708	p = str;
709	if ( isdigit (*p) )		/* keytag starts with digit */
710	{
711		sscanf (p, "%u", tagp);	/* read keytag as number */
712		do			/* eat up to the end of the number */
713			p++;
714		while ( isdigit (*p) );
715
716		if ( *p == ':' )	/* label follows ? */
717			return p+1;	/* return that */
718		if ( *p == '\0' )
719			return NULL;	/* no label */
720	}
721	return str;	/* return as label string if not a numeric keytag */
722}
723