Sync usage with man page.
[netbsd-mini2440.git] / external / bsd / bind / dist / contrib / zkt / dnssec-signer.c
blob33cc145ba34981e59889790e38f48c9dfdf6642a
1 /* $NetBSD$ */
3 /*****************************************************************
4 **
5 ** @(#) dnssec-signer.c (c) Jan 2005 Holger Zuleger hznet.de
6 **
7 ** A wrapper around the BIND dnssec-signzone command which is able
8 ** to resign a zone if necessary and doing a zone or key signing key rollover.
9 **
10 ** Copyright (c) 2005 - 2008, Holger Zuleger HZnet. All rights reserved.
11 ** This software is open source.
13 ** Redistribution and use in source and binary forms, with or without
14 ** modification, are permitted provided that the following conditions
15 ** are met:
17 ** Redistributions of source code must retain the above copyright notice,
18 ** this list of conditions and the following disclaimer.
20 ** Redistributions in binary form must reproduce the above copyright notice,
21 ** this list of conditions and the following disclaimer in the documentation
22 ** and/or other materials provided with the distribution.
24 ** Neither the name of Holger Zuleger HZnet nor the names of its contributors may
25 ** be used to endorse or promote products derived from this software without
26 ** specific prior written permission.
28 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
30 ** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
31 ** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
32 ** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
33 ** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
34 ** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
35 ** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
36 ** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
37 ** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38 ** POSSIBILITY OF SUCH DAMAGE.
40 *****************************************************************/
42 # include <stdio.h>
43 # include <string.h>
44 # include <stdlib.h>
45 # include <assert.h>
46 # include <dirent.h>
47 # include <errno.h>
48 # include <unistd.h>
49 # include <ctype.h>
51 #ifdef HAVE_CONFIG_H
52 # include <config.h>
53 #endif
54 # include "config_zkt.h"
55 #if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG
56 # include <getopt.h>
57 #endif
58 # include "zconf.h"
59 # include "debug.h"
60 # include "misc.h"
61 # include "ncparse.h"
62 # include "nscomm.h"
63 # include "soaserial.h"
64 # include "zone.h"
65 # include "dki.h"
66 # include "rollover.h"
67 # include "log.h"
69 #if defined(BIND_VERSION) && BIND_VERSION >= 940
70 # define short_options "c:L:V:D:N:o:O:dfHhnrv"
71 #else
72 # define short_options "c:L:V:D:N:o:O:fHhnrv"
73 #endif
74 #if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG
75 static struct option long_options[] = {
76 {"reload", no_argument, NULL, 'r'},
77 {"force", no_argument, NULL, 'f'},
78 {"noexec", no_argument, NULL, 'n'},
79 {"verbose", no_argument, NULL, 'v'},
80 {"directory", no_argument, NULL, 'd'},
81 {"config", required_argument, NULL, 'c'},
82 {"option", required_argument, NULL, 'O'},
83 {"config-option", required_argument, NULL, 'O'},
84 {"logfile", required_argument, NULL, 'L' },
85 {"view", required_argument, NULL, 'V' },
86 {"directory", required_argument, NULL, 'D'},
87 {"named-conf", required_argument, NULL, 'N'},
88 {"origin", required_argument, NULL, 'o'},
89 #if defined(BIND_VERSION) && BIND_VERSION >= 940
90 {"dynamic", no_argument, NULL, 'd' },
91 #endif
92 {"help", no_argument, NULL, 'h'},
93 {0, 0, 0, 0}
95 #endif
98 /** function declaration **/
99 static void usage (char *mesg, zconf_t *conf);
100 static int add2zonelist (const char *dir, const char *view, const char *zone, const char *file);
101 static int parsedir (const char *dir, zone_t **zp, const zconf_t *conf);
102 static int dosigning (zone_t *zonelist, zone_t *zp);
103 static int check_keydb_timestamp (dki_t *keylist, time_t reftime);
104 static int new_keysetfiles (const char *dir, time_t zone_signing_time);
105 static int writekeyfile (const char *fname, const dki_t *list, int key_ttl);
106 static int sign_zone (const char *dir, const char *domain, const char *file, const zconf_t *conf);
107 static void register_key (dki_t *listp, const zconf_t *z);
108 static void copy_keyset (const char *dir, const char *domain, const zconf_t *conf);
110 /** global command line options **/
111 extern int optopt;
112 extern int opterr;
113 extern int optind;
114 extern char *optarg;
115 const char *progname;
116 static const char *viewname = NULL;
117 static const char *logfile = NULL;
118 static const char *origin = NULL;
119 static const char *namedconf = NULL;
120 static const char *dirname = NULL;
121 static int verbose = 0;
122 static int force = 0;
123 static int reloadflag = 0;
124 static int noexec = 0;
125 static int dynamic_zone = 0; /* dynamic zone ? */
126 static zone_t *zonelist = NULL; /* must be static global because add2zonelist use it */
127 static zconf_t *config;
129 /** macros **/
130 #define set_bind94_dynzone(dz) ((dz) = 1)
131 #define set_bind96_dynzone(dz) ((dz) = 6)
132 #define bind94_dynzone(dz) ( (dz) > 0 && (dz) < 6 )
133 #define bind96_dynzone(dz) ( (dz) >= 6 )
135 int main (int argc, char *const argv[])
137 int c;
138 int errcnt;
139 #if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG
140 int opt_index;
141 #endif
142 char errstr[255+1];
143 char *p;
144 const char *defconfname;
145 zone_t *zp;
147 progname = *argv;
148 if ( (p = strrchr (progname, '/')) )
149 progname = ++p;
150 viewname = getnameappendix (progname, "dnssec-signer");
152 defconfname = getdefconfname (viewname);
153 config = loadconfig ("", (zconf_t *)NULL); /* load build-in config */
154 if ( fileexist (defconfname) ) /* load default config file */
155 config = loadconfig (defconfname, config);
156 if ( config == NULL )
157 fatal ("Couldn't load config: Out of memory\n");
159 zonelist = NULL;
160 opterr = 0;
161 #if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG
162 while ( (c = getopt_long (argc, argv, short_options, long_options, &opt_index)) != -1 )
163 #else
164 while ( (c = getopt (argc, argv, short_options)) != -1 )
165 #endif
167 switch ( c )
169 case 'V': /* view name */
170 viewname = optarg;
171 defconfname = getdefconfname (viewname);
172 if ( fileexist (defconfname) ) /* load default config file */
173 config = loadconfig (defconfname, config);
174 if ( config == NULL )
175 fatal ("Out of memory\n");
176 break;
177 case 'c': /* load config from file */
178 config = loadconfig (optarg, config);
179 if ( config == NULL )
180 fatal ("Out of memory\n");
181 break;
182 case 'O': /* load config option from commandline */
183 config = loadconfig_fromstr (optarg, config);
184 if ( config == NULL )
185 fatal ("Out of memory\n");
186 break;
187 case 'o':
188 origin = optarg;
189 break;
190 case 'N':
191 namedconf = optarg;
192 break;
193 case 'D':
194 dirname = optarg;
195 break;
196 case 'L': /* error log file|directory */
197 logfile = optarg;
198 break;
199 case 'f':
200 force++;
201 break;
202 case 'H':
203 case 'h':
204 usage (NULL, config);
205 break;
206 #if defined(BIND_VERSION) && BIND_VERSION >= 940
207 case 'd':
208 # if BIND_VERSION >= 960
209 set_bind96_dynzone (dynamic_zone);
210 # else
211 set_bind94_dynzone(dynamic_zone);
212 # endif
213 /* dynamic zone requires a name server reload... */
214 reloadflag = 0; /* ...but "rndc thaw" reloads the zone anyway */
215 break;
216 #endif
217 case 'n':
218 noexec = 1;
219 break;
220 case 'r':
221 if ( !dynamic_zone ) /* dynamic zones don't need a rndc reload (see "-d" */
222 reloadflag = 1;
223 break;
224 case 'v':
225 verbose++;
226 break;
227 case '?':
228 if ( isprint (optopt) )
229 snprintf (errstr, sizeof(errstr),
230 "Unknown option \"-%c\".\n", optopt);
231 else
232 snprintf (errstr, sizeof (errstr),
233 "Unknown option char \\x%x.\n", optopt);
234 usage (errstr, config);
235 break;
236 default:
237 abort();
240 dbg_line();
242 /* store some of the commandline parameter in the config structure */
243 setconfigpar (config, "--view", viewname);
244 setconfigpar (config, "-v", &verbose);
245 setconfigpar (config, "--noexec", &noexec);
246 if ( logfile == NULL )
247 logfile = config->logfile;
249 if ( lg_open (progname, config->syslogfacility, config->sysloglevel, config->zonedir, logfile, config->loglevel) < -1 )
250 fatal ("Couldn't open logfile %s in dir %s\n", logfile, config->zonedir);
252 #if defined(DBG) && DBG
253 for ( zp = zonelist; zp; zp = zp->next )
254 zone_print ("in main: ", zp);
255 #endif
256 lg_args (LG_NOTICE, argc, argv);
258 if ( origin ) /* option -o ? */
260 int ret;
262 if ( (argc - optind) <= 0 ) /* no arguments left ? */
263 ret = zone_readdir (".", origin, NULL, &zonelist, config, dynamic_zone);
264 else
265 ret = zone_readdir (".", origin, argv[optind], &zonelist, config, dynamic_zone);
267 /* anyway, "delete" all (remaining) arguments */
268 optind = argc;
270 /* complain if nothing could read in */
271 if ( ret != 1 || zonelist == NULL )
273 lg_mesg (LG_FATAL, "\"%s\": couldn't read", origin);
274 fatal ("Couldn't read zone \"%s\"\n", origin);
277 if ( namedconf ) /* option -N ? */
279 char dir[255+1];
281 memset (dir, '\0', sizeof (dir));
282 if ( config->zonedir )
283 strncpy (dir, config->zonedir, sizeof(dir));
284 if ( !parse_namedconf (namedconf, config->chroot_dir, dir, sizeof (dir), add2zonelist) )
285 fatal ("Can't read file %s as namedconf file\n", namedconf);
286 if ( zonelist == NULL )
287 fatal ("No signed zone found in file %s\n", namedconf);
289 if ( dirname ) /* option -D ? */
291 char *dir = strdup (dirname);
293 p = dir + strlen (dir);
294 if ( p > dir )
295 p--;
296 if ( *p == '/' )
297 *p = '\0'; /* remove trailing path seperator */
299 if ( !parsedir (dir, &zonelist, config) )
300 fatal ("Can't read directory tree %s\n", dir);
301 if ( zonelist == NULL )
302 fatal ("No signed zone found in directory tree %s\n", dir);
303 free (dir);
306 /* none of the above: read current directory tree */
307 if ( zonelist == NULL )
308 parsedir (config->zonedir, &zonelist, config);
310 for ( zp = zonelist; zp; zp = zp->next )
311 if ( in_strarr (zp->zone, &argv[optind], argc - optind) )
313 dosigning (zonelist, zp);
314 verbmesg (1, zp->conf, "\n");
317 zone_freelist (&zonelist);
319 errcnt = lg_geterrcnt ();
320 lg_mesg (LG_NOTICE, "end of run: %d error%s occured", errcnt, errcnt == 1 ? "" : "s");
321 lg_close ();
323 return errcnt < 64 ? errcnt : 64;
326 # define sopt_usage(mesg, value) fprintf (stderr, mesg, value)
327 #if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG
328 # define lopt_usage(mesg, value) fprintf (stderr, mesg, value)
329 # define loptstr(lstr, sstr) lstr
330 #else
331 # define lopt_usage(mesg, value)
332 # define loptstr(lstr, sstr) sstr
333 #endif
334 static void usage (char *mesg, zconf_t *conf)
336 fprintf (stderr, "%s version %s\n", progname, ZKT_VERSION);
337 fprintf (stderr, "\n");
339 fprintf (stderr, "usage: %s [-c file] [-O optstr] ", progname);
340 fprintf (stderr, "[-D directorytree] ");
341 fprintf (stderr, "[-fhnr] [-v [-v]] [zone ...]\n");
343 fprintf (stderr, "usage: %s [-c file] [-O optstr] ", progname);
344 fprintf (stderr, "-N named.conf ");
345 fprintf (stderr, "[-fhnr] [-v [-v]] [zone ...]\n");
347 fprintf (stderr, "usage: %s [-c file] [-O optstr] ", progname);
348 fprintf (stderr, "-o origin ");
349 fprintf (stderr, "[-fhnr] [-v [-v]] [zonefile.signed]\n");
351 fprintf (stderr, "\t-c file%s", loptstr (", --config=file\n", ""));
352 fprintf (stderr, "\t\t read config from <file> instead of %s\n", CONFIG_FILE);
353 fprintf (stderr, "\t-O optstr%s", loptstr (", --config-option=\"optstr\"\n", ""));
354 fprintf (stderr, "\t\t set config options on the commandline\n");
355 fprintf (stderr, "\t-L file|dir%s", loptstr (", --logfile=file|dir\n", ""));
356 fprintf (stderr, "\t\t specify file or directory for the log output\n");
357 fprintf (stderr, "\t-D dir%s", loptstr (", --directory=dir\n", ""));
358 fprintf (stderr, "\t\t parse the given directory tree for a list of secure zones \n");
359 fprintf (stderr, "\t-N file%s", loptstr (", --named-conf=file\n", ""));
360 fprintf (stderr, "\t\t get the list of secure zones out of the named like config file \n");
361 fprintf (stderr, "\t-o zone%s", loptstr (", --origin=zone", ""));
362 fprintf (stderr, "\tspecify the name of the zone \n");
363 fprintf (stderr, "\t\t The file to sign should be given as an argument (default is \"%s.signed\")\n", conf->zonefile);
364 fprintf (stderr, "\t-h%s\t print this help\n", loptstr (", --help", "\t"));
365 fprintf (stderr, "\t-f%s\t force re-signing\n", loptstr (", --force", "\t"));
366 fprintf (stderr, "\t-n%s\t no execution of external signing command\n", loptstr (", --noexec", "\t"));
367 // fprintf (stderr, "\t-r%s\t reload zone via <rndc reload zone> (or via the external distribution command)\n", loptstr (", --reload", "\t"));
368 fprintf (stderr, "\t-r%s\t reload zone via %s\n", loptstr (", --reload", "\t"), conf->dist_cmd ? conf->dist_cmd: "rndc");
369 fprintf (stderr, "\t-v%s\t be verbose (use twice to be very verbose)\n", loptstr (", --verbose", "\t"));
371 fprintf (stderr, "\t[zone]\t sign only those zones given as argument\n");
373 fprintf (stderr, "\n");
374 fprintf (stderr, "\tif neither -D nor -N nor -o is given, the directory tree specified\n");
375 fprintf (stderr, "\tin the dnssec config file (\"%s\") will be parsed\n", conf->zonedir);
377 if ( mesg && *mesg )
378 fprintf (stderr, "%s\n", mesg);
379 exit (127);
382 /** fill zonelist with infos coming out of named.conf **/
383 static int add2zonelist (const char *dir, const char *view, const char *zone, const char *file)
385 #ifdef DBG
386 fprintf (stderr, "printzone ");
387 fprintf (stderr, "view \"%s\" " , view);
388 fprintf (stderr, "zone \"%s\" " , zone);
389 fprintf (stderr, "file ");
390 if ( dir && *dir )
391 fprintf (stderr, "%s/", dir);
392 fprintf (stderr, "%s", file);
393 fprintf (stderr, "\n");
394 #endif
395 dbg_line ();
396 if ( view[0] != '\0' ) /* view found in named.conf */
398 if ( viewname == NULL || viewname[0] == '\0' ) /* viewname wasn't set on startup ? */
400 dbg_line ();
401 error ("zone \"%s\" in view \"%s\" found in name server config, but no matching view was set on startup\n", zone, view);
402 lg_mesg (LG_ERROR, "\"%s\" in view \"%s\" found in name server config, but no matching view was set on startup", zone, view);
403 return 0;
405 dbg_line ();
406 if ( strcmp (viewname, view) != 0 ) /* zone is _not_ in current view */
407 return 0;
409 return zone_readdir (dir, zone, file, &zonelist, config, dynamic_zone);
412 static int parsedir (const char *dir, zone_t **zp, const zconf_t *conf)
414 DIR *dirp;
415 struct dirent *dentp;
416 char path[MAX_PATHSIZE+1];
418 dbg_val ("parsedir: (%s)\n", dir);
419 if ( !is_directory (dir) )
420 return 0;
422 dbg_line ();
423 zone_readdir (dir, NULL, NULL, zp, conf, dynamic_zone);
425 dbg_val ("parsedir: opendir(%s)\n", dir);
426 if ( (dirp = opendir (dir)) == NULL )
427 return 0;
429 while ( (dentp = readdir (dirp)) != NULL )
431 if ( is_dotfilename (dentp->d_name) )
432 continue;
434 pathname (path, sizeof (path), dir, dentp->d_name, NULL);
435 if ( !is_directory (path) )
436 continue;
438 dbg_val ("parsedir: recursive %s\n", path);
439 parsedir (path, zp, conf);
441 closedir (dirp);
442 return 1;
445 static int dosigning (zone_t *zonelist, zone_t *zp)
447 char path[MAX_PATHSIZE+1];
448 int err;
449 int newkey;
450 int newkeysetfile;
451 int use_unixtime;
452 time_t currtime;
453 time_t zfile_time;
454 time_t zfilesig_time;
455 char mesg[255+1];
457 verbmesg (1, zp->conf, "parsing zone \"%s\" in dir \"%s\"\n", zp->zone, zp->dir);
459 pathname (path, sizeof (path), zp->dir, zp->sfile, NULL);
460 dbg_val("parsezonedir fileexist (%s)\n", path);
461 if ( !fileexist (path) )
463 error ("Not a secure zone directory (%s)!\n", zp->dir);
464 lg_mesg (LG_ERROR, "\"%s\": not a secure zone directory (%s)!", zp->zone, zp->dir);
465 return 1;
467 zfilesig_time = file_mtime (path);
469 pathname (path, sizeof (path), zp->dir, zp->file, NULL);
470 dbg_val("parsezonedir fileexist (%s)\n", path);
471 if ( !fileexist (path) )
473 error ("No zone file found (%s)!\n", path);
474 lg_mesg (LG_ERROR, "\"%s\": no zone file found (%s)!", zp->zone, path);
475 return 2;
478 zfile_time = file_mtime (path);
479 currtime = time (NULL);
481 /* check rfc5011 key signing keys, create new one if necessary */
482 dbg_msg("parsezonedir check rfc 5011 ksk ");
483 newkey = ksk5011status (&zp->keys, zp->dir, zp->zone, zp->conf);
484 if ( (newkey & 02) != 02 ) /* not a rfc 5011 zone ? */
486 verbmesg (2, zp->conf, "\t\t->not a rfc5011 zone, looking for a regular ksk rollover\n");
487 /* check key signing keys, create new one if necessary */
488 dbg_msg("parsezonedir check ksk ");
489 newkey |= kskstatus (zonelist, zp);
491 else
492 newkey &= ~02; /* reset bit 2 */
494 /* check age of zone keys, probably retire (depreciate) or remove old keys */
495 dbg_msg("parsezonedir check zsk ");
496 newkey += zskstatus (&zp->keys, zp->dir, zp->zone, zp->conf);
498 /* check age of "dnskey.db" file against age of keyfiles */
499 pathname (path, sizeof (path), zp->dir, zp->conf->keyfile, NULL);
500 dbg_val("parsezonedir check_keydb_timestamp (%s)\n", path);
501 if ( !newkey )
502 newkey = check_keydb_timestamp (zp->keys, file_mtime (path));
504 newkeysetfile = 0;
505 #if defined(ALWAYS_CHECK_KEYSETFILES) && ALWAYS_CHECK_KEYSETFILES /* patch from Shane Wegner 15. June 2009 */
506 /* check if there is a new keyset- file */
507 if ( !newkey )
508 newkeysetfile = new_keysetfiles (zp->dir, zfilesig_time);
509 #else
510 /* if we work in subdir mode, check if there is a new keyset- file */
511 if ( !newkey && zp->conf->keysetdir && strcmp (zp->conf->keysetdir, "..") == 0 )
512 newkeysetfile = new_keysetfiles (zp->dir, zfilesig_time);
513 #endif
516 ** Check if it is time to do a re-sign. This is the case if
517 ** a) the command line flag -f is set, or
518 ** b) new keys are generated, or
519 ** c) we found a new KSK of a delegated domain, or
520 ** d) the "dnskey.db" file is newer than "zone.db"
521 ** e) the "zone.db" is newer than "zone.db.signed" or
522 ** f) "zone.db.signed" is older than the re-sign interval
524 mesg[0] = '\0';
525 if ( force )
526 snprintf (mesg, sizeof(mesg), "Option -f");
527 else if ( newkey )
528 snprintf (mesg, sizeof(mesg), "Modfied zone key set");
529 else if ( newkeysetfile )
530 snprintf (mesg, sizeof(mesg), "Modified KSK in delegated domain");
531 else if ( file_mtime (path) > zfilesig_time )
532 snprintf (mesg, sizeof(mesg), "Modified keys");
533 else if ( zfile_time > zfilesig_time )
534 snprintf (mesg, sizeof(mesg), "Zone file edited");
535 else if ( (currtime - zfilesig_time) > zp->conf->resign - (OFFSET) )
536 snprintf (mesg, sizeof(mesg), "re-signing interval (%s) reached",
537 str_delspace (age2str (zp->conf->resign)));
538 else if ( bind94_dynzone (dynamic_zone) )
539 snprintf (mesg, sizeof(mesg), "dynamic zone");
541 if ( *mesg )
542 verbmesg (1, zp->conf, "\tRe-signing necessary: %s\n", mesg);
543 else
544 verbmesg (1, zp->conf, "\tRe-signing not necessary!\n");
546 if ( *mesg )
547 lg_mesg (LG_NOTICE, "\"%s\": re-signing triggered: %s", zp->zone, mesg);
549 dbg_line ();
550 if ( !(force || newkey || newkeysetfile || zfile_time > zfilesig_time ||
551 file_mtime (path) > zfilesig_time ||
552 (currtime - zfilesig_time) > zp->conf->resign - (OFFSET) ||
553 bind94_dynzone (dynamic_zone)) )
555 verbmesg (2, zp->conf, "\tCheck if there is a parent file to copy\n");
556 if ( zp->conf->keysetdir && strcmp (zp->conf->keysetdir, "..") == 0 )
557 copy_keyset (zp->dir, zp->zone, zp->conf); /* copy the parent- file if it exist */
558 return 0; /* nothing to do */
561 /* let's start signing the zone */
562 dbg_line ();
564 /* create new "dnskey.db" file */
565 pathname (path, sizeof (path), zp->dir, zp->conf->keyfile, NULL);
566 verbmesg (1, zp->conf, "\tWriting key file \"%s\"\n", path);
567 if ( !writekeyfile (path, zp->keys, zp->conf->key_ttl) )
569 error ("Can't create keyfile %s \n", path);
570 lg_mesg (LG_ERROR, "\"%s\": can't create keyfile %s", zp->zone , path);
573 err = 1;
574 use_unixtime = ( zp->conf->serialform == Unixtime );
575 dbg_val1 ("Use unixtime = %d\n", use_unixtime);
576 #if defined(BIND_VERSION) && BIND_VERSION >= 940
577 if ( !dynamic_zone && !use_unixtime ) /* increment serial number in static zone files */
578 #else
579 if ( !dynamic_zone ) /* increment serial no in static zone files */
580 #endif
582 pathname (path, sizeof (path), zp->dir, zp->file, NULL);
583 err = 0;
584 if ( noexec == 0 )
586 if ( (err = inc_serial (path, use_unixtime)) < 0 )
588 error ("could not increment serialno of domain %s in file %s: %s!\n",
589 zp->zone, path, inc_errstr (err));
590 lg_mesg (LG_ERROR,
591 "zone \"%s\": couldn't increment serialno in file %s: %s",
592 zp->zone, path, inc_errstr (err));
594 else
595 verbmesg (1, zp->conf, "\tIncrementing serial number in file \"%s\"\n", path);
597 else
598 verbmesg (1, zp->conf, "\tIncrementing serial number in file \"%s\"\n", path);
601 /* at last, sign the zone file */
602 if ( err > 0 )
604 time_t timer;
606 verbmesg (1, zp->conf, "\tSigning zone \"%s\"\n", zp->zone);
607 logflush ();
609 /* dynamic zones uses incremental signing, so we have to */
610 /* prepare the old (signed) file as new input file */
611 if ( dynamic_zone )
613 char zfile[MAX_PATHSIZE+1];
615 dyn_update_freeze (zp->zone, zp->conf, 1); /* freeze dynamic zone ! */
617 pathname (zfile, sizeof (zfile), zp->dir, zp->file, NULL);
618 pathname (path, sizeof (path), zp->dir, zp->sfile, NULL);
619 if ( filesize (path) == 0L ) /* initial signing request ? */
621 verbmesg (1, zp->conf, "\tDynamic Zone signing: Initial signing request: Add DNSKEYs to zonefile\n");
622 copyfile (zfile, path, zp->conf->keyfile);
624 #if 1
625 else if ( zfile_time > zfilesig_time ) /* zone.db is newer than signed file */
627 verbmesg (1, zp->conf, "\tDynamic Zone signing: zone file manually edited: Use it as new input file\n");
628 copyfile (zfile, path, NULL);
630 #endif
631 verbmesg (1, zp->conf, "\tDynamic Zone signing: copy old signed zone file %s to new input file %s\n",
632 path, zfile);
634 if ( newkey ) /* if we have new keys, they should be added to the zone file */
635 copyzonefile (path, zfile, zp->conf->keyfile);
636 else /* else we can do a simple file copy */
637 copyfile (path, zfile, NULL);
640 timer = start_timer ();
641 if ( (err = sign_zone (zp->dir, zp->zone, zp->file, zp->conf)) < 0 )
643 error ("\tSigning of zone %s failed (%d)!\n", zp->zone, err);
644 lg_mesg (LG_ERROR, "\"%s\": signing failed!", zp->zone);
646 timer = stop_timer (timer);
648 if ( dynamic_zone )
649 dyn_update_freeze (zp->zone, zp->conf, 0); /* thaw dynamic zone file */
651 if ( err >= 0 )
653 const char *tstr = str_delspace (age2str (timer));
655 if ( !tstr || *tstr == '\0' )
656 tstr = "0s";
657 verbmesg (1, zp->conf, "\tSigning completed after %s.\n", tstr);
661 copy_keyset (zp->dir, zp->zone, zp->conf);
663 if ( err >= 0 && reloadflag )
665 if ( zp->conf->dist_cmd )
666 dist_and_reload (zp);
667 else
668 reload_zone (zp->zone, zp->conf);
670 register_key (zp->keys, zp->conf);
673 return err;
676 static void register_key (dki_t *list, const zconf_t *z)
678 dki_t *dkp;
679 time_t currtime;
680 time_t age;
682 assert ( list != NULL );
683 assert ( z != NULL );
685 currtime = time (NULL);
686 for ( dkp = list; dkp && dki_isksk (dkp); dkp = dkp->next )
688 age = dki_age (dkp, currtime);
689 #if 0
690 /* announce "new" and active key signing keys */
691 if ( REG_URL && *REG_URL && dki_status (dkp) == DKI_ACT && age <= z->resign * 4 )
693 if ( verbose )
694 logmesg ("\tRegister new KSK with tag %d for domain %s\n",
695 dkp->tag, dkp->name);
697 #endif
702 * This function is not working with symbolic links to keyset- files,
703 * because file_mtime() returns the mtime of the underlying file, and *not*
704 * that of the symlink file.
705 * This is bad, because the keyset-file will be newly generated by dnssec-signzone
706 * on every re-signing call.
707 * Instead, in the case of a hierarchical directory structure, we copy the file
708 * (and so we change the timestamp) only if it was modified after the last
709 * generation (checked with cmpfile(), see func sign_zone()).
711 # define KEYSET_FILE_PFX "keyset-"
712 static int new_keysetfiles (const char *dir, time_t zone_signing_time)
714 DIR *dirp;
715 struct dirent *dentp;
716 char path[MAX_PATHSIZE+1];
717 int newkeysetfile;
719 if ( (dirp = opendir (dir)) == NULL )
720 return 0;
722 newkeysetfile = 0;
723 dbg_val2 ("new_keysetfile (%s, %s)\n", dir, time2str (zone_signing_time, 's'));
724 while ( !newkeysetfile && (dentp = readdir (dirp)) != NULL )
726 if ( strncmp (dentp->d_name, KEYSET_FILE_PFX, strlen (KEYSET_FILE_PFX)) != 0 )
727 continue;
729 pathname (path, sizeof (path), dir, dentp->d_name, NULL);
730 dbg_val2 ("newkeysetfile timestamp of %s = %s\n", path, time2str (file_mtime(path), 's'));
731 if ( file_mtime (path) > zone_signing_time )
732 newkeysetfile = 1;
734 closedir (dirp);
736 return newkeysetfile;
739 static int check_keydb_timestamp (dki_t *keylist, time_t reftime)
741 dki_t *key;
743 assert ( keylist != NULL );
744 if ( reftime == 0 )
745 return 1;
747 for ( key = keylist; key; key = key->next )
748 if ( dki_time (key) > reftime )
749 return 1;
751 return 0;
754 static int writekeyfile (const char *fname, const dki_t *list, int key_ttl)
756 FILE *fp;
757 const dki_t *dkp;
758 time_t curr = time (NULL);
759 int ksk;
761 if ( (fp = fopen (fname, "w")) == NULL )
762 return 0;
763 fprintf (fp, ";\n");
764 fprintf (fp, ";\t!!! Don\'t edit this file by hand.\n");
765 fprintf (fp, ";\t!!! It will be generated by %s.\n", progname);
766 fprintf (fp, ";\n");
767 fprintf (fp, ";\t Last generation time %s\n", time2str (curr, 's'));
768 fprintf (fp, ";\n");
770 fprintf (fp, "\n");
771 fprintf (fp, "; *** List of Key Signing Keys ***\n");
772 ksk = 1;
773 for ( dkp = list; dkp; dkp = dkp->next )
775 if ( ksk && !dki_isksk (dkp) )
777 fprintf (fp, "; *** List of Zone Signing Keys ***\n");
778 ksk = 0;
780 dki_prt_comment (dkp, fp);
781 dki_prt_dnskeyttl (dkp, fp, key_ttl);
782 putc ('\n', fp);
785 fclose (fp);
786 return 1;
789 static int sign_zone (const char *dir, const char *domain, const char *file, const zconf_t *conf)
791 char cmd[2047+1];
792 char str[1023+1];
793 char rparam[254+1];
794 char nsec3param[637+1];
795 char keysetdir[254+1];
796 const char *gends;
797 const char *pseudo;
798 const char *param;
799 int len;
800 FILE *fp;
802 assert (conf != NULL);
803 assert (domain != NULL);
805 len = 0;
806 str[0] = '\0';
807 if ( conf->lookaside && conf->lookaside[0] )
808 len = snprintf (str, sizeof (str), "-l %.250s", conf->lookaside);
810 dbg_line();
811 #if defined(BIND_VERSION) && BIND_VERSION >= 940
812 if ( !dynamic_zone && conf->serialform == Unixtime )
813 snprintf (str+len, sizeof (str) - len, " -N unixtime");
814 #endif
816 gends = "";
817 if ( conf->sig_gends )
818 gends = "-g ";
820 pseudo = "";
821 if ( conf->sig_pseudo )
822 pseudo = "-p ";
824 param = "";
825 if ( conf->sig_param && conf->sig_param[0] )
826 param = conf->sig_param;
828 nsec3param[0] = '\0';
829 #if defined(BIND_VERSION) && BIND_VERSION >= 960
830 if ( conf->k_algo == DK_ALGO_NSEC3DSA || conf->k_algo == DK_ALGO_NSEC3RSASHA1 )
832 char salt[510+1]; /* salt has a maximum of 255 bytes == 510 hex nibbles */
834 if ( gensalt (salt, sizeof (salt), conf->saltbits) )
835 snprintf (nsec3param, sizeof (nsec3param), "-3 %s ", salt);
837 #endif
839 dbg_line();
840 rparam[0] = '\0';
841 if ( conf->sig_random && conf->sig_random[0] )
842 snprintf (rparam, sizeof (rparam), "-r %.250s ", conf->sig_random);
844 dbg_line();
845 keysetdir[0] = '\0';
846 if ( conf->keysetdir && conf->keysetdir[0] && strcmp (conf->keysetdir, "..") != 0 )
847 snprintf (keysetdir, sizeof (keysetdir), "-d %.250s ", conf->keysetdir);
849 if ( dir == NULL || *dir == '\0' )
850 dir = ".";
852 dbg_line();
853 #if defined(BIND_VERSION) && BIND_VERSION >= 940
854 if ( dynamic_zone )
855 snprintf (cmd, sizeof (cmd), "cd %s; %s %s %s%s%s%s%s-o %s -e +%ld %s -N increment -f %s.dsigned %s K*.private 2>&1",
856 dir, SIGNCMD, param, nsec3param, gends, pseudo, rparam, keysetdir, domain, conf->sigvalidity, str, file, file);
857 else
858 #endif
859 snprintf (cmd, sizeof (cmd), "cd %s; %s %s %s%s%s%s%s-o %s -e +%ld %s %s K*.private 2>&1",
860 dir, SIGNCMD, param, nsec3param, gends, pseudo, rparam, keysetdir, domain, conf->sigvalidity, str, file);
861 verbmesg (2, conf, "\t Run cmd \"%s\"\n", cmd);
862 *str = '\0';
863 if ( noexec == 0 )
865 #if 0
866 if ( (fp = popen (cmd, "r")) == NULL || fgets (str, sizeof str, fp) == NULL )
867 return -1;
868 #else
869 if ( (fp = popen (cmd, "r")) == NULL )
870 return -1;
871 str[0] = '\0';
872 while ( fgets (str, sizeof str, fp) != NULL ) /* eat up all output until the last line */
874 #endif
875 pclose (fp);
878 dbg_line();
879 verbmesg (2, conf, "\t Cmd dnssec-signzone return: \"%s\"\n", str_chop (str, '\n'));
880 len = strlen (str) - 6;
881 if ( len < 0 || strcmp (str+len, "signed") != 0 )
882 return -1;
884 return 0;
887 static void copy_keyset (const char *dir, const char *domain, const zconf_t *conf)
889 char fromfile[1024];
890 char tofile[1024];
891 int ret;
893 /* propagate "keyset"-file to parent dir */
894 if ( conf->keysetdir && strcmp (conf->keysetdir, "..") == 0 )
896 /* check if special parent-file exist (ksk rollover) */
897 snprintf (fromfile, sizeof (fromfile), "%s/parent-%s", dir, domain);
898 if ( !fileexist (fromfile) ) /* use "normal" keyset-file */
899 snprintf (fromfile, sizeof (fromfile), "%s/keyset-%s", dir, domain);
901 /* verbmesg (2, conf, "\t check \"%s\" against parent dir\n", fromfile); */
902 snprintf (tofile, sizeof (tofile), "%s/../keyset-%s", dir, domain);
903 if ( cmpfile (fromfile, tofile) != 0 )
905 verbmesg (2, conf, "\t copy \"%s\" to parent dir\n", fromfile);
906 if ( (ret = copyfile (fromfile, tofile, NULL)) != 0 )
908 error ("Couldn't copy \"%s\" to parent dir (%d:%s)\n",
909 fromfile, ret, strerror(errno));
910 lg_mesg (LG_ERROR, "\%s\": can't copy \"%s\" to parent dir (%d:%s)",
911 domain, fromfile, ret, strerror(errno));