diff options
Diffstat (limited to 'contrib/zkt/dnssec-zkt.c')
-rw-r--r-- | contrib/zkt/dnssec-zkt.c | 823 |
1 files changed, 823 insertions, 0 deletions
diff --git a/contrib/zkt/dnssec-zkt.c b/contrib/zkt/dnssec-zkt.c new file mode 100644 index 0000000..803cbc3 --- /dev/null +++ b/contrib/zkt/dnssec-zkt.c @@ -0,0 +1,823 @@ +/***************************************************************** +** +** @(#) dnssec-zkt.c (c) Jan 2005 Holger Zuleger hznet.de +** +** Secure DNS zone key tool +** A wrapper command around the BIND dnssec-keygen utility +** +** 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 <stdlib.h> /* abort(), exit(), ... */ +# include <string.h> +# include <dirent.h> +# include <assert.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 "debug.h" +# include "misc.h" +# include "strlist.h" +# include "zconf.h" +# include "dki.h" +# include "zkt.h" + +extern int optopt; +extern int opterr; +extern int optind; +extern char *optarg; +const char *progname; + +char *labellist = NULL; + +int headerflag = 1; +int ageflag = 0; +int lifetime = 0; +int lifetimeflag = 0; +int timeflag = 1; +int exptimeflag = 0; +int pathflag = 0; +int kskflag = 1; +int zskflag = 1; +int ljustflag = 0; + +static int dirflag = 0; +static int recflag = RECURSIVE; +static int trustedkeyflag = 0; +static char *kskdomain = ""; +static const char *view = ""; + +# define short_options ":0:1:2:3:9A:C:D:P:S:R:HKTs:ZV:afF:c:O:dhkLl:prtez" +#if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG +static struct option long_options[] = { + {"ksk-rollover", no_argument, NULL, '9'}, + {"ksk-status", required_argument, NULL, '0'}, + {"ksk-roll-status", required_argument, NULL, '0'}, + {"ksk-newkey", required_argument, NULL, '1'}, + {"ksk-publish", required_argument, NULL, '2'}, + {"ksk-delkey", required_argument, NULL, '3'}, + {"ksk-roll-phase1", required_argument, NULL, '1'}, + {"ksk-roll-phase2", required_argument, NULL, '2'}, + {"ksk-roll-phase3", required_argument, NULL, '3'}, + {"list-dnskeys", no_argument, NULL, 'K'}, + {"list-trustedkeys", no_argument, NULL, 'T'}, + {"ksk", no_argument, NULL, 'k'}, + {"zsk", no_argument, NULL, 'z'}, + {"age", no_argument, NULL, 'a'}, + {"lifetime", no_argument, NULL, 'f'}, + {"time", no_argument, NULL, 't'}, + {"expire", no_argument, NULL, 'e'}, + {"recursive", no_argument, NULL, 'r'}, + {"zone-config", no_argument, NULL, 'Z'}, + {"leftjust", no_argument, NULL, 'L'}, + {"path", no_argument, NULL, 'p'}, + {"nohead", no_argument, NULL, 'h'}, + {"directory", no_argument, NULL, 'd'}, + {"config", required_argument, NULL, 'c'}, + {"option", required_argument, NULL, 'O'}, + {"config-option", required_argument, NULL, 'O'}, + {"published", required_argument, NULL, 'P'}, + {"standby", required_argument, NULL, 'S'}, + {"active", required_argument, NULL, 'A'}, + {"depreciated", required_argument, NULL, 'D'}, + {"create", required_argument, NULL, 'C'}, + {"revoke", required_argument, NULL, 'R'}, + {"remove", required_argument, NULL, 19 }, + {"destroy", required_argument, NULL, 20 }, + {"setlifetime", required_argument, NULL, 'F' }, + {"view", required_argument, NULL, 'V' }, + {"help", no_argument, NULL, 'H'}, + {0, 0, 0, 0} +}; +#endif + +static int parsedirectory (const char *dir, dki_t **listp); +static void parsefile (const char *file, dki_t **listp); +static void createkey (const char *keyname, const dki_t *list, const zconf_t *conf); +static void ksk_roll (const char *keyname, int phase, const dki_t *list, const zconf_t *conf); +static int create_parent_file (const char *fname, int phase, int ttl, const dki_t *dkp); +static void usage (char *mesg, zconf_t *cp); +static const char *parsetag (const char *str, int *tagp); + +static void setglobalflags (zconf_t *config) +{ + recflag = config->recursive; + ageflag = config->printage; + timeflag = config->printtime; + ljustflag = config->ljust; +} + +int main (int argc, char *argv[]) +{ + dki_t *data = NULL; + dki_t *dkp; + int c; + int opt_index; + int action; + const char *file; + const char *defconfname = NULL; + char *p; + char str[254+1]; + const char *keyname = NULL; + int searchtag; + zconf_t *config; + + progname = *argv; + if ( (p = strrchr (progname, '/')) ) + progname = ++p; + view = getnameappendix (progname, "dnssec-zkt"); + + defconfname = getdefconfname (view); + config = loadconfig ("", (zconf_t *)NULL); /* load built in config */ + if ( fileexist (defconfname) ) /* load default config file */ + config = loadconfig (defconfname, config); + if ( config == NULL ) + fatal ("Out of memory\n"); + setglobalflags (config); + + opterr = 0; + opt_index = 0; + action = 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 '9': /* ksk rollover help */ + ksk_roll ("help", c - '0', NULL, NULL); + exit (1); + case '1': /* ksk rollover: create new key */ + case '2': /* ksk rollover: publish DS */ + case '3': /* ksk rollover: delete old key */ + case '0': /* ksk rollover: show current status */ + action = c; + if ( !optarg ) + usage ("ksk rollover requires an domain argument", config); + kskdomain = str_tolowerdup (optarg); + break; + case 'T': + trustedkeyflag = 1; + zskflag = pathflag = 0; + /* fall through */ + case 'H': + case 'K': + case 'Z': + action = c; + break; + case 'C': + pathflag = !pathflag; + /* fall through */ + case 'P': + case 'S': + case 'A': + case 'D': + case 'R': + case 's': + case 19: + case 20: + if ( (keyname = parsetag (optarg, &searchtag)) != NULL ) + { + int len = strlen (keyname); + if ( len > 0 && keyname[len-1] != '.' ) + { + snprintf (str, sizeof(str), "%s.", keyname); + keyname = str; + } + } + keyname = str_tolowerdup (keyname); + action = c; + break; + case 'a': /* age */ + ageflag = !ageflag; + break; + case 'f': /* key lifetime */ + lifetimeflag = !lifetimeflag; + break; + case 'F': /* set key lifetime */ + lifetime = atoi (optarg); + lifetimeflag = 1; /* set some flags for more informative output */ + exptimeflag = 1; + timeflag = 1; + action = c; + break; + case 'V': /* view name */ + view = optarg; + defconfname = getdefconfname (view); + if ( fileexist (defconfname) ) /* load default config file */ + config = loadconfig (defconfname, config); + if ( config == NULL ) + fatal ("Out of memory\n"); + setglobalflags (config); + break; + case 'c': + config = loadconfig (optarg, config); + setglobalflags (config); + checkconfig (config); + break; + case 'O': /* read option from commandline */ + config = loadconfig_fromstr (optarg, config); + setglobalflags (config); + checkconfig (config); + break; + case 'd': /* ignore directory arg */ + dirflag = 1; + break; + case 'h': /* print no headline */ + headerflag = 0; + break; + case 'k': /* ksk only */ + zskflag = 0; + break; + case 'L': /* ljust */ + ljustflag = !ljustflag; + break; + case 'l': /* label list */ + labellist = prepstrlist (optarg, LISTDELIM); + if ( labellist == NULL ) + fatal ("Out of memory\n"); + break; + case 'p': /* print path */ + pathflag = 1; + break; + case 'r': /* switch recursive flag */ + recflag = !recflag; + break; + case 't': /* time */ + timeflag = !timeflag; + break; + case 'e': /* expire time */ + exptimeflag = !exptimeflag; + break; + case 'z': /* zsk only */ + kskflag = 0; + break; + case ':': + snprintf (str, sizeof(str), "option \"-%c\" requires an argument.\n", + optopt); + usage (str, config); + break; + case '?': + if ( isprint (optopt) ) + snprintf (str, sizeof(str), "Unknown option \"-%c\".\n", + optopt); + else + snprintf (str, sizeof (str), "Unknown option char \\x%x.\n", + optopt); + usage (str, config); + break; + default: + abort(); + } + } + + /* it's better to do this before we read the whole directory tree */ + if ( action == 'Z' ) + { + printconfig ("stdout", config); + return 0; + } + + if ( kskflag == 0 && zskflag == 0 ) + kskflag = zskflag = 1; + + c = optind; + do { + if ( c >= argc ) /* no args left */ + file = config->zonedir; /* use default directory */ + else + file = argv[c++]; + + if ( is_directory (file) ) + parsedirectory (file, &data); + else + parsefile (file, &data); + + } while ( c < argc ); /* for all arguments */ + + switch ( action ) + { + case 'H': + usage ("", config); + case 'C': + createkey (keyname, data, config); + break; + case 'P': + case 'S': + case 'A': + case 'D': + if ( (dkp = (dki_t*)zkt_search (data, searchtag, keyname)) == NULL ) + fatal ("Key with tag %u not found\n", searchtag); + else if ( dkp == (void *) 01 ) + fatal ("Key with tag %u found multiple times\n", searchtag); + if ( (c = dki_setstatus_preservetime (dkp, action)) != 0 ) + fatal ("Couldn't change status of key %u: %d\n", searchtag, c); + break; + case 19: /* remove (rename) key file */ + if ( (dkp = (dki_t *)zkt_search (data, searchtag, keyname)) == NULL ) + fatal ("Key with tag %u not found\n", searchtag); + else if ( dkp == (void *) 01 ) + fatal ("Key with tag %u found multiple times\n", searchtag); + dki_remove (dkp); + break; + case 20: /* destroy the key (remove the files!) */ + if ( (dkp = (dki_t *)zkt_search (data, searchtag, keyname)) == NULL ) + fatal ("Key with tag %u not found\n", searchtag); + else if ( dkp == (void *) 01 ) + fatal ("Key with tag %u found multiple times\n", searchtag); + dki_destroy (dkp); + break; + case 'R': + if ( (dkp = (dki_t *)zkt_search (data, searchtag, keyname)) == NULL ) + fatal ("Key with tag %u not found\n", searchtag); + else if ( dkp == (void *) 01 ) + fatal ("Key with tag %u found multiple times\n", searchtag); + if ( (c = dki_setstatus (dkp, action)) != 0 ) + fatal ("Couldn't change status of key %u: %d\n", searchtag, c); + break; + case 's': + if ( (dkp = (dki_t *)zkt_search (data, searchtag, keyname)) == NULL ) + fatal ("Key with tag %u not found\n", searchtag); + else if ( dkp == (void *) 01 ) + fatal ("Key with tag %u found multiple times\n", searchtag); + dki_prt_dnskey (dkp, stdout); + break; + case 'K': + zkt_list_dnskeys (data); + break; + case 'T': + zkt_list_trustedkeys (data); + break; + case '1': /* ksk rollover new key */ + case '2': /* ksk rollover publish DS */ + case '3': /* ksk rollover delete old key */ + case '0': /* ksk rollover status */ + ksk_roll (kskdomain, action - '0', data, config); + break; + case 'F': + zkt_setkeylifetime (data); + /* fall through */ + default: + zkt_list_keys (data); + } + + return 0; +} + +# 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 *cp) +{ + fprintf (stderr, "Secure DNS Zone Key Tool %s\n", ZKT_VERSION); + fprintf (stderr, "\n"); + fprintf (stderr, "Show zone config parameter as %s file\n", LOCALCONF_FILE); + sopt_usage ("\tusage: %s -Z\n", progname); + lopt_usage ("\tusage: %s --zone-config\n", progname); + fprintf (stderr, "\n"); + fprintf (stderr, "List keys in current or given directory (-r for recursive mode)\n"); + sopt_usage ("\tusage: %s [-dhatkzpr] [-c config] [file|dir ...]\n", progname); + fprintf (stderr, "\n"); + fprintf (stderr, "List public part of keys in DNSKEY RR format\n"); + sopt_usage ("\tusage: %s -K [-dhkzr] [-c config] [file|dir ...]\n", progname); + lopt_usage ("\tusage: %s --list-dnskeys [-dhkzr] [-c config] [file|dir ...]\n", progname); + fprintf (stderr, "\n"); + fprintf (stderr, "List keys (output is suitable for trusted-keys section)\n"); + sopt_usage ("\tusage: %s -T [-dhzr] [-c config] [file|dir ...]\n", progname); + lopt_usage ("\tusage: %s --list-trustedkeys [-dhzr] [-c config] [file|dir ...]\n", progname); + fprintf (stderr, "\n"); + fprintf (stderr, "Create a new key \n"); + sopt_usage ("\tusage: %s -C <name> [-k] [-dpr] [-c config] [dir ...]\n", progname); + lopt_usage ("\tusage: %s --create=<name> [-k] [-dpr] [-c config] [dir ...]\n", progname); + fprintf (stderr, "\t\tKSK (use -k): %s %d bits\n", dki_algo2str (cp->k_algo), cp->k_bits); + fprintf (stderr, "\t\tZSK (default): %s %d bits\n", dki_algo2str (cp->z_algo), cp->z_bits); + fprintf (stderr, "\n"); + fprintf (stderr, "Change key status of specified key to published, active or depreciated\n"); + fprintf (stderr, "\t(<keyspec> := tag | tag:name) \n"); + sopt_usage ("\tusage: %s -P|-A|-D <keyspec> [-dr] [-c config] [dir ...]\n", progname); + lopt_usage ("\tusage: %s --published=<keyspec> [-dr] [-c config] [dir ...]\n", progname); + lopt_usage ("\tusage: %s --active=<keyspec> [-dr] [-c config] [dir ...]\n", progname); + lopt_usage ("\tusage: %s --depreciated=<keyspec> [-dr] [-c config] [dir ...]\n", progname); + fprintf (stderr, "\n"); + fprintf (stderr, "Revoke specified key (<keyspec> := tag | tag:name) \n"); + sopt_usage ("\tusage: %s -R <keyspec> [-dr] [-c config] [dir ...]\n", progname); + lopt_usage ("\tusage: %s --revoke=<keyspec> [-dr] [-c config] [dir ...]\n", progname); + fprintf (stderr, "\n"); + fprintf (stderr, "Remove (rename) or destroy (delete) specified key (<keyspec> := tag | tag:name) \n"); + lopt_usage ("\tusage: %s --remove=<keyspec> [-dr] [-c config] [dir ...]\n", progname); + lopt_usage ("\tusage: %s --destroy=<keyspec> [-dr] [-c config] [dir ...]\n", progname); + fprintf (stderr, "\n"); + fprintf (stderr, "Initiate a semi-automated KSK rollover"); + fprintf (stderr, "('%s -9%s' prints out a short description)\n", progname, loptstr ("|--ksk-rollover", "")); + sopt_usage ("\tusage: %s {-1} do.ma.in.\n", progname); + lopt_usage ("\tusage: %s {--ksk-roll-phase1|--ksk-newkey} do.ma.in.\n", progname); + sopt_usage ("\tusage: %s {-2} do.ma.in.\n", progname); + lopt_usage ("\tusage: %s {--ksk-roll-phase2|--ksk-publish} do.ma.in.\n", progname); + sopt_usage ("\tusage: %s {-3} do.ma.in.\n", progname); + lopt_usage ("\tusage: %s {--ksk-roll-phase3|--ksk-delkey} do.ma.in.\n", progname); + sopt_usage ("\tusage: %s {-0} do.ma.in.\n", progname); + lopt_usage ("\tusage: %s {--ksk-roll-status|--ksk-status} do.ma.in.\n", progname); + fprintf (stderr, "\n"); + + fprintf (stderr, "\n"); + fprintf (stderr, "General options \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 read config options from commandline\n"); + fprintf (stderr, "\t-h%s\t no headline or trusted-key section header/trailer in -T mode\n", loptstr (", --nohead", "\t")); + fprintf (stderr, "\t-d%s\t skip directory arguments\n", loptstr (", --directory", "\t")); + fprintf (stderr, "\t-L%s\t print the domain name left justified (default: %s)\n", loptstr (", --leftjust", "\t"), ljustflag ? "on": "off"); + fprintf (stderr, "\t-l list\t\t print out only zone keys out of the given domain list\n"); + fprintf (stderr, "\t-p%s\t show path of keyfile / create key in current directory\n", loptstr (", --path", "\t")); + fprintf (stderr, "\t-r%s\t recursive mode on/off (default: %s)\n", loptstr(", --recursive", "\t"), recflag ? "on": "off"); + fprintf (stderr, "\t-a%s\t print age of key (default: %s)\n", loptstr (", --age", "\t"), ageflag ? "on": "off"); + fprintf (stderr, "\t-t%s\t print key generation time (default: %s)\n", loptstr (", --time", "\t"), + timeflag ? "on": "off"); + fprintf (stderr, "\t-e%s\t print key expiration time\n", loptstr (", --expire", "\t")); + fprintf (stderr, "\t-f%s\t print key lifetime\n", loptstr (", --lifetime", "\t")); + fprintf (stderr, "\t-F days%s=days\t set key lifetime\n", loptstr (", --setlifetime", "\t")); + fprintf (stderr, "\t-k%s\t key signing keys only\n", loptstr (", --ksk", "\t")); + fprintf (stderr, "\t-z%s\t zone signing keys only\n", loptstr (", --zsk", "\t")); + if ( mesg && *mesg ) + fprintf (stderr, "%s\n", mesg); + exit (1); +} + +static void createkey (const char *keyname, const dki_t *list, const zconf_t *conf) +{ + const char *dir = ""; + dki_t *dkp; + + if ( keyname == NULL || *keyname == '\0' ) + fatal ("Create key: no keyname!"); + + dbg_val2 ("createkey: keyname %s, pathflag = %d\n", keyname, pathflag); + /* search for already existent key to get the directory name */ + if ( pathflag && (dkp = (dki_t *)zkt_search (list, 0, keyname)) != NULL ) + { + char path[MAX_PATHSIZE+1]; + zconf_t localconf; + + dir = dkp->dname; + pathname (path, sizeof (path), dir, LOCALCONF_FILE, NULL); + if ( fileexist (path) ) /* load local config file */ + { + dbg_val ("Load local config file \"%s\"\n", path); + memcpy (&localconf, conf, sizeof (zconf_t)); + conf = loadconfig (path, &localconf); + } + } + + if ( zskflag ) + dkp = dki_new (dir, keyname, DKI_ZSK, conf->z_algo, conf->z_bits, conf->z_random, conf->z_life / DAYSEC); + else + dkp = dki_new (dir, keyname, DKI_KSK, conf->k_algo, conf->k_bits, conf->k_random, conf->k_life / DAYSEC); + if ( dkp == NULL ) + fatal ("Can't create key %s: %s!\n", keyname, dki_geterrstr ()); + + /* create a new key always in state published, which means "standby" for ksk */ + dki_setstatus (dkp, DKI_PUB); +} + +static int get_parent_phase (const char *file) +{ + FILE *fp; + int phase; + + if ( (fp = fopen (file, "r")) == NULL ) + return -1; + + phase = 0; + if ( fscanf (fp, "; KSK rollover phase%d", &phase) != 1 ) + phase = 0; + + fclose (fp); + return phase; +} + +static void ksk_roll (const char *keyname, int phase, const dki_t *list, const zconf_t *conf) +{ + char path[MAX_PATHSIZE+1]; + zconf_t localconf; + const char *dir; + dki_t *keylist; + dki_t *dkp; + dki_t *standby; + int parent_exist; + int parent_age; + int parent_phase; + int parent_propagation; + int key_ttl; + int ksk; + + if ( phase == 9 ) /* usage */ + { + fprintf (stderr, "A KSK rollover requires three consecutive steps:\n"); + fprintf (stderr, "\n"); + fprintf (stderr, "-1%s", loptstr ("|--ksk-roll-phase1 (--ksk-newkey)\n", "")); + fprintf (stderr, "\t Create a new KSK.\n"); + fprintf (stderr, "\t This step also creates a parent-<domain> file which contains only\n"); + fprintf (stderr, "\t the _old_ key. This file will be copied in hierarchical mode\n"); + fprintf (stderr, "\t by dnssec-signer to the parent directory as keyset-<domain> file.\n"); + fprintf (stderr, "\t Wait until the new keyset is propagated, before going to the next step.\n"); + fprintf (stderr, "\n"); + fprintf (stderr, "-2%s", loptstr ("|--ksk-roll-phase2 (--ksk-publish)\n", "")); + fprintf (stderr, "\t This step creates a parent-<domain> file with the _new_ key only.\n"); + fprintf (stderr, "\t Please send this file immediately to the parent (In hierarchical\n"); + fprintf (stderr, "\t mode this will be done automatically by the dnssec-signer command).\n"); + fprintf (stderr, "\t Then wait until the new DS is generated by the parent and propagated\n"); + fprintf (stderr, "\t to all the parent name server, plus the old DS TTL before going to step three.\n"); + fprintf (stderr, "\n"); + fprintf (stderr, "-3%s", loptstr ("|--ksk-roll-phase3 (--ksk-delkey)\n", "")); + fprintf (stderr, "\t Remove (rename) the old KSK and the parent-<domain> file.\n"); + fprintf (stderr, "\t You have to manually delete the old KSK (look at file names beginning\n"); + fprintf (stderr, "\t with an lower 'k').\n"); + fprintf (stderr, "\n"); + fprintf (stderr, "-0%s", loptstr ("|--ksk-roll-stat (--ksk-status)\n", "")); + fprintf (stderr, "\t Show the current KSK rollover state of a domain.\n"); + + fprintf (stderr, "\n"); + + return; + } + + if ( keyname == NULL || *keyname == '\0' ) + fatal ("ksk rollover: no domain!"); + + dbg_val2 ("ksk_roll: keyname %s, phase = %d\n", keyname, phase); + + /* search for already existent key to get the directory name */ + if ( (keylist = (dki_t *)zkt_search (list, 0, keyname)) == NULL ) + fatal ("ksk rollover: domain %s not found!\n", keyname); + dkp = keylist; + + /* try to read local config file */ + dir = dkp->dname; + pathname (path, sizeof (path), dir, LOCALCONF_FILE, NULL); + if ( fileexist (path) ) /* load local config file */ + { + dbg_val ("Load local config file \"%s\"\n", path); + memcpy (&localconf, conf, sizeof (zconf_t)); + conf = loadconfig (path, &localconf); + } + key_ttl = conf->key_ttl; + + /* check if parent-file already exist */ + pathname (path, sizeof (path), dir, "parent-", keyname); + parent_phase = parent_age = 0; + if ( (parent_exist = fileexist (path)) != 0 ) + { + parent_phase = get_parent_phase (path); + parent_age = file_age (path); + } + // parent_propagation = 2 * DAYSEC; + parent_propagation = 5 * MINSEC; + + ksk = 0; /* count active(!) key signing keys */ + standby = NULL; /* find standby key if available */ + for ( dkp = keylist; dkp; dkp = dkp->next ) + if ( dki_isksk (dkp) ) + { + if ( dki_status (dkp) == DKI_ACT ) + ksk++; + else if ( dki_status (dkp) == DKI_PUB ) + standby = dkp; + } + + switch ( phase ) + { + case 0: /* print status (debug) */ + fprintf (stdout, "ksk_rollover:\n"); + fprintf (stdout, "\t domain = %s\n", keyname); + fprintf (stdout, "\t phase = %d\n", parent_phase); + fprintf (stdout, "\t parent_file %s %s\n", path, parent_exist ? "exist": "not exist"); + if ( parent_exist ) + fprintf (stdout, "\t age of parent_file %d %s\n", parent_age, str_delspace (age2str (parent_age))); + fprintf (stdout, "\t # of active key signing keys %d\n", ksk); + fprintf (stdout, "\t parent_propagation %d %s\n", parent_propagation, str_delspace (age2str (parent_propagation))); + fprintf (stdout, "\t keys ttl %d %s\n", key_ttl, age2str (key_ttl)); + + for ( dkp = keylist; dkp; dkp = dkp->next ) + { + /* TODO: Nur zum testen */ + dki_prt_dnskey (dkp, stdout); + } + break; + case 1: + if ( parent_exist || ksk > 1 ) + fatal ("Can\'t create new ksk because there is already an ksk rollover in progress\n"); + + fprintf (stdout, "create new ksk \n"); + dkp = dki_new (dir, keyname, DKI_KSK, conf->k_algo, conf->k_bits, conf->k_random, conf->k_life / DAYSEC); + if ( dkp == NULL ) + fatal ("Can't create key %s: %s!\n", keyname, dki_geterrstr ()); + if ( standby ) + { + dki_setstatus (standby, DKI_ACT); /* activate standby key */ + dki_setstatus (dkp, DKI_PUB); /* new key will be the new standby */ + } + + // dkp = keylist; /* use old key to create the parent file */ + if ( (dkp = (dki_t *)dki_find (keylist, 1, 'a', 1)) == NULL ) /* find the oldest active ksk to create the parent file */ + fatal ("ksk_rollover phase1: Couldn't find the old active key\n"); + if ( !create_parent_file (path, phase, key_ttl, dkp) ) + fatal ("Couldn't create parentfile %s\n", path); + break; + + case 2: + if ( ksk < 2 ) + fatal ("Can\'t publish new key because no one exist\n"); + if ( !parent_exist ) + fatal ("More than one KSK but no parent file found!\n"); + if ( parent_phase != 1 ) + fatal ("Parent file exists but is in wrong state (phase = %d)\n", parent_phase); + if ( parent_age < conf->proptime + key_ttl ) + fatal ("ksk_rollover (phase2): you have to wait for the propagation of the new KSK (at least %dsec or %s)\n", + conf->proptime + key_ttl - parent_age, + str_delspace (age2str (conf->proptime + key_ttl - parent_age))); + + fprintf (stdout, "save new ksk in parent file\n"); + dkp = keylist->next; /* set dkp to new ksk */ + if ( !create_parent_file (path, phase, key_ttl, dkp) ) + fatal ("Couldn't create parentfile %s\n", path); + break; + case 3: + if ( !parent_exist || ksk < 2 ) + fatal ("ksk-delkey only allowed after ksk-publish\n"); + if ( parent_phase != 2 ) + fatal ("Parent file exists but is in wrong state (phase = %d)\n", parent_phase); + if ( parent_age < parent_propagation + key_ttl ) + fatal ("ksk_rollover (phase3): you have to wait for DS propagation (at least %dsec or %s)\n", + parent_propagation + key_ttl - parent_age, + str_delspace (age2str (parent_propagation + key_ttl - parent_age))); + /* remove the parentfile */ + fprintf (stdout, "remove parentfile \n"); + unlink (path); + /* remove or rename the old key */ + fprintf (stdout, "old ksk renamed \n"); + dkp = keylist; /* set dkp to old ksk */ + dki_remove (dkp); + break; + default: assert (phase == 1 || phase == 2 || phase == 3); + } +} + +/***************************************************************** +** create_parent_file () +*****************************************************************/ +static int create_parent_file (const char *fname, int phase, int ttl, const dki_t *dkp) +{ + FILE *fp; + + assert ( fname != NULL ); + + if ( dkp == NULL || (phase != 1 && phase != 2) ) + return 0; + + if ( (fp = fopen (fname, "w")) == NULL ) + fatal ("can\'t create new parentfile \"%s\"\n", fname); + + if ( phase == 1 ) + fprintf (fp, "; KSK rollover phase1 (old key)\n"); + else + fprintf (fp, "; KSK rollover phase2 (new key)\n"); + + dki_prt_dnskeyttl (dkp, fp, ttl); + fclose (fp); + + return phase; +} + +static int parsedirectory (const char *dir, dki_t **listp) +{ + dki_t *dkp; + DIR *dirp; + struct dirent *dentp; + char path[MAX_PATHSIZE+1]; + + if ( dirflag ) + return 0; + + dbg_val ("directory: opendir(%s)\n", dir); + if ( (dirp = opendir (dir)) == NULL ) + return 0; + + while ( (dentp = readdir (dirp)) != NULL ) + { + if ( is_dotfile (dentp->d_name) ) + continue; + + dbg_val ("directory: check %s\n", dentp->d_name); + pathname (path, sizeof (path), dir, dentp->d_name, NULL); + if ( is_directory (path) && recflag ) + { + dbg_val ("directory: recursive %s\n", path); + parsedirectory (path, listp); + } + else if ( is_keyfilename (dentp->d_name) ) + if ( (dkp = dki_read (dir, dentp->d_name)) ) + { + // fprintf (stderr, "parsedir: tssearch (%d %s)\n", dkp, dkp->name); +#if defined (USE_TREE) && USE_TREE + dki_tadd (listp, dkp); +#else + dki_add (listp, dkp); +#endif + } + } + closedir (dirp); + return 1; +} + +static void parsefile (const char *file, dki_t **listp) +{ + char path[MAX_PATHSIZE+1]; + dki_t *dkp; + + /* file arg contains path ? ... */ + file = splitpath (path, sizeof (path), file); /* ... then split of */ + + if ( is_keyfilename (file) ) /* plain file name looks like DNS key file ? */ + { + if ( (dkp = dki_read (path, file)) ) /* read DNS key file ... */ +#if defined (USE_TREE) && USE_TREE + dki_tadd (listp, dkp); /* ... and add to tree */ +#else + dki_add (listp, dkp); /* ... and add to list */ +#endif + else + error ("error parsing %s: (%s)\n", file, dki_geterrstr()); + } +} + +static const char *parsetag (const char *str, int *tagp) +{ + const char *p; + + *tagp = 0; + while ( isspace (*str) ) /* skip leading ws */ + str++; + + p = str; + if ( isdigit (*p) ) /* keytag starts with digit */ + { + sscanf (p, "%u", tagp); /* read keytag as number */ + do /* eat up to the end of the number */ + p++; + while ( isdigit (*p) ); + + if ( *p == ':' ) /* label follows ? */ + return p+1; /* return that */ + if ( *p == '\0' ) + return NULL; /* no label */ + } + return str; /* return as label string if not a numeric keytag */ +} + |