From c4a4d603796c727b9555867571f89483be9c565e Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 16 Nov 2011 23:15:31 -0800 Subject: userns: Use cred->user_ns instead of cred->user->user_ns Optimize performance and prepare for the removal of the user_ns reference from user_struct. Remove the slow long walk through cred->user->user_ns and instead go straight to cred->user_ns. Acked-by: Serge Hallyn Signed-off-by: Eric W. Biederman --- security/commoncap.c | 14 +++++++------- security/keys/key.c | 2 +- security/keys/permission.c | 2 +- security/keys/process_keys.c | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) (limited to 'security') diff --git a/security/commoncap.c b/security/commoncap.c index 0cf4b53480a7..8b3e10e2eac7 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -81,7 +81,7 @@ int cap_capable(const struct cred *cred, struct user_namespace *targ_ns, return 0; /* Do we have the necessary capabilities? */ - if (targ_ns == cred->user->user_ns) + if (targ_ns == cred->user_ns) return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM; /* Have we tried all of the parent namespaces? */ @@ -136,10 +136,10 @@ int cap_ptrace_access_check(struct task_struct *child, unsigned int mode) rcu_read_lock(); cred = current_cred(); child_cred = __task_cred(child); - if (cred->user->user_ns == child_cred->user->user_ns && + if (cred->user_ns == child_cred->user_ns && cap_issubset(child_cred->cap_permitted, cred->cap_permitted)) goto out; - if (ns_capable(child_cred->user->user_ns, CAP_SYS_PTRACE)) + if (ns_capable(child_cred->user_ns, CAP_SYS_PTRACE)) goto out; ret = -EPERM; out: @@ -168,10 +168,10 @@ int cap_ptrace_traceme(struct task_struct *parent) rcu_read_lock(); cred = __task_cred(parent); child_cred = current_cred(); - if (cred->user->user_ns == child_cred->user->user_ns && + if (cred->user_ns == child_cred->user_ns && cap_issubset(child_cred->cap_permitted, cred->cap_permitted)) goto out; - if (has_ns_capability(parent, child_cred->user->user_ns, CAP_SYS_PTRACE)) + if (has_ns_capability(parent, child_cred->user_ns, CAP_SYS_PTRACE)) goto out; ret = -EPERM; out: @@ -214,7 +214,7 @@ static inline int cap_inh_is_capped(void) /* they are so limited unless the current task has the CAP_SETPCAP * capability */ - if (cap_capable(current_cred(), current_cred()->user->user_ns, + if (cap_capable(current_cred(), current_cred()->user_ns, CAP_SETPCAP, SECURITY_CAP_AUDIT) == 0) return 0; return 1; @@ -866,7 +866,7 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3, || ((new->securebits & SECURE_ALL_LOCKS & ~arg2)) /*[2]*/ || (arg2 & ~(SECURE_ALL_LOCKS | SECURE_ALL_BITS)) /*[3]*/ || (cap_capable(current_cred(), - current_cred()->user->user_ns, CAP_SETPCAP, + current_cred()->user_ns, CAP_SETPCAP, SECURITY_CAP_AUDIT) != 0) /*[4]*/ /* * [1] no changing of bits that are locked diff --git a/security/keys/key.c b/security/keys/key.c index 06783cffb3af..7e6034793af3 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -253,7 +253,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, cred->user->user_ns); + user = key_user_lookup(uid, cred->user_ns); if (!user) goto no_memory_1; diff --git a/security/keys/permission.c b/security/keys/permission.c index c35b5229e3cd..e146cbd714bd 100644 --- a/security/keys/permission.c +++ b/security/keys/permission.c @@ -36,7 +36,7 @@ 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) + if (key->user->user_ns != cred->user_ns) goto use_other_perms; /* use the second 8-bits of permissions for keys the caller owns */ diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index be7ecb2018dd..70febff06da9 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -858,7 +858,7 @@ void key_replace_session_keyring(void) new-> sgid = old-> sgid; new->fsgid = old->fsgid; new->user = get_uid(old->user); - new->user_ns = new->user->user_ns; + new->user_ns = new->user_ns; new->group_info = get_group_info(old->group_info); new->securebits = old->securebits; -- cgit v1.2.3-59-g8ed1b From 0093ccb68f3753c0ba4d74c89d7e0f444b8d6123 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 16 Nov 2011 21:52:53 -0800 Subject: cred: Refcount the user_ns pointed to by the cred. struct user_struct will shortly loose it's user_ns reference so make the cred user_ns reference a proper reference complete with reference counting. Acked-by: Serge Hallyn Signed-off-by: Eric W. Biederman --- include/linux/cred.h | 2 +- kernel/cred.c | 8 +++----- kernel/user_namespace.c | 8 +++++--- security/keys/process_keys.c | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) (limited to 'security') diff --git a/include/linux/cred.h b/include/linux/cred.h index d12c4e475c15..2c60ec802678 100644 --- a/include/linux/cred.h +++ b/include/linux/cred.h @@ -146,7 +146,7 @@ struct cred { void *security; /* subjective LSM security */ #endif struct user_struct *user; /* real user ID subscription */ - struct user_namespace *user_ns; /* cached user->user_ns */ + struct user_namespace *user_ns; /* user_ns the caps and keyrings are relative to. */ struct group_info *group_info; /* supplementary groups for euid/fsgid */ struct rcu_head rcu; /* RCU deletion hook */ }; diff --git a/kernel/cred.c b/kernel/cred.c index 97b36eeca4c9..7a0d80669886 100644 --- a/kernel/cred.c +++ b/kernel/cred.c @@ -148,6 +148,7 @@ static void put_cred_rcu(struct rcu_head *rcu) if (cred->group_info) put_group_info(cred->group_info); free_uid(cred->user); + put_user_ns(cred->user_ns); kmem_cache_free(cred_jar, cred); } @@ -303,6 +304,7 @@ struct cred *prepare_creds(void) set_cred_subscribers(new, 0); get_group_info(new->group_info); get_uid(new->user); + get_user_ns(new->user_ns); #ifdef CONFIG_KEYS key_get(new->thread_keyring); @@ -412,11 +414,6 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags) goto error_put; } - /* cache user_ns in cred. Doesn't need a refcount because it will - * stay pinned by cred->user - */ - new->user_ns = new->user->user_ns; - #ifdef CONFIG_KEYS /* new threads get their own thread keyrings if their parent already * had one */ @@ -676,6 +673,7 @@ struct cred *prepare_kernel_cred(struct task_struct *daemon) atomic_set(&new->usage, 1); set_cred_subscribers(new, 0); get_uid(new->user); + get_user_ns(new->user_ns); get_group_info(new->group_info); #ifdef CONFIG_KEYS diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c index f084083a0fd3..58bb8781a778 100644 --- a/kernel/user_namespace.c +++ b/kernel/user_namespace.c @@ -24,7 +24,7 @@ static struct kmem_cache *user_ns_cachep __read_mostly; */ int create_user_ns(struct cred *new) { - struct user_namespace *ns; + struct user_namespace *ns, *parent_ns = new->user_ns; struct user_struct *root_user; int n; @@ -57,8 +57,10 @@ int create_user_ns(struct cred *new) #endif /* tgcred will be cleared in our caller bc CLONE_THREAD won't be set */ - /* root_user holds a reference to ns, our reference can be dropped */ - put_user_ns(ns); + /* Leave the reference to our user_ns with the new cred */ + new->user_ns = ns; + + put_user_ns(parent_ns); return 0; } diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 70febff06da9..447fb7618ff3 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -858,7 +858,7 @@ void key_replace_session_keyring(void) new-> sgid = old-> sgid; new->fsgid = old->fsgid; new->user = get_uid(old->user); - new->user_ns = new->user_ns; + new->user_ns = get_user_ns(new->user_ns); new->group_info = get_group_info(old->group_info); new->securebits = old->securebits; -- cgit v1.2.3-59-g8ed1b From aeb3ae9da9b50a386b22af786d19b623e8d9f0fa Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 16 Nov 2011 21:59:43 -0800 Subject: userns: Add an explicit reference to the parent user namespace I am about to remove the struct user_namespace reference from struct user_struct. So keep an explicit track of the parent user namespace. Take advantage of this new reference and replace instances of user_ns->creator->user_ns with user_ns->parent. Acked-by: Serge Hallyn Signed-off-by: Eric W. Biederman --- include/linux/user_namespace.h | 1 + kernel/user_namespace.c | 13 ++++++------- security/commoncap.c | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) (limited to 'security') diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h index faf467944baf..dc2d85a76376 100644 --- a/include/linux/user_namespace.h +++ b/include/linux/user_namespace.h @@ -12,6 +12,7 @@ struct user_namespace { struct kref kref; struct hlist_head uidhash_table[UIDHASH_SZ]; + struct user_namespace *parent; struct user_struct *creator; struct work_struct destroyer; }; diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c index 58bb8781a778..c15e533d6bc5 100644 --- a/kernel/user_namespace.c +++ b/kernel/user_namespace.c @@ -45,6 +45,7 @@ int create_user_ns(struct cred *new) } /* set the new root user in the credentials under preparation */ + ns->parent = parent_ns; ns->creator = new->user; new->user = root_user; new->uid = new->euid = new->suid = new->fsuid = 0; @@ -60,8 +61,6 @@ int create_user_ns(struct cred *new) /* Leave the reference to our user_ns with the new cred */ new->user_ns = ns; - put_user_ns(parent_ns); - return 0; } @@ -72,10 +71,12 @@ int create_user_ns(struct cred *new) */ static void free_user_ns_work(struct work_struct *work) { - struct user_namespace *ns = + struct user_namespace *parent, *ns = container_of(work, struct user_namespace, destroyer); + parent = ns->parent; free_uid(ns->creator); kmem_cache_free(user_ns_cachep, ns); + put_user_ns(parent); } void free_user_ns(struct kref *kref) @@ -99,8 +100,7 @@ uid_t user_ns_map_uid(struct user_namespace *to, const struct cred *cred, uid_t /* Is cred->user the creator of the target user_ns * or the creator of one of it's parents? */ - for ( tmp = to; tmp != &init_user_ns; - tmp = tmp->creator->user_ns ) { + for ( tmp = to; tmp != &init_user_ns; tmp = tmp->parent ) { if (cred->user == tmp->creator) { return (uid_t)0; } @@ -120,8 +120,7 @@ gid_t user_ns_map_gid(struct user_namespace *to, const struct cred *cred, gid_t /* Is cred->user the creator of the target user_ns * or the creator of one of it's parents? */ - for ( tmp = to; tmp != &init_user_ns; - tmp = tmp->creator->user_ns ) { + for ( tmp = to; tmp != &init_user_ns; tmp = tmp->parent ) { if (cred->user == tmp->creator) { return (gid_t)0; } diff --git a/security/commoncap.c b/security/commoncap.c index 8b3e10e2eac7..435d074853f3 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -92,7 +92,7 @@ int cap_capable(const struct cred *cred, struct user_namespace *targ_ns, *If you have a capability in a parent user ns, then you have * it over all children user namespaces as well. */ - targ_ns = targ_ns->creator->user_ns; + targ_ns = targ_ns->parent; } /* We never get here */ -- cgit v1.2.3-59-g8ed1b From 783291e6900292521a3895583785e0c04a56c5b3 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 17 Nov 2011 01:32:59 -0800 Subject: userns: Simplify the user_namespace by making userns->creator a kuid. - Transform userns->creator from a user_struct reference to a simple kuid_t, kgid_t pair. In cap_capable this allows the check to see if we are the creator of a namespace to become the classic suser style euid permission check. This allows us to remove the need for a struct cred in the mapping functions and still be able to dispaly the user namespace creators uid and gid as 0. - Remove the now unnecessary delayed_work in free_user_ns. All that is left for free_user_ns to do is to call kmem_cache_free and put_user_ns. Those functions can be called in any context so call them directly from free_user_ns removing the need for delayed work. Acked-by: Serge Hallyn Signed-off-by: Eric W. Biederman --- include/linux/user_namespace.h | 4 ++-- kernel/user.c | 7 ++++--- kernel/user_namespace.c | 42 ++++++++++++++++++++---------------------- security/commoncap.c | 5 +++-- 4 files changed, 29 insertions(+), 29 deletions(-) (limited to 'security') diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h index d767508db4f9..8a391bd53de2 100644 --- a/include/linux/user_namespace.h +++ b/include/linux/user_namespace.h @@ -9,8 +9,8 @@ struct user_namespace { struct kref kref; struct user_namespace *parent; - struct user_struct *creator; - struct work_struct destroyer; + kuid_t owner; + kgid_t group; }; extern struct user_namespace init_user_ns; diff --git a/kernel/user.c b/kernel/user.c index 025077e54a7c..cff385659175 100644 --- a/kernel/user.c +++ b/kernel/user.c @@ -25,7 +25,8 @@ struct user_namespace init_user_ns = { .kref = { .refcount = ATOMIC_INIT(3), }, - .creator = &root_user, + .owner = GLOBAL_ROOT_UID, + .group = GLOBAL_ROOT_GID, }; EXPORT_SYMBOL_GPL(init_user_ns); @@ -54,9 +55,9 @@ struct hlist_head uidhash_table[UIDHASH_SZ]; */ static DEFINE_SPINLOCK(uidhash_lock); -/* root_user.__count is 2, 1 for init task cred, 1 for init_user_ns->user_ns */ +/* root_user.__count is 1, for init task cred */ struct user_struct root_user = { - .__count = ATOMIC_INIT(2), + .__count = ATOMIC_INIT(1), .processes = ATOMIC_INIT(1), .files = ATOMIC_INIT(0), .sigpending = ATOMIC_INIT(0), diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c index 898e973bd1e8..ed08836558e0 100644 --- a/kernel/user_namespace.c +++ b/kernel/user_namespace.c @@ -27,6 +27,16 @@ int create_user_ns(struct cred *new) { struct user_namespace *ns, *parent_ns = new->user_ns; struct user_struct *root_user; + kuid_t owner = make_kuid(new->user_ns, new->euid); + kgid_t group = make_kgid(new->user_ns, new->egid); + + /* The creator needs a mapping in the parent user namespace + * or else we won't be able to reasonably tell userspace who + * created a user_namespace. + */ + if (!kuid_has_mapping(parent_ns, owner) || + !kgid_has_mapping(parent_ns, group)) + return -EPERM; ns = kmem_cache_alloc(user_ns_cachep, GFP_KERNEL); if (!ns) @@ -43,7 +53,9 @@ int create_user_ns(struct cred *new) /* set the new root user in the credentials under preparation */ ns->parent = parent_ns; - ns->creator = new->user; + ns->owner = owner; + ns->group = group; + free_uid(new->user); new->user = root_user; new->uid = new->euid = new->suid = new->fsuid = 0; new->gid = new->egid = new->sgid = new->fsgid = 0; @@ -63,35 +75,22 @@ int create_user_ns(struct cred *new) #endif /* tgcred will be cleared in our caller bc CLONE_THREAD won't be set */ - /* Leave the reference to our user_ns with the new cred */ + /* Leave the new->user_ns reference with the new user namespace. */ + /* Leave the reference to our user_ns with the new cred. */ new->user_ns = ns; return 0; } -/* - * Deferred destructor for a user namespace. This is required because - * free_user_ns() may be called with uidhash_lock held, but we need to call - * back to free_uid() which will want to take the lock again. - */ -static void free_user_ns_work(struct work_struct *work) +void free_user_ns(struct kref *kref) { struct user_namespace *parent, *ns = - container_of(work, struct user_namespace, destroyer); + container_of(kref, struct user_namespace, kref); + parent = ns->parent; - free_uid(ns->creator); kmem_cache_free(user_ns_cachep, ns); put_user_ns(parent); } - -void free_user_ns(struct kref *kref) -{ - struct user_namespace *ns = - container_of(kref, struct user_namespace, kref); - - INIT_WORK(&ns->destroyer, free_user_ns_work); - schedule_work(&ns->destroyer); -} EXPORT_SYMBOL(free_user_ns); uid_t user_ns_map_uid(struct user_namespace *to, const struct cred *cred, uid_t uid) @@ -101,12 +100,11 @@ uid_t user_ns_map_uid(struct user_namespace *to, const struct cred *cred, uid_t if (likely(to == cred->user_ns)) return uid; - /* Is cred->user the creator of the target user_ns * or the creator of one of it's parents? */ for ( tmp = to; tmp != &init_user_ns; tmp = tmp->parent ) { - if (cred->user == tmp->creator) { + if (uid_eq(cred->user->uid, tmp->owner)) { return (uid_t)0; } } @@ -126,7 +124,7 @@ gid_t user_ns_map_gid(struct user_namespace *to, const struct cred *cred, gid_t * or the creator of one of it's parents? */ for ( tmp = to; tmp != &init_user_ns; tmp = tmp->parent ) { - if (cred->user == tmp->creator) { + if (uid_eq(cred->user->uid, tmp->owner)) { return (gid_t)0; } } diff --git a/security/commoncap.c b/security/commoncap.c index 435d074853f3..f2399d8afbe0 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -76,8 +76,9 @@ int cap_capable(const struct cred *cred, struct user_namespace *targ_ns, int cap, int audit) { for (;;) { - /* The creator of the user namespace has all caps. */ - if (targ_ns != &init_user_ns && targ_ns->creator == cred->user) + /* The owner of the user namespace has all caps. */ + if (targ_ns != &init_user_ns && uid_eq(targ_ns->owner, + make_kuid(cred->user_ns, cred->euid))) return 0; /* Do we have the necessary capabilities? */ -- cgit v1.2.3-59-g8ed1b From ae2975bc3476243b45a1e2344236d7920c268f38 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 14 Nov 2011 15:56:38 -0800 Subject: userns: Convert group_info values from gid_t to kgid_t. As a first step to converting struct cred to be all kuid_t and kgid_t values convert the group values stored in group_info to always be kgid_t values. Unless user namespaces are used this change should have no effect. Acked-by: Serge Hallyn Signed-off-by: Eric W. Biederman --- arch/s390/kernel/compat_linux.c | 14 ++++++++++-- fs/nfsd/auth.c | 5 ++-- fs/proc/array.c | 5 +++- include/linux/cred.h | 9 ++++---- kernel/groups.c | 48 ++++++++++++++++++++------------------- kernel/uid16.c | 14 ++++++++++-- net/ipv4/ping.c | 11 ++++++--- net/sunrpc/auth_generic.c | 4 ++-- net/sunrpc/auth_gss/svcauth_gss.c | 7 +++++- net/sunrpc/auth_unix.c | 15 ++++++++---- net/sunrpc/svcauth_unix.c | 18 +++++++++++---- security/keys/permission.c | 3 ++- 12 files changed, 104 insertions(+), 49 deletions(-) (limited to 'security') diff --git a/arch/s390/kernel/compat_linux.c b/arch/s390/kernel/compat_linux.c index ab64bdbab2ae..f0273ed760ef 100644 --- a/arch/s390/kernel/compat_linux.c +++ b/arch/s390/kernel/compat_linux.c @@ -173,11 +173,14 @@ asmlinkage long sys32_setfsgid16(u16 gid) static int groups16_to_user(u16 __user *grouplist, struct group_info *group_info) { + struct user_namespace *user_ns = current_user_ns(); int i; u16 group; + kgid_t kgid; for (i = 0; i < group_info->ngroups; i++) { - group = (u16)GROUP_AT(group_info, i); + kgid = GROUP_AT(group_info, i); + group = (u16)from_kgid_munged(user_ns, kgid); if (put_user(group, grouplist+i)) return -EFAULT; } @@ -187,13 +190,20 @@ static int groups16_to_user(u16 __user *grouplist, struct group_info *group_info static int groups16_from_user(struct group_info *group_info, u16 __user *grouplist) { + struct user_namespace *user_ns = current_user_ns(); int i; u16 group; + kgid_t kgid; for (i = 0; i < group_info->ngroups; i++) { if (get_user(group, grouplist+i)) return -EFAULT; - GROUP_AT(group_info, i) = (gid_t)group; + + kgid = make_kgid(user_ns, (gid_t)group); + if (!gid_valid(kgid)) + return -EINVAL; + + GROUP_AT(group_info, i) = kgid; } return 0; diff --git a/fs/nfsd/auth.c b/fs/nfsd/auth.c index 79717a40daba..204438cc914e 100644 --- a/fs/nfsd/auth.c +++ b/fs/nfsd/auth.c @@ -1,6 +1,7 @@ /* Copyright (C) 1995, 1996 Olaf Kirch */ #include +#include #include "nfsd.h" #include "auth.h" @@ -56,8 +57,8 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp) goto oom; for (i = 0; i < rqgi->ngroups; i++) { - if (!GROUP_AT(rqgi, i)) - GROUP_AT(gi, i) = exp->ex_anon_gid; + if (gid_eq(GLOBAL_ROOT_GID, GROUP_AT(rqgi, i))) + GROUP_AT(gi, i) = make_kgid(&init_user_ns, exp->ex_anon_gid); else GROUP_AT(gi, i) = GROUP_AT(rqgi, i); } diff --git a/fs/proc/array.c b/fs/proc/array.c index f9bd395b3473..36a0a9192ece 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -81,6 +81,7 @@ #include #include #include +#include #include #include @@ -161,6 +162,7 @@ static inline const char *get_task_state(struct task_struct *tsk) static inline void task_state(struct seq_file *m, struct pid_namespace *ns, struct pid *pid, struct task_struct *p) { + struct user_namespace *user_ns = current_user_ns(); struct group_info *group_info; int g; struct fdtable *fdt = NULL; @@ -205,7 +207,8 @@ static inline void task_state(struct seq_file *m, struct pid_namespace *ns, task_unlock(p); for (g = 0; g < min(group_info->ngroups, NGROUPS_SMALL); g++) - seq_printf(m, "%d ", GROUP_AT(group_info, g)); + seq_printf(m, "%d ", + from_kgid_munged(user_ns, GROUP_AT(group_info, g))); put_cred(cred); seq_putc(m, '\n'); diff --git a/include/linux/cred.h b/include/linux/cred.h index 2c60ec802678..0ab3cda4a774 100644 --- a/include/linux/cred.h +++ b/include/linux/cred.h @@ -17,6 +17,7 @@ #include #include #include +#include struct user_struct; struct cred; @@ -26,14 +27,14 @@ struct inode; * COW Supplementary groups list */ #define NGROUPS_SMALL 32 -#define NGROUPS_PER_BLOCK ((unsigned int)(PAGE_SIZE / sizeof(gid_t))) +#define NGROUPS_PER_BLOCK ((unsigned int)(PAGE_SIZE / sizeof(kgid_t))) struct group_info { atomic_t usage; int ngroups; int nblocks; - gid_t small_block[NGROUPS_SMALL]; - gid_t *blocks[0]; + kgid_t small_block[NGROUPS_SMALL]; + kgid_t *blocks[0]; }; /** @@ -66,7 +67,7 @@ extern struct group_info init_groups; extern void groups_free(struct group_info *); extern int set_current_groups(struct group_info *); extern int set_groups(struct cred *, struct group_info *); -extern int groups_search(const struct group_info *, gid_t); +extern int groups_search(const struct group_info *, kgid_t); /* access the groups "array" with this macro */ #define GROUP_AT(gi, i) \ diff --git a/kernel/groups.c b/kernel/groups.c index 99b53d1eb7ea..84156f2d4c8c 100644 --- a/kernel/groups.c +++ b/kernel/groups.c @@ -31,7 +31,7 @@ struct group_info *groups_alloc(int gidsetsize) group_info->blocks[0] = group_info->small_block; else { for (i = 0; i < nblocks; i++) { - gid_t *b; + kgid_t *b; b = (void *)__get_free_page(GFP_USER); if (!b) goto out_undo_partial_alloc; @@ -66,18 +66,15 @@ EXPORT_SYMBOL(groups_free); static int groups_to_user(gid_t __user *grouplist, const struct group_info *group_info) { + struct user_namespace *user_ns = current_user_ns(); int i; unsigned int count = group_info->ngroups; - for (i = 0; i < group_info->nblocks; i++) { - unsigned int cp_count = min(NGROUPS_PER_BLOCK, count); - unsigned int len = cp_count * sizeof(*grouplist); - - if (copy_to_user(grouplist, group_info->blocks[i], len)) + for (i = 0; i < count; i++) { + gid_t gid; + gid = from_kgid_munged(user_ns, GROUP_AT(group_info, i)); + if (put_user(gid, grouplist+i)) return -EFAULT; - - grouplist += NGROUPS_PER_BLOCK; - count -= cp_count; } return 0; } @@ -86,18 +83,21 @@ static int groups_to_user(gid_t __user *grouplist, static int groups_from_user(struct group_info *group_info, gid_t __user *grouplist) { + struct user_namespace *user_ns = current_user_ns(); int i; unsigned int count = group_info->ngroups; - for (i = 0; i < group_info->nblocks; i++) { - unsigned int cp_count = min(NGROUPS_PER_BLOCK, count); - unsigned int len = cp_count * sizeof(*grouplist); - - if (copy_from_user(group_info->blocks[i], grouplist, len)) + for (i = 0; i < count; i++) { + gid_t gid; + kgid_t kgid; + if (get_user(gid, grouplist+i)) return -EFAULT; - grouplist += NGROUPS_PER_BLOCK; - count -= cp_count; + kgid = make_kgid(user_ns, gid); + if (!gid_valid(kgid)) + return -EINVAL; + + GROUP_AT(group_info, i) = kgid; } return 0; } @@ -117,9 +117,9 @@ static void groups_sort(struct group_info *group_info) for (base = 0; base < max; base++) { int left = base; int right = left + stride; - gid_t tmp = GROUP_AT(group_info, right); + kgid_t tmp = GROUP_AT(group_info, right); - while (left >= 0 && GROUP_AT(group_info, left) > tmp) { + while (left >= 0 && gid_gt(GROUP_AT(group_info, left), tmp)) { GROUP_AT(group_info, right) = GROUP_AT(group_info, left); right = left; @@ -132,7 +132,7 @@ static void groups_sort(struct group_info *group_info) } /* a simple bsearch */ -int groups_search(const struct group_info *group_info, gid_t grp) +int groups_search(const struct group_info *group_info, kgid_t grp) { unsigned int left, right; @@ -143,9 +143,9 @@ int groups_search(const struct group_info *group_info, gid_t grp) right = group_info->ngroups; while (left < right) { unsigned int mid = (left+right)/2; - if (grp > GROUP_AT(group_info, mid)) + if (gid_gt(grp, GROUP_AT(group_info, mid))) left = mid + 1; - else if (grp < GROUP_AT(group_info, mid)) + else if (gid_lt(grp, GROUP_AT(group_info, mid))) right = mid; else return 1; @@ -262,7 +262,8 @@ int in_group_p(gid_t grp) int retval = 1; if (grp != cred->fsgid) - retval = groups_search(cred->group_info, grp); + retval = groups_search(cred->group_info, + make_kgid(cred->user_ns, grp)); return retval; } @@ -274,7 +275,8 @@ int in_egroup_p(gid_t grp) int retval = 1; if (grp != cred->egid) - retval = groups_search(cred->group_info, grp); + retval = groups_search(cred->group_info, + make_kgid(cred->user_ns, grp)); return retval; } diff --git a/kernel/uid16.c b/kernel/uid16.c index 51c6e89e8619..e530bc34c4cf 100644 --- a/kernel/uid16.c +++ b/kernel/uid16.c @@ -134,11 +134,14 @@ SYSCALL_DEFINE1(setfsgid16, old_gid_t, gid) static int groups16_to_user(old_gid_t __user *grouplist, struct group_info *group_info) { + struct user_namespace *user_ns = current_user_ns(); int i; old_gid_t group; + kgid_t kgid; for (i = 0; i < group_info->ngroups; i++) { - group = high2lowgid(GROUP_AT(group_info, i)); + kgid = GROUP_AT(group_info, i); + group = high2lowgid(from_kgid_munged(user_ns, kgid)); if (put_user(group, grouplist+i)) return -EFAULT; } @@ -149,13 +152,20 @@ static int groups16_to_user(old_gid_t __user *grouplist, static int groups16_from_user(struct group_info *group_info, old_gid_t __user *grouplist) { + struct user_namespace *user_ns = current_user_ns(); int i; old_gid_t group; + kgid_t kgid; for (i = 0; i < group_info->ngroups; i++) { if (get_user(group, grouplist+i)) return -EFAULT; - GROUP_AT(group_info, i) = low2highgid(group); + + kgid = make_kgid(user_ns, low2highgid(group)); + if (!gid_valid(kgid)) + return -EINVAL; + + GROUP_AT(group_info, i) = kgid; } return 0; diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index 50009c787bcd..9d3044ff45b9 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c @@ -205,17 +205,22 @@ static int ping_init_sock(struct sock *sk) gid_t range[2]; struct group_info *group_info = get_current_groups(); int i, j, count = group_info->ngroups; + kgid_t low, high; inet_get_ping_group_range_net(net, range, range+1); + low = make_kgid(&init_user_ns, range[0]); + high = make_kgid(&init_user_ns, range[1]); + if (!gid_valid(low) || !gid_valid(high) || gid_lt(high, low)) + return -EACCES; + if (range[0] <= group && group <= range[1]) return 0; for (i = 0; i < group_info->nblocks; i++) { int cp_count = min_t(int, NGROUPS_PER_BLOCK, count); - for (j = 0; j < cp_count; j++) { - group = group_info->blocks[i][j]; - if (range[0] <= group && group <= range[1]) + kgid_t gid = group_info->blocks[i][j]; + if (gid_lte(low, gid) && gid_lte(gid, high)) return 0; } diff --git a/net/sunrpc/auth_generic.c b/net/sunrpc/auth_generic.c index 75762f346975..6ed6f201b022 100644 --- a/net/sunrpc/auth_generic.c +++ b/net/sunrpc/auth_generic.c @@ -160,8 +160,8 @@ generic_match(struct auth_cred *acred, struct rpc_cred *cred, int flags) if (gcred->acred.group_info->ngroups != acred->group_info->ngroups) goto out_nomatch; for (i = 0; i < gcred->acred.group_info->ngroups; i++) { - if (GROUP_AT(gcred->acred.group_info, i) != - GROUP_AT(acred->group_info, i)) + if (!gid_eq(GROUP_AT(gcred->acred.group_info, i), + GROUP_AT(acred->group_info, i))) goto out_nomatch; } out_match: diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index 1600cfb1618c..28b62dbb6d1e 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -470,9 +471,13 @@ static int rsc_parse(struct cache_detail *cd, status = -EINVAL; for (i=0; i #include #include +#include #define NFS_NGROUPS 16 @@ -78,8 +79,11 @@ unx_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags) groups = NFS_NGROUPS; cred->uc_gid = acred->gid; - for (i = 0; i < groups; i++) - cred->uc_gids[i] = GROUP_AT(acred->group_info, i); + for (i = 0; i < groups; i++) { + gid_t gid; + gid = from_kgid(&init_user_ns, GROUP_AT(acred->group_info, i)); + cred->uc_gids[i] = gid; + } if (i < NFS_NGROUPS) cred->uc_gids[i] = NOGROUP; @@ -126,9 +130,12 @@ unx_match(struct auth_cred *acred, struct rpc_cred *rcred, int flags) groups = acred->group_info->ngroups; if (groups > NFS_NGROUPS) groups = NFS_NGROUPS; - for (i = 0; i < groups ; i++) - if (cred->uc_gids[i] != GROUP_AT(acred->group_info, i)) + for (i = 0; i < groups ; i++) { + gid_t gid; + gid = from_kgid(&init_user_ns, GROUP_AT(acred->group_info, i)); + if (cred->uc_gids[i] != gid) return 0; + } if (groups < NFS_NGROUPS && cred->uc_gids[groups] != NOGROUP) return 0; diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c index 521d8f7dc833..71ec8530ec8c 100644 --- a/net/sunrpc/svcauth_unix.c +++ b/net/sunrpc/svcauth_unix.c @@ -14,6 +14,7 @@ #include #include #include +#include #define RPCDBG_FACILITY RPCDBG_AUTH #include @@ -530,11 +531,15 @@ static int unix_gid_parse(struct cache_detail *cd, for (i = 0 ; i < gids ; i++) { int gid; + kgid_t kgid; rv = get_int(&mesg, &gid); err = -EINVAL; if (rv) goto out; - GROUP_AT(ug.gi, i) = gid; + kgid = make_kgid(&init_user_ns, gid); + if (!gid_valid(kgid)) + goto out; + GROUP_AT(ug.gi, i) = kgid; } ugp = unix_gid_lookup(cd, uid); @@ -563,6 +568,7 @@ static int unix_gid_show(struct seq_file *m, struct cache_detail *cd, struct cache_head *h) { + struct user_namespace *user_ns = current_user_ns(); struct unix_gid *ug; int i; int glen; @@ -580,7 +586,7 @@ static int unix_gid_show(struct seq_file *m, seq_printf(m, "%u %d:", ug->uid, glen); for (i = 0; i < glen; i++) - seq_printf(m, " %d", GROUP_AT(ug->gi, i)); + seq_printf(m, " %d", from_kgid_munged(user_ns, GROUP_AT(ug->gi, i))); seq_printf(m, "\n"); return 0; } @@ -831,8 +837,12 @@ svcauth_unix_accept(struct svc_rqst *rqstp, __be32 *authp) cred->cr_group_info = groups_alloc(slen); if (cred->cr_group_info == NULL) return SVC_CLOSE; - for (i = 0; i < slen; i++) - GROUP_AT(cred->cr_group_info, i) = svc_getnl(argv); + for (i = 0; i < slen; i++) { + kgid_t kgid = make_kgid(&init_user_ns, svc_getnl(argv)); + if (!gid_valid(kgid)) + goto badcred; + GROUP_AT(cred->cr_group_info, i) = kgid; + } if (svc_getu32(argv) != htonl(RPC_AUTH_NULL) || svc_getu32(argv) != 0) { *authp = rpc_autherr_badverf; return SVC_DENIED; diff --git a/security/keys/permission.c b/security/keys/permission.c index e146cbd714bd..5442900d2929 100644 --- a/security/keys/permission.c +++ b/security/keys/permission.c @@ -53,7 +53,8 @@ int key_task_permission(const key_ref_t key_ref, const struct cred *cred, goto use_these_perms; } - ret = groups_search(cred->group_info, key->gid); + ret = groups_search(cred->group_info, + make_kgid(current_user_ns(), key->gid)); if (ret) { kperm = key->perm >> 8; goto use_these_perms; -- cgit v1.2.3-59-g8ed1b From 078de5f706ece36afd73bb4b8283314132d2dfdf Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 8 Feb 2012 07:00:08 -0800 Subject: userns: Store uid and gid values in struct cred with kuid_t and kgid_t types cred.h and a few trivial users of struct cred are changed. The rest of the users of struct cred are left for other patches as there are too many changes to make in one go and leave the change reviewable. If the user namespace is disabled and CONFIG_UIDGID_STRICT_TYPE_CHECKS are disabled the code will contiue to compile and behave correctly. Acked-by: Serge Hallyn Signed-off-by: Eric W. Biederman --- arch/x86/mm/fault.c | 2 +- fs/ioprio.c | 8 ++------ include/linux/cred.h | 16 ++++++++-------- include/linux/user_namespace.h | 8 ++++---- kernel/cred.c | 36 ++++++++++++++++++++++-------------- kernel/signal.c | 14 ++++++++------ kernel/sys.c | 26 +++++++++----------------- kernel/user_namespace.c | 4 ++-- mm/oom_kill.c | 4 ++-- security/commoncap.c | 3 +-- 10 files changed, 59 insertions(+), 62 deletions(-) (limited to 'security') diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index 3ecfd1aaf214..76dcd9d8e0bc 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c @@ -582,7 +582,7 @@ show_fault_oops(struct pt_regs *regs, unsigned long error_code, pte_t *pte = lookup_address(address, &level); if (pte && pte_present(*pte) && !pte_exec(*pte)) - printk(nx_warning, current_uid()); + printk(nx_warning, from_kuid(&init_user_ns, current_uid())); } printk(KERN_ALERT "BUG: unable to handle kernel "); diff --git a/fs/ioprio.c b/fs/ioprio.c index 8e35e964d9ed..2072e41785d2 100644 --- a/fs/ioprio.c +++ b/fs/ioprio.c @@ -123,9 +123,7 @@ SYSCALL_DEFINE3(ioprio_set, int, which, int, who, int, ioprio) break; do_each_thread(g, p) { - const struct cred *tcred = __task_cred(p); - kuid_t tcred_uid = make_kuid(tcred->user_ns, tcred->uid); - if (!uid_eq(tcred_uid, uid)) + if (!uid_eq(task_uid(p), uid)) continue; ret = set_task_ioprio(p, ioprio); if (ret) @@ -220,9 +218,7 @@ SYSCALL_DEFINE2(ioprio_get, int, which, int, who) break; do_each_thread(g, p) { - const struct cred *tcred = __task_cred(p); - kuid_t tcred_uid = make_kuid(tcred->user_ns, tcred->uid); - if (!uid_eq(tcred_uid, user->uid)) + if (!uid_eq(task_uid(p), user->uid)) continue; tmpio = get_task_ioprio(p); if (tmpio < 0) diff --git a/include/linux/cred.h b/include/linux/cred.h index 0ab3cda4a774..fac0579258fc 100644 --- a/include/linux/cred.h +++ b/include/linux/cred.h @@ -123,14 +123,14 @@ struct cred { #define CRED_MAGIC 0x43736564 #define CRED_MAGIC_DEAD 0x44656144 #endif - uid_t uid; /* real UID of the task */ - gid_t gid; /* real GID of the task */ - uid_t suid; /* saved UID of the task */ - gid_t sgid; /* saved GID of the task */ - uid_t euid; /* effective UID of the task */ - gid_t egid; /* effective GID of the task */ - uid_t fsuid; /* UID for VFS ops */ - gid_t fsgid; /* GID for VFS ops */ + kuid_t uid; /* real UID of the task */ + kgid_t gid; /* real GID of the task */ + kuid_t suid; /* saved UID of the task */ + kgid_t sgid; /* saved GID of the task */ + kuid_t euid; /* effective UID of the task */ + kgid_t egid; /* effective GID of the task */ + kuid_t fsuid; /* UID for VFS ops */ + kgid_t fsgid; /* GID for VFS ops */ unsigned securebits; /* SUID-less security management */ kernel_cap_t cap_inheritable; /* caps our children can inherit */ kernel_cap_t cap_permitted; /* caps we're permitted */ diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h index 4c9846d90741..a2c61457cba1 100644 --- a/include/linux/user_namespace.h +++ b/include/linux/user_namespace.h @@ -70,15 +70,15 @@ static inline void put_user_ns(struct user_namespace *ns) #endif static inline uid_t user_ns_map_uid(struct user_namespace *to, - const struct cred *cred, uid_t uid) + const struct cred *cred, kuid_t uid) { - return from_kuid_munged(to, make_kuid(cred->user_ns, uid)); + return from_kuid_munged(to, uid); } static inline gid_t user_ns_map_gid(struct user_namespace *to, - const struct cred *cred, gid_t gid) + const struct cred *cred, kgid_t gid) { - return from_kgid_munged(to, make_kgid(cred->user_ns, gid)); + return from_kgid_munged(to, gid); } #endif /* _LINUX_USER_H */ diff --git a/kernel/cred.c b/kernel/cred.c index 7a0d80669886..eddc5e2e9587 100644 --- a/kernel/cred.c +++ b/kernel/cred.c @@ -49,6 +49,14 @@ struct cred init_cred = { .subscribers = ATOMIC_INIT(2), .magic = CRED_MAGIC, #endif + .uid = GLOBAL_ROOT_UID, + .gid = GLOBAL_ROOT_GID, + .suid = GLOBAL_ROOT_UID, + .sgid = GLOBAL_ROOT_GID, + .euid = GLOBAL_ROOT_UID, + .egid = GLOBAL_ROOT_GID, + .fsuid = GLOBAL_ROOT_UID, + .fsgid = GLOBAL_ROOT_GID, .securebits = SECUREBITS_DEFAULT, .cap_inheritable = CAP_EMPTY_SET, .cap_permitted = CAP_FULL_SET, @@ -488,10 +496,10 @@ int commit_creds(struct cred *new) get_cred(new); /* we will require a ref for the subj creds too */ /* dumpability changes */ - if (old->euid != new->euid || - old->egid != new->egid || - old->fsuid != new->fsuid || - old->fsgid != new->fsgid || + if (!uid_eq(old->euid, new->euid) || + !gid_eq(old->egid, new->egid) || + !uid_eq(old->fsuid, new->fsuid) || + !gid_eq(old->fsgid, new->fsgid) || !cap_issubset(new->cap_permitted, old->cap_permitted)) { if (task->mm) set_dumpable(task->mm, suid_dumpable); @@ -500,9 +508,9 @@ int commit_creds(struct cred *new) } /* alter the thread keyring */ - if (new->fsuid != old->fsuid) + if (!uid_eq(new->fsuid, old->fsuid)) key_fsuid_changed(task); - if (new->fsgid != old->fsgid) + if (!gid_eq(new->fsgid, old->fsgid)) key_fsgid_changed(task); /* do it @@ -519,16 +527,16 @@ int commit_creds(struct cred *new) alter_cred_subscribers(old, -2); /* send notifications */ - if (new->uid != old->uid || - new->euid != old->euid || - new->suid != old->suid || - new->fsuid != old->fsuid) + if (!uid_eq(new->uid, old->uid) || + !uid_eq(new->euid, old->euid) || + !uid_eq(new->suid, old->suid) || + !uid_eq(new->fsuid, old->fsuid)) proc_id_connector(task, PROC_EVENT_UID); - if (new->gid != old->gid || - new->egid != old->egid || - new->sgid != old->sgid || - new->fsgid != old->fsgid) + if (!gid_eq(new->gid, old->gid) || + !gid_eq(new->egid, old->egid) || + !gid_eq(new->sgid, old->sgid) || + !gid_eq(new->fsgid, old->fsgid)) proc_id_connector(task, PROC_EVENT_GID); /* release the old obj and subj refs both */ diff --git a/kernel/signal.c b/kernel/signal.c index e2c5d84f2dac..2734dc965f69 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -1038,8 +1038,10 @@ static inline void userns_fixup_signal_uid(struct siginfo *info, struct task_str if (SI_FROMKERNEL(info)) return; - info->si_uid = user_ns_map_uid(task_cred_xxx(t, user_ns), - current_cred(), info->si_uid); + rcu_read_lock(); + info->si_uid = from_kuid_munged(task_cred_xxx(t, user_ns), + make_kuid(current_user_ns(), info->si_uid)); + rcu_read_unlock(); } #else static inline void userns_fixup_signal_uid(struct siginfo *info, struct task_struct *t) @@ -1106,7 +1108,7 @@ static int __send_signal(int sig, struct siginfo *info, struct task_struct *t, q->info.si_code = SI_USER; q->info.si_pid = task_tgid_nr_ns(current, task_active_pid_ns(t)); - q->info.si_uid = current_uid(); + q->info.si_uid = from_kuid_munged(current_user_ns(), current_uid()); break; case (unsigned long) SEND_SIG_PRIV: q->info.si_signo = sig; @@ -1973,7 +1975,7 @@ static void ptrace_do_notify(int signr, int exit_code, int why) info.si_signo = signr; info.si_code = exit_code; info.si_pid = task_pid_vnr(current); - info.si_uid = current_uid(); + info.si_uid = from_kuid_munged(current_user_ns(), current_uid()); /* Let the debugger run. */ ptrace_stop(exit_code, why, 1, &info); @@ -2828,7 +2830,7 @@ SYSCALL_DEFINE2(kill, pid_t, pid, int, sig) info.si_errno = 0; info.si_code = SI_USER; info.si_pid = task_tgid_vnr(current); - info.si_uid = current_uid(); + info.si_uid = from_kuid_munged(current_user_ns(), current_uid()); return kill_something_info(sig, &info, pid); } @@ -2871,7 +2873,7 @@ static int do_tkill(pid_t tgid, pid_t pid, int sig) info.si_errno = 0; info.si_code = SI_TKILL; info.si_pid = task_tgid_vnr(current); - info.si_uid = current_uid(); + info.si_uid = from_kuid_munged(current_user_ns(), current_uid()); return do_send_specific(tgid, pid, sig, &info); } diff --git a/kernel/sys.c b/kernel/sys.c index f0c43b4b6657..39962818c008 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -175,7 +175,6 @@ SYSCALL_DEFINE3(setpriority, int, which, int, who, int, niceval) const struct cred *cred = current_cred(); int error = -EINVAL; struct pid *pgrp; - kuid_t cred_uid; kuid_t uid; if (which > PRIO_USER || which < PRIO_PROCESS) @@ -209,22 +208,19 @@ SYSCALL_DEFINE3(setpriority, int, which, int, who, int, niceval) } while_each_pid_thread(pgrp, PIDTYPE_PGID, p); break; case PRIO_USER: - cred_uid = make_kuid(cred->user_ns, cred->uid); uid = make_kuid(cred->user_ns, who); user = cred->user; if (!who) - uid = cred_uid; - else if (!uid_eq(uid, cred_uid) && + uid = cred->uid; + else if (!uid_eq(uid, cred->uid) && !(user = find_user(uid))) goto out_unlock; /* No processes for this user */ do_each_thread(g, p) { - const struct cred *tcred = __task_cred(p); - kuid_t tcred_uid = make_kuid(tcred->user_ns, tcred->uid); - if (uid_eq(tcred_uid, uid)) + if (uid_eq(task_uid(p), uid)) error = set_one_prio(p, niceval, error); } while_each_thread(g, p); - if (!uid_eq(uid, cred_uid)) + if (!uid_eq(uid, cred->uid)) free_uid(user); /* For find_user() */ break; } @@ -248,7 +244,6 @@ SYSCALL_DEFINE2(getpriority, int, which, int, who) const struct cred *cred = current_cred(); long niceval, retval = -ESRCH; struct pid *pgrp; - kuid_t cred_uid; kuid_t uid; if (which > PRIO_USER || which < PRIO_PROCESS) @@ -280,25 +275,22 @@ SYSCALL_DEFINE2(getpriority, int, which, int, who) } while_each_pid_thread(pgrp, PIDTYPE_PGID, p); break; case PRIO_USER: - cred_uid = make_kuid(cred->user_ns, cred->uid); uid = make_kuid(cred->user_ns, who); user = cred->user; if (!who) - uid = cred_uid; - else if (!uid_eq(uid, cred_uid) && + uid = cred->uid; + else if (!uid_eq(uid, cred->uid) && !(user = find_user(uid))) goto out_unlock; /* No processes for this user */ do_each_thread(g, p) { - const struct cred *tcred = __task_cred(p); - kuid_t tcred_uid = make_kuid(tcred->user_ns, tcred->uid); - if (uid_eq(tcred_uid, uid)) { + if (uid_eq(task_uid(p), uid)) { niceval = 20 - task_nice(p); if (niceval > retval) retval = niceval; } } while_each_thread(g, p); - if (!uid_eq(uid, cred_uid)) + if (!uid_eq(uid, cred->uid)) free_uid(user); /* for find_user() */ break; } @@ -641,7 +633,7 @@ static int set_user(struct cred *new) { struct user_struct *new_user; - new_user = alloc_uid(make_kuid(new->user_ns, new->uid)); + new_user = alloc_uid(new->uid); if (!new_user) return -EAGAIN; diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c index 7eff867bfac5..86602316422d 100644 --- a/kernel/user_namespace.c +++ b/kernel/user_namespace.c @@ -36,8 +36,8 @@ static bool new_idmap_permitted(struct user_namespace *ns, int cap_setid, int create_user_ns(struct cred *new) { struct user_namespace *ns, *parent_ns = new->user_ns; - kuid_t owner = make_kuid(new->user_ns, new->euid); - kgid_t group = make_kgid(new->user_ns, new->egid); + kuid_t owner = new->euid; + kgid_t group = new->egid; /* The creator needs a mapping in the parent user namespace * or else we won't be able to reasonably tell userspace who diff --git a/mm/oom_kill.c b/mm/oom_kill.c index 46bf2ed5594c..9f09a1fde9f9 100644 --- a/mm/oom_kill.c +++ b/mm/oom_kill.c @@ -410,8 +410,8 @@ static void dump_tasks(const struct mem_cgroup *memcg, const nodemask_t *nodemas } pr_info("[%5d] %5d %5d %8lu %8lu %3u %3d %5d %s\n", - task->pid, task_uid(task), task->tgid, - task->mm->total_vm, get_mm_rss(task->mm), + task->pid, from_kuid(&init_user_ns, task_uid(task)), + task->tgid, task->mm->total_vm, get_mm_rss(task->mm), task_cpu(task), task->signal->oom_adj, task->signal->oom_score_adj, task->comm); task_unlock(task); diff --git a/security/commoncap.c b/security/commoncap.c index f2399d8afbe0..dbd465a59286 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -77,8 +77,7 @@ int cap_capable(const struct cred *cred, struct user_namespace *targ_ns, { for (;;) { /* The owner of the user namespace has all caps. */ - if (targ_ns != &init_user_ns && uid_eq(targ_ns->owner, - make_kuid(cred->user_ns, cred->euid))) + if (targ_ns != &init_user_ns && uid_eq(targ_ns->owner, cred->euid)) return 0; /* Do we have the necessary capabilities? */ -- cgit v1.2.3-59-g8ed1b From 18815a18085364d8514c0d0c4c986776cb74272c Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 7 Feb 2012 16:45:47 -0800 Subject: userns: Convert capabilities related permsion checks - Use uid_eq when comparing kuids Use gid_eq when comparing kgids - Use make_kuid(user_ns, 0) to talk about the user_namespace root uid Acked-by: Serge Hallyn Signed-off-by: Eric W. Biederman --- fs/open.c | 3 ++- security/commoncap.c | 41 ++++++++++++++++++++++++++--------------- 2 files changed, 28 insertions(+), 16 deletions(-) (limited to 'security') diff --git a/fs/open.c b/fs/open.c index 5720854156db..92335f663545 100644 --- a/fs/open.c +++ b/fs/open.c @@ -316,7 +316,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode) if (!issecure(SECURE_NO_SETUID_FIXUP)) { /* Clear the capabilities if we switch to a non-root user */ - if (override_cred->uid) + kuid_t root_uid = make_kuid(override_cred->user_ns, 0); + if (!uid_eq(override_cred->uid, root_uid)) cap_clear(override_cred->cap_effective); else override_cred->cap_effective = diff --git a/security/commoncap.c b/security/commoncap.c index dbd465a59286..ff9b113bb07c 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -472,19 +472,22 @@ int cap_bprm_set_creds(struct linux_binprm *bprm) struct cred *new = bprm->cred; bool effective, has_cap = false; int ret; + kuid_t root_uid; effective = false; ret = get_file_caps(bprm, &effective, &has_cap); if (ret < 0) return ret; + root_uid = make_kuid(new->user_ns, 0); + if (!issecure(SECURE_NOROOT)) { /* * If the legacy file capability is set, then don't set privs * for a setuid root binary run by a non-root user. Do set it * for a root user just to cause least surprise to an admin. */ - if (has_cap && new->uid != 0 && new->euid == 0) { + if (has_cap && !uid_eq(new->uid, root_uid) && uid_eq(new->euid, root_uid)) { warn_setuid_and_fcaps_mixed(bprm->filename); goto skip; } @@ -495,12 +498,12 @@ int cap_bprm_set_creds(struct linux_binprm *bprm) * * If only the real uid is 0, we do not set the effective bit. */ - if (new->euid == 0 || new->uid == 0) { + if (uid_eq(new->euid, root_uid) || uid_eq(new->uid, root_uid)) { /* pP' = (cap_bset & ~0) | (pI & ~0) */ new->cap_permitted = cap_combine(old->cap_bset, old->cap_inheritable); } - if (new->euid == 0) + if (uid_eq(new->euid, root_uid)) effective = true; } skip: @@ -508,8 +511,8 @@ skip: /* Don't let someone trace a set[ug]id/setpcap binary with the revised * credentials unless they have the appropriate permit */ - if ((new->euid != old->uid || - new->egid != old->gid || + if ((!uid_eq(new->euid, old->uid) || + !gid_eq(new->egid, old->gid) || !cap_issubset(new->cap_permitted, old->cap_permitted)) && bprm->unsafe & ~LSM_UNSAFE_PTRACE_CAP) { /* downgrade; they get no more than they had, and maybe less */ @@ -544,7 +547,7 @@ skip: */ if (!cap_isclear(new->cap_effective)) { if (!cap_issubset(CAP_FULL_SET, new->cap_effective) || - new->euid != 0 || new->uid != 0 || + !uid_eq(new->euid, root_uid) || !uid_eq(new->uid, root_uid) || issecure(SECURE_NOROOT)) { ret = audit_log_bprm_fcaps(bprm, new, old); if (ret < 0) @@ -569,16 +572,17 @@ skip: int cap_bprm_secureexec(struct linux_binprm *bprm) { const struct cred *cred = current_cred(); + kuid_t root_uid = make_kuid(cred->user_ns, 0); - if (cred->uid != 0) { + if (!uid_eq(cred->uid, root_uid)) { if (bprm->cap_effective) return 1; if (!cap_isclear(cred->cap_permitted)) return 1; } - return (cred->euid != cred->uid || - cred->egid != cred->gid); + return (!uid_eq(cred->euid, cred->uid) || + !gid_eq(cred->egid, cred->gid)); } /** @@ -668,15 +672,21 @@ int cap_inode_removexattr(struct dentry *dentry, const char *name) */ static inline void cap_emulate_setxuid(struct cred *new, const struct cred *old) { - if ((old->uid == 0 || old->euid == 0 || old->suid == 0) && - (new->uid != 0 && new->euid != 0 && new->suid != 0) && + kuid_t root_uid = make_kuid(old->user_ns, 0); + + if ((uid_eq(old->uid, root_uid) || + uid_eq(old->euid, root_uid) || + uid_eq(old->suid, root_uid)) && + (!uid_eq(new->uid, root_uid) && + !uid_eq(new->euid, root_uid) && + !uid_eq(new->suid, root_uid)) && !issecure(SECURE_KEEP_CAPS)) { cap_clear(new->cap_permitted); cap_clear(new->cap_effective); } - if (old->euid == 0 && new->euid != 0) + if (uid_eq(old->euid, root_uid) && !uid_eq(new->euid, root_uid)) cap_clear(new->cap_effective); - if (old->euid != 0 && new->euid == 0) + if (!uid_eq(old->euid, root_uid) && uid_eq(new->euid, root_uid)) new->cap_effective = new->cap_permitted; } @@ -709,11 +719,12 @@ int cap_task_fix_setuid(struct cred *new, const struct cred *old, int flags) * if not, we might be a bit too harsh here. */ if (!issecure(SECURE_NO_SETUID_FIXUP)) { - if (old->fsuid == 0 && new->fsuid != 0) + kuid_t root_uid = make_kuid(old->user_ns, 0); + if (uid_eq(old->fsuid, root_uid) && !uid_eq(new->fsuid, root_uid)) new->cap_effective = cap_drop_fs_set(new->cap_effective); - if (old->fsuid != 0 && new->fsuid == 0) + if (!uid_eq(old->fsuid, root_uid) && uid_eq(new->fsuid, root_uid)) new->cap_effective = cap_raise_fs_set(new->cap_effective, new->cap_permitted); -- cgit v1.2.3-59-g8ed1b