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");
}
}
+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;
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();
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':
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",
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",
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",
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));
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;
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)
<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>
#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];
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)
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);
status=0
n=0
-log=1
################################################################################
# Utilities #
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.
#
# 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
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
# 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)"
#
# 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
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:"
};
"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];
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;
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;
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) {
goto cleanup;
}
+ /*
+ * Read the comment line.
+ */
+ READLINE(lex, opt, &token);
+
/*
* Read the algorithm line.
*/
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.
*/
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.
*/
int tag;
NEXTTOKEN_OR_EOF(lex, opt, &token);
+ if (ret == ISC_R_EOF) {
+ break;
+ }
if (token.type != isc_tokentype_string) {
BADTOKEN();
}
/* 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;
}
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;
}
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. */
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.
*/
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);
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);
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 */
"Delete:",
"DSPublish:",
"SyncPublish:",
- "SyncDelete:"
+ "SyncDelete:",
+ NULL,
+ NULL,
+ NULL,
+ NULL
};
#define NUMERIC_NTAGS (DST_MAX_NUMERIC + 1)
"Predecessor:",
"Successor:",
"MaxTTL:",
- "RollPeriod:"
+ "RollPeriod:",
+ NULL
};
struct parse_map {
if (timetags[i] != NULL) {
fprintf(fp, "%s %.*s\n", timetags[i],
- (int)r.length, r.base);
+ (int)r.length, r.base);
}
}
}
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 */
#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
#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.
*
*/
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.
*
* "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);
/*%<
dst_key_getgssctx
dst_key_getnum
dst_key_getprivateformat
+dst_key_getstate
dst_key_gettime
dst_key_getttl
dst_key_id
dst_key_setinactive
dst_key_setnum
dst_key_setprivateformat
+dst_key_setstate
dst_key_settime
dst_key_setttl
dst_key_sigsize
dst_key_tofile
dst_key_unsetbool
dst_key_unsetnum
+dst_key_unsetstate
dst_key_unsettime
dst_lib_destroy
dst_lib_init