diff options
Diffstat (limited to 'security/keys')
-rw-r--r-- | security/keys/internal.h | 4 | ||||
-rw-r--r-- | security/keys/key.c | 11 | ||||
-rw-r--r-- | security/keys/keyctl.c | 2 | ||||
-rw-r--r-- | security/keys/keyring.c | 3 | ||||
-rw-r--r-- | security/keys/permission.c | 5 | ||||
-rw-r--r-- | security/keys/proc.c | 55 | ||||
-rw-r--r-- | security/keys/process_keys.c | 2 | ||||
-rw-r--r-- | security/keys/request_key.c | 2 |
8 files changed, 73 insertions, 11 deletions
diff --git a/security/keys/internal.h b/security/keys/internal.h index 81932abefe7b..9fb679c66b8a 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -53,6 +53,7 @@ struct key_user { atomic_t nkeys; /* number of keys */ atomic_t nikeys; /* number of instantiated keys */ uid_t uid; + struct user_namespace *user_ns; int qnkeys; /* number of keys allocated to this user */ int qnbytes; /* number of bytes allocated to this user */ }; @@ -61,7 +62,8 @@ extern struct rb_root key_user_tree; extern spinlock_t key_user_lock; extern struct key_user root_key_user; -extern struct key_user *key_user_lookup(uid_t uid); +extern struct key_user *key_user_lookup(uid_t uid, + struct user_namespace *user_ns); extern void key_user_put(struct key_user *user); /* diff --git a/security/keys/key.c b/security/keys/key.c index f76c8a546fd3..4a1297d1ada4 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -18,6 +18,7 @@ #include <linux/workqueue.h> #include <linux/random.h> #include <linux/err.h> +#include <linux/user_namespace.h> #include "internal.h" static struct kmem_cache *key_jar; @@ -60,7 +61,7 @@ void __key_check(const struct key *key) * get the key quota record for a user, allocating a new record if one doesn't * already exist */ -struct key_user *key_user_lookup(uid_t uid) +struct key_user *key_user_lookup(uid_t uid, struct user_namespace *user_ns) { struct key_user *candidate = NULL, *user; struct rb_node *parent = NULL; @@ -79,6 +80,10 @@ struct key_user *key_user_lookup(uid_t uid) p = &(*p)->rb_left; else if (uid > user->uid) p = &(*p)->rb_right; + else if (user_ns < user->user_ns) + p = &(*p)->rb_left; + else if (user_ns > user->user_ns) + p = &(*p)->rb_right; else goto found; } @@ -106,6 +111,7 @@ struct key_user *key_user_lookup(uid_t uid) atomic_set(&candidate->nkeys, 0); atomic_set(&candidate->nikeys, 0); candidate->uid = uid; + candidate->user_ns = get_user_ns(user_ns); candidate->qnkeys = 0; candidate->qnbytes = 0; spin_lock_init(&candidate->lock); @@ -136,6 +142,7 @@ void key_user_put(struct key_user *user) if (atomic_dec_and_lock(&user->usage, &key_user_lock)) { rb_erase(&user->node, &key_user_tree); spin_unlock(&key_user_lock); + put_user_ns(user->user_ns); kfree(user); } @@ -234,7 +241,7 @@ struct key *key_alloc(struct key_type *type, const char *desc, quotalen = desclen + type->def_datalen; /* get hold of the key tracking for this user */ - user = key_user_lookup(uid); + user = key_user_lookup(uid, cred->user->user_ns); if (!user) goto no_memory_1; diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index b1ec3b4ee17d..7f09fb897d2b 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -726,7 +726,7 @@ long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid) /* change the UID */ if (uid != (uid_t) -1 && uid != key->uid) { ret = -ENOMEM; - newowner = key_user_lookup(uid); + newowner = key_user_lookup(uid, current_user_ns()); if (!newowner) goto error_put; diff --git a/security/keys/keyring.c b/security/keys/keyring.c index ed851574d073..3dba81c2eba3 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -539,6 +539,9 @@ struct key *find_keyring_by_name(const char *name, bool skip_perm_check) &keyring_name_hash[bucket], type_data.link ) { + if (keyring->user->user_ns != current_user_ns()) + continue; + if (test_bit(KEY_FLAG_REVOKED, &keyring->flags)) continue; diff --git a/security/keys/permission.c b/security/keys/permission.c index 5d9fc7b93f2e..0ed802c9e698 100644 --- a/security/keys/permission.c +++ b/security/keys/permission.c @@ -35,6 +35,9 @@ int key_task_permission(const key_ref_t key_ref, const struct cred *cred, key = key_ref_to_ptr(key_ref); + if (key->user->user_ns != cred->user->user_ns) + goto use_other_perms; + /* use the second 8-bits of permissions for keys the caller owns */ if (key->uid == cred->fsuid) { kperm = key->perm >> 16; @@ -56,6 +59,8 @@ int key_task_permission(const key_ref_t key_ref, const struct cred *cred, } } +use_other_perms: + /* otherwise use the least-significant 8-bits */ kperm = key->perm; diff --git a/security/keys/proc.c b/security/keys/proc.c index 7f508def50e3..769f9bdfd2b3 100644 --- a/security/keys/proc.c +++ b/security/keys/proc.c @@ -91,6 +91,28 @@ __initcall(key_proc_init); */ #ifdef CONFIG_KEYS_DEBUG_PROC_KEYS +static struct rb_node *__key_serial_next(struct rb_node *n) +{ + while (n) { + struct key *key = rb_entry(n, struct key, serial_node); + if (key->user->user_ns == current_user_ns()) + break; + n = rb_next(n); + } + return n; +} + +static struct rb_node *key_serial_next(struct rb_node *n) +{ + return __key_serial_next(rb_next(n)); +} + +static struct rb_node *key_serial_first(struct rb_root *r) +{ + struct rb_node *n = rb_first(r); + return __key_serial_next(n); +} + static int proc_keys_open(struct inode *inode, struct file *file) { return seq_open(file, &proc_keys_ops); @@ -104,10 +126,10 @@ static void *proc_keys_start(struct seq_file *p, loff_t *_pos) spin_lock(&key_serial_lock); - _p = rb_first(&key_serial_tree); + _p = key_serial_first(&key_serial_tree); while (pos > 0 && _p) { pos--; - _p = rb_next(_p); + _p = key_serial_next(_p); } return _p; @@ -117,7 +139,7 @@ static void *proc_keys_start(struct seq_file *p, loff_t *_pos) static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos) { (*_pos)++; - return rb_next((struct rb_node *) v); + return key_serial_next((struct rb_node *) v); } @@ -203,6 +225,27 @@ static int proc_keys_show(struct seq_file *m, void *v) #endif /* CONFIG_KEYS_DEBUG_PROC_KEYS */ +static struct rb_node *__key_user_next(struct rb_node *n) +{ + while (n) { + struct key_user *user = rb_entry(n, struct key_user, node); + if (user->user_ns == current_user_ns()) + break; + n = rb_next(n); + } + return n; +} + +static struct rb_node *key_user_next(struct rb_node *n) +{ + return __key_user_next(rb_next(n)); +} + +static struct rb_node *key_user_first(struct rb_root *r) +{ + struct rb_node *n = rb_first(r); + return __key_user_next(n); +} /*****************************************************************************/ /* * implement "/proc/key-users" to provides a list of the key users @@ -220,10 +263,10 @@ static void *proc_key_users_start(struct seq_file *p, loff_t *_pos) spin_lock(&key_user_lock); - _p = rb_first(&key_user_tree); + _p = key_user_first(&key_user_tree); while (pos > 0 && _p) { pos--; - _p = rb_next(_p); + _p = key_user_next(_p); } return _p; @@ -233,7 +276,7 @@ static void *proc_key_users_start(struct seq_file *p, loff_t *_pos) static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos) { (*_pos)++; - return rb_next((struct rb_node *) v); + return key_user_next((struct rb_node *) v); } diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 2f5d89e92b85..276d27882ce8 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -17,6 +17,7 @@ #include <linux/fs.h> #include <linux/err.h> #include <linux/mutex.h> +#include <linux/user_namespace.h> #include <asm/uaccess.h> #include "internal.h" @@ -34,6 +35,7 @@ struct key_user root_key_user = { .nkeys = ATOMIC_INIT(2), .nikeys = ATOMIC_INIT(2), .uid = 0, + .user_ns = &init_user_ns, }; /*****************************************************************************/ diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 0e04f72ef2d4..22a31582bfaa 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -365,7 +365,7 @@ static struct key *construct_key_and_link(struct key_type *type, kenter(""); - user = key_user_lookup(current_fsuid()); + user = key_user_lookup(current_fsuid(), current_user_ns()); if (!user) return ERR_PTR(-ENOMEM); |