I had a need to export individual keys from the keyrings used by our WebKDC and main WebLogin servers to ensure that our dev pool and production pool had a common key from which to pivot around. This allowed the previous keys used to still be valid but also provide the shared key to cut-over to our next-gen servers. Digging into the keyring.c library I noticed that most of the framework was already in the library and that only a couple accessory functions needed to be written for the wa_keyring binary. The patch for this is below.
From d824d427eff88c318877d64e0dfe3b2a56e4191f Mon Sep 17 00:00:00 2001
From: Greg Kuchyt <gkuchyt@uvm.edu>
Date: Tue, 7 Jun 2016 08:44:37 -0400
Subject: [PATCH] Add export/import functionality of individual keys in wa_keyring
---
tools/wa_keyring.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 68 insertions(+), 0 deletions(-)
diff --git a/tools/wa_keyring.c b/tools/wa_keyring.c
index e78f7fb..44ad3b0 100644
--- a/tools/wa_keyring.c
+++ b/tools/wa_keyring.c
@@ -32,6 +32,7 @@ Usage: %s [-hv] -f <keyring> list\n\
\n\
Functions:\n\
add <valid-after> # add a new random key\n\
+ export <id> # export key by id\n\
gc <oldest-valid-after-to-keep> # garbage collect old keys\n\
list # list keys\n\
remove <id> # remove key by id\n\
@@ -273,6 +274,60 @@ list_keyring(struct webauth_context *ctx, const char *keyring, bool verbose)
}
}
+/*
+ * Export the key at the given slot such that it can be imported into a
+ * keyring on a different server. Thus allowing keys to be synchronized
+ * across servers that have divergent key histories.
+ */
+static void
+export_key(struct webauth_context *ctx, const char *keyring, unsigned long n)
+{
+ struct webauth_keyring *ring;
+ struct webauth_keyring *newring;
+ const char *newkeyring = "export_wakeyring";
+ struct webauth_keyring_entry *entry;
+ int s;
+
+ s = webauth_keyring_read(ctx, keyring, &ring);
+ if (s != WA_ERR_NONE)
+ die_webauth(ctx, s, "cannot read keyring %s", keyring);
+
+ entry = &APR_ARRAY_IDX(ring->entries, n, struct webauth_keyring_entry);
+ newring = webauth_keyring_new(ctx, 1);
+ webauth_keyring_add(ctx, newring, entry->creation, entry->valid_after, entry->key);
+ s = webauth_keyring_write(ctx, newring, newkeyring);
+ if (s != WA_ERR_NONE)
+ die_webauth(ctx, s, "cannot write new keyring %s", newkeyring);
+}
+
+/*
+ * Import one keyring into another keyring. This allows keys from another
+ * server to be synchronized with servers that have a divergent key history.
+ */
+static void
+import_key(struct webauth_context *ctx, const char *keyring, const char *impkeyring) {
+ struct webauth_keyring *ring;
+ struct webauth_keyring *impring;
+ struct webauth_keyring_entry *impentry;
+ int s;
+ size_t i;
+
+ s = webauth_keyring_read(ctx, keyring, &ring);
+ if (s != WA_ERR_NONE)
+ die_webauth(ctx, s, "cannot read keyring %s", keyring);
+
+ s = webauth_keyring_read(ctx, impkeyring, &impring);
+ if (s != WA_ERR_NONE)
+ die_webauth(ctx, s, "cannot read keyring %s", impkeyring);
+
+ for (i = 0; i < (size_t) impring->entries->nelts; i++) {
+ impentry = &APR_ARRAY_IDX(impring->entries, i, struct webauth_keyring_entry);
+ webauth_keyring_add(ctx, ring, impentry->creation, impentry->valid_after, impentry->key);
+ }
+ s = webauth_keyring_write(ctx, ring, keyring);
+ if (s != WA_ERR_NONE)
+ die_webauth(ctx, s, "cannot write new keyring %s", keyring);
+}
/*
* Add a new key to a keyring. Takes the path to the keyring and the offset
@@ -418,6 +473,19 @@ main(int argc, char **argv)
if (argc > 0)
usage(1);
list_keyring(ctx, keyring, verbose);
+ } else if (strcmp(command, "export") == 0) {
+ if (argc != 1)
+ usage(1);
+ errno = 0;
+ id = strtoul(argv[0], &end, 10);
+ if (errno != 0 || *end != '\0')
+ die("invalid key id: %s", argv[0]);
+ export_key(ctx, keyring, id);
+ } else if (strcmp(command, "import") == 0) {
+ if (argc != 1)
+ usage(1);
+ errno = 0;
+ import_key(ctx, keyring, argv[0]);
} else if (strcmp(command, "add") == 0) {
if (argc != 1)
usage(1);
--
1.7.1