--- ./fs/nfs/dir.c.orig 2005-02-06 18:05:03.000000000 +0100 +++ ./fs/nfs/dir.c 2005-02-14 21:38:40.000000000 +0100 @@ -1518,7 +1518,7 @@ if (!NFS_PROTO(inode)->access) goto out_notsup; - cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0); + cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, NULL, 0); res = nfs_do_access(inode, cred, mask); put_rpccred(cred); unlock_kernel(); --- ./fs/nfs/inode.c.orig 2005-02-14 21:36:53.000000000 +0100 +++ ./fs/nfs/inode.c 2005-02-14 21:38:41.000000000 +0100 @@ -920,7 +920,7 @@ struct nfs_open_context *ctx; struct rpc_cred *cred; - if ((cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0)) == NULL) + if ((cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, NULL, 0)) == NULL) return -ENOMEM; ctx = alloc_nfs_open_context(filp->f_dentry, cred); if (ctx == NULL) @@ -1612,7 +1612,7 @@ } clnt->cl_chatty = 1; clp->cl_rpcclient = clnt; - clp->cl_cred = rpcauth_lookupcred(clnt->cl_auth, 0); + clp->cl_cred = rpcauth_lookupcred(clnt->cl_auth, NULL, 0); memcpy(clp->cl_ipaddr, server->ip_addr, sizeof(clp->cl_ipaddr)); nfs_idmap_new(clp); } --- ./fs/nfs/nfs4proc.c.orig 2005-02-14 21:36:53.000000000 +0100 +++ ./fs/nfs/nfs4proc.c 2005-02-14 21:38:41.000000000 +0100 @@ -773,7 +773,7 @@ BUG_ON(nd->intent.open.flags & O_CREAT); } - cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0); + cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, NULL, 0); state = nfs4_do_open(dir, dentry, nd->intent.open.flags, &attr, cred); put_rpccred(cred); if (IS_ERR(state)) @@ -788,7 +788,7 @@ struct nfs4_state *state; struct inode *inode; - cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0); + cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, NULL, 0); state = nfs4_open_delegated(dentry->d_inode, openflags, cred); if (IS_ERR(state)) state = nfs4_do_open(dir, dentry, openflags, NULL, cred); @@ -1008,7 +1008,7 @@ fattr->valid = 0; if (size_change) { - struct rpc_cred *cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0); + struct rpc_cred *cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, NULL, 0); state = nfs4_find_state(inode, cred, FMODE_WRITE); if (state == NULL) { state = nfs4_open_delegated(dentry->d_inode, @@ -1323,7 +1323,7 @@ struct nfs4_state *state = NULL; struct rpc_cred *cred; - cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0); + cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, NULL, 0); state = nfs4_do_open(dir, dentry, flags, sattr, cred); put_rpccred(cred); if (!IS_ERR(state)) { @@ -2000,7 +2000,7 @@ /* Find our open stateid */ - cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0); + cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, NULL, 0); if (unlikely(cred == NULL)) return -ENOMEM; ctx = alloc_nfs_open_context(dentry, cred); --- ./fs/nfs/unlink.c.orig 2005-02-06 18:05:04.000000000 +0100 +++ ./fs/nfs/unlink.c 2005-02-14 21:38:41.000000000 +0100 @@ -183,7 +183,7 @@ spin_lock(&dentry->d_lock); dentry->d_flags |= DCACHE_NFSFS_RENAMED; spin_unlock(&dentry->d_lock); - data->cred = rpcauth_lookupcred(clnt->cl_auth, 0); + data->cred = rpcauth_lookupcred(clnt->cl_auth, NULL, 0); rpc_sleep_on(&nfs_delete_queue, task, NULL, NULL); status = 0; --- ./fs/nfsd/nfs4callback.c.orig 2005-02-06 18:05:04.000000000 +0100 +++ ./fs/nfsd/nfs4callback.c 2005-02-14 22:36:56.000000000 +0100 @@ -351,19 +351,18 @@ { struct auth_cred acred; struct rpc_clnt *clnt = clp->cl_callback.cb_client; + struct svc_cred *cr = &clp->cl_cred; struct rpc_cred *ret = NULL; if (!clnt) goto out; - get_group_info(clp->cl_cred.cr_group_info); - acred.uid = clp->cl_cred.cr_uid; - acred.gid = clp->cl_cred.cr_gid; - acred.group_info = clp->cl_cred.cr_group_info; - + acred.uid = cr->cr_uid; dprintk("NFSD: looking up %s cred\n", clnt->cl_auth->au_ops->au_name); + if (clnt->cl_auth->au_ops->cr_add_groups) + clnt->cl_auth->au_ops->cr_add_groups(&acred, cr->cr_gid, cr->cr_group_info, NULL); ret = rpcauth_lookup_credcache(clnt->cl_auth, &acred, taskflags); - put_group_info(clp->cl_cred.cr_group_info); + dprintk("NFSD: cred %p\n", ret); out: return ret; } --- ./include/linux/sunrpc/auth.h.orig 2005-02-14 21:37:34.000000000 +0100 +++ ./include/linux/sunrpc/auth.h 2005-02-14 22:14:43.000000000 +0100 @@ -24,11 +24,16 @@ /* Maximum size (in bytes) of an rpc credential or verifier */ #define RPC_MAX_AUTH_SIZE (400) +struct rpc_groups { + int ngroups; + gid_t groups[RPC_MAXGROUPS]; +}; + /* Work around the lack of a VFS credential */ struct auth_cred { - uid_t uid; - gid_t gid; - struct group_info *group_info; + uid_t uid; + gid_t gid; + struct rpc_groups rg; }; /* @@ -92,6 +97,7 @@ void (*destroy)(struct rpc_auth *); struct rpc_cred * (*crcreate)(struct rpc_auth*, struct auth_cred *, int); + void (*cr_add_groups)(struct auth_cred *, gid_t, struct group_info *, struct rpc_groups *); }; struct rpc_credops { @@ -118,7 +124,7 @@ struct rpc_auth * rpcauth_create(rpc_authflavor_t, struct rpc_clnt *); void rpcauth_destroy(struct rpc_auth *); struct rpc_cred * rpcauth_lookup_credcache(struct rpc_auth *, struct auth_cred *, int); -struct rpc_cred * rpcauth_lookupcred(struct rpc_auth *, int); +struct rpc_cred * rpcauth_lookupcred(struct rpc_auth *, struct rpc_groups *, int); void put_rpccred(struct rpc_cred *); u32 * rpcauth_marshcred(struct rpc_task *, u32 *); u32 * rpcauth_checkverf(struct rpc_task *, u32 *); --- ./net/sunrpc/auth.c.orig 2005-02-14 21:37:34.000000000 +0100 +++ ./net/sunrpc/auth.c 2005-02-14 22:15:03.000000000 +0100 @@ -251,20 +251,19 @@ } struct rpc_cred * -rpcauth_lookupcred(struct rpc_auth *auth, int taskflags) +rpcauth_lookupcred(struct rpc_auth *auth, struct rpc_groups *rg, int taskflags) { struct auth_cred acred; struct rpc_cred *ret; - get_group_info(current->group_info); - acred.uid = current->fsuid; - acred.gid = current->fsgid; - acred.group_info = current->group_info; + dprintk("RPC: looking up %s cred\n", auth->au_ops->au_name); - dprintk("RPC: looking up %s cred\n", - auth->au_ops->au_name); + acred.uid = current->fsuid; + if (auth->au_ops->cr_add_groups) + auth->au_ops->cr_add_groups(&acred, current->fsgid, current->group_info, rg); ret = rpcauth_lookup_credcache(auth, &acred, taskflags); - put_group_info(current->group_info); + + dprintk("RPC: cred %p\n", ret); return ret; } --- ./net/sunrpc/auth_unix.c.orig 2005-02-14 21:36:53.000000000 +0100 +++ ./net/sunrpc/auth_unix.c 2005-02-14 22:37:33.000000000 +0100 @@ -30,6 +30,7 @@ #ifdef RPC_DEBUG # define RPCDBG_FACILITY RPCDBG_AUTH +# define RG(rg,i) ((i) < (rg).ngroups ? (int)(rg).groups[i] : -1) #endif static struct rpc_credops unix_credops; @@ -63,7 +64,6 @@ unx_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags) { struct unx_cred *cred; - int i; dprintk("RPC: allocating UNIX cred for uid %d gid %d\n", acred->uid, acred->gid); @@ -78,22 +78,55 @@ cred->uc_gid = 0; cred->uc_gids[0] = NOGROUP; } else { - int groups = acred->group_info->ngroups; - if (groups > RPC_MAXGROUPS) - groups = RPC_MAXGROUPS; - + int n = acred->rg.ngroups; cred->uc_uid = acred->uid; cred->uc_gid = acred->gid; - for (i = 0; i < groups; i++) - cred->uc_gids[i] = GROUP_AT(acred->group_info, i); - if (i < RPC_MAXGROUPS) - cred->uc_gids[i] = NOGROUP; + memcpy(cred->uc_gids, acred->rg.groups, n * sizeof (gid_t)); + if (n < RPC_MAXGROUPS) + cred->uc_gids[n] = NOGROUP; } cred->uc_base.cr_ops = &unix_credops; return (struct rpc_cred *) cred; } +/* + * Add groups to acred. When there are too many then try to be smart by + * picking only the relevant ones from our secondary group list. + */ +static void +unx_add_groups(struct auth_cred *acred, gid_t gid, struct group_info *gi, struct rpc_groups *rg) +{ + int i, n; + gid_t gid; + + acred->gid = gid; + get_group_info(gi); + n = gi->ngroups; + if (n <= RPC_MAXGROUPS) + rg = NULL; + else + n = RPC_MAXGROUPS; /* too many groups for AUTH_UNIX */ + if (rg) { + n = 0; /* pick the few relevant groups we're a member of */ + for (i = 0; i < rg->ngroups; ++i) { + gid = rg->groups[i]; + if (in_group_p(gid)) + acred->rg.groups[n++] = gid; + } + acred->rg.ngroups = n; + dprintk("RPC: unx_add_groups(): rg=%d:%d,%d,%d -> %d:%d,%d,%d\n", + rg->ngroups, RG(*rg, 0), RG(*rg, 1), RG(*rg, 2), + n, RG(acred->rg, 0), RG(acred->rg, 1), RG(acred->rg, 2)); + } else { + dprintk("RPC: unx_add_groups(): ngroups=%d\n", gi->ngroups); + for (i = 0; i < n; ++i) + acred->rg.groups[i] = GROUP_AT(gi, i); + acred->rg.ngroups = n; + } + put_group_info(gi); +} + static void unx_destroy_cred(struct rpc_cred *cred) { @@ -109,22 +142,19 @@ unx_match(struct auth_cred *acred, struct rpc_cred *rcred, int taskflags) { struct unx_cred *cred = (struct unx_cred *) rcred; - int i; if (!(taskflags & RPC_TASK_ROOTCREDS)) { - int groups; + int n = acred->rg.ngroups; if (cred->uc_uid != acred->uid || cred->uc_gid != acred->gid) return 0; - groups = acred->group_info->ngroups; - if (groups > RPC_MAXGROUPS) - groups = RPC_MAXGROUPS; - for (i = 0; i < groups ; i++) - if (cred->uc_gids[i] != GROUP_AT(acred->group_info, i)) - return 0; - return 1; + if (n < RPC_MAXGROUPS && cred->uc_gids[n] != NOGROUP) + return 0; + + return !memcmp(cred->uc_gids, acred->rg.groups, + n * sizeof (gid_t)); } return (cred->uc_uid == 0 && cred->uc_gid == 0 @@ -210,6 +240,7 @@ .create = unx_create, .destroy = unx_destroy, .crcreate = unx_create_cred, + .cr_add_groups = unx_add_groups, }; static --- ./net/sunrpc/clnt.c.orig 2005-02-14 21:37:34.000000000 +0100 +++ ./net/sunrpc/clnt.c 2005-02-14 21:38:41.000000000 +0100 @@ -430,7 +430,7 @@ /* we copied msg->rpc_cred, hold it */ get_rpccred(task->tk_msg.rpc_cred); } else { - task->tk_msg.rpc_cred = rpcauth_lookupcred(task->tk_auth, task->tk_flags); + task->tk_msg.rpc_cred = rpcauth_lookupcred(task->tk_auth, NULL, task->tk_flags); if (task->tk_msg.rpc_cred == NULL) task->tk_status = -ENOMEM; }