zkt-signer.c revision e2d635d630f6f61fefd3d4475c45b097b16b8a2a
/*****************************************************************
**
** @(#) zkt-signer.c (c) Jan 2005 - Jan 2010 Holger Zuleger hznet.de
**
** A wrapper around the BIND dnssec-signzone command which is able
** to resign a zone if necessary and doing a zone or key signing key rollover.
**
** Copyright (c) 2005 - 2010, 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 <assert.h>
# include <dirent.h>
# include <errno.h>
# include <unistd.h>
# include <ctype.h>
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
# include "config_zkt.h"
#if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG
# include <getopt.h>
#endif
# include "zconf.h"
# include "debug.h"
# include "misc.h"
# include "ncparse.h"
# include "nscomm.h"
# include "soaserial.h"
# include "zone.h"
# include "dki.h"
# include "rollover.h"
# include "log.h"
# define short_options "c:L:V:D:N:o:O:dfHhnrv"
#else
# define short_options "c:L:V:D:N:o:O:fHhnrv"
#endif
#if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG
static struct option long_options[] = {
#endif
{0, 0, 0, 0}
};
#endif
/** function declaration **/
/** global command line options **/
extern int optopt;
extern int opterr;
extern int optind;
extern char *optarg;
const char *progname;
static int verbose = 0;
static int force = 0;
static int reloadflag = 0;
static int noexec = 0;
static int dynamic_zone = 0; /* dynamic zone ? */
/** macros **/
{
int c;
int errcnt;
#if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG
int opt_index;
#endif
char *p;
const char *defconfname;
progname = ++p;
{
}
else
fatal ("Couldn't load config: Out of memory\n");
opterr = 0;
#if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG
#else
#endif
{
switch ( c )
{
case 'V': /* view name */
fatal ("Out of memory\n");
break;
case 'c': /* load config from file */
fatal ("Out of memory\n");
break;
case 'O': /* load config option from commandline */
fatal ("Out of memory\n");
break;
case 'o':
break;
case 'N':
break;
case 'D':
break;
case 'L': /* error log file|directory */
break;
case 'f':
force++;
break;
case 'H':
case 'h':
break;
case 'd':
# if BIND_VERSION >= 960
# else
# endif
/* dynamic zone requires a name server reload... */
reloadflag = 0; /* ...but "rndc thaw" reloads the zone anyway */
break;
#endif
case 'n':
noexec = 1;
break;
case 'r':
if ( !dynamic_zone ) /* dynamic zones don't need a rndc reload (see "-d" */
reloadflag = 1;
break;
case 'v':
verbose++;
break;
case '?':
"Unknown option \"-%c\".\n", optopt);
else
"Unknown option char \\x%x.\n", optopt);
break;
default:
abort();
}
}
dbg_line();
/* store some of the commandline parameter in the config structure */
if ( lg_open (progname, config->syslogfacility, config->sysloglevel, config->zonedir, logfile, config->loglevel) < -1 )
/* 1.0rc1: If the ttl is 0 or not known because of dynamic zone signing, ... */
/* ... use sig valid time for this */
{
// config = dupconfig (config);
}
if ( origin ) /* option -o ? */
{
int ret;
else
/* anyway, "delete" all (remaining) arguments */
/* complain if nothing could read in */
{
}
}
if ( namedconf ) /* option -N ? */
{
}
if ( dirname ) /* option -D ? */
{
if ( p > dir )
p--;
if ( *p == '/' )
*p = '\0'; /* remove trailing path seperator */
}
/* none of the above: read default directory tree */
#endif
{
}
errcnt = lg_geterrcnt ();
lg_close ();
}
#if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG
#else
#endif
{
fprintf (stderr, "\t\t The file to sign should be given as an argument (default is \"%s.signed\")\n", conf->zonefile);
fprintf (stderr, "\t-n%s\t no execution of external signing command\n", loptstr (", --noexec", "\t"));
// fprintf (stderr, "\t-r%s\t reload zone via <rndc reload zone> (or via the external distribution command)\n", loptstr (", --reload", "\t"));
fprintf (stderr, "\t-r%s\t reload zone via %s\n", loptstr (", --reload", "\t"), conf->dist_cmd ? conf->dist_cmd: "rndc");
fprintf (stderr, "\t-v%s\t be verbose (use twice to be very verbose)\n", loptstr (", --verbose", "\t"));
exit (127);
}
/** fill zonelist with infos coming out of named.conf **/
{
#ifdef DBG
#endif
dbg_line ();
{
{
dbg_line ();
error ("zone \"%s\" in view \"%s\" found in name server config, but no matching view was set on startup\n", zone, view);
lg_mesg (LG_ERROR, "\"%s\" in view \"%s\" found in name server config, but no matching view was set on startup", zone, view);
return 0;
}
dbg_line ();
return 0;
}
}
{
if ( !is_directory (dir) )
return 0;
dbg_line ();
return 0;
{
continue;
if ( !is_directory (path) )
continue;
}
return 1;
}
{
int err;
int newkey;
int newkeysetfile;
int use_unixtime;
{
return 1;
}
{
return 2;
}
/* check for domain based logging */
{
else
}
/* check rfc5011 key signing keys, create new one if necessary */
dbg_msg("parsezonedir check rfc 5011 ksk ");
{
/* check key signing keys, create new one if necessary */
dbg_msg("parsezonedir check ksk ");
}
else
/* check age of zone keys, probably retire (depreciate) or remove old keys */
dbg_msg("parsezonedir check zsk ");
/* check age of "dnskey.db" file against age of keyfiles */
if ( !newkey )
newkeysetfile = 0;
#if defined(ALWAYS_CHECK_KEYSETFILES) && ALWAYS_CHECK_KEYSETFILES /* patch from Shane Wegner 15. June 2009 */
/* check if there is a new keyset- file */
if ( !newkey )
#else
/* if we work in subdir mode, check if there is a new keyset- file */
#endif
/**
** Check if it is time to do a re-sign. This is the case if
** a) the command line flag -f is set, or
** b) new keys are generated, or
** c) we found a new KSK of a delegated domain, or
** d) the "dnskey.db" file is newer than "zone.db"
** e) the "zone.db" is newer than "zone.db.signed" or
** f) "zone.db.signed" is older than the re-sign interval
**/
mesg[0] = '\0';
if ( force )
else if ( newkey )
else if ( newkeysetfile )
else if ( zfile_time > zfilesig_time )
else if ( bind94_dynzone (dynamic_zone) )
if ( *mesg )
else
if ( *mesg )
dbg_line ();
{
lg_zone_end ();
return 0; /* nothing to do */
}
/* let's start signing the zone */
dbg_line ();
/* create new "dnskey.db" file */
{
}
err = 1;
#else
if ( !dynamic_zone ) /* increment serial no in static zone files */
#endif
{
err = 0;
if ( noexec == 0 )
{
{
error ("could not increment serialno of domain %s in file %s: %s!\n",
"zone \"%s\": couldn't increment serialno in file %s: %s",
}
else
}
else
}
/* at last, sign the zone file */
if ( err >= 0 )
{
logflush ();
/* dynamic zones uses incremental signing, so we have to */
/* prepare the old (signed) file as new input file */
if ( dynamic_zone )
{
{
verbmesg (1, zp->conf, "\tDynamic Zone signing: Initial signing request: Add DNSKEYs to zonefile\n");
}
#if 1
{
verbmesg (1, zp->conf, "\tDynamic Zone signing: zone file manually edited: Use it as new input file\n");
}
#endif
verbmesg (1, zp->conf, "\tDynamic Zone signing: copy old signed zone file %s to new input file %s\n",
if ( newkey ) /* if we have new keys, they should be added to the zone file */
{
#if 0
#endif
}
else /* else we can do a simple file copy */
}
timer = start_timer ();
{
}
if ( dynamic_zone )
if ( err >= 0 )
{
tstr = "0s";
}
}
if ( err >= 0 && reloadflag )
{
else
}
lg_zone_end ();
return err;
}
{
{
#if 0
/* announce "new" and active key signing keys */
{
if ( verbose )
logmesg ("\tRegister new KSK with tag %d for domain %s\n",
}
#endif
}
}
/*
* This function is not working with symbolic links to keyset- files,
* because file_mtime() returns the mtime of the underlying file, and *not*
* that of the symlink file.
* This is bad, because the keyset-file will be newly generated by dnssec-signzone
* on every re-signing call.
* Instead, in the case of a hierarchical directory structure, we copy the file
* (and so we change the timestamp) only if it was modified after the last
* generation (checked with cmpfile(), see func sign_zone()).
*/
# define KEYSET_FILE_PFX "keyset-"
{
int newkeysetfile;
return 0;
newkeysetfile = 0;
{
continue;
newkeysetfile = 1;
}
return newkeysetfile;
}
{
if ( reftime == 0 )
return 1;
return 1;
return 0;
}
{
int ksk;
return 0;
ksk = 1;
{
{
ksk = 0;
}
}
return 1;
}
{
const char *gends;
const char *dnskeyksk;
const char *pseudo;
const char *param;
int len;
const char *dir;
const char *domain;
const char *file;
len = 0;
str[0] = '\0';
dbg_line();
#endif
gends = "";
gends = "-C -g ";
#else
gends = "-g ";
#endif
dnskeyksk = "";
if ( conf->sig_dnskeyksk )
dnskeyksk = "-x ";
#endif
pseudo = "";
if ( conf->sig_pseudo )
pseudo = "-p ";
param = "";
nsec3param[0] = '\0';
{
const char *update;
const char *optout;
unsigned int seed;
# else
update = "";
# endif
optout = "-A ";
else
optout = "";
/* static zones can use always a new salt (full zone signing) */
seed = 0L; /* no seed: use mechanism build in gensalt() */
if ( dynamic_zone )
{ /* dynamic zones have to reuse the salt on signing */
/* use gentime timestamp of ZSK for seeding rand generator */
else
}
}
#endif
dbg_line();
rparam[0] = '\0';
dbg_line();
keysetdir[0] = '\0';
dir = ".";
dbg_line();
if ( dynamic_zone )
snprintf (cmd, sizeof (cmd), "cd %s; %s %s %s%s%s%s%s%s-o %s -e +%ld %s -N increment -f %s.dsigned %s K*.private 2>&1",
dir, SIGNCMD, param, nsec3param, dnskeyksk, gends, pseudo, rparam, keysetdir, domain, conf->sigvalidity, str, file, file);
else
#endif
dir, SIGNCMD, param, nsec3param, dnskeyksk, gends, pseudo, rparam, keysetdir, domain, conf->sigvalidity, str, file);
*str = '\0';
if ( noexec == 0 )
{
#if 0
return -1;
#else
return -1;
str[0] = '\0';
;
#endif
}
dbg_line();
return -1;
return 0;
}
{
char fromfile[1024];
char tofile[1024];
int ret;
/* propagate "keyset"-file to parent dir */
{
/* check if special parent-file exist (ksk rollover) */
/* verbmesg (2, conf, "\t check \"%s\" against parent dir\n", fromfile); */
{
{
error ("Couldn't copy \"%s\" to parent dir (%d:%s)\n",
}
}
}
}