lkml.org 
[lkml]   [2012]   [Apr]   [8]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Date
    Subject[PATCH 18/43] userns: Convert group_info values from gid_t to kgid_t.
    From: Eric W. Biederman <ebiederm@xmission.com>

    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.

    Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
    ---
    arch/s390/kernel/compat_linux.c | 13 ++++++++-
    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, 103 insertions(+), 49 deletions(-)

    diff --git a/arch/s390/kernel/compat_linux.c b/arch/s390/kernel/compat_linux.c
    index ab64bdb..5baac18 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,19 @@ 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;

    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 79717a4..204438c 100644
    --- a/fs/nfsd/auth.c
    +++ b/fs/nfsd/auth.c
    @@ -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);
    }
    diff --git a/fs/proc/array.c b/fs/proc/array.c
    index f9bd395..36a0a91 100644
    --- a/fs/proc/array.c
    +++ b/fs/proc/array.c
    @@ -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');
    diff --git a/include/linux/cred.h b/include/linux/cred.h
    index 2c60ec8..0ab3cda 100644
    --- a/include/linux/cred.h
    +++ b/include/linux/cred.h
    @@ -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) \
    diff --git a/kernel/groups.c b/kernel/groups.c
    index 99b53d1..84156f2 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 51c6e89..e530bc3 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 50009c7..9d3044f 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 75762f3..6ed6f20 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 1600cfb..28b62db 100644
    --- a/net/sunrpc/auth_gss/svcauth_gss.c
    +++ b/net/sunrpc/auth_gss/svcauth_gss.c
    @@ -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 */
    diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c
    index e50502d..52c5abd 100644
    --- a/net/sunrpc/auth_unix.c
    +++ b/net/sunrpc/auth_unix.c
    @@ -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;
    diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c
    index 521d8f7..71ec853 100644
    --- a/net/sunrpc/svcauth_unix.c
    +++ b/net/sunrpc/svcauth_unix.c
    @@ -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;
    diff --git a/security/keys/permission.c b/security/keys/permission.c
    index e146cbd..5442900 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;
    --
    1.7.2.5


    \
     
     \ /
      Last update: 2012-04-08 07:21    [W:0.051 / U:32.552 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site