summaryrefslogtreecommitdiff
path: root/keyutils-1.5.6/keyctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'keyutils-1.5.6/keyctl.c')
-rw-r--r--keyutils-1.5.6/keyctl.c1762
1 files changed, 1762 insertions, 0 deletions
diff --git a/keyutils-1.5.6/keyctl.c b/keyutils-1.5.6/keyctl.c
new file mode 100644
index 0000000..a137e08
--- /dev/null
+++ b/keyutils-1.5.6/keyctl.c
@@ -0,0 +1,1762 @@
+/* keyctl.c: key control program
+ *
+ * Copyright (C) 2005, 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <errno.h>
+#include <asm/unistd.h>
+#include "keyutils.h"
+
+struct command {
+ int (*action)(int argc, char *argv[]);
+ const char *name;
+ const char *format;
+};
+
+static int act_keyctl___version(int argc, char *argv[]);
+static int act_keyctl_show(int argc, char *argv[]);
+static int act_keyctl_add(int argc, char *argv[]);
+static int act_keyctl_padd(int argc, char *argv[]);
+static int act_keyctl_request(int argc, char *argv[]);
+static int act_keyctl_request2(int argc, char *argv[]);
+static int act_keyctl_prequest2(int argc, char *argv[]);
+static int act_keyctl_update(int argc, char *argv[]);
+static int act_keyctl_pupdate(int argc, char *argv[]);
+static int act_keyctl_newring(int argc, char *argv[]);
+static int act_keyctl_revoke(int argc, char *argv[]);
+static int act_keyctl_clear(int argc, char *argv[]);
+static int act_keyctl_link(int argc, char *argv[]);
+static int act_keyctl_unlink(int argc, char *argv[]);
+static int act_keyctl_search(int argc, char *argv[]);
+static int act_keyctl_read(int argc, char *argv[]);
+static int act_keyctl_pipe(int argc, char *argv[]);
+static int act_keyctl_print(int argc, char *argv[]);
+static int act_keyctl_list(int argc, char *argv[]);
+static int act_keyctl_rlist(int argc, char *argv[]);
+static int act_keyctl_describe(int argc, char *argv[]);
+static int act_keyctl_rdescribe(int argc, char *argv[]);
+static int act_keyctl_chown(int argc, char *argv[]);
+static int act_keyctl_chgrp(int argc, char *argv[]);
+static int act_keyctl_setperm(int argc, char *argv[]);
+static int act_keyctl_session(int argc, char *argv[]);
+static int act_keyctl_instantiate(int argc, char *argv[]);
+static int act_keyctl_pinstantiate(int argc, char *argv[]);
+static int act_keyctl_negate(int argc, char *argv[]);
+static int act_keyctl_timeout(int argc, char *argv[]);
+static int act_keyctl_security(int argc, char *argv[]);
+static int act_keyctl_new_session(int argc, char *argv[]);
+static int act_keyctl_reject(int argc, char *argv[]);
+static int act_keyctl_reap(int argc, char *argv[]);
+static int act_keyctl_purge(int argc, char *argv[]);
+static int act_keyctl_invalidate(int argc, char *argv[]);
+
+const struct command commands[] = {
+ { act_keyctl___version, "--version", "" },
+ { act_keyctl_add, "add", "<type> <desc> <data> <keyring>" },
+ { act_keyctl_chgrp, "chgrp", "<key> <gid>" },
+ { act_keyctl_chown, "chown", "<key> <uid>" },
+ { act_keyctl_clear, "clear", "<keyring>" },
+ { act_keyctl_describe, "describe", "<keyring>" },
+ { act_keyctl_instantiate, "instantiate","<key> <data> <keyring>" },
+ { act_keyctl_invalidate,"invalidate", "<key>" },
+ { act_keyctl_link, "link", "<key> <keyring>" },
+ { act_keyctl_list, "list", "<keyring>" },
+ { act_keyctl_negate, "negate", "<key> <timeout> <keyring>" },
+ { act_keyctl_new_session, "new_session", "" },
+ { act_keyctl_newring, "newring", "<name> <keyring>" },
+ { act_keyctl_padd, "padd", "<type> <desc> <keyring>" },
+ { act_keyctl_pinstantiate, "pinstantiate","<key> <keyring>" },
+ { act_keyctl_pipe, "pipe", "<key>" },
+ { act_keyctl_prequest2, "prequest2", "<type> <desc> [<dest_keyring>]" },
+ { act_keyctl_print, "print", "<key>" },
+ { act_keyctl_pupdate, "pupdate", "<key>" },
+ { act_keyctl_purge, "purge", "<type>" },
+ { NULL, "purge", "[-p] [-i] <type> <desc>" },
+ { NULL, "purge", "-s <type> <desc>" },
+ { act_keyctl_rdescribe, "rdescribe", "<keyring> [sep]" },
+ { act_keyctl_read, "read", "<key>" },
+ { act_keyctl_reap, "reap", "[-v]" },
+ { act_keyctl_reject, "reject", "<key> <timeout> <error> <keyring>" },
+ { act_keyctl_request, "request", "<type> <desc> [<dest_keyring>]" },
+ { act_keyctl_request2, "request2", "<type> <desc> <info> [<dest_keyring>]" },
+ { act_keyctl_revoke, "revoke", "<key>" },
+ { act_keyctl_rlist, "rlist", "<keyring>" },
+ { act_keyctl_search, "search", "<keyring> <type> <desc> [<dest_keyring>]" },
+ { act_keyctl_security, "security", "<key>" },
+ { act_keyctl_session, "session", "" },
+ { NULL, "session", "- [<prog> <arg1> <arg2> ...]" },
+ { NULL, "session", "<name> [<prog> <arg1> <arg2> ...]" },
+ { act_keyctl_setperm, "setperm", "<key> <mask>" },
+ { act_keyctl_show, "show", "[-x] [<keyring>]" },
+ { act_keyctl_timeout, "timeout", "<key> <timeout>" },
+ { act_keyctl_unlink, "unlink", "<key> [<keyring>]" },
+ { act_keyctl_update, "update", "<key> <data>" },
+ { NULL, NULL, NULL }
+};
+
+static int dump_key_tree(key_serial_t keyring, const char *name, int hex_key_IDs);
+static void format(void) __attribute__((noreturn));
+static void error(const char *msg) __attribute__((noreturn));
+static key_serial_t get_key_id(const char *arg);
+
+static uid_t myuid;
+static gid_t mygid, *mygroups;
+static int myngroups;
+static int verbose;
+
+/*****************************************************************************/
+/*
+ * handle an error
+ */
+static inline void error(const char *msg)
+{
+ perror(msg);
+ exit(1);
+
+} /* end error() */
+
+/*****************************************************************************/
+/*
+ * execute the appropriate subcommand
+ */
+int main(int argc, char *argv[])
+{
+ const struct command *cmd, *best;
+ int n;
+
+ argv++;
+ argc--;
+
+ if (argc == 0)
+ format();
+
+ /* find the best fit command */
+ best = NULL;
+ n = strlen(*argv);
+
+ for (cmd = commands; cmd->name; cmd++) {
+ if (!cmd->action)
+ continue;
+ if (memcmp(cmd->name, *argv, n) != 0)
+ continue;
+
+ if (cmd->name[n] == 0) {
+ /* exact match */
+ best = cmd;
+ break;
+ }
+
+ /* partial match */
+ if (best) {
+ fprintf(stderr, "Ambiguous command\n");
+ exit(2);
+ }
+
+ best = cmd;
+ }
+
+ if (!best) {
+ fprintf(stderr, "Unknown command\n");
+ exit(2);
+ }
+
+ /* grab my UID, GID and groups */
+ myuid = geteuid();
+ mygid = getegid();
+ myngroups = getgroups(0, NULL);
+
+ if (myuid == -1 || mygid == -1 || myngroups == -1)
+ error("Unable to get UID/GID/#Groups\n");
+
+ mygroups = calloc(myngroups, sizeof(gid_t));
+ if (!mygroups)
+ error("calloc");
+
+ myngroups = getgroups(myngroups, mygroups);
+ if (myngroups < 0)
+ error("Unable to get Groups\n");
+
+ return best->action(argc, argv);
+
+} /* end main() */
+
+/*****************************************************************************/
+/*
+ * display command format information
+ */
+static void format(void)
+{
+ const struct command *cmd;
+
+ fprintf(stderr, "Format:\n");
+
+ for (cmd = commands; cmd->name; cmd++)
+ fprintf(stderr, " keyctl %s %s\n", cmd->name, cmd->format);
+
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Key/keyring ID:\n");
+ fprintf(stderr, " <nnn> numeric keyring ID\n");
+ fprintf(stderr, " @t thread keyring\n");
+ fprintf(stderr, " @p process keyring\n");
+ fprintf(stderr, " @s session keyring\n");
+ fprintf(stderr, " @u user keyring\n");
+ fprintf(stderr, " @us user default session keyring\n");
+ fprintf(stderr, " @g group keyring\n");
+ fprintf(stderr, " @a assumed request_key authorisation key\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, "<type> can be \"user\" for a user-defined keyring\n");
+ fprintf(stderr, "If you do this, prefix the description with \"<subtype>:\"\n");
+
+ exit(2);
+
+} /* end format() */
+
+/*****************************************************************************/
+/*
+ * Display version information
+ */
+static int act_keyctl___version(int argc, char *argv[])
+{
+ printf("keyctl from %s (Built %s)\n",
+ keyutils_version_string, keyutils_build_string);
+ return 0;
+}
+
+/*****************************************************************************/
+/*
+ * grab data from stdin
+ */
+static char *grab_stdin(size_t *_size)
+{
+ static char input[1024 * 1024 + 1];
+ int n, tmp;
+
+ n = 0;
+ do {
+ tmp = read(0, input + n, sizeof(input) - 1 - n);
+ if (tmp < 0)
+ error("stdin");
+
+ if (tmp == 0)
+ break;
+
+ n += tmp;
+
+ } while (n < sizeof(input));
+
+ if (n >= sizeof(input)) {
+ fprintf(stderr, "Too much data read on stdin\n");
+ exit(1);
+ }
+
+ input[n] = '\0';
+ *_size = n;
+
+ return input;
+
+} /* end grab_stdin() */
+
+/*****************************************************************************/
+/*
+ * convert the permissions mask to a string representing the permissions we
+ * have actually been granted
+ */
+static void calc_perms(char *pretty, key_perm_t perm, uid_t uid, gid_t gid)
+{
+ unsigned perms;
+ gid_t *pg;
+ int loop;
+
+ perms = (perm & KEY_POS_ALL) >> 24;
+
+ if (uid == myuid) {
+ perms |= (perm & KEY_USR_ALL) >> 16;
+ goto write_mask;
+ }
+
+ if (gid != -1) {
+ if (gid == mygid) {
+ perms |= (perm & KEY_GRP_ALL) >> 8;
+ goto write_mask;
+ }
+
+ pg = mygroups;
+ for (loop = myngroups; loop > 0; loop--, pg++) {
+ if (gid == *pg) {
+ perms |= (perm & KEY_GRP_ALL) >> 8;
+ goto write_mask;
+ }
+ }
+ }
+
+ perms |= (perm & KEY_OTH_ALL);
+
+write_mask:
+ sprintf(pretty, "--%c%c%c%c%c%c",
+ perms & KEY_OTH_SETATTR ? 'a' : '-',
+ perms & KEY_OTH_LINK ? 'l' : '-',
+ perms & KEY_OTH_SEARCH ? 's' : '-',
+ perms & KEY_OTH_WRITE ? 'w' : '-',
+ perms & KEY_OTH_READ ? 'r' : '-',
+ perms & KEY_OTH_VIEW ? 'v' : '-');
+
+} /* end calc_perms() */
+
+/*****************************************************************************/
+/*
+ * show the parent process's session keyring
+ */
+static int act_keyctl_show(int argc, char *argv[])
+{
+ key_serial_t keyring = KEY_SPEC_SESSION_KEYRING;
+ int hex_key_IDs = 0;
+
+ if (argc >= 2 && strcmp(argv[1], "-x") == 0) {
+ hex_key_IDs = 1;
+ argc--;
+ argv++;
+ }
+
+ if (argc > 2)
+ format();
+
+ if (argc == 2)
+ keyring = get_key_id(argv[1]);
+
+ dump_key_tree(keyring, argc == 2 ? "Keyring" : "Session Keyring", hex_key_IDs);
+ return 0;
+
+} /* end act_keyctl_show() */
+
+/*****************************************************************************/
+/*
+ * add a key
+ */
+static int act_keyctl_add(int argc, char *argv[])
+{
+ key_serial_t dest;
+ int ret;
+
+ if (argc != 5)
+ format();
+
+ dest = get_key_id(argv[4]);
+
+ ret = add_key(argv[1], argv[2], argv[3], strlen(argv[3]), dest);
+ if (ret < 0)
+ error("add_key");
+
+ /* print the resulting key ID */
+ printf("%d\n", ret);
+ return 0;
+
+} /* end act_keyctl_add() */
+
+/*****************************************************************************/
+/*
+ * add a key, reading from a pipe
+ */
+static int act_keyctl_padd(int argc, char *argv[])
+{
+ key_serial_t dest;
+ size_t datalen;
+ void *data;
+ int ret;
+
+
+ if (argc != 4)
+ format();
+
+ dest = get_key_id(argv[3]);
+
+ data = grab_stdin(&datalen);
+
+ ret = add_key(argv[1], argv[2], data, datalen, dest);
+ if (ret < 0)
+ error("add_key");
+
+ /* print the resulting key ID */
+ printf("%d\n", ret);
+ return 0;
+
+} /* end act_keyctl_padd() */
+
+/*****************************************************************************/
+/*
+ * request a key
+ */
+static int act_keyctl_request(int argc, char *argv[])
+{
+ key_serial_t dest;
+ int ret;
+
+ if (argc != 3 && argc != 4)
+ format();
+
+ dest = 0;
+ if (argc == 4)
+ dest = get_key_id(argv[3]);
+
+ ret = request_key(argv[1], argv[2], NULL, dest);
+ if (ret < 0)
+ error("request_key");
+
+ /* print the resulting key ID */
+ printf("%d\n", ret);
+ return 0;
+
+} /* end act_keyctl_request() */
+
+/*****************************************************************************/
+/*
+ * request a key, with recourse to /sbin/request-key
+ */
+static int act_keyctl_request2(int argc, char *argv[])
+{
+ key_serial_t dest;
+ int ret;
+
+ if (argc != 4 && argc != 5)
+ format();
+
+ dest = 0;
+ if (argc == 5)
+ dest = get_key_id(argv[4]);
+
+ ret = request_key(argv[1], argv[2], argv[3], dest);
+ if (ret < 0)
+ error("request_key");
+
+ /* print the resulting key ID */
+ printf("%d\n", ret);
+ return 0;
+
+} /* end act_keyctl_request2() */
+
+/*****************************************************************************/
+/*
+ * request a key, with recourse to /sbin/request-key, reading the callout info
+ * from a pipe
+ */
+static int act_keyctl_prequest2(int argc, char *argv[])
+{
+ char *args[6];
+ size_t datalen;
+
+ if (argc != 3 && argc != 4)
+ format();
+
+ args[0] = argv[0];
+ args[1] = argv[1];
+ args[2] = argv[2];
+ args[3] = grab_stdin(&datalen);
+ args[4] = argv[3];
+ args[5] = NULL;
+
+ return act_keyctl_request2(argc + 1, args);
+
+} /* end act_keyctl_prequest2() */
+
+/*****************************************************************************/
+/*
+ * update a key
+ */
+static int act_keyctl_update(int argc, char *argv[])
+{
+ key_serial_t key;
+
+ if (argc != 3)
+ format();
+
+ key = get_key_id(argv[1]);
+
+ if (keyctl_update(key, argv[2], strlen(argv[2])) < 0)
+ error("keyctl_update");
+
+ return 0;
+
+} /* end act_keyctl_update() */
+
+/*****************************************************************************/
+/*
+ * update a key, reading from a pipe
+ */
+static int act_keyctl_pupdate(int argc, char *argv[])
+{
+ key_serial_t key;
+ size_t datalen;
+ void *data;
+
+ if (argc != 2)
+ format();
+
+ key = get_key_id(argv[1]);
+ data = grab_stdin(&datalen);
+
+ if (keyctl_update(key, data, datalen) < 0)
+ error("keyctl_update");
+
+ return 0;
+
+} /* end act_keyctl_pupdate() */
+
+/*****************************************************************************/
+/*
+ * create a new keyring
+ */
+static int act_keyctl_newring(int argc, char *argv[])
+{
+ key_serial_t dest;
+ int ret;
+
+ if (argc != 3)
+ format();
+
+ dest = get_key_id(argv[2]);
+
+ ret = add_key("keyring", argv[1], NULL, 0, dest);
+ if (ret < 0)
+ error("add_key");
+
+ printf("%d\n", ret);
+ return 0;
+
+} /* end act_keyctl_newring() */
+
+/*****************************************************************************/
+/*
+ * revoke a key
+ */
+static int act_keyctl_revoke(int argc, char *argv[])
+{
+ key_serial_t key;
+
+ if (argc != 2)
+ format();
+
+ key = get_key_id(argv[1]);
+
+ if (keyctl_revoke(key) < 0)
+ error("keyctl_revoke");
+
+ return 0;
+
+} /* end act_keyctl_revoke() */
+
+/*****************************************************************************/
+/*
+ * clear a keyring
+ */
+static int act_keyctl_clear(int argc, char *argv[])
+{
+ key_serial_t keyring;
+
+ if (argc != 2)
+ format();
+
+ keyring = get_key_id(argv[1]);
+
+ if (keyctl_clear(keyring) < 0)
+ error("keyctl_clear");
+
+ return 0;
+
+} /* end act_keyctl_clear() */
+
+/*****************************************************************************/
+/*
+ * link a key to a keyring
+ */
+static int act_keyctl_link(int argc, char *argv[])
+{
+ key_serial_t keyring, key;
+
+ if (argc != 3)
+ format();
+
+ key = get_key_id(argv[1]);
+ keyring = get_key_id(argv[2]);
+
+ if (keyctl_link(key, keyring) < 0)
+ error("keyctl_link");
+
+ return 0;
+
+} /* end act_keyctl_link() */
+
+/*
+ * Attempt to unlink a key matching the ID
+ */
+static int act_keyctl_unlink_func(key_serial_t parent, key_serial_t key,
+ char *desc, int desc_len, void *data)
+{
+ key_serial_t *target = data;
+
+ if (key == *target)
+ return keyctl_unlink(key, parent) < 0 ? 0 : 1;
+ return 0;
+}
+
+/*
+ * Unlink a key from a keyring or from the session keyring tree.
+ */
+static int act_keyctl_unlink(int argc, char *argv[])
+{
+ key_serial_t keyring, key;
+ int n;
+
+ if (argc != 2 && argc != 3)
+ format();
+
+ key = get_key_id(argv[1]);
+
+ if (argc == 3) {
+ keyring = get_key_id(argv[2]);
+ if (keyctl_unlink(key, keyring) < 0)
+ error("keyctl_unlink");
+ } else {
+ n = recursive_session_key_scan(act_keyctl_unlink_func, &key);
+ printf("%d links removed\n", n);
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+/*
+ * search a keyring for a key
+ */
+static int act_keyctl_search(int argc, char *argv[])
+{
+ key_serial_t keyring, dest;
+ int ret;
+
+ if (argc != 4 && argc != 5)
+ format();
+
+ keyring = get_key_id(argv[1]);
+
+ dest = 0;
+ if (argc == 5)
+ dest = get_key_id(argv[4]);
+
+ ret = keyctl_search(keyring, argv[2], argv[3], dest);
+ if (ret < 0)
+ error("keyctl_search");
+
+ /* print the ID of the key we found */
+ printf("%d\n", ret);
+ return 0;
+
+} /* end act_keyctl_search() */
+
+/*****************************************************************************/
+/*
+ * read a key
+ */
+static int act_keyctl_read(int argc, char *argv[])
+{
+ key_serial_t key;
+ void *buffer;
+ char *p;
+ int ret, sep, col;
+
+ if (argc != 2)
+ format();
+
+ key = get_key_id(argv[1]);
+
+ /* read the key payload data */
+ ret = keyctl_read_alloc(key, &buffer);
+ if (ret < 0)
+ error("keyctl_read_alloc");
+
+ if (ret == 0) {
+ printf("No data in key\n");
+ return 0;
+ }
+
+ /* hexdump the contents */
+ printf("%u bytes of data in key:\n", ret);
+
+ sep = 0;
+ col = 0;
+ p = buffer;
+
+ do {
+ if (sep) {
+ putchar(sep);
+ sep = 0;
+ }
+
+ printf("%02hhx", *p);
+ p++;
+
+ col++;
+ if (col % 32 == 0)
+ sep = '\n';
+ else if (col % 4 == 0)
+ sep = ' ';
+
+ } while (--ret > 0);
+
+ printf("\n");
+ return 0;
+
+} /* end act_keyctl_read() */
+
+/*****************************************************************************/
+/*
+ * read a key and dump raw to stdout
+ */
+static int act_keyctl_pipe(int argc, char *argv[])
+{
+ key_serial_t key;
+ void *buffer;
+ int ret;
+
+ if (argc != 2)
+ format();
+
+ key = get_key_id(argv[1]);
+
+ /* read the key payload data */
+ ret = keyctl_read_alloc(key, &buffer);
+ if (ret < 0)
+ error("keyctl_read_alloc");
+
+ if (ret > 0 && write(1, buffer, ret) < 0)
+ error("write");
+ return 0;
+
+} /* end act_keyctl_pipe() */
+
+/*****************************************************************************/
+/*
+ * read a key and dump to stdout in printable form
+ */
+static int act_keyctl_print(int argc, char *argv[])
+{
+ key_serial_t key;
+ void *buffer;
+ char *p;
+ int loop, ret;
+
+ if (argc != 2)
+ format();
+
+ key = get_key_id(argv[1]);
+
+ /* read the key payload data */
+ ret = keyctl_read_alloc(key, &buffer);
+ if (ret < 0)
+ error("keyctl_read_alloc");
+
+ /* see if it's printable */
+ p = buffer;
+ for (loop = ret; loop > 0; loop--, p++)
+ if (!isprint(*p))
+ goto not_printable;
+
+ /* it is */
+ printf("%s\n", (char *) buffer);
+ return 0;
+
+not_printable:
+ /* it isn't */
+ printf(":hex:");
+ p = buffer;
+ for (loop = ret; loop > 0; loop--, p++)
+ printf("%02hhx", *p);
+ printf("\n");
+ return 0;
+
+} /* end act_keyctl_print() */
+
+/*****************************************************************************/
+/*
+ * list a keyring
+ */
+static int act_keyctl_list(int argc, char *argv[])
+{
+ key_serial_t keyring, key, *pk;
+ key_perm_t perm;
+ void *keylist;
+ char *buffer, pretty_mask[9];
+ uid_t uid;
+ gid_t gid;
+ int count, tlen, dpos, n, ret;
+
+ if (argc != 2)
+ format();
+
+ keyring = get_key_id(argv[1]);
+
+ /* read the key payload data */
+ count = keyctl_read_alloc(keyring, &keylist);
+ if (count < 0)
+ error("keyctl_read_alloc");
+
+ count /= sizeof(key_serial_t);
+
+ if (count == 0) {
+ printf("keyring is empty\n");
+ return 0;
+ }
+
+ /* list the keys in the keyring */
+ if (count == 1)
+ printf("1 key in keyring:\n");
+ else
+ printf("%u keys in keyring:\n", count);
+
+ pk = keylist;
+ do {
+ key = *pk++;
+
+ ret = keyctl_describe_alloc(key, &buffer);
+ if (ret < 0) {
+ printf("%9d: key inaccessible (%m)\n", key);
+ continue;
+ }
+
+ uid = 0;
+ gid = 0;
+ perm = 0;
+
+ tlen = -1;
+ dpos = -1;
+
+ n = sscanf((char *) buffer, "%*[^;]%n;%d;%d;%x;%n",
+ &tlen, &uid, &gid, &perm, &dpos);
+ if (n != 3) {
+ fprintf(stderr, "Unparseable description obtained for key %d\n", key);
+ exit(3);
+ }
+
+ calc_perms(pretty_mask, perm, uid, gid);
+
+ printf("%9d: %s %5d %5d %*.*s: %s\n",
+ key,
+ pretty_mask,
+ uid, gid,
+ tlen, tlen, buffer,
+ buffer + dpos);
+
+ free(buffer);
+
+ } while (--count);
+
+ return 0;
+
+} /* end act_keyctl_list() */
+
+/*****************************************************************************/
+/*
+ * produce a raw list of a keyring
+ */
+static int act_keyctl_rlist(int argc, char *argv[])
+{
+ key_serial_t keyring, key, *pk;
+ void *keylist;
+ int count;
+
+ if (argc != 2)
+ format();
+
+ keyring = get_key_id(argv[1]);
+
+ /* read the key payload data */
+ count = keyctl_read_alloc(keyring, &keylist);
+ if (count < 0)
+ error("keyctl_read_alloc");
+
+ count /= sizeof(key_serial_t);
+
+ /* list the keys in the keyring */
+ if (count <= 0) {
+ printf("\n");
+ }
+ else {
+ pk = keylist;
+ for (; count > 0; count--) {
+ key = *pk++;
+ printf("%d%c", key, count == 1 ? '\n' : ' ');
+ }
+ }
+
+ return 0;
+
+} /* end act_keyctl_rlist() */
+
+/*****************************************************************************/
+/*
+ * describe a key
+ */
+static int act_keyctl_describe(int argc, char *argv[])
+{
+ key_serial_t key;
+ key_perm_t perm;
+ char *buffer;
+ uid_t uid;
+ gid_t gid;
+ int tlen, dpos, n, ret;
+
+ if (argc != 2)
+ format();
+
+ key = get_key_id(argv[1]);
+
+ /* get key description */
+ ret = keyctl_describe_alloc(key, &buffer);
+ if (ret < 0)
+ error("keyctl_describe");
+
+ /* parse it */
+ uid = 0;
+ gid = 0;
+ perm = 0;
+
+ tlen = -1;
+ dpos = -1;
+
+ n = sscanf(buffer, "%*[^;]%n;%d;%d;%x;%n",
+ &tlen, &uid, &gid, &perm, &dpos);
+ if (n != 3) {
+ fprintf(stderr, "Unparseable description obtained for key %d\n", key);
+ exit(3);
+ }
+
+ /* display it */
+ printf("%9d:"
+ " %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c"
+ " %5d %5d %*.*s: %s\n",
+ key,
+ perm & KEY_POS_SETATTR ? 'a' : '-',
+ perm & KEY_POS_LINK ? 'l' : '-',
+ perm & KEY_POS_SEARCH ? 's' : '-',
+ perm & KEY_POS_WRITE ? 'w' : '-',
+ perm & KEY_POS_READ ? 'r' : '-',
+ perm & KEY_POS_VIEW ? 'v' : '-',
+
+ perm & KEY_USR_SETATTR ? 'a' : '-',
+ perm & KEY_USR_LINK ? 'l' : '-',
+ perm & KEY_USR_SEARCH ? 's' : '-',
+ perm & KEY_USR_WRITE ? 'w' : '-',
+ perm & KEY_USR_READ ? 'r' : '-',
+ perm & KEY_USR_VIEW ? 'v' : '-',
+
+ perm & KEY_GRP_SETATTR ? 'a' : '-',
+ perm & KEY_GRP_LINK ? 'l' : '-',
+ perm & KEY_GRP_SEARCH ? 's' : '-',
+ perm & KEY_GRP_WRITE ? 'w' : '-',
+ perm & KEY_GRP_READ ? 'r' : '-',
+ perm & KEY_GRP_VIEW ? 'v' : '-',
+
+ perm & KEY_OTH_SETATTR ? 'a' : '-',
+ perm & KEY_OTH_LINK ? 'l' : '-',
+ perm & KEY_OTH_SEARCH ? 's' : '-',
+ perm & KEY_OTH_WRITE ? 'w' : '-',
+ perm & KEY_OTH_READ ? 'r' : '-',
+ perm & KEY_OTH_VIEW ? 'v' : '-',
+ uid, gid,
+ tlen, tlen, buffer,
+ buffer + dpos);
+
+ return 0;
+
+} /* end act_keyctl_describe() */
+
+/*****************************************************************************/
+/*
+ * get raw key description
+ */
+static int act_keyctl_rdescribe(int argc, char *argv[])
+{
+ key_serial_t key;
+ char *buffer, *q;
+ int ret;
+
+ if (argc != 2 && argc != 3)
+ format();
+ if (argc == 3 && !argv[2][0])
+ format();
+
+ key = get_key_id(argv[1]);
+
+ /* get key description */
+ ret = keyctl_describe_alloc(key, &buffer);
+ if (ret < 0)
+ error("keyctl_describe");
+
+ /* replace semicolon separators with requested alternative */
+ if (argc == 3) {
+ for (q = buffer; *q; q++)
+ if (*q == ';')
+ *q = argv[2][0];
+ }
+
+ /* display raw description */
+ printf("%s\n", buffer);
+ return 0;
+
+} /* end act_keyctl_rdescribe() */
+
+/*****************************************************************************/
+/*
+ * change a key's ownership
+ */
+static int act_keyctl_chown(int argc, char *argv[])
+{
+ key_serial_t key;
+ uid_t uid;
+ char *q;
+
+ if (argc != 3)
+ format();
+
+ key = get_key_id(argv[1]);
+
+ uid = strtoul(argv[2], &q, 0);
+ if (*q) {
+ fprintf(stderr, "Unparsable uid: '%s'\n", argv[2]);
+ exit(2);
+ }
+
+ if (keyctl_chown(key, uid, -1) < 0)
+ error("keyctl_chown");
+
+ return 0;
+
+} /* end act_keyctl_chown() */
+
+/*****************************************************************************/
+/*
+ * change a key's group ownership
+ */
+static int act_keyctl_chgrp(int argc, char *argv[])
+{
+ key_serial_t key;
+ gid_t gid;
+ char *q;
+
+ if (argc != 3)
+ format();
+
+ key = get_key_id(argv[1]);
+
+ gid = strtoul(argv[2], &q, 0);
+ if (*q) {
+ fprintf(stderr, "Unparsable gid: '%s'\n", argv[2]);
+ exit(2);
+ }
+
+ if (keyctl_chown(key, -1, gid) < 0)
+ error("keyctl_chown");
+
+ return 0;
+
+} /* end act_keyctl_chgrp() */
+
+/*****************************************************************************/
+/*
+ * set the permissions on a key
+ */
+static int act_keyctl_setperm(int argc, char *argv[])
+{
+ key_serial_t key;
+ key_perm_t perm;
+ char *q;
+
+ if (argc != 3)
+ format();
+
+ key = get_key_id(argv[1]);
+ perm = strtoul(argv[2], &q, 0);
+ if (*q) {
+ fprintf(stderr, "Unparsable permissions: '%s'\n", argv[2]);
+ exit(2);
+ }
+
+ if (keyctl_setperm(key, perm) < 0)
+ error("keyctl_setperm");
+
+ return 0;
+
+} /* end act_keyctl_setperm() */
+
+/*****************************************************************************/
+/*
+ * start a process in a new session
+ */
+static int act_keyctl_session(int argc, char *argv[])
+{
+ char *p, *q;
+ int ret;
+
+ argv++;
+ argc--;
+
+ /* no extra arguments signifies a standard shell in an anonymous
+ * session */
+ p = NULL;
+ if (argc != 0) {
+ /* a dash signifies an anonymous session */
+ p = *argv;
+ if (strcmp(p, "-") == 0)
+ p = NULL;
+
+ argv++;
+ argc--;
+ }
+
+ /* create a new session keyring */
+ ret = keyctl_join_session_keyring(p);
+ if (ret < 0)
+ error("keyctl_join_session_keyring");
+
+ fprintf(stderr, "Joined session keyring: %d\n", ret);
+
+ /* run the standard shell if no arguments */
+ if (argc == 0) {
+ q = getenv("SHELL");
+ if (!q)
+ q = "/bin/sh";
+ execl(q, q, NULL);
+ error(q);
+ }
+
+ /* run the command specified */
+ execvp(argv[0], argv);
+ error(argv[0]);
+
+} /* end act_keyctl_session() */
+
+/*****************************************************************************/
+/*
+ * instantiate a key that's under construction
+ */
+static int act_keyctl_instantiate(int argc, char *argv[])
+{
+ key_serial_t key, dest;
+
+ if (argc != 4)
+ format();
+
+ key = get_key_id(argv[1]);
+ dest = get_key_id(argv[3]);
+
+ if (keyctl_instantiate(key, argv[2], strlen(argv[2]), dest) < 0)
+ error("keyctl_instantiate");
+
+ return 0;
+
+} /* end act_keyctl_instantiate() */
+
+/*****************************************************************************/
+/*
+ * instantiate a key, reading from a pipe
+ */
+static int act_keyctl_pinstantiate(int argc, char *argv[])
+{
+ key_serial_t key, dest;
+ size_t datalen;
+ void *data;
+
+ if (argc != 3)
+ format();
+
+ key = get_key_id(argv[1]);
+ dest = get_key_id(argv[2]);
+ data = grab_stdin(&datalen);
+
+ if (keyctl_instantiate(key, data, datalen, dest) < 0)
+ error("keyctl_instantiate");
+
+ return 0;
+
+} /* end act_keyctl_pinstantiate() */
+
+/*****************************************************************************/
+/*
+ * negate a key that's under construction
+ */
+static int act_keyctl_negate(int argc, char *argv[])
+{
+ unsigned long timeout;
+ key_serial_t key, dest;
+ char *q;
+
+ if (argc != 4)
+ format();
+
+ key = get_key_id(argv[1]);
+
+ timeout = strtoul(argv[2], &q, 10);
+ if (*q) {
+ fprintf(stderr, "Unparsable timeout: '%s'\n", argv[2]);
+ exit(2);
+ }
+
+ dest = get_key_id(argv[3]);
+
+ if (keyctl_negate(key, timeout, dest) < 0)
+ error("keyctl_negate");
+
+ return 0;
+
+} /* end act_keyctl_negate() */
+
+/*****************************************************************************/
+/*
+ * set a key's timeout
+ */
+static int act_keyctl_timeout(int argc, char *argv[])
+{
+ unsigned long timeout;
+ key_serial_t key;
+ char *q;
+
+ if (argc != 3)
+ format();
+
+ key = get_key_id(argv[1]);
+
+ timeout = strtoul(argv[2], &q, 10);
+ if (*q) {
+ fprintf(stderr, "Unparsable timeout: '%s'\n", argv[2]);
+ exit(2);
+ }
+
+ if (keyctl_set_timeout(key, timeout) < 0)
+ error("keyctl_set_timeout");
+
+ return 0;
+
+} /* end act_keyctl_timeout() */
+
+/*****************************************************************************/
+/*
+ * get a key's security label
+ */
+static int act_keyctl_security(int argc, char *argv[])
+{
+ key_serial_t key;
+ char *buffer;
+ int ret;
+
+ if (argc != 2)
+ format();
+
+ key = get_key_id(argv[1]);
+
+ /* get key description */
+ ret = keyctl_get_security_alloc(key, &buffer);
+ if (ret < 0)
+ error("keyctl_getsecurity");
+
+ printf("%s\n", buffer);
+ return 0;
+}
+
+/*****************************************************************************/
+/*
+ * install a new session keyring on the parent process
+ */
+static int act_keyctl_new_session(int argc, char *argv[])
+{
+ key_serial_t keyring;
+
+ if (argc != 1)
+ format();
+
+ if (keyctl_join_session_keyring(NULL) < 0)
+ error("keyctl_join_session_keyring");
+
+ if (keyctl_session_to_parent() < 0)
+ error("keyctl_session_to_parent");
+
+ keyring = keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 0);
+ if (keyring < 0)
+ error("keyctl_get_keyring_ID");
+
+ /* print the resulting key ID */
+ printf("%d\n", keyring);
+ return 0;
+}
+
+/*****************************************************************************/
+/*
+ * reject a key that's under construction
+ */
+static int act_keyctl_reject(int argc, char *argv[])
+{
+ unsigned long timeout;
+ key_serial_t key, dest;
+ unsigned long rejerr;
+ char *q;
+
+ if (argc != 5)
+ format();
+
+ key = get_key_id(argv[1]);
+
+ timeout = strtoul(argv[2], &q, 10);
+ if (*q) {
+ fprintf(stderr, "Unparsable timeout: '%s'\n", argv[2]);
+ exit(2);
+ }
+
+ if (strcmp(argv[3], "rejected") == 0) {
+ rejerr = EKEYREJECTED;
+ } else if (strcmp(argv[3], "revoked") == 0) {
+ rejerr = EKEYREVOKED;
+ } else if (strcmp(argv[3], "expired") == 0) {
+ rejerr = EKEYEXPIRED;
+ } else {
+ rejerr = strtoul(argv[3], &q, 10);
+ if (*q) {
+ fprintf(stderr, "Unparsable error: '%s'\n", argv[3]);
+ exit(2);
+ }
+ }
+
+ dest = get_key_id(argv[4]);
+
+ if (keyctl_reject(key, timeout, rejerr, dest) < 0)
+ error("keyctl_negate");
+
+ return 0;
+}
+
+/*
+ * Attempt to unlink a key if we can't read it for reasons other than we don't
+ * have permission
+ */
+static int act_keyctl_reap_func(key_serial_t parent, key_serial_t key,
+ char *desc, int desc_len, void *data)
+{
+ if (desc_len < 0 && errno != EACCES) {
+ if (verbose)
+ printf("Reap %d", key);
+ if (keyctl_unlink(key, parent) < 0) {
+ if (verbose)
+ printf("... failed %m\n");
+ return 0;
+ } else {
+ if (verbose)
+ printf("\n");
+ return 1;
+ };
+ }
+ return 0;
+}
+
+/*
+ * Reap the dead keys from the session keyring tree
+ */
+static int act_keyctl_reap(int argc, char *argv[])
+{
+ int n;
+
+ if (argc > 1 && strcmp(argv[1], "-v") == 0) {
+ verbose = 1;
+ argc--;
+ argv++;
+ }
+
+ if (argc != 1)
+ format();
+
+ n = recursive_session_key_scan(act_keyctl_reap_func, NULL);
+ printf("%d keys reaped\n", n);
+ return 0;
+}
+
+struct purge_data {
+ const char *type;
+ const char *desc;
+ size_t desc_len;
+ size_t type_len;
+ char prefix_match;
+ char case_indep;
+};
+
+/*
+ * Attempt to unlink a key matching the type
+ */
+static int act_keyctl_purge_type_func(key_serial_t parent, key_serial_t key,
+ char *raw, int raw_len, void *data)
+{
+ const struct purge_data *purge = data;
+ char *p, *type;
+
+ if (parent == 0 || !raw)
+ return 0;
+
+ /* type is everything before the first semicolon */
+ type = raw;
+ p = memchr(raw, ';', raw_len);
+ if (!p)
+ return 0;
+ *p = 0;
+ if (strcmp(type, purge->type) != 0)
+ return 0;
+
+ return keyctl_unlink(key, parent) < 0 ? 0 : 1;
+}
+
+/*
+ * Attempt to unlink a key matching the type and description literally
+ */
+static int act_keyctl_purge_literal_func(key_serial_t parent, key_serial_t key,
+ char *raw, int raw_len, void *data)
+{
+ const struct purge_data *purge = data;
+ size_t tlen;
+ char *p, *type, *desc;
+
+ if (parent == 0 || !raw)
+ return 0;
+
+ /* type is everything before the first semicolon */
+ type = raw;
+ p = memchr(type, ';', raw_len);
+ if (!p)
+ return 0;
+
+ tlen = p - type;
+ if (tlen != purge->type_len)
+ return 0;
+ if (memcmp(type, purge->type, tlen) != 0)
+ return 0;
+
+ /* description is everything after the last semicolon */
+ p++;
+ desc = memrchr(p, ';', raw + raw_len - p);
+ if (!desc)
+ return 0;
+ desc++;
+
+ if (purge->prefix_match) {
+ if (raw_len - (desc - raw) < purge->desc_len)
+ return 0;
+ } else {
+ if (raw_len - (desc - raw) != purge->desc_len)
+ return 0;
+ }
+
+ if (purge->case_indep) {
+ if (strncasecmp(purge->desc, desc, purge->desc_len) != 0)
+ return 0;
+ } else {
+ if (memcmp(purge->desc, desc, purge->desc_len) != 0)
+ return 0;
+ }
+
+ printf("%*.*s '%s'\n", (int)tlen, (int)tlen, type, desc);
+
+ return keyctl_unlink(key, parent) < 0 ? 0 : 1;
+}
+
+/*
+ * Attempt to unlink a key matching the type and description literally
+ */
+static int act_keyctl_purge_search_func(key_serial_t parent, key_serial_t keyring,
+ char *raw, int raw_len, void *data)
+{
+ const struct purge_data *purge = data;
+ key_serial_t key;
+ int kcount = 0;
+
+ if (!raw || memcmp(raw, "keyring;", 8) != 0)
+ return 0;
+
+ for (;;) {
+ key = keyctl_search(keyring, purge->type, purge->desc, 0);
+ if (keyctl_unlink(key, keyring) < 0)
+ return kcount;
+ kcount++;
+ }
+ return kcount;
+}
+
+/*
+ * Purge matching keys from a keyring
+ */
+static int act_keyctl_purge(int argc, char *argv[])
+{
+ recursive_key_scanner_t func;
+ struct purge_data purge = {
+ .prefix_match = 0,
+ .case_indep = 0,
+ };
+ int n = 0, search_mode = 0;
+
+ argc--;
+ argv++;
+ while (argc > 0 && argv[0][0] == '-') {
+ if (argv[0][1] == 's')
+ search_mode = 1;
+ else if (argv[0][1] == 'p')
+ purge.prefix_match = 1;
+ else if (argv[0][1] == 'i')
+ purge.case_indep = 1;
+ else
+ format();
+ argc--;
+ argv++;
+ }
+
+ if (argc < 1)
+ format();
+
+ purge.type = argv[0];
+ purge.desc = argv[1];
+ purge.type_len = strlen(purge.type);
+ purge.desc_len = purge.desc ? strlen(purge.desc) : 0;
+
+ if (search_mode == 1) {
+ if (argc != 2 || purge.prefix_match || purge.case_indep)
+ format();
+ /* purge all keys of a specific type and description, according
+ * to the kernel's comparator */
+ func = act_keyctl_purge_search_func;
+ } else if (argc == 1) {
+ if (purge.prefix_match || purge.case_indep)
+ format();
+ /* purge all keys of a specific type */
+ func = act_keyctl_purge_type_func;
+ } else if (argc == 2) {
+ /* purge all keys of a specific type with literally matching
+ * description */
+ func = act_keyctl_purge_literal_func;
+ } else {
+ format();
+ }
+
+ n = recursive_session_key_scan(func, &purge);
+ printf("purged %d keys\n", n);
+ return 0;
+}
+
+/*****************************************************************************/
+/*
+ * Invalidate a key
+ */
+static int act_keyctl_invalidate(int argc, char *argv[])
+{
+ key_serial_t key;
+
+ if (argc != 2)
+ format();
+
+ key = get_key_id(argv[1]);
+
+ if (keyctl_invalidate(key) < 0)
+ error("keyctl_invalidate");
+
+ return 0;
+}
+
+/*****************************************************************************/
+/*
+ * parse a key identifier
+ */
+static key_serial_t get_key_id(const char *arg)
+{
+ key_serial_t id;
+ char *end;
+
+ /* handle a special keyring name */
+ if (arg[0] == '@') {
+ if (strcmp(arg, "@t" ) == 0) return KEY_SPEC_THREAD_KEYRING;
+ if (strcmp(arg, "@p" ) == 0) return KEY_SPEC_PROCESS_KEYRING;
+ if (strcmp(arg, "@s" ) == 0) return KEY_SPEC_SESSION_KEYRING;
+ if (strcmp(arg, "@u" ) == 0) return KEY_SPEC_USER_KEYRING;
+ if (strcmp(arg, "@us") == 0) return KEY_SPEC_USER_SESSION_KEYRING;
+ if (strcmp(arg, "@g" ) == 0) return KEY_SPEC_GROUP_KEYRING;
+ if (strcmp(arg, "@a" ) == 0) return KEY_SPEC_REQKEY_AUTH_KEY;
+
+ fprintf(stderr, "Unknown special key: '%s'\n", arg);
+ exit(2);
+ }
+
+ /* handle a numeric key ID */
+ id = strtoul(arg, &end, 0);
+ if (*end) {
+ fprintf(stderr, "Unparsable key: '%s'\n", arg);
+ exit(2);
+ }
+
+ return id;
+
+} /* end get_key_id() */
+
+/*****************************************************************************/
+/*
+ * recursively display a key/keyring tree
+ */
+static int dump_key_tree_aux(key_serial_t key, int depth, int more, int hex_key_IDs)
+{
+ static char dumpindent[64];
+ key_serial_t *pk;
+ key_perm_t perm;
+ size_t ringlen, desclen;
+ void *payload;
+ char *desc, type[255], pretty_mask[9];
+ int uid, gid, ret, n, dpos, rdepth, kcount = 0;
+
+ if (depth > 8 * 4)
+ return 0;
+
+ /* find out how big this key's description is */
+ ret = keyctl_describe(key, NULL, 0);
+ if (ret < 0) {
+ printf("%d: key inaccessible (%m)\n", key);
+ return 0;
+ }
+ desclen = ret + 1;
+
+ desc = malloc(desclen);
+ if (!desc)
+ error("malloc");
+
+ /* read the description */
+ ret = keyctl_describe(key, desc, desclen);
+ if (ret < 0) {
+ printf("%d: key inaccessible (%m)\n", key);
+ free(desc);
+ return 0;
+ }
+
+ desclen = ret < desclen ? ret : desclen;
+
+ desc[desclen] = 0;
+
+ /* parse */
+ type[0] = 0;
+ uid = 0;
+ gid = 0;
+ perm = 0;
+
+ n = sscanf(desc, "%[^;];%d;%d;%x;%n",
+ type, &uid, &gid, &perm, &dpos);
+
+ if (n != 4) {
+ fprintf(stderr, "Unparseable description obtained for key %d\n", key);
+ exit(3);
+ }
+
+ /* and print */
+ calc_perms(pretty_mask, perm, uid, gid);
+
+ if (hex_key_IDs)
+ printf("0x%08x %s %5d %5d %s%s%s: %s\n",
+ key,
+ pretty_mask,
+ uid, gid,
+ dumpindent,
+ depth > 0 ? "\\_ " : "",
+ type, desc + dpos);
+ else
+ printf("%10d %s %5d %5d %s%s%s: %s\n",
+ key,
+ pretty_mask,
+ uid, gid,
+ dumpindent,
+ depth > 0 ? "\\_ " : "",
+ type, desc + dpos);
+
+ /* if it's a keyring then we're going to want to recursively
+ * display it if we can */
+ if (strcmp(type, "keyring") == 0) {
+ /* find out how big the keyring is */
+ ret = keyctl_read(key, NULL, 0);
+ if (ret < 0)
+ error("keyctl_read");
+ if (ret == 0)
+ return 0;
+ ringlen = ret;
+
+ /* read its contents */
+ payload = malloc(ringlen);
+ if (!payload)
+ error("malloc");
+
+ ret = keyctl_read(key, payload, ringlen);
+ if (ret < 0)
+ error("keyctl_read");
+
+ ringlen = ret < ringlen ? ret : ringlen;
+ kcount = ringlen / sizeof(key_serial_t);
+
+ /* walk the keyring */
+ pk = payload;
+ do {
+ key = *pk++;
+
+ /* recurse into nexted keyrings */
+ if (strcmp(type, "keyring") == 0) {
+ if (depth == 0) {
+ rdepth = depth;
+ dumpindent[rdepth++] = ' ';
+ dumpindent[rdepth] = 0;
+ }
+ else {
+ rdepth = depth;
+ dumpindent[rdepth++] = ' ';
+ dumpindent[rdepth++] = ' ';
+ dumpindent[rdepth++] = ' ';
+ dumpindent[rdepth++] = ' ';
+ dumpindent[rdepth] = 0;
+ }
+
+ if (more)
+ dumpindent[depth + 0] = '|';
+
+ kcount += dump_key_tree_aux(key,
+ rdepth,
+ ringlen - 4 >= sizeof(key_serial_t),
+ hex_key_IDs);
+ }
+
+ } while (ringlen -= 4, ringlen >= sizeof(key_serial_t));
+
+ free(payload);
+ }
+
+ free(desc);
+ return kcount;
+
+} /* end dump_key_tree_aux() */
+
+/*****************************************************************************/
+/*
+ * recursively list a keyring's contents
+ */
+static int dump_key_tree(key_serial_t keyring, const char *name, int hex_key_IDs)
+{
+ printf("%s\n", name);
+
+ keyring = keyctl_get_keyring_ID(keyring, 0);
+ if (keyring == -1)
+ error("Unable to dump key");
+
+ return dump_key_tree_aux(keyring, 0, 0, hex_key_IDs);
+
+} /* end dump_key_tree() */