Skip to content

Commit

Permalink
keys: Move the user and user-session keyrings to the user_namespace
Browse files Browse the repository at this point in the history
Move the user and user-session keyrings to the user_namespace struct rather
than pinning them from the user_struct struct.  This prevents these
keyrings from propagating across user-namespaces boundaries with regard to
the KEY_SPEC_* flags, thereby making them more useful in a containerised
environment.

The issue is that a single user_struct may be represent UIDs in several
different namespaces.

The way the patch does this is by attaching a 'register keyring' in each
user_namespace and then sticking the user and user-session keyrings into
that.  It can then be searched to retrieve them.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Jann Horn <jannh@google.com>
  • Loading branch information
David Howells committed Jun 26, 2019
1 parent b206f28 commit 0f44e4d
Show file tree
Hide file tree
Showing 9 changed files with 196 additions and 129 deletions.
14 changes: 0 additions & 14 deletions include/linux/sched/user.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
#include <linux/refcount.h>
#include <linux/ratelimit.h>

struct key;

/*
* Some day this will be a full-fledged user tracking system..
*/
Expand All @@ -30,18 +28,6 @@ struct user_struct {
unsigned long unix_inflight; /* How many files in flight in unix sockets */
atomic_long_t pipe_bufs; /* how many pages are allocated in pipe buffers */

#ifdef CONFIG_KEYS
/*
* These pointers can only change from NULL to a non-NULL value once.
* Writes are protected by key_user_keyring_mutex.
* Unlocked readers should use READ_ONCE() unless they know that
* install_user_keyrings() has been called successfully (which sets
* these members to non-NULL values, preventing further modifications).
*/
struct key *uid_keyring; /* UID specific keyring */
struct key *session_keyring; /* UID's default session keyring */
#endif

/* Hash table maintenance information */
struct hlist_node uidhash_node;
kuid_t uid;
Expand Down
9 changes: 7 additions & 2 deletions include/linux/user_namespace.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,19 @@ struct user_namespace {
unsigned long flags;

#ifdef CONFIG_KEYS
/* List of joinable keyrings in this namespace */
/* List of joinable keyrings in this namespace. Modification access of
* these pointers is controlled by keyring_sem. Once
* user_keyring_register is set, it won't be changed, so it can be
* accessed directly with READ_ONCE().
*/
struct list_head keyring_name_list;
struct key *user_keyring_register;
struct rw_semaphore keyring_sem;
#endif

/* Register of per-UID persistent keyrings for this namespace */
#ifdef CONFIG_PERSISTENT_KEYRINGS
struct key *persistent_keyring_register;
struct rw_semaphore persistent_keyring_register_sem;
#endif
struct work_struct work;
#ifdef CONFIG_SYSCTL
Expand Down
7 changes: 1 addition & 6 deletions kernel/user.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,7 @@ struct user_namespace init_user_ns = {
.flags = USERNS_INIT_FLAGS,
#ifdef CONFIG_KEYS
.keyring_name_list = LIST_HEAD_INIT(init_user_ns.keyring_name_list),
#endif
#ifdef CONFIG_PERSISTENT_KEYRINGS
.persistent_keyring_register_sem =
__RWSEM_INITIALIZER(init_user_ns.persistent_keyring_register_sem),
.keyring_sem = __RWSEM_INITIALIZER(init_user_ns.keyring_sem),
#endif
};
EXPORT_SYMBOL_GPL(init_user_ns);
Expand Down Expand Up @@ -143,8 +140,6 @@ static void free_user(struct user_struct *up, unsigned long flags)
{
uid_hash_remove(up);
spin_unlock_irqrestore(&uidhash_lock, flags);
key_put(up->uid_keyring);
key_put(up->session_keyring);
kmem_cache_free(uid_cachep, up);
}

Expand Down
4 changes: 1 addition & 3 deletions kernel/user_namespace.c
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,7 @@ int create_user_ns(struct cred *new)

#ifdef CONFIG_KEYS
INIT_LIST_HEAD(&ns->keyring_name_list);
#endif
#ifdef CONFIG_PERSISTENT_KEYRINGS
init_rwsem(&ns->persistent_keyring_register_sem);
init_rwsem(&ns->keyring_sem);
#endif
ret = -ENOMEM;
if (!setup_userns_sysctls(ns))
Expand Down
3 changes: 2 additions & 1 deletion security/keys/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,8 @@ extern key_ref_t search_process_keyrings_rcu(struct keyring_search_context *ctx)

extern struct key *find_keyring_by_name(const char *name, bool uid_keyring);

extern int install_user_keyrings(void);
extern int look_up_user_keyrings(struct key **, struct key **);
extern struct key *get_user_session_keyring_rcu(const struct cred *);
extern int install_thread_keyring_to_cred(struct cred *);
extern int install_process_keyring_to_cred(struct cred *);
extern int install_session_keyring_to_cred(struct cred *, struct key *);
Expand Down
1 change: 1 addition & 0 deletions security/keys/keyring.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ void key_free_user_ns(struct user_namespace *ns)
list_del_init(&ns->keyring_name_list);
write_unlock(&keyring_name_lock);

key_put(ns->user_keyring_register);
#ifdef CONFIG_PERSISTENT_KEYRINGS
key_put(ns->persistent_keyring_register);
#endif
Expand Down
8 changes: 4 additions & 4 deletions security/keys/persistent.c
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,9 @@ static long key_get_persistent(struct user_namespace *ns, kuid_t uid,

if (ns->persistent_keyring_register) {
reg_ref = make_key_ref(ns->persistent_keyring_register, true);
down_read(&ns->persistent_keyring_register_sem);
down_read(&ns->keyring_sem);
persistent_ref = find_key_to_update(reg_ref, &index_key);
up_read(&ns->persistent_keyring_register_sem);
up_read(&ns->keyring_sem);

if (persistent_ref)
goto found;
Expand All @@ -102,9 +102,9 @@ static long key_get_persistent(struct user_namespace *ns, kuid_t uid,
/* It wasn't in the register, so we'll need to create it. We might
* also need to create the register.
*/
down_write(&ns->persistent_keyring_register_sem);
down_write(&ns->keyring_sem);
persistent_ref = key_create_persistent(ns, uid, &index_key);
up_write(&ns->persistent_keyring_register_sem);
up_write(&ns->keyring_sem);
if (!IS_ERR(persistent_ref))
goto found;

Expand Down
Loading

0 comments on commit 0f44e4d

Please sign in to comment.