include/uapi/asm-generic/siginfo.h | 1 + security/keys/Kconfig | 9 ++ security/keys/Makefile | 1 + security/keys/internal.h | 4 + security/keys/keyagent.c | 158 +++++++++++++++++++++++++++++ security/keys/request_key.c | 9 ++ 6 files changed, 182 insertions(+) create mode 100644 security/keys/keyagent.c
A persistent unsolved problem exists: how can the kernel find and/or create
the appropriate "container" within which to execute a userspace program to
construct keys or satisfy users of call_usermodehelper()?
I believe the latest serious attempt to solve this problem was David's "Make
containers kernel objects":
https://lore.kernel.org/lkml/149547014649.10599.12025037906646164347.stgit@warthog.procyon.org.uk/
Over in NFS' space, we've most recently pondered this issue while looking at
ways to pass a kernel socket to userspace in order to handle TLS events:
https://lore.kernel.org/linux-nfs/E2BF9CFF-9361-400B-BDEE-CF5E0AFDCA63@redhat.com/
The problem is that containers are not kernel objects, rather a collection
of namespaces, cgroups, etc. Attempts at making the kernel aware of
containers have been mired in discussion and problems. It has been
suggested that the best representation of a "container" from the kernel's
perspective is a process.
Keyagents are processes represented by a key. If a keyagent's key is linked
to a session_keyring, it can be sent a realtime signal when a calling
process requests a matching key_type. That signal will dispatch the process
to construct the desired key within the keyagent process context. Keyagents
are similar to ssh-agents. To use a keyagent, one must execute a keyagent
process in the desired context, and then link the keyagent's key onto other
process' session_keyrings.
This method of linking keyagent keys to session_keyrings can be used to
construct the various mappings of callers to keyagents that containers may
need. A single keyagent process can answer request-key upcalls across
container boundaries, or upcalls can be restricted to specific containers.
I'm aware that building on realtime signals may not be a popular choice, but
using realtime signals makes this work simple and ensures delivery. Realtime
signals are able to convey everything needed to construct keys in userspace:
the under-construction key's serial number.
This work is not complete; it has security implications, it needs
documentation, it has not been reviewed by anyone. Thanks for reading this
RFC. I wish to collect criticism and validate this approach.
Below the diffstat in this message is an example userspace program to answer
keyagent requests for user keys. It can be compiled with:
gcc -lkeyutils -o ka_simple ka_simple.c
Benjamin Coddington (2):
KEYS: Add key_type keyagent
KEYS: Add keyagent request_key
include/uapi/asm-generic/siginfo.h | 1 +
security/keys/Kconfig | 9 ++
security/keys/Makefile | 1 +
security/keys/internal.h | 4 +
security/keys/keyagent.c | 158 +++++++++++++++++++++++++++++
security/keys/request_key.c | 9 ++
6 files changed, 182 insertions(+)
create mode 100644 security/keys/keyagent.c
--
// SPDX-License-Identifier: GPL-2.0-only
/* ka_simple.c: userspace keyagent example
*
* Copyright (C) 2022 Red Hat Inc. All Rights Reserved.
* Written by Benjamin Coddington (bcodding@redhat.com)
*
* This programs registers a simple keyagent for user keys that will handle
* requests from the kernel keyagent, and instantiate keys that have
* callout_info == "test_callout_info".
*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <err.h>
#include <errno.h>
#include <stdlib.h>
#include <signal.h>
#include <linux/types.h>
#include <keyutils.h>
#include <sys/signalfd.h>
int ka_sig_fd = 0;
key_serial_t ka_key_serial;
__be16 ka_signal;
/* Setup a signalfd masked to SIGRTMIN + 1 */
void ka_sig_setup()
{
int ret;
sigset_t mask;
/* Which realtime signal are we using? */
ka_signal = SIGRTMIN + 1;
sigemptyset(&mask);
sigaddset(&mask, ka_signal);
ret = sigprocmask(SIG_BLOCK, &mask, NULL);
if (ret != 0)
err(ret, "rt_sigprocmask");
ka_sig_fd = signalfd(-1, &mask, 0);
if (ka_sig_fd == -1)
err(ret, "signalfd");
}
/* Register this process as a keyagent for user keys to be notified by
* signal number SIGRTMIN + 1 by creating a keyagent key with a description
* of "user", and payload of SIGRTMIN + 1 */
void ka_register()
{
printf("Registering as keyagent for user keys with signal %d\n", ka_signal);
/* The kernel will place authorization keys on our process keyring.
* Make sure we have a process keyring: */
keyctl_get_keyring_ID(KEY_SPEC_PROCESS_KEYRING, 1);
ka_key_serial = add_key("keyagent", "user", &ka_signal, sizeof(unsigned int), KEY_SPEC_SESSION_KEYRING);
if (ka_key_serial == -1)
err(errno, "add_key");
/* Permissions for the keyagent's key: */
keyctl_setperm(ka_key_serial, KEY_USR_ALL);
}
/* Handle kernel request_key(). The serial number of the key is the int
* passed in the realtime signal */
int ka_request_key(key_serial_t key) {
int ret, ntype, dpos, n;
char *buf_type_desc, *key_type, *key_desc;
void *callout;
printf("ka_request_key %d\n", key);
ret = keyctl_assume_authority(key);
if (ret < 0) {
warn("failed to assume authority over key %d (%m)\n", key);
goto out;
}
ret = keyctl_describe_alloc(key, &buf_type_desc);
if (ret < 0) {
warn("key %d inaccessible (%m)\n", key);
goto out;
}
printf("Key descriptor: \"%s\"\n", buf_type_desc);
/* Shamelessly copied from libkeyutils/request_key.c: */
ntype = -1;
dpos = -1;
n = sscanf(buf_type_desc, "%*[^;]%n;%*d;%*d;%x;%n", &ntype, &n, &dpos);
if (n != 1)
printf("Failed to parse key description\n");
key_type = buf_type_desc;
key_type[ntype] = 0;
key_desc = buf_type_desc + dpos;
ret = keyctl_read_alloc(KEY_SPEC_REQKEY_AUTH_KEY, &callout);
if (ret < 0) {
warn("failed to retrieve callout info (%m)\n");
goto out_free_type;
}
if (strcmp(buf_type_desc, "user") == 0 && strcmp(callout, "test_callout_info") == 0) {
keyctl_instantiate(key, "keyagent_payload", sizeof("keyagent_payload"), KEY_SPEC_SESSION_KEYRING);
printf("instantiated key %d with payload \"keyagent_payload\" on session keyring\n", key);
} else {
keyctl_reject(key, 10, EKEYREJECTED, KEY_SPEC_SESSION_KEYRING);
printf("this keyagent only instantiates user keys with callout \"test_callout_info\"\n");
}
/* De-assume the authority (for now) */
ret = keyctl_assume_authority(0);
free(callout);
out_free_type:
free(buf_type_desc);
out:
return ret;
}
/* Handle signals from our signalfd, dispatch ka_request_key() */
int ka_process()
{
struct signalfd_siginfo fdsi;
ssize_t size;
for (;;) {
size = read(ka_sig_fd, &fdsi, sizeof(struct signalfd_siginfo));
if (size != sizeof(struct signalfd_siginfo))
err(EINVAL, "reading signal_fd");
if (ka_request_key(fdsi.ssi_int))
break;
}
}
int main(int argc, char **argv)
{
ka_sig_setup();
ka_register();
printf("Registered as keyagent with key %d\n", ka_key_serial);
printf("Subscribe to this keyagent by linking it into your session keyring with:\n\tkeyctl link %d @s\n", ka_key_serial);
printf("then, you can send a request to this agent with:\n\tkeyctl request2 user <description> \"test_callout_info\"\n");
ka_process();
}
--
2.31.1
Adding the containers list to the discussion so more interested people have a chance of seeing this. Benjamin Coddington <bcodding@redhat.com> writes: > A persistent unsolved problem exists: how can the kernel find and/or create > the appropriate "container" within which to execute a userspace program to > construct keys or satisfy users of call_usermodehelper()? > > I believe the latest serious attempt to solve this problem was David's "Make > containers kernel objects": > https://lore.kernel.org/lkml/149547014649.10599.12025037906646164347.stgit@warthog.procyon.org.uk/ > > Over in NFS' space, we've most recently pondered this issue while looking at > ways to pass a kernel socket to userspace in order to handle TLS events: > https://lore.kernel.org/linux-nfs/E2BF9CFF-9361-400B-BDEE-CF5E0AFDCA63@redhat.com/ > > The problem is that containers are not kernel objects, rather a collection > of namespaces, cgroups, etc. Attempts at making the kernel aware of > containers have been mired in discussion and problems. It has been > suggested that the best representation of a "container" from the kernel's > perspective is a process. > > Keyagents are processes represented by a key. If a keyagent's key is linked > to a session_keyring, it can be sent a realtime signal when a calling > process requests a matching key_type. That signal will dispatch the process > to construct the desired key within the keyagent process context. Keyagents > are similar to ssh-agents. To use a keyagent, one must execute a keyagent > process in the desired context, and then link the keyagent's key onto other > process' session_keyrings. > > This method of linking keyagent keys to session_keyrings can be used to > construct the various mappings of callers to keyagents that containers may > need. A single keyagent process can answer request-key upcalls across > container boundaries, or upcalls can be restricted to specific containers. > > I'm aware that building on realtime signals may not be a popular choice, but > using realtime signals makes this work simple and ensures delivery. Realtime > signals are able to convey everything needed to construct keys in userspace: > the under-construction key's serial number. > > This work is not complete; it has security implications, it needs > documentation, it has not been reviewed by anyone. Thanks for reading this > RFC. I wish to collect criticism and validate this approach. At a high level I do agree that we need to send a message to a userspace process and that message should contain enough information to start the user mode helper. Then a daemon or possibly the container init can receive the message and dispatch the user mode helper. Fundamentally that design solves all of the container issues, and I think solves a few of the user mode helper issues as well. The challenge with this design is that it requires someone standing up a daemon to receive the messages and call a user mode helper to retain compatibility with current systems. I would prefer to see a file descriptor rather than a signal used to deliver the message. Signals suck for many many reasons and a file descriptor based notification potentially can be much simpler. One of those many reasons is that by not following the common pattern for filling in kernel_siginfo you have left uninitialized padding in your structure that will be copied to userspace thus creating a kernel information leak. Similarly your code doesn't fill in about half the fields that are present in the siginfo union for the _rt case. I think a file descriptor based design could additionally address the back and forth your design needs with keys to figure out what event has happened and what user mode helper to invoke. Ideally I would also like to see a design less tied to keys. So that we could use this for the other user mode helper cases as well. That said solving request_key appears to be the truly important part, there aren't many other user mode helpers. Still it would be nice if in theory the design could be used to dispatch the coredump helper as well. Eric
On 12 Jul 2022, at 10:16, Eric W. Biederman wrote: > Adding the containers list to the discussion so more interested people > have a chance of seeing this. > > Benjamin Coddington <bcodding@redhat.com> writes: > >> A persistent unsolved problem exists: how can the kernel find and/or >> create >> the appropriate "container" within which to execute a userspace >> program to >> construct keys or satisfy users of call_usermodehelper()? >> >> I believe the latest serious attempt to solve this problem was >> David's "Make >> containers kernel objects": >> https://lore.kernel.org/lkml/149547014649.10599.12025037906646164347.stgit@warthog.procyon.org.uk/ >> >> Over in NFS' space, we've most recently pondered this issue while >> looking at >> ways to pass a kernel socket to userspace in order to handle TLS >> events: >> https://lore.kernel.org/linux-nfs/E2BF9CFF-9361-400B-BDEE-CF5E0AFDCA63@redhat.com/ >> >> The problem is that containers are not kernel objects, rather a >> collection >> of namespaces, cgroups, etc. Attempts at making the kernel aware of >> containers have been mired in discussion and problems. It has been >> suggested that the best representation of a "container" from the >> kernel's >> perspective is a process. >> >> Keyagents are processes represented by a key. If a keyagent's key is >> linked >> to a session_keyring, it can be sent a realtime signal when a calling >> process requests a matching key_type. That signal will dispatch the >> process >> to construct the desired key within the keyagent process context. >> Keyagents >> are similar to ssh-agents. To use a keyagent, one must execute a >> keyagent >> process in the desired context, and then link the keyagent's key onto >> other >> process' session_keyrings. >> >> This method of linking keyagent keys to session_keyrings can be used >> to >> construct the various mappings of callers to keyagents that >> containers may >> need. A single keyagent process can answer request-key upcalls >> across >> container boundaries, or upcalls can be restricted to specific >> containers. >> >> I'm aware that building on realtime signals may not be a popular >> choice, but >> using realtime signals makes this work simple and ensures delivery. >> Realtime >> signals are able to convey everything needed to construct keys in >> userspace: >> the under-construction key's serial number. >> >> This work is not complete; it has security implications, it needs >> documentation, it has not been reviewed by anyone. Thanks for >> reading this >> RFC. I wish to collect criticism and validate this approach. > > At a high level I do agree that we need to send a message to a > userspace > process and that message should contain enough information to start > the > user mode helper. > > Then a daemon or possibly the container init can receive the message > and dispatch the user mode helper. > > Fundamentally that design solves all of the container issues, and I > think solves a few of the user mode helper issues as well. > > The challenge with this design is that it requires someone standing up > a > daemon to receive the messages and call a user mode helper to retain > compatibility with current systems. Yes.. > I would prefer to see a file descriptor rather than a signal used to > deliver the message. Signals suck for many many reasons and a file > descriptor based notification potentially can be much simpler. In the example keyagent on userspace side, signal handling is done with signalfd(2), which greatly simplifies things. > One of those many reasons is that by not following the common pattern > for filling in kernel_siginfo you have left uninitialized padding in > your structure that will be copied to userspace thus creating a kernel > information leak. Similarly your code doesn't fill in about half the > fields that are present in the siginfo union for the _rt case. Yes, I just used the stack and only filled in the bare minimum. > I think a file descriptor based design could additionally address the > back and forth your design needs with keys to figure out what event > has > happened and what user mode helper to invoke. The keys have already built out a fairly rich interface for accepting authorization keys, and instantiating partially-constructed keys. I think the only communication needed (currently) is to dispatch and pass the key serial value. If we used file descriptors instead of rt signals, there'd be some protocol engineering to do. > Ideally I would also like to see a design less tied to keys. So that > we > could use this for the other user mode helper cases as well. That > said > solving request_key appears to be the truly important part, there > aren't > many other user mode helpers. Still it would be nice if in theory the > design could be used to dispatch the coredump helper as well. What if there was a key_type "usermode_helper"? Requesting a key of that type executes the binary specified in the callout info. A keyagent could satisfy the creation of this key, which would allow the usermode_helper process to execute in the context of a container. If no keyagent, fall back to the legacy call_usermode_helper. Thanks for the look, Ben
© 2016 - 2026 Red Hat, Inc.