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 *****************************************************************/
66 static	char	dki_estr[255+1];
67 
dki_alloc()68 static	dki_t	*dki_alloc ()
69 {
70 	dki_estr[0] = '\0';
71 	dki_t	*dkp;
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 
dki_readfile(FILE * fp,dki_t * dkp)84 static	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 
dki_writeinfo(const dki_t * dkp,const char * path)161 static	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 
171 	dbg_val1 ("dki_writeinfo %s\n", path);
172 	if ( dki_prt_dnskey_raw (dkp, fp) == 0 )
173 	{
174 		fclose (fp);
175 		return 0;
176 	}
177 
178 	fclose (fp);
179 	touch (path, dkp->time);	/* restore time of key file */
180 
181 	return 1;
182 }
183 
184 static	int	dki_setstat (dki_t *dkp, int status, int preserve_time);
185 
186 /*****************************************************************
187 **	public function definition
188 *****************************************************************/
189 
190 /*****************************************************************
191 **	dki_free ()
192 *****************************************************************/
dki_free(dki_t * dkp)193 void	dki_free (dki_t *dkp)
194 {
195 	assert (dkp != NULL);
196 
197 	if ( dkp->pubkey )
198 		free (dkp->pubkey);
199 	free (dkp);
200 }
201 
202 /*****************************************************************
203 **	dki_freelist ()
204 *****************************************************************/
dki_freelist(dki_t ** listp)205 void	dki_freelist (dki_t **listp)
206 {
207 	dki_t	*curr;
208 	dki_t	*next;
209 
210 	assert (listp != NULL);
211 
212 	curr = *listp;
213 	while ( curr )
214 	{
215 		next = curr->next;
216 		dki_free (curr);
217 		curr = next;
218 	}
219 	if ( *listp )
220 		*listp = NULL;
221 }
222 
223 #if defined(USE_TREE) && USE_TREE
224 /*****************************************************************
225 **	dki_tfree ()
226 *****************************************************************/
dki_tfree(dki_t ** tree)227 void	dki_tfree (dki_t **tree)
228 {
229 	assert (tree != NULL);
230 	// TODO: tdestroy is a GNU extension
231 	// tdestroy (*tree, dki_free);
232 }
233 #endif
234 
235 # define	KEYGEN_COMPMODE	"-C -q "	/* this is the compability mode needed since BIND 9.7 */
236 /*****************************************************************
237 **	dki_new ()
238 **	create new keyfile
239 **	allocate memory for new dki key and init with keyfile
240 *****************************************************************/
dki_new(const char * dir,const char * name,int ksk,int algo,int bitsize,const char * rfile,int lf_days)241 dki_t	*dki_new (const char *dir, const char *name, int ksk, int algo, int bitsize, const char *rfile, int lf_days)
242 {
243 	char	cmdline[511+1];
244 	char	fname[254+1];
245 	char	randfile[254+1];
246 	FILE	*fp;
247 	int	len;
248 	char	*flag = "";
249 	char    *expflag = "";
250 	dki_t	*new;
251 
252 	if ( ksk )
253 		flag = "-f KSK";
254 
255 	randfile[0] = '\0';
256 	if ( rfile && *rfile )
257 		snprintf (randfile, sizeof (randfile), "-r %.250s ", rfile);
258 
259 #if defined(BIND_VERSION) && BIND_VERSION < 90902
260 	if ( algo == DK_ALGO_RSA || algo == DK_ALGO_RSASHA1 || algo == DK_ALGO_RSASHA256 || algo == DK_ALGO_RSASHA512 )
261 		expflag = "-e ";
262 #endif
263 	if ( dir && *dir )
264 		snprintf (cmdline, sizeof (cmdline), "cd %s ; %s %s%s%s-n ZONE -a %s -b %d %s %s 2>&1",
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 2>&1",
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 )
273 		return NULL;
274 
275 	fname[0] = '\0';
276 	fgets (fname, sizeof fname, fp);
277 
278 	pclose (fp);
279 
280 	len = strlen (fname) - 1;
281 	if ( len >= 0 && fname[len] == '\n' )
282 		fname[len] = '\0';
283 
284 	new = dki_read (dir, fname);
285 	if ( new )
286 		dki_setlifetime (new, lf_days);	/* sets gentime + proposed lifetime */
287 
288 	return new;
289 }
290 
291 /*****************************************************************
292 **	dki_read ()
293 **	read key from file 'filename' (independed of the extension)
294 *****************************************************************/
dki_read(const char * dirname,const char * filename)295 dki_t	*dki_read (const char *dirname, const char *filename)
296 {
297 	dki_t	*dkp;
298 	FILE	*fp;
299 	struct	stat	st;
300 	int	len;
301 	int	err;
302 	char	fname[MAX_FNAMESIZE+1];
303 	char	path[MAX_PATHSIZE+1];
304 
305 	dki_estr[0] = '\0';
306 	if ( (dkp = dki_alloc ()) == NULL )
307 		return (NULL);
308 
309 	len = sizeof (fname) - 1;
310 	fname[len] = '\0';
311 	strncpy (fname, filename, len);
312 
313 	len = strlen (fname);			/* delete extension */
314 	if ( len > 4 && strcmp (&fname[len - 4], DKI_KEY_FILEEXT) == 0 )
315 		fname[len - 4] = '\0';
316 	else if ( len > 10 && strcmp (&fname[len - 10], DKI_PUB_FILEEXT) == 0 )
317 		fname[len - 10] = '\0';
318 	else if ( len > 8 && strcmp (&fname[len - 8], DKI_ACT_FILEEXT) == 0 )
319 		fname[len - 8] = '\0';
320 	else if ( len > 12 && strcmp (&fname[len - 12], DKI_DEP_FILEEXT) == 0 )
321 		fname[len - 12] = '\0';
322 	dbg_line ();
323 
324 	assert (strlen (dirname)+1 < sizeof (dkp->dname));
325 	strcpy (dkp->dname, dirname);
326 
327 	assert (strlen (fname)+1 < sizeof (dkp->fname));
328 	strcpy (dkp->fname, fname);
329 	dbg_line ();
330 	if ( sscanf (fname, "K%254[^+]+%hd+%d", dkp->name, &dkp->algo, &dkp->tag) != 3 )
331 	{
332 		snprintf (dki_estr, sizeof (dki_estr),
333 			"dki_read: Filename don't match expected format (%s)", fname);
334 		dki_free (dkp);
335 		return (NULL);
336 	}
337 
338 	pathname (path, sizeof (path), dkp->dname, dkp->fname, DKI_KEY_FILEEXT);
339 	dbg_val ("dki_read: path \"%s\"\n", path);
340 	if ( (fp = fopen (path, "r")) == NULL )
341 	{
342 		snprintf (dki_estr, sizeof (dki_estr),
343 			"dki_read: Can\'t open file \"%s\" for reading", path);
344 		dki_free (dkp);
345 		return (NULL);
346 	}
347 
348 	dbg_line ();
349 	if ( (err = dki_readfile (fp, dkp)) != 0 )
350 	{
351 		dbg_line ();
352 		snprintf (dki_estr, sizeof (dki_estr),
353 			"dki_read: Can\'t read key from file %s (errno %d)", path, err);
354 		fclose (fp);
355 		dki_free (dkp);
356 		return (NULL);
357 	}
358 
359 	dbg_line ();
360 	if ( fstat (fileno(fp), &st) )
361 	{
362 		snprintf (dki_estr, sizeof (dki_estr),
363 			"dki_read: Can\'t stat file %s", fname);
364 		fclose (fp);
365 		dki_free (dkp);
366 		return (NULL);
367 	}
368 	dkp->time = st.st_mtime;
369 
370 	dbg_line ();
371 	pathname (path, sizeof (path), dkp->dname, dkp->fname, DKI_ACT_FILEEXT);
372 	if ( fileexist (path) )
373 	{
374 		if ( dki_isrevoked (dkp) )
375 			dkp->status = DKI_REV;
376 		else
377 			dkp->status = DKI_ACT;
378 	}
379 	else
380 	{
381 		pathname (path, sizeof (path), dkp->dname, dkp->fname, DKI_PUB_FILEEXT);
382 		if ( fileexist (path) )
383 			dkp->status = DKI_PUB;
384 		else
385 		{
386 			pathname (path, sizeof (path), dkp->dname, dkp->fname, DKI_DEP_FILEEXT);
387 			if ( fileexist (path) )
388 				dkp->status = DKI_DEP;
389 			else
390 				dkp->status = DKI_SEP;
391 		}
392 	}
393 
394 	dbg_line ();
395 	fclose (fp);
396 
397 	dbg_line ();
398 	return dkp;
399 }
400 
401 /*****************************************************************
402 **	dki_readdir ()
403 **	read key files from directory 'dir' and, if recursive is
404 **	true, from all directorys below that.
405 *****************************************************************/
dki_readdir(const char * dir,dki_t ** listp,int recursive)406 int	dki_readdir (const char *dir, dki_t **listp, int recursive)
407 {
408 	dki_t	*dkp;
409 	DIR	*dirp;
410 	struct  dirent  *dentp;
411 	char	path[MAX_PATHSIZE+1];
412 
413 	dbg_val ("directory: opendir(%s)\n", dir);
414 	if ( (dirp = opendir (dir)) == NULL )
415 		return 0;
416 
417 	while ( (dentp = readdir (dirp)) != NULL )
418 	{
419 		if ( is_dotfilename (dentp->d_name) )
420 			continue;
421 
422 		dbg_val ("directory: check %s\n", dentp->d_name);
423 		pathname (path, sizeof (path), dir, dentp->d_name, NULL);
424 		if ( is_directory (path) && recursive )
425 		{
426 			dbg_val ("directory: recursive %s\n", path);
427 			dki_readdir (path, listp, recursive);
428 		}
429 		else if ( is_keyfilename (dentp->d_name) )
430 			if ( (dkp = dki_read (dir, dentp->d_name)) )
431 				dki_add (listp, dkp);
432 	}
433 	closedir (dirp);
434 	return 1;
435 }
436 
437 /*****************************************************************
438 **	dki_setstatus_preservetime ()
439 **	set status of key and change extension to
440 **	".published", ".private" or ".depreciated"
441 *****************************************************************/
dki_setstatus_preservetime(dki_t * dkp,int status)442 int	dki_setstatus_preservetime (dki_t *dkp, int status)
443 {
444 	return dki_setstat (dkp, status, 1);
445 }
446 
447 /*****************************************************************
448 **	dki_setstatus ()
449 **	set status of key and change extension to
450 **	".published", ".private" or ".depreciated"
451 *****************************************************************/
dki_setstatus(dki_t * dkp,int status)452 int	dki_setstatus (dki_t *dkp, int status)
453 {
454 	return dki_setstat (dkp, status, 0);
455 }
456 
457 /*****************************************************************
458 **	dki_setstat ()
459 **	low level function of dki_setstatus and dki_setstatus_preservetime
460 *****************************************************************/
dki_setstat(dki_t * dkp,int status,int preserve_time)461 static	int	dki_setstat (dki_t *dkp, int status, int preserve_time)
462 {
463 	char	frompath[MAX_PATHSIZE+1];
464 	char	topath[MAX_PATHSIZE+1];
465 	time_t	totime;
466 
467 	if ( dkp == NULL )
468 		return 0;
469 
470 	status = tolower (status);
471 	switch ( dkp->status )	/* look at old status */
472 	{
473 	case 'r':
474 		if ( status == 'r' )
475 			return 1;
476 		break;
477 	case 'a':
478 		if ( status == 'a' )
479 			return 1;
480 		pathname (frompath, sizeof (frompath), dkp->dname, dkp->fname, DKI_ACT_FILEEXT);
481 		break;
482 	case 'd':
483 		if ( status == 'd' )
484 			return 1;
485 		pathname (frompath, sizeof (frompath), dkp->dname, dkp->fname, DKI_DEP_FILEEXT);
486 		break;
487 	case 'p':	/* or 's' */
488 		if ( status == 'p' || status == 's' )
489 			return 1;
490 		pathname (frompath, sizeof (frompath), dkp->dname, dkp->fname, DKI_PUB_FILEEXT);
491 		break;
492 	default:
493 		/* TODO: set error code */
494 		return 0;
495 	}
496 
497 	dbg_val ("dki_setstat: \"%s\"\n", frompath);
498 	dbg_val ("dki_setstat: to status \"%c\"\n", status);
499 
500 	/* a state change could result in different things: */
501 	/* 1) write a new keyfile when the REVOKE bit is set or unset */
502 	if ( status == 'r' || (status == 'a' && dki_isrevoked (dkp)) )
503 	{
504 		pathname (topath, sizeof (topath), dkp->dname, dkp->fname, DKI_KEY_FILEEXT);
505 
506 		if ( status == 'r' )
507 			dki_setflag (dkp, DK_FLAG_REVOKE);	/* set REVOKE bit */
508 		else
509 			dki_unsetflag (dkp, DK_FLAG_REVOKE);	/* clear REVOKE bit */
510 
511 
512 		dki_writeinfo (dkp, topath);	/* ..and write it to the key file */
513 
514 		if ( !preserve_time )
515 			touch (topath, time (NULL));
516 
517 		return 0;
518 	}
519 
520 
521 	/* 2) change the filename of the private key in all other cases */
522 	totime = 0L;
523 	if ( preserve_time )
524 		totime = file_mtime (frompath);    /* get original timestamp */
525 	topath[0] = '\0';
526 	switch ( status )
527 	{
528 	case 'a':
529 		pathname (topath, sizeof (topath), dkp->dname, dkp->fname, DKI_ACT_FILEEXT);
530 		break;
531 	case 'd':
532 		pathname (topath, sizeof (topath), dkp->dname, dkp->fname, DKI_DEP_FILEEXT);
533 		break;
534 	case 's':		/* standby means a "published KSK" */
535 		if ( !dki_isksk (dkp) )
536 			return 2;
537 		status = 'p';
538 		/* fall through */
539 	case 'p':
540 		pathname (topath, sizeof (topath), dkp->dname, dkp->fname, DKI_PUB_FILEEXT);
541 		break;
542 	}
543 
544 	if ( topath[0] )
545 	{
546 		dbg_val ("dki_setstat: to  \"%s\"\n", topath);
547 		if ( link (frompath, topath) == 0 )
548 			unlink (frompath);
549 		dkp->status = status;
550 		if ( !totime )
551 			totime = time (NULL);	/* set .key file to current time */
552 		pathname (topath, sizeof (topath), dkp->dname, dkp->fname, DKI_KEY_FILEEXT);
553 		touch (topath, totime);	/* store/restore time of status change */
554 	}
555 
556 	return 0;
557 }
558 
559 /*****************************************************************
560 **	dki_remove ()
561 **	rename files associated with key, so that the keys are not
562 **	recognized by the zkt tools e.g.
563 **	Kdo.ma.in.+001+12345.key ==> kdo.ma.in.+001+12345.key
564 **	(second one starts with a lower case 'k')
565 *****************************************************************/
dki_remove(dki_t * dkp)566 dki_t	*dki_remove (dki_t *dkp)
567 {
568 	char	path[MAX_PATHSIZE+1];
569 	char	newpath[MAX_PATHSIZE+1];
570 	char	newfile[MAX_FNAMESIZE+1];
571 	dki_t	*next;
572 	const	char	**pext;
573 	static	const	char	*ext[] = {
574 		DKI_KEY_FILEEXT, DKI_PUB_FILEEXT,
575 		DKI_ACT_FILEEXT, DKI_DEP_FILEEXT,
576 		NULL
577 	};
578 
579 	if ( dkp == NULL )
580 		return NULL;
581 
582 	strncpy (newfile, dkp->fname, sizeof (newfile));
583 	*newfile = tolower (*newfile);
584 	for ( pext = ext; *pext; pext++ )
585 	{
586 		pathname (path, sizeof (path), dkp->dname, dkp->fname, *pext);
587 		if ( fileexist (path) )
588 		{
589 			pathname (newpath, sizeof (newpath), dkp->dname, newfile, *pext);
590 
591 			dbg_val2 ("dki_remove: %s ==> %s \n", path, newpath);
592 			rename (path, newpath);
593 		}
594 	}
595 	next = dkp->next;
596 	dki_free (dkp);
597 
598 	return next;
599 }
600 
601 /*****************************************************************
602 **	dki_destroy ()
603 **	delete files associated with key and free allocated memory
604 *****************************************************************/
dki_destroy(dki_t * dkp)605 dki_t	*dki_destroy (dki_t *dkp)
606 {
607 	char	path[MAX_PATHSIZE+1];
608 	dki_t	*next;
609 	const	char	**pext;
610 	static	const	char	*ext[] = {
611 		DKI_KEY_FILEEXT, DKI_PUB_FILEEXT,
612 		DKI_ACT_FILEEXT, DKI_DEP_FILEEXT,
613 		NULL
614 	};
615 
616 	if ( dkp == NULL )
617 		return NULL;
618 
619 	for ( pext = ext; *pext; pext++ )
620 	{
621 		pathname (path, sizeof (path), dkp->dname, dkp->fname, *pext);
622 		if ( fileexist (path) )
623 		{
624 			dbg_val ("dki_remove: %s \n", path);
625 			unlink (path);
626 		}
627 	}
628 	next = dkp->next;
629 	dki_free (dkp);
630 
631 	return next;
632 }
633 
634 /*****************************************************************
635 **	dki_algo2str ()
636 **	return a string describing the key algorithm
637 *****************************************************************/
dki_algo2str(int algo)638 char	*dki_algo2str (int algo)
639 {
640 	switch ( algo )
641 	{
642 	case DK_ALGO_RSA:		return ("RSAMD5");
643 	case DK_ALGO_DH:		return ("DH");
644 	case DK_ALGO_DSA:		return ("DSA");
645 	case DK_ALGO_EC:		return ("EC");
646 	case DK_ALGO_RSASHA1:		return ("RSASHA1");
647 	case DK_ALGO_NSEC3DSA:		return ("NSEC3DSA");
648 	case DK_ALGO_NSEC3RSASHA1:	return ("NSEC3RSASHA1");
649 	case DK_ALGO_RSASHA256:		return ("RSASHA256");
650 	case DK_ALGO_RSASHA512:		return ("RSASHA512");
651 	case DK_ALGO_ECDSAP256SHA256:	return ("ECDSAP256SHA256");
652 	case DK_ALGO_ECDSAP384SHA384:	return ("ECDSAP384SHA384");
653 	}
654 	return ("unknown");
655 }
656 
657 /*****************************************************************
658 **	dki_algo2sstr ()
659 **	return a short string describing the key algorithm
660 *****************************************************************/
dki_algo2sstr(int algo)661 char	*dki_algo2sstr (int algo)
662 {
663 	switch ( algo )
664 	{
665 	case DK_ALGO_RSA:		return ("RSAMD5");
666 	case DK_ALGO_DH:		return ("DH");
667 	case DK_ALGO_DSA:		return ("DSA");
668 	case DK_ALGO_EC:		return ("EC");
669 	case DK_ALGO_RSASHA1:		return ("RSASHA1");
670 	case DK_ALGO_NSEC3DSA:		return ("N3DSA");
671 	case DK_ALGO_NSEC3RSASHA1:	return ("N3RSA1");
672 	case DK_ALGO_RSASHA256:		return ("RSASHA2");
673 	case DK_ALGO_RSASHA512:		return ("RSASHA5");
674 	case DK_ALGO_ECDSAP256SHA256:	return ("P256");
675 	case DK_ALGO_ECDSAP384SHA384:	return ("P384");
676 	}
677 	return ("unknown");
678 }
679 
680 /*****************************************************************
681 **	dki_geterrstr ()
682 **	return error string
683 *****************************************************************/
dki_geterrstr()684 const	char	*dki_geterrstr ()
685 {
686 	return dki_estr;
687 }
688 
689 /*****************************************************************
690 **	dki_prt_dnskey ()
691 *****************************************************************/
dki_prt_dnskey(const dki_t * dkp,FILE * fp)692 int	dki_prt_dnskey (const dki_t *dkp, FILE *fp)
693 {
694 	return dki_prt_dnskeyttl (dkp, fp, 0);
695 }
696 
697 /*****************************************************************
698 **	dki_prt_dnskeyttl ()
699 *****************************************************************/
dki_prt_dnskeyttl(const dki_t * dkp,FILE * fp,int ttl)700 int	dki_prt_dnskeyttl (const dki_t *dkp, FILE *fp, int ttl)
701 {
702 	char	*p;
703 
704 	if ( dkp == NULL )
705 		return 0;
706 
707 	fprintf (fp, "%s ", dkp->name);
708 	if ( ttl > 0 )
709 		fprintf (fp, "%d ", ttl);
710 	fprintf (fp, "IN DNSKEY  ");
711 	fprintf (fp, "%d 3 %d (", dkp->flags, dkp->algo);
712 	fprintf (fp, "\n\t\t\t");
713 	for ( p = dkp->pubkey; *p ; p++ )
714 		if ( *p == ' ' )
715 			fprintf (fp, "\n\t\t\t");
716 		else
717 			putc (*p, fp);
718 	fprintf (fp, "\n\t\t");
719 	if ( dki_isrevoked (dkp) )
720 		fprintf (fp, ") ; key id = %u (original key id = %u)", (dkp->tag + 128) % 65535, dkp->tag);
721 	else
722 		fprintf (fp, ") ; key id = %u", dkp->tag);
723 	fprintf (fp, "\n");
724 
725 	return 1;
726 }
727 
728 /*****************************************************************
729 **	dki_prt_dnskey_raw ()
730 *****************************************************************/
dki_prt_dnskey_raw(const dki_t * dkp,FILE * fp)731 int	dki_prt_dnskey_raw (const dki_t *dkp, FILE *fp)
732 {
733 	int	days;
734 
735 	if ( dkp == NULL )
736 		return 0;
737 
738 	if ( dkp->gentime  )
739 		fprintf (fp, ";%%\tgenerationtime=%s\n", time2isostr (dkp->gentime, 's'));
740 	if ( (days = dki_lifetimedays (dkp)) )
741 		fprintf (fp, ";%%\tlifetime=%dd\n", days);
742 	if ( dkp->exptime  )
743 		fprintf (fp, ";%%\texpirationtime=%s\n", time2isostr (dkp->exptime, 's'));
744 
745 	fprintf (fp, "%s ", dkp->name);
746 #if 0
747 	if ( ttl > 0 )
748 		fprintf (fp, "%d ", ttl);
749 #endif
750 	fprintf (fp, "IN DNSKEY  ");
751 	fprintf (fp, "%d 3 %d ", dkp->flags, dkp->algo);
752 	fprintf (fp, "%s\n", dkp->pubkey);
753 
754 	return 1;
755 }
756 
757 /*****************************************************************
758 **	dki_prt_comment ()
759 *****************************************************************/
dki_prt_comment(const dki_t * dkp,FILE * fp)760 int	dki_prt_comment (const dki_t *dkp, FILE *fp)
761 {
762 	int	len = 0;
763 
764 	if ( dkp == NULL )
765 		return len;
766 	len += fprintf (fp, "; %s  ", dkp->name);
767 	len += fprintf (fp, "tag=%u  ", dkp->tag);
768 	len += fprintf (fp, "algo=%s  ", dki_algo2str(dkp->algo));
769 	len += fprintf (fp, "generated %s\n", time2str (dkp->time, 's'));
770 
771 	return len;
772 }
773 
774 /*****************************************************************
775 **	dki_prt_trustedkey ()
776 *****************************************************************/
dki_prt_trustedkey(const dki_t * dkp,FILE * fp)777 int	dki_prt_trustedkey (const dki_t *dkp, FILE *fp)
778 {
779 	char	*p;
780 	int	spaces;
781 	int	len = 0;
782 
783 	if ( dkp == NULL )
784 		return len;
785 	len += fprintf (fp, "\"%s\"  ", dkp->name);
786 	spaces = 22 - (strlen (dkp->name) + 3);
787 	len += fprintf (fp, "%*s", spaces > 0 ? spaces : 0 , " ");
788 	len += fprintf (fp, "%d 3 %d ", dkp->flags, dkp->algo);
789 	if ( spaces < 0 )
790 		len += fprintf (fp, "\n\t\t\t%7s", " ");
791 	len += fprintf (fp, "\"");
792 	for ( p = dkp->pubkey; *p ; p++ )
793 		if ( *p == ' ' )
794 			len += fprintf (fp, "\n\t\t\t\t");
795 		else
796 			putc (*p, fp), len += 1;
797 
798 	if ( dki_isrevoked (dkp) )
799 		len += fprintf (fp, "\" ; # key id = %u (original key id = %u)\n\n", (dkp->tag + 128) % 65535, dkp->tag);
800 	else
801 		len += fprintf (fp, "\" ; # key id = %u\n\n", dkp->tag);
802 	return len;
803 }
804 
805 /*****************************************************************
806 **	dki_prt_managedkey ()
807 *****************************************************************/
dki_prt_managedkey(const dki_t * dkp,FILE * fp)808 int	dki_prt_managedkey (const dki_t *dkp, FILE *fp)
809 {
810 	char	*p;
811 	int	spaces;
812 	int	len = 0;
813 
814 	if ( dkp == NULL )
815 		return len;
816 	len += fprintf (fp, "\"%s\"  ", dkp->name);
817 	spaces = 22 - (strlen (dkp->name) + 3);
818 	len += fprintf (fp, "initial-key  ");
819 	spaces -= 13;
820 	len += fprintf (fp, "%*s", spaces > 0 ? spaces : 0 , " ");
821 	len += fprintf (fp, "%d 3 %d ", dkp->flags, dkp->algo);
822 	if ( spaces < 0 )
823 		len += fprintf (fp, "\n\t\t\t%7s", " ");
824 	len += fprintf (fp, "\"");
825 	for ( p = dkp->pubkey; *p ; p++ )
826 		if ( *p == ' ' )
827 			len += fprintf (fp, "\n\t\t\t\t");
828 		else
829 			putc (*p, fp), len += 1;
830 
831 	if ( dki_isrevoked (dkp) )
832 		len += fprintf (fp, "\" ; # key id = %u (original key id = %u)\n\n", (dkp->tag + 128) % 65535, dkp->tag);
833 	else
834 		len += fprintf (fp, "\" ; # key id = %u\n\n", dkp->tag);
835 	return len;
836 }
837 
838 
839 /*****************************************************************
840 **	dki_cmp () 	return <0 | 0 | >0
841 *****************************************************************/
dki_cmp(const dki_t * a,const dki_t * b)842 int	dki_cmp (const dki_t *a, const dki_t *b)
843 {
844 	int	res;
845 
846 	if ( a == NULL ) return -1;
847 	if ( b == NULL ) return 1;
848 
849 	/* sort by domain name, */
850 	if ( (res = domaincmp (a->name, b->name)) != 0 )
851 		return res;
852 
853 	/* then by key type, */
854 	if ( (res = dki_isksk (b) - dki_isksk (a)) != 0 )
855 		return res;
856 
857 	/* and last by creation time,  */
858 	return (ulong)a->time - (ulong)b->time;
859 }
860 
861 #if defined(USE_TREE) && USE_TREE
862 /*****************************************************************
863 **	dki_allcmp () 	return <0 | 0 | >0
864 *****************************************************************/
dki_allcmp(const dki_t * a,const dki_t * b)865 int	dki_allcmp (const dki_t *a, const dki_t *b)
866 {
867 	int	res;
868 
869 	if ( a == NULL ) return -1;
870 	if ( b == NULL ) return 1;
871 
872 // fprintf (stderr, "dki_allcmp %s, %s)\n", a->name, b->name);
873 	/* sort by domain name, */
874 	if ( (res = domaincmp (a->name, b->name)) != 0 )
875 		return res;
876 
877 	/* then by key type, */
878 	if ( (res = dki_isksk (b) - dki_isksk (a)) != 0 )
879 		return res;
880 
881 	/* creation time,  */
882 	if ( (res = (ulong)a->time - (ulong)b->time) != 0 )
883 		return res;
884 
885 	/* and last by tag */
886 	return a->tag - b->tag;
887 }
888 
889 /*****************************************************************
890 **	dki_namecmp () 	return <0 | 0 | >0
891 *****************************************************************/
dki_namecmp(const dki_t * a,const dki_t * b)892 int	dki_namecmp (const dki_t *a, const dki_t *b)
893 {
894 	if ( a == NULL ) return -1;
895 	if ( b == NULL ) return 1;
896 
897 	return domaincmp (a->name, b->name);
898 }
899 
900 /*****************************************************************
901 **	dki_revnamecmp () 	return <0 | 0 | >0
902 *****************************************************************/
dki_revnamecmp(const dki_t * a,const dki_t * b)903 int	dki_revnamecmp (const dki_t *a, const dki_t *b)
904 {
905 	if ( a == NULL ) return -1;
906 	if ( b == NULL ) return 1;
907 
908 	return domaincmp_dir (a->name, b->name, 0);
909 }
910 
911 /*****************************************************************
912 **	dki_tagcmp () 	return <0 | 0 | >0
913 *****************************************************************/
dki_tagcmp(const dki_t * a,const dki_t * b)914 int	dki_tagcmp (const dki_t *a, const dki_t *b)
915 {
916 	if ( a == NULL ) return -1;
917 	if ( b == NULL ) return 1;
918 
919 	return a->tag - b->tag;
920 }
921 #endif
922 
923 /*****************************************************************
924 **	dki_timecmp ()
925 *****************************************************************/
dki_timecmp(const dki_t * a,const dki_t * b)926 int	dki_timecmp (const dki_t *a, const dki_t *b)
927 {
928 	if ( a == NULL ) return -1;
929 	if ( b == NULL ) return 1;
930 
931 	return ((ulong)a->time - (ulong)b->time);
932 }
933 
934 /*****************************************************************
935 **	dki_algo ()	return the algorithm of the key
936 *****************************************************************/
dki_algo(const dki_t * dkp)937 time_t	dki_algo (const dki_t *dkp)
938 {
939 	assert (dkp != NULL);
940 	return (dkp->algo);
941 }
942 
943 /*****************************************************************
944 **	dki_time ()	return the timestamp of the key
945 *****************************************************************/
dki_time(const dki_t * dkp)946 time_t	dki_time (const dki_t *dkp)
947 {
948 	assert (dkp != NULL);
949 	return (dkp->time);
950 }
951 
952 /*****************************************************************
953 **	dki_exptime ()	return the expiration timestamp of the key
954 *****************************************************************/
dki_exptime(const dki_t * dkp)955 time_t	dki_exptime (const dki_t *dkp)
956 {
957 	assert (dkp != NULL);
958 	return (dkp->exptime);
959 }
960 
961 /*****************************************************************
962 **	dki_lifetime (dkp)	return the lifetime of the key in sec!
963 *****************************************************************/
dki_lifetime(const dki_t * dkp)964 time_t	dki_lifetime (const dki_t *dkp)
965 {
966 	assert (dkp != NULL);
967 	return (dkp->lifetime);
968 }
969 
970 /*****************************************************************
971 **	dki_lifetimedays (dkp)	return the lifetime of the key in days!
972 *****************************************************************/
dki_lifetimedays(const dki_t * dkp)973 ushort	dki_lifetimedays (const dki_t *dkp)
974 {
975 	assert (dkp != NULL);
976 	return (dkp->lifetime / DAYSEC);
977 }
978 
979 /*****************************************************************
980 **	dki_gentime (dkp)	return the generation timestamp of the key
981 *****************************************************************/
dki_gentime(const dki_t * dkp)982 time_t	dki_gentime (const dki_t *dkp)
983 {
984 	assert (dkp != NULL);
985 	return (dkp->gentime > 0L ? dkp->gentime: dkp->time);
986 }
987 
988 /*****************************************************************
989 **	dki_setlifetime (dkp, int days)
990 **	set the lifetime in days (and also the gentime if not set)
991 **	return the old lifetime of the key in days!
992 *****************************************************************/
dki_setlifetime(dki_t * dkp,int days)993 ushort	dki_setlifetime (dki_t *dkp, int days)
994 {
995 	ulong	lifetsec;
996 	char	path[MAX_PATHSIZE+1];
997 
998 	assert (dkp != NULL);
999 
1000 	lifetsec = dkp->lifetime;		/* old lifetime */
1001 	dkp->lifetime = days * DAYSEC;		/* set new lifetime */
1002 
1003 	dbg_val1 ("dki_setlifetime (%d)\n", days);
1004 	if ( lifetsec == 0 )	/* initial setup (old lifetime was zero)? */
1005 		dkp->gentime = dkp->time;
1006 
1007 	pathname (path, sizeof (path), dkp->dname, dkp->fname, DKI_KEY_FILEEXT);
1008 	dki_writeinfo (dkp, path);
1009 
1010 	return (lifetsec / DAYSEC);
1011 }
1012 
1013 /*****************************************************************
1014 **	dki_setexptime (dkp, time_t sec)
1015 **	set the expiration time of the key in seconds since the epoch
1016 **	return the old exptime
1017 *****************************************************************/
dki_setexptime(dki_t * dkp,time_t sec)1018 time_t	dki_setexptime (dki_t *dkp, time_t sec)
1019 {
1020 	char	path[MAX_PATHSIZE+1];
1021 	time_t	oldexptime;
1022 
1023 	assert (dkp != NULL);
1024 
1025 	dbg_val1 ("dki_setexptime (%ld)\n", sec);
1026 	oldexptime = dkp->exptime;
1027 	dkp->exptime = sec;
1028 
1029 	pathname (path, sizeof (path), dkp->dname, dkp->fname, DKI_KEY_FILEEXT);
1030 	dki_writeinfo (dkp, path);
1031 
1032 #if 0	/* not necessary ? */
1033 	touch (path, time (NULL));
1034 #endif
1035 	return (oldexptime);
1036 }
1037 
1038 /*****************************************************************
1039 **	dki_age ()	return age of key in seconds since 'curr'
1040 *****************************************************************/
dki_age(const dki_t * dkp,time_t curr)1041 int	dki_age (const dki_t *dkp, time_t curr)
1042 {
1043 	assert (dkp != NULL);
1044 	return ((ulong)curr - (ulong)dkp->time);
1045 }
1046 
1047 /*****************************************************************
1048 **	dki_getflag ()	return the flags field of a key
1049 *****************************************************************/
dki_getflag(const dki_t * dkp,time_t curr)1050 dk_flag_t	dki_getflag (const dki_t *dkp, time_t curr)
1051 {
1052 	return dkp->flags;
1053 }
1054 
1055 /*****************************************************************
1056 **	dki_setflag ()	set a flag of a key
1057 *****************************************************************/
dki_setflag(dki_t * dkp,dk_flag_t flag)1058 dk_flag_t	dki_setflag (dki_t *dkp, dk_flag_t flag)
1059 {
1060 	return dkp->flags |= (ushort)flag;
1061 }
1062 
1063 /*****************************************************************
1064 **	dki_unsetflag ()	unset a flag of a key
1065 *****************************************************************/
dki_unsetflag(dki_t * dkp,dk_flag_t flag)1066 dk_flag_t	dki_unsetflag (dki_t *dkp, dk_flag_t flag)
1067 {
1068 	return dkp->flags &= ~((ushort)flag);
1069 }
1070 
1071 /*****************************************************************
1072 **	dki_isksk ()
1073 *****************************************************************/
dki_isksk(const dki_t * dkp)1074 int	dki_isksk (const dki_t *dkp)
1075 {
1076 	assert (dkp != NULL);
1077 	return (dkp->flags & DK_FLAG_KSK) == DK_FLAG_KSK;
1078 }
1079 
1080 /*****************************************************************
1081 **	dki_isrevoked ()
1082 *****************************************************************/
dki_isrevoked(const dki_t * dkp)1083 int	dki_isrevoked (const dki_t *dkp)
1084 {
1085 	assert (dkp != NULL);
1086 	return (dkp->flags & DK_FLAG_REVOKE) == DK_FLAG_REVOKE;
1087 }
1088 
1089 /*****************************************************************
1090 **	dki_isdepreciated ()
1091 *****************************************************************/
dki_isdepreciated(const dki_t * dkp)1092 int	dki_isdepreciated (const dki_t *dkp)
1093 {
1094 	return dki_status (dkp) == DKI_DEPRECIATED;
1095 }
1096 
1097 /*****************************************************************
1098 **	dki_isactive ()
1099 *****************************************************************/
dki_isactive(const dki_t * dkp)1100 int	dki_isactive (const dki_t *dkp)
1101 {
1102 	return dki_status (dkp) == DKI_ACTIVE;
1103 }
1104 
1105 /*****************************************************************
1106 **	dki_ispublished ()
1107 *****************************************************************/
dki_ispublished(const dki_t * dkp)1108 int	dki_ispublished (const dki_t *dkp)
1109 {
1110 	return dki_status (dkp) == DKI_PUBLISHED;
1111 }
1112 
1113 
1114 /*****************************************************************
1115 **	dki_status ()	return key status
1116 *****************************************************************/
dki_status(const dki_t * dkp)1117 dk_status_t	dki_status (const dki_t *dkp)
1118 {
1119 	assert (dkp != NULL);
1120 	return (dkp->status);
1121 }
1122 
1123 /*****************************************************************
1124 **	dki_statusstr ()	return key status as string
1125 *****************************************************************/
dki_statusstr(const dki_t * dkp)1126 const	char	*dki_statusstr (const dki_t *dkp)
1127 {
1128 	assert (dkp != NULL);
1129 	switch ( dkp->status )
1130 	{
1131 	case DKI_ACT:	return "active";
1132 	case DKI_PUB:   if ( dki_isksk (dkp) )
1133 				return "standby";
1134 			else
1135 				return "published";
1136 	case DKI_DEP:   return "depreciated";
1137 	case DKI_REV:   return "revoked";
1138 	case DKI_SEP:   return "sep";
1139 	}
1140 	return "unknown";
1141 }
1142 
1143 /*****************************************************************
1144 **	dki_add ()	add a key to the given list
1145 *****************************************************************/
dki_add(dki_t ** list,dki_t * new)1146 dki_t	*dki_add (dki_t **list, dki_t *new)
1147 {
1148 	dki_t	*curr;
1149 	dki_t	*last;
1150 
1151 	if ( list == NULL )
1152 		return NULL;
1153 	if ( new == NULL )
1154 		return *list;
1155 
1156 	last = curr = *list;
1157 	while ( curr && dki_cmp (curr, new) < 0 )
1158 	{
1159 		last = curr;
1160 		curr = curr->next;
1161 	}
1162 
1163 	if ( curr == *list )	/* add node at start of list */
1164 		*list = new;
1165 	else			/* add node at end or between two nodes */
1166 		last->next = new;
1167 	new->next = curr;
1168 
1169 	return *list;
1170 }
1171 
1172 /*****************************************************************
1173 **	dki_search ()	search a key with the given tag, or the first
1174 **			occurence of a key with the given name
1175 *****************************************************************/
dki_search(const dki_t * list,int tag,const char * name)1176 const dki_t	*dki_search (const dki_t *list, int tag, const char *name)
1177 {
1178 	const dki_t	*curr;
1179 
1180 	curr = list;
1181 	if ( tag )
1182 		while ( curr && (tag != curr->tag ||
1183 				(name && *name && strcmp (name, curr->name) != 0)) )
1184 			curr = curr->next;
1185 	else if ( name && *name )
1186 		while ( curr && strcmp (name, curr->name) != 0 )
1187 			curr = curr->next;
1188 	else
1189 		curr = NULL;
1190 
1191 	return curr;
1192 }
1193 
1194 #if defined(USE_TREE) && USE_TREE
1195 /*****************************************************************
1196 **	dki_tadd ()	add a key to the given tree
1197 *****************************************************************/
dki_tadd(dki_t ** tree,dki_t * new,int sub_before)1198 dki_t	*dki_tadd (dki_t **tree, dki_t *new, int sub_before)
1199 {
1200 	dki_t	**p;
1201 
1202 	if ( sub_before )
1203 		p = tsearch (new, tree, dki_namecmp);
1204 	else
1205 		p = tsearch (new, tree, dki_revnamecmp);
1206 	if ( *p == new )
1207 		dbg_val ("dki_tadd: New entry %s added\n", new->name);
1208 	else
1209 	{
1210 		dbg_val ("dki_tadd: New key added to %s\n", new->name);
1211 		dki_add (p, new);
1212 	}
1213 
1214 	return *p;
1215 }
1216 
1217 /*****************************************************************
1218 **	dki_tsearch ()	search a key with the given tag, or the first
1219 **			occurence of a key with the given name
1220 *****************************************************************/
dki_tsearch(const dki_t * tree,int tag,const char * name)1221 const dki_t	*dki_tsearch (const dki_t *tree, int tag, const char *name)
1222 {
1223 	dki_t	search;
1224 	dki_t	**p;
1225 
1226 	search.tag = tag;
1227 	snprintf (search.name, sizeof (search.name), "%s", name);
1228 	p = tfind (&search, &tree, dki_namecmp);
1229 	if ( p == NULL )
1230 		return NULL;
1231 
1232 	return dki_search (*p, tag, name);
1233 }
1234 #endif
1235 
1236 /*****************************************************************
1237 **	dki_find ()	find the n'th ksk or zsk key with given status
1238 *****************************************************************/
dki_find(const dki_t * list,int ksk,int status,int no)1239 const dki_t	*dki_find (const dki_t *list, int ksk, int status, int no)
1240 {
1241 	const	dki_t	*dkp;
1242 	const	dki_t	*last;
1243 
1244 	last = NULL;
1245 	for ( dkp = list; no > 0 && dkp; dkp = dkp->next )
1246 		if ( dki_isksk (dkp) == ksk && dki_status (dkp) == status )
1247 		{
1248 			no--;
1249 			last = dkp;
1250 		}
1251 
1252 	return last;
1253 }
1254 
1255 /*****************************************************************
1256 **	dki_findalgo ()	find the n'th ksk or zsk key with given
1257 **			algorithm and status
1258 *****************************************************************/
dki_findalgo(const dki_t * list,int ksk,int alg,int status,int no)1259 const dki_t	*dki_findalgo (const dki_t *list, int ksk, int alg, int status, int no)
1260 {
1261 	const	dki_t	*dkp;
1262 	const	dki_t	*last;
1263 
1264 	last = NULL;
1265 	for ( dkp = list; no > 0 && dkp; dkp = dkp->next )
1266 		if ( dki_isksk (dkp) == ksk && dki_algo (dkp) == alg &&
1267 						dki_status (dkp) == status )
1268 		{
1269 			no--;
1270 			last = dkp;
1271 		}
1272 
1273 	return last;
1274 }
1275