1/*	$NetBSD$	*/
2
3/*****************************************************************
4**
5**	@(#) dki.c  (c) Jan 2005  Holger Zuleger  hznet.de
6**
7**	A library for managing BIND dnssec key files.
8**
9**	Copyright (c) Jan 2005, Holger Zuleger HZnet. All rights reserved.
10**
11**	This software is open source.
12**
13**	Redistribution and use in source and binary forms, with or without
14**	modification, are permitted provided that the following conditions
15**	are met:
16**
17**	Redistributions of source code must retain the above copyright notice,
18**	this list of conditions and the following disclaimer.
19**
20**	Redistributions in binary form must reproduce the above copyright notice,
21**	this list of conditions and the following disclaimer in the documentation
22**	and/or other materials provided with the distribution.
23**
24**	Neither the name of Holger Zuleger HZnet nor the names of its contributors may
25**	be used to endorse or promote products derived from this software without
26**	specific prior written permission.
27**
28**	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29**	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
30**	TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
31**	PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
32**	LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
33**	CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
34**	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
35**	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
36**	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
37**	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38**	POSSIBILITY OF SUCH DAMAGE.
39**
40**
41*****************************************************************/
42
43# include <stdio.h>
44# include <string.h>
45# include <ctype.h>	/* tolower(), ... */
46# include <unistd.h>	/* link(), unlink(), ... */
47# include <stdlib.h>
48# include <sys/types.h>
49# include <sys/time.h>
50# include <sys/stat.h>
51# include <dirent.h>
52# include <assert.h>
53#ifdef HAVE_CONFIG_H
54# include <config.h>
55#endif
56# include "config_zkt.h"
57# include "debug.h"
58# include "domaincmp.h"
59# include "misc.h"
60# include "zconf.h"
61#define	extern
62# include "dki.h"
63#undef	extern
64
65/*****************************************************************
66**	private (static) function declaration and definition
67*****************************************************************/
68static	char	dki_estr[255+1];
69
70static	dki_t	*dki_alloc ()
71{
72	dki_estr[0] = '\0';
73	dki_t	*dkp = malloc (sizeof (dki_t));
74
75	if ( (dkp = malloc (sizeof (dki_t))) )
76	{
77		memset (dkp, 0, sizeof (dki_t));
78		return dkp;
79	}
80
81	snprintf (dki_estr, sizeof (dki_estr),
82			"dki_alloc: Out of memory");
83	return NULL;
84}
85
86static	int	dki_readfile (FILE *fp, dki_t *dkp)
87{
88	int	algo,	flags,	type;
89	int	c;
90	char	*p;
91	char	buf[4095+1];
92	char	tag[25+1];
93	char	val[14+1];	/* e.g. "YYYYMMDDhhmmss" | "60d" */
94
95	assert (dkp != NULL);
96	assert (fp != NULL);
97
98	while ( (c = getc (fp)) == ';' )	/* line start with comment ? */
99	{
100		tag[0] = val[0] = '\0';
101		if ( (c = getc (fp)) == '%' )	/* special comment? */
102		{
103			while ( (c = getc (fp)) == ' ' || c == '\t' )
104				;
105			ungetc (c, fp);
106			/* then try to read in the creation, expire and lifetime */
107			if ( fscanf (fp, "%25[a-zA-Z]=%14s", tag, val) == 2 )
108			{
109				dbg_val2 ("dki_readfile: tag=%s val=%s \n", tag, val);
110				switch ( tolower (tag[0]) )
111				{
112				case 'g': dkp->gentime = timestr2time (val);	break;
113				case 'e': dkp->exptime = timestr2time (val);	break;
114				case 'l': dkp->lifetime = atoi (val) * DAYSEC;	break;
115				}
116			}
117		}
118		else
119			ungetc (c, fp);
120		while ( (c = getc (fp)) != EOF && c != '\n' )	/* eat up rest of the line */
121			;
122	}
123	ungetc (c, fp);	/* push back last char */
124
125	if ( fscanf (fp, "%4095s", buf) != 1 )	/* read label */
126		return -1;
127
128	if ( strcmp (buf, dkp->name) != 0 )
129		return -2;
130
131#if defined(TTL_IN_KEYFILE_ALLOWED) && TTL_IN_KEYFILE_ALLOWED
132	/* skip optional TTL value */
133	while ( (c = getc (fp)) != EOF && isspace (c) )	/* skip spaces */
134		;
135	if ( isdigit (c) )				/* skip ttl */
136		fscanf (fp, "%*d");
137	else
138		ungetc (c, fp);				/* oops, no ttl */
139#endif
140
141	if ( (c = fscanf (fp, " IN DNSKEY %d %d %d", &flags, &type, &algo)) != 3 &&
142	     (c = fscanf (fp, "KEY %d %d %d", &flags, &type, &algo)) != 3 )
143		return -3;
144	if ( type != 3 || algo != dkp->algo )
145		return -4;		/* no DNSKEY or algorithm mismatch */
146	if ( ((flags >> 8) & 0xFF) != 01 )
147		return -5;		/* no ZONE key */
148	dkp->flags = flags;
149
150	if ( fgets (buf, sizeof buf, fp) == NULL || buf[0] == '\0' )
151		return -6;
152	p = buf + strlen (buf);
153	*--p = '\0';		/* delete trailing \n */
154	/* delete leading ws */
155	for ( p = buf; *p  && isspace (*p); p++ )
156		;
157
158	dkp->pubkey = strdup (p);
159
160	return 0;
161}
162
163static	int	dki_writeinfo (const dki_t *dkp, const char *path)
164{
165	FILE	*fp;
166
167	assert (dkp != NULL);
168	assert (path != NULL && path[0] != '\0');
169
170	if ( (fp = fopen (path, "w")) == NULL )
171		return 0;
172	dbg_val1 ("dki_writeinfo %s\n", path);
173	if ( dki_prt_dnskey_raw (dkp, fp) == 0 )
174		return 0;
175	fclose (fp);
176	touch (path, dkp->time);	/* restore time of key file */
177
178	return 1;
179}
180
181static	int	dki_setstat (dki_t *dkp, int status, int preserve_time);
182
183/*****************************************************************
184**	public function definition
185*****************************************************************/
186
187/*****************************************************************
188**	dki_free ()
189*****************************************************************/
190void	dki_free (dki_t *dkp)
191{
192	assert (dkp != NULL);
193
194	if ( dkp->pubkey )
195		free (dkp->pubkey);
196	free (dkp);
197}
198
199/*****************************************************************
200**	dki_freelist ()
201*****************************************************************/
202void	dki_freelist (dki_t **listp)
203{
204	dki_t	*curr;
205	dki_t	*next;
206
207	assert (listp != NULL);
208
209	curr = *listp;
210	while ( curr )
211	{
212		next = curr->next;
213		dki_free (curr);
214		curr = next;
215	}
216	if ( *listp )
217		*listp = NULL;
218}
219
220#if defined(USE_TREE) && USE_TREE
221/*****************************************************************
222**	dki_tfree ()
223*****************************************************************/
224void	dki_tfree (dki_t **tree)
225{
226	assert (tree != NULL);
227	// TODO: tdestroy is a GNU extension
228	// tdestroy (*tree, dki_free);
229}
230#endif
231
232#if defined(BIND_VERSION) && BIND_VERSION >= 970
233# define	KEYGEN_COMPMODE	"-C -q "	/* this is the compability mode needed by BIND 9.7 */
234#else
235# define	KEYGEN_COMPMODE	""
236#endif
237/*****************************************************************
238**	dki_new ()
239**	create new keyfile
240**	allocate memory for new dki key and init with keyfile
241*****************************************************************/
242dki_t	*dki_new (const char *dir, const char *name, int ksk, int algo, int bitsize, const char *rfile, int lf_days)
243{
244	char	cmdline[511+1];
245	char	fname[254+1];
246	char	randfile[254+1];
247	FILE	*fp;
248	int	len;
249	char	*flag = "";
250	char	*expflag = "";
251	dki_t	*new;
252
253	if ( ksk )
254		flag = "-f KSK";
255
256	randfile[0] = '\0';
257	if ( rfile && *rfile )
258		snprintf (randfile, sizeof (randfile), "-r %.250s ", rfile);
259
260	if ( algo == DK_ALGO_RSA || algo == DK_ALGO_RSASHA1 || algo == DK_ALGO_RSASHA256 || algo == DK_ALGO_RSASHA512 )
261		expflag = "-e ";
262
263	if ( dir && *dir )
264		snprintf (cmdline, sizeof (cmdline), "cd %s ; %s %s%s%s-n ZONE -a %s -b %d %s %s",
265			dir, KEYGENCMD, KEYGEN_COMPMODE, randfile, expflag, dki_algo2str(algo), bitsize, flag, name);
266	else
267		snprintf (cmdline, sizeof (cmdline), "%s %s%s%s-n ZONE -a %s -b %d %s %s",
268			KEYGENCMD, KEYGEN_COMPMODE, randfile, expflag, dki_algo2str(algo), bitsize, flag, name);
269
270	dbg_msg (cmdline);
271
272	if ( (fp = popen (cmdline, "r")) == NULL || fgets (fname, sizeof fname, fp) == NULL )
273		return NULL;
274	pclose (fp);
275
276	len = strlen (fname) - 1;
277	if ( len >= 0 && fname[len] == '\n' )
278		fname[len] = '\0';
279
280	new = dki_read (dir, fname);
281	if ( new )
282		dki_setlifetime (new, lf_days);	/* sets gentime + proposed lifetime */
283
284	return new;
285}
286
287/*****************************************************************
288**	dki_read ()
289**	read key from file 'filename' (independed of the extension)
290*****************************************************************/
291dki_t	*dki_read (const char *dirname, const char *filename)
292{
293	dki_t	*dkp;
294	FILE	*fp;
295	struct	stat	st;
296	int	len;
297	int	err;
298	char	fname[MAX_FNAMESIZE+1];
299	char	path[MAX_PATHSIZE+1];
300
301	dki_estr[0] = '\0';
302	if ( (dkp = dki_alloc ()) == NULL )
303		return (NULL);
304
305	len = sizeof (fname) - 1;
306	fname[len] = '\0';
307	strncpy (fname, filename, len);
308
309	len = strlen (fname);			/* delete extension */
310	if ( len > 4 && strcmp (&fname[len - 4], DKI_KEY_FILEEXT) == 0 )
311		fname[len - 4] = '\0';
312	else if ( len > 10 && strcmp (&fname[len - 10], DKI_PUB_FILEEXT) == 0 )
313		fname[len - 10] = '\0';
314	else if ( len > 8 && strcmp (&fname[len - 8], DKI_ACT_FILEEXT) == 0 )
315		fname[len - 8] = '\0';
316	else if ( len > 12 && strcmp (&fname[len - 12], DKI_DEP_FILEEXT) == 0 )
317		fname[len - 12] = '\0';
318	dbg_line ();
319
320	assert (strlen (dirname)+1 < sizeof (dkp->dname));
321	strcpy (dkp->dname, dirname);
322
323	assert (strlen (fname)+1 < sizeof (dkp->fname));
324	strcpy (dkp->fname, fname);
325	dbg_line ();
326	if ( sscanf (fname, "K%254[^+]+%hd+%d", dkp->name, &dkp->algo, &dkp->tag) != 3 )
327	{
328		snprintf (dki_estr, sizeof (dki_estr),
329			"dki_read: Filename don't match expected format (%s)", fname);
330		return (NULL);
331	}
332
333	pathname (path, sizeof (path), dkp->dname, dkp->fname, DKI_KEY_FILEEXT);
334	dbg_val ("dki_read: path \"%s\"\n", path);
335	if ( (fp = fopen (path, "r")) == NULL )
336	{
337		snprintf (dki_estr, sizeof (dki_estr),
338			"dki_read: Can\'t open file \"%s\" for reading", path);
339		return (NULL);
340	}
341
342	dbg_line ();
343	if ( (err = dki_readfile (fp, dkp)) != 0 )
344	{
345		dbg_line ();
346		snprintf (dki_estr, sizeof (dki_estr),
347			"dki_read: Can\'t read key from file %s (errno %d)", path, err);
348		fclose (fp);
349		return (NULL);
350	}
351
352	dbg_line ();
353	if ( fstat (fileno(fp), &st) )
354	{
355		snprintf (dki_estr, sizeof (dki_estr),
356			"dki_read: Can\'t stat file %s", fname);
357		return (NULL);
358	}
359	dkp->time = st.st_mtime;
360
361	dbg_line ();
362	pathname (path, sizeof (path), dkp->dname, dkp->fname, DKI_ACT_FILEEXT);
363	if ( fileexist (path) )
364	{
365		if ( dki_isrevoked (dkp) )
366			dkp->status = DKI_REV;
367		else
368			dkp->status = DKI_ACT;
369	}
370	else
371	{
372		pathname (path, sizeof (path), dkp->dname, dkp->fname, DKI_PUB_FILEEXT);
373		if ( fileexist (path) )
374			dkp->status = DKI_PUB;
375		else
376		{
377			pathname (path, sizeof (path), dkp->dname, dkp->fname, DKI_DEP_FILEEXT);
378			if ( fileexist (path) )
379				dkp->status = DKI_DEP;
380			else
381				dkp->status = DKI_SEP;
382		}
383	}
384
385	dbg_line ();
386	fclose (fp);
387
388	dbg_line ();
389	return dkp;
390}
391
392/*****************************************************************
393**	dki_readdir ()
394**	read key files from directory 'dir' and, if recursive is
395**	true, from all directorys below that.
396*****************************************************************/
397int	dki_readdir (const char *dir, dki_t **listp, int recursive)
398{
399	dki_t	*dkp;
400	DIR	*dirp;
401	struct  dirent  *dentp;
402	char	path[MAX_PATHSIZE+1];
403
404	dbg_val ("directory: opendir(%s)\n", dir);
405	if ( (dirp = opendir (dir)) == NULL )
406		return 0;
407
408	while ( (dentp = readdir (dirp)) != NULL )
409	{
410		if ( is_dotfilename (dentp->d_name) )
411			continue;
412
413		dbg_val ("directory: check %s\n", dentp->d_name);
414		pathname (path, sizeof (path), dir, dentp->d_name, NULL);
415		if ( is_directory (path) && recursive )
416		{
417			dbg_val ("directory: recursive %s\n", path);
418			dki_readdir (path, listp, recursive);
419		}
420		else if ( is_keyfilename (dentp->d_name) )
421			if ( (dkp = dki_read (dir, dentp->d_name)) )
422				dki_add (listp, dkp);
423	}
424	closedir (dirp);
425	return 1;
426}
427
428/*****************************************************************
429**	dki_setstatus_preservetime ()
430**	set status of key and change extension to
431**	".published", ".private" or ".depreciated"
432*****************************************************************/
433int	dki_setstatus_preservetime (dki_t *dkp, int status)
434{
435	return dki_setstat (dkp, status, 1);
436}
437
438/*****************************************************************
439**	dki_setstatus ()
440**	set status of key and change extension to
441**	".published", ".private" or ".depreciated"
442*****************************************************************/
443int	dki_setstatus (dki_t *dkp, int status)
444{
445	return dki_setstat (dkp, status, 0);
446}
447
448/*****************************************************************
449**	dki_setstat ()
450**	low level function of dki_setstatus and dki_setstatus_preservetime
451*****************************************************************/
452static	int	dki_setstat (dki_t *dkp, int status, int preserve_time)
453{
454	char	frompath[MAX_PATHSIZE+1];
455	char	topath[MAX_PATHSIZE+1];
456	time_t	totime;
457	time_t	currtime;
458
459	if ( dkp == NULL )
460		return 0;
461
462	currtime = time (NULL);
463	status = tolower (status);
464	switch ( dkp->status )	/* look at old status */
465	{
466	case 'r':
467		if ( status == 'r' )
468			return 1;
469		break;
470	case 'a':
471		if ( status == 'a' )
472			return 1;
473		pathname (frompath, sizeof (frompath), dkp->dname, dkp->fname, DKI_ACT_FILEEXT);
474		break;
475	case 'd':
476		if ( status == 'd' )
477			return 1;
478		pathname (frompath, sizeof (frompath), dkp->dname, dkp->fname, DKI_DEP_FILEEXT);
479		break;
480	case 'p':	/* or 's' */
481		if ( status == 'p' || status == 's' )
482			return 1;
483		pathname (frompath, sizeof (frompath), dkp->dname, dkp->fname, DKI_PUB_FILEEXT);
484		break;
485	default:
486		/* TODO: set error code */
487		return 0;
488	}
489
490	dbg_val ("dki_setstat: \"%s\"\n", frompath);
491	dbg_val ("dki_setstat: to status \"%c\"\n", status);
492
493	/* a state change could result in different things: */
494	/* 1) write a new keyfile when the REVOKE bit is set or unset */
495	if ( status == 'r' || (status == 'a' && dki_isrevoked (dkp)) )
496	{
497		pathname (topath, sizeof (topath), dkp->dname, dkp->fname, DKI_KEY_FILEEXT);
498
499		if ( status == 'r' )
500			dki_setflag (dkp, DK_FLAG_REVOKE);	/* set REVOKE bit */
501		else
502			dki_unsetflag (dkp, DK_FLAG_REVOKE);	/* clear REVOKE bit */
503
504
505		dki_writeinfo (dkp, topath);	/* ..and write it to the key file */
506
507		if ( !preserve_time )
508			touch (topath, time (NULL));
509
510		return 0;
511	}
512
513
514	/* 2) change the filename of the private key in all other cases */
515	totime = 0L;
516	if ( preserve_time )
517		totime = file_mtime (frompath);    /* get original timestamp */
518	topath[0] = '\0';
519	switch ( status )
520	{
521	case 'a':
522		pathname (topath, sizeof (topath), dkp->dname, dkp->fname, DKI_ACT_FILEEXT);
523		break;
524	case 'd':
525		pathname (topath, sizeof (topath), dkp->dname, dkp->fname, DKI_DEP_FILEEXT);
526		break;
527	case 's':		/* standby means a "published KSK" */
528		if ( !dki_isksk (dkp) )
529			return 2;
530		status = 'p';
531		/* fall through */
532	case 'p':
533		pathname (topath, sizeof (topath), dkp->dname, dkp->fname, DKI_PUB_FILEEXT);
534		break;
535	}
536
537	if ( topath[0] )
538	{
539		dbg_val ("dki_setstat: to  \"%s\"\n", topath);
540		if ( link (frompath, topath) == 0 )
541			unlink (frompath);
542		dkp->status = status;
543		if ( !totime )
544			totime = time (NULL);	/* set .key file to current time */
545		pathname (topath, sizeof (topath), dkp->dname, dkp->fname, DKI_KEY_FILEEXT);
546		touch (topath, totime);	/* store/restore time of status change */
547	}
548
549	return 0;
550}
551
552/*****************************************************************
553**	dki_remove ()
554**	rename files associated with key, so that the keys are not
555**	recognized by the zkt tools e.g.
556**	Kdo.ma.in.+001+12345.key ==> kdo.ma.in.+001+12345.key
557**	(second one starts with a lower case 'k')
558*****************************************************************/
559dki_t	*dki_remove (dki_t *dkp)
560{
561	char	path[MAX_PATHSIZE+1];
562	char	newpath[MAX_PATHSIZE+1];
563	char	newfile[MAX_FNAMESIZE+1];
564	dki_t	*next;
565	const	char	**pext;
566	static	const	char	*ext[] = {
567		DKI_KEY_FILEEXT, DKI_PUB_FILEEXT,
568		DKI_ACT_FILEEXT, DKI_DEP_FILEEXT,
569		NULL
570	};
571
572	if ( dkp == NULL )
573		return NULL;
574
575	strncpy (newfile, dkp->fname, sizeof (newfile));
576	*newfile = tolower (*newfile);
577	for ( pext = ext; *pext; pext++ )
578	{
579		pathname (path, sizeof (path), dkp->dname, dkp->fname, *pext);
580		if ( fileexist (path) )
581		{
582			pathname (newpath, sizeof (newpath), dkp->dname, newfile, *pext);
583
584			dbg_val2 ("dki_remove: %s ==> %s \n", path, newpath);
585			rename (path, newpath);
586		}
587	}
588	next = dkp->next;
589	dki_free (dkp);
590
591	return next;
592}
593
594/*****************************************************************
595**	dki_destroy ()
596**	delete files associated with key and free allocated memory
597*****************************************************************/
598dki_t	*dki_destroy (dki_t *dkp)
599{
600	char	path[MAX_PATHSIZE+1];
601	dki_t	*next;
602	const	char	**pext;
603	static	const	char	*ext[] = {
604		DKI_KEY_FILEEXT, DKI_PUB_FILEEXT,
605		DKI_ACT_FILEEXT, DKI_DEP_FILEEXT,
606		NULL
607	};
608
609	if ( dkp == NULL )
610		return NULL;
611
612	for ( pext = ext; *pext; pext++ )
613	{
614		pathname (path, sizeof (path), dkp->dname, dkp->fname, *pext);
615		if ( fileexist (path) )
616		{
617			dbg_val ("dki_remove: %s \n", path);
618			unlink (path);
619		}
620	}
621	next = dkp->next;
622	dki_free (dkp);
623
624	return next;
625}
626
627/*****************************************************************
628**	dki_algo2str ()
629**	return a string describing the key algorithm
630*****************************************************************/
631char	*dki_algo2str (int algo)
632{
633	switch ( algo )
634	{
635	case DK_ALGO_RSA:		return ("RSAMD5");
636	case DK_ALGO_DH:		return ("DH");
637	case DK_ALGO_DSA:		return ("DSA");
638	case DK_ALGO_EC:		return ("EC");
639	case DK_ALGO_RSASHA1:		return ("RSASHA1");
640	case DK_ALGO_NSEC3DSA:		return ("NSEC3DSA");
641	case DK_ALGO_NSEC3RSASHA1:	return ("NSEC3RSASHA1");
642	case DK_ALGO_RSASHA256:		return ("RSASHA256");
643	case DK_ALGO_RSASHA512:		return ("RSASHA512");
644	}
645	return ("unknown");
646}
647
648/*****************************************************************
649**	dki_algo2sstr ()
650**	return a short string describing the key algorithm
651*****************************************************************/
652char	*dki_algo2sstr (int algo)
653{
654	switch ( algo )
655	{
656	case DK_ALGO_RSA:		return ("RSAMD5");
657	case DK_ALGO_DH:		return ("DH");
658	case DK_ALGO_DSA:		return ("DSA");
659	case DK_ALGO_EC:		return ("EC");
660	case DK_ALGO_RSASHA1:		return ("RSASHA1");
661	case DK_ALGO_NSEC3DSA:		return ("N3DSA");
662	case DK_ALGO_NSEC3RSASHA1:	return ("N3RSA1");
663	case DK_ALGO_RSASHA256:		return ("RSASHA2");
664	case DK_ALGO_RSASHA512:		return ("RSASHA5");
665	}
666	return ("unknown");
667}
668
669/*****************************************************************
670**	dki_geterrstr ()
671**	return error string
672*****************************************************************/
673const	char	*dki_geterrstr ()
674{
675	return dki_estr;
676}
677
678/*****************************************************************
679**	dki_prt_dnskey ()
680*****************************************************************/
681int	dki_prt_dnskey (const dki_t *dkp, FILE *fp)
682{
683	return dki_prt_dnskeyttl (dkp, fp, 0);
684}
685
686/*****************************************************************
687**	dki_prt_dnskeyttl ()
688*****************************************************************/
689int	dki_prt_dnskeyttl (const dki_t *dkp, FILE *fp, int ttl)
690{
691	char	*p;
692
693	if ( dkp == NULL )
694		return 0;
695
696	fprintf (fp, "%s ", dkp->name);
697	if ( ttl > 0 )
698		fprintf (fp, "%d ", ttl);
699	fprintf (fp, "IN DNSKEY  ");
700	fprintf (fp, "%d 3 %d (", dkp->flags, dkp->algo);
701	fprintf (fp, "\n\t\t\t");
702	for ( p = dkp->pubkey; *p ; p++ )
703		if ( *p == ' ' )
704			fprintf (fp, "\n\t\t\t");
705		else
706			putc (*p, fp);
707	fprintf (fp, "\n\t\t");
708	if ( dki_isrevoked (dkp) )
709		fprintf (fp, ") ; key id = %u (original key id = %u)", (dkp->tag + 128) % 65535, dkp->tag);
710	else
711		fprintf (fp, ") ; key id = %u", dkp->tag);
712	fprintf (fp, "\n");
713
714	return 1;
715}
716
717/*****************************************************************
718**	dki_prt_dnskey_raw ()
719*****************************************************************/
720int	dki_prt_dnskey_raw (const dki_t *dkp, FILE *fp)
721{
722	int	days;
723
724	if ( dkp == NULL )
725		return 0;
726
727	if ( dkp->gentime  )
728		fprintf (fp, ";%%\tgenerationtime=%s\n", time2isostr (dkp->gentime, 's'));
729	if ( (days = dki_lifetimedays (dkp)) )
730		fprintf (fp, ";%%\tlifetime=%dd\n", days);
731	if ( dkp->exptime  )
732		fprintf (fp, ";%%\texpirationtime=%s\n", time2isostr (dkp->exptime, 's'));
733
734	fprintf (fp, "%s ", dkp->name);
735#if 0
736	if ( ttl > 0 )
737		fprintf (fp, "%d ", ttl);
738#endif
739	fprintf (fp, "IN DNSKEY  ");
740	fprintf (fp, "%d 3 %d ", dkp->flags, dkp->algo);
741	fprintf (fp, "%s\n", dkp->pubkey);
742
743	return 1;
744}
745
746/*****************************************************************
747**	dki_prt_comment ()
748*****************************************************************/
749int	dki_prt_comment (const dki_t *dkp, FILE *fp)
750{
751	int	len = 0;
752
753	if ( dkp == NULL )
754		return len;
755	len += fprintf (fp, "; %s  ", dkp->name);
756	len += fprintf (fp, "tag=%u  ", dkp->tag);
757	len += fprintf (fp, "algo=%s  ", dki_algo2str(dkp->algo));
758	len += fprintf (fp, "generated %s\n", time2str (dkp->time, 's'));
759
760	return len;
761}
762
763/*****************************************************************
764**	dki_prt_trustedkey ()
765*****************************************************************/
766int	dki_prt_trustedkey (const dki_t *dkp, FILE *fp)
767{
768	char	*p;
769	int	spaces;
770	int	len = 0;
771
772	if ( dkp == NULL )
773		return len;
774	len += fprintf (fp, "\"%s\"  ", dkp->name);
775	spaces = 22 - (strlen (dkp->name) + 3);
776	len += fprintf (fp, "%*s", spaces > 0 ? spaces : 0 , " ");
777	len += fprintf (fp, "%d 3 %d ", dkp->flags, dkp->algo);
778	if ( spaces < 0 )
779		len += fprintf (fp, "\n\t\t\t%7s", " ");
780	len += fprintf (fp, "\"");
781	for ( p = dkp->pubkey; *p ; p++ )
782		if ( *p == ' ' )
783			len += fprintf (fp, "\n\t\t\t\t");
784		else
785			putc (*p, fp), len += 1;
786
787	if ( dki_isrevoked (dkp) )
788		len += fprintf (fp, "\" ; # key id = %u (original key id = %u)\n\n", (dkp->tag + 128) % 65535, dkp->tag);
789	else
790		len += fprintf (fp, "\" ; # key id = %u\n\n", dkp->tag);
791	return len;
792}
793
794
795/*****************************************************************
796**	dki_cmp () 	return <0 | 0 | >0
797*****************************************************************/
798int	dki_cmp (const dki_t *a, const dki_t *b)
799{
800	int	res;
801
802	if ( a == NULL ) return -1;
803	if ( b == NULL ) return 1;
804
805	/* sort by domain name, */
806	if ( (res = domaincmp (a->name, b->name)) != 0 )
807		return res;
808
809	/* then by key type, */
810	if ( (res = dki_isksk (b) - dki_isksk (a)) != 0 )
811		return res;
812
813	/* and last by creation time,  */
814	return (ulong)a->time - (ulong)b->time;
815}
816
817#if defined(USE_TREE) && USE_TREE
818/*****************************************************************
819**	dki_allcmp () 	return <0 | 0 | >0
820*****************************************************************/
821int	dki_allcmp (const dki_t *a, const dki_t *b)
822{
823	int	res;
824
825	if ( a == NULL ) return -1;
826	if ( b == NULL ) return 1;
827
828// fprintf (stderr, "dki_allcmp %s, %s)\n", a->name, b->name);
829	/* sort by domain name, */
830	if ( (res = domaincmp (a->name, b->name)) != 0 )
831		return res;
832
833	/* then by key type, */
834	if ( (res = dki_isksk (b) - dki_isksk (a)) != 0 )
835		return res;
836
837	/* creation time,  */
838	if ( (res = (ulong)a->time - (ulong)b->time) != 0 )
839		return res;
840
841	/* and last by tag */
842	return a->tag - b->tag;
843}
844
845/*****************************************************************
846**	dki_namecmp () 	return <0 | 0 | >0
847*****************************************************************/
848int	dki_namecmp (const dki_t *a, const dki_t *b)
849{
850	if ( a == NULL ) return -1;
851	if ( b == NULL ) return 1;
852
853	return domaincmp (a->name, b->name);
854}
855
856/*****************************************************************
857**	dki_revnamecmp () 	return <0 | 0 | >0
858*****************************************************************/
859int	dki_revnamecmp (const dki_t *a, const dki_t *b)
860{
861	if ( a == NULL ) return -1;
862	if ( b == NULL ) return 1;
863
864	return domaincmp_dir (a->name, b->name, 0);
865}
866
867/*****************************************************************
868**	dki_tagcmp () 	return <0 | 0 | >0
869*****************************************************************/
870int	dki_tagcmp (const dki_t *a, const dki_t *b)
871{
872	if ( a == NULL ) return -1;
873	if ( b == NULL ) return 1;
874
875	return a->tag - b->tag;
876}
877#endif
878
879/*****************************************************************
880**	dki_timecmp ()
881*****************************************************************/
882int	dki_timecmp (const dki_t *a, const dki_t *b)
883{
884	if ( a == NULL ) return -1;
885	if ( b == NULL ) return 1;
886
887	return ((ulong)a->time - (ulong)b->time);
888}
889
890/*****************************************************************
891**	dki_algo ()	return the algorithm of the key
892*****************************************************************/
893time_t	dki_algo (const dki_t *dkp)
894{
895	assert (dkp != NULL);
896	return (dkp->algo);
897}
898
899/*****************************************************************
900**	dki_time ()	return the timestamp of the key
901*****************************************************************/
902time_t	dki_time (const dki_t *dkp)
903{
904	assert (dkp != NULL);
905	return (dkp->time);
906}
907
908/*****************************************************************
909**	dki_exptime ()	return the expiration timestamp of the key
910*****************************************************************/
911time_t	dki_exptime (const dki_t *dkp)
912{
913	assert (dkp != NULL);
914	return (dkp->exptime);
915}
916
917/*****************************************************************
918**	dki_lifetime (dkp)	return the lifetime of the key in sec!
919*****************************************************************/
920time_t	dki_lifetime (const dki_t *dkp)
921{
922	assert (dkp != NULL);
923	return (dkp->lifetime);
924}
925
926/*****************************************************************
927**	dki_lifetimedays (dkp)	return the lifetime of the key in days!
928*****************************************************************/
929ushort	dki_lifetimedays (const dki_t *dkp)
930{
931	assert (dkp != NULL);
932	return (dkp->lifetime / DAYSEC);
933}
934
935/*****************************************************************
936**	dki_gentime (dkp)	return the generation timestamp of the key
937*****************************************************************/
938time_t	dki_gentime (const dki_t *dkp)
939{
940	assert (dkp != NULL);
941	return (dkp->gentime > 0L ? dkp->gentime: dkp->time);
942}
943
944/*****************************************************************
945**	dki_setlifetime (dkp, int days)
946**	set the lifetime in days (and also the gentime if not set)
947**	return the old lifetime of the key in days!
948*****************************************************************/
949ushort	dki_setlifetime (dki_t *dkp, int days)
950{
951	ulong	lifetsec;
952	char	path[MAX_PATHSIZE+1];
953
954	assert (dkp != NULL);
955
956	lifetsec = dkp->lifetime;		/* old lifetime */
957	dkp->lifetime = days * DAYSEC;		/* set new lifetime */
958
959	dbg_val1 ("dki_setlifetime (%d)\n", days);
960	if ( lifetsec == 0 )	/* initial setup (old lifetime was zero)? */
961		dkp->gentime = dkp->time;
962
963	pathname (path, sizeof (path), dkp->dname, dkp->fname, DKI_KEY_FILEEXT);
964	dki_writeinfo (dkp, path);
965
966	return (lifetsec / DAYSEC);
967}
968
969/*****************************************************************
970**	dki_setexptime (dkp, time_t sec)
971**	set the expiration time of the key in seconds since the epoch
972**	return the old exptime
973*****************************************************************/
974time_t	dki_setexptime (dki_t *dkp, time_t sec)
975{
976	char	path[MAX_PATHSIZE+1];
977	time_t	oldexptime;
978
979	assert (dkp != NULL);
980
981	dbg_val1 ("dki_setexptime (%ld)\n", sec);
982	oldexptime = dkp->exptime;
983	dkp->exptime = sec;
984
985	pathname (path, sizeof (path), dkp->dname, dkp->fname, DKI_KEY_FILEEXT);
986	dki_writeinfo (dkp, path);
987
988#if 0	/* not necessary ? */
989	touch (path, time (NULL));
990#endif
991	return (oldexptime);
992}
993
994/*****************************************************************
995**	dki_age ()	return age of key in seconds since 'curr'
996*****************************************************************/
997int	dki_age (const dki_t *dkp, time_t curr)
998{
999	assert (dkp != NULL);
1000	return ((ulong)curr - (ulong)dkp->time);
1001}
1002
1003/*****************************************************************
1004**	dki_getflag ()	return the flags field of a key
1005*****************************************************************/
1006dk_flag_t	dki_getflag (const dki_t *dkp, time_t curr)
1007{
1008	return dkp->flags;
1009}
1010
1011/*****************************************************************
1012**	dki_setflag ()	set a flag of a key
1013*****************************************************************/
1014dk_flag_t	dki_setflag (dki_t *dkp, dk_flag_t flag)
1015{
1016	return dkp->flags |= (ushort)flag;
1017}
1018
1019/*****************************************************************
1020**	dki_unsetflag ()	unset a flag of a key
1021*****************************************************************/
1022dk_flag_t	dki_unsetflag (dki_t *dkp, dk_flag_t flag)
1023{
1024	return dkp->flags &= ~((ushort)flag);
1025}
1026
1027/*****************************************************************
1028**	dki_isksk ()
1029*****************************************************************/
1030int	dki_isksk (const dki_t *dkp)
1031{
1032	assert (dkp != NULL);
1033	return (dkp->flags & DK_FLAG_KSK) == DK_FLAG_KSK;
1034}
1035
1036/*****************************************************************
1037**	dki_isrevoked ()
1038*****************************************************************/
1039int	dki_isrevoked (const dki_t *dkp)
1040{
1041	assert (dkp != NULL);
1042	return (dkp->flags & DK_FLAG_REVOKE) == DK_FLAG_REVOKE;
1043}
1044
1045/*****************************************************************
1046**	dki_isdepreciated ()
1047*****************************************************************/
1048int	dki_isdepreciated (const dki_t *dkp)
1049{
1050	return dki_status (dkp) == DKI_DEPRECIATED;
1051}
1052
1053/*****************************************************************
1054**	dki_isactive ()
1055*****************************************************************/
1056int	dki_isactive (const dki_t *dkp)
1057{
1058	return dki_status (dkp) == DKI_ACTIVE;
1059}
1060
1061/*****************************************************************
1062**	dki_ispublished ()
1063*****************************************************************/
1064int	dki_ispublished (const dki_t *dkp)
1065{
1066	return dki_status (dkp) == DKI_PUBLISHED;
1067}
1068
1069
1070/*****************************************************************
1071**	dki_status ()	return key status
1072*****************************************************************/
1073dk_status_t	dki_status (const dki_t *dkp)
1074{
1075	assert (dkp != NULL);
1076	return (dkp->status);
1077}
1078
1079/*****************************************************************
1080**	dki_statusstr ()	return key status as string
1081*****************************************************************/
1082const	char	*dki_statusstr (const dki_t *dkp)
1083{
1084	assert (dkp != NULL);
1085	switch ( dkp->status )
1086	{
1087	case DKI_ACT:	return "active";
1088	case DKI_PUB:   if ( dki_isksk (dkp) )
1089				return "standby";
1090			else
1091				return "published";
1092	case DKI_DEP:   return "depreciated";
1093	case DKI_REV:   return "revoked";
1094	case DKI_SEP:   return "sep";
1095	}
1096	return "unknown";
1097}
1098
1099/*****************************************************************
1100**	dki_add ()	add a key to the given list
1101*****************************************************************/
1102dki_t	*dki_add (dki_t **list, dki_t *new)
1103{
1104	dki_t	*curr;
1105	dki_t	*last;
1106
1107	if ( list == NULL )
1108		return NULL;
1109	if ( new == NULL )
1110		return *list;
1111
1112	last = curr = *list;
1113	while ( curr && dki_cmp (curr, new) < 0 )
1114	{
1115		last = curr;
1116		curr = curr->next;
1117	}
1118
1119	if ( curr == *list )	/* add node at start of list */
1120		*list = new;
1121	else			/* add node at end or between two nodes */
1122		last->next = new;
1123	new->next = curr;
1124
1125	return *list;
1126}
1127
1128/*****************************************************************
1129**	dki_search ()	search a key with the given tag, or the first
1130**			occurence of a key with the given name
1131*****************************************************************/
1132const dki_t	*dki_search (const dki_t *list, int tag, const char *name)
1133{
1134	const dki_t	*curr;
1135
1136	curr = list;
1137	if ( tag )
1138		while ( curr && (tag != curr->tag ||
1139				(name && *name && strcmp (name, curr->name) != 0)) )
1140			curr = curr->next;
1141	else if ( name && *name )
1142		while ( curr && strcmp (name, curr->name) != 0 )
1143			curr = curr->next;
1144	else
1145		curr = NULL;
1146
1147	return curr;
1148}
1149
1150#if defined(USE_TREE) && USE_TREE
1151/*****************************************************************
1152**	dki_tadd ()	add a key to the given tree
1153*****************************************************************/
1154dki_t	*dki_tadd (dki_t **tree, dki_t *new, int sub_before)
1155{
1156	dki_t	**p;
1157
1158	if ( sub_before )
1159		p = tsearch (new, tree, dki_namecmp);
1160	else
1161		p = tsearch (new, tree, dki_revnamecmp);
1162	if ( *p == new )
1163		dbg_val ("dki_tadd: New entry %s added\n", new->name);
1164	else
1165	{
1166		dbg_val ("dki_tadd: New key added to %s\n", new->name);
1167		dki_add (p, new);
1168	}
1169
1170	return *p;
1171}
1172
1173/*****************************************************************
1174**	dki_tsearch ()	search a key with the given tag, or the first
1175**			occurence of a key with the given name
1176*****************************************************************/
1177const dki_t	*dki_tsearch (const dki_t *tree, int tag, const char *name)
1178{
1179	dki_t	search;
1180	dki_t	**p;
1181
1182	search.tag = tag;
1183	snprintf (search.name, sizeof (search.name), "%s", name);
1184	p = tfind (&search, &tree, dki_namecmp);
1185	if ( p == NULL )
1186		return NULL;
1187
1188	return dki_search (*p, tag, name);
1189}
1190#endif
1191
1192/*****************************************************************
1193**	dki_find ()	find the n'th ksk or zsk key with given status
1194*****************************************************************/
1195const dki_t	*dki_find (const dki_t *list, int ksk, int status, int no)
1196{
1197	const	dki_t	*dkp;
1198	const	dki_t	*last;
1199
1200	last = NULL;
1201	for ( dkp = list; no > 0 && dkp; dkp = dkp->next )
1202		if ( dki_isksk (dkp) == ksk && dki_status (dkp) == status )
1203		{
1204			no--;
1205			last = dkp;
1206		}
1207
1208	return last;
1209}
1210
1211/*****************************************************************
1212**	dki_findalgo ()	find the n'th ksk or zsk key with given
1213**			algorithm and status
1214*****************************************************************/
1215const dki_t	*dki_findalgo (const dki_t *list, int ksk, int alg, int status, int no)
1216{
1217	const	dki_t	*dkp;
1218	const	dki_t	*last;
1219
1220	last = NULL;
1221	for ( dkp = list; no > 0 && dkp; dkp = dkp->next )
1222		if ( dki_isksk (dkp) == ksk && dki_algo (dkp) == alg &&
1223						dki_status (dkp) == status )
1224		{
1225			no--;
1226			last = dkp;
1227		}
1228
1229	return last;
1230}
1231