/*****************************************************************
**
** @(#) rollover.c -- The key rollover functions
**
** Copyright (c) Jan 2005 - May 2008, Holger Zuleger HZnet. All rights reserved.
**
** This software is open source.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** Redistributions of source code must retain the above copyright notice,
** this list of conditions and the following disclaimer.
**
** Redistributions in binary form must reproduce the above copyright notice,
** this list of conditions and the following disclaimer in the documentation
**
** Neither the name of Holger Zuleger HZnet nor the names of its contributors may
** be used to endorse or promote products derived from this software without
** specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
** POSSIBILITY OF SUCH DAMAGE.
**
*****************************************************************/
# include <stdio.h>
# include <string.h>
# include <stdlib.h>
# include <ctype.h>
# include <time.h>
# include <assert.h>
# include <dirent.h>
# include <errno.h>
# include <unistd.h>
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
# include "config_zkt.h"
# include "zconf.h"
# include "debug.h"
# include "misc.h"
# include "zone.h"
# include "dki.h"
# include "log.h"
#define extern
# include "rollover.h"
#undef extern
/*****************************************************************
** local function definition
*****************************************************************/
static dki_t *genkey (dki_t **listp, const char *dir, const char *domain, int ksk, const zconf_t *conf, int status)
{
return NULL;
if ( ksk )
dkp = dki_new (dir, domain, DKI_KSK, conf->k_algo, conf->k_bits, conf->k_random, conf->k_life / DAYSEC);
else
dkp = dki_new (dir, domain, DKI_ZSK, conf->k_algo, conf->z_bits, conf->z_random, conf->z_life / DAYSEC);
return dkp;
}
static dki_t *genkey2 (dki_t **listp, const char *dir, const char *domain, int ksk, const zconf_t *conf, int status)
{
return NULL;
if ( ksk )
dkp = dki_new (dir, domain, DKI_KSK, conf->k2_algo, conf->k_bits, conf->k_random, conf->k_life / DAYSEC);
else
dkp = dki_new (dir, domain, DKI_ZSK, conf->k2_algo, conf->z_bits, conf->z_random, conf->z_life / DAYSEC);
return dkp;
}
{
if ( exptime == 0L )
{
if ( dki_lifetime (key) )
else
}
return exptime;
}
/*****************************************************************
** is_parentdirsigned (name)
** Check if the parent directory of the zone specified by zp
** is a directory with a signed zone
** Returns 0 | 1
*****************************************************************/
{
const char *ext;
#if 0
/* check if there is a local config file to get the name of the zone file */
else
/* build the path of the .signed zone file */
#else
/* currently we use the signed zone file name of the
* current directory for checking if the file exist.
* TODO: Instead we have to use the name of the zone file
* used in the parent dir (see above)
*/
/* hack: we are using the standard zone file name for a static zone here */
else
{
# if 1
const char *parentname;
/* find out name of parent */
return 0;
/* try to find parent zone in zonelist */
return 0;
# else
# endif
}
#endif
lg_mesg (LG_DEBUG, "%s: is_parentdirsigned = %d fileexist (%s)\n", zp->zone, fileexist (path), path);
}
/*****************************************************************
** create_parent_file ()
*****************************************************************/
{
return 0;
if ( phase == 1 )
else
return phase;
}
/*****************************************************************
** get_parent_phase ()
*****************************************************************/
{
int phase;
return -1;
phase = 0;
phase = 0;
return phase;
}
/*****************************************************************
** kskrollover ()
*****************************************************************/
{
const zconf_t *z;
int currphase;
int parfile_age;
int parent_propagation;
int parent_resign;
int parent_keyttl;
/* check ksk lifetime */
/* build path of parent-file */
/* check if we have to change the ksk ? */
if ( lifetime > 0 && age > lifetime && !fileexist (path) ) /* lifetime is over and no kskrollover in progress */
{
/* we are in hierachical mode and the parent directory contains a signed zone ? */
{
/* create a new key: this is phase one of a double signing key rollover */
{
return 0;
}
/* find the oldest active ksk to create the parent file */
}
else /* print out a warning only */
{
logmesg ("\t\tWarning: Lifetime of Key Signing Key %d exceeded: %s\n",
}
return 1;
}
/* now check if there is an ongoing key rollover */
/* check if parent-file already exist */
return 0; /* ok, that's it */
/* check the ksk rollover phase we are in */
/* TODO: Set these values to the one found in the parent dnssec.conf file */
parent_resign = z->resign;
parent_keyttl = z->key_ttl;
switch ( currphase )
{
case 1: /* we are currently in state one (new ksk already generated) */
{
lg_mesg (LG_INFO, "\"%s\": kskrollover phase2: send new key %d to the parent zone", zp->zone, ksk->tag);
return 1;
}
else
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);
break;
case 2: /* we are currently in state two (propagation of new key to the parent) */
#if 0
if ( parfile_age >= parent_propagation + parent_resign + parent_keyttl ) /* can we go to phase 3 ? */
#else
#endif
{
/* remove the parentfile */
/* remove oldest key from list and mark file as removed */
// verbmesg (2, z, "kskrollover: remove parentfile and rename old key to k<zone>+<algo>+<tag>.key\n");
return 1;
}
else
#if 0
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);
#else
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);
#endif
break;
default:
/* NOTREACHED */
}
return 0;
}
/*****************************************************************
** global function definition
*****************************************************************/
/*****************************************************************
** ksk5011status ()
** Check if the list of zone keys containing a revoked or a
** standby key.
** Remove the revoked key if it is older than 30 days.
** If the lifetime of the active key is reached, do a rfc5011
** keyrollover.
** Returns an int with the rightmost bit set if a resigning
** is required. The second rightmost bit is set, if it is an
** rfc5011 zone.
*****************************************************************/
{
int ret;
if ( z->k_life == 0 )
return 0;
ret = 0;
/* go through the list of key signing keys, */
/* remove revoked keys and set a pointer to standby and active key */
{
if ( dki_isrevoked (dkp) )
lg_mesg (LG_DEBUG, "zone \"%s\": found revoked key (id=%d exptime=%s); waiting for remove hold down time",
/* revoked key is older than 30 days? */
{
/* remove key from list and mark file as removed */
else /* anywhere in the middle of the list */
}
/* remember oldest standby and active key */
standbykey = dkp;
}
/* no activekey or no standby key and also no revoked key found ? */
return ret; /* Seems that this is a non rfc5011 zone! */
#if 0
lg_mesg (LG_DEBUG, "Stb time+wait: %s", time2str (dki_time (standbykey) + min (DAYSEC * 30, z->key_ttl), 's'));
#endif
/* At the first time we introduce a standby key, the lifetime of the current KSK shouldn't be expired, */
/* otherwise we run into an (nearly) immediate key rollover! */
{
dkp = genkey (listp, dir, domain, DKI_KSK, z, DKI_PUBLISHED); /* gentime == now; lifetime = z->k_life; exp = 0 */
if ( !dkp )
{
error ("\tcould not generate new standby KSK\n");
}
else
/* standby key gets active */
/* active key should be revoked */
}
return ret;
}
/*****************************************************************
** kskstatus ()
** Check the ksk status of a zone if a ksk lifetime is set.
** If there is no key signing key present create a new one.
** Prints out a warning message if the lifetime of the current
** key signing key is over.
** Returns 1 if a resigning of the zone is necessary, otherwise
** the function returns 0.
*****************************************************************/
{
const zconf_t *z;
if ( z->k_life == 0 )
return 0;
/* check if a key signing key exist ? */
{
if ( !akey )
{
error ("\tcould not generate new KSK\n");
}
else
}
else /* try to start a full automated ksk rollover */
/* is a second algorithm requested ? (since 0.99) */
{
/* check for ksk supporting the additional algorithm */
{
if ( !akey )
{
error ("\tcould not generate new KSK for additional algorithm\n");
}
else
return 1; /* return value of 1 forces a resigning of the zone */
}
}
return 0;
}
/*****************************************************************
** zskstatus ()
** Check the zsk status of a zone.
** Returns 1 if a resigning of the zone is necessary, otherwise
** the function returns 0.
*****************************************************************/
{
int keychange;
/* dir can be NULL */
keychange = 0;
/* Is the depreciated key expired ? */
/* As mentioned by olaf, this is the max_ttl of all the rr in the zone */
while ( dkp )
{
keychange = 1;
dbg_msg("zskstatus: depreciated key removed ");
if ( last )
else
}
else
{
}
/* check status of active key */
dbg_msg("zskstatus check status of active key ");
{
}
else /* active key exist */
{
if ( dki_lifetime (akey) )
/* lifetime of active key is expired and published key exist ? */
{
/* depreciate the key only if there is another active or published key */
/* Is the published key sufficient long in the zone ? */
/* As mentioned by Olaf, this should be the ttl of the DNSKEY RR ! */
{
keychange = 1;
lg_mesg (LG_NOTICE, "\"%s\": lifetime of zone signing key %d exceeded: ZSK rollover done", domain, akey->tag);
}
else
{
lg_mesg (LG_NOTICE, "\"%s\": lifetime of zone signing key %d exceeded since %s: ZSK rollover deferred: waiting for published key",
}
}
}
/* Should we add a new publish key? This is necessary if the active
* key will be expired at the next re-signing interval (The published
* time will be checked just before the active key will be removed.
* See above).
*/
{
keychange = 1;
if ( nextkey )
{
}
else
{
domain, dki_geterrstr());
}
}
/* is a second algorithm requested ? (since 0.99) */
{
/* check for zsk supporting the additional algorithm */
{
if ( !akey )
{
error ("\tcould not generate new ZSK for 2nd algorithm\n");
domain, dki_geterrstr());
}
else
return 1; /* return value of 1 forces a resigning of the zone */
}
}
return keychange;
}