blob: 321fdb97bbc2e4d7629c08a67a0c52306955a303 [file] [log] [blame]
/* 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"
#include <limits.h>
struct command {
void (*action)(int argc, char *argv[]) __attribute__((noreturn));
const char *name;
const char *format;
};
#define nr __attribute__((noreturn))
static nr void act_keyctl___version(int argc, char *argv[]);
static nr void act_keyctl_show(int argc, char *argv[]);
static nr void act_keyctl_add(int argc, char *argv[]);
static nr void act_keyctl_padd(int argc, char *argv[]);
static nr void act_keyctl_request(int argc, char *argv[]);
static nr void act_keyctl_request2(int argc, char *argv[]);
static nr void act_keyctl_prequest2(int argc, char *argv[]);
static nr void act_keyctl_update(int argc, char *argv[]);
static nr void act_keyctl_pupdate(int argc, char *argv[]);
static nr void act_keyctl_newring(int argc, char *argv[]);
static nr void act_keyctl_revoke(int argc, char *argv[]);
static nr void act_keyctl_clear(int argc, char *argv[]);
static nr void act_keyctl_link(int argc, char *argv[]);
static nr void act_keyctl_unlink(int argc, char *argv[]);
static nr void act_keyctl_search(int argc, char *argv[]);
static nr void act_keyctl_read(int argc, char *argv[]);
static nr void act_keyctl_pipe(int argc, char *argv[]);
static nr void act_keyctl_print(int argc, char *argv[]);
static nr void act_keyctl_list(int argc, char *argv[]);
static nr void act_keyctl_rlist(int argc, char *argv[]);
static nr void act_keyctl_describe(int argc, char *argv[]);
static nr void act_keyctl_rdescribe(int argc, char *argv[]);
static nr void act_keyctl_chown(int argc, char *argv[]);
static nr void act_keyctl_chgrp(int argc, char *argv[]);
static nr void act_keyctl_setperm(int argc, char *argv[]);
static nr void act_keyctl_session(int argc, char *argv[]);
static nr void act_keyctl_instantiate(int argc, char *argv[]);
static nr void act_keyctl_pinstantiate(int argc, char *argv[]);
static nr void act_keyctl_negate(int argc, char *argv[]);
static nr void act_keyctl_timeout(int argc, char *argv[]);
static nr void act_keyctl_security(int argc, char *argv[]);
static nr void act_keyctl_new_session(int argc, char *argv[]);
static nr void act_keyctl_reject(int argc, char *argv[]);
static nr void act_keyctl_reap(int argc, char *argv[]);
static nr void act_keyctl_purge(int argc, char *argv[]);
static nr void act_keyctl_invalidate(int argc, char *argv[]);
static nr void act_keyctl_get_persistent(int argc, char *argv[]);
static nr void act_keyctl_dh_compute(int argc, char *argv[]);
static nr void act_keyctl_dh_compute_kdf(int argc, char *argv[]);
static nr void act_keyctl_dh_compute_kdf_oi(int argc, char *argv[]);
static nr void act_keyctl_restrict_keyring(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_dh_compute, "dh_compute", "<private> <prime> <base>" },
{ act_keyctl_dh_compute_kdf, "dh_compute_kdf", "<private> <prime> <base> <len> <hash_name>" },
{ act_keyctl_dh_compute_kdf_oi, "dh_compute_kdf_oi", "<private> <prime> <base> <len> <hash_name>" },
{ act_keyctl_instantiate, "instantiate","<key> <data> <keyring>" },
{ act_keyctl_invalidate,"invalidate", "<key>" },
{ act_keyctl_get_persistent, "get_persistent", "<keyring> [<uid>]" },
{ 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_restrict_keyring, "restrict_keyring", "<keyring> [<type> [<restriction>]]" },
{ 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(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 (strlen(cmd->name) > n)
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);
}
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 void act_keyctl___version(int argc, char *argv[])
{
printf("keyctl from %s (Built %s)\n",
keyutils_version_string, keyutils_build_string);
exit(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() */
/*
* Load the groups list and grab the process's UID and GID.
*/
static void grab_creds(void)
{
static int inited;
if (inited)
return;
inited = 1;
/* 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");
}
/*****************************************************************************/
/*
* 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;
grab_creds();
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 void 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);
exit(0);
} /* end act_keyctl_show() */
/*****************************************************************************/
/*
* add a key
*/
static void 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);
exit(0);
} /* end act_keyctl_add() */
/*****************************************************************************/
/*
* add a key, reading from a pipe
*/
static void 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);
exit(0);
} /* end act_keyctl_padd() */
/*****************************************************************************/
/*
* request a key
*/
static void 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);
exit(0);
} /* end act_keyctl_request() */
/*****************************************************************************/
/*
* request a key, with recourse to /sbin/request-key
*/
static void 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);
exit(0);
} /* end act_keyctl_request2() */
/*****************************************************************************/
/*
* request a key, with recourse to /sbin/request-key, reading the callout info
* from a pipe
*/
static void 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;
act_keyctl_request2(argc + 1, args);
} /* end act_keyctl_prequest2() */
/*****************************************************************************/
/*
* update a key
*/
static void 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");
exit(0);
} /* end act_keyctl_update() */
/*****************************************************************************/
/*
* update a key, reading from a pipe
*/
static void 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");
exit(0);
} /* end act_keyctl_pupdate() */
/*****************************************************************************/
/*
* create a new keyring
*/
static void 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);
exit(0);
} /* end act_keyctl_newring() */
/*****************************************************************************/
/*
* revoke a key
*/
static void 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");
exit(0);
} /* end act_keyctl_revoke() */
/*****************************************************************************/
/*
* clear a keyring
*/
static void 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");
exit(0);
} /* end act_keyctl_clear() */
/*****************************************************************************/
/*
* link a key to a keyring
*/
static void 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");
exit(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 void 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);
}
exit(0);
}
/*****************************************************************************/
/*
* search a keyring for a key
*/
static void 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);
exit(0);
} /* end act_keyctl_search() */
/*****************************************************************************/
/*
* read a key
*/
static void 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");
exit(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");
exit(0);
} /* end act_keyctl_read() */
/*****************************************************************************/
/*
* read a key and dump raw to stdout
*/
static void 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");
exit(0);
} /* end act_keyctl_pipe() */
/*****************************************************************************/
/*
* read a key and dump to stdout in printable form
*/
static void 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);
exit(0);
not_printable:
/* it isn't */
printf(":hex:");
p = buffer;
for (loop = ret; loop > 0; loop--, p++)
printf("%02hhx", *p);
printf("\n");
exit(0);
} /* end act_keyctl_print() */
/*****************************************************************************/
/*
* list a keyring
*/
static void 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");
exit(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);
exit(0);
} /* end act_keyctl_list() */
/*****************************************************************************/
/*
* produce a raw list of a keyring
*/
static void 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' : ' ');
}
}
exit(0);
} /* end act_keyctl_rlist() */
/*****************************************************************************/
/*
* describe a key
*/
static void 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_alloc");
/* 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);
exit(0);
} /* end act_keyctl_describe() */
/*****************************************************************************/
/*
* get raw key description
*/
static void 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);
exit(0);
} /* end act_keyctl_rdescribe() */
/*****************************************************************************/
/*
* change a key's ownership
*/
static void 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");
exit(0);
} /* end act_keyctl_chown() */
/*****************************************************************************/
/*
* change a key's group ownership
*/
static void 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");
exit(0);
} /* end act_keyctl_chgrp() */
/*****************************************************************************/
/*
* set the permissions on a key
*/
static void 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");
exit(0);
} /* end act_keyctl_setperm() */
/*****************************************************************************/
/*
* start a process in a new session
*/
static void 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 void 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");
exit(0);
} /* end act_keyctl_instantiate() */
/*****************************************************************************/
/*
* instantiate a key, reading from a pipe
*/
static void 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");
exit(0);
} /* end act_keyctl_pinstantiate() */
/*****************************************************************************/
/*
* negate a key that's under construction
*/
static void 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");
exit(0);
} /* end act_keyctl_negate() */
/*****************************************************************************/
/*
* set a key's timeout
*/
static void 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");
exit(0);
} /* end act_keyctl_timeout() */
/*****************************************************************************/
/*
* get a key's security label
*/
static void 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);
exit(0);
}
/*****************************************************************************/
/*
* install a new session keyring on the parent process
*/
static void 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);
exit(0);
}
/*****************************************************************************/
/*
* reject a key that's under construction
*/
static void 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");
exit(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 void 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);
exit(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 void 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);
exit(0);
}
/*****************************************************************************/
/*
* Invalidate a key
*/
static void 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");
exit(0);
}
/*****************************************************************************/
/*
* Get the per-UID persistent keyring
*/
static void act_keyctl_get_persistent(int argc, char *argv[])
{
key_serial_t dest, ret;
uid_t uid = -1;
char *q;
if (argc != 2 && argc != 3)
format();
dest = get_key_id(argv[1]);
if (argc > 2) {
uid = strtoul(argv[2], &q, 0);
if (*q) {
fprintf(stderr, "Unparsable uid: '%s'\n", argv[2]);
exit(2);
}
}
ret = keyctl_get_persistent(uid, dest);
if (ret < 0)
error("keyctl_get_persistent");
/* print the resulting key ID */
printf("%d\n", ret);
exit(0);
}
/*****************************************************************************/
/*
* Perform Diffie-Hellman computation
*/
static void act_keyctl_dh_compute(int argc, char *argv[])
{
key_serial_t priv, prime, base;
void *buffer;
char *p;
int ret, sep, col;
if (argc != 4)
format();
priv = get_key_id(argv[1]);
prime = get_key_id(argv[2]);
base = get_key_id(argv[3]);
ret = keyctl_dh_compute_alloc(priv, prime, base, &buffer);
if (ret < 0)
error("keyctl_dh_compute_alloc");
/* hexdump the contents */
printf("%u bytes of data in result:\n", ret);
sep = 0;
col = 0;
p = buffer;
do {
if (sep) {
putchar(sep);
sep = 0;
}
printf("%02hhx", *p);
*p = 0x00; /* zeroize buffer */
p++;
col++;
if (col % 32 == 0)
sep = '\n';
else if (col % 4 == 0)
sep = ' ';
} while (--ret > 0);
printf("\n");
free(buffer);
exit(0);
}
static void act_keyctl_dh_compute_kdf(int argc, char *argv[])
{
key_serial_t private, prime, base;
char *buffer;
char *p;
int ret, sep, col;
unsigned long buflen = 0;
if (argc != 6)
format();
private = get_key_id(argv[1]);
prime = get_key_id(argv[2]);
base = get_key_id(argv[3]);
buflen = strtoul(argv[4], NULL, 10);
if (buflen == ULONG_MAX)
error("dh_compute: cannot convert generated length value");
buffer = malloc(buflen);
if (!buffer)
error("dh_compute: cannot allocate memory");
ret = keyctl_dh_compute_kdf(private, prime, base, argv[5], NULL, 0,
buffer, buflen);
if (ret < 0)
error("keyctl_dh_compute_kdf");
/* hexdump the contents */
printf("%u bytes of data in result:\n", ret);
sep = 0;
col = 0;
p = buffer;
do {
if (sep) {
putchar(sep);
sep = 0;
}
printf("%02hhx", *p);
*p = 0x00; /* zeroize buffer */
p++;
col++;
if (col % 32 == 0)
sep = '\n';
else if (col % 4 == 0)
sep = ' ';
} while (--ret > 0);
printf("\n");
free(buffer);
exit(0);
}
static void act_keyctl_dh_compute_kdf_oi(int argc, char *argv[])
{
key_serial_t private, prime, base;
char *buffer;
char *p;
int ret, sep, col;
unsigned long buflen = 0;
size_t oilen;
void *oi;
if (argc != 6)
format();
private = get_key_id(argv[1]);
prime = get_key_id(argv[2]);
base = get_key_id(argv[3]);
buflen = strtoul(argv[4], NULL, 10);
if (buflen == ULONG_MAX)
error("dh_compute: cannot convert generated length value");
buffer = malloc(buflen);
if (!buffer)
error("dh_compute: cannot allocate memory");
oi = grab_stdin(&oilen);
ret = keyctl_dh_compute_kdf(private, prime, base, argv[5], oi, oilen,
buffer, buflen);
if (ret < 0)
error("keyctl_dh_compute_kdf");
/* hexdump the contents */
printf("%u bytes of data in result:\n", ret);
sep = 0;
col = 0;
p = buffer;
do {
if (sep) {
putchar(sep);
sep = 0;
}
printf("%02hhx", *p);
*p = 0x00; /* zeroize buffer */
p++;
col++;
if (col % 32 == 0)
sep = '\n';
else if (col % 4 == 0)
sep = ' ';
} while (--ret > 0);
printf("\n");
free(buffer);
exit(0);
}
/*****************************************************************************/
/*
* Restrict the keys that may be added to a keyring
*/
static void act_keyctl_restrict_keyring(int argc, char *argv[])
{
key_serial_t keyring;
char *type = NULL;
char *restriction = NULL;
long ret;
if (argc < 2 || argc > 4)
format();
keyring = get_key_id(argv[1]);
if (argc > 2)
type = argv[2];
if (argc == 4)
restriction = argv[3];
ret = keyctl_restrict_keyring(keyring, type, restriction);
if (ret < 0)
error("keyctl_restrict_keyring");
exit(0);
}
/*****************************************************************************/
/*
* parse a key identifier
*/
static key_serial_t get_key_id(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 lookup-by-name request "%<type>:<desc>", eg: "%keyring:_ses" */
if (arg[0] == '%') {
char *type;
arg++;
if (!*arg)
goto incorrect_key_by_name_spec;
if (*arg == ':') {
type = "keyring";
arg++;
} else {
type = arg;
arg = strchr(arg, ':');
if (!arg)
goto incorrect_key_by_name_spec;
*(arg++) = '\0';
}
if (!*arg)
goto incorrect_key_by_name_spec;
id = find_key_by_type_and_desc(type, arg, 0);
if (id == -1) {
fprintf(stderr, "Can't find '%s:%s'\n", type, arg);
exit(1);
}
return id;
}
/* handle a numeric key ID */
id = strtoul(arg, &end, 0);
if (*end) {
fprintf(stderr, "Unparsable key: '%s'\n", arg);
exit(2);
}
return id;
incorrect_key_by_name_spec:
fprintf(stderr, "Incorrect key-by-name spec\n");
exit(2);
} /* 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;
void *payload;
char *desc, type[255], pretty_mask[9];
int uid, gid, ret, n, dpos, rdepth, kcount = 0;
if (depth > 8 * 4)
return 0;
/* read the description */
ret = keyctl_describe_alloc(key, &desc);
if (ret < 0) {
printf("%d: key inaccessible (%m)\n", key);
return 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);
free(desc);
/* 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 next 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);
}
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() */