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