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