Skip to contentSkip to conversation

Keyagents: another call_usermodehelper approach for namespaces

RFC PATCH In review Benjamin Coddington 3 participants 8 messages Jul 12, 2022 – Jul 15, 2022

Subsystems (6)
lore
Share & tools
lore
#0 lore raw

cover Keyagents: another call_usermodehelper approach for namespaces

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/[email protected]/

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
Signature · 153 lines
// 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 ([email protected])
 *
 * 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
#1 lore raw

PATCH 1/2 KEYS: Add key_type keyagent

Define and register a new key_type called keyagent. When instantiated, keyagent keys take a reference on the struct pid of the current task, and store a number between SIGRTMIN and SIGRTMAX.

In a later patch, we'll use that number to send a realtime signal to the keyagent task in order to answer request-key callouts for other key types.

---
 security/keys/Kconfig    |  9 +++++
 security/keys/Makefile   |  1 +
 security/keys/keyagent.c | 73 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 83 insertions(+)
 create mode 100644 security/keys/keyagent.c
security/keys/Kconfig +9 -0
index abb03a1b2a5c..f31a0f94ca88 100644
@@ -112,6 +112,15 @@ config USER_DECRYPTED_DATA
 
 	  If you are unsure as to whether this is required, answer N.
 
+config KEYAGENT
+	bool "KEYAGENT"
+	depends on KEYS
+	help
+	  This option allows persistent userland processes to answer
+	  request-key callouts.
+
+	  If you are unsure as to whether this is required, answer N.
+
 config KEY_DH_OPERATIONS
        bool "Diffie-Hellman operations on retained keys"
        depends on KEYS
security/keys/Makefile +1 -0
index 5f40807f05b3..c753f8f79c38 100644
@@ -23,6 +23,7 @@ obj-$(CONFIG_SYSCTL) += sysctl.o
 obj-$(CONFIG_PERSISTENT_KEYRINGS) += persistent.o
 obj-$(CONFIG_KEY_DH_OPERATIONS) += dh.o
 obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += keyctl_pkey.o
+obj-$(CONFIG_KEYAGENT) += keyagent.o
 
 #
 # Key types
security/keys/keyagent.c new file +73 -0

73 lines not on this page · raw message

Signature · 1 line
2.31.1
#2 lore raw

PATCH 2/2 KEYS: Add keyagent request_key

In reply to Benjamin Coddington

During key construction, search the calling process' session keyring for a keyagent key with a description that matches the requested key_type. If found, link the authkey into the keyagent's process_keyring, and signal the keyagent task with a realtime signal containing the serial number of the key that needs to be constructed.

---
 include/uapi/asm-generic/siginfo.h |  1 +
 security/keys/internal.h           |  4 ++
 security/keys/keyagent.c           | 85 ++++++++++++++++++++++++++++++
 security/keys/request_key.c        |  9 ++++
 4 files changed, 99 insertions(+)
include/uapi/asm-generic/siginfo.h +1 -0
index ffbe4cec9f32..542e297f4466 100644
@@ -185,6 +185,7 @@ typedef struct siginfo {
 #define SI_SIGIO	-5		/* sent by queued SIGIO */
 #define SI_TKILL	-6		/* sent by tkill system call */
 #define SI_DETHREAD	-7		/* sent by execve() killing subsidiary threads */
+#define SI_KEYAGENT	-8		/* sent by request-key */
 #define SI_ASYNCNL	-60		/* sent by glibc async name lookup completion */
 
 #define SI_FROMUSER(siptr)	((siptr)->si_code <= 0)
security/keys/internal.h +4 -0
index 9b9cf3b6fcbb..a6db6eecfff5 100644
@@ -372,5 +372,9 @@ static inline void key_check(const struct key *key)
 
 #define key_check(key) do {} while(0)
 
+#endif
+
+#ifdef CONFIG_KEYAGENT
+extern int keyagent_request_key(struct key *authkey, void *aux);
 #endif
 #endif /* _INTERNAL_H */
security/keys/keyagent.c +85 -0

99 lines not on this page · raw message

security/keys/request_key.c +9 -0
index 2da4404276f0..4c1f5ef55856 100644
@@ -240,9 +240,18 @@ static int construct_key(struct key *key, const void *callout_info,
 	actor = call_sbin_request_key;
 	if (key->type->request_key)
 		actor = key->type->request_key;
+#ifdef CONFIG_KEYAGENT
+	else {
+		ret = keyagent_request_key(authkey, aux);
 
+		/* ENOKEY: no keyagents match on calling process' keyrings */
+		if (ret != -ENOKEY)
+			goto done;
+	}
+#endif
 	ret = actor(authkey, aux);
 
+done:
 	/* check that the actor called complete_request_key() prior to
 	 * returning an error */
 	WARN_ON(ret < 0 &&
Signature · 1 line
2.31.1
#3 lore raw

Re: [PATCH 2/2] KEYS: Add keyagent request_key

Thank you for the patch! Perhaps something to improve:

Hi Benjamin,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on jmorris-security/next-testing] [also build test WARNING on dhowells-fs/fscache-next]
[If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Benjamin-Coddington/Keyagents-another-call_usermodehelper-approach-for-namespaces/20220712-203658
base:   https://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security.git next-testing
config: hexagon-randconfig-r005-20220714 (https://download.01.org/0day-ci/archive/20220715/[email protected]/config)
compiler: clang version 15.0.0 (https://github.com/llvm/llvm-project 5e61b9c556267086ef9b743a0b57df302eef831b)

reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/intel-lab-lkp/linux/commit/4d4f4ae463335d3e611bdb71330ab37af115cde9
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Benjamin-Coddington/Keyagents-another-call_usermodehelper-approach-for-namespaces/20220712-203658 git checkout 4d4f4ae463335d3e611bdb71330ab37af115cde9
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=hexagon SHELL=/bin/bash security/keys/

If you fix the issue, kindly add following tag where applicable

All warnings (new ones prefixed by >>):

>> security/keys/request_key.c:254:1: warning: unused label 'done' [-Wunused-label]

done:
^~~~~
1 warning generated.

vim +/done +254 security/keys/request_key.c

217
218 /*
219 * Call out to userspace for key construction.
220 *
221 * Program failure is ignored in favour of key status.
222 */
223 static int construct_key(struct key *key, const void *callout_info,
224 size_t callout_len, void *aux,
225 struct key *dest_keyring)
226 {
227 request_key_actor_t actor;
228 struct key *authkey;
229 int ret;
230
231 kenter("%d,%p,%zu,%p", key->serial, callout_info, callout_len, aux);
232
233 /* allocate an authorisation key */
234 authkey = request_key_auth_new(key, "create", callout_info, callout_len,
235 dest_keyring);
236 if (IS_ERR(authkey))
237 return PTR_ERR(authkey);
238
239 /* Make the call */
240 actor = call_sbin_request_key;
241 if (key->type->request_key)
242 actor = key->type->request_key;
243 #ifdef CONFIG_KEYAGENT
244 else {
245 ret = keyagent_request_key(authkey, aux);
246
247 /* ENOKEY: no keyagents match on calling process' keyrings */
248 if (ret != -ENOKEY)
249 goto done;
250 }
251 #endif
252 ret = actor(authkey, aux);
253

 > 254	done:

255 /* check that the actor called complete_request_key() prior to
256 * returning an error */
257 WARN_ON(ret < 0 &&
258 !test_bit(KEY_FLAG_INVALIDATED, &authkey->flags));
259
260 key_put(authkey);
261 kleave(" = %d", ret);
262 return ret;
263 }
264

Signature · 2 lines
0-DAY CI Kernel Test Service
https://01.org/lkp
#4 lore raw

Re: [PATCH 2/2] KEYS: Add keyagent request_key

In reply to Benjamin Coddington
Thank you for the patch! Perhaps something to improve:

Hi Benjamin,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on jmorris-security/next-testing]
[also build test WARNING on dhowells-fs/fscache-next arnd-asm-generic/master linus/master v5.19-rc6 next-20220714] [If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Benjamin-Coddington/Keyagents-another-call_usermodehelper-approach-for-namespaces/20220712-203658
base:   https://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security.git next-testing
config: i386-defconfig (https://download.01.org/0day-ci/archive/20220715/[email protected]/config)
compiler: gcc-11 (Debian 11.3.0-3) 11.3.0
reproduce (this is a W=1 build):
        # https://github.com/intel-lab-lkp/linux/commit/4d4f4ae463335d3e611bdb71330ab37af115cde9
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Benjamin-Coddington/Keyagents-another-call_usermodehelper-approach-for-namespaces/20220712-203658
        git checkout 4d4f4ae463335d3e611bdb71330ab37af115cde9
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        make W=1 O=build_dir ARCH=i386 SHELL=/bin/bash security/keys/

If you fix the issue, kindly add following tag where applicable

All warnings (new ones prefixed by >>):

security/keys/request_key.c: In function 'construct_key':

>> security/keys/request_key.c:254:1: warning: label 'done' defined but not used [-Wunused-label]
     254 | done:
         | ^~~~

vim +/done +254 security/keys/request_key.c

217
218 /*
219 * Call out to userspace for key construction.
220 *
221 * Program failure is ignored in favour of key status.
222 */
223 static int construct_key(struct key *key, const void *callout_info,
224 size_t callout_len, void *aux,
225 struct key *dest_keyring)
226 {
227 request_key_actor_t actor;
228 struct key *authkey;
229 int ret;
230
231 kenter("%d,%p,%zu,%p", key->serial, callout_info, callout_len, aux);
232
233 /* allocate an authorisation key */
234 authkey = request_key_auth_new(key, "create", callout_info, callout_len,
235 dest_keyring);
236 if (IS_ERR(authkey))
237 return PTR_ERR(authkey);
238
239 /* Make the call */
240 actor = call_sbin_request_key;
241 if (key->type->request_key)
242 actor = key->type->request_key;
243 #ifdef CONFIG_KEYAGENT
244 else {
245 ret = keyagent_request_key(authkey, aux);
246
247 /* ENOKEY: no keyagents match on calling process' keyrings */
248 if (ret != -ENOKEY)
249 goto done;
250 }
251 #endif
252 ret = actor(authkey, aux);
253

 > 254	done:

255 /* check that the actor called complete_request_key() prior to
256 * returning an error */
257 WARN_ON(ret < 0 &&
258 !test_bit(KEY_FLAG_INVALIDATED, &authkey->flags));
259
260 key_put(authkey);
261 kleave(" = %d", ret);
262 return ret;
263 }
264

Signature · 2 lines
0-DAY CI Kernel Test Service
https://01.org/lkp
#5 lore raw

Re: [PATCH 2/2] KEYS: Add keyagent request_key

In reply to Benjamin Coddington
Thank you for the patch! Perhaps something to improve:

Hi Benjamin,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on jmorris-security/next-testing]
[also build test WARNING on dhowells-fs/fscache-next arnd-asm-generic/master linus/master v5.19-rc6 next-20220714] [If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Benjamin-Coddington/Keyagents-another-call_usermodehelper-approach-for-namespaces/20220712-203658
base:   https://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security.git next-testing
config: i386-allyesconfig (https://download.01.org/0day-ci/archive/20220715/[email protected]/config)
compiler: gcc-11 (Debian 11.3.0-3) 11.3.0
reproduce (this is a W=1 build):
        # https://github.com/intel-lab-lkp/linux/commit/4d4f4ae463335d3e611bdb71330ab37af115cde9
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Benjamin-Coddington/Keyagents-another-call_usermodehelper-approach-for-namespaces/20220712-203658
        git checkout 4d4f4ae463335d3e611bdb71330ab37af115cde9
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        make W=1 O=build_dir ARCH=i386 SHELL=/bin/bash security/keys/

If you fix the issue, kindly add following tag where applicable

All warnings (new ones prefixed by >>):

>> security/keys/keyagent.c:78:5: warning: no previous prototype for 'keyagent_request_key' [-Wmissing-prototypes]

78 | int keyagent_request_key(struct key *authkey, void *aux)
| ^~~~~~~~~~~~~~~~~~~~

vim +/keyagent_request_key +78 security/keys/keyagent.c

72
73 /*
74 * Search the calling process' keyrings for a keyagent that
75 * matches the requested key type. If found, signal the keyagent
76 * to construct and link the key, else return -ENOKEY.
77 */

  > 78	int keyagent_request_key(struct key *authkey, void *aux)
    79	{
    80		struct key *ka_key, *target_key;
    81		struct request_key_auth *rka;
    82		key_ref_t ka_ref;
    83		const struct cred *cred = current_cred();
    84		int ret;
    85	
    86		/* We must be careful not to touch authkey and aux if
    87		 * returning -ENOKEY, since it will be reused.   */
    88		rka = get_request_key_auth(authkey);
    89		target_key = rka->target_key;
    90	
    91		/* Does the calling process have a keyagent in its session keyring? */
    92		ka_ref = keyring_search(
    93						make_key_ref(cred->session_keyring, 1),
    94						&key_type_keyagent,
    95						target_key->type->name, false);
    96	
    97		if (IS_ERR(ka_ref))
    98			return -ENOKEY;
    99	

100 /* We found a keyagent, let's call out to it. */
101 ka_key = key_ref_to_ptr(ka_ref);
102 ret = keyagent_signal(ka_key, target_key, authkey);
103 key_put(key_ref_to_ptr(ka_ref));
104
105 return ret;
106 }
107

Signature · 2 lines
0-DAY CI Kernel Test Service
https://01.org/lkp
#6 lore raw
In reply to Benjamin Coddington

Adding the containers list to the discussion so more interested people have a chance of seeing this.

Quoted 31 lines · Benjamin Coddington

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/[email protected]/

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.

Quoted 8 lines

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

#7 lore raw
Quoted 52 lines · Eric W. Biederman

Adding the containers list to the discussion so more interested people have a chance of seeing this.

Quoted 31 lines · Benjamin Coddington

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/[email protected]/

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.

Quoted 8 lines

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.

Quoted 6 lines

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..

Quoted 3 lines

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.

Quoted 5 lines

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.

Quoted 3 lines

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.

Quoted 5 lines

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