]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
dnssec-settime: Allow manipulating state files
authorMatthijs Mekking <matthijs@isc.org>
Thu, 12 Sep 2019 09:57:55 +0000 (11:57 +0200)
committerMatthijs Mekking <matthijs@isc.org>
Wed, 6 Nov 2019 21:31:45 +0000 (22:31 +0100)
Introduce a new option '-s' for dnssec-settime that when manipulating
timing metadata, it also updates the key state file.

For testing purposes, add options to dnssec-settime to set key
states and when they last changed.

The dst code adds ways to write and read the new key states and
timing metadata. It updates the parsing code for private key files
to not parse the newly introduced metadata (these are for state
files only).

Introduce key goal (the state the key wants to be in).

bin/dnssec/dnssec-settime.c
bin/dnssec/dnssec-settime.docbook
bin/dnssec/dnssectool.c
bin/dnssec/dnssectool.h
bin/tests/system/kasp/tests.sh
lib/dns/dst_api.c
lib/dns/dst_internal.h
lib/dns/dst_parse.c
lib/dns/include/dst/dst.h
lib/dns/win32/libdns.def.in

index bcf32c72b7973a0e9980385c175e4e03a50beb7a..cc72e55662fd128f0879985dacb21257d05d4bcc 100644 (file)
@@ -88,6 +88,15 @@ usage(void) {
        fprintf(stderr, "    -i <interval>: prepublication interval for "
                                           "successor key "
                                           "(default: 30 days)\n");
+       fprintf(stderr, "Key state options:\n");
+       fprintf(stderr, "    -s: update key state file (default no)\n");
+       fprintf(stderr, "    -g state: set the goal state for this key\n");
+       fprintf(stderr, "    -d state date/[+-]offset: set the DS state\n");
+       fprintf(stderr, "    -k state date/[+-]offset: set the DNSKEY state\n");
+       fprintf(stderr, "    -r state date/[+-]offset: set the RRSIG (KSK) "
+                                                     "state\n");
+       fprintf(stderr, "    -z state date/[+-]offset: set the RRSIG (ZSK) "
+                                                     "state\n");
        fprintf(stderr, "Printing options:\n");
        fprintf(stderr, "    -p C/P/Psync/A/R/I/D/Dsync/all: print a "
                                        "particular time value or values\n");
@@ -123,29 +132,87 @@ printtime(dst_key_t *key, int type, const char *tag, bool epoch,
        }
 }
 
+static void
+writekey(dst_key_t *key, const char *directory, bool write_state)
+{
+       char newname[1024];
+       char keystr[DST_KEY_FORMATSIZE];
+       isc_buffer_t buf;
+       isc_result_t result;
+       int options = DST_TYPE_PUBLIC|DST_TYPE_PRIVATE;
+
+       if (write_state) {
+               options |= DST_TYPE_STATE;
+       }
+
+       isc_buffer_init(&buf, newname, sizeof(newname));
+       result = dst_key_buildfilename(key, DST_TYPE_PUBLIC, directory, &buf);
+       if (result != ISC_R_SUCCESS) {
+               fatal("Failed to build public key filename: %s",
+                     isc_result_totext(result));
+       }
+
+       result = dst_key_tofile(key, options, directory);
+       if (result != ISC_R_SUCCESS) {
+               dst_key_format(key, keystr, sizeof(keystr));
+               fatal("Failed to write key %s: %s", keystr,
+                     isc_result_totext(result));
+       }
+       printf("%s\n", newname);
+
+       isc_buffer_clear(&buf);
+       result = dst_key_buildfilename(key, DST_TYPE_PRIVATE, directory, &buf);
+       if (result != ISC_R_SUCCESS) {
+               fatal("Failed to build private key filename: %s",
+                     isc_result_totext(result));
+       }
+       printf("%s\n", newname);
+
+       if (write_state) {
+               isc_buffer_clear(&buf);
+               result = dst_key_buildfilename(key, DST_TYPE_STATE, directory,
+                                              &buf);
+               if (result != ISC_R_SUCCESS) {
+                       fatal("Failed to build key state filename: %s",
+                             isc_result_totext(result));
+               }
+               printf("%s\n", newname);
+       }
+}
+
 int
 main(int argc, char **argv) {
        isc_result_t    result;
        const char      *engine = NULL;
        const char      *filename = NULL;
        char            *directory = NULL;
-       char            newname[1024];
        char            keystr[DST_KEY_FORMATSIZE];
        char            *endp, *p;
        int             ch;
        const char      *predecessor = NULL;
        dst_key_t       *prevkey = NULL;
        dst_key_t       *key = NULL;
-       isc_buffer_t    buf;
        dns_name_t      *name = NULL;
        dns_secalg_t    alg = 0;
        unsigned int    size = 0;
        uint16_t        flags = 0;
        int             prepub = -1;
+       int             options;
        dns_ttl_t       ttl = 0;
        isc_stdtime_t   now;
+       isc_stdtime_t   dstime = 0, dnskeytime = 0;
+       isc_stdtime_t   krrsigtime = 0, zrrsigtime = 0;
        isc_stdtime_t   pub = 0, act = 0, rev = 0, inact = 0, del = 0;
        isc_stdtime_t   prevact = 0, previnact = 0, prevdel = 0;
+       dst_key_state_t goal = DST_KEY_STATE_NA;
+       dst_key_state_t ds = DST_KEY_STATE_NA;
+       dst_key_state_t dnskey = DST_KEY_STATE_NA;
+       dst_key_state_t krrsig = DST_KEY_STATE_NA;
+       dst_key_state_t zrrsig = DST_KEY_STATE_NA;
+       bool    setgoal = false, setds = false, setdnskey = false;
+       bool    setkrrsig = false, setzrrsig = false;
+       bool    setdstime = false, setdnskeytime = false;
+       bool    setkrrsigtime = false, setzrrsigtime = false;
        bool    setpub = false, setact = false;
        bool    setrev = false, setinact = false;
        bool    setdel = false, setttl = false;
@@ -156,14 +223,17 @@ main(int argc, char **argv) {
        bool    printact = false,  printrev = false;
        bool    printinact = false, printdel = false;
        bool    force = false;
-       bool   epoch = false;
-       bool   changed = false;
+       bool    epoch = false;
+       bool    changed = false;
+       bool    write_state = false;
        isc_log_t       *log = NULL;
        isc_stdtime_t   syncadd = 0, syncdel = 0;
        bool    unsetsyncadd = false, setsyncadd = false;
        bool    unsetsyncdel = false, setsyncdel = false;
        bool    printsyncadd = false, printsyncdel = false;
 
+       options = DST_TYPE_PUBLIC|DST_TYPE_PRIVATE|DST_TYPE_STATE;
+
        if (argc == 1)
                usage();
 
@@ -180,7 +250,7 @@ main(int argc, char **argv) {
 
        isc_stdtime_get(&now);
 
-#define CMDLINE_FLAGS "A:D:E:fhI:i:K:L:P:p:R:S:uv:V"
+#define CMDLINE_FLAGS "A:D:d:E:fg:hI:i:K:k:L:P:p:R:r:S:suv:Vz:"
        while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
                switch (ch) {
                case 'E':
@@ -339,6 +409,70 @@ main(int argc, char **argv) {
                case 'i':
                        prepub = strtottl(isc_commandline_argument);
                        break;
+               case 's':
+                       write_state = true;
+                       break;
+               case 'g':
+                       if (setgoal) {
+                               fatal("-g specified more than once");
+                       }
+
+                       goal = strtokeystate(isc_commandline_argument);
+                       if (goal != DST_KEY_STATE_NA &&
+                           goal != DST_KEY_STATE_HIDDEN &&
+                           goal != DST_KEY_STATE_OMNIPRESENT) {
+                               fatal("-g must be either none, hidden, or "
+                                     "omnipresent");
+                       }
+                       setgoal = true;
+                       break;
+               case 'd':
+                       if (setds) {
+                               fatal("-d specified more than once");
+                       }
+
+                       ds = strtokeystate(isc_commandline_argument);
+                       setds = true;
+                       /* time */
+                       (void)isoptarg(isc_commandline_argument, argv, usage);
+                       dstime = strtotime(isc_commandline_argument,
+                                          now, now, &setdstime);
+                       break;
+               case 'k':
+                       if (setdnskey) {
+                               fatal("-k specified more than once");
+                       }
+
+                       dnskey = strtokeystate(isc_commandline_argument);
+                       setdnskey = true;
+                       /* time */
+                       (void)isoptarg(isc_commandline_argument, argv, usage);
+                       dnskeytime = strtotime(isc_commandline_argument,
+                                              now, now, &setdnskeytime);
+                       break;
+               case 'r':
+                       if (setkrrsig) {
+                               fatal("-r specified more than once");
+                       }
+
+                       krrsig = strtokeystate(isc_commandline_argument);
+                       setkrrsig = true;
+                       /* time */
+                       (void)isoptarg(isc_commandline_argument, argv, usage);
+                       krrsigtime = strtotime(isc_commandline_argument,
+                                              now, now, &setkrrsigtime);
+                       break;
+               case 'z':
+                       if (setzrrsig) {
+                               fatal("-z specified more than once");
+                       }
+
+                       zrrsig = strtokeystate(isc_commandline_argument);
+                       setzrrsig = true;
+                       (void)isoptarg(isc_commandline_argument, argv, usage);
+                       zrrsigtime = strtotime(isc_commandline_argument,
+                                              now, now, &setzrrsigtime);
+                       break;
                case '?':
                        if (isc_commandline_option != '?')
                                fprintf(stderr, "%s: invalid argument -%c\n",
@@ -365,6 +499,12 @@ main(int argc, char **argv) {
        if (argc > isc_commandline_index + 1)
                fatal("Extraneous arguments");
 
+       if ((setgoal || setds || setdnskey || setkrrsig || setzrrsig) &&
+           !write_state)
+       {
+               fatal("Options -g, -d, -k, -r and -z require -s to be set");
+       }
+
        result = dst_lib_init(mctx, engine);
        if (result != ISC_R_SUCCESS)
                fatal("Could not initialize dst: %s",
@@ -381,9 +521,7 @@ main(int argc, char **argv) {
                if (setact || unsetact)
                        fatal("-S and -A cannot be used together");
 
-               result = dst_key_fromnamedfile(predecessor, directory,
-                                              DST_TYPE_PUBLIC |
-                                              DST_TYPE_PRIVATE,
+               result = dst_key_fromnamedfile(predecessor, directory, options,
                                               mctx, &prevkey);
                if (result != ISC_R_SUCCESS)
                        fatal("Invalid keyfile %s: %s",
@@ -475,9 +613,8 @@ main(int argc, char **argv) {
                              isc_result_totext(result));
        }
 
-       result = dst_key_fromnamedfile(filename, directory,
-                                      DST_TYPE_PUBLIC | DST_TYPE_PRIVATE,
-                                      mctx, &key);
+       result = dst_key_fromnamedfile(filename, directory, options, mctx,
+                                      &key);
        if (result != ISC_R_SUCCESS)
                fatal("Invalid keyfile %s: %s",
                      filename, isc_result_totext(result));
@@ -588,6 +725,63 @@ main(int argc, char **argv) {
                changed = true;
        }
 
+       /*
+        * Make sure the key state goals are written.
+        */
+       if (write_state) {
+               if (setgoal) {
+                       if (goal == DST_KEY_STATE_NA) {
+                               dst_key_unsetstate(key, DST_KEY_GOAL);
+                       } else {
+                               dst_key_setstate(key, DST_KEY_GOAL, goal);
+                       }
+                       changed = true;
+               }
+               if (setds) {
+                       if (ds == DST_KEY_STATE_NA) {
+                               dst_key_unsetstate(key, DST_KEY_DS);
+                               dst_key_unsettime(key, DST_TIME_DS);
+                       } else {
+                               dst_key_setstate(key, DST_KEY_DS, ds);
+                               dst_key_settime(key, DST_TIME_DS, dstime);
+                       }
+                       changed = true;
+               }
+               if (setdnskey) {
+                       if (dnskey == DST_KEY_STATE_NA) {
+                               dst_key_unsetstate(key, DST_KEY_DNSKEY);
+                               dst_key_unsettime(key, DST_TIME_DNSKEY);
+                       } else {
+                               dst_key_setstate(key, DST_KEY_DNSKEY, dnskey);
+                               dst_key_settime(key, DST_TIME_DNSKEY,
+                                               dnskeytime);
+                       }
+                       changed = true;
+               }
+               if (setkrrsig) {
+                       if (krrsig == DST_KEY_STATE_NA) {
+                               dst_key_unsetstate(key, DST_KEY_KRRSIG);
+                               dst_key_unsettime(key, DST_TIME_KRRSIG);
+                       } else {
+                               dst_key_setstate(key, DST_KEY_KRRSIG, krrsig);
+                               dst_key_settime(key, DST_TIME_KRRSIG,
+                                               krrsigtime);
+                       }
+                       changed = true;
+               }
+               if (setzrrsig) {
+                       if (zrrsig == DST_KEY_STATE_NA) {
+                               dst_key_unsetstate(key, DST_KEY_ZRRSIG);
+                               dst_key_unsettime(key, DST_TIME_ZRRSIG);
+                       } else {
+                               dst_key_setstate(key, DST_KEY_ZRRSIG, zrrsig);
+                               dst_key_settime(key, DST_TIME_ZRRSIG,
+                                               zrrsigtime);
+                       }
+                       changed = true;
+               }
+       }
+
        if (!changed && setttl)
                changed = true;
 
@@ -621,32 +815,7 @@ main(int argc, char **argv) {
                          epoch, stdout);
 
        if (changed) {
-               isc_buffer_init(&buf, newname, sizeof(newname));
-               result = dst_key_buildfilename(key, DST_TYPE_PUBLIC, directory,
-                                              &buf);
-               if (result != ISC_R_SUCCESS) {
-                       fatal("Failed to build public key filename: %s",
-                             isc_result_totext(result));
-               }
-
-               result = dst_key_tofile(key, DST_TYPE_PUBLIC|DST_TYPE_PRIVATE,
-                                       directory);
-               if (result != ISC_R_SUCCESS) {
-                       dst_key_format(key, keystr, sizeof(keystr));
-                       fatal("Failed to write key %s: %s", keystr,
-                             isc_result_totext(result));
-               }
-
-               printf("%s\n", newname);
-
-               isc_buffer_clear(&buf);
-               result = dst_key_buildfilename(key, DST_TYPE_PRIVATE, directory,
-                                              &buf);
-               if (result != ISC_R_SUCCESS) {
-                       fatal("Failed to build private key filename: %s",
-                             isc_result_totext(result));
-               }
-               printf("%s\n", newname);
+               writekey(key, directory, write_state);
        }
 
        if (prevkey != NULL)
index bf432bd44e6fb3ebbad8132e49cc61b01530733d..905d72af8f7c1a2075a44cc10fed3f59ab78923b 100644 (file)
       <arg choice="opt" rep="norepeat"><option>-V</option></arg>
       <arg choice="opt" rep="norepeat"><option>-v <replaceable class="parameter">level</replaceable></option></arg>
       <arg choice="opt" rep="norepeat"><option>-E <replaceable class="parameter">engine</replaceable></option></arg>
+      <arg choice="opt" rep="norepeat"><option>-s</option></arg>
+      <arg choice="opt" rep="norepeat"><option>-g <replaceable class="parameter">state</replaceable></option></arg>
+      <arg choice="opt" rep="norepeat"><option>-d <replaceable class="parameter">state</replaceable> <replaceable class="parameter">date/offset</replaceable></option></arg>
+      <arg choice="opt" rep="norepeat"><option>-k <replaceable class="parameter">state</replaceable> <replaceable class="parameter">date/offset</replaceable></option></arg>
+      <arg choice="opt" rep="norepeat"><option>-r <replaceable class="parameter">state</replaceable> <replaceable class="parameter">date/offset</replaceable></option></arg>
+      <arg choice="opt" rep="norepeat"><option>-z <replaceable class="parameter">state</replaceable> <replaceable class="parameter">date/offset</replaceable></option></arg>
       <arg choice="req" rep="norepeat">keyfile</arg>
     </cmdsynopsis>
   </refsynopsisdiv>
       When key metadata fields are changed, both files of a key
       pair (<filename>Knnnn.+aaa+iiiii.key</filename> and
       <filename>Knnnn.+aaa+iiiii.private</filename>) are regenerated.
+    </para>
+    <para>
       Metadata fields are stored in the private file.  A human-readable
       description of the metadata is also placed in comments in the key
       file.  The private file's permissions are always set to be
       inaccessible to anyone other than the owner (mode 0600).
     </para>
+    <para>
+      When working with state files, it is possible to update the timing
+      metadata in those files as well with <option>-s</option>.  If this
+      option is used you can also update key states with <option>-d</option>
+      (DS), <option>-k</option> (DNSKEY), <option>-r</option> (RRSIG of KSK),
+      or <option>-z</option> (RRSIG of ZSK). Allowed states are HIDDEN,
+      RUMOURED, OMNIPRESENT, and UNRETENTIVE.
+    </para>
+    <para>
+      You can also set the goal state of the key with <option>-g</option>.
+      This should be either HIDDEN or OMNIPRESENT (representing whether the
+      key should be removed from the zone, or published).
+    </para>
+    <para>
+      It is NOT RECOMMENDED to manipulate state files manually except for
+      testing purposes.
+    </para>
   </refsection>
 
   <refsection><info><title>OPTIONS</title></info>
     </variablelist>
   </refsection>
 
+  <refsection><info><title>KEY STATE OPTIONS</title></info>
+
+    <para>
+      Known key states are HIDDEN, RUMOURED, OMNIPRESENT and UNRETENTIVE.
+      These should not be set manually except for testing purposes.
+    </para>
+
+    <variablelist>
+
+      <varlistentry>
+        <term>-s</term>
+        <listitem>
+          <para>
+           When setting key timing data, also update the state file.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-g</term>
+        <listitem>
+          <para>
+           Set the goal state for this key. Must be HIDDEN or OMNIPRESENT.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-d</term>
+        <listitem>
+          <para>
+           Set the DS state for this key, and when it was last changed.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-k</term>
+        <listitem>
+          <para>
+           Set the DNSKEY state for this key, and when it was last changed.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-r</term>
+        <listitem>
+          <para>
+           Set the RRSIG (KSK) state for this key, and when it was last
+           changed.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-z</term>
+        <listitem>
+          <para>
+           Set the RRSIG (ZSK) state for this key, and when it was last
+           changed.
+          </para>
+        </listitem>
+      </varlistentry>
+
+    </variablelist>
+  </refsection>
+
   <refsection><info><title>PRINTING OPTIONS</title></info>
 
     <para>
index d409965fed51ba79737b471b8b53de9a36cf7f20..aadd2ee39ce1b4fff83a3cb66e82e465c01e4f52 100644 (file)
 
 #include "dnssectool.h"
 
+#define KEYSTATES_NVALUES 4
+static const char *keystates[KEYSTATES_NVALUES] = {
+       "hidden", "rumoured", "omnipresent", "unretentive",
+};
+
 int verbose = 0;
 bool quiet = false;
 uint8_t dtype[8];
@@ -244,6 +249,21 @@ strtottl(const char *str) {
        return (ttl);
 }
 
+dst_key_state_t
+strtokeystate(const char *str) {
+       if (isnone(str)) {
+               return (DST_KEY_STATE_NA);
+       }
+
+       for (int i = 0; i < KEYSTATES_NVALUES; i++) {
+               if (keystates[i] != NULL &&
+                   strcasecmp(str, keystates[i]) == 0) {
+                       return (dst_key_state_t) i;
+               }
+       }
+       fatal("unknown key state");
+}
+
 isc_stdtime_t
 strtotime(const char *str, int64_t now, int64_t base,
          bool *setp)
index cddfb2f902b1b259a2bb5c16b83772ee57465e16..3e88e2959eb57c6ec30ea897453723b519a6620a 100644 (file)
@@ -71,6 +71,8 @@ cleanup_logging(isc_log_t **logp);
 
 dns_ttl_t strtottl(const char *str);
 
+dst_key_state_t strtokeystate(const char *str);
+
 isc_stdtime_t
 strtotime(const char *str, int64_t now, int64_t base,
          bool *setp);
index 92de7567d8a19d670cd84f758a69af38675f01f6..d7353ab9fa7fb6837cd0811f7aa2b4f7ef89c2b1 100644 (file)
@@ -17,7 +17,6 @@ set -e
 
 status=0
 n=0
-log=1
 
 ################################################################################
 # Utilities                                                                    #
@@ -35,9 +34,14 @@ get_keyids() {
        ls ${start}*${end} | sed "s/$dir\/K${zone}.+${algorithm}+\([0-9]\{5\}\)${end}/\1/"
 }
 
+# By default log errors and don't quit immediately.
+_log=1
+_continue=1
 log_error() {
-       test $log -eq 1 && echo_i "error: $1"
+       test $_log -eq 1 && echo_i "error: $1"
        ret=$((ret+1))
+
+       test $_continue -eq 1 || exit 1
 }
 
 # Check the created key in directory $1 for zone $2.
@@ -115,18 +119,6 @@ check_created_key() {
 #
 # dnssec-keygen
 #
-n=$((n+1))
-echo_i "check that 'dnssec-keygen -k' (default policy) creates valid files ($n)"
-ret=0
-$KEYGEN -k _default kasp > keygen.out._default.test$n 2>/dev/null || ret=1
-lines=$(cat keygen.out._default.test$n | wc -l)
-test "$lines" -eq 1 || log_error "wrong number of keys created for policy _default"
-KEY_ID=$(get_keyids "." "kasp" "13")
-echo_i "check key $KEY_ID..."
-check_created_key "." "kasp" "csk" $KEY_ID "13" "ECDSAP256SHA256" "256" "3600" "0"
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status+ret))
-
 n=$((n+1))
 echo_i "check that 'dnssec-keygen -k' (configured policy) creates valid files ($n)"
 ret=0
@@ -138,7 +130,7 @@ KEY_ID=$(get_keyids "keys" "kasp" "13")
 echo_i "check key $KEY_ID..."
 check_created_key "keys" "kasp" "csk" $KEY_ID "13" "ECDSAP256SHA256" "256" "200" "31536000"
 # Temporarily don't log errors because we are searching multiple files.
-log=0
+_log=0
 # Check the other algorithm.
 KEY_IDS=$(get_keyids "keys" "kasp" "8")
 for KEY_ID in $KEY_IDS; do
@@ -151,10 +143,21 @@ for KEY_ID in $KEY_IDS; do
        # If ret is non-zero, non of the files matched.
        test "$ret" -eq 0 || echo_i "failed"
        status=$((status+ret))
-
 done
 # Turn error logs on again.
-log=1
+_log=1
+
+n=$((n+1))
+echo_i "check that 'dnssec-keygen -k' (default policy) creates valid files ($n)"
+ret=0
+$KEYGEN -k _default kasp > keygen.out._default.test$n 2>/dev/null || ret=1
+lines=$(cat keygen.out._default.test$n | wc -l)
+test "$lines" -eq 1 || log_error "wrong number of keys created for policy _default"
+KEY_ID=$(get_keyids "." "kasp" "13")
+echo_i "check key $KEY_ID..."
+check_created_key "." "kasp" "csk" $KEY_ID "13" "ECDSAP256SHA256" "256" "3600" "0"
+test "$ret" -eq 0 || echo_i "failed"
+status=$((status+ret))
 
 n=$((n+1))
 echo_i "check that 'dnssec-keygen -k' (default policy) creates valid files ($n)"
@@ -171,6 +174,45 @@ status=$((status+ret))
 #
 # dnssec-settime
 #
+BASE_FILE="K${zone}.+${alg_numpad}+${key_idpad}"
+KEY_FILE="${BASE_FILE}.key"
+PRIVATE_FILE="${BASE_FILE}.private"
+STATE_FILE="${BASE_FILE}.state"
+CMP_FILE="${BASE_FILE}.cmp"
+
+n=$((n+1))
+echo_i "check that 'dnssec-settime' by default does not edit key state file ($n)"
+ret=0
+cp $STATE_FILE $CMP_FILE
+$SETTIME -P +3600 $BASE_FILE >/dev/null || log_error "settime failed"
+grep "; Publish: " $KEY_FILE > /dev/null || log_error "mismatch published in $KEY_FILE"
+grep "Publish: " $PRIVATE_FILE > /dev/null || log_error "mismatch published in $PRIVATE_FILE"
+$DIFF $CMP_FILE $STATE_FILE || log_error "unexpected file change in $STATE_FILE"
+test "$ret" -eq 0 || echo_i "failed"
+status=$((status+ret))
+
+n=$((n+1))
+echo_i "check that 'dnssec-settime -s' also sets time metadata in key state file ($n)"
+ret=0
+cp $STATE_FILE $CMP_FILE
+now=$(date +%Y%m%d%H%M%S)
+$SETTIME -s -P $now $BASE_FILE >/dev/null || log_error "settime failed"
+grep "; Publish: $now" $KEY_FILE > /dev/null || log_error "mismatch published in $KEY_FILE"
+grep "Publish: $now" $PRIVATE_FILE > /dev/null || log_error "mismatch published in $PRIVATE_FILE"
+grep "Published: $now" $STATE_FILE > /dev/null || log_error "mismatch published in $STATE_FILE"
+test "$ret" -eq 0 || echo_i "failed"
+status=$((status+ret))
+
+n=$((n+1))
+echo_i "check that 'dnssec-settime -s' also unsets time metadata in key state file ($n)"
+ret=0
+cp $STATE_FILE $CMP_FILE
+$SETTIME -s -P none $BASE_FILE >/dev/null || log_error "settime failed"
+grep "; Publish:" $KEY_FILE > /dev/null && log_error "unexpected published in $KEY_FILE"
+grep "Publish:" $PRIVATE_FILE > /dev/null && log_error "unexpected published in $PRIVATE_FILE"
+grep "Published:" $STATE_FILE > /dev/null && log_error "unexpected published in $STATE_FILE"
+test "$ret" -eq 0 || echo_i "failed"
+status=$((status+ret))
 
 #
 # named
index eaacd89209811f8eaf1f32fb79070c0e9fb57857..978f8839c48aca7ed6b2ce0c8c1e82fc38430250 100644 (file)
                goto cleanup;                                   \
        }
 
-#define NEXTTOKEN_OR_EOF(lex, opt, token) {                    \
-       ret = isc_lex_gettoken(lex, opt, token);                \
-       if (ret == ISC_R_EOF)                                   \
-               break;                                          \
-       if (ret != ISC_R_SUCCESS)                               \
-               goto cleanup;                                   \
-       }
+#define NEXTTOKEN_OR_EOF(lex, opt, token)                      \
+       do {                                                    \
+               ret = isc_lex_gettoken(lex, opt, token);        \
+               if (ret == ISC_R_EOF)                           \
+                       break;                                  \
+               if (ret != ISC_R_SUCCESS)                       \
+                       goto cleanup;                           \
+       } while ((*token).type == isc_tokentype_eol);           \
 
 #define READLINE(lex, opt, token)                              \
        do {                                                    \
                ret = isc_lex_gettoken(lex, opt, token);        \
                if (ret == ISC_R_EOF)                           \
                        break;                                  \
-               else if (ret != ISC_R_SUCCESS)                  \
+               if (ret != ISC_R_SUCCESS)                       \
                        goto cleanup;                           \
        } while ((*token).type != isc_tokentype_eol)
 
 
 #define NUMERIC_NTAGS (DST_MAX_NUMERIC + 1)
 static const char *numerictags[NUMERIC_NTAGS] = {
+       "Predecessor:",
+       "Successor:",
+       "MaxTTL:",
+       "RollPeriod:",
        "Lifetime:"
 };
 
@@ -108,18 +113,38 @@ static const char *timingtags[TIMING_NTAGS] = {
        "Generated:",
        "Published:",
        "Active:",
-       "Retired:",
        "Revoked:",
+       "Retired:",
        "Removed:",
 
        "DSPublish:",
        "SyncPublish:",
-       "SyncDelete:"
+       "SyncDelete:",
+
+       "DNSKEYChange:",
+       "ZRRSIGChange:",
+       "KRRSIGChange:",
+       "DSChange:"
+};
+
+#define KEYSTATES_NTAGS (DST_MAX_KEYSTATES + 1)
+static const char *keystatestags[KEYSTATES_NTAGS] = {
+       "DNSKEYState:",
+       "ZRRSIGState:",
+       "KRRSIGState:",
+       "DSState:",
+       "GoalState:"
+};
+
+#define KEYSTATES_NVALUES 4
+static const char *keystates[KEYSTATES_NVALUES] = {
+       "hidden", "rumoured", "omnipresent", "unretentive",
 };
 
 #define STATE_ALGORITHM_STR "Algorithm:"
 #define STATE_LENGTH_STR "Length:"
-#define MAX_NTAGS (DST_MAX_NUMERIC + DST_MAX_BOOLEAN + DST_MAX_TIMES)
+#define MAX_NTAGS (DST_MAX_NUMERIC + DST_MAX_BOOLEAN + \
+                  DST_MAX_TIMES + DST_MAX_KEYSTATES)
 
 static dst_func_t *dst_t_func[DST_MAX_ALGS];
 
@@ -604,10 +629,23 @@ dst_key_fromnamedfile(const char *filename, const char *dirname,
                return (result);
        }
 
+       key = get_key_struct(pubkey->key_name, pubkey->key_alg,
+                            pubkey->key_flags, pubkey->key_proto,
+                            pubkey->key_size, pubkey->key_class,
+                            pubkey->key_ttl, mctx);
+       if (key == NULL) {
+               dst_key_free(&pubkey);
+               return (ISC_R_NOMEMORY);
+       }
+
+       if (key->func->parse == NULL)
+               RETERR(DST_R_UNSUPPORTEDALG);
+
        /*
         * Read the state file, if requested by type.
         */
        if ((type & DST_TYPE_STATE) != 0) {
+
                newfilenamelen = strlen(filename) + 7;
                if (dirname != NULL) {
                        newfilenamelen += strlen(dirname) + 1;
@@ -617,23 +655,17 @@ dst_key_fromnamedfile(const char *filename, const char *dirname,
                                   dirname, filename, ".state");
                INSIST(result == ISC_R_SUCCESS);
 
-               result = dst_key_read_state(newfilename, mctx, pubkey);
+               result = dst_key_read_state(newfilename, mctx, &key);
+               if (result == ISC_R_FILENOTFOUND) {
+                       /* Having no state is valid. */
+                       result = ISC_R_SUCCESS;
+               }
+
                isc_mem_put(mctx, newfilename, newfilenamelen);
                newfilename = NULL;
                RETERR(result);
        }
 
-       key = get_key_struct(pubkey->key_name, pubkey->key_alg,
-                            pubkey->key_flags, pubkey->key_proto, 0,
-                            pubkey->key_class, pubkey->key_ttl, mctx);
-       if (key == NULL) {
-               dst_key_free(&pubkey);
-               return (ISC_R_NOMEMORY);
-       }
-
-       if (key->func->parse == NULL)
-               RETERR(DST_R_UNSUPPORTEDALG);
-
        newfilenamelen = strlen(filename) + 9;
        if (dirname != NULL)
                newfilenamelen += strlen(dirname) + 1;
@@ -1611,17 +1643,32 @@ find_timingdata(const char *s) {
        return (find_metadata(s, timingtags, TIMING_NTAGS));
 }
 
+static int
+find_keystatedata(const char *s) {
+       return (find_metadata(s, keystatestags, KEYSTATES_NTAGS));
+}
+
+static isc_result_t
+keystate_fromtext(const char *s, dst_key_state_t *state) {
+       for (int i = 0; i < KEYSTATES_NVALUES; i++) {
+               if (keystates[i] != NULL && strcasecmp(s, keystates[i]) == 0) {
+                       *state = (dst_key_state_t) i;
+                       return (ISC_R_SUCCESS);
+               }
+       }
+       return (ISC_R_NOTFOUND);
+}
 
 /*%
  * Reads a key state from disk.
  */
 isc_result_t
-dst_key_read_state(const char *filename, isc_mem_t *mctx, dst_key_t *key)
+dst_key_read_state(const char *filename, isc_mem_t *mctx, dst_key_t **keyp)
 {
        isc_lex_t *lex = NULL;
        isc_token_t token;
        isc_result_t ret;
-       unsigned int opt = ISC_LEXOPT_DNSMULTILINE;
+       unsigned int opt = ISC_LEXOPT_EOL;
 
        ret = isc_lex_create(mctx, 1500, &lex);
        if (ret != ISC_R_SUCCESS) {
@@ -1634,6 +1681,11 @@ dst_key_read_state(const char *filename, isc_mem_t *mctx, dst_key_t *key)
                goto cleanup;
        }
 
+       /*
+        * Read the comment line.
+        */
+       READLINE(lex, opt, &token);
+
        /*
         * Read the algorithm line.
         */
@@ -1646,11 +1698,13 @@ dst_key_read_state(const char *filename, isc_mem_t *mctx, dst_key_t *key)
 
        NEXTTOKEN(lex, opt | ISC_LEXOPT_NUMBER, &token);
        if (token.type != isc_tokentype_number ||
-               token.value.as_ulong != (unsigned long) dst_key_alg(key))
+               token.value.as_ulong != (unsigned long) dst_key_alg(*keyp))
        {
                BADTOKEN();
        }
 
+       READLINE(lex, opt, &token);
+
        /*
         * Read the length line.
         */
@@ -1663,11 +1717,13 @@ dst_key_read_state(const char *filename, isc_mem_t *mctx, dst_key_t *key)
 
        NEXTTOKEN(lex, opt | ISC_LEXOPT_NUMBER, &token);
        if (token.type != isc_tokentype_number ||
-               token.value.as_ulong != (unsigned long) dst_key_size(key))
+               token.value.as_ulong != (unsigned long) dst_key_size(*keyp))
        {
                BADTOKEN();
        }
 
+       READLINE(lex, opt, &token);
+
        /*
         * Read the metadata.
         */
@@ -1675,6 +1731,9 @@ dst_key_read_state(const char *filename, isc_mem_t *mctx, dst_key_t *key)
                int tag;
 
                NEXTTOKEN_OR_EOF(lex, opt, &token);
+               if (ret == ISC_R_EOF) {
+                       break;
+               }
                if (token.type != isc_tokentype_string) {
                        BADTOKEN();
                }
@@ -1682,12 +1741,14 @@ dst_key_read_state(const char *filename, isc_mem_t *mctx, dst_key_t *key)
                /* Numeric metadata */
                tag = find_numericdata(DST_AS_STR(token));
                if (tag >= 0) {
+                       INSIST(tag < NUMERIC_NTAGS);
+
                        NEXTTOKEN(lex, opt | ISC_LEXOPT_NUMBER, &token);
                        if (token.type != isc_tokentype_number) {
                                BADTOKEN();
                        }
-                       dst_key_setnum(key, tag, token.value.as_ulong);
 
+                       dst_key_setnum(*keyp, tag, token.value.as_ulong);
                        goto next;
                }
 
@@ -1700,14 +1761,14 @@ dst_key_read_state(const char *filename, isc_mem_t *mctx, dst_key_t *key)
                        if (token.type != isc_tokentype_string) {
                                BADTOKEN();
                        }
+
                        if (strcmp(DST_AS_STR(token), "yes") == 0) {
-                               dst_key_setbool(key, tag, true);
+                               dst_key_setbool(*keyp, tag, true);
                        } else if (strcmp(DST_AS_STR(token), "no") == 0) {
-                               dst_key_setbool(key, tag, false);
+                               dst_key_setbool(*keyp, tag, false);
                        } else {
                                BADTOKEN();
                        }
-
                        goto next;
                }
 
@@ -1727,13 +1788,35 @@ dst_key_read_state(const char *filename, isc_mem_t *mctx, dst_key_t *key)
                        if (ret != ISC_R_SUCCESS) {
                                goto cleanup;
                        }
-                       dst_key_settime(key, tag, when);
 
+                       dst_key_settime(*keyp, tag, when);
+                       goto next;
+               }
+
+               /* Keystate metadata */
+               tag = find_keystatedata(DST_AS_STR(token));
+               if (tag >= 0) {
+                       dst_key_state_t state;
+
+                       INSIST(tag < KEYSTATES_NTAGS);
+
+                       NEXTTOKEN(lex, opt, &token);
+                       if (token.type != isc_tokentype_string) {
+                               BADTOKEN();
+                       }
+
+                       ret = keystate_fromtext(DST_AS_STR(token), &state);
+                       if (ret != ISC_R_SUCCESS) {
+                               goto cleanup;
+                       }
+
+                       dst_key_setstate(*keyp, tag, state);
                        goto next;
                }
 
 next:
                READLINE(lex, opt, &token);
+
        }
 
        /* Done, successfully parsed the whole file. */
@@ -1847,6 +1930,21 @@ printtime(const dst_key_t *key, int type, const char *tag, FILE *stream) {
        fprintf(stream, "%s: (set, unable to display)\n", tag);
 }
 
+/*%
+ * Write key state metadata to a file pointer, preceded by 'tag'
+ */
+static void
+printstate(const dst_key_t *key, int type, const char *tag, FILE *stream) {
+       isc_result_t result;
+       dst_key_state_t value = 0;
+
+       result = dst_key_getstate(key, type, &value);
+       if (result != ISC_R_SUCCESS) {
+               return;
+       }
+       fprintf(stream, "%s: %s\n", tag, keystates[value]);
+}
+
 /*%
  * Writes a key state to disk.
  */
@@ -1898,6 +1996,11 @@ write_key_state(const dst_key_t *key, int type, const char *directory) {
                fprintf(fp, "Algorithm: %u\n", key->key_alg);
                fprintf(fp, "Length: %u\n", key->key_size);
 
+               printnum(key, DST_NUM_LIFETIME, "Lifetime", fp);
+
+               printbool(key, DST_BOOL_KSK, "KSK", fp);
+               printbool(key, DST_BOOL_ZSK, "ZSK", fp);
+
                printtime(key, DST_TIME_CREATED, "Generated", fp);
                printtime(key, DST_TIME_PUBLISH, "Published", fp);
                printtime(key, DST_TIME_ACTIVATE, "Active", fp);
@@ -1905,10 +2008,16 @@ write_key_state(const dst_key_t *key, int type, const char *directory) {
                printtime(key, DST_TIME_REVOKE, "Revoked", fp);
                printtime(key, DST_TIME_DELETE, "Removed", fp);
 
-               printnum(key, DST_NUM_LIFETIME, "Lifetime", fp);
+               printtime(key, DST_TIME_DNSKEY, "DNSKEYChange", fp);
+               printtime(key, DST_TIME_ZRRSIG, "ZRRSIGChange", fp);
+               printtime(key, DST_TIME_KRRSIG, "KRRSIGChange", fp);
+               printtime(key, DST_TIME_DS, "DSChange", fp);
 
-               printbool(key, DST_BOOL_KSK, "KSK", fp);
-               printbool(key, DST_BOOL_ZSK, "ZSK", fp);
+               printstate(key, DST_KEY_DNSKEY, "DNSKEYState", fp);
+               printstate(key, DST_KEY_ZRRSIG, "ZRRSIGState", fp);
+               printstate(key, DST_KEY_KRRSIG, "KRRSIGState", fp);
+               printstate(key, DST_KEY_DS, "DSState", fp);
+               printstate(key, DST_KEY_GOAL, "GoalState", fp);
        }
 
        fflush(fp);
index 463954fa53ab9de369ae243bd6f6476f03abdfba..0d8be243d8200f57b144ae4723afd65ebaf71133 100644 (file)
@@ -108,11 +108,16 @@ struct dst_key {
 
        isc_stdtime_t   times[DST_MAX_TIMES + 1];    /*%< timing metadata */
        bool            timeset[DST_MAX_TIMES + 1];  /*%< data set? */
+
        uint32_t        nums[DST_MAX_NUMERIC + 1];   /*%< numeric metadata */
        bool            numset[DST_MAX_NUMERIC + 1]; /*%< data set? */
+
        bool            bools[DST_MAX_BOOLEAN + 1];   /*%< boolean metadata */
        bool            boolset[DST_MAX_BOOLEAN + 1]; /*%< data set? */
 
+       dst_key_state_t keystates[DST_MAX_KEYSTATES + 1]; /*%< key states */
+       bool            keystateset[DST_MAX_KEYSTATES + 1]; /*%< data set? */
+
        bool    inactive;      /*%< private key not present as it is inactive */
        bool    external;      /*%< external key */
 
index 7130c29501f3e2c665a7a72f0eef6cd9f6ae5582..e989be5301acdedcb1a7558754d147dab98d896e 100644 (file)
@@ -61,7 +61,11 @@ static const char *timetags[TIMING_NTAGS] = {
        "Delete:",
        "DSPublish:",
        "SyncPublish:",
-       "SyncDelete:"
+       "SyncDelete:",
+       NULL,
+       NULL,
+       NULL,
+       NULL
 };
 
 #define NUMERIC_NTAGS (DST_MAX_NUMERIC + 1)
@@ -69,7 +73,8 @@ static const char *numerictags[NUMERIC_NTAGS] = {
        "Predecessor:",
        "Successor:",
        "MaxTTL:",
-       "RollPeriod:"
+       "RollPeriod:",
+       NULL
 };
 
 struct parse_map {
@@ -754,7 +759,7 @@ dst__privstruct_writefile(const dst_key_t *key, const dst_private_t *priv,
 
                        if (timetags[i] != NULL) {
                                fprintf(fp, "%s %.*s\n", timetags[i],
-                                       (int)r.length, r.base);
+                                       (int)r.length, r.base);
                        }
                }
        }
index e92de43417a456e5ce26ffd592ca2afee1313166..7f8517c4a91b209bd0c2ad3ff04ab76623af96f0 100644 (file)
@@ -44,6 +44,38 @@ ISC_LANG_BEGINDECLS
 typedef struct dst_key         dst_key_t;
 typedef struct dst_context     dst_context_t;
 
+/*%
+ * Key states for the DNSSEC records related to a key: DNSKEY, RRSIG (ksk),
+ * RRSIG (zsk), and DS.
+ *
+ * DST_KEY_STATE_HIDDEN:      Records of this type are not published in zone.
+ *                            This may be because the key parts were never
+ *                            introduced in the zone, or because the key has
+ *                            retired and has no records of this type left in
+ *                            the zone.
+ * DST_KEY_STATE_RUMOURED:    Records of this type are published in zone, but
+ *                            not long enough to ensure all resolvers know
+ *                            about it.
+ * DST_KEY_STATE_OMNIPRESENT: Records of this type are published in zone long
+ *                            enough so that all resolvers that know about
+ *                            these records, no longer have outdated data.
+ * DST_KEY_STATE_UNRETENTIVE: Records of this type have been removed from the
+ *                            zone, but there may be resolvers that still have
+ *                            have predecessor records cached.  Note that RRSIG
+ *                            records in this state may actually still be in the
+ *                            zone because they are reused, but retired RRSIG
+ *                            records will never be refreshed: A successor key
+ *                            is used to create signatures.
+ * DST_KEY_STATE_NA:          The state is not applicable for this record type.
+ */
+typedef enum dst_key_state {
+       DST_KEY_STATE_HIDDEN = 0,
+       DST_KEY_STATE_RUMOURED = 1,
+       DST_KEY_STATE_OMNIPRESENT = 2,
+       DST_KEY_STATE_UNRETENTIVE = 3,
+       DST_KEY_STATE_NA = 4
+} dst_key_state_t;
+
 /* DST algorithm codes */
 #define DST_ALG_UNKNOWN                0
 #define DST_ALG_RSA            1 /* Used for parsing RSASHA1, RSASHA256 and RSASHA512 */
@@ -97,7 +129,11 @@ typedef struct dst_context  dst_context_t;
 #define DST_TIME_DSPUBLISH     6
 #define DST_TIME_SYNCPUBLISH   7
 #define DST_TIME_SYNCDELETE    8
-#define DST_MAX_TIMES          8
+#define DST_TIME_DNSKEY                9
+#define DST_TIME_ZRRSIG                10
+#define DST_TIME_KRRSIG                11
+#define DST_TIME_DS            12
+#define DST_MAX_TIMES          12
 
 /* Numeric metadata definitions */
 #define DST_NUM_PREDECESSOR    0
@@ -112,6 +148,14 @@ typedef struct dst_context         dst_context_t;
 #define DST_BOOL_ZSK           1
 #define DST_MAX_BOOLEAN                1
 
+/* Key state metadata definitions */
+#define DST_KEY_DNSKEY         0
+#define DST_KEY_ZRRSIG         1
+#define DST_KEY_KRRSIG         2
+#define DST_KEY_DS             3
+#define DST_KEY_GOAL           4
+#define DST_MAX_KEYSTATES      4
+
 /*
  * Current format version number of the private key parser.
  *
@@ -391,7 +435,7 @@ dst_key_read_public(const char *filename, int type,
  */
 
 isc_result_t
-dst_key_read_state(const char *filename, isc_mem_t *mctx, dst_key_t *keyp);
+dst_key_read_state(const char *filename, isc_mem_t *mctx, dst_key_t **keyp);
 /*%<
  * Reads a key state from permanent storage.
  *
@@ -929,6 +973,38 @@ dst_key_unsettime(dst_key_t *key, int type);
  *     "type" is no larger than DST_MAX_TIMES
  */
 
+isc_result_t
+dst_key_getstate(const dst_key_t *key, int type, dst_key_state_t *statep);
+/*%<
+ * Get a member of the keystate metadata array and place it in '*statep'.
+ *
+ * Requires:
+ *     "key" is a valid key.
+ *     "type" is no larger than DST_MAX_KEYSTATES
+ *     "statep" is not null.
+ */
+
+void
+dst_key_setstate(dst_key_t *key, int type, dst_key_state_t state);
+/*%<
+ * Set a member of the keystate metadata array.
+ *
+ * Requires:
+ *     "key" is a valid key.
+ *     "state" is a valid state.
+ *     "type" is no larger than DST_MAX_KEYSTATES
+ */
+
+void
+dst_key_unsetstate(dst_key_t *key, int type);
+/*%<
+ * Flag a member of the keystate metadata array as "not set".
+ *
+ * Requires:
+ *     "key" is a valid key.
+ *     "type" is no larger than DST_MAX_KEYSTATES
+ */
+
 isc_result_t
 dst_key_getprivateformat(const dst_key_t *key, int *majorp, int *minorp);
 /*%<
index a5c7949118fb28f0c080b5b06a04a9786136178b..153efc2e7b6062ad64a7c455444d0df8e2a6120a 100644 (file)
@@ -1411,6 +1411,7 @@ dst_key_getfilename
 dst_key_getgssctx
 dst_key_getnum
 dst_key_getprivateformat
+dst_key_getstate
 dst_key_gettime
 dst_key_getttl
 dst_key_id
@@ -1436,6 +1437,7 @@ dst_key_setflags
 dst_key_setinactive
 dst_key_setnum
 dst_key_setprivateformat
+dst_key_setstate
 dst_key_settime
 dst_key_setttl
 dst_key_sigsize
@@ -1446,6 +1448,7 @@ dst_key_todns
 dst_key_tofile
 dst_key_unsetbool
 dst_key_unsetnum
+dst_key_unsetstate
 dst_key_unsettime
 dst_lib_destroy
 dst_lib_init