1/***************************************************************** 2** 3** @(#) zkt-signer.c (c) Jan 2005 - Jan 2010 Holger Zuleger hznet.de 4** 5** A wrapper around the BIND dnssec-signzone command which is able 6** to resign a zone if necessary and doing a zone or key signing key rollover. 7** 8** Copyright (c) 2005 - 2010, Holger Zuleger HZnet. All rights reserved. 9** This software is open source. 10** 11** Redistribution and use in source and binary forms, with or without 12** modification, are permitted provided that the following conditions 13** are met: 14** 15** Redistributions of source code must retain the above copyright notice, 16** this list of conditions and the following disclaimer. 17** 18** Redistributions in binary form must reproduce the above copyright notice, 19** this list of conditions and the following disclaimer in the documentation 20** and/or other materials provided with the distribution. 21** 22** Neither the name of Holger Zuleger HZnet nor the names of its contributors may 23** be used to endorse or promote products derived from this software without 24** specific prior written permission. 25** 26** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 27** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE 30** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36** POSSIBILITY OF SUCH DAMAGE. 37** 38*****************************************************************/ 39 40# include <stdio.h> 41# include <string.h> 42# include <stdlib.h> 43# include <assert.h> 44# include <dirent.h> 45# include <errno.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# include "zconf.h" 57# include "debug.h" 58# include "misc.h" 59# include "ncparse.h" 60# include "nscomm.h" 61# include "soaserial.h" 62# include "zone.h" 63# include "dki.h" 64# include "rollover.h" 65# include "log.h" 66 67#if defined(BIND_VERSION) && BIND_VERSION >= 940 68# define short_options "c:L:V:D:N:o:O:dfHhnrv" 69#else 70# define short_options "c:L:V:D:N:o:O:fHhnrv" 71#endif 72#if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG 73static struct option long_options[] = { 74 {"reload", no_argument, NULL, 'r'}, 75 {"force", no_argument, NULL, 'f'}, 76 {"noexec", no_argument, NULL, 'n'}, 77 {"verbose", no_argument, NULL, 'v'}, 78 {"directory", no_argument, NULL, 'd'}, 79 {"config", required_argument, NULL, 'c'}, 80 {"option", required_argument, NULL, 'O'}, 81 {"config-option", required_argument, NULL, 'O'}, 82 {"logfile", required_argument, NULL, 'L' }, 83 {"view", required_argument, NULL, 'V' }, 84 {"directory", required_argument, NULL, 'D'}, 85 {"named-conf", required_argument, NULL, 'N'}, 86 {"origin", required_argument, NULL, 'o'}, 87#if defined(BIND_VERSION) && BIND_VERSION >= 940 88 {"dynamic", no_argument, NULL, 'd' }, 89#endif 90 {"help", no_argument, NULL, 'h'}, 91 {0, 0, 0, 0} 92}; 93#endif 94 95 96/** function declaration **/ 97static void usage (char *mesg, zconf_t *conf); 98static int add2zonelist (const char *dir, const char *view, const char *zone, const char *file); 99static int parsedir (const char *dir, zone_t **zp, const zconf_t *conf); 100static int dosigning (zone_t *zonelist, zone_t *zp); 101static int check_keydb_timestamp (dki_t *keylist, time_t reftime); 102static int new_keysetfiles (const char *dir, time_t zone_signing_time); 103static int writekeyfile (const char *fname, const dki_t *list, int key_ttl); 104static int sign_zone (const zone_t *zp); 105static void register_key (dki_t *listp, const zconf_t *z); 106static void copy_keyset (const char *dir, const char *domain, const zconf_t *conf); 107 108/** global command line options **/ 109extern int optopt; 110extern int opterr; 111extern int optind; 112extern char *optarg; 113const char *progname; 114static const char *viewname = NULL; 115static const char *logfile = NULL; 116static const char *origin = NULL; 117static const char *namedconf = NULL; 118static const char *dirname = NULL; 119static int verbose = 0; 120static int force = 0; 121static int reloadflag = 0; 122static int noexec = 0; 123static int dynamic_zone = 0; /* dynamic zone ? */ 124static zone_t *zonelist = NULL; /* must be static global because add2zonelist use it */ 125static zconf_t *config; 126 127/** macros **/ 128#define set_bind94_dynzone(dz) ((dz) = 1) 129#define set_bind96_dynzone(dz) ((dz) = 6) 130#define bind94_dynzone(dz) ( (dz) > 0 && (dz) < 6 ) 131#define bind96_dynzone(dz) ( (dz) >= 6 ) 132#define is_defined(str) ( (str) && *(str) ) 133 134int main (int argc, char *const argv[]) 135{ 136 int c; 137 int errcnt; 138#if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG 139 int opt_index; 140#endif 141 char errstr[255+1]; 142 char *p; 143 const char *defconfname; 144 zone_t *zp; 145 146 progname = *argv; 147 if ( (p = strrchr (progname, '/')) ) 148 progname = ++p; 149 150 if ( strncmp (progname, "dnssec-signer", 13) == 0 ) 151 { 152 fprintf (stderr, "The use of dnssec-signer is deprecated, please run zkt-signer instead\n"); 153 viewname = getnameappendix (progname, "dnssec-signer"); 154 } 155 else 156 viewname = getnameappendix (progname, "zkt-signer"); 157 defconfname = getdefconfname (viewname); 158 config = loadconfig ("", (zconf_t *)NULL); /* load build-in config */ 159 if ( fileexist (defconfname) ) /* load default config file */ 160 config = loadconfig (defconfname, config); 161 if ( config == NULL ) 162 fatal ("Couldn't load config: Out of memory\n"); 163 164 zonelist = NULL; 165 opterr = 0; 166#if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG 167 while ( (c = getopt_long (argc, argv, short_options, long_options, &opt_index)) != -1 ) 168#else 169 while ( (c = getopt (argc, argv, short_options)) != -1 ) 170#endif 171 { 172 switch ( c ) 173 { 174 case 'V': /* view name */ 175 viewname = optarg; 176 defconfname = getdefconfname (viewname); 177 if ( fileexist (defconfname) ) /* load default config file */ 178 config = loadconfig (defconfname, config); 179 if ( config == NULL ) 180 fatal ("Out of memory\n"); 181 break; 182 case 'c': /* load config from file */ 183 config = loadconfig (optarg, config); 184 if ( config == NULL ) 185 fatal ("Out of memory\n"); 186 break; 187 case 'O': /* load config option from commandline */ 188 config = loadconfig_fromstr (optarg, config); 189 if ( config == NULL ) 190 fatal ("Out of memory\n"); 191 break; 192 case 'o': 193 origin = optarg; 194 break; 195 case 'N': 196 namedconf = optarg; 197 break; 198 case 'D': 199 dirname = optarg; 200 break; 201 case 'L': /* error log file|directory */ 202 logfile = optarg; 203 break; 204 case 'f': 205 force++; 206 break; 207 case 'H': 208 case 'h': 209 usage (NULL, config); 210 break; 211#if defined(BIND_VERSION) && BIND_VERSION >= 940 212 case 'd': 213# if BIND_VERSION >= 960 214 set_bind96_dynzone (dynamic_zone); 215# else 216 set_bind94_dynzone(dynamic_zone); 217# endif 218 /* dynamic zone requires a name server reload... */ 219 reloadflag = 0; /* ...but "rndc thaw" reloads the zone anyway */ 220 break; 221#endif 222 case 'n': 223 noexec = 1; 224 break; 225 case 'r': 226 if ( !dynamic_zone ) /* dynamic zones don't need a rndc reload (see "-d" */ 227 reloadflag = 1; 228 break; 229 case 'v': 230 verbose++; 231 break; 232 case '?': 233 if ( isprint (optopt) ) 234 snprintf (errstr, sizeof(errstr), 235 "Unknown option \"-%c\".\n", optopt); 236 else 237 snprintf (errstr, sizeof (errstr), 238 "Unknown option char \\x%x.\n", optopt); 239 usage (errstr, config); 240 break; 241 default: 242 abort(); 243 } 244 } 245 dbg_line(); 246 247 /* store some of the commandline parameter in the config structure */ 248 setconfigpar (config, "--view", viewname); 249 setconfigpar (config, "-v", &verbose); 250 setconfigpar (config, "--noexec", &noexec); 251 if ( logfile == NULL ) 252 logfile = config->logfile; 253 254 if ( lg_open (progname, config->syslogfacility, config->sysloglevel, config->zonedir, logfile, config->loglevel) < -1 ) 255 fatal ("Couldn't open logfile %s in dir %s\n", logfile, config->zonedir); 256 257#if defined(DBG) && DBG 258 for ( zp = zonelist; zp; zp = zp->next ) 259 zone_print ("in main: ", zp); 260#endif 261 lg_args (LG_NOTICE, argc, argv); 262 263 /* 1.0rc1: If the ttl for dynamic zones is not known or if it is 0, use sig valid time for this */ 264 if ( config->max_ttl <= 0 || dynamic_zone ) 265 { 266 // config = dupconfig (config); 267 config->max_ttl = config->sigvalidity; 268 } 269 270 271 if ( origin ) /* option -o ? */ 272 { 273 int ret; 274 275 if ( (argc - optind) <= 0 ) /* no arguments left ? */ 276 ret = zone_readdir (".", origin, NULL, &zonelist, config, dynamic_zone); 277 else 278 ret = zone_readdir (".", origin, argv[optind], &zonelist, config, dynamic_zone); 279 280 /* anyway, "delete" all (remaining) arguments */ 281 optind = argc; 282 283 /* complain if nothing could read in */ 284 if ( ret != 1 || zonelist == NULL ) 285 { 286 lg_mesg (LG_FATAL, "\"%s\": couldn't read", origin); 287 fatal ("Couldn't read zone \"%s\"\n", origin); 288 } 289 } 290 if ( namedconf ) /* option -N ? */ 291 { 292 char dir[255+1]; 293 294 memset (dir, '\0', sizeof (dir)); 295 if ( config->zonedir ) 296 strncpy (dir, config->zonedir, sizeof(dir)); 297 if ( !parse_namedconf (namedconf, config->chroot_dir, dir, sizeof (dir), add2zonelist) ) 298 fatal ("Can't read file %s as namedconf file\n", namedconf); 299 if ( zonelist == NULL ) 300 fatal ("No signed zone found in file %s\n", namedconf); 301 } 302 if ( dirname ) /* option -D ? */ 303 { 304 char *dir = strdup (dirname); 305 306 p = dir + strlen (dir); 307 if ( p > dir ) 308 p--; 309 if ( *p == '/' ) 310 *p = '\0'; /* remove trailing path seperator */ 311 312 if ( !parsedir (dir, &zonelist, config) ) 313 fatal ("Can't read directory tree %s\n", dir); 314 if ( zonelist == NULL ) 315 fatal ("No signed zone found in directory tree %s\n", dir); 316 free (dir); 317 } 318 319 /* none of the above: read current directory tree */ 320 if ( zonelist == NULL ) 321 parsedir (config->zonedir, &zonelist, config); 322 323 for ( zp = zonelist; zp; zp = zp->next ) 324 if ( in_strarr (zp->zone, &argv[optind], argc - optind) ) 325 { 326 dosigning (zonelist, zp); 327 verbmesg (1, zp->conf, "\n"); 328 } 329 330 zone_freelist (&zonelist); 331 332 errcnt = lg_geterrcnt (); 333 lg_mesg (LG_NOTICE, "end of run: %d error%s occured", errcnt, errcnt == 1 ? "" : "s"); 334 lg_close (); 335 336 return errcnt < 64 ? errcnt : 64; 337} 338 339# define sopt_usage(mesg, value) fprintf (stderr, mesg, value) 340#if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG 341# define lopt_usage(mesg, value) fprintf (stderr, mesg, value) 342# define loptstr(lstr, sstr) lstr 343#else 344# define lopt_usage(mesg, value) 345# define loptstr(lstr, sstr) sstr 346#endif 347static void usage (char *mesg, zconf_t *conf) 348{ 349 fprintf (stderr, "%s version %s compiled for BIND %d\n", progname, ZKT_VERSION, BIND_VERSION); 350 fprintf (stderr, "ZKT %s\n", ZKT_COPYRIGHT); 351 fprintf (stderr, "\n"); 352 353 fprintf (stderr, "usage: %s [-L] [-V view] [-c file] [-O optstr] ", progname); 354 fprintf (stderr, "[-D directorytree] "); 355 fprintf (stderr, "[-fhnr] [-v [-v]] [zone ...]\n"); 356 357 fprintf (stderr, "usage: %s [-L] [-V view] [-c file] [-O optstr] ", progname); 358 fprintf (stderr, "-N named.conf "); 359 fprintf (stderr, "[-fhnr] [-v [-v]] [zone ...]\n"); 360 361 fprintf (stderr, "usage: %s [-L] [-V view] [-c file] [-O optstr] ", progname); 362 fprintf (stderr, "-o origin "); 363 fprintf (stderr, "[-fhnr] [-v [-v]] [zonefile.signed]\n"); 364 365 fprintf (stderr, "\t-c file%s", loptstr (", --config=file\n", "")); 366 fprintf (stderr, "\t\t read config from <file> instead of %s\n", CONFIG_FILE); 367 fprintf (stderr, "\t-O optstr%s", loptstr (", --config-option=\"optstr\"\n", "")); 368 fprintf (stderr, "\t\t set config options on the commandline\n"); 369 fprintf (stderr, "\t-L file|dir%s", loptstr (", --logfile=file|dir\n", "")); 370 fprintf (stderr, "\t\t specify file or directory for the log output\n"); 371 fprintf (stderr, "\t-V name%s", loptstr (", --view=name\n", "")); 372 fprintf (stderr, "\t\t specify the view name \n"); 373 fprintf (stderr, "\t-D dir%s", loptstr (", --directory=dir\n", "")); 374 fprintf (stderr, "\t\t parse the given directory tree for a list of secure zones \n"); 375 fprintf (stderr, "\t-N file%s", loptstr (", --named-conf=file\n", "")); 376 fprintf (stderr, "\t\t get the list of secure zones out of the named like config file \n"); 377 fprintf (stderr, "\t-o zone%s", loptstr (", --origin=zone", "")); 378 fprintf (stderr, "\tspecify the name of the zone \n"); 379 fprintf (stderr, "\t\t The file to sign should be given as an argument (default is \"%s.signed\")\n", conf->zonefile); 380 fprintf (stderr, "\t-h%s\t print this help\n", loptstr (", --help", "\t")); 381 fprintf (stderr, "\t-f%s\t force re-signing\n", loptstr (", --force", "\t")); 382 fprintf (stderr, "\t-n%s\t no execution of external signing command\n", loptstr (", --noexec", "\t")); 383 // fprintf (stderr, "\t-r%s\t reload zone via <rndc reload zone> (or via the external distribution command)\n", loptstr (", --reload", "\t")); 384 fprintf (stderr, "\t-r%s\t reload zone via %s\n", loptstr (", --reload", "\t"), conf->dist_cmd ? conf->dist_cmd: "rndc"); 385 fprintf (stderr, "\t-v%s\t be verbose (use twice to be very verbose)\n", loptstr (", --verbose", "\t")); 386 387 fprintf (stderr, "\t[zone]\t sign only those zones given as argument\n"); 388 389 fprintf (stderr, "\n"); 390 fprintf (stderr, "\tif neither -D nor -N nor -o is given, the directory tree specified\n"); 391 fprintf (stderr, "\tin the dnssec config file (\"%s\") will be parsed\n", conf->zonedir); 392 393 if ( mesg && *mesg ) 394 fprintf (stderr, "%s\n", mesg); 395 exit (127); 396} 397 398/** fill zonelist with infos coming out of named.conf **/ 399static int add2zonelist (const char *dir, const char *view, const char *zone, const char *file) 400{ 401#ifdef DBG 402 fprintf (stderr, "printzone "); 403 fprintf (stderr, "view \"%s\" " , view); 404 fprintf (stderr, "zone \"%s\" " , zone); 405 fprintf (stderr, "file "); 406 if ( dir && *dir ) 407 fprintf (stderr, "%s/", dir); 408 fprintf (stderr, "%s", file); 409 fprintf (stderr, "\n"); 410#endif 411 dbg_line (); 412 if ( view[0] != '\0' ) /* view found in named.conf */ 413 { 414 if ( viewname == NULL || viewname[0] == '\0' ) /* viewname wasn't set on startup ? */ 415 { 416 dbg_line (); 417 error ("zone \"%s\" in view \"%s\" found in name server config, but no matching view was set on startup\n", zone, view); 418 lg_mesg (LG_ERROR, "\"%s\" in view \"%s\" found in name server config, but no matching view was set on startup", zone, view); 419 return 0; 420 } 421 dbg_line (); 422 if ( strcmp (viewname, view) != 0 ) /* zone is _not_ in current view */ 423 return 0; 424 } 425 return zone_readdir (dir, zone, file, &zonelist, config, dynamic_zone); 426} 427 428static int parsedir (const char *dir, zone_t **zp, const zconf_t *conf) 429{ 430 DIR *dirp; 431 struct dirent *dentp; 432 char path[MAX_PATHSIZE+1]; 433 434 dbg_val ("parsedir: (%s)\n", dir); 435 if ( !is_directory (dir) ) 436 return 0; 437 438 dbg_line (); 439 zone_readdir (dir, NULL, NULL, zp, conf, dynamic_zone); 440 441 dbg_val ("parsedir: opendir(%s)\n", dir); 442 if ( (dirp = opendir (dir)) == NULL ) 443 return 0; 444 445 while ( (dentp = readdir (dirp)) != NULL ) 446 { 447 if ( is_dotfilename (dentp->d_name) ) 448 continue; 449 450 pathname (path, sizeof (path), dir, dentp->d_name, NULL); 451 if ( !is_directory (path) ) 452 continue; 453 454 dbg_val ("parsedir: recursive %s\n", path); 455 parsedir (path, zp, conf); 456 } 457 closedir (dirp); 458 return 1; 459} 460 461static int dosigning (zone_t *zonelist, zone_t *zp) 462{ 463 char path[MAX_PATHSIZE+1]; 464 int err; 465 int newkey; 466 int newkeysetfile; 467 int use_unixtime; 468 time_t currtime; 469 time_t zfile_time; 470 time_t zfilesig_time; 471 char mesg[255+1]; 472 473 verbmesg (1, zp->conf, "parsing zone \"%s\" in dir \"%s\"\n", zp->zone, zp->dir); 474 475 pathname (path, sizeof (path), zp->dir, zp->sfile, NULL); 476 dbg_val("parsezonedir fileexist (%s)\n", path); 477 if ( !fileexist (path) ) 478 { 479 error ("Not a secure zone directory (%s)!\n", zp->dir); 480 lg_mesg (LG_ERROR, "\"%s\": not a secure zone directory (%s)!", zp->zone, zp->dir); 481 return 1; 482 } 483 zfilesig_time = file_mtime (path); 484 485 pathname (path, sizeof (path), zp->dir, zp->file, NULL); 486 dbg_val("parsezonedir fileexist (%s)\n", path); 487 if ( !fileexist (path) ) 488 { 489 error ("No zone file found (%s)!\n", path); 490 lg_mesg (LG_ERROR, "\"%s\": no zone file found (%s)!", zp->zone, path); 491 return 2; 492 } 493 494 zfile_time = file_mtime (path); 495 currtime = time (NULL); 496 497 /* check for domain based logging */ 498 if ( is_defined (zp->conf->logdomaindir) ) /* parameter is not null or empty ? */ 499 { 500 if ( strcmp (zp->conf->logdomaindir, ".") == 0 ) /* current (".") means zone directory */ 501 lg_zone_start (zp->dir, zp->zone); 502 else 503 lg_zone_start (zp->conf->logdomaindir, zp->zone); 504 } 505 506 /* check rfc5011 key signing keys, create new one if necessary */ 507 dbg_msg("parsezonedir check rfc 5011 ksk "); 508 newkey = ksk5011status (&zp->keys, zp->dir, zp->zone, zp->conf); 509 if ( (newkey & 02) != 02 ) /* not a rfc 5011 zone ? */ 510 { 511 verbmesg (2, zp->conf, "\t\t->not a rfc5011 zone, looking for a regular ksk rollover\n"); 512 /* check key signing keys, create new one if necessary */ 513 dbg_msg("parsezonedir check ksk "); 514 newkey |= kskstatus (zonelist, zp); 515 } 516 else 517 newkey &= ~02; /* reset bit 2 */ 518 519 /* check age of zone keys, probably retire (depreciate) or remove old keys */ 520 dbg_msg("parsezonedir check zsk "); 521 newkey += zskstatus (&zp->keys, zp->dir, zp->zone, zp->conf); 522 523 /* check age of "dnskey.db" file against age of keyfiles */ 524 pathname (path, sizeof (path), zp->dir, zp->conf->keyfile, NULL); 525 dbg_val("parsezonedir check_keydb_timestamp (%s)\n", path); 526 if ( !newkey ) 527 newkey = check_keydb_timestamp (zp->keys, file_mtime (path)); 528 529 newkeysetfile = 0; 530#if defined(ALWAYS_CHECK_KEYSETFILES) && ALWAYS_CHECK_KEYSETFILES /* patch from Shane Wegner 15. June 2009 */ 531 /* check if there is a new keyset- file */ 532 if ( !newkey ) 533 newkeysetfile = new_keysetfiles (zp->dir, zfilesig_time); 534#else 535 /* if we work in subdir mode, check if there is a new keyset- file */ 536 if ( !newkey && zp->conf->keysetdir && strcmp (zp->conf->keysetdir, "..") == 0 ) 537 newkeysetfile = new_keysetfiles (zp->dir, zfilesig_time); 538#endif 539 540 /** 541 ** Check if it is time to do a re-sign. This is the case if 542 ** a) the command line flag -f is set, or 543 ** b) new keys are generated, or 544 ** c) we found a new KSK of a delegated domain, or 545 ** d) the "dnskey.db" file is newer than "zone.db" 546 ** e) the "zone.db" is newer than "zone.db.signed" or 547 ** f) "zone.db.signed" is older than the re-sign interval 548 **/ 549 mesg[0] = '\0'; 550 if ( force ) 551 snprintf (mesg, sizeof(mesg), "Option -f"); 552 else if ( newkey ) 553 snprintf (mesg, sizeof(mesg), "Modfied zone key set"); 554 else if ( newkeysetfile ) 555 snprintf (mesg, sizeof(mesg), "Modified KSK in delegated domain"); 556 else if ( file_mtime (path) > zfilesig_time ) 557 snprintf (mesg, sizeof(mesg), "Modified keys"); 558 else if ( zfile_time > zfilesig_time ) 559 snprintf (mesg, sizeof(mesg), "Zone file edited"); 560 else if ( (currtime - zfilesig_time) > zp->conf->resign - (OFFSET) ) 561 snprintf (mesg, sizeof(mesg), "re-signing interval (%s) reached", 562 str_delspace (age2str (zp->conf->resign))); 563 else if ( bind94_dynzone (dynamic_zone) ) 564 snprintf (mesg, sizeof(mesg), "dynamic zone"); 565 566 if ( *mesg ) 567 verbmesg (1, zp->conf, "\tRe-signing necessary: %s\n", mesg); 568 else 569 verbmesg (1, zp->conf, "\tRe-signing not necessary!\n"); 570 571 if ( *mesg ) 572 lg_mesg (LG_NOTICE, "\"%s\": re-signing triggered: %s", zp->zone, mesg); 573 574 dbg_line (); 575 if ( !(force || newkey || newkeysetfile || zfile_time > zfilesig_time || 576 file_mtime (path) > zfilesig_time || 577 (currtime - zfilesig_time) > zp->conf->resign - (OFFSET) || 578 bind94_dynzone (dynamic_zone)) ) 579 { 580 verbmesg (2, zp->conf, "\tCheck if there is a parent file to copy\n"); 581 if ( zp->conf->keysetdir && strcmp (zp->conf->keysetdir, "..") == 0 ) 582 copy_keyset (zp->dir, zp->zone, zp->conf); /* copy the parent- file if it exist */ 583 if ( is_defined (zp->conf->logdomaindir) ) 584 lg_zone_end (); 585 return 0; /* nothing to do */ 586 } 587 588 /* let's start signing the zone */ 589 dbg_line (); 590 591 /* create new "dnskey.db" file */ 592 pathname (path, sizeof (path), zp->dir, zp->conf->keyfile, NULL); 593 verbmesg (1, zp->conf, "\tWriting key file \"%s\"\n", path); 594 if ( !writekeyfile (path, zp->keys, zp->conf->key_ttl) ) 595 { 596 error ("Can't create keyfile %s \n", path); 597 lg_mesg (LG_ERROR, "\"%s\": can't create keyfile %s", zp->zone , path); 598 } 599 600 err = 1; 601 use_unixtime = ( zp->conf->serialform == Unixtime ); 602 dbg_val1 ("Use unixtime = %d\n", use_unixtime); 603#if defined(BIND_VERSION) && BIND_VERSION >= 940 604 if ( !dynamic_zone && !use_unixtime ) /* increment serial number in static zone files */ 605#else 606 if ( !dynamic_zone ) /* increment serial no in static zone files */ 607#endif 608 { 609 pathname (path, sizeof (path), zp->dir, zp->file, NULL); 610 err = 0; 611 if ( noexec == 0 ) 612 { 613 if ( (err = inc_serial (path, use_unixtime)) < 0 ) 614 { 615 error ("could not increment serialno of domain %s in file %s: %s!\n", 616 zp->zone, path, inc_errstr (err)); 617 lg_mesg (LG_ERROR, 618 "zone \"%s\": couldn't increment serialno in file %s: %s", 619 zp->zone, path, inc_errstr (err)); 620 } 621 else 622 verbmesg (1, zp->conf, "\tIncrementing serial number in file \"%s\"\n", path); 623 } 624 else 625 verbmesg (1, zp->conf, "\tIncrementing serial number in file \"%s\"\n", path); 626 } 627 628 /* at last, sign the zone file */ 629 if ( err > 0 ) 630 { 631 time_t timer; 632 633 verbmesg (1, zp->conf, "\tSigning zone \"%s\"\n", zp->zone); 634 logflush (); 635 636 /* dynamic zones uses incremental signing, so we have to */ 637 /* prepare the old (signed) file as new input file */ 638 if ( dynamic_zone ) 639 { 640 char zfile[MAX_PATHSIZE+1]; 641 642 dyn_update_freeze (zp->zone, zp->conf, 1); /* freeze dynamic zone ! */ 643 644 pathname (zfile, sizeof (zfile), zp->dir, zp->file, NULL); 645 pathname (path, sizeof (path), zp->dir, zp->sfile, NULL); 646 if ( filesize (path) == 0L ) /* initial signing request ? */ 647 { 648 verbmesg (1, zp->conf, "\tDynamic Zone signing: Initial signing request: Add DNSKEYs to zonefile\n"); 649 copyfile (zfile, path, zp->conf->keyfile); 650 } 651#if 1 652 else if ( zfile_time > zfilesig_time ) /* zone.db is newer than signed file */ 653 { 654 verbmesg (1, zp->conf, "\tDynamic Zone signing: zone file manually edited: Use it as new input file\n"); 655 copyfile (zfile, path, NULL); 656 } 657#endif 658 verbmesg (1, zp->conf, "\tDynamic Zone signing: copy old signed zone file %s to new input file %s\n", 659 path, zfile); 660 661 if ( newkey ) /* if we have new keys, they should be added to the zone file */ 662 { 663 copyzonefile (path, zfile, zp->conf->keyfile); 664#if 0 665 if ( zp->conf->dist_cmd ) 666 dist_and_reload (zp, 2); /* ... and send to the name server */ 667#endif 668 } 669 else /* else we can do a simple file copy */ 670 copyfile (path, zfile, NULL); 671 } 672 673 timer = start_timer (); 674 if ( (err = sign_zone (zp)) < 0 ) 675 { 676 error ("\tSigning of zone %s failed (%d)!\n", zp->zone, err); 677 lg_mesg (LG_ERROR, "\"%s\": signing failed!", zp->zone); 678 } 679 timer = stop_timer (timer); 680 681 if ( dynamic_zone ) 682 dyn_update_freeze (zp->zone, zp->conf, 0); /* thaw dynamic zone file */ 683 684 if ( err >= 0 ) 685 { 686 const char *tstr = str_delspace (age2str (timer)); 687 688 if ( !tstr || *tstr == '\0' ) 689 tstr = "0s"; 690 verbmesg (1, zp->conf, "\tSigning completed after %s.\n", tstr); 691 } 692 } 693 694 copy_keyset (zp->dir, zp->zone, zp->conf); 695 696 if ( err >= 0 && reloadflag ) 697 { 698 if ( zp->conf->dist_cmd ) 699 dist_and_reload (zp, 1); 700 else 701 reload_zone (zp->zone, zp->conf); 702 703 register_key (zp->keys, zp->conf); 704 } 705 706 if ( is_defined (zp->conf->logdomaindir) ) 707 lg_zone_end (); 708 709 return err; 710} 711 712static void register_key (dki_t *list, const zconf_t *z) 713{ 714 dki_t *dkp; 715 time_t currtime; 716 time_t age; 717 718 assert ( list != NULL ); 719 assert ( z != NULL ); 720 721 currtime = time (NULL); 722 for ( dkp = list; dkp && dki_isksk (dkp); dkp = dkp->next ) 723 { 724 age = dki_age (dkp, currtime); 725#if 0 726 /* announce "new" and active key signing keys */ 727 if ( REG_URL && *REG_URL && dki_status (dkp) == DKI_ACT && age <= z->resign * 4 ) 728 { 729 if ( verbose ) 730 logmesg ("\tRegister new KSK with tag %d for domain %s\n", 731 dkp->tag, dkp->name); 732 } 733#endif 734 } 735} 736 737/* 738 * This function is not working with symbolic links to keyset- files, 739 * because file_mtime() returns the mtime of the underlying file, and *not* 740 * that of the symlink file. 741 * This is bad, because the keyset-file will be newly generated by dnssec-signzone 742 * on every re-signing call. 743 * Instead, in the case of a hierarchical directory structure, we copy the file 744 * (and so we change the timestamp) only if it was modified after the last 745 * generation (checked with cmpfile(), see func sign_zone()). 746 */ 747# define KEYSET_FILE_PFX "keyset-" 748static int new_keysetfiles (const char *dir, time_t zone_signing_time) 749{ 750 DIR *dirp; 751 struct dirent *dentp; 752 char path[MAX_PATHSIZE+1]; 753 int newkeysetfile; 754 755 if ( (dirp = opendir (dir)) == NULL ) 756 return 0; 757 758 newkeysetfile = 0; 759 dbg_val2 ("new_keysetfile (%s, %s)\n", dir, time2str (zone_signing_time, 's')); 760 while ( !newkeysetfile && (dentp = readdir (dirp)) != NULL ) 761 { 762 if ( strncmp (dentp->d_name, KEYSET_FILE_PFX, strlen (KEYSET_FILE_PFX)) != 0 ) 763 continue; 764 765 pathname (path, sizeof (path), dir, dentp->d_name, NULL); 766 dbg_val2 ("newkeysetfile timestamp of %s = %s\n", path, time2str (file_mtime(path), 's')); 767 if ( file_mtime (path) > zone_signing_time ) 768 newkeysetfile = 1; 769 } 770 closedir (dirp); 771 772 return newkeysetfile; 773} 774 775static int check_keydb_timestamp (dki_t *keylist, time_t reftime) 776{ 777 dki_t *key; 778 779 assert ( keylist != NULL ); 780 if ( reftime == 0 ) 781 return 1; 782 783 for ( key = keylist; key; key = key->next ) 784 if ( dki_time (key) > reftime ) 785 return 1; 786 787 return 0; 788} 789 790static int writekeyfile (const char *fname, const dki_t *list, int key_ttl) 791{ 792 FILE *fp; 793 const dki_t *dkp; 794 time_t curr = time (NULL); 795 int ksk; 796 797 if ( (fp = fopen (fname, "w")) == NULL ) 798 return 0; 799 fprintf (fp, ";\n"); 800 fprintf (fp, ";\t!!! Don\'t edit this file by hand.\n"); 801 fprintf (fp, ";\t!!! It will be generated by %s.\n", progname); 802 fprintf (fp, ";\n"); 803 fprintf (fp, ";\t Last generation time %s\n", time2str (curr, 's')); 804 fprintf (fp, ";\n"); 805 806 fprintf (fp, "\n"); 807 fprintf (fp, "; *** List of Key Signing Keys ***\n"); 808 ksk = 1; 809 for ( dkp = list; dkp; dkp = dkp->next ) 810 { 811 if ( ksk && !dki_isksk (dkp) ) 812 { 813 fprintf (fp, "; *** List of Zone Signing Keys ***\n"); 814 ksk = 0; 815 } 816 dki_prt_comment (dkp, fp); 817 dki_prt_dnskeyttl (dkp, fp, key_ttl); 818 putc ('\n', fp); 819 } 820 821 fclose (fp); 822 return 1; 823} 824 825static int sign_zone (const zone_t *zp) 826{ 827 char cmd[2047+1]; 828 char str[1023+1]; 829 char rparam[254+1]; 830 char nsec3param[637+1]; 831 char keysetdir[254+1]; 832 const char *gends; 833 const char *dnskeyksk; 834 const char *pseudo; 835 const char *param; 836 int len; 837 FILE *fp; 838 839 const char *dir; 840 const char *domain; 841 const char *file; 842 const zconf_t *conf; 843 844 assert (zp != NULL); 845 dir = zp->dir; 846 domain = zp->zone; 847 file = zp->file; 848 conf = zp->conf; 849 850 len = 0; 851 str[0] = '\0'; 852 if ( conf->lookaside && conf->lookaside[0] ) 853 len = snprintf (str, sizeof (str), "-l %.250s", conf->lookaside); 854 855 dbg_line(); 856#if defined(BIND_VERSION) && BIND_VERSION >= 940 857 if ( !dynamic_zone && conf->serialform == Unixtime ) 858 snprintf (str+len, sizeof (str) - len, " -N unixtime"); 859#endif 860 861 gends = ""; 862 if ( conf->sig_gends ) 863#if defined(BIND_VERSION) && BIND_VERSION >= 970 864 gends = "-C -g "; 865#else 866 gends = "-g "; 867#endif 868 869 dnskeyksk = ""; 870#if defined(BIND_VERSION) && BIND_VERSION >= 970 871 if ( conf->sig_dnskeyksk ) 872 dnskeyksk = "-x "; 873#endif 874 875 pseudo = ""; 876 if ( conf->sig_pseudo ) 877 pseudo = "-p "; 878 879 param = ""; 880 if ( conf->sig_param && conf->sig_param[0] ) 881 param = conf->sig_param; 882 883 nsec3param[0] = '\0'; 884#if defined(BIND_VERSION) && BIND_VERSION >= 960 885 if ( conf->k_algo == DK_ALGO_NSEC3DSA || conf->k_algo == DK_ALGO_NSEC3RSASHA1 || 886 conf->nsec3 != NSEC3_OFF ) 887 { 888 char salt[510+1]; /* salt has a maximum of 255 bytes == 510 hex nibbles */ 889 const char *update; 890 const char *optout; 891 unsigned int seed; 892 893# if defined(BIND_VERSION) && BIND_VERSION >= 970 894 update = "-u "; /* trailing blank is necessary */ 895# else 896 update = ""; 897# endif 898 if ( conf->nsec3 == NSEC3_OPTOUT ) 899 optout = "-A "; 900 else 901 optout = ""; 902 903 /* static zones can use always a new salt (full zone signing) */ 904 seed = 0L; /* no seed: use mechanism build in gensalt() */ 905 if ( dynamic_zone ) 906 { /* dynamic zones have to reuse the salt on signing */ 907 const dki_t *kp; 908 909 /* use gentime timestamp of ZSK for seeding rand generator */ 910 kp = dki_find (zp->keys, DKI_ZSK, DKI_ACTIVE, 1); 911 assert ( kp != NULL ); 912 if ( kp->gentime ) 913 seed = kp->gentime; 914 else 915 seed = kp->time; 916 } 917 918 if ( gensalt (salt, sizeof (salt), conf->saltbits, seed) ) 919 snprintf (nsec3param, sizeof (nsec3param), "%s%s-3 %s ", update, optout, salt); 920 } 921#endif 922 923 dbg_line(); 924 rparam[0] = '\0'; 925 if ( conf->sig_random && conf->sig_random[0] ) 926 snprintf (rparam, sizeof (rparam), "-r %.250s ", conf->sig_random); 927 928 dbg_line(); 929 keysetdir[0] = '\0'; 930 if ( conf->keysetdir && conf->keysetdir[0] && strcmp (conf->keysetdir, "..") != 0 ) 931 snprintf (keysetdir, sizeof (keysetdir), "-d %.250s ", conf->keysetdir); 932 933 if ( dir == NULL || *dir == '\0' ) 934 dir = "."; 935 936 dbg_line(); 937#if defined(BIND_VERSION) && BIND_VERSION >= 940 938 if ( dynamic_zone ) 939 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", 940 dir, SIGNCMD, param, nsec3param, dnskeyksk, gends, pseudo, rparam, keysetdir, domain, conf->sigvalidity, str, file, file); 941 else 942#endif 943 snprintf (cmd, sizeof (cmd), "cd %s; %s %s %s%s%s%s%s%s-o %s -e +%ld %s %s K*.private 2>&1", 944 dir, SIGNCMD, param, nsec3param, dnskeyksk, gends, pseudo, rparam, keysetdir, domain, conf->sigvalidity, str, file); 945 verbmesg (2, conf, "\t Run cmd \"%s\"\n", cmd); 946 *str = '\0'; 947 if ( noexec == 0 ) 948 { 949#if 0 950 if ( (fp = popen (cmd, "r")) == NULL || fgets (str, sizeof str, fp) == NULL ) 951 return -1; 952#else 953 if ( (fp = popen (cmd, "r")) == NULL ) 954 return -1; 955 str[0] = '\0'; 956 while ( fgets (str, sizeof str, fp) != NULL ) /* eat up all output until the last line */ 957 ; 958#endif 959 pclose (fp); 960 } 961 962 dbg_line(); 963 verbmesg (2, conf, "\t Cmd dnssec-signzone return: \"%s\"\n", str_chop (str, '\n')); 964 len = strlen (str) - 6; 965 if ( len < 0 || strcmp (str+len, "signed") != 0 ) 966 return -1; 967 968 return 0; 969} 970 971static void copy_keyset (const char *dir, const char *domain, const zconf_t *conf) 972{ 973 char fromfile[1024]; 974 char tofile[1024]; 975 int ret; 976 977 /* propagate "keyset"-file to parent dir */ 978 if ( conf->keysetdir && strcmp (conf->keysetdir, "..") == 0 ) 979 { 980 /* check if special parent-file exist (ksk rollover) */ 981 snprintf (fromfile, sizeof (fromfile), "%s/parent-%s", dir, domain); 982 if ( !fileexist (fromfile) ) /* use "normal" keyset-file */ 983 snprintf (fromfile, sizeof (fromfile), "%s/keyset-%s", dir, domain); 984 985 /* verbmesg (2, conf, "\t check \"%s\" against parent dir\n", fromfile); */ 986 snprintf (tofile, sizeof (tofile), "%s/../keyset-%s", dir, domain); 987 if ( cmpfile (fromfile, tofile) != 0 ) 988 { 989 verbmesg (2, conf, "\t copy \"%s\" to parent dir\n", fromfile); 990 if ( (ret = copyfile (fromfile, tofile, NULL)) != 0 ) 991 { 992 error ("Couldn't copy \"%s\" to parent dir (%d:%s)\n", 993 fromfile, ret, strerror(errno)); 994 lg_mesg (LG_ERROR, "\%s\": can't copy \"%s\" to parent dir (%d:%s)", 995 domain, fromfile, ret, strerror(errno)); 996 } 997 } 998 } 999} 1000