1 /*	$NetBSD: rollover.c,v 1.1.1.1 2015/07/08 15:37:48 christos Exp $	*/
2 
3 /*****************************************************************
4 **
5 **	@(#) rollover.c -- The key rollover functions
6 **
7 **	Copyright (c) Jan 2005 - May 2008, 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 # include <stdio.h>
40 # include <string.h>
41 # include <stdlib.h>
42 # include <ctype.h>
43 # include <time.h>
44 # include <assert.h>
45 # include <dirent.h>
46 # include <errno.h>
47 # include <unistd.h>
48 #ifdef HAVE_CONFIG_H
49 # include <config.h>
50 #endif
51 # include "config_zkt.h"
52 # include "zconf.h"
53 # include "debug.h"
54 
55 # include "misc.h"
56 # include "zone.h"
57 # include "dki.h"
58 # include "log.h"
59 #define extern
60 # include "rollover.h"
61 #undef extern
62 
63 /*****************************************************************
64 **	local function definition
65 *****************************************************************/
66 
67 static	dki_t	*genkey (int addkey, dki_t **listp, const char *dir, const char *domain, int ksk, const zconf_t *conf, int status);
68 
69 /*	generate the first (or primary) key (algorithm k_algo) */
70 static	dki_t	*genfirstkey (dki_t **listp, const char *dir, const char *domain, int ksk, const zconf_t *conf, int status)
71 {
72 	return genkey (0, listp, dir, domain, ksk, conf, status);
73 }
74 
75 /*	generate the additional (or second) key (algorithm k2_algo) */
76 static	dki_t	*genaddkey (dki_t **listp, const char *dir, const char *domain, int ksk, const zconf_t *conf, int status)
77 {
78 	return genkey (1, listp, dir, domain, ksk, conf, status);
79 }
80 
81 
82 /*	generate a DNSKEY key  */
83 static	dki_t	*genkey (int addkey, dki_t **listp, const char *dir, const char *domain, int ksk, const zconf_t *conf, int status)
84 {
85 	dki_t	*dkp;
86 	int	confalgo;
87 	int	algo;
88 
89 #if 0
90 	if ( listp == NULL || domain == NULL )
91 		return NULL;
92 #else
93 	assert ( listp != NULL );
94 	assert ( domain != NULL );
95 #endif
96 
97 	if ( addkey )	/* generating an additional key ? */
98 		confalgo = conf->k2_algo;
99 	else
100 		confalgo = conf->k_algo;
101 
102 	algo = confalgo;
103 	if ( conf->nsec3 != NSEC3_OFF )		/* is nsec3 turned on ? */
104 	{
105 		if ( confalgo == DK_ALGO_RSASHA1 )
106 			algo = DK_ALGO_NSEC3RSASHA1;
107 		else if ( confalgo == DK_ALGO_DSA )
108 			algo = DK_ALGO_NSEC3DSA;
109 	}
110 
111 	if ( ksk )
112 		dkp = dki_new (dir, domain, DKI_KSK, algo, conf->k_bits, conf->k_random, conf->k_life / DAYSEC);
113 	else
114 		dkp = dki_new (dir, domain, DKI_ZSK, algo, conf->z_bits, conf->z_random, conf->z_life / DAYSEC);
115 	dki_add (listp, dkp);
116 	dki_setstatus (dkp, status);
117 
118 	return dkp;
119 }
120 
121 /* get expiration time */
122 static	time_t	get_exptime (dki_t *key, const zconf_t *z)
123 {
124 	time_t	exptime;
125 
126 	exptime = dki_exptime (key);
127 	if ( exptime == 0L )
128 	{
129 		if ( dki_lifetime (key) )
130 			exptime = dki_time (key) + dki_lifetime (key);
131 		else
132 			exptime = dki_time (key) + z->k_life;
133 	}
134 
135 	return exptime;
136 }
137 
138 /*****************************************************************
139 **	is_parentdirsigned (name)
140 **	Check if the parent directory of the zone specified by zp
141 **	is a directory with a signed zone
142 **	Returns 0 | 1
143 *****************************************************************/
144 static	int	is_parentdirsigned (const zone_t *zonelist, const zone_t *zp)
145 {
146 	char	path[MAX_PATHSIZE+1];
147 	const	char	*ext;
148 #if 0
149 	const	zconf_t	*conf;
150 
151 	/* check if there is a local config file to get the name of the zone file */
152 	snprintf (path, sizeof (path), "%s/../%s", zp->dir, LOCALCONF_FILE);
153 	if ( fileexist (path) )		/* parent dir has local config file ? */
154 		conf = loadconfig (path, NULL);
155 	else
156 		conf = zp->conf;
157 
158 	/* build the path of the .signed zone file */
159 	snprintf (path, sizeof (path), "%s/../%s.signed", conf->dir, conf->zonefile);
160 	if ( conf != zp->conf )	/* if we read in a local config file.. */
161 		free (conf);	/* ..free the memory used */
162 
163 #else
164 	/* currently we use the signed zone file name of the
165 	 * current directory for checking if the file exist.
166 	 * TODO: Instead we have to use the name of the zone file
167 	 * used in the parent dir (see above)
168 	 */
169 
170 	ext = strrchr (zp->sfile, '.');
171 	if ( ext && strcmp (zp->sfile, ".dsigned") == 0 )	/* is the current zone a dynamic one ? */
172 		/* hack: we are using the standard zone file name for a static zone here */
173 		snprintf (path, sizeof (path), "%s/../%s", zp->dir, "zone.db.signed");
174 	else
175 	{
176 # if 1
177 		const	zone_t	*parent;
178 		const	char	*parentname;
179 
180 		/* find out name of parent */
181 		parentname = strchr (zp->zone, '.');	/* find first dot in zone name */
182 		if ( parentname == NULL )	/* no parent found! */
183 			return 0;
184 		parentname += 1;	/* skip '.' */
185 
186 		/* try to find parent zone in zonelist */
187 		if ( (parent = zone_search (zonelist, parentname)) == NULL )
188 			return 0;
189 		snprintf (path, sizeof (path), "%s/%s", parent->dir, parent->sfile);
190 # else
191 		snprintf (path, sizeof (path), "%s/../%s", zp->dir, zp->sfile);
192 # endif
193 	}
194 #endif
195 lg_mesg (LG_DEBUG, "%s: is_parentdirsigned = %d fileexist (%s)\n", zp->zone, fileexist (path), path);
196 	return fileexist (path);	/* parent dir has zone.db.signed file ? */
197 }
198 
199 /*****************************************************************
200 **	create_parent_file ()
201 *****************************************************************/
202 static	int	create_parent_file (const char *fname, int phase, int ttl, const dki_t *dkp)
203 {
204 	FILE	*fp;
205 
206 	assert ( fname != NULL );
207 
208 	if ( dkp == NULL || (phase != 1 && phase != 2) )
209 		return 0;
210 
211 	if ( (fp = fopen (fname, "w")) == NULL )
212 		fatal ("can\'t create new parentfile \"%s\"\n", fname);
213 
214 	if ( phase == 1 )
215 		fprintf (fp, "; KSK rollover phase1 (new key generated but this is alread the old one)\n");
216 	else
217 		fprintf (fp, "; KSK rollover phase2 (this is the new key)\n");
218 
219 	dki_prt_dnskeyttl (dkp, fp, ttl);
220 	fclose (fp);
221 
222 	return phase;
223 }
224 
225 /*****************************************************************
226 **	get_parent_phase ()
227 *****************************************************************/
228 static	int	get_parent_phase (const char *file)
229 {
230 	FILE	*fp;
231 	int	phase;
232 
233 	if ( (fp = fopen (file, "r")) == NULL )
234 		return -1;
235 
236 	phase = 0;
237 	if ( fscanf (fp, "; KSK rollover phase%d", &phase) != 1 )
238 		phase = 0;
239 
240 	fclose (fp);
241 	return phase;
242 }
243 
244 /*****************************************************************
245 **	kskrollover ()
246 *****************************************************************/
247 static	int	kskrollover (dki_t *ksk, zone_t *zonelist, zone_t *zp)
248 {
249 	char	path[MAX_PATHSIZE+1];
250 	const	zconf_t	*z;
251 	time_t	lifetime;
252 	time_t	currtime;
253 	time_t	age;
254 	int	currphase;
255 	int	parfile_age;
256 	int	parent_propagation;
257 	int	parent_resign;
258 	int	parent_keyttl;
259 
260 
261 	assert ( ksk != NULL );
262 	assert ( zp != NULL );
263 
264 	z = zp->conf;
265 	/* check ksk lifetime */
266 	if ( (lifetime = dki_lifetime (ksk)) == 0 )	/* if lifetime of key is not set.. */
267 		lifetime = z->k_life;			/* ..use global configured lifetime */
268 
269 	currtime = time (NULL);
270 	age = dki_age (ksk, currtime);
271 
272 	/* build path of parent-file */
273 	pathname (path, sizeof (path), zp->dir, "parent-", zp->zone);
274 
275 	/* check if we have to change the ksk ? */
276 	if ( lifetime > 0 && age > lifetime && !fileexist (path) )	/* lifetime is over and no kskrollover in progress */
277 	{
278 		/* we are in hierachical mode and the parent directory contains a signed zone ? */
279 		if ( z->keysetdir && strcmp (z->keysetdir, "..") == 0 && is_parentdirsigned (zonelist, zp) )
280 		{
281 			verbmesg (2, z, "\t\tkskrollover: create new key signing key\n");
282 			/* create a new key: this is phase one of a double signing key rollover */
283 			ksk = genfirstkey (&zp->keys, zp->dir, zp->zone, DKI_KSK, z, DKI_ACTIVE);
284 			if ( ksk == NULL )
285 			{
286 				lg_mesg (LG_ERROR, "\"%s\": unable to generate new ksk for double signing rollover", zp->zone);
287 				return 0;
288 			}
289 			lg_mesg (LG_INFO, "\"%s\": kskrollover phase1: New key %d generated", zp->zone, ksk->tag);
290 
291 			/* find the oldest active ksk to create the parent file */
292 			if ( (ksk = (dki_t *)dki_findalgo (zp->keys, DKI_KSK, zp->conf->k_algo, 'a', 1)) == NULL )
293 				lg_mesg (LG_ERROR, "kskrollover phase1: Couldn't find the old active key\n");
294 			if ( !create_parent_file (path, 1, z->key_ttl, ksk) )
295 				lg_mesg (LG_ERROR, "Couldn't create parentfile %s\n", path);
296 
297 		}
298 		else	/* print out a warning only */
299 		{
300 			logmesg ("\t\tWarning: Lifetime of Key Signing Key %d exceeded: %s\n",
301 							ksk->tag, str_delspace (age2str (age)));
302 			lg_mesg (LG_WARNING, "\"%s\": lifetime of key signing key %d exceeded since %s",
303 							zp->zone, ksk->tag, str_delspace (age2str (age - lifetime)));
304 		}
305 		return 1;
306 	}
307 
308 	/* now check if there is an ongoing key rollover */
309 
310 	/* check if parent-file already exist */
311 	if ( !fileexist (path) )	/* no parent-<zone> file found ? */
312 		return 0;	/* ok, that's it */
313 
314 	/* check the ksk rollover phase we are in */
315 	currphase = get_parent_phase (path);	/* this is the actual state we are in */
316 	parfile_age = file_age (path);
317 
318 	/* TODO: Set these values to the one found in the parent dnssec.conf file */
319 	parent_propagation = PARENT_PROPAGATION;
320 	parent_resign = z->resign;
321 	parent_keyttl = z->key_ttl;
322 
323 	switch ( currphase )
324 	{
325 	case 1:	/* we are currently in state one (new ksk already generated) */
326 		if ( parfile_age > z->proptime + z->key_ttl )	/* can we go to phase 2 ? */
327 		{
328 			verbmesg (2, z, "\t\tkskrollover: save new ksk in parent file\n");
329 			ksk = ksk->next;    /* set ksk to new ksk */
330 			if ( !create_parent_file (path, currphase+1, z->key_ttl, ksk) )
331 				lg_mesg (LG_ERROR, "Couldn't create parentfile %s\n", path);
332 			lg_mesg (LG_INFO, "\"%s\": kskrollover phase2: send new key %d to the parent zone", zp->zone, ksk->tag);
333 			return 1;
334 		}
335 		else
336 			verbmesg (2, z, "\t\tkskrollover: we are in state 1 and waiting for propagation of the new key (parentfile %dsec < prop %dsec + keyttl %dsec\n", parfile_age, z->proptime, z->key_ttl);
337 		break;
338 	case 2:	/* we are currently in state two (propagation of new key to the parent) */
339 #if 0
340 		if ( parfile_age >= parent_propagation + parent_resign + parent_keyttl )	/* can we go to phase 3 ? */
341 #else
342 		if ( parfile_age >= parent_propagation + parent_keyttl )	/* can we go to phase 3 ? */
343 #endif
344 		{
345 			/* remove the parentfile */
346 			unlink (path);
347 
348 			/* remove oldest key from list and mark file as removed */
349 			zp->keys = dki_remove (ksk);
350 
351 			// verbmesg (2, z, "kskrollover: remove parentfile and rename old key to k<zone>+<algo>+<tag>.key\n");
352 			verbmesg (2, z, "\t\tkskrollover: remove parentfile and rename old key to k%s+%03d+%05d.key\n",
353 									ksk->name, ksk->algo, ksk->tag);
354 			lg_mesg (LG_INFO, "\"%s\": kskrollover phase3: Remove old key %d", zp->zone, ksk->tag);
355 			return 1;
356 		}
357 		else
358 #if 0
359 			verbmesg (2, z, "\t\tkskrollover: we are in state 2 and  waiting for parent propagation (parentfile %d < parentprop %d + parentresig %d + parentkeyttl %d\n", parfile_age, parent_propagation, parent_resign, parent_keyttl);
360 #else
361 			verbmesg (2, z, "\t\tkskrollover: we are in state 2 and waiting for parent propagation (parentfile %dsec < parentprop %dsec + parentkeyttl %dsec\n", parfile_age, parent_propagation, parent_keyttl);
362 #endif
363 		break;
364 	default:
365 		assert ( currphase == 1 || currphase == 2 );
366 		/* NOTREACHED */
367 	}
368 
369 	return 0;
370 }
371 
372 /*****************************************************************
373 **	global function definition
374 *****************************************************************/
375 
376 /*****************************************************************
377 **	ksk5011status ()
378 **	Check if the list of zone keys containing a revoked or a
379 **	standby key.
380 **	Remove the revoked key if it is older than 30 days.
381 **	If the lifetime of the active key is reached, do a rfc5011
382 **	keyrollover.
383 **	Returns an int with the rightmost bit set if a resigning
384 **	is required. The second rightmost bit is set, if it is an
385 **	rfc5011 zone.
386 *****************************************************************/
387 int	ksk5011status (dki_t **listp, const char *dir, const char *domain, const zconf_t *z)
388 {
389 	dki_t	*standbykey;
390 	dki_t	*activekey;
391 	dki_t	*dkp;
392 	dki_t	*prev;
393 	time_t	currtime;
394 	time_t	exptime;
395 	int	ret;
396 
397 	assert ( listp != NULL );
398 	assert ( z != NULL );
399 
400 	if ( z->k_life == 0 )
401 		return 0;
402 
403 	verbmesg (1, z, "\tCheck RFC5011 status\n");
404 
405 	ret = 0;
406 	currtime = time (NULL);
407 
408 	/* go through the list of key signing keys,	*/
409 	/* remove revoked keys and set a pointer to standby and active key */
410 	standbykey = activekey = NULL;
411 	prev = NULL;
412 	for ( dkp = *listp; dkp && dki_isksk (dkp); dkp = dkp->next )
413 	{
414 		exptime = get_exptime (dkp, z);
415 		if ( dki_isrevoked (dkp) )
416 			lg_mesg (LG_DEBUG, "zone \"%s\": found revoked key (id=%d exptime=%s); waiting for remove hold down time",
417 							domain, dkp->tag, time2str (exptime, 's'));
418 
419 		/* revoked key is older than 30 days? */
420 		if ( dki_isrevoked (dkp) && currtime > exptime + REMOVE_HOLD_DOWN )
421 		{
422 			verbmesg (1, z, "\tRemove revoked key %d which is older than 30 days\n", dkp->tag);
423 			lg_mesg (LG_NOTICE, "zone \"%s\": removing revoked key %d", domain, dkp->tag);
424 
425 			/* remove key from list and mark file as removed */
426 			if ( prev == NULL )		/* at the beginning of the list ? */
427 				*listp = dki_remove (dkp);
428 			else				/* anywhere in the middle of the list */
429 				prev->next = dki_remove (dkp);
430 
431 			ret |= 01;		/* from now on a resigning is necessary */
432 		}
433 
434 		/* remember oldest standby and active key */
435 		if ( dki_status (dkp) == DKI_PUBLISHED )
436 			standbykey = dkp;
437 		if ( dki_status (dkp) == DKI_ACTIVE )
438 			activekey = dkp;
439 	}
440 				/* no activekey or no standby key and also no revoked key found ? */
441 	if ( activekey == NULL || (standbykey == NULL && ret == 0) )
442 		return ret;				/* Seems that this is a non rfc5011 zone! */
443 
444 	ret |= 02;		/* Zone looks like a rfc5011 zone */
445 
446 	exptime = get_exptime (activekey, z);
447 #if 0
448 	lg_mesg (LG_DEBUG, "Act Exptime: %s", time2str (exptime, 's'));
449 	lg_mesg (LG_DEBUG, "Stb time: %s", time2str (dki_time (standbykey), 's'));
450 	lg_mesg (LG_DEBUG, "Stb time+wait: %s", time2str (dki_time (standbykey) + min (DAYSEC * 30, z->key_ttl), 's'));
451 #endif
452 	/* At the first time we introduce a standby key, the lifetime of the current KSK shouldn't be expired, */
453 	/* otherwise we run into an (nearly) immediate key rollover!	*/
454 	if ( currtime > exptime && currtime > dki_time (standbykey) + min (ADD_HOLD_DOWN, z->key_ttl) )
455 	{
456 		lg_mesg (LG_NOTICE, "\"%s\": starting rfc5011 rollover", domain);
457 		verbmesg (1, z, "\tLifetime of Key Signing Key %d exceeded (%s): Starting rfc5011 rollover!\n",
458 							activekey->tag, str_delspace (age2str (dki_age (activekey, currtime))));
459 		verbmesg (2, z, "\t\t=>Generating new standby key signing key\n");
460 		dkp = genfirstkey (listp, dir, domain, DKI_KSK, z, DKI_PUBLISHED);	/* gentime == now; lifetime = z->k_life; exp = 0 */
461 		if ( !dkp )
462 		{
463 			error ("\tcould not generate new standby KSK\n");
464 			lg_mesg (LG_ERROR, "\%s\": can't generate new standby KSK", domain);
465 		}
466 		else
467 			lg_mesg (LG_NOTICE, "\"%s\": generated new standby KSK %d", domain, dkp->tag);
468 
469 		/* standby key gets active  */
470 		verbmesg (2, z, "\t\t=>Activating old standby key %d \n", standbykey->tag);
471 		dki_setstatus (standbykey, DKI_ACT);
472 
473 		/* active key should be revoked */
474 		verbmesg (2, z, "\t\t=>Revoking old active key %d \n", activekey->tag);
475 		dki_setstatus (activekey, DKI_REVOKED);
476 		dki_setexptime (activekey, currtime);	/* now the key is expired */
477 
478 		ret |= 01;		/* resigning necessary */
479 	}
480 
481 	return ret;
482 }
483 
484 /*****************************************************************
485 **	kskstatus ()
486 **	Check the ksk status of a zone if a ksk lifetime is set.
487 **	If there is no key signing key present create a new one.
488 **	Prints out a warning message if the lifetime of the current
489 **	key signing key is over.
490 **	Returns 1 if a resigning of the zone is necessary, otherwise
491 **	the function returns 0.
492 *****************************************************************/
493 int	kskstatus (zone_t *zonelist, zone_t *zp)
494 {
495 	dki_t	*akey;
496 	const	zconf_t	*z;
497 
498 	assert ( zp != NULL );
499 
500 	z = zp->conf;
501 	if ( z->k_life == 0 )
502 		return 0;
503 
504 	verbmesg (1, z, "\tCheck KSK status\n");
505 	/* check if a key signing key exist ? */
506 	akey = (dki_t *)dki_findalgo (zp->keys, DKI_KSK, z->k_algo, 'a', 1);
507 	if ( akey == NULL )
508 	{
509 		verbmesg (1, z, "\tNo active KSK found: generate new one\n");
510 		akey = genfirstkey (&zp->keys, zp->dir, zp->zone, DKI_KSK, z, DKI_ACTIVE);
511 		if ( !akey )
512 		{
513 			error ("\tcould not generate new KSK\n");
514 			lg_mesg (LG_ERROR, "\"%s\": can't generate new KSK: \"%s\"",
515 								zp->zone, dki_geterrstr());
516 		}
517 		else
518 			lg_mesg (LG_INFO, "\"%s\": generated new KSK %d", zp->zone, akey->tag);
519 		return akey != NULL;	/* return value of 1 forces a resigning of the zone */
520 	}
521 	else	/* try to start a full automated ksk rollover */
522 		kskrollover (akey, zonelist, zp);
523 
524 	/* is a second algorithm requested ? (since 0.99) */
525 	if ( z->k2_algo && z->k2_algo != z->k_algo )
526 	{
527 		/* check for ksk supporting the additional algorithm */
528 		akey = (dki_t *)dki_findalgo (zp->keys, DKI_KSK, z->k2_algo, 'a', 1);
529 		if ( akey == NULL )
530 		{
531 			verbmesg (1, z, "\tNo active KSK for additional algorithm found: generate new one\n");
532 			akey = genaddkey (&zp->keys, zp->dir, zp->zone, DKI_KSK, z, DKI_ACTIVE);
533 			if ( !akey )
534 			{
535 				error ("\tcould not generate new KSK for additional algorithm\n");
536 				lg_mesg (LG_ERROR, "\"%s\": can't generate new KSK for 2nd algorithm: \"%s\"",
537 									zp->zone, dki_geterrstr());
538 			}
539 			else
540 				lg_mesg (LG_INFO, "\"%s\": generated new KSK %d for additional algorithm",
541 										zp->zone, akey->tag);
542 			return 1;	/* return value of 1 forces a resigning of the zone */
543 		}
544 	}
545 
546 	return 0;
547 }
548 
549 /*****************************************************************
550 **	zskstatus ()
551 **	Check the zsk status of a zone.
552 **	Returns 1 if a resigning of the zone is necessary, otherwise
553 **	the function returns 0.
554 *****************************************************************/
555 int	zskstatus (dki_t **listp, const char *dir, const char *domain, const zconf_t *z)
556 {
557 	dki_t	*akey;
558 	dki_t	*nextkey;
559 	dki_t	*dkp, *last;
560 	int	keychange;
561 	time_t	lifetime;
562 	time_t	age;
563 	time_t	currtime;
564 
565 	assert ( listp != NULL );
566 	/* dir can be NULL */
567 	assert ( domain != NULL );
568 	assert ( z != NULL );
569 
570 	currtime = time (NULL);
571 
572 	verbmesg (1, z, "\tCheck ZSK status\n");
573 	dbg_val("zskstatus for %s \n", domain);
574 	keychange = 0;
575 	/* Is the depreciated key expired ? */
576 	/* As mentioned by olaf, this is the max_ttl of all the rr in the zone */
577 	lifetime = z->max_ttl + z->proptime;	/* draft kolkman/gieben */
578 	last = NULL;
579 	dkp = *listp;
580 	while ( dkp )
581 		if ( !dki_isksk (dkp) &&
582 		     dki_status (dkp) == DKI_DEPRECIATED &&
583 		     dki_age (dkp, currtime) > lifetime )
584 		{
585 			keychange = 1;
586 			verbmesg (1, z, "\tLifetime(%d sec) of depreciated key %d exceeded (%d sec)\n",
587 					 lifetime, dkp->tag, dki_age (dkp, currtime));
588 			lg_mesg (LG_INFO, "\"%s\": old ZSK %d removed", domain, dkp->tag);
589 			dkp = dki_destroy (dkp);	/* delete the keyfiles */
590 			dbg_msg("zskstatus: depreciated key removed ");
591 			if ( last )
592 				last->next = dkp;
593 			else
594 				*listp = dkp;
595 			verbmesg (1, z, "\t\t->remove it\n");
596 		}
597 		else
598 		{
599 			last = dkp;
600 			dkp = dkp->next;
601 		}
602 
603 	/* check status of active key */
604 	dbg_msg("zskstatus check status of active key ");
605 	lifetime = z->z_life;			/* global configured lifetime for zsk */
606 	akey = (dki_t *)dki_findalgo (*listp, DKI_ZSK, z->k_algo, 'a', 1);
607 	if ( akey == NULL && lifetime > 0 )	/* no active key found */
608 	{
609 		verbmesg (1, z, "\tNo active ZSK found: generate new one\n");
610 		akey = genfirstkey (listp, dir, domain, DKI_ZSK, z, DKI_ACTIVE);
611 		if ( !akey )
612 		{
613 			error ("\tcould not generate new ZSK\n");
614 			lg_mesg (LG_ERROR, "\%s\": can't generate new ZSK", domain);
615 		}
616 		else
617 			lg_mesg (LG_INFO, "\"%s\": generated new ZSK %d", domain, akey->tag);
618 	}
619 	else	/* active key exist */
620 	{
621 		if ( dki_lifetime (akey) )
622 			lifetime = dki_lifetime (akey);	/* set lifetime to lt of active key */
623 
624 		/* lifetime of active key is expired and published key exist ? */
625 		age = dki_age (akey, currtime);
626 		if ( lifetime > 0 && age > lifetime - (OFFSET) )
627 		{
628 			verbmesg (1, z, "\tLifetime(%d +/-%d sec) of active key %d exceeded (%d sec)\n",
629 					lifetime, (OFFSET) , akey->tag, dki_age (akey, currtime) );
630 
631 			/* depreciate the key only if there is another active or published key */
632 			if ( (nextkey = (dki_t *)dki_findalgo (*listp, DKI_ZSK, z->k_algo, 'a', 2)) == NULL ||
633 			      nextkey == akey )
634 				nextkey = (dki_t *)dki_findalgo (*listp, DKI_ZSK, z->k_algo, 'p', 1);
635 
636 			/* Is the published key sufficient long in the zone ? */
637 			/* As mentioned by Olaf, this should be the ttl of the DNSKEY RR ! */
638 			if ( nextkey && dki_age (nextkey, currtime) > z->key_ttl + z->proptime )
639 			{
640 				keychange = 1;
641 				verbmesg (1, z, "\t\t->depreciate it\n");
642 				dki_setstatus (akey, 'd');	/* depreciate the active key */
643 				verbmesg (1, z, "\t\t->activate published key %d\n", nextkey->tag);
644 				dki_setstatus (nextkey, 'a');	/* activate published key */
645 				lg_mesg (LG_NOTICE, "\"%s\": lifetime of zone signing key %d exceeded: ZSK rollover done", domain, akey->tag);
646 				akey = nextkey;
647 				nextkey = NULL;
648 				lifetime = dki_lifetime (akey);	/* set lifetime to lt of the new active key (F. Behrens) */
649 			}
650 			else
651 			{
652 				verbmesg (1, z, "\t\t->waiting for published key\n");
653 				lg_mesg (LG_NOTICE, "\"%s\": lifetime of zone signing key %d exceeded since %s: ZSK rollover deferred: waiting for published key",
654 						domain, akey->tag, str_delspace (age2str (age - lifetime)));
655 			}
656 		}
657 	}
658 
659 	/* Should we add a new publish key? */
660 	nextkey = (dki_t *)dki_findalgo (*listp, DKI_ZSK, z->k_algo, 'p', 1);	/* is there a published ZSK? */
661 #if defined(ALLOW_ALWAYS_PREPUBLISH_ZSK) && ALLOW_ALWAYS_PREPUBLISH_ZSK
662 	if ( z->z_always )	/* always add a pre-publish ZSK  (patch from Hrant Dadivanyan) */
663 	{
664 		if ( nextkey == NULL )
665 		{
666 			verbmesg (1, z, "\tNew key for pre-publishing needed\n");
667 			nextkey = genfirstkey (listp, dir, domain, DKI_ZSK, z, DKI_PUB);
668 			if ( nextkey )
669 			{
670 				keychange = 1;
671 				verbmesg (1, z, "\t\t->creating new key %d\n", nextkey->tag);
672 				lg_mesg (LG_INFO, "\"%s\": new key %d generated for pre-publishing", domain, nextkey->tag);
673 			}
674 			else
675 			{
676 				error ("\tcould not generate new ZSK: \"%s\"\n", dki_geterrstr());
677 				lg_mesg (LG_ERROR, "\"%s\": can't generate new ZSK: \"%s\"",
678 									domain, dki_geterrstr());
679 			}
680 		}
681 	}
682 	else	/* do we need a new ZSK ? */
683 #endif
684 	{
685 		/* This is necessary if the active key will be expired at the
686 		 * next re-signing interval (The published time will be checked
687 		 * just before the active key will be removed. See above).
688 		 */
689 		if ( nextkey == NULL && lifetime > 0 && (akey == NULL ||
690 		     dki_age (akey, currtime + z->resign) > lifetime - (OFFSET)) )
691 		{
692 			verbmesg (1, z, "\tNew ZSK for publishing needed\n");
693 			nextkey = genfirstkey (listp, dir, domain, DKI_ZSK, z, DKI_PUB);
694 			if ( nextkey )
695 			{
696 				keychange = 1;
697 				verbmesg (1, z, "\t\t->creating new key %d\n", nextkey->tag);
698 				lg_mesg (LG_INFO, "\"%s\": new zone signing key %d generated for publishing", domain, nextkey->tag);
699 			}
700 			else
701 			{
702 				error ("\tcould not generate new ZSK: \"%s\"\n", dki_geterrstr());
703 				lg_mesg (LG_ERROR, "\"%s\": can't generate new ZSK: \"%s\"",
704 									domain, dki_geterrstr());
705 			}
706 		}
707 	}
708 
709 	/* is a second algorithm requested ? (since 0.99) */
710 	if ( z->k2_algo && z->k2_algo != z->k_algo )
711 	{
712 		/* check for zsk supporting the additional algorithm */
713 		akey = (dki_t *)dki_findalgo (*listp, DKI_ZSK, z->k2_algo, 'a', 1);
714 		if ( akey == NULL )
715 		{
716 			verbmesg (1, z, "\tNo active ZSK for second algorithm found: generate new one\n");
717 			akey = genaddkey (listp, dir, domain, DKI_ZSK, z, DKI_ACTIVE);
718 			if ( !akey )
719 			{
720 				error ("\tcould not generate new ZSK for 2nd algorithm\n");
721 				lg_mesg (LG_ERROR, "\"%s\": can't generate new ZSK for 2nd algorithm: \"%s\"",
722 									domain, dki_geterrstr());
723 			}
724 			else
725 				lg_mesg (LG_INFO, "\"%s\": generated new ZSK %d for 2nd algorithm",
726 										domain, akey->tag);
727 			return 1;	/* return value of 1 forces a resigning of the zone */
728 		}
729 	}
730 
731 	return keychange;
732 }
733 
734