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