From: Matthijs Mekking Date: Thu, 12 Sep 2019 09:45:10 +0000 (+0200) Subject: Add functionality to read key state from disk X-Git-Tag: v9.15.6~26^2~26 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c55625b0359f3c4d777efd882001d80de53962e7;p=thirdparty%2Fbind9.git Add functionality to read key state from disk When reading a key from file, you can set the DST_TYPE_STATE option to also read the key state. This expects the Algorithm and Length fields go above the metadata, so update the write functionality to do so accordingly. Introduce new DST metadata types for KSK, ZSK, Lifetime and the timing metadata used in state files. --- diff --git a/lib/dns/dst_api.c b/lib/dns/dst_api.c index 97cc044da0e..eaacd892098 100644 --- a/lib/dns/dst_api.c +++ b/lib/dns/dst_api.c @@ -64,6 +64,63 @@ #define DST_AS_STR(t) ((t).value.as_textregion.base) +#define NEXTTOKEN(lex, opt, token) { \ + ret = isc_lex_gettoken(lex, opt, token); \ + if (ret != ISC_R_SUCCESS) \ + 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 READLINE(lex, opt, token) \ + do { \ + ret = isc_lex_gettoken(lex, opt, token); \ + if (ret == ISC_R_EOF) \ + break; \ + else if (ret != ISC_R_SUCCESS) \ + goto cleanup; \ + } while ((*token).type != isc_tokentype_eol) + +#define BADTOKEN() { \ + ret = ISC_R_UNEXPECTEDTOKEN; \ + goto cleanup; \ + } + +#define NUMERIC_NTAGS (DST_MAX_NUMERIC + 1) +static const char *numerictags[NUMERIC_NTAGS] = { + "Lifetime:" +}; + +#define BOOLEAN_NTAGS (DST_MAX_BOOLEAN + 1) +static const char *booleantags[BOOLEAN_NTAGS] = { + "KSK:", + "ZSK:" +}; + +#define TIMING_NTAGS (DST_MAX_TIMES + 1) +static const char *timingtags[TIMING_NTAGS] = { + "Generated:", + "Published:", + "Active:", + "Retired:", + "Revoked:", + "Removed:", + + "DSPublish:", + "SyncPublish:", + "SyncDelete:" +}; + +#define STATE_ALGORITHM_STR "Algorithm:" +#define STATE_LENGTH_STR "Length:" +#define MAX_NTAGS (DST_MAX_NUMERIC + DST_MAX_BOOLEAN + DST_MAX_TIMES) + static dst_func_t *dst_t_func[DST_MAX_ALGS]; static bool dst_initialized = false; @@ -84,7 +141,7 @@ static dst_key_t * get_key_struct(const dns_name_t *name, static isc_result_t write_public_key(const dst_key_t *key, int type, const char *directory); static isc_result_t write_key_state(const dst_key_t *key, int type, - const char *directory); + const char *directory); static isc_result_t buildfilename(dns_name_t *name, dns_keytag_t id, unsigned int alg, @@ -424,7 +481,8 @@ dst_key_getfilename(dns_name_t *name, dns_keytag_t id, REQUIRE(dst_initialized == true); REQUIRE(dns_name_isabsolute(name)); - REQUIRE((type & (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC)) != 0); + REQUIRE((type & + (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC | DST_TYPE_STATE)) != 0); REQUIRE(mctx != NULL); REQUIRE(buf != NULL); @@ -546,6 +604,25 @@ dst_key_fromnamedfile(const char *filename, const char *dirname, return (result); } + /* + * 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; + } + newfilename = isc_mem_get(mctx, newfilenamelen); + result = addsuffix(newfilename, newfilenamelen, + dirname, filename, ".state"); + INSIST(result == ISC_R_SUCCESS); + + result = dst_key_read_state(newfilename, mctx, pubkey); + 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); @@ -1396,7 +1473,7 @@ dst_key_setinactive(dst_key_t *key, bool inactive) { } /*% - * Reads a public key from disk + * Reads a public key from disk. */ isc_result_t dst_key_read_public(const char *filename, int type, @@ -1438,17 +1515,6 @@ dst_key_read_public(const char *filename, int type, if (ret != ISC_R_SUCCESS) goto cleanup; -#define NEXTTOKEN(lex, opt, token) { \ - ret = isc_lex_gettoken(lex, opt, token); \ - if (ret != ISC_R_SUCCESS) \ - goto cleanup; \ - } - -#define BADTOKEN() { \ - ret = ISC_R_UNEXPECTEDTOKEN; \ - goto cleanup; \ - } - /* Read the domain name */ NEXTTOKEN(lex, opt, &token); if (token.type != isc_tokentype_string) @@ -1521,6 +1587,165 @@ dst_key_read_public(const char *filename, int type, return (ret); } +static int +find_metadata(const char *s, const char *tags[], int ntags) { + for (int i = 0; i < ntags; i++) { + if (tags[i] != NULL && strcasecmp(s, tags[i]) == 0) + return (i); + } + return (-1); +} + +static int +find_numericdata(const char *s) { + return (find_metadata(s, numerictags, NUMERIC_NTAGS)); +} + +static int +find_booleandata(const char *s) { + return (find_metadata(s, booleantags, BOOLEAN_NTAGS)); +} + +static int +find_timingdata(const char *s) { + return (find_metadata(s, timingtags, TIMING_NTAGS)); +} + + +/*% + * Reads a key state from disk. + */ +isc_result_t +dst_key_read_state(const char *filename, isc_mem_t *mctx, dst_key_t *key) +{ + isc_lex_t *lex = NULL; + isc_token_t token; + isc_result_t ret; + unsigned int opt = ISC_LEXOPT_DNSMULTILINE; + + ret = isc_lex_create(mctx, 1500, &lex); + if (ret != ISC_R_SUCCESS) { + goto cleanup; + } + isc_lex_setcomments(lex, ISC_LEXCOMMENT_DNSMASTERFILE); + + ret = isc_lex_openfile(lex, filename); + if (ret != ISC_R_SUCCESS) { + goto cleanup; + } + + /* + * Read the algorithm line. + */ + NEXTTOKEN(lex, opt, &token); + if (token.type != isc_tokentype_string || + strcmp(DST_AS_STR(token), STATE_ALGORITHM_STR) != 0) + { + BADTOKEN(); + } + + NEXTTOKEN(lex, opt | ISC_LEXOPT_NUMBER, &token); + if (token.type != isc_tokentype_number || + token.value.as_ulong != (unsigned long) dst_key_alg(key)) + { + BADTOKEN(); + } + + /* + * Read the length line. + */ + NEXTTOKEN(lex, opt, &token); + if (token.type != isc_tokentype_string || + strcmp(DST_AS_STR(token), STATE_LENGTH_STR) != 0) + { + BADTOKEN(); + } + + NEXTTOKEN(lex, opt | ISC_LEXOPT_NUMBER, &token); + if (token.type != isc_tokentype_number || + token.value.as_ulong != (unsigned long) dst_key_size(key)) + { + BADTOKEN(); + } + + /* + * Read the metadata. + */ + for (int n = 0; n < MAX_NTAGS; n++) { + int tag; + + NEXTTOKEN_OR_EOF(lex, opt, &token); + if (token.type != isc_tokentype_string) { + BADTOKEN(); + } + + /* Numeric metadata */ + tag = find_numericdata(DST_AS_STR(token)); + if (tag >= 0) { + NEXTTOKEN(lex, opt | ISC_LEXOPT_NUMBER, &token); + if (token.type != isc_tokentype_number) { + BADTOKEN(); + } + dst_key_setnum(key, tag, token.value.as_ulong); + + goto next; + } + + /* Boolean metadata */ + tag = find_booleandata(DST_AS_STR(token)); + if (tag >= 0) { + INSIST(tag < BOOLEAN_NTAGS); + + NEXTTOKEN(lex, opt, &token); + if (token.type != isc_tokentype_string) { + BADTOKEN(); + } + if (strcmp(DST_AS_STR(token), "yes") == 0) { + dst_key_setbool(key, tag, true); + } else if (strcmp(DST_AS_STR(token), "no") == 0) { + dst_key_setbool(key, tag, false); + } else { + BADTOKEN(); + } + + goto next; + } + + /* Timing metadata */ + tag = find_timingdata(DST_AS_STR(token)); + if (tag >= 0) { + uint32_t when; + + INSIST(tag < TIMING_NTAGS); + + NEXTTOKEN(lex, opt, &token); + if (token.type != isc_tokentype_string) { + BADTOKEN(); + } + + ret = dns_time32_fromtext(DST_AS_STR(token), &when); + if (ret != ISC_R_SUCCESS) { + goto cleanup; + } + dst_key_settime(key, tag, when); + + goto next; + } + +next: + READLINE(lex, opt, &token); + } + + /* Done, successfully parsed the whole file. */ + ret = ISC_R_SUCCESS; + +cleanup: + if (lex != NULL) { + isc_lex_destroy(&lex); + } + return (ret); +} + static bool issymmetric(const dst_key_t *key) { REQUIRE(dst_initialized == true); @@ -1670,6 +1895,9 @@ write_key_state(const dst_key_t *key, int type, const char *directory) { } fputc('\n', fp); + fprintf(fp, "Algorithm: %u\n", key->key_alg); + fprintf(fp, "Length: %u\n", key->key_size); + printtime(key, DST_TIME_CREATED, "Generated", fp); printtime(key, DST_TIME_PUBLISH, "Published", fp); printtime(key, DST_TIME_ACTIVATE, "Active", fp); @@ -1678,8 +1906,6 @@ write_key_state(const dst_key_t *key, int type, const char *directory) { printtime(key, DST_TIME_DELETE, "Removed", fp); printnum(key, DST_NUM_LIFETIME, "Lifetime", fp); - fprintf(fp, "Algorithm: %u\n", key->key_alg); - fprintf(fp, "Length: %u\n", key->key_size); printbool(key, DST_BOOL_KSK, "KSK", fp); printbool(key, DST_BOOL_ZSK, "ZSK", fp); diff --git a/lib/dns/include/dst/dst.h b/lib/dns/include/dst/dst.h index f747619a40e..e92de43417a 100644 --- a/lib/dns/include/dst/dst.h +++ b/lib/dns/include/dst/dst.h @@ -317,16 +317,17 @@ dst_key_fromfile(dns_name_t *name, dns_keytag_t id, unsigned int alg, int type, const char *directory, isc_mem_t *mctx, dst_key_t **keyp); /*%< * Reads a key from permanent storage. The key can either be a public or - * private key, and is specified by name, algorithm, and id. If a private key - * is specified, the public key must also be present. If directory is NULL, - * the current directory is assumed. + * private key, or a key state. It specified by name, algorithm, and id. If + * a private key or key state is specified, the public key must also be + * present. If directory is NULL, the current directory is assumed. * * Requires: * \li "name" is a valid absolute dns name. * \li "id" is a valid key tag identifier. * \li "alg" is a supported key algorithm. - * \li "type" is DST_TYPE_PUBLIC, DST_TYPE_PRIVATE, or the bitwise union. - * DST_TYPE_KEY look for a KEY record otherwise DNSKEY + * \li "type" is DST_TYPE_PUBLIC, DST_TYPE_PRIVATE or the bitwise union. + * DST_TYPE_KEY look for a KEY record otherwise DNSKEY. + * DST_TYPE_STATE to also read the key state. * \li "mctx" is a valid memory context. * \li "keyp" is not NULL and "*keyp" is NULL. * @@ -343,8 +344,8 @@ dst_key_fromnamedfile(const char *filename, const char *dirname, int type, isc_mem_t *mctx, dst_key_t **keyp); /*%< * Reads a key from permanent storage. The key can either be a public or - * key, and is specified by filename. If a private key is specified, the - * public key must also be present. + * private key, or a key stae. It is specified by filename. If a private key + * or key state is specified, the public key must also be present. * * If 'dirname' is not NULL, and 'filename' is a relative path, * then the file is looked up relative to the given directory. @@ -352,8 +353,9 @@ dst_key_fromnamedfile(const char *filename, const char *dirname, * * Requires: * \li "filename" is not NULL - * \li "type" is DST_TYPE_PUBLIC, DST_TYPE_PRIVATE, or the bitwise union - * DST_TYPE_KEY look for a KEY record otherwise DNSKEY + * \li "type" is DST_TYPE_PUBLIC, DST_TYPE_PRIVATE, or the bitwise union. + * DST_TYPE_KEY look for a KEY record otherwise DNSKEY. + * DST_TYPE_STATE to also read the key state. * \li "mctx" is a valid memory context * \li "keyp" is not NULL and "*keyp" is NULL. * @@ -373,9 +375,9 @@ dst_key_read_public(const char *filename, int type, * Reads a public key from permanent storage. The key must be a public key. * * Requires: - * \li "filename" is not NULL - * \li "type" is DST_TYPE_KEY look for a KEY record otherwise DNSKEY - * \li "mctx" is a valid memory context + * \li "filename" is not NULL. + * \li "type" is DST_TYPE_KEY look for a KEY record otherwise DNSKEY. + * \li "mctx" is a valid memory context. * \li "keyp" is not NULL and "*keyp" is NULL. * * Returns: @@ -388,6 +390,22 @@ dst_key_read_public(const char *filename, int type, * \li If successful, *keyp will contain a valid key. */ +isc_result_t +dst_key_read_state(const char *filename, isc_mem_t *mctx, dst_key_t *keyp); +/*%< + * Reads a key state from permanent storage. + * + * Requires: + * \li "filename" is not NULL. + * \li "mctx" is a valid memory context. + * \li "keyp" is not NULL and "*keyp" is NULL. + * + * Returns: + * \li ISC_R_SUCCESS + * \li ISC_R_UNEXPECTEDTOKEN if the file can not be parsed as a public key + * \li any other result indicates failure + */ + isc_result_t dst_key_tofile(const dst_key_t *key, int type, const char *directory); /*%< diff --git a/lib/dns/win32/libdns.def.in b/lib/dns/win32/libdns.def.in index beef70a5d7b..a5c7949118f 100644 --- a/lib/dns/win32/libdns.def.in +++ b/lib/dns/win32/libdns.def.in @@ -1425,6 +1425,7 @@ dst_key_privatefrombuffer dst_key_proto dst_key_pubcompare dst_key_read_public +dst_key_read_state dst_key_restore dst_key_rid dst_key_secretsize