1/***************************************************************** 2** 3** @(#) dnssec-zkt.c (c) Jan 2005 Holger Zuleger hznet.de 4** 5** Secure DNS zone key tool 6** A wrapper command around the BIND dnssec-keygen utility 7** 8** Copyright (c) 2005 - 2008, Holger Zuleger HZnet. All rights reserved. 9** 10** This software is open source. 11** 12** Redistribution and use in source and binary forms, with or without 13** modification, are permitted provided that the following conditions 14** are met: 15** 16** Redistributions of source code must retain the above copyright notice, 17** this list of conditions and the following disclaimer. 18** 19** Redistributions in binary form must reproduce the above copyright notice, 20** this list of conditions and the following disclaimer in the documentation 21** and/or other materials provided with the distribution. 22** 23** Neither the name of Holger Zuleger HZnet nor the names of its contributors may 24** be used to endorse or promote products derived from this software without 25** specific prior written permission. 26** 27** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 28** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE 31** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37** POSSIBILITY OF SUCH DAMAGE. 38** 39*****************************************************************/ 40 41# include <stdio.h> 42# include <stdlib.h> /* abort(), exit(), ... */ 43# include <string.h> 44# include <dirent.h> 45# include <assert.h> 46# include <unistd.h> 47# include <ctype.h> 48 49#ifdef HAVE_CONFIG_H 50# include <config.h> 51#endif 52# include "config_zkt.h" 53#if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG 54# include <getopt.h> 55#endif 56 57# include "debug.h" 58# include "misc.h" 59# include "strlist.h" 60# include "zconf.h" 61# include "dki.h" 62# include "zkt.h" 63 64extern int optopt; 65extern int opterr; 66extern int optind; 67extern char *optarg; 68const char *progname; 69 70char *labellist = NULL; 71 72int headerflag = 1; 73int ageflag = 0; 74int lifetime = 0; 75int lifetimeflag = 0; 76int timeflag = 1; 77int exptimeflag = 0; 78int pathflag = 0; 79int kskflag = 1; 80int zskflag = 1; 81int ljustflag = 0; 82 83static int dirflag = 0; 84static int recflag = RECURSIVE; 85static int trustedkeyflag = 0; 86static char *kskdomain = ""; 87static const char *view = ""; 88 89# define short_options ":0:1:2:3:9A:C:D:P:S:R:HKTs:ZV:afF:c:O:dhkLl:prtez" 90#if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG 91static struct option long_options[] = { 92 {"ksk-rollover", no_argument, NULL, '9'}, 93 {"ksk-status", required_argument, NULL, '0'}, 94 {"ksk-roll-status", required_argument, NULL, '0'}, 95 {"ksk-newkey", required_argument, NULL, '1'}, 96 {"ksk-publish", required_argument, NULL, '2'}, 97 {"ksk-delkey", required_argument, NULL, '3'}, 98 {"ksk-roll-phase1", required_argument, NULL, '1'}, 99 {"ksk-roll-phase2", required_argument, NULL, '2'}, 100 {"ksk-roll-phase3", required_argument, NULL, '3'}, 101 {"list-dnskeys", no_argument, NULL, 'K'}, 102 {"list-trustedkeys", no_argument, NULL, 'T'}, 103 {"ksk", no_argument, NULL, 'k'}, 104 {"zsk", no_argument, NULL, 'z'}, 105 {"age", no_argument, NULL, 'a'}, 106 {"lifetime", no_argument, NULL, 'f'}, 107 {"time", no_argument, NULL, 't'}, 108 {"expire", no_argument, NULL, 'e'}, 109 {"recursive", no_argument, NULL, 'r'}, 110 {"zone-config", no_argument, NULL, 'Z'}, 111 {"leftjust", no_argument, NULL, 'L'}, 112 {"path", no_argument, NULL, 'p'}, 113 {"nohead", no_argument, NULL, 'h'}, 114 {"directory", no_argument, NULL, 'd'}, 115 {"config", required_argument, NULL, 'c'}, 116 {"option", required_argument, NULL, 'O'}, 117 {"config-option", required_argument, NULL, 'O'}, 118 {"published", required_argument, NULL, 'P'}, 119 {"standby", required_argument, NULL, 'S'}, 120 {"active", required_argument, NULL, 'A'}, 121 {"depreciated", required_argument, NULL, 'D'}, 122 {"create", required_argument, NULL, 'C'}, 123 {"revoke", required_argument, NULL, 'R'}, 124 {"remove", required_argument, NULL, 19 }, 125 {"destroy", required_argument, NULL, 20 }, 126 {"setlifetime", required_argument, NULL, 'F' }, 127 {"view", required_argument, NULL, 'V' }, 128 {"help", no_argument, NULL, 'H'}, 129 {0, 0, 0, 0} 130}; 131#endif 132 133static int parsedirectory (const char *dir, dki_t **listp); 134static void parsefile (const char *file, dki_t **listp); 135static void createkey (const char *keyname, const dki_t *list, const zconf_t *conf); 136static void ksk_roll (const char *keyname, int phase, const dki_t *list, const zconf_t *conf); 137static int create_parent_file (const char *fname, int phase, int ttl, const dki_t *dkp); 138static void usage (char *mesg, zconf_t *cp); 139static const char *parsetag (const char *str, int *tagp); 140 141static void setglobalflags (zconf_t *config) 142{ 143 recflag = config->recursive; 144 ageflag = config->printage; 145 timeflag = config->printtime; 146 ljustflag = config->ljust; 147} 148 149int main (int argc, char *argv[]) 150{ 151 dki_t *data = NULL; 152 dki_t *dkp; 153 int c; 154 int opt_index; 155 int action; 156 const char *file; 157 const char *defconfname = NULL; 158 char *p; 159 char str[254+1]; 160 const char *keyname = NULL; 161 int searchtag; 162 zconf_t *config; 163 164 progname = *argv; 165 if ( (p = strrchr (progname, '/')) ) 166 progname = ++p; 167 view = getnameappendix (progname, "dnssec-zkt"); 168 169 defconfname = getdefconfname (view); 170 config = loadconfig ("", (zconf_t *)NULL); /* load built in config */ 171 if ( fileexist (defconfname) ) /* load default config file */ 172 config = loadconfig (defconfname, config); 173 if ( config == NULL ) 174 fatal ("Out of memory\n"); 175 setglobalflags (config); 176 177 opterr = 0; 178 opt_index = 0; 179 action = 0; 180#if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG 181 while ( (c = getopt_long (argc, argv, short_options, long_options, &opt_index)) != -1 ) 182#else 183 while ( (c = getopt (argc, argv, short_options)) != -1 ) 184#endif 185 { 186 switch ( c ) 187 { 188 case '9': /* ksk rollover help */ 189 ksk_roll ("help", c - '0', NULL, NULL); 190 exit (1); 191 case '1': /* ksk rollover: create new key */ 192 case '2': /* ksk rollover: publish DS */ 193 case '3': /* ksk rollover: delete old key */ 194 case '0': /* ksk rollover: show current status */ 195 action = c; 196 if ( !optarg ) 197 usage ("ksk rollover requires an domain argument", config); 198 kskdomain = domain_canonicdup (optarg); 199 break; 200 case 'T': 201 trustedkeyflag = 1; 202 zskflag = pathflag = 0; 203 /* fall through */ 204 case 'H': 205 case 'K': 206 case 'Z': 207 action = c; 208 break; 209 case 'C': 210 pathflag = !pathflag; 211 /* fall through */ 212 case 'P': 213 case 'S': 214 case 'A': 215 case 'D': 216 case 'R': 217 case 's': 218 case 19: 219 case 20: 220 if ( (keyname = parsetag (optarg, &searchtag)) != NULL ) 221 keyname = domain_canonicdup (keyname); 222 action = c; 223 break; 224 case 'a': /* age */ 225 ageflag = !ageflag; 226 break; 227 case 'f': /* key lifetime */ 228 lifetimeflag = !lifetimeflag; 229 break; 230 case 'F': /* set key lifetime */ 231 lifetime = atoi (optarg); 232 lifetimeflag = 1; /* set some flags for more informative output */ 233 exptimeflag = 1; 234 timeflag = 1; 235 action = c; 236 break; 237 case 'V': /* view name */ 238 view = optarg; 239 defconfname = getdefconfname (view); 240 if ( fileexist (defconfname) ) /* load default config file */ 241 config = loadconfig (defconfname, config); 242 if ( config == NULL ) 243 fatal ("Out of memory\n"); 244 setglobalflags (config); 245 break; 246 case 'c': 247 config = loadconfig (optarg, config); 248 setglobalflags (config); 249 checkconfig (config); 250 break; 251 case 'O': /* read option from commandline */ 252 config = loadconfig_fromstr (optarg, config); 253 setglobalflags (config); 254 checkconfig (config); 255 break; 256 case 'd': /* ignore directory arg */ 257 dirflag = 1; 258 break; 259 case 'h': /* print no headline */ 260 headerflag = 0; 261 break; 262 case 'k': /* ksk only */ 263 zskflag = 0; 264 break; 265 case 'L': /* ljust */ 266 ljustflag = !ljustflag; 267 break; 268 case 'l': /* label list */ 269 labellist = prepstrlist (optarg, LISTDELIM); 270 if ( labellist == NULL ) 271 fatal ("Out of memory\n"); 272 break; 273 case 'p': /* print path */ 274 pathflag = 1; 275 break; 276 case 'r': /* switch recursive flag */ 277 recflag = !recflag; 278 break; 279 case 't': /* time */ 280 timeflag = !timeflag; 281 break; 282 case 'e': /* expire time */ 283 exptimeflag = !exptimeflag; 284 break; 285 case 'z': /* zsk only */ 286 kskflag = 0; 287 break; 288 case ':': 289 snprintf (str, sizeof(str), "option \"-%c\" requires an argument.\n", 290 optopt); 291 usage (str, config); 292 break; 293 case '?': 294 if ( isprint (optopt) ) 295 snprintf (str, sizeof(str), "Unknown option \"-%c\".\n", 296 optopt); 297 else 298 snprintf (str, sizeof (str), "Unknown option char \\x%x.\n", 299 optopt); 300 usage (str, config); 301 break; 302 default: 303 abort(); 304 } 305 } 306 307 /* it's better to do this before we read the whole directory tree */ 308 if ( action == 'Z' ) 309 { 310 fprintf (stderr, "The use of -Z is deprecated. Please use zkt-conf instead\n"); 311 printconfig ("stdout", config); 312 return 0; 313 } 314 315 if ( kskflag == 0 && zskflag == 0 ) 316 kskflag = zskflag = 1; 317 318 c = optind; 319 do { 320 if ( c >= argc ) /* no args left */ 321 file = config->zonedir; /* use default directory */ 322 else 323 file = argv[c++]; 324 325 if ( is_directory (file) ) 326 parsedirectory (file, &data); 327 else 328 parsefile (file, &data); 329 330 } while ( c < argc ); /* for all arguments */ 331 332 switch ( action ) 333 { 334 case 'H': 335 usage ("", config); 336 case 'C': 337 createkey (keyname, data, config); 338 break; 339 case 'P': 340 case 'S': 341 case 'A': 342 case 'D': 343 if ( (dkp = (dki_t*)zkt_search (data, searchtag, keyname)) == NULL ) 344 fatal ("Key with tag %u not found\n", searchtag); 345 else if ( dkp == (void *) 01 ) 346 fatal ("Key with tag %u found multiple times\n", searchtag); 347 if ( (c = dki_setstatus_preservetime (dkp, action)) != 0 ) 348 fatal ("Couldn't change status of key %u: %d\n", searchtag, c); 349 break; 350 case 19: /* remove (rename) key file */ 351 if ( (dkp = (dki_t *)zkt_search (data, searchtag, keyname)) == NULL ) 352 fatal ("Key with tag %u not found\n", searchtag); 353 else if ( dkp == (void *) 01 ) 354 fatal ("Key with tag %u found multiple times\n", searchtag); 355 dki_remove (dkp); 356 break; 357 case 20: /* destroy the key (remove the files!) */ 358 if ( (dkp = (dki_t *)zkt_search (data, searchtag, keyname)) == NULL ) 359 fatal ("Key with tag %u not found\n", searchtag); 360 else if ( dkp == (void *) 01 ) 361 fatal ("Key with tag %u found multiple times\n", searchtag); 362 dki_destroy (dkp); 363 break; 364 case 'R': 365 if ( (dkp = (dki_t *)zkt_search (data, searchtag, keyname)) == NULL ) 366 fatal ("Key with tag %u not found\n", searchtag); 367 else if ( dkp == (void *) 01 ) 368 fatal ("Key with tag %u found multiple times\n", searchtag); 369 if ( (c = dki_setstatus (dkp, action)) != 0 ) 370 fatal ("Couldn't change status of key %u: %d\n", searchtag, c); 371 break; 372 case 's': 373 if ( (dkp = (dki_t *)zkt_search (data, searchtag, keyname)) == NULL ) 374 fatal ("Key with tag %u not found\n", searchtag); 375 else if ( dkp == (void *) 01 ) 376 fatal ("Key with tag %u found multiple times\n", searchtag); 377 dki_prt_dnskey (dkp, stdout); 378 break; 379 case 'K': 380 zkt_list_dnskeys (data); 381 break; 382 case 'T': 383 zkt_list_trustedkeys (data); 384 break; 385 case '1': /* ksk rollover new key */ 386 case '2': /* ksk rollover publish DS */ 387 case '3': /* ksk rollover delete old key */ 388 case '0': /* ksk rollover status */ 389 ksk_roll (kskdomain, action - '0', data, config); 390 break; 391 case 'F': 392 zkt_setkeylifetime (data); 393 /* fall through */ 394 default: 395 zkt_list_keys (data); 396 } 397 398 return 0; 399} 400 401# define sopt_usage(mesg, value) fprintf (stderr, mesg, value) 402#if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG 403# define lopt_usage(mesg, value) fprintf (stderr, mesg, value) 404# define loptstr(lstr, sstr) lstr 405#else 406# define lopt_usage(mesg, value) 407# define loptstr(lstr, sstr) sstr 408#endif 409static void usage (char *mesg, zconf_t *cp) 410{ 411 fprintf (stderr, "Secure DNS Zone Key Tool %s\n", ZKT_VERSION); 412 fprintf (stderr, "\n"); 413 fprintf (stderr, "Show zone config parameter as %s file\n", LOCALCONF_FILE); 414 sopt_usage ("\tusage: %s -Z\n", progname); 415 lopt_usage ("\tusage: %s --zone-config\n", progname); 416 fprintf (stderr, "\n"); 417 fprintf (stderr, "List keys in current or given directory (-r for recursive mode)\n"); 418 sopt_usage ("\tusage: %s [-dhatkzpr] [-c config] [file|dir ...]\n", progname); 419 fprintf (stderr, "\n"); 420 fprintf (stderr, "List public part of keys in DNSKEY RR format\n"); 421 sopt_usage ("\tusage: %s -K [-dhkzr] [-c config] [file|dir ...]\n", progname); 422 lopt_usage ("\tusage: %s --list-dnskeys [-dhkzr] [-c config] [file|dir ...]\n", progname); 423 fprintf (stderr, "\n"); 424 fprintf (stderr, "List keys (output is suitable for trusted-keys section)\n"); 425 sopt_usage ("\tusage: %s -T [-dhzr] [-c config] [file|dir ...]\n", progname); 426 lopt_usage ("\tusage: %s --list-trustedkeys [-dhzr] [-c config] [file|dir ...]\n", progname); 427 fprintf (stderr, "\n"); 428 fprintf (stderr, "Create a new key \n"); 429 sopt_usage ("\tusage: %s -C <name> [-k] [-dpr] [-c config] [dir ...]\n", progname); 430 lopt_usage ("\tusage: %s --create=<name> [-k] [-dpr] [-c config] [dir ...]\n", progname); 431 fprintf (stderr, "\t\tKSK (use -k): %s %d bits\n", dki_algo2str (cp->k_algo), cp->k_bits); 432 fprintf (stderr, "\t\tZSK (default): %s %d bits\n", dki_algo2str (cp->k_algo), cp->z_bits); 433 fprintf (stderr, "\n"); 434 fprintf (stderr, "Change key status of specified key to published, active or depreciated\n"); 435 fprintf (stderr, "\t(<keyspec> := tag | tag:name) \n"); 436 sopt_usage ("\tusage: %s -P|-A|-D <keyspec> [-dr] [-c config] [dir ...]\n", progname); 437 lopt_usage ("\tusage: %s --published=<keyspec> [-dr] [-c config] [dir ...]\n", progname); 438 lopt_usage ("\tusage: %s --active=<keyspec> [-dr] [-c config] [dir ...]\n", progname); 439 lopt_usage ("\tusage: %s --depreciated=<keyspec> [-dr] [-c config] [dir ...]\n", progname); 440 fprintf (stderr, "\n"); 441 fprintf (stderr, "Revoke specified key (<keyspec> := tag | tag:name) \n"); 442 sopt_usage ("\tusage: %s -R <keyspec> [-dr] [-c config] [dir ...]\n", progname); 443 lopt_usage ("\tusage: %s --revoke=<keyspec> [-dr] [-c config] [dir ...]\n", progname); 444 fprintf (stderr, "\n"); 445 fprintf (stderr, "Remove (rename) or destroy (delete) specified key (<keyspec> := tag | tag:name) \n"); 446 lopt_usage ("\tusage: %s --remove=<keyspec> [-dr] [-c config] [dir ...]\n", progname); 447 lopt_usage ("\tusage: %s --destroy=<keyspec> [-dr] [-c config] [dir ...]\n", progname); 448 fprintf (stderr, "\n"); 449 fprintf (stderr, "Initiate a semi-automated KSK rollover"); 450 fprintf (stderr, "('%s -9%s' prints out a short description)\n", progname, loptstr ("|--ksk-rollover", "")); 451 sopt_usage ("\tusage: %s {-1} do.ma.in.\n", progname); 452 lopt_usage ("\tusage: %s {--ksk-roll-phase1|--ksk-newkey} do.ma.in.\n", progname); 453 sopt_usage ("\tusage: %s {-2} do.ma.in.\n", progname); 454 lopt_usage ("\tusage: %s {--ksk-roll-phase2|--ksk-publish} do.ma.in.\n", progname); 455 sopt_usage ("\tusage: %s {-3} do.ma.in.\n", progname); 456 lopt_usage ("\tusage: %s {--ksk-roll-phase3|--ksk-delkey} do.ma.in.\n", progname); 457 sopt_usage ("\tusage: %s {-0} do.ma.in.\n", progname); 458 lopt_usage ("\tusage: %s {--ksk-roll-status|--ksk-status} do.ma.in.\n", progname); 459 fprintf (stderr, "\n"); 460 461 fprintf (stderr, "\n"); 462 fprintf (stderr, "General options \n"); 463 fprintf (stderr, "\t-c file%s", loptstr (", --config=file\n", "")); 464 fprintf (stderr, "\t\t read config from <file> instead of %s\n", CONFIG_FILE); 465 fprintf (stderr, "\t-O optstr%s", loptstr (", --config-option=\"optstr\"\n", "")); 466 fprintf (stderr, "\t\t read config options from commandline\n"); 467 fprintf (stderr, "\t-h%s\t no headline or trusted-key section header/trailer in -T mode\n", loptstr (", --nohead", "\t")); 468 fprintf (stderr, "\t-d%s\t skip directory arguments\n", loptstr (", --directory", "\t")); 469 fprintf (stderr, "\t-L%s\t print the domain name left justified (default: %s)\n", loptstr (", --leftjust", "\t"), ljustflag ? "on": "off"); 470 fprintf (stderr, "\t-l list\t\t print out only zone keys out of the given domain list\n"); 471 fprintf (stderr, "\t-p%s\t show path of keyfile / create key in current directory\n", loptstr (", --path", "\t")); 472 fprintf (stderr, "\t-r%s\t recursive mode on/off (default: %s)\n", loptstr(", --recursive", "\t"), recflag ? "on": "off"); 473 fprintf (stderr, "\t-a%s\t print age of key (default: %s)\n", loptstr (", --age", "\t"), ageflag ? "on": "off"); 474 fprintf (stderr, "\t-t%s\t print key generation time (default: %s)\n", loptstr (", --time", "\t"), 475 timeflag ? "on": "off"); 476 fprintf (stderr, "\t-e%s\t print key expiration time\n", loptstr (", --expire", "\t")); 477 fprintf (stderr, "\t-f%s\t print key lifetime\n", loptstr (", --lifetime", "\t")); 478 fprintf (stderr, "\t-F days%s=days\t set key lifetime\n", loptstr (", --setlifetime", "\t")); 479 fprintf (stderr, "\t-k%s\t key signing keys only\n", loptstr (", --ksk", "\t")); 480 fprintf (stderr, "\t-z%s\t zone signing keys only\n", loptstr (", --zsk", "\t")); 481 if ( mesg && *mesg ) 482 fprintf (stderr, "%s\n", mesg); 483 exit (1); 484} 485 486static void createkey (const char *keyname, const dki_t *list, const zconf_t *conf) 487{ 488 const char *dir = ""; 489 dki_t *dkp; 490 491 if ( keyname == NULL || *keyname == '\0' ) 492 fatal ("Create key: no keyname!"); 493 494 dbg_val2 ("createkey: keyname %s, pathflag = %d\n", keyname, pathflag); 495 /* search for already existent key to get the directory name */ 496 if ( pathflag && (dkp = (dki_t *)zkt_search (list, 0, keyname)) != NULL ) 497 { 498 char path[MAX_PATHSIZE+1]; 499 zconf_t localconf; 500 501 dir = dkp->dname; 502 pathname (path, sizeof (path), dir, LOCALCONF_FILE, NULL); 503 if ( fileexist (path) ) /* load local config file */ 504 { 505 dbg_val ("Load local config file \"%s\"\n", path); 506 memcpy (&localconf, conf, sizeof (zconf_t)); 507 conf = loadconfig (path, &localconf); 508 } 509 } 510 511 if ( zskflag ) 512 dkp = dki_new (dir, keyname, DKI_ZSK, conf->k_algo, conf->z_bits, conf->z_random, conf->z_life / DAYSEC); 513 else 514 dkp = dki_new (dir, keyname, DKI_KSK, conf->k_algo, conf->k_bits, conf->k_random, conf->k_life / DAYSEC); 515 if ( dkp == NULL ) 516 fatal ("Can't create key %s: %s!\n", keyname, dki_geterrstr ()); 517 518 /* create a new key always in state published, which means "standby" for ksk */ 519 dki_setstatus (dkp, DKI_PUB); 520} 521 522static int get_parent_phase (const char *file) 523{ 524 FILE *fp; 525 int phase; 526 527 if ( (fp = fopen (file, "r")) == NULL ) 528 return -1; 529 530 phase = 0; 531 if ( fscanf (fp, "; KSK rollover phase%d", &phase) != 1 ) 532 phase = 0; 533 534 fclose (fp); 535 return phase; 536} 537 538static void ksk_roll (const char *keyname, int phase, const dki_t *list, const zconf_t *conf) 539{ 540 char path[MAX_PATHSIZE+1]; 541 zconf_t localconf; 542 const char *dir; 543 dki_t *keylist; 544 dki_t *dkp; 545 dki_t *standby; 546 int parent_exist; 547 int parent_age; 548 int parent_phase; 549 int parent_propagation; 550 int key_ttl; 551 int ksk; 552 553 if ( phase == 9 ) /* usage */ 554 { 555 fprintf (stderr, "A KSK rollover requires three consecutive steps:\n"); 556 fprintf (stderr, "\n"); 557 fprintf (stderr, "-1%s", loptstr ("|--ksk-roll-phase1 (--ksk-newkey)\n", "")); 558 fprintf (stderr, "\t Create a new KSK.\n"); 559 fprintf (stderr, "\t This step also creates a parent-<domain> file which contains only\n"); 560 fprintf (stderr, "\t the _old_ key. This file will be copied in hierarchical mode\n"); 561 fprintf (stderr, "\t by dnssec-signer to the parent directory as keyset-<domain> file.\n"); 562 fprintf (stderr, "\t Wait until the new keyset is propagated, before going to the next step.\n"); 563 fprintf (stderr, "\n"); 564 fprintf (stderr, "-2%s", loptstr ("|--ksk-roll-phase2 (--ksk-publish)\n", "")); 565 fprintf (stderr, "\t This step creates a parent-<domain> file with the _new_ key only.\n"); 566 fprintf (stderr, "\t Please send this file immediately to the parent (In hierarchical\n"); 567 fprintf (stderr, "\t mode this will be done automatically by the dnssec-signer command).\n"); 568 fprintf (stderr, "\t Then wait until the new DS is generated by the parent and propagated\n"); 569 fprintf (stderr, "\t to all the parent name server, plus the old DS TTL before going to step three.\n"); 570 fprintf (stderr, "\n"); 571 fprintf (stderr, "-3%s", loptstr ("|--ksk-roll-phase3 (--ksk-delkey)\n", "")); 572 fprintf (stderr, "\t Remove (rename) the old KSK and the parent-<domain> file.\n"); 573 fprintf (stderr, "\t You have to manually delete the old KSK (look at file names beginning\n"); 574 fprintf (stderr, "\t with an lower 'k').\n"); 575 fprintf (stderr, "\n"); 576 fprintf (stderr, "-0%s", loptstr ("|--ksk-roll-stat (--ksk-status)\n", "")); 577 fprintf (stderr, "\t Show the current KSK rollover state of a domain.\n"); 578 579 fprintf (stderr, "\n"); 580 581 return; 582 } 583 584 if ( keyname == NULL || *keyname == '\0' ) 585 fatal ("ksk rollover: no domain!"); 586 587 dbg_val2 ("ksk_roll: keyname %s, phase = %d\n", keyname, phase); 588 589 /* search for already existent key to get the directory name */ 590 if ( (keylist = (dki_t *)zkt_search (list, 0, keyname)) == NULL ) 591 fatal ("ksk rollover: domain %s not found!\n", keyname); 592 dkp = keylist; 593 594 /* try to read local config file */ 595 dir = dkp->dname; 596 pathname (path, sizeof (path), dir, LOCALCONF_FILE, NULL); 597 if ( fileexist (path) ) /* load local config file */ 598 { 599 dbg_val ("Load local config file \"%s\"\n", path); 600 memcpy (&localconf, conf, sizeof (zconf_t)); 601 conf = loadconfig (path, &localconf); 602 } 603 key_ttl = conf->key_ttl; 604 605 /* check if parent-file already exist */ 606 pathname (path, sizeof (path), dir, "parent-", keyname); 607 parent_phase = parent_age = 0; 608 if ( (parent_exist = fileexist (path)) != 0 ) 609 { 610 parent_phase = get_parent_phase (path); 611 parent_age = file_age (path); 612 } 613 // parent_propagation = 2 * DAYSEC; 614 parent_propagation = 5 * MINSEC; 615 616 ksk = 0; /* count active(!) key signing keys */ 617 standby = NULL; /* find standby key if available */ 618 for ( dkp = keylist; dkp; dkp = dkp->next ) 619 if ( dki_isksk (dkp) ) 620 { 621 if ( dki_status (dkp) == DKI_ACT ) 622 ksk++; 623 else if ( dki_status (dkp) == DKI_PUB ) 624 standby = dkp; 625 } 626 627 switch ( phase ) 628 { 629 case 0: /* print status (debug) */ 630 fprintf (stdout, "ksk_rollover:\n"); 631 fprintf (stdout, "\t domain = %s\n", keyname); 632 fprintf (stdout, "\t phase = %d\n", parent_phase); 633 fprintf (stdout, "\t parent_file %s %s\n", path, parent_exist ? "exist": "not exist"); 634 if ( parent_exist ) 635 fprintf (stdout, "\t age of parent_file %d %s\n", parent_age, str_delspace (age2str (parent_age))); 636 fprintf (stdout, "\t # of active key signing keys %d\n", ksk); 637 fprintf (stdout, "\t parent_propagation %d %s\n", parent_propagation, str_delspace (age2str (parent_propagation))); 638 fprintf (stdout, "\t keys ttl %d %s\n", key_ttl, age2str (key_ttl)); 639 640 for ( dkp = keylist; dkp; dkp = dkp->next ) 641 { 642 /* TODO: Nur zum testen */ 643 dki_prt_dnskey (dkp, stdout); 644 } 645 break; 646 case 1: 647 if ( parent_exist || ksk > 1 ) 648 fatal ("Can\'t create new ksk because there is already an ksk rollover in progress\n"); 649 650 fprintf (stdout, "create new ksk \n"); 651 dkp = dki_new (dir, keyname, DKI_KSK, conf->k_algo, conf->k_bits, conf->k_random, conf->k_life / DAYSEC); 652 if ( dkp == NULL ) 653 fatal ("Can't create key %s: %s!\n", keyname, dki_geterrstr ()); 654 if ( standby ) 655 { 656 dki_setstatus (standby, DKI_ACT); /* activate standby key */ 657 dki_setstatus (dkp, DKI_PUB); /* new key will be the new standby */ 658 } 659 660 // dkp = keylist; /* use old key to create the parent file */ 661 if ( (dkp = (dki_t *)dki_findalgo (keylist, 1, conf->k_algo, 'a', 1)) == NULL ) /* find the oldest active ksk to create the parent file */ 662 fatal ("ksk_rollover phase1: Couldn't find the old active key\n"); 663 if ( !create_parent_file (path, phase, key_ttl, dkp) ) 664 fatal ("Couldn't create parentfile %s\n", path); 665 break; 666 667 case 2: 668 if ( ksk < 2 ) 669 fatal ("Can\'t publish new key because no one exist\n"); 670 if ( !parent_exist ) 671 fatal ("More than one KSK but no parent file found!\n"); 672 if ( parent_phase != 1 ) 673 fatal ("Parent file exists but is in wrong state (phase = %d)\n", parent_phase); 674 if ( parent_age < conf->proptime + key_ttl ) 675 fatal ("ksk_rollover (phase2): you have to wait for the propagation of the new KSK (at least %dsec or %s)\n", 676 conf->proptime + key_ttl - parent_age, 677 str_delspace (age2str (conf->proptime + key_ttl - parent_age))); 678 679 fprintf (stdout, "save new ksk in parent file\n"); 680 dkp = keylist->next; /* set dkp to new ksk */ 681 if ( !create_parent_file (path, phase, key_ttl, dkp) ) 682 fatal ("Couldn't create parentfile %s\n", path); 683 break; 684 case 3: 685 if ( !parent_exist || ksk < 2 ) 686 fatal ("ksk-delkey only allowed after ksk-publish\n"); 687 if ( parent_phase != 2 ) 688 fatal ("Parent file exists but is in wrong state (phase = %d)\n", parent_phase); 689 if ( parent_age < parent_propagation + key_ttl ) 690 fatal ("ksk_rollover (phase3): you have to wait for DS propagation (at least %dsec or %s)\n", 691 parent_propagation + key_ttl - parent_age, 692 str_delspace (age2str (parent_propagation + key_ttl - parent_age))); 693 /* remove the parentfile */ 694 fprintf (stdout, "remove parentfile \n"); 695 unlink (path); 696 /* remove or rename the old key */ 697 fprintf (stdout, "old ksk renamed \n"); 698 dkp = keylist; /* set dkp to old ksk */ 699 dki_remove (dkp); 700 break; 701 default: assert (phase == 1 || phase == 2 || phase == 3); 702 } 703} 704 705/***************************************************************** 706** create_parent_file () 707*****************************************************************/ 708static int create_parent_file (const char *fname, int phase, int ttl, const dki_t *dkp) 709{ 710 FILE *fp; 711 712 assert ( fname != NULL ); 713 714 if ( dkp == NULL || (phase != 1 && phase != 2) ) 715 return 0; 716 717 if ( (fp = fopen (fname, "w")) == NULL ) 718 fatal ("can\'t create new parentfile \"%s\"\n", fname); 719 720 if ( phase == 1 ) 721 fprintf (fp, "; KSK rollover phase1 (old key)\n"); 722 else 723 fprintf (fp, "; KSK rollover phase2 (new key)\n"); 724 725 dki_prt_dnskeyttl (dkp, fp, ttl); 726 fclose (fp); 727 728 return phase; 729} 730 731static int parsedirectory (const char *dir, dki_t **listp) 732{ 733 dki_t *dkp; 734 DIR *dirp; 735 struct dirent *dentp; 736 char path[MAX_PATHSIZE+1]; 737 738 if ( dirflag ) 739 return 0; 740 741 dbg_val ("directory: opendir(%s)\n", dir); 742 if ( (dirp = opendir (dir)) == NULL ) 743 return 0; 744 745 while ( (dentp = readdir (dirp)) != NULL ) 746 { 747 if ( is_dotfilename (dentp->d_name) ) 748 continue; 749 750 dbg_val ("directory: check %s\n", dentp->d_name); 751 pathname (path, sizeof (path), dir, dentp->d_name, NULL); 752 if ( is_directory (path) && recflag ) 753 { 754 dbg_val ("directory: recursive %s\n", path); 755 parsedirectory (path, listp); 756 } 757 else if ( is_keyfilename (dentp->d_name) ) 758 if ( (dkp = dki_read (dir, dentp->d_name)) ) 759 { 760 // fprintf (stderr, "parsedir: tssearch (%d %s)\n", dkp, dkp->name); 761#if defined (USE_TREE) && USE_TREE 762 dki_tadd (listp, dkp, 1); 763#else 764 dki_add (listp, dkp); 765#endif 766 } 767 } 768 closedir (dirp); 769 return 1; 770} 771 772static void parsefile (const char *file, dki_t **listp) 773{ 774 char path[MAX_PATHSIZE+1]; 775 dki_t *dkp; 776 777 /* file arg contains path ? ... */ 778 file = splitpath (path, sizeof (path), file); /* ... then split of */ 779 780 if ( is_keyfilename (file) ) /* plain file name looks like DNS key file ? */ 781 { 782 if ( (dkp = dki_read (path, file)) ) /* read DNS key file ... */ 783#if defined (USE_TREE) && USE_TREE 784 dki_tadd (listp, dkp, 1); /* ... and add to tree */ 785#else 786 dki_add (listp, dkp); /* ... and add to list */ 787#endif 788 else 789 error ("error parsing %s: (%s)\n", file, dki_geterrstr()); 790 } 791} 792 793static const char *parsetag (const char *str, int *tagp) 794{ 795 const char *p; 796 797 *tagp = 0; 798 while ( isspace (*str) ) /* skip leading ws */ 799 str++; 800 801 p = str; 802 if ( isdigit (*p) ) /* keytag starts with digit */ 803 { 804 sscanf (p, "%u", tagp); /* read keytag as number */ 805 do /* eat up to the end of the number */ 806 p++; 807 while ( isdigit (*p) ); 808 809 if ( *p == ':' ) /* label follows ? */ 810 return p+1; /* return that */ 811 if ( *p == '\0' ) 812 return NULL; /* no label */ 813 } 814 return str; /* return as label string if not a numeric keytag */ 815} 816 817