]> CyberLeo.Net >> Repos - FreeBSD/releng/9.0.git/blob - contrib/bind9/bin/dnssec/dnssec-settime.c
Copy stable/9 to releng/9.0 as part of the FreeBSD 9.0-RELEASE release
[FreeBSD/releng/9.0.git] / contrib / bind9 / bin / dnssec / dnssec-settime.c
1 /*
2  * Copyright (C) 2009-2011  Internet Systems Consortium, Inc. ("ISC")
3  *
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14  * PERFORMANCE OF THIS SOFTWARE.
15  */
16
17 /* $Id: dnssec-settime.c,v 1.28.16.3 2011-06-02 20:24:11 each Exp $ */
18
19 /*! \file */
20
21 #include <config.h>
22
23 #include <libgen.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <errno.h>
27 #include <time.h>
28
29 #include <isc/buffer.h>
30 #include <isc/commandline.h>
31 #include <isc/entropy.h>
32 #include <isc/file.h>
33 #include <isc/hash.h>
34 #include <isc/mem.h>
35 #include <isc/print.h>
36 #include <isc/string.h>
37 #include <isc/util.h>
38
39 #include <dns/keyvalues.h>
40 #include <dns/result.h>
41
42 #include <dst/dst.h>
43
44 #include "dnssectool.h"
45
46 const char *program = "dnssec-settime";
47 int verbose;
48
49 static isc_mem_t        *mctx = NULL;
50
51 ISC_PLATFORM_NORETURN_PRE static void
52 usage(void) ISC_PLATFORM_NORETURN_POST;
53
54 static void
55 usage(void) {
56         fprintf(stderr, "Usage:\n");
57         fprintf(stderr, "    %s [options] keyfile\n\n", program);
58         fprintf(stderr, "Version: %s\n", VERSION);
59         fprintf(stderr, "General options:\n");
60 #ifdef USE_PKCS11
61         fprintf(stderr, "    -E engine:          specify OpenSSL engine "
62                                                  "(default \"pkcs11\")\n");
63 #else
64         fprintf(stderr, "    -E engine:          specify OpenSSL engine\n");
65 #endif
66         fprintf(stderr, "    -f:                 force update of old-style "
67                                                  "keys\n");
68         fprintf(stderr, "    -K directory:       set key file location\n");
69         fprintf(stderr, "    -v level:           set level of verbosity\n");
70         fprintf(stderr, "    -h:                 help\n");
71         fprintf(stderr, "Timing options:\n");
72         fprintf(stderr, "    -P date/[+-]offset/none: set/unset key "
73                                                      "publication date\n");
74         fprintf(stderr, "    -A date/[+-]offset/none: set/unset key "
75                                                      "activation date\n");
76         fprintf(stderr, "    -R date/[+-]offset/none: set/unset key "
77                                                      "revocation date\n");
78         fprintf(stderr, "    -I date/[+-]offset/none: set/unset key "
79                                                      "inactivation date\n");
80         fprintf(stderr, "    -D date/[+-]offset/none: set/unset key "
81                                                      "deletion date\n");
82         fprintf(stderr, "Printing options:\n");
83         fprintf(stderr, "    -p C/P/A/R/I/D/all: print a particular time "
84                                                 "value or values\n");
85         fprintf(stderr, "    -u:                 print times in unix epoch "
86                                                 "format\n");
87         fprintf(stderr, "Output:\n");
88         fprintf(stderr, "     K<name>+<alg>+<new id>.key, "
89                              "K<name>+<alg>+<new id>.private\n");
90
91         exit (-1);
92 }
93
94 static void
95 printtime(dst_key_t *key, int type, const char *tag, isc_boolean_t epoch,
96           FILE *stream)
97 {
98         isc_result_t result;
99         const char *output = NULL;
100         isc_stdtime_t when;
101
102         if (tag != NULL)
103                 fprintf(stream, "%s: ", tag);
104
105         result = dst_key_gettime(key, type, &when);
106         if (result == ISC_R_NOTFOUND) {
107                 fprintf(stream, "UNSET\n");
108         } else if (epoch) {
109                 fprintf(stream, "%d\n", (int) when);
110         } else {
111                 time_t time = when;
112                 output = ctime(&time);
113                 fprintf(stream, "%s", output);
114         }
115 }
116
117 int
118 main(int argc, char **argv) {
119         isc_result_t    result;
120 #ifdef USE_PKCS11
121         const char      *engine = "pkcs11";
122 #else
123         const char      *engine = NULL;
124 #endif
125         char            *filename = NULL, *directory = NULL;
126         char            newname[1024];
127         char            keystr[DST_KEY_FORMATSIZE];
128         char            *endp, *p;
129         int             ch;
130         isc_entropy_t   *ectx = NULL;
131         const char      *predecessor = NULL;
132         dst_key_t       *prevkey = NULL;
133         dst_key_t       *key = NULL;
134         isc_buffer_t    buf;
135         dns_name_t      *name = NULL;
136         dns_secalg_t    alg = 0;
137         unsigned int    size = 0;
138         isc_uint16_t    flags = 0;
139         int             prepub = -1;
140         isc_stdtime_t   now;
141         isc_stdtime_t   pub = 0, act = 0, rev = 0, inact = 0, del = 0;
142         isc_boolean_t   setpub = ISC_FALSE, setact = ISC_FALSE;
143         isc_boolean_t   setrev = ISC_FALSE, setinact = ISC_FALSE;
144         isc_boolean_t   setdel = ISC_FALSE;
145         isc_boolean_t   unsetpub = ISC_FALSE, unsetact = ISC_FALSE;
146         isc_boolean_t   unsetrev = ISC_FALSE, unsetinact = ISC_FALSE;
147         isc_boolean_t   unsetdel = ISC_FALSE;
148         isc_boolean_t   printcreate = ISC_FALSE, printpub = ISC_FALSE;
149         isc_boolean_t   printact = ISC_FALSE,  printrev = ISC_FALSE;
150         isc_boolean_t   printinact = ISC_FALSE, printdel = ISC_FALSE;
151         isc_boolean_t   force = ISC_FALSE;
152         isc_boolean_t   epoch = ISC_FALSE;
153         isc_boolean_t   changed = ISC_FALSE;
154
155         if (argc == 1)
156                 usage();
157
158         result = isc_mem_create(0, 0, &mctx);
159         if (result != ISC_R_SUCCESS)
160                 fatal("Out of memory");
161
162         dns_result_register();
163
164         isc_commandline_errprint = ISC_FALSE;
165
166         isc_stdtime_get(&now);
167
168 #define CMDLINE_FLAGS "A:D:E:fhI:i:K:P:p:R:S:uv:"
169         while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
170                 switch (ch) {
171                 case 'E':
172                         engine = isc_commandline_argument;
173                         break;
174                 case 'f':
175                         force = ISC_TRUE;
176                         break;
177                 case 'p':
178                         p = isc_commandline_argument;
179                         if (!strcasecmp(p, "all")) {
180                                 printcreate = ISC_TRUE;
181                                 printpub = ISC_TRUE;
182                                 printact = ISC_TRUE;
183                                 printrev = ISC_TRUE;
184                                 printinact = ISC_TRUE;
185                                 printdel = ISC_TRUE;
186                                 break;
187                         }
188
189                         do {
190                                 switch (*p++) {
191                                 case 'C':
192                                         printcreate = ISC_TRUE;
193                                         break;
194                                 case 'P':
195                                         printpub = ISC_TRUE;
196                                         break;
197                                 case 'A':
198                                         printact = ISC_TRUE;
199                                         break;
200                                 case 'R':
201                                         printrev = ISC_TRUE;
202                                         break;
203                                 case 'I':
204                                         printinact = ISC_TRUE;
205                                         break;
206                                 case 'D':
207                                         printdel = ISC_TRUE;
208                                         break;
209                                 case ' ':
210                                         break;
211                                 default:
212                                         usage();
213                                         break;
214                                 }
215                         } while (*p != '\0');
216                         break;
217                 case 'u':
218                         epoch = ISC_TRUE;
219                         break;
220                 case 'K':
221                         /*
222                          * We don't have to copy it here, but do it to
223                          * simplify cleanup later
224                          */
225                         directory = isc_mem_strdup(mctx,
226                                                    isc_commandline_argument);
227                         if (directory == NULL) {
228                                 fatal("Failed to allocate memory for "
229                                       "directory");
230                         }
231                         break;
232                 case 'v':
233                         verbose = strtol(isc_commandline_argument, &endp, 0);
234                         if (*endp != '\0')
235                                 fatal("-v must be followed by a number");
236                         break;
237                 case 'P':
238                         if (setpub || unsetpub)
239                                 fatal("-P specified more than once");
240
241                         changed = ISC_TRUE;
242                         if (!strcasecmp(isc_commandline_argument, "none")) {
243                                 unsetpub = ISC_TRUE;
244                         } else {
245                                 setpub = ISC_TRUE;
246                                 pub = strtotime(isc_commandline_argument,
247                                                 now, now);
248                         }
249                         break;
250                 case 'A':
251                         if (setact || unsetact)
252                                 fatal("-A specified more than once");
253
254                         changed = ISC_TRUE;
255                         if (!strcasecmp(isc_commandline_argument, "none")) {
256                                 unsetact = ISC_TRUE;
257                         } else {
258                                 setact = ISC_TRUE;
259                                 act = strtotime(isc_commandline_argument,
260                                                 now, now);
261                         }
262                         break;
263                 case 'R':
264                         if (setrev || unsetrev)
265                                 fatal("-R specified more than once");
266
267                         changed = ISC_TRUE;
268                         if (!strcasecmp(isc_commandline_argument, "none")) {
269                                 unsetrev = ISC_TRUE;
270                         } else {
271                                 setrev = ISC_TRUE;
272                                 rev = strtotime(isc_commandline_argument,
273                                                 now, now);
274                         }
275                         break;
276                 case 'I':
277                         if (setinact || unsetinact)
278                                 fatal("-I specified more than once");
279
280                         changed = ISC_TRUE;
281                         if (!strcasecmp(isc_commandline_argument, "none")) {
282                                 unsetinact = ISC_TRUE;
283                         } else {
284                                 setinact = ISC_TRUE;
285                                 inact = strtotime(isc_commandline_argument,
286                                                 now, now);
287                         }
288                         break;
289                 case 'D':
290                         if (setdel || unsetdel)
291                                 fatal("-D specified more than once");
292
293                         changed = ISC_TRUE;
294                         if (!strcasecmp(isc_commandline_argument, "none")) {
295                                 unsetdel = ISC_TRUE;
296                         } else {
297                                 setdel = ISC_TRUE;
298                                 del = strtotime(isc_commandline_argument,
299                                                 now, now);
300                         }
301                         break;
302                 case 'S':
303                         predecessor = isc_commandline_argument;
304                         break;
305                 case 'i':
306                         prepub = strtottl(isc_commandline_argument);
307                         break;
308                 case '?':
309                         if (isc_commandline_option != '?')
310                                 fprintf(stderr, "%s: invalid argument -%c\n",
311                                         program, isc_commandline_option);
312                         /* Falls into */
313                 case 'h':
314                         usage();
315
316                 default:
317                         fprintf(stderr, "%s: unhandled option -%c\n",
318                                 program, isc_commandline_option);
319                         exit(1);
320                 }
321         }
322
323         if (argc < isc_commandline_index + 1 ||
324             argv[isc_commandline_index] == NULL)
325                 fatal("The key file name was not specified");
326         if (argc > isc_commandline_index + 1)
327                 fatal("Extraneous arguments");
328
329         if (ectx == NULL)
330                 setup_entropy(mctx, NULL, &ectx);
331         result = isc_hash_create(mctx, ectx, DNS_NAME_MAXWIRE);
332         if (result != ISC_R_SUCCESS)
333                 fatal("Could not initialize hash");
334         result = dst_lib_init2(mctx, ectx, engine,
335                                ISC_ENTROPY_BLOCKING | ISC_ENTROPY_GOODONLY);
336         if (result != ISC_R_SUCCESS)
337                 fatal("Could not initialize dst: %s",
338                       isc_result_totext(result));
339         isc_entropy_stopcallbacksources(ectx);
340
341         if (predecessor != NULL) {
342                 char keystr[DST_KEY_FORMATSIZE];
343                 isc_stdtime_t when;
344                 int major, minor;
345
346                 if (prepub == -1)
347                         prepub = (30 * 86400);
348
349                 if (setpub || unsetpub)
350                         fatal("-S and -P cannot be used together");
351                 if (setact || unsetact)
352                         fatal("-S and -A cannot be used together");
353
354                 result = dst_key_fromnamedfile(predecessor, directory,
355                                                DST_TYPE_PUBLIC |
356                                                DST_TYPE_PRIVATE,
357                                                mctx, &prevkey);
358                 if (result != ISC_R_SUCCESS)
359                         fatal("Invalid keyfile %s: %s",
360                               filename, isc_result_totext(result));
361                 if (!dst_key_isprivate(prevkey))
362                         fatal("%s is not a private key", filename);
363
364                 name = dst_key_name(prevkey);
365                 alg = dst_key_alg(prevkey);
366                 size = dst_key_size(prevkey);
367                 flags = dst_key_flags(prevkey);
368
369                 dst_key_format(prevkey, keystr, sizeof(keystr));
370                 dst_key_getprivateformat(prevkey, &major, &minor);
371                 if (major != DST_MAJOR_VERSION || minor < DST_MINOR_VERSION)
372                         fatal("Predecessor has incompatible format "
373                               "version %d.%d\n\t", major, minor);
374
375                 result = dst_key_gettime(prevkey, DST_TIME_ACTIVATE, &when);
376                 if (result != ISC_R_SUCCESS)
377                         fatal("Predecessor has no activation date. "
378                               "You must set one before\n\t"
379                               "generating a successor.");
380
381                 result = dst_key_gettime(prevkey, DST_TIME_INACTIVE, &act);
382                 if (result != ISC_R_SUCCESS)
383                         fatal("Predecessor has no inactivation date. "
384                               "You must set one before\n\t"
385                               "generating a successor.");
386
387                 pub = act - prepub;
388                 if (pub < now && prepub != 0)
389                         fatal("Predecessor will become inactive before the\n\t"
390                               "prepublication period ends.  Either change "
391                               "its inactivation date,\n\t"
392                               "or use the -i option to set a shorter "
393                               "prepublication interval.");
394
395                 result = dst_key_gettime(prevkey, DST_TIME_DELETE, &when);
396                 if (result != ISC_R_SUCCESS)
397                         fprintf(stderr, "%s: WARNING: Predecessor has no "
398                                         "removal date;\n\t"
399                                         "it will remain in the zone "
400                                         "indefinitely after rollover.\n",
401                                         program);
402
403                 changed = setpub = setact = ISC_TRUE;
404                 dst_key_free(&prevkey);
405         } else {
406                 if (prepub < 0)
407                         prepub = 0;
408
409                 if (prepub > 0) {
410                         if (setpub && setact && (act - prepub) < pub)
411                                 fatal("Activation and publication dates "
412                                       "are closer together than the\n\t"
413                                       "prepublication interval.");
414
415                         if (setpub && !setact) {
416                                 setact = ISC_TRUE;
417                                 act = pub + prepub;
418                         } else if (setact && !setpub) {
419                                 setpub = ISC_TRUE;
420                                 pub = act - prepub;
421                         }
422
423                         if ((act - prepub) < now)
424                                 fatal("Time until activation is shorter "
425                                       "than the\n\tprepublication interval.");
426                 }
427         }
428
429         if (directory != NULL) {
430                 filename = argv[isc_commandline_index];
431         } else {
432                 result = isc_file_splitpath(mctx, argv[isc_commandline_index],
433                                             &directory, &filename);
434                 if (result != ISC_R_SUCCESS)
435                         fatal("cannot process filename %s: %s",
436                               argv[isc_commandline_index],
437                               isc_result_totext(result));
438         }
439
440         result = dst_key_fromnamedfile(filename, directory,
441                                        DST_TYPE_PUBLIC | DST_TYPE_PRIVATE,
442                                        mctx, &key);
443         if (result != ISC_R_SUCCESS)
444                 fatal("Invalid keyfile %s: %s",
445                       filename, isc_result_totext(result));
446
447         if (!dst_key_isprivate(key))
448                 fatal("%s is not a private key", filename);
449
450         dst_key_format(key, keystr, sizeof(keystr));
451
452         if (predecessor != NULL) {
453                 if (!dns_name_equal(name, dst_key_name(key)))
454                         fatal("Key name mismatch");
455                 if (alg != dst_key_alg(key))
456                         fatal("Key algorithm mismatch");
457                 if (size != dst_key_size(key))
458                         fatal("Key size mismatch");
459                 if (flags != dst_key_flags(key))
460                         fatal("Key flags mismatch");
461         }
462
463         if (force)
464                 set_keyversion(key);
465         else
466                 check_keyversion(key, keystr);
467
468         if (verbose > 2)
469                 fprintf(stderr, "%s: %s\n", program, keystr);
470
471         /*
472          * Set time values.
473          */
474         if (setpub)
475                 dst_key_settime(key, DST_TIME_PUBLISH, pub);
476         else if (unsetpub)
477                 dst_key_unsettime(key, DST_TIME_PUBLISH);
478
479         if (setact)
480                 dst_key_settime(key, DST_TIME_ACTIVATE, act);
481         else if (unsetact)
482                 dst_key_unsettime(key, DST_TIME_ACTIVATE);
483
484         if (setrev) {
485                 if ((dst_key_flags(key) & DNS_KEYFLAG_REVOKE) != 0)
486                         fprintf(stderr, "%s: warning: Key %s is already "
487                                         "revoked; changing the revocation date "
488                                         "will not affect this.\n",
489                                         program, keystr);
490                 if ((dst_key_flags(key) & DNS_KEYFLAG_KSK) == 0)
491                         fprintf(stderr, "%s: warning: Key %s is not flagged as "
492                                         "a KSK, but -R was used.  Revoking a "
493                                         "ZSK is legal, but undefined.\n",
494                                         program, keystr);
495                 dst_key_settime(key, DST_TIME_REVOKE, rev);
496         } else if (unsetrev) {
497                 if ((dst_key_flags(key) & DNS_KEYFLAG_REVOKE) != 0)
498                         fprintf(stderr, "%s: warning: Key %s is already "
499                                         "revoked; removing the revocation date "
500                                         "will not affect this.\n",
501                                         program, keystr);
502                 dst_key_unsettime(key, DST_TIME_REVOKE);
503         }
504
505         if (setinact)
506                 dst_key_settime(key, DST_TIME_INACTIVE, inact);
507         else if (unsetinact)
508                 dst_key_unsettime(key, DST_TIME_INACTIVE);
509
510         if (setdel)
511                 dst_key_settime(key, DST_TIME_DELETE, del);
512         else if (unsetdel)
513                 dst_key_unsettime(key, DST_TIME_DELETE);
514
515         /*
516          * No metadata changes were made but we're forcing an upgrade
517          * to the new format anyway: use "-P now -A now" as the default
518          */
519         if (force && !changed) {
520                 dst_key_settime(key, DST_TIME_PUBLISH, now);
521                 dst_key_settime(key, DST_TIME_ACTIVATE, now);
522                 changed = ISC_TRUE;
523         }
524
525         /*
526          * Print out time values, if -p was used.
527          */
528         if (printcreate)
529                 printtime(key, DST_TIME_CREATED, "Created", epoch, stdout);
530
531         if (printpub)
532                 printtime(key, DST_TIME_PUBLISH, "Publish", epoch, stdout);
533
534         if (printact)
535                 printtime(key, DST_TIME_ACTIVATE, "Activate", epoch, stdout);
536
537         if (printrev)
538                 printtime(key, DST_TIME_REVOKE, "Revoke", epoch, stdout);
539
540         if (printinact)
541                 printtime(key, DST_TIME_INACTIVE, "Inactive", epoch, stdout);
542
543         if (printdel)
544                 printtime(key, DST_TIME_DELETE, "Delete", epoch, stdout);
545
546         if (changed) {
547                 isc_buffer_init(&buf, newname, sizeof(newname));
548                 result = dst_key_buildfilename(key, DST_TYPE_PUBLIC, directory,
549                                                &buf);
550                 if (result != ISC_R_SUCCESS) {
551                         fatal("Failed to build public key filename: %s",
552                               isc_result_totext(result));
553                 }
554
555                 result = dst_key_tofile(key, DST_TYPE_PUBLIC|DST_TYPE_PRIVATE,
556                                         directory);
557                 if (result != ISC_R_SUCCESS) {
558                         dst_key_format(key, keystr, sizeof(keystr));
559                         fatal("Failed to write key %s: %s", keystr,
560                               isc_result_totext(result));
561                 }
562
563                 printf("%s\n", newname);
564
565                 isc_buffer_clear(&buf);
566                 result = dst_key_buildfilename(key, DST_TYPE_PRIVATE, directory,
567                                                &buf);
568                 if (result != ISC_R_SUCCESS) {
569                         fatal("Failed to build private key filename: %s",
570                               isc_result_totext(result));
571                 }
572                 printf("%s\n", newname);
573         }
574
575         dst_key_free(&key);
576         dst_lib_destroy();
577         isc_hash_destroy();
578         cleanup_entropy(&ectx);
579         if (verbose > 10)
580                 isc_mem_stats(mctx, stdout);
581         isc_mem_free(mctx, directory);
582         isc_mem_destroy(&mctx);
583
584         return (0);
585 }