summaryrefslogtreecommitdiffstats
path: root/contrib/zkt/dnssec-signer.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/zkt/dnssec-signer.c')
-rw-r--r--contrib/zkt/dnssec-signer.c1002
1 files changed, 1002 insertions, 0 deletions
diff --git a/contrib/zkt/dnssec-signer.c b/contrib/zkt/dnssec-signer.c
new file mode 100644
index 0000000..5b2b8f6
--- /dev/null
+++ b/contrib/zkt/dnssec-signer.c
@@ -0,0 +1,1002 @@
+/*****************************************************************
+**
+** @(#) dnssec-signer.c (c) Jan 2005 Holger Zuleger hznet.de
+**
+** A wrapper around the BIND dnssec-signzone command which is able
+** to resign a zone if neccessary and doing a zone or key signing key rollover.
+**
+** Copyright (c) 2005 - 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
+** and/or other materials provided with the distribution.
+**
+** 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 "zone.h"
+# include "dki.h"
+# include "rollover.h"
+# include "log.h"
+
+#if defined(BIND_VERSION) && BIND_VERSION >= 940
+# 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[] = {
+ {"reload", no_argument, NULL, 'r'},
+ {"force", no_argument, NULL, 'f'},
+ {"noexec", no_argument, NULL, 'n'},
+ {"verbose", no_argument, NULL, 'v'},
+ {"directory", no_argument, NULL, 'd'},
+ {"config", required_argument, NULL, 'c'},
+ {"option", required_argument, NULL, 'O'},
+ {"config-option", required_argument, NULL, 'O'},
+ {"logfile", required_argument, NULL, 'L' },
+ {"view", required_argument, NULL, 'V' },
+ {"directory", required_argument, NULL, 'D'},
+ {"named-conf", required_argument, NULL, 'N'},
+ {"origin", required_argument, NULL, 'o'},
+#if defined(BIND_VERSION) && BIND_VERSION >= 940
+ {"dynamic", no_argument, NULL, 'd' },
+#endif
+ {"help", no_argument, NULL, 'h'},
+ {0, 0, 0, 0}
+};
+#endif
+
+
+/** function declaration **/
+static void usage (char *mesg, zconf_t *conf);
+static int add2zonelist (const char *dir, const char *view, const char *zone, const char *file);
+static int parsedir (const char *dir, zone_t **zp, const zconf_t *conf);
+static int dosigning (zone_t *zonelist, zone_t *zp);
+static int check_keydb_timestamp (dki_t *keylist, time_t reftime);
+static int new_keysetfiles (const char *dir, time_t zone_signing_time);
+static int writekeyfile (const char *fname, const dki_t *list, int key_ttl);
+static int sign_zone (const char *dir, const char *domain, const char *file, const zconf_t *conf);
+static int dyn_update_freeze (const char *domain, const zconf_t *z, int freeze);
+static int reload_zone (const char *domain, const zconf_t *z);
+static int dist_and_reload (const zone_t *zp);
+static void register_key (dki_t *listp, const zconf_t *z);
+static void copy_keyset (const char *dir, const char *domain, const zconf_t *conf);
+
+/** global command line options **/
+extern int optopt;
+extern int opterr;
+extern int optind;
+extern char *optarg;
+const char *progname;
+const char *viewname = NULL;
+const char *logfile = NULL;
+const char *origin = NULL;
+const char *namedconf = NULL;
+const char *dirname = NULL;
+static int verbose = 0;
+static int force = 0;
+static int reloadflag = 0;
+static int noexec = 0;
+static int dynamic_zone = 0; /* dynamic zone ? */
+static zone_t *zonelist = NULL; /* must be static global because add2zonelist use it */
+static zconf_t *config;
+
+int main (int argc, char *const argv[])
+{
+ int c;
+ int errcnt;
+ int opt_index;
+ char errstr[255+1];
+ char *p;
+ const char *defconfname;
+ zone_t *zp;
+
+ progname = *argv;
+ if ( (p = strrchr (progname, '/')) )
+ progname = ++p;
+ viewname = getnameappendix (progname, "dnssec-signer");
+
+ defconfname = getdefconfname (viewname);
+ config = loadconfig ("", (zconf_t *)NULL); /* load built in config */
+ if ( fileexist (defconfname) ) /* load default config file */
+ config = loadconfig (defconfname, config);
+ if ( config == NULL )
+ fatal ("Couldn't load config: Out of memory\n");
+
+ zonelist = NULL;
+ opterr = 0;
+#if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG
+ while ( (c = getopt_long (argc, argv, short_options, long_options, &opt_index)) != -1 )
+#else
+ while ( (c = getopt (argc, argv, short_options)) != -1 )
+#endif
+ {
+ switch ( c )
+ {
+ case 'V': /* view name */
+ viewname = optarg;
+ defconfname = getdefconfname (viewname);
+ if ( fileexist (defconfname) ) /* load default config file */
+ config = loadconfig (defconfname, config);
+ if ( config == NULL )
+ fatal ("Out of memory\n");
+ break;
+ case 'c': /* load config from file */
+ config = loadconfig (optarg, config);
+ if ( config == NULL )
+ fatal ("Out of memory\n");
+ break;
+ case 'O': /* load config option from commandline */
+ config = loadconfig_fromstr (optarg, config);
+ if ( config == NULL )
+ fatal ("Out of memory\n");
+ break;
+ case 'o':
+ origin = optarg;
+ break;
+ case 'N':
+ namedconf = optarg;
+ break;
+ case 'D':
+ dirname = optarg;
+ break;
+ case 'L': /* error log file|directory */
+ logfile = optarg;
+ break;
+ case 'f':
+ force++;
+ break;
+ case 'H':
+ case 'h':
+ usage (NULL, config);
+ break;
+#if defined(BIND_VERSION) && BIND_VERSION >= 940
+ case 'd':
+ dynamic_zone = 1;
+ /* 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':
+ reloadflag = 1;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case '?':
+ if ( isprint (optopt) )
+ snprintf (errstr, sizeof(errstr),
+ "Unknown option \"-%c\".\n", optopt);
+ else
+ snprintf (errstr, sizeof (errstr),
+ "Unknown option char \\x%x.\n", optopt);
+ usage (errstr, config);
+ break;
+ default:
+ abort();
+ }
+ }
+ dbg_line();
+
+ /* store some of the commandline parameter in the config structure */
+ setconfigpar (config, "--view", viewname);
+ setconfigpar (config, "-v", &verbose);
+ if ( logfile == NULL )
+ logfile = config->logfile;
+
+ if ( lg_open (progname, config->syslogfacility, config->sysloglevel, config->zonedir, logfile, config->loglevel) < -1 )
+ fatal ("Couldn't open logfile %s in dir %s\n", logfile, config->zonedir);
+
+#if defined(DBG) && DBG
+ for ( zp = zonelist; zp; zp = zp->next )
+ zone_print ("in main: ", zp);
+#endif
+ lg_args (LG_NOTICE, argc, argv);
+
+ if ( origin ) /* option -o ? */
+ {
+ if ( (argc - optind) <= 0 ) /* no arguments left ? */
+ zone_readdir (".", origin, NULL, &zonelist, config, dynamic_zone);
+ else
+ zone_readdir (".", origin, argv[optind], &zonelist, config, dynamic_zone);
+
+ /* anyway, "delete" all (remaining) arguments */
+ optind = argc;
+
+ /* complain if nothing could read in */
+ if ( zonelist == NULL )
+ {
+ lg_mesg (LG_FATAL, "\"%s\": couldn't read", origin);
+ fatal ("Couldn't read zone \"%s\"\n", origin);
+ }
+ }
+ if ( namedconf ) /* option -N ? */
+ {
+ char dir[255+1];
+
+ memset (dir, '\0', sizeof (dir));
+ if ( config->zonedir )
+ strncpy (dir, config->zonedir, sizeof(dir));
+ if ( !parse_namedconf (namedconf, dir, sizeof (dir), add2zonelist) )
+ fatal ("Can't read file %s as namedconf file\n", namedconf);
+ if ( zonelist == NULL )
+ fatal ("No signed zone found in file %s\n", namedconf);
+ }
+ if ( dirname ) /* option -D ? */
+ {
+ if ( !parsedir (dirname, &zonelist, config) )
+ fatal ("Can't read directory tree %s\n", dirname);
+ if ( zonelist == NULL )
+ fatal ("No signed zone found in directory tree %s\n", dirname);
+ }
+
+ /* none of the above: read current directory tree */
+ if ( zonelist == NULL )
+ parsedir (config->zonedir, &zonelist, config);
+
+ for ( zp = zonelist; zp; zp = zp->next )
+ if ( in_strarr (zp->zone, &argv[optind], argc - optind) )
+ {
+ dosigning (zonelist, zp);
+ verbmesg (1, zp->conf, "\n");
+ }
+
+ zone_freelist (&zonelist);
+
+ errcnt = lg_geterrcnt ();
+ lg_mesg (LG_NOTICE, "end of run: %d error%s occured", errcnt, errcnt == 1 ? "" : "s");
+ lg_close ();
+
+ return errcnt < 64 ? errcnt : 64;
+}
+
+# define sopt_usage(mesg, value) fprintf (stderr, mesg, value)
+#if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG
+# define lopt_usage(mesg, value) fprintf (stderr, mesg, value)
+# define loptstr(lstr, sstr) lstr
+#else
+# define lopt_usage(mesg, value)
+# define loptstr(lstr, sstr) sstr
+#endif
+static void usage (char *mesg, zconf_t *conf)
+{
+ fprintf (stderr, "%s version %s\n", progname, ZKT_VERSION);
+ fprintf (stderr, "\n");
+
+ fprintf (stderr, "usage: %s [-c file] [-O optstr] ", progname);
+ fprintf (stderr, "[-D directorytree] ");
+ fprintf (stderr, "[-fhnr] [-v [-v]] [zone ...]\n");
+
+ fprintf (stderr, "usage: %s [-c file] [-O optstr] ", progname);
+ fprintf (stderr, "-N named.conf ");
+ fprintf (stderr, "[-fhnr] [-v [-v]] [zone ...]\n");
+
+ fprintf (stderr, "usage: %s [-c file] [-O optstr] ", progname);
+ fprintf (stderr, "-o origin ");
+ fprintf (stderr, "[-fhnr] [-v [-v]] [zonefile.signed]\n");
+
+ fprintf (stderr, "\t-c file%s", loptstr (", --config=file\n", ""));
+ fprintf (stderr, "\t\t read config from <file> instead of %s\n", CONFIG_FILE);
+ fprintf (stderr, "\t-O optstr%s", loptstr (", --config-option=\"optstr\"\n", ""));
+ fprintf (stderr, "\t\t set config options on the commandline\n");
+ fprintf (stderr, "\t-L file|dir%s", loptstr (", --logfile=file|dir\n", ""));
+ fprintf (stderr, "\t\t specify file or directory for the log output\n");
+ fprintf (stderr, "\t-D dir%s", loptstr (", --directory=dir\n", ""));
+ fprintf (stderr, "\t\t parse the given directory tree for a list of secure zones \n");
+ fprintf (stderr, "\t-N file%s", loptstr (", --named-conf=file\n", ""));
+ fprintf (stderr, "\t\t get the list of secure zones out of the named like config file \n");
+ fprintf (stderr, "\t-o zone%s", loptstr (", --origin=zone", ""));
+ fprintf (stderr, "\tspecify the name of the zone \n");
+ fprintf (stderr, "\t\t The file to sign should be given as an argument (default is \"%s.signed\")\n", conf->zonefile);
+ fprintf (stderr, "\t-h%s\t print this help\n", loptstr (", --help", "\t"));
+ fprintf (stderr, "\t-f%s\t force re-signing\n", loptstr (", --force", "\t"));
+ 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"));
+
+ fprintf (stderr, "\t[zone]\t sign only those zones given as argument\n");
+
+ fprintf (stderr, "\n");
+ fprintf (stderr, "\tif neither -D nor -N nor -o is given, the directory tree specified\n");
+ fprintf (stderr, "\tin the dnssec config file (\"%s\") will be parsed\n", conf->zonedir);
+
+ if ( mesg && *mesg )
+ fprintf (stderr, "%s\n", mesg);
+ exit (127);
+}
+
+/** fill zonelist with infos coming out of named.conf **/
+static int add2zonelist (const char *dir, const char *view, const char *zone, const char *file)
+{
+#ifdef DBG
+ fprintf (stderr, "printzone ");
+ fprintf (stderr, "view \"%s\" " , view);
+ fprintf (stderr, "zone \"%s\" " , zone);
+ fprintf (stderr, "file ");
+ if ( dir && *dir )
+ fprintf (stderr, "%s/", dir);
+ fprintf (stderr, "%s", file);
+ fprintf (stderr, "\n");
+#endif
+ dbg_line ();
+ if ( view[0] != '\0' ) /* view found in named.conf */
+ {
+ if ( viewname == NULL || viewname[0] == '\0' ) /* viewname wasn't set on startup ? */
+ {
+ 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 ();
+ if ( strcmp (viewname, view) != 0 ) /* zone is _not_ in current view */
+ return 0;
+ }
+ return zone_readdir (dir, zone, file, &zonelist, config, dynamic_zone);
+}
+
+static int parsedir (const char *dir, zone_t **zp, const zconf_t *conf)
+{
+ DIR *dirp;
+ struct dirent *dentp;
+ char path[MAX_PATHSIZE+1];
+
+ dbg_val ("parsedir: (%s)\n", dir);
+ if ( !is_directory (dir) )
+ return 0;
+
+ dbg_line ();
+ zone_readdir (dir, NULL, NULL, zp, conf, dynamic_zone);
+
+ dbg_val ("parsedir: opendir(%s)\n", dir);
+ if ( (dirp = opendir (dir)) == NULL )
+ return 0;
+
+ while ( (dentp = readdir (dirp)) != NULL )
+ {
+ if ( is_dotfile (dentp->d_name) )
+ continue;
+
+ pathname (path, sizeof (path), dir, dentp->d_name, NULL);
+ if ( !is_directory (path) )
+ continue;
+
+ dbg_val ("parsedir: recursive %s\n", path);
+ parsedir (path, zp, conf);
+ }
+ closedir (dirp);
+ return 1;
+}
+
+static int dosigning (zone_t *zonelist, zone_t *zp)
+{
+ char path[MAX_PATHSIZE+1];
+ int err;
+ int newkey;
+ int newkeysetfile;
+ int use_unixtime;
+ time_t currtime;
+ time_t zfile_time;
+ time_t zfilesig_time;
+ char mesg[255+1];
+
+ verbmesg (1, zp->conf, "parsing zone \"%s\" in dir \"%s\"\n", zp->zone, zp->dir);
+
+ pathname (path, sizeof (path), zp->dir, zp->sfile, NULL);
+ dbg_val("parsezonedir fileexist (%s)\n", path);
+ if ( !fileexist (path) )
+ {
+ error ("Not a secure zone directory (%s)!\n", zp->dir);
+ lg_mesg (LG_ERROR, "\"%s\": not a secure zone directory (%s)!", zp->zone, zp->dir);
+ return 1;
+ }
+ zfilesig_time = file_mtime (path);
+
+ pathname (path, sizeof (path), zp->dir, zp->file, NULL);
+ dbg_val("parsezonedir fileexist (%s)\n", path);
+ if ( !fileexist (path) )
+ {
+ error ("No zone file found (%s)!\n", path);
+ lg_mesg (LG_ERROR, "\"%s\": no zone file found (%s)!", zp->zone, path);
+ return 2;
+ }
+
+ zfile_time = file_mtime (path);
+ currtime = time (NULL);
+
+ /* check rfc5011 key signing keys, create new one if neccessary */
+ dbg_msg("parsezonedir check rfc 5011 ksk ");
+ newkey = ksk5011status (&zp->keys, zp->dir, zp->zone, zp->conf);
+ if ( (newkey & 02) != 02 ) /* not a rfc 5011 zone ? */
+ {
+ verbmesg (2, zp->conf, "\t\t->not a rfc5011 zone, looking for a regular ksk rollover\n");
+ /* check key signing keys, create new one if neccessary */
+ dbg_msg("parsezonedir check ksk ");
+ newkey |= kskstatus (zonelist, zp);
+ }
+ else
+ newkey &= ~02; /* reset bit 2 */
+
+ /* check age of zone keys, probably retire (depreciate) or remove old keys */
+ dbg_msg("parsezonedir check zsk ");
+ newkey += zskstatus (&zp->keys, zp->dir, zp->zone, zp->conf);
+
+ /* check age of "dnskey.db" file against age of keyfiles */
+ pathname (path, sizeof (path), zp->dir, zp->conf->keyfile, NULL);
+ dbg_val("parsezonedir check_keydb_timestamp (%s)\n", path);
+ if ( !newkey )
+ newkey = check_keydb_timestamp (zp->keys, file_mtime (path));
+
+ /* if we work in subdir mode, check if there is a new keyset- file */
+ newkeysetfile = 0;
+ if ( !newkey && zp->conf->keysetdir && strcmp (zp->conf->keysetdir, "..") == 0 )
+ newkeysetfile = new_keysetfiles (zp->dir, zfilesig_time);
+
+ /**
+ ** 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 )
+ snprintf (mesg, sizeof(mesg), "Option -f");
+ else if ( newkey )
+ snprintf (mesg, sizeof(mesg), "New zone key");
+ else if ( newkeysetfile )
+ snprintf (mesg, sizeof(mesg), "Modified KSK in delegated domain");
+ else if ( file_mtime (path) > zfilesig_time )
+ snprintf (mesg, sizeof(mesg), "Modified keys");
+ else if ( zfile_time > zfilesig_time )
+ snprintf (mesg, sizeof(mesg), "Zone file edited");
+ else if ( (currtime - zfilesig_time) > zp->conf->resign - (OFFSET) )
+ snprintf (mesg, sizeof(mesg), "re-signing interval (%s) reached",
+ str_delspace (age2str (zp->conf->resign)));
+ else if ( dynamic_zone )
+ snprintf (mesg, sizeof(mesg), "dynamic zone");
+
+ if ( *mesg )
+ verbmesg (1, zp->conf, "\tRe-signing necessary: %s\n", mesg);
+ else
+ verbmesg (1, zp->conf, "\tRe-signing not necessary!\n");
+
+ if ( *mesg )
+ lg_mesg (LG_NOTICE, "\"%s\": re-signing triggered: %s", zp->zone, mesg);
+
+ dbg_line ();
+ if ( !(force || newkey || newkeysetfile || zfile_time > zfilesig_time ||
+ file_mtime (path) > zfilesig_time ||
+ (currtime - zfilesig_time) > zp->conf->resign - (OFFSET) || dynamic_zone) )
+ {
+ verbmesg (2, zp->conf, "\tCheck if there is a parent file to copy\n");
+ if ( zp->conf->keysetdir && strcmp (zp->conf->keysetdir, "..") == 0 )
+ copy_keyset (zp->dir, zp->zone, zp->conf); /* copy the parent- file if it exist */
+ return 0; /* nothing to do */
+ }
+
+ /* let's start signing the zone */
+ dbg_line ();
+
+ /* create new "dnskey.db" file */
+ pathname (path, sizeof (path), zp->dir, zp->conf->keyfile, NULL);
+ verbmesg (1, zp->conf, "\tWriting key file \"%s\"\n", path);
+ if ( !writekeyfile (path, zp->keys, zp->conf->key_ttl) )
+ {
+ error ("Can't create keyfile %s \n", path);
+ lg_mesg (LG_ERROR, "\"%s\": can't create keyfile %s", zp->zone , path);
+ }
+
+ err = 1;
+ use_unixtime = ( zp->conf->serialform == Unixtime );
+ dbg_val1 ("Use unixtime = %d\n", use_unixtime);
+#if defined(BIND_VERSION) && BIND_VERSION >= 940
+ if ( !dynamic_zone && !use_unixtime ) /* increment serial no in static zone files */
+#else
+ if ( !dynamic_zone ) /* increment serial no in static zone files */
+#endif
+ {
+ pathname (path, sizeof (path), zp->dir, zp->file, NULL);
+ err = 0;
+ if ( noexec == 0 )
+ {
+ if ( (err = inc_serial (path, use_unixtime)) < 0 )
+ {
+ error ("could not increment serialno of domain %s in file %s: %s!\n",
+ zp->zone, path, inc_errstr (err));
+ lg_mesg (LG_ERROR,
+ "zone \"%s\": couldn't increment serialno in file %s: %s",
+ zp->zone, path, inc_errstr (err));
+ }
+ else
+ verbmesg (1, zp->conf, "\tIncrementing serial number in file \"%s\"\n", path);
+ }
+ else
+ verbmesg (1, zp->conf, "\tIncrementing serial number in file \"%s\"\n", path);
+ }
+
+ /* at last, sign the zone file */
+ if ( err > 0 )
+ {
+ time_t timer;
+
+ verbmesg (1, zp->conf, "\tSigning zone \"%s\"\n", zp->zone);
+ logflush ();
+
+ /* dynamic zones uses incremental signing, so we have to */
+ /* prepare the old (signed) file as new input file */
+ if ( dynamic_zone )
+ {
+ char zfile[MAX_PATHSIZE+1];
+
+ dyn_update_freeze (zp->zone, zp->conf, 1); /* freeze dynamic zone ! */
+
+ pathname (zfile, sizeof (zfile), zp->dir, zp->file, NULL);
+ pathname (path, sizeof (path), zp->dir, zp->sfile, NULL);
+ if ( filesize (path) == 0L ) /* initial signing request */
+ {
+ verbmesg (1, zp->conf, "\tDynamic Zone signing: Initial signing request: Add DNSKEYs to zonefile\n");
+ copyfile (zfile, path, zp->conf->keyfile);
+ }
+ verbmesg (1, zp->conf, "\tDynamic Zone signing: copy old signed zone file %s to new input file %s\n",
+ path, zfile);
+ if ( newkey ) /* if we have new keys, they should be added to the zone file */
+ copyzonefile (path, zfile, zp->conf->keyfile);
+ else /* else we can do a simple file copy */
+ copyfile (path, zfile, NULL);
+ }
+
+ timer = start_timer ();
+ if ( (err = sign_zone (zp->dir, zp->zone, zp->file, zp->conf)) < 0 )
+ {
+ error ("Signing of zone %s failed (%d)!\n", zp->zone, err);
+ lg_mesg (LG_ERROR, "\"%s\": signing failed!", zp->zone);
+ }
+ timer = stop_timer (timer);
+
+ if ( dynamic_zone )
+ dyn_update_freeze (zp->zone, zp->conf, 0); /* thaw dynamic zone file */
+
+ {
+ const char *tstr = str_delspace (age2str (timer));
+
+ if ( !tstr || *tstr == '\0' )
+ tstr = "0s";
+ verbmesg (1, zp->conf, "\tSigning completed after %s.\n", tstr);
+ }
+ }
+
+ copy_keyset (zp->dir, zp->zone, zp->conf);
+
+ if ( err >= 0 && reloadflag )
+ {
+ if ( zp->conf->dist_cmd )
+ dist_and_reload (zp);
+ else
+ reload_zone (zp->zone, zp->conf);
+
+ register_key (zp->keys, zp->conf);
+ }
+
+ return err;
+}
+
+static void register_key (dki_t *list, const zconf_t *z)
+{
+ dki_t *dkp;
+ time_t currtime;
+ time_t age;
+
+ assert ( list != NULL );
+ assert ( z != NULL );
+
+ currtime = time (NULL);
+ for ( dkp = list; dkp && dki_isksk (dkp); dkp = dkp->next )
+ {
+ age = dki_age (dkp, currtime);
+#if 0
+ /* announce "new" and active key signing keys */
+ if ( REG_URL && *REG_URL && dki_status (dkp) == DKI_ACT && age <= z->resign * 4 )
+ {
+ if ( verbose )
+ logmesg ("\tRegister new KSK with tag %d for domain %s\n",
+ dkp->tag, dkp->name);
+ }
+#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-"
+static int new_keysetfiles (const char *dir, time_t zone_signing_time)
+{
+ DIR *dirp;
+ struct dirent *dentp;
+ char path[MAX_PATHSIZE+1];
+ int newkeysetfile;
+
+ if ( (dirp = opendir (dir)) == NULL )
+ return 0;
+
+ newkeysetfile = 0;
+ dbg_val2 ("new_keysetfile (%s, %s)\n", dir, time2str (zone_signing_time, 's'));
+ while ( !newkeysetfile && (dentp = readdir (dirp)) != NULL )
+ {
+ if ( strncmp (dentp->d_name, KEYSET_FILE_PFX, strlen (KEYSET_FILE_PFX)) != 0 )
+ continue;
+
+ pathname (path, sizeof (path), dir, dentp->d_name, NULL);
+ dbg_val2 ("newkeysetfile timestamp of %s = %s\n", path, time2str (file_mtime(path), 's'));
+ if ( file_mtime (path) > zone_signing_time )
+ newkeysetfile = 1;
+ }
+ closedir (dirp);
+
+ return newkeysetfile;
+}
+
+static int check_keydb_timestamp (dki_t *keylist, time_t reftime)
+{
+ dki_t *key;
+
+ assert ( keylist != NULL );
+ if ( reftime == 0 )
+ return 1;
+
+ for ( key = keylist; key; key = key->next )
+ if ( dki_time (key) > reftime )
+ return 1;
+
+ return 0;
+}
+
+static int writekeyfile (const char *fname, const dki_t *list, int key_ttl)
+{
+ FILE *fp;
+ const dki_t *dkp;
+ time_t curr = time (NULL);
+ int ksk;
+
+ if ( (fp = fopen (fname, "w")) == NULL )
+ return 0;
+ fprintf (fp, ";\n");
+ fprintf (fp, ";\t!!! Don\'t edit this file by hand.\n");
+ fprintf (fp, ";\t!!! It will be generated by %s.\n", progname);
+ fprintf (fp, ";\n");
+ fprintf (fp, ";\t Last generation time %s\n", time2str (curr, 's'));
+ fprintf (fp, ";\n");
+
+ fprintf (fp, "\n");
+ fprintf (fp, "; *** List of Key Signing Keys ***\n");
+ ksk = 1;
+ for ( dkp = list; dkp; dkp = dkp->next )
+ {
+ if ( ksk && !dki_isksk (dkp) )
+ {
+ fprintf (fp, "; *** List of Zone Signing Keys ***\n");
+ ksk = 0;
+ }
+ dki_prt_comment (dkp, fp);
+ dki_prt_dnskeyttl (dkp, fp, key_ttl);
+ putc ('\n', fp);
+ }
+
+ fclose (fp);
+ return 1;
+}
+
+static int sign_zone (const char *dir, const char *domain, const char *file, const zconf_t *conf)
+{
+ char cmd[1023+1];
+ char str[1023+1];
+ char rparam[254+1];
+ char keysetdir[254+1];
+ const char *gends;
+ const char *pseudo;
+ const char *param;
+ int len;
+ FILE *fp;
+
+ assert (conf != NULL);
+ assert (domain != NULL);
+
+ len = 0;
+ str[0] = '\0';
+ if ( conf->lookaside && conf->lookaside[0] )
+ len = snprintf (str, sizeof (str), "-l %.250s", conf->lookaside);
+
+ dbg_line();
+#if defined(BIND_VERSION) && BIND_VERSION >= 940
+ if ( !dynamic_zone && conf->serialform == Unixtime )
+ snprintf (str+len, sizeof (str) - len, " -N unixtime");
+#endif
+
+ gends = "";
+ if ( conf->sig_gends )
+ gends = "-g ";
+
+ pseudo = "";
+ if ( conf->sig_pseudo )
+ pseudo = "-p ";
+
+ param = "";
+ if ( conf->sig_param && conf->sig_param[0] )
+ param = conf->sig_param;
+
+ dbg_line();
+ rparam[0] = '\0';
+ if ( conf->sig_random && conf->sig_random[0] )
+ snprintf (rparam, sizeof (rparam), "-r %.250s ", conf->sig_random);
+
+ dbg_line();
+ keysetdir[0] = '\0';
+ if ( conf->keysetdir && conf->keysetdir[0] && strcmp (conf->keysetdir, "..") != 0 )
+ snprintf (keysetdir, sizeof (keysetdir), "-d %.250s ", conf->keysetdir);
+
+ if ( dir == NULL || *dir == '\0' )
+ dir = ".";
+
+ dbg_line();
+#if defined(BIND_VERSION) && BIND_VERSION >= 940
+ if ( dynamic_zone )
+ snprintf (cmd, sizeof (cmd), "cd %s; %s %s %s%s%s%s-o %s -e +%d %s -N increment -f %s.dsigned %s K*.private",
+ dir, SIGNCMD, param, gends, pseudo, rparam, keysetdir, domain, conf->sigvalidity, str, file, file);
+ else
+#endif
+ snprintf (cmd, sizeof (cmd), "cd %s; %s %s %s%s%s%s-o %s -e +%d %s %s K*.private",
+ dir, SIGNCMD, param, gends, pseudo, rparam, keysetdir, domain, conf->sigvalidity, str, file);
+ verbmesg (2, conf, "\t Run cmd \"%s\"\n", cmd);
+ *str = '\0';
+ if ( noexec == 0 )
+ {
+ if ( (fp = popen (cmd, "r")) == NULL || fgets (str, sizeof str, fp) == NULL )
+ return -1;
+ pclose (fp);
+ }
+
+ dbg_line();
+ verbmesg (2, conf, "\t Cmd dnssec-signzone return: \"%s\"\n", str_chop (str, '\n'));
+
+ return 0;
+}
+
+static void copy_keyset (const char *dir, const char *domain, const zconf_t *conf)
+{
+ char fromfile[1024];
+ char tofile[1024];
+ int ret;
+
+ /* propagate "keyset"-file to parent dir */
+ if ( conf->keysetdir && strcmp (conf->keysetdir, "..") == 0 )
+ {
+ /* check if special parent-file exist (ksk rollover) */
+ snprintf (fromfile, sizeof (fromfile), "%s/parent-%s", dir, domain);
+ if ( !fileexist (fromfile) ) /* use "normal" keyset-file */
+ snprintf (fromfile, sizeof (fromfile), "%s/keyset-%s", dir, domain);
+
+ /* verbmesg (2, conf, "\t check \"%s\" against parent dir\n", fromfile); */
+ snprintf (tofile, sizeof (tofile), "%s/../keyset-%s", dir, domain);
+ if ( cmpfile (fromfile, tofile) != 0 )
+ {
+ verbmesg (2, conf, "\t copy \"%s\" to parent dir\n", fromfile);
+ if ( (ret = copyfile (fromfile, tofile, NULL)) != 0 )
+ {
+ error ("Couldn't copy \"%s\" to parent dir (%d:%s)\n",
+ fromfile, ret, strerror(errno));
+ lg_mesg (LG_ERROR, "\%s\": can't copy \"%s\" to parent dir (%d:%s)",
+ domain, fromfile, ret, strerror(errno));
+ }
+ }
+ }
+}
+
+static int dyn_update_freeze (const char *domain, const zconf_t *z, int freeze)
+{
+ char cmdline[254+1];
+ char str[254+1];
+ char *action;
+ FILE *fp;
+
+ assert (z != NULL);
+ if ( freeze )
+ action = "freeze";
+ else
+ action = "thaw";
+
+ if ( z->view )
+ snprintf (str, sizeof (str), "\"%s\" in view \"%s\"", domain, z->view);
+ else
+ snprintf (str, sizeof (str), "\"%s\"", domain);
+
+ lg_mesg (LG_NOTICE, "%s: %s dynamic zone", str, action);
+ verbmesg (1, z, "\t%s dynamic zone %s\n", action, str);
+
+ if ( z->view )
+ snprintf (cmdline, sizeof (cmdline), "%s %s %s IN %s", RELOADCMD, action, domain, z->view);
+ else
+ snprintf (cmdline, sizeof (cmdline), "%s %s %s", RELOADCMD, action, domain);
+
+ verbmesg (2, z, "\t Run cmd \"%s\"\n", cmdline);
+ *str = '\0';
+ if ( noexec == 0 )
+ {
+ if ( (fp = popen (cmdline, "r")) == NULL || fgets (str, sizeof str, fp) == NULL )
+ return -1;
+ pclose (fp);
+ }
+
+ verbmesg (2, z, "\t rndc %s return: \"%s\"\n", action, str_chop (str, '\n'));
+
+ return 0;
+}
+
+/*****************************************************************
+** distribute and reload a zone via "distribute_command"
+*****************************************************************/
+static int dist_and_reload (const zone_t *zp)
+{
+ char path[MAX_PATHSIZE+1];
+ char cmdline[254+1];
+ char zone[254+1];
+ char str[254+1];
+ FILE *fp;
+
+ assert (zp != NULL);
+ assert (zp->conf->dist_cmd != NULL);
+
+ if ( !is_exec_ok (zp->conf->dist_cmd) )
+ {
+ char *mesg;
+
+ if ( getuid () == 0 )
+ mesg = "\tDistribution command %s not run as root\n";
+ else
+ mesg = "\tDistribution command %s not run due to strange file mode settings\n";
+
+ verbmesg (1, zp->conf, mesg, zp->conf->dist_cmd);
+ lg_mesg (LG_ERROR, "exec of distribution command %s disabled due to security reasons", zp->conf->dist_cmd);
+
+ return -1;
+ }
+
+ if ( zp->conf->view )
+ snprintf (zone, sizeof (zone), "\"%s\" in view \"%s\"", zp->zone, zp->conf->view);
+ else
+ snprintf (zone, sizeof (zone), "\"%s\"", zp->zone);
+
+
+ pathname (path, sizeof (path), zp->dir, zp->sfile, NULL);
+
+ lg_mesg (LG_NOTICE, "%s: distribution triggered", zone);
+ verbmesg (1, zp->conf, "\tDistribute zone %s\n", zone);
+ if ( zp->conf->view )
+ snprintf (cmdline, sizeof (cmdline), "%s distribute %s %s %s", zp->conf->dist_cmd, zp->zone, path, zp->conf->view);
+ else
+ snprintf (cmdline, sizeof (cmdline), "%s distribute %s %s", zp->conf->dist_cmd, zp->zone, path);
+
+ *str = '\0';
+ if ( noexec == 0 )
+ {
+ verbmesg (2, zp->conf, "\t Run cmd \"%s\"\n", cmdline);
+ if ( (fp = popen (cmdline, "r")) == NULL || fgets (str, sizeof str, fp) == NULL )
+ return -2;
+ pclose (fp);
+ verbmesg (2, zp->conf, "\t %s distribute return: \"%s\"\n", zp->conf->dist_cmd, str_chop (str, '\n'));
+ }
+
+
+ lg_mesg (LG_NOTICE, "%s: reload triggered", zone);
+ verbmesg (1, zp->conf, "\tReload zone %s\n", zone);
+ if ( zp->conf->view )
+ snprintf (cmdline, sizeof (cmdline), "%s reload %s %s %s", zp->conf->dist_cmd, zp->zone, path, zp->conf->view);
+ else
+ snprintf (cmdline, sizeof (cmdline), "%s reload %s %s", zp->conf->dist_cmd, zp->zone, path);
+
+ *str = '\0';
+ if ( noexec == 0 )
+ {
+ verbmesg (2, zp->conf, "\t Run cmd \"%s\"\n", cmdline);
+ if ( (fp = popen (cmdline, "r")) == NULL || fgets (str, sizeof str, fp) == NULL )
+ return -2;
+ pclose (fp);
+ verbmesg (2, zp->conf, "\t %s reload return: \"%s\"\n", zp->conf->dist_cmd, str_chop (str, '\n'));
+ }
+
+ return 0;
+}
+
+/*****************************************************************
+** reload a zone via "rndc"
+*****************************************************************/
+static int reload_zone (const char *domain, const zconf_t *z)
+{
+ char cmdline[254+1];
+ char str[254+1];
+ FILE *fp;
+
+ assert (z != NULL);
+ // fprintf (stderr, "reload_zone %d :%s: :%s:\n", z->verbosity, domain, z->view);
+ if ( z->view )
+ snprintf (str, sizeof (str), "\"%s\" in view \"%s\"", domain, z->view);
+ else
+ snprintf (str, sizeof (str), "\"%s\"", domain);
+
+ lg_mesg (LG_NOTICE, "%s: reload triggered", str);
+ verbmesg (1, z, "\tReload zone %s\n", str);
+
+ if ( z->view )
+ snprintf (cmdline, sizeof (cmdline), "%s reload %s IN %s", RELOADCMD, domain, z->view);
+ else
+ snprintf (cmdline, sizeof (cmdline), "%s reload %s", RELOADCMD, domain);
+
+ *str = '\0';
+ if ( noexec == 0 )
+ {
+ verbmesg (2, z, "\t Run cmd \"%s\"\n", cmdline);
+ if ( (fp = popen (cmdline, "r")) == NULL || fgets (str, sizeof str, fp) == NULL )
+ return -1;
+ pclose (fp);
+ verbmesg (2, z, "\t rndc reload return: \"%s\"\n", str_chop (str, '\n'));
+ }
+
+ return 0;
+}