1 /* $NetBSD: zkt-signer.c,v 1.1.1.1 2015/07/08 15:37:48 christos Exp $ */
3 /*****************************************************************
5 ** @(#) zkt-signer.c (c) Jan 2005 - Jan 2010 Holger Zuleger hznet.de
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.
10 ** Copyright (c) 2005 - 2010, 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
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 *****************************************************************/
54 # include "config_zkt.h"
55 #if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG
63 # include "soaserial.h"
66 # include "rollover.h"
69 # define short_options "c:L:V:D:N:o:O:dfHhnrv"
70 #if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG
71 static struct option long_options
[] = {
72 {"reload", no_argument
, NULL
, 'r'},
73 {"force", no_argument
, NULL
, 'f'},
74 {"noexec", no_argument
, NULL
, 'n'},
75 {"verbose", no_argument
, NULL
, 'v'},
76 {"directory", no_argument
, NULL
, 'd'},
77 {"config", required_argument
, NULL
, 'c'},
78 {"option", required_argument
, NULL
, 'O'},
79 {"config-option", required_argument
, NULL
, 'O'},
80 {"logfile", required_argument
, NULL
, 'L' },
81 {"view", required_argument
, NULL
, 'V' },
82 {"directory", required_argument
, NULL
, 'D'},
83 {"named-conf", required_argument
, NULL
, 'N'},
84 {"origin", required_argument
, NULL
, 'o'},
85 {"dynamic", no_argument
, NULL
, 'd' },
86 {"help", no_argument
, NULL
, 'h'},
92 /** function declaration **/
93 static void usage (char *mesg
, zconf_t
*conf
);
94 static int add2zonelist (const char *dir
, const char *view
, const char *zone
, const char *file
);
95 static int parsedir (const char *dir
, zone_t
**zp
, const zconf_t
*conf
);
96 static int dosigning (zone_t
*zonelist
, zone_t
*zp
);
97 static int check_keydb_timestamp (dki_t
*keylist
, time_t reftime
);
98 static int new_keysetfiles (const char *dir
, time_t zone_signing_time
);
99 static int writekeyfile (const char *fname
, const dki_t
*list
, int key_ttl
);
100 static int sign_zone (const zone_t
*zp
);
101 static void register_key (dki_t
*listp
, const zconf_t
*z
);
102 static void copy_keyset (const char *dir
, const char *domain
, const zconf_t
*conf
);
104 /** global command line options **/
109 const char *progname
;
110 static const char *viewname
= NULL
;
111 static const char *logfile
= NULL
;
112 static const char *origin
= NULL
;
113 static const char *namedconf
= NULL
;
114 static const char *dirname
= NULL
;
115 static int verbose
= 0;
116 static int force
= 0;
117 static int reloadflag
= 0;
118 static int noexec
= 0;
119 static int dynamic_zone
= 0; /* dynamic zone ? */
120 static zone_t
*zonelist
= NULL
; /* must be static global because add2zonelist use it */
121 static zconf_t
*config
;
124 #define set_bind96_dynzone(dz) ((dz) = 6)
125 #define bind96_dynzone(dz) ( (dz) >= 6 )
126 #define is_defined(str) ( (str) && *(str) )
128 int main (int argc
, char *const argv
[])
132 #if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG
137 const char *defconfname
;
141 if ( (p
= strrchr (progname
, '/')) )
144 if ( strncmp (progname
, "dnssec-signer", 13) == 0 )
146 fprintf (stderr
, "The use of dnssec-signer is deprecated, please run zkt-signer instead\n");
147 viewname
= getnameappendix (progname
, "dnssec-signer");
150 viewname
= getnameappendix (progname
, "zkt-signer");
151 defconfname
= getdefconfname (viewname
);
152 config
= loadconfig ("", (zconf_t
*)NULL
); /* load build-in config */
153 if ( fileexist (defconfname
) ) /* load default config file */
154 config
= loadconfig (defconfname
, config
);
155 if ( config
== NULL
)
156 fatal ("Couldn't load config: Out of memory\n");
160 #if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG
161 while ( (c
= getopt_long (argc
, argv
, short_options
, long_options
, &opt_index
)) != -1 )
163 while ( (c
= getopt (argc
, argv
, short_options
)) != -1 )
168 case 'V': /* view name */
170 defconfname
= getdefconfname (viewname
);
171 if ( fileexist (defconfname
) ) /* load default config file */
172 config
= loadconfig (defconfname
, config
);
173 if ( config
== NULL
)
174 fatal ("Out of memory\n");
176 case 'c': /* load config from file */
177 config
= loadconfig (optarg
, config
);
178 if ( config
== NULL
)
179 fatal ("Out of memory\n");
181 case 'O': /* load config option from commandline */
182 config
= loadconfig_fromstr (optarg
, config
);
183 if ( config
== NULL
)
184 fatal ("Out of memory\n");
195 case 'L': /* error log file|directory */
203 usage (NULL
, config
);
207 /* dynamic zone requires a name server reload... */
208 reloadflag
= 0; /* ...but "rndc thaw" reloads the zone anyway */
214 if ( !dynamic_zone
) /* dynamic zones don't need a rndc reload (see "-d" */
221 if ( isprint (optopt
) )
222 snprintf (errstr
, sizeof(errstr
),
223 "Unknown option \"-%c\".\n", optopt
);
225 snprintf (errstr
, sizeof (errstr
),
226 "Unknown option char \\x%x.\n", optopt
);
227 usage (errstr
, config
);
235 /* store some of the commandline parameter in the config structure */
236 setconfigpar (config
, "--view", viewname
);
237 setconfigpar (config
, "-v", &verbose
);
238 setconfigpar (config
, "--noexec", &noexec
);
239 if ( logfile
== NULL
)
240 logfile
= config
->logfile
;
242 if ( lg_open (progname
, config
->syslogfacility
, config
->sysloglevel
, config
->zonedir
, logfile
, config
->loglevel
) < -1 )
243 fatal ("Couldn't open logfile %s in dir %s\n", logfile
, config
->zonedir
);
245 lg_args (LG_NOTICE
, argc
, argv
);
247 /* 1.0rc1: If the ttl is 0 or not known because of dynamic zone signing, ... */
248 /* ... use sig valid time for this */
249 if ( config
->max_ttl
<= 0 || dynamic_zone
)
251 // config = dupconfig (config);
252 config
->max_ttl
= config
->sigvalidity
;
256 if ( origin
) /* option -o ? */
260 if ( (argc
- optind
) <= 0 ) /* no arguments left ? */
261 ret
= zone_readdir (".", origin
, NULL
, &zonelist
, config
, dynamic_zone
);
263 ret
= zone_readdir (".", origin
, argv
[optind
], &zonelist
, config
, dynamic_zone
);
265 /* anyway, "delete" all (remaining) arguments */
268 /* complain if nothing could read in */
269 if ( ret
!= 1 || zonelist
== NULL
)
271 lg_mesg (LG_FATAL
, "\"%s\": couldn't read", origin
);
272 fatal ("Couldn't read zone \"%s\"\n", origin
);
275 if ( namedconf
) /* option -N ? */
279 memset (dir
, '\0', sizeof (dir
));
280 if ( config
->zonedir
)
281 strncpy (dir
, config
->zonedir
, sizeof(dir
));
282 if ( !parse_namedconf (namedconf
, config
->chroot_dir
, dir
, sizeof (dir
), add2zonelist
) )
283 fatal ("Can't read file %s as namedconf file\n", namedconf
);
284 if ( zonelist
== NULL
)
285 fatal ("No signed zone found in file %s\n", namedconf
);
287 if ( dirname
) /* option -D ? */
289 char *dir
= strdup (dirname
);
291 p
= dir
+ strlen (dir
);
295 *p
= '\0'; /* remove trailing path seperator */
297 if ( !parsedir (dir
, &zonelist
, config
) )
298 fatal ("Can't read directory tree %s\n", dir
);
299 if ( zonelist
== NULL
)
300 fatal ("No signed zone found in directory tree %s\n", dir
);
304 /* none of the above: read default directory tree */
305 if ( zonelist
== NULL
)
306 parsedir (config
->zonedir
, &zonelist
, config
);
308 #if defined(DBG) && DBG
309 for ( zp
= zonelist
; zp
; zp
= zp
->next
)
310 zone_print ("in main: ", zp
);
312 for ( zp
= zonelist
; zp
; zp
= zp
->next
)
313 if ( in_strarr (zp
->zone
, &argv
[optind
], argc
- optind
) )
315 dosigning (zonelist
, zp
);
316 verbmesg (1, zp
->conf
, "\n");
319 zone_freelist (&zonelist
);
321 errcnt
= lg_geterrcnt ();
322 lg_mesg (LG_NOTICE
, "end of run: %d error%s occured", errcnt
, errcnt
== 1 ? "" : "s");
325 return errcnt
< 64 ? errcnt
: 64;
328 # define sopt_usage(mesg, value) fprintf (stderr, mesg, value)
329 #if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG
330 # define lopt_usage(mesg, value) fprintf (stderr, mesg, value)
331 # define loptstr(lstr, sstr) lstr
333 # define lopt_usage(mesg, value)
334 # define loptstr(lstr, sstr) sstr
336 static void usage (char *mesg
, zconf_t
*conf
)
338 fprintf (stderr
, "%s version %s compiled for BIND %d\n", progname
, ZKT_VERSION
, BIND_VERSION
);
339 fprintf (stderr
, "ZKT %s\n", ZKT_COPYRIGHT
);
340 fprintf (stderr
, "\n");
342 fprintf (stderr
, "usage: %s [-L] [-V view] [-c file] [-O optstr] ", progname
);
343 fprintf (stderr
, "[-D directorytree] ");
344 fprintf (stderr
, "[-fhnr] [-v [-v]] [zone ...]\n");
346 fprintf (stderr
, "usage: %s [-L] [-V view] [-c file] [-O optstr] ", progname
);
347 fprintf (stderr
, "-N named.conf ");
348 fprintf (stderr
, "[-fhnr] [-v [-v]] [zone ...]\n");
350 fprintf (stderr
, "usage: %s [-L] [-V view] [-c file] [-O optstr] ", progname
);
351 fprintf (stderr
, "-o origin ");
352 fprintf (stderr
, "[-fhnr] [-v [-v]] [zonefile.signed]\n");
354 fprintf (stderr
, "\t-c file%s", loptstr (", --config=file\n", ""));
355 fprintf (stderr
, "\t\t read config from <file> instead of %s\n", CONFIG_FILE
);
356 fprintf (stderr
, "\t-O optstr%s", loptstr (", --config-option=\"optstr\"\n", ""));
357 fprintf (stderr
, "\t\t set config options on the commandline\n");
358 fprintf (stderr
, "\t-L file|dir%s", loptstr (", --logfile=file|dir\n", ""));
359 fprintf (stderr
, "\t\t specify file or directory for the log output\n");
360 fprintf (stderr
, "\t-V name%s", loptstr (", --view=name\n", ""));
361 fprintf (stderr
, "\t\t specify the view name \n");
362 fprintf (stderr
, "\t-D dir%s", loptstr (", --directory=dir\n", ""));
363 fprintf (stderr
, "\t\t parse the given directory tree for a list of secure zones \n");
364 fprintf (stderr
, "\t-N file%s", loptstr (", --named-conf=file\n", ""));
365 fprintf (stderr
, "\t\t get the list of secure zones out of the named like config file \n");
366 fprintf (stderr
, "\t-o zone%s", loptstr (", --origin=zone", ""));
367 fprintf (stderr
, "\tspecify the name of the zone \n");
368 fprintf (stderr
, "\t\t The file to sign should be given as an argument (default is \"%s.signed\")\n", conf
->zonefile
);
369 fprintf (stderr
, "\t-h%s\t print this help\n", loptstr (", --help", "\t"));
370 fprintf (stderr
, "\t-f%s\t force re-signing\n", loptstr (", --force", "\t"));
371 fprintf (stderr
, "\t-n%s\t no execution of external signing command\n", loptstr (", --noexec", "\t"));
372 // fprintf (stderr, "\t-r%s\t reload zone via <rndc reload zone> (or via the external distribution command)\n", loptstr (", --reload", "\t"));
373 fprintf (stderr
, "\t-r%s\t reload zone via %s\n", loptstr (", --reload", "\t"), conf
->dist_cmd
? conf
->dist_cmd
: "rndc");
374 fprintf (stderr
, "\t-v%s\t be verbose (use twice to be very verbose)\n", loptstr (", --verbose", "\t"));
376 fprintf (stderr
, "\t[zone]\t sign only those zones given as argument\n");
378 fprintf (stderr
, "\n");
379 fprintf (stderr
, "\tif neither -D nor -N nor -o is given, the directory tree specified\n");
380 fprintf (stderr
, "\tin the dnssec config file (\"%s\") will be parsed\n", conf
->zonedir
);
383 fprintf (stderr
, "%s\n", mesg
);
387 /** fill zonelist with infos coming out of named.conf **/
388 static int add2zonelist (const char *dir
, const char *view
, const char *zone
, const char *file
)
391 fprintf (stderr
, "printzone ");
392 fprintf (stderr
, "view \"%s\" " , view
);
393 fprintf (stderr
, "zone \"%s\" " , zone
);
394 fprintf (stderr
, "file ");
396 fprintf (stderr
, "%s/", dir
);
397 fprintf (stderr
, "%s", file
);
398 fprintf (stderr
, "\n");
401 if ( view
[0] != '\0' ) /* view found in named.conf */
403 if ( viewname
== NULL
|| viewname
[0] == '\0' ) /* viewname wasn't set on startup ? */
406 error ("zone \"%s\" in view \"%s\" found in name server config, but no matching view was set on startup\n", zone
, view
);
407 lg_mesg (LG_ERROR
, "\"%s\" in view \"%s\" found in name server config, but no matching view was set on startup", zone
, view
);
411 if ( strcmp (viewname
, view
) != 0 ) /* zone is _not_ in current view */
414 return zone_readdir (dir
, zone
, file
, &zonelist
, config
, dynamic_zone
);
417 static int parsedir (const char *dir
, zone_t
**zp
, const zconf_t
*conf
)
420 struct dirent
*dentp
;
421 char path
[MAX_PATHSIZE
+1];
423 dbg_val ("parsedir: (%s)\n", dir
);
424 if ( !is_directory (dir
) )
428 zone_readdir (dir
, NULL
, NULL
, zp
, conf
, dynamic_zone
);
430 dbg_val ("parsedir: opendir(%s)\n", dir
);
431 if ( (dirp
= opendir (dir
)) == NULL
)
434 while ( (dentp
= readdir (dirp
)) != NULL
)
436 if ( is_dotfilename (dentp
->d_name
) )
439 pathname (path
, sizeof (path
), dir
, dentp
->d_name
, NULL
);
440 if ( !is_directory (path
) )
443 dbg_val ("parsedir: recursive %s\n", path
);
444 parsedir (path
, zp
, conf
);
450 static int dosigning (zone_t
*zonelist
, zone_t
*zp
)
452 char path
[MAX_PATHSIZE
+1];
459 time_t zfilesig_time
;
462 verbmesg (1, zp
->conf
, "parsing zone \"%s\" in dir \"%s\"\n", zp
->zone
, zp
->dir
);
464 pathname (path
, sizeof (path
), zp
->dir
, zp
->sfile
, NULL
);
465 dbg_val("parsezonedir fileexist (%s)\n", path
);
466 if ( !fileexist (path
) )
468 error ("Not a secure zone directory (%s)!\n", zp
->dir
);
469 lg_mesg (LG_ERROR
, "\"%s\": not a secure zone directory (%s)!", zp
->zone
, zp
->dir
);
472 zfilesig_time
= file_mtime (path
);
474 pathname (path
, sizeof (path
), zp
->dir
, zp
->file
, NULL
);
475 dbg_val("parsezonedir fileexist (%s)\n", path
);
476 if ( !fileexist (path
) )
478 error ("No zone file found (%s)!\n", path
);
479 lg_mesg (LG_ERROR
, "\"%s\": no zone file found (%s)!", zp
->zone
, path
);
483 zfile_time
= file_mtime (path
);
484 currtime
= time (NULL
);
486 /* check for domain based logging */
487 if ( is_defined (zp
->conf
->logdomaindir
) ) /* parameter is not null or empty ? */
489 if ( strcmp (zp
->conf
->logdomaindir
, ".") == 0 ) /* current (".") means zone directory */
490 lg_zone_start (zp
->dir
, zp
->zone
);
492 lg_zone_start (zp
->conf
->logdomaindir
, zp
->zone
);
495 /* check rfc5011 key signing keys, create new one if necessary */
496 dbg_msg("parsezonedir check rfc 5011 ksk ");
497 newkey
= ksk5011status (&zp
->keys
, zp
->dir
, zp
->zone
, zp
->conf
);
498 if ( (newkey
& 02) != 02 ) /* not a rfc 5011 zone ? */
500 verbmesg (2, zp
->conf
, "\t\t->not a rfc5011 zone, looking for a regular ksk rollover\n");
501 /* check key signing keys, create new one if necessary */
502 dbg_msg("parsezonedir check ksk ");
503 newkey
|= kskstatus (zonelist
, zp
);
506 newkey
&= ~02; /* reset bit 2 */
508 /* check age of zone keys, probably retire (depreciate) or remove old keys */
509 dbg_msg("parsezonedir check zsk ");
510 newkey
+= zskstatus (&zp
->keys
, zp
->dir
, zp
->zone
, zp
->conf
);
512 /* check age of "dnskey.db" file against age of keyfiles */
513 pathname (path
, sizeof (path
), zp
->dir
, zp
->conf
->keyfile
, NULL
);
514 dbg_val("parsezonedir check_keydb_timestamp (%s)\n", path
);
516 newkey
= check_keydb_timestamp (zp
->keys
, file_mtime (path
));
519 #if defined(ALWAYS_CHECK_KEYSETFILES) && ALWAYS_CHECK_KEYSETFILES /* patch from Shane Wegner 15. June 2009 */
520 /* check if there is a new keyset- file */
522 newkeysetfile
= new_keysetfiles (zp
->dir
, zfilesig_time
);
524 /* if we work in subdir mode, check if there is a new keyset- file */
525 if ( !newkey
&& zp
->conf
->keysetdir
&& strcmp (zp
->conf
->keysetdir
, "..") == 0 )
526 newkeysetfile
= new_keysetfiles (zp
->dir
, zfilesig_time
);
529 /* is there a list of files included in zone.db ? */
530 if ( zp
->conf
->dependfiles
&& *zp
->conf
->dependfiles
)
535 time_t incfile_mtime
;
537 /* check the timestamp of each file against "zone.db" */
538 p
= zp
->conf
->dependfiles
;
541 while ( isflistdelim (*p
) )
544 for ( i
= 0; i
< 255 && *p
&& !isflistdelim (*p
); i
++ )
548 pathname (path
, sizeof (path
), zp
->dir
, file
, NULL
);
550 incfile_mtime
= file_mtime (path
);
551 if ( incfile_mtime
> zfile_time
) /* include file is newer? */
552 zfile_time
= incfile_mtime
; /* take this one as new mtime */
557 ** Check if it is time to do a re-sign. This is the case if
558 ** a) the command line flag -f is set, or
559 ** b) new keys are generated, or
560 ** c) we found a new KSK of a delegated domain, or
561 ** d) the "dnskey.db" file is newer than "zone.db"
562 ** e) the "zone.db" is newer than "zone.db.signed" or
563 ** f) "zone.db.signed" is older than the re-sign interval
567 snprintf (mesg
, sizeof(mesg
), "Option -f");
569 snprintf (mesg
, sizeof(mesg
), "Modified zone key set");
570 else if ( newkeysetfile
)
571 snprintf (mesg
, sizeof(mesg
), "Modified KSK in delegated domain");
572 else if ( file_mtime (path
) > zfilesig_time
)
573 snprintf (mesg
, sizeof(mesg
), "Modified keys");
574 else if ( zfile_time
> zfilesig_time
)
575 snprintf (mesg
, sizeof(mesg
), "Zone file edited");
576 else if ( (currtime
- zfilesig_time
) > zp
->conf
->resign
- (OFFSET
) )
577 snprintf (mesg
, sizeof(mesg
), "re-signing interval (%s) reached",
578 str_delspace (age2str (zp
->conf
->resign
)));
581 verbmesg (1, zp
->conf
, "\tRe-signing necessary: %s\n", mesg
);
583 verbmesg (1, zp
->conf
, "\tRe-signing not necessary!\n");
586 lg_mesg (LG_NOTICE
, "\"%s\": re-signing triggered: %s", zp
->zone
, mesg
);
589 if ( !(force
|| newkey
|| newkeysetfile
|| zfile_time
> zfilesig_time
||
590 file_mtime (path
) > zfilesig_time
||
591 (currtime
- zfilesig_time
) > zp
->conf
->resign
- (OFFSET
)) )
593 verbmesg (2, zp
->conf
, "\tCheck if there is a parent file to copy\n");
594 if ( zp
->conf
->keysetdir
&& strcmp (zp
->conf
->keysetdir
, "..") == 0 )
595 copy_keyset (zp
->dir
, zp
->zone
, zp
->conf
); /* copy the parent- file if it exist */
596 if ( is_defined (zp
->conf
->logdomaindir
) )
598 return 0; /* nothing to do */
601 /* let's start signing the zone */
604 /* create new "dnskey.db" file */
605 pathname (path
, sizeof (path
), zp
->dir
, zp
->conf
->keyfile
, NULL
);
606 verbmesg (1, zp
->conf
, "\tWriting key file \"%s\"\n", path
);
607 if ( !writekeyfile (path
, zp
->keys
, zp
->conf
->key_ttl
) )
609 error ("Can't create keyfile %s \n", path
);
610 lg_mesg (LG_ERROR
, "\"%s\": can't create keyfile %s", zp
->zone
, path
);
614 use_unixtime
= ( zp
->conf
->serialform
== Unixtime
);
615 dbg_val1 ("Use unixtime = %d\n", use_unixtime
);
616 if ( !dynamic_zone
&& !use_unixtime
) /* increment serial number in static zone files */
618 pathname (path
, sizeof (path
), zp
->dir
, zp
->file
, NULL
);
622 if ( (err
= inc_serial (path
, use_unixtime
)) < 0 )
624 error ("could not increment serialno of domain %s in file %s: %s!\n",
625 zp
->zone
, path
, inc_errstr (err
));
627 "zone \"%s\": couldn't increment serialno in file %s: %s",
628 zp
->zone
, path
, inc_errstr (err
));
631 verbmesg (1, zp
->conf
, "\tIncrementing serial number in file \"%s\"\n", path
);
634 verbmesg (1, zp
->conf
, "\tIncrementing serial number in file \"%s\"\n", path
);
637 /* at last, sign the zone file */
642 verbmesg (1, zp
->conf
, "\tSigning zone \"%s\"\n", zp
->zone
);
645 /* dynamic zones uses incremental signing, so we have to */
646 /* prepare the old (signed) file as new input file */
649 char zfile
[MAX_PATHSIZE
+1];
651 dyn_update_freeze (zp
->zone
, zp
->conf
, 1); /* freeze dynamic zone ! */
653 pathname (zfile
, sizeof (zfile
), zp
->dir
, zp
->file
, NULL
);
654 pathname (path
, sizeof (path
), zp
->dir
, zp
->sfile
, NULL
);
655 if ( filesize (path
) == 0L ) /* initial signing request ? */
657 verbmesg (1, zp
->conf
, "\tDynamic Zone signing: Initial signing request: Add DNSKEYs to zonefile\n");
658 copyfile (zfile
, path
, zp
->conf
->keyfile
);
661 else if ( zfile_time
> zfilesig_time
) /* zone.db is newer than signed file */
663 verbmesg (1, zp
->conf
, "\tDynamic Zone signing: zone file manually edited: Use it as new input file\n");
664 copyfile (zfile
, path
, NULL
);
667 verbmesg (1, zp
->conf
, "\tDynamic Zone signing: copy old signed zone file %s to new input file %s\n",
670 if ( newkey
) /* if we have new keys, they should be added to the zone file */
672 copyzonefile (path
, zfile
, zp
->conf
->keyfile
);
674 if ( zp
->conf
->dist_cmd
)
675 dist_and_reload (zp
, 2); /* ... and send to the name server */
678 else /* else we can do a simple file copy */
679 copyfile (path
, zfile
, NULL
);
682 timer
= start_timer ();
683 if ( (err
= sign_zone (zp
)) < 0 )
685 error ("\tSigning of zone %s failed (%d)!\n", zp
->zone
, err
);
686 lg_mesg (LG_ERROR
, "\"%s\": signing failed!", zp
->zone
);
688 timer
= stop_timer (timer
);
691 dyn_update_freeze (zp
->zone
, zp
->conf
, 0); /* thaw dynamic zone file */
695 const char *tstr
= str_delspace (age2str (timer
));
697 if ( !tstr
|| *tstr
== '\0' )
699 verbmesg (1, zp
->conf
, "\tSigning completed after %s.\n", tstr
);
703 copy_keyset (zp
->dir
, zp
->zone
, zp
->conf
);
705 if ( err
>= 0 && reloadflag
)
707 if ( zp
->conf
->dist_cmd
)
708 dist_and_reload (zp
, 1);
710 reload_zone (zp
->zone
, zp
->conf
);
712 register_key (zp
->keys
, zp
->conf
);
715 if ( is_defined (zp
->conf
->logdomaindir
) )
721 static void register_key (dki_t
*list
, const zconf_t
*z
)
727 assert ( list
!= NULL
);
728 assert ( z
!= NULL
);
730 currtime
= time (NULL
);
731 for ( dkp
= list
; dkp
&& dki_isksk (dkp
); dkp
= dkp
->next
)
733 age
= dki_age (dkp
, currtime
);
735 /* announce "new" and active key signing keys */
736 if ( REG_URL
&& *REG_URL
&& dki_status (dkp
) == DKI_ACT
&& age
<= z
->resign
* 4 )
739 logmesg ("\tRegister new KSK with tag %d for domain %s\n",
740 dkp
->tag
, dkp
->name
);
747 * This function is not working with symbolic links to keyset- files,
748 * because file_mtime() returns the mtime of the underlying file, and *not*
749 * that of the symlink file.
750 * This is bad, because the keyset-file will be newly generated by dnssec-signzone
751 * on every re-signing call.
752 * Instead, in the case of a hierarchical directory structure, we copy the file
753 * (and so we change the timestamp) only if it was modified after the last
754 * generation (checked with cmpfile(), see func sign_zone()).
756 # define KEYSET_FILE_PFX "keyset-"
757 static int new_keysetfiles (const char *dir
, time_t zone_signing_time
)
760 struct dirent
*dentp
;
761 char path
[MAX_PATHSIZE
+1];
764 if ( (dirp
= opendir (dir
)) == NULL
)
768 dbg_val2 ("new_keysetfile (%s, %s)\n", dir
, time2str (zone_signing_time
, 's'));
769 while ( !newkeysetfile
&& (dentp
= readdir (dirp
)) != NULL
)
771 if ( strncmp (dentp
->d_name
, KEYSET_FILE_PFX
, strlen (KEYSET_FILE_PFX
)) != 0 )
774 pathname (path
, sizeof (path
), dir
, dentp
->d_name
, NULL
);
775 dbg_val2 ("newkeysetfile timestamp of %s = %s\n", path
, time2str (file_mtime(path
), 's'));
776 if ( file_mtime (path
) > zone_signing_time
)
781 return newkeysetfile
;
784 static int check_keydb_timestamp (dki_t
*keylist
, time_t reftime
)
788 assert ( keylist
!= NULL
);
792 for ( key
= keylist
; key
; key
= key
->next
)
793 if ( dki_time (key
) > reftime
)
799 static int writekeyfile (const char *fname
, const dki_t
*list
, int key_ttl
)
803 time_t curr
= time (NULL
);
806 if ( (fp
= fopen (fname
, "w")) == NULL
)
809 fprintf (fp
, ";\t!!! Don\'t edit this file by hand.\n");
810 fprintf (fp
, ";\t!!! It will be generated by %s.\n", progname
);
812 fprintf (fp
, ";\t Last generation time %s\n", time2str (curr
, 's'));
816 fprintf (fp
, "; *** List of Key Signing Keys ***\n");
818 for ( dkp
= list
; dkp
; dkp
= dkp
->next
)
820 if ( ksk
&& !dki_isksk (dkp
) )
822 fprintf (fp
, "; *** List of Zone Signing Keys ***\n");
825 dki_prt_comment (dkp
, fp
);
826 dki_prt_dnskeyttl (dkp
, fp
, key_ttl
);
834 static int sign_zone (const zone_t
*zp
)
839 char nsec3param
[637+1];
840 char keysetdir
[254+1];
842 const char *dnskeyksk
;
861 if ( conf
->lookaside
&& conf
->lookaside
[0] )
862 len
= snprintf (str
, sizeof (str
), "-l %.250s", conf
->lookaside
);
865 if ( !dynamic_zone
&& conf
->serialform
== Unixtime
)
866 snprintf (str
+len
, sizeof (str
) - len
, " -N unixtime");
869 if ( conf
->sig_gends
)
873 if ( conf
->sig_dnskeyksk
)
877 if ( conf
->sig_pseudo
)
881 if ( conf
->sig_param
&& conf
->sig_param
[0] )
882 param
= conf
->sig_param
;
884 nsec3param
[0] = '\0';
885 if ( conf
->k_algo
== DK_ALGO_NSEC3DSA
|| conf
->k_algo
== DK_ALGO_NSEC3RSASHA1
||
886 conf
->nsec3
!= NSEC3_OFF
)
888 char salt
[510+1]; /* salt has a maximum of 255 bytes == 510 hex nibbles */
893 update
= "-u "; /* trailing blank is necessary */
894 if ( conf
->nsec3
== NSEC3_OPTOUT
)
899 /* static zones can use always a new salt (full zone signing) */
900 seed
= 0L; /* no seed: use mechanism build in gensalt() */
902 { /* dynamic zones have to reuse the salt on signing */
905 /* use gentime timestamp of ZSK for seeding rand generator */
906 kp
= dki_find (zp
->keys
, DKI_ZSK
, DKI_ACTIVE
, 1);
907 assert ( kp
!= NULL
);
914 if ( gensalt (salt
, sizeof (salt
), conf
->saltbits
, seed
) )
915 snprintf (nsec3param
, sizeof (nsec3param
), "%s%s-3 %s ", update
, optout
, salt
);
920 if ( conf
->sig_random
&& conf
->sig_random
[0] )
921 snprintf (rparam
, sizeof (rparam
), "-r %.250s ", conf
->sig_random
);
925 if ( conf
->keysetdir
&& conf
->keysetdir
[0] && strcmp (conf
->keysetdir
, "..") != 0 )
926 snprintf (keysetdir
, sizeof (keysetdir
), "-d %.250s ", conf
->keysetdir
);
928 if ( dir
== NULL
|| *dir
== '\0' )
933 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",
934 dir
, SIGNCMD
, param
, nsec3param
, dnskeyksk
, gends
, pseudo
, rparam
, keysetdir
, domain
, conf
->sigvalidity
, str
, file
, file
);
936 snprintf (cmd
, sizeof (cmd
), "cd %s; %s %s %s%s%s%s%s%s-o %s -e +%ld %s %s K*.private 2>&1",
937 dir
, SIGNCMD
, param
, nsec3param
, dnskeyksk
, gends
, pseudo
, rparam
, keysetdir
, domain
, conf
->sigvalidity
, str
, file
);
938 verbmesg (2, conf
, "\t Run cmd \"%s\"\n", cmd
);
943 if ( (fp
= popen (cmd
, "r")) == NULL
|| fgets (str
, sizeof str
, fp
) == NULL
)
946 if ( (fp
= popen (cmd
, "r")) == NULL
)
949 while ( fgets (str
, sizeof str
, fp
) != NULL
) /* eat up all output until the last line */
956 verbmesg (2, conf
, "\t Cmd dnssec-signzone return: \"%s\"\n", str_chop (str
, '\n'));
957 len
= strlen (str
) - 6;
958 if ( len
< 0 || strcmp (str
+len
, "signed") != 0 )
964 static void copy_keyset (const char *dir
, const char *domain
, const zconf_t
*conf
)
970 /* propagate "keyset"-file to parent dir */
971 if ( conf
->keysetdir
&& strcmp (conf
->keysetdir
, "..") == 0 )
973 /* check if special parent-file exist (ksk rollover) */
974 snprintf (fromfile
, sizeof (fromfile
), "%s/parent-%s", dir
, domain
);
975 if ( !fileexist (fromfile
) ) /* use "normal" keyset-file */
976 snprintf (fromfile
, sizeof (fromfile
), "%s/keyset-%s", dir
, domain
);
978 /* verbmesg (2, conf, "\t check \"%s\" against parent dir\n", fromfile); */
979 snprintf (tofile
, sizeof (tofile
), "%s/../keyset-%s", dir
, domain
);
980 if ( cmpfile (fromfile
, tofile
) != 0 )
982 verbmesg (2, conf
, "\t copy \"%s\" to parent dir\n", fromfile
);
983 if ( (ret
= copyfile (fromfile
, tofile
, NULL
)) != 0 )
985 error ("Couldn't copy \"%s\" to parent dir (%d:%s)\n",
986 fromfile
, ret
, strerror(errno
));
987 lg_mesg (LG_ERROR
, "\%s\": can't copy \"%s\" to parent dir (%d:%s)",
988 domain
, fromfile
, ret
, strerror(errno
));