Re: Keyrings, user namespaces and the user_struct

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



David Howells <dhowells@xxxxxxxxxx> writes:

> Andy Lutomirski <luto@xxxxxxxxxxxxxx> wrote:
>
>> ... Perhaps we could simply *remove* the concept of named keys and keyrings.
>
> See Linus's dictum about breaking userspace.
>
> The problem isn't named keys: keys have to be named - the description is how
> they're looked up typically.  Further, non-keyring keys can't be looked up
> directly by name - you have to search for them in a keyring.
>
> The issue here is named keyrings and keyctl_join_session_keyring().  It might
> well have been a bad idea - though I've seen some people arguing for a single
> session keyring shared across all a user's logins, in which case, we might
> want this after all (or use the user-default session).
>
> One thing we perhaps do want to do, though, is restrict the names of keyrings
> to the user_namespace in which the keyring was created.

Grr...

The first round of user namespace support had actually restricted the
description lookup to a single user namespace.  Then I missed a detail
and converted the code to it's current form.  Ooops!

I think the answer for all of the issues raised in this conversation is
to just make the keyring names and the keyring name lookup per user
namespace.

Maybe a few small additional tweaks to install_user_keyrings to notice
if we have the user keyring from the wrong user namespace.

Something like the untested patch below.

Eric

diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index eb209d4523f5..5ea474df16bd 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -52,6 +52,12 @@ struct user_namespace {
 	struct key		*persistent_keyring_register;
 	struct rw_semaphore	persistent_keyring_register_sem;
 #endif
+#ifdef CONFIG_KEYS
+# ifndef KEYRING_NAME_HASH_SIZE
+# define KEYRING_NAME_HASH_SIZE	(1 << 5)
+# endif
+	struct list_head keyring_name_hash[KEYRING_NAME_HASH_SIZE];
+#endif
 	struct work_struct	work;
 #ifdef CONFIG_SYSCTL
 	struct ctl_table_set	set;
diff --git a/security/keys/internal.h b/security/keys/internal.h
index a705a7d92ad7..e83e9e6129fc 100644
--- a/security/keys/internal.h
+++ b/security/keys/internal.h
@@ -18,6 +18,7 @@
 #include <linux/keyctl.h>
 
 struct iovec;
+struct user_namespace;
 
 #ifdef __KDEBUG
 #define kenter(FMT, ...) \
@@ -137,7 +138,7 @@ extern key_ref_t keyring_search_aux(key_ref_t keyring_ref,
 extern key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx);
 extern key_ref_t search_process_keyrings(struct keyring_search_context *ctx);
 
-extern struct key *find_keyring_by_name(const char *name, bool skip_perm_check);
+extern struct key *find_keyring_by_name(struct user_namespace *ns, const char *name, bool skip_perm_check);
 
 extern int install_user_keyrings(void);
 extern int install_thread_keyring_to_cred(struct cred *);
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index c91e4e0cea08..be1bb5ca1c3a 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -20,6 +20,7 @@
 #include <keys/user-type.h>
 #include <linux/assoc_array_priv.h>
 #include <linux/uaccess.h>
+#include <linux/user_namespace.h>
 #include "internal.h"
 
 /*
@@ -55,7 +56,6 @@ static inline void *keyring_key_to_ptr(struct key *key)
 	return key;
 }
 
-static struct list_head	keyring_name_hash[KEYRING_NAME_HASH_SIZE];
 static DEFINE_RWLOCK(keyring_name_lock);
 
 static inline unsigned keyring_hash(const char *desc)
@@ -106,7 +106,7 @@ static DECLARE_RWSEM(keyring_serialise_link_sem);
  * Publish the name of a keyring so that it can be found by name (if it has
  * one).
  */
-static void keyring_publish_name(struct key *keyring)
+static void keyring_publish_name(struct user_namespace *ns, struct key *keyring)
 {
 	int bucket;
 
@@ -115,11 +115,11 @@ static void keyring_publish_name(struct key *keyring)
 
 		write_lock(&keyring_name_lock);
 
-		if (!keyring_name_hash[bucket].next)
-			INIT_LIST_HEAD(&keyring_name_hash[bucket]);
+		if (!ns->keyring_name_hash[bucket].next)
+			INIT_LIST_HEAD(&ns->keyring_name_hash[bucket]);
 
 		list_add_tail(&keyring->name_link,
-			      &keyring_name_hash[bucket]);
+			      &ns->keyring_name_hash[bucket]);
 
 		write_unlock(&keyring_name_lock);
 	}
@@ -148,9 +148,11 @@ static void keyring_free_preparse(struct key_preparsed_payload *prep)
 static int keyring_instantiate(struct key *keyring,
 			       struct key_preparsed_payload *prep)
 {
+	struct user_namespace *ns = (void *)prep->data;
+
 	assoc_array_init(&keyring->keys);
 	/* make the keyring available by name if it has one */
-	keyring_publish_name(keyring);
+	keyring_publish_name(ns, keyring);
 	return 0;
 }
 
@@ -497,13 +499,14 @@ struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid,
 					       const union key_payload *),
 			  struct key *dest)
 {
+	void *data = cred->user_ns;
 	struct key *keyring;
 	int ret;
 
 	keyring = key_alloc(&key_type_keyring, description,
 			    uid, gid, cred, perm, flags, restrict_link);
 	if (!IS_ERR(keyring)) {
-		ret = key_instantiate_and_link(keyring, NULL, 0, dest, NULL);
+		ret = key_instantiate_and_link(keyring, data, 0, dest, NULL);
 		if (ret < 0) {
 			key_put(keyring);
 			keyring = ERR_PTR(ret);
@@ -997,7 +1000,8 @@ key_ref_t find_key_to_update(key_ref_t keyring_ref,
  * Returns a pointer to the keyring with the keyring's refcount having being
  * incremented on success.  -ENOKEY is returned if a key could not be found.
  */
-struct key *find_keyring_by_name(const char *name, bool skip_perm_check)
+struct key *find_keyring_by_name(struct user_namespace *ns,
+				 const char *name, bool skip_perm_check)
 {
 	struct key *keyring;
 	int bucket;
@@ -1009,16 +1013,13 @@ struct key *find_keyring_by_name(const char *name, bool skip_perm_check)
 
 	read_lock(&keyring_name_lock);
 
-	if (keyring_name_hash[bucket].next) {
+	if (ns->keyring_name_hash[bucket].next) {
 		/* search this hash bucket for a keyring with a matching name
 		 * that's readable and that hasn't been revoked */
 		list_for_each_entry(keyring,
-				    &keyring_name_hash[bucket],
+				    &ns->keyring_name_hash[bucket],
 				    name_link
 				    ) {
-			if (!kuid_has_mapping(current_user_ns(), keyring->user->uid))
-				continue;
-
 			if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
 				continue;
 
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
index 40a885239782..5f527dcb4e79 100644
--- a/security/keys/process_keys.c
+++ b/security/keys/process_keys.c
@@ -72,7 +72,7 @@ int install_user_keyrings(void)
 		 *   may have been destroyed by setuid */
 		sprintf(buf, "_uid.%u", uid);
 
-		uid_keyring = find_keyring_by_name(buf, true);
+		uid_keyring = find_keyring_by_name(cred->user_ns, buf, true);
 		if (IS_ERR(uid_keyring)) {
 			uid_keyring = keyring_alloc(buf, user->uid, INVALID_GID,
 						    cred, user_keyring_perm,
@@ -88,7 +88,7 @@ int install_user_keyrings(void)
 		 * already) */
 		sprintf(buf, "_uid_ses.%u", uid);
 
-		session_keyring = find_keyring_by_name(buf, true);
+		session_keyring = find_keyring_by_name(cred->user_ns, buf, true);
 		if (IS_ERR(session_keyring)) {
 			session_keyring =
 				keyring_alloc(buf, user->uid, INVALID_GID,
@@ -783,7 +783,7 @@ long join_session_keyring(const char *name)
 	mutex_lock(&key_session_mutex);
 
 	/* look for an existing keyring of this name */
-	keyring = find_keyring_by_name(name, false);
+	keyring = find_keyring_by_name(old->user_ns, name, false);
 	if (PTR_ERR(keyring) == -ENOKEY) {
 		/* not found - try and create a new one */
 		keyring = keyring_alloc(
_______________________________________________
Containers mailing list
Containers@xxxxxxxxxxxxxxxxxxxxxxxxxx
https://lists.linuxfoundation.org/mailman/listinfo/containers



[Index of Archives]     [Cgroups]     [Netdev]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite Forum]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux