]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Add functionality to read key state from disk
authorMatthijs Mekking <matthijs@isc.org>
Thu, 12 Sep 2019 09:45:10 +0000 (11:45 +0200)
committerMatthijs Mekking <matthijs@isc.org>
Wed, 6 Nov 2019 21:31:45 +0000 (22:31 +0100)
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.

lib/dns/dst_api.c
lib/dns/include/dst/dst.h
lib/dns/win32/libdns.def.in

index 97cc044da0eb852a63c8aadd434c99069dba2028..eaacd89209811f8eaf1f32fb79070c0e9fb57857 100644 (file)
 
 #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);
index f747619a40ef9d0362a0aaefe4592da894a5f6aa..e92de43417a456e5ce26ffd592ca2afee1313166 100644 (file)
@@ -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);
 /*%<
index beef70a5d7b5e1ddcf861d5ac353f8708520c6ab..a5c7949118fb28f0c080b5b06a04a9786136178b 100644 (file)
@@ -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