userns: Convert group_info values from gid_t to kgid_t.
authorEric W. Biederman <ebiederm@xmission.com>
Mon, 14 Nov 2011 23:56:38 +0000 (15:56 -0800)
committerEric W. Biederman <ebiederm@xmission.com>
Thu, 3 May 2012 10:27:21 +0000 (03:27 -0700)
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 <serge.hallyn@canonical.com>
Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
12 files changed:
arch/s390/kernel/compat_linux.c
fs/nfsd/auth.c
fs/proc/array.c
include/linux/cred.h
kernel/groups.c
kernel/uid16.c
net/ipv4/ping.c
net/sunrpc/auth_generic.c
net/sunrpc/auth_gss/svcauth_gss.c
net/sunrpc/auth_unix.c
net/sunrpc/svcauth_unix.c
security/keys/permission.c

index ab64bdbab2ae3cf7b7025ad1d4afe1838c6cd6ec..f0273ed760efe6d23b93ab6b98ff32030ce2dbc1 100644 (file)
@@ -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;
index 79717a40daba2e57cb8460f885dc09b8db63ccc8..204438cc914ea522b83907aaf618bb0dbcfa4068 100644 (file)
@@ -1,6 +1,7 @@
 /* Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> */
 
 #include <linux/sched.h>
+#include <linux/user_namespace.h>
 #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);
                }
index f9bd395b3473f6f1ca5f0697679d7d469e9fdb7c..36a0a9192ece07c4ca1e574aaa9322b3794c2fb7 100644 (file)
@@ -81,6 +81,7 @@
 #include <linux/pid_namespace.h>
 #include <linux/ptrace.h>
 #include <linux/tracehook.h>
+#include <linux/user_namespace.h>
 
 #include <asm/pgtable.h>
 #include <asm/processor.h>
@@ -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');
index 2c60ec802678b6437e169fc83546feb1fe1c298a..0ab3cda4a774c9053074e85cde6099cb14dc726d 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/key.h>
 #include <linux/selinux.h>
 #include <linux/atomic.h>
+#include <linux/uidgid.h>
 
 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) \
index 99b53d1eb7ea50856424329f52f6e6ab3cc83d61..84156f2d4c8c39b64a3d707b836340627307098d 100644 (file)
@@ -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;
 }
 
index 51c6e89e8619a56e28949d26f7f7053b5d32b5af..e530bc34c4cffc0d3f9ede9890c61716b1f22f31 100644 (file)
@@ -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;
index 50009c787bcdc3ae79291adf6e688dad2d612331..9d3044ff45b93d4a2bcfa916b1577f597863074c 100644 (file)
@@ -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;
                }
 
index 75762f346975ed6e2be6d639ed7a3e1d83ede077..6ed6f201b022aae4c2b01b97b21dd9bdeddbed2f 100644 (file)
@@ -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:
index 1600cfb1618cd2abb4ae14be87f96554100a862a..28b62dbb6d1e4be36358055a9c231143f0e45d9e 100644 (file)
@@ -41,6 +41,7 @@
 #include <linux/types.h>
 #include <linux/module.h>
 #include <linux/pagemap.h>
+#include <linux/user_namespace.h>
 
 #include <linux/sunrpc/auth_gss.h>
 #include <linux/sunrpc/gss_err.h>
@@ -470,9 +471,13 @@ static int rsc_parse(struct cache_detail *cd,
                status = -EINVAL;
                for (i=0; i<N; i++) {
                        gid_t gid;
+                       kgid_t kgid;
                        if (get_int(&mesg, &gid))
                                goto out;
-                       GROUP_AT(rsci.cred.cr_group_info, i) = gid;
+                       kgid = make_kgid(&init_user_ns, gid);
+                       if (!gid_valid(kgid))
+                               goto out;
+                       GROUP_AT(rsci.cred.cr_group_info, i) = kgid;
                }
 
                /* mech name */
index e50502d8ceb777a6c6795e22673753c4eea6e538..52c5abdee211cde762075809a09ca2d849ddeca3 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/module.h>
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/auth.h>
+#include <linux/user_namespace.h>
 
 #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;
index 521d8f7dc833ac769afe83daf0b03fe818c5be32..71ec8530ec8cb7ac10abae2fbf6c2571b14c8c98 100644 (file)
@@ -14,6 +14,7 @@
 #include <net/sock.h>
 #include <net/ipv6.h>
 #include <linux/kernel.h>
+#include <linux/user_namespace.h>
 #define RPCDBG_FACILITY        RPCDBG_AUTH
 
 #include <linux/sunrpc/clnt.h>
@@ -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;
index e146cbd714bd57e7c1a174301472dacf76a0b1bb..5442900d2929c3da7cf70f8a7acd40fa115df969 100644 (file)
@@ -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;