8a3177604b
When we look up a new cred in the auth_gss downcall so that we can stuff the credcache, we do not want that lookup to queue up an upcall in order to initialise it. To do an upcall here not only redundant, but since we are already holding the inode->i_mutex, it will trigger a lock recursion. This patch allows rpcauth cache searches to indicate that they can cope with uninitialised credentials. Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
240 lines
5.4 KiB
C
240 lines
5.4 KiB
C
/*
|
|
* linux/net/sunrpc/auth_unix.c
|
|
*
|
|
* UNIX-style authentication; no AUTH_SHORT support
|
|
*
|
|
* Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
|
|
*/
|
|
|
|
#include <linux/types.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/module.h>
|
|
#include <linux/sunrpc/clnt.h>
|
|
#include <linux/sunrpc/auth.h>
|
|
|
|
#define NFS_NGROUPS 16
|
|
|
|
struct unx_cred {
|
|
struct rpc_cred uc_base;
|
|
gid_t uc_gid;
|
|
gid_t uc_gids[NFS_NGROUPS];
|
|
};
|
|
#define uc_uid uc_base.cr_uid
|
|
#define uc_count uc_base.cr_count
|
|
#define uc_flags uc_base.cr_flags
|
|
#define uc_expire uc_base.cr_expire
|
|
|
|
#define UNX_CRED_EXPIRE (60 * HZ)
|
|
|
|
#define UNX_WRITESLACK (21 + (UNX_MAXNODENAME >> 2))
|
|
|
|
#ifdef RPC_DEBUG
|
|
# define RPCDBG_FACILITY RPCDBG_AUTH
|
|
#endif
|
|
|
|
static struct rpc_auth unix_auth;
|
|
static struct rpc_cred_cache unix_cred_cache;
|
|
static struct rpc_credops unix_credops;
|
|
|
|
static struct rpc_auth *
|
|
unx_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor)
|
|
{
|
|
dprintk("RPC: creating UNIX authenticator for client %p\n", clnt);
|
|
if (atomic_inc_return(&unix_auth.au_count) == 0)
|
|
unix_cred_cache.nextgc = jiffies + (unix_cred_cache.expire >> 1);
|
|
return &unix_auth;
|
|
}
|
|
|
|
static void
|
|
unx_destroy(struct rpc_auth *auth)
|
|
{
|
|
dprintk("RPC: destroying UNIX authenticator %p\n", auth);
|
|
rpcauth_free_credcache(auth);
|
|
}
|
|
|
|
/*
|
|
* Lookup AUTH_UNIX creds for current process
|
|
*/
|
|
static struct rpc_cred *
|
|
unx_lookup_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags)
|
|
{
|
|
return rpcauth_lookup_credcache(auth, acred, flags);
|
|
}
|
|
|
|
static struct rpc_cred *
|
|
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);
|
|
|
|
if (!(cred = kmalloc(sizeof(*cred), GFP_KERNEL)))
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
atomic_set(&cred->uc_count, 1);
|
|
cred->uc_flags = RPCAUTH_CRED_UPTODATE;
|
|
if (flags & RPCAUTH_LOOKUP_ROOTCREDS) {
|
|
cred->uc_uid = 0;
|
|
cred->uc_gid = 0;
|
|
cred->uc_gids[0] = NOGROUP;
|
|
} else {
|
|
int groups = acred->group_info->ngroups;
|
|
if (groups > NFS_NGROUPS)
|
|
groups = NFS_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 < NFS_NGROUPS)
|
|
cred->uc_gids[i] = NOGROUP;
|
|
}
|
|
cred->uc_base.cr_ops = &unix_credops;
|
|
|
|
return (struct rpc_cred *) cred;
|
|
}
|
|
|
|
static void
|
|
unx_destroy_cred(struct rpc_cred *cred)
|
|
{
|
|
kfree(cred);
|
|
}
|
|
|
|
/*
|
|
* Match credentials against current process creds.
|
|
* The root_override argument takes care of cases where the caller may
|
|
* request root creds (e.g. for NFS swapping).
|
|
*/
|
|
static int
|
|
unx_match(struct auth_cred *acred, struct rpc_cred *rcred, int flags)
|
|
{
|
|
struct unx_cred *cred = (struct unx_cred *) rcred;
|
|
int i;
|
|
|
|
if (!(flags & RPCAUTH_LOOKUP_ROOTCREDS)) {
|
|
int groups;
|
|
|
|
if (cred->uc_uid != acred->uid
|
|
|| cred->uc_gid != acred->gid)
|
|
return 0;
|
|
|
|
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))
|
|
return 0;
|
|
return 1;
|
|
}
|
|
return (cred->uc_uid == 0
|
|
&& cred->uc_gid == 0
|
|
&& cred->uc_gids[0] == (gid_t) NOGROUP);
|
|
}
|
|
|
|
/*
|
|
* Marshal credentials.
|
|
* Maybe we should keep a cached credential for performance reasons.
|
|
*/
|
|
static u32 *
|
|
unx_marshal(struct rpc_task *task, u32 *p)
|
|
{
|
|
struct rpc_clnt *clnt = task->tk_client;
|
|
struct unx_cred *cred = (struct unx_cred *) task->tk_msg.rpc_cred;
|
|
u32 *base, *hold;
|
|
int i;
|
|
|
|
*p++ = htonl(RPC_AUTH_UNIX);
|
|
base = p++;
|
|
*p++ = htonl(jiffies/HZ);
|
|
|
|
/*
|
|
* Copy the UTS nodename captured when the client was created.
|
|
*/
|
|
p = xdr_encode_array(p, clnt->cl_nodename, clnt->cl_nodelen);
|
|
|
|
*p++ = htonl((u32) cred->uc_uid);
|
|
*p++ = htonl((u32) cred->uc_gid);
|
|
hold = p++;
|
|
for (i = 0; i < 16 && cred->uc_gids[i] != (gid_t) NOGROUP; i++)
|
|
*p++ = htonl((u32) cred->uc_gids[i]);
|
|
*hold = htonl(p - hold - 1); /* gid array length */
|
|
*base = htonl((p - base - 1) << 2); /* cred length */
|
|
|
|
*p++ = htonl(RPC_AUTH_NULL);
|
|
*p++ = htonl(0);
|
|
|
|
return p;
|
|
}
|
|
|
|
/*
|
|
* Refresh credentials. This is a no-op for AUTH_UNIX
|
|
*/
|
|
static int
|
|
unx_refresh(struct rpc_task *task)
|
|
{
|
|
task->tk_msg.rpc_cred->cr_flags |= RPCAUTH_CRED_UPTODATE;
|
|
return 0;
|
|
}
|
|
|
|
static u32 *
|
|
unx_validate(struct rpc_task *task, u32 *p)
|
|
{
|
|
rpc_authflavor_t flavor;
|
|
u32 size;
|
|
|
|
flavor = ntohl(*p++);
|
|
if (flavor != RPC_AUTH_NULL &&
|
|
flavor != RPC_AUTH_UNIX &&
|
|
flavor != RPC_AUTH_SHORT) {
|
|
printk("RPC: bad verf flavor: %u\n", flavor);
|
|
return NULL;
|
|
}
|
|
|
|
size = ntohl(*p++);
|
|
if (size > RPC_MAX_AUTH_SIZE) {
|
|
printk("RPC: giant verf size: %u\n", size);
|
|
return NULL;
|
|
}
|
|
task->tk_auth->au_rslack = (size >> 2) + 2;
|
|
p += (size >> 2);
|
|
|
|
return p;
|
|
}
|
|
|
|
struct rpc_authops authunix_ops = {
|
|
.owner = THIS_MODULE,
|
|
.au_flavor = RPC_AUTH_UNIX,
|
|
#ifdef RPC_DEBUG
|
|
.au_name = "UNIX",
|
|
#endif
|
|
.create = unx_create,
|
|
.destroy = unx_destroy,
|
|
.lookup_cred = unx_lookup_cred,
|
|
.crcreate = unx_create_cred,
|
|
};
|
|
|
|
static
|
|
struct rpc_cred_cache unix_cred_cache = {
|
|
.expire = UNX_CRED_EXPIRE,
|
|
};
|
|
|
|
static
|
|
struct rpc_auth unix_auth = {
|
|
.au_cslack = UNX_WRITESLACK,
|
|
.au_rslack = 2, /* assume AUTH_NULL verf */
|
|
.au_ops = &authunix_ops,
|
|
.au_count = ATOMIC_INIT(0),
|
|
.au_credcache = &unix_cred_cache,
|
|
};
|
|
|
|
static
|
|
struct rpc_credops unix_credops = {
|
|
.cr_name = "AUTH_UNIX",
|
|
.crdestroy = unx_destroy_cred,
|
|
.crmatch = unx_match,
|
|
.crmarshal = unx_marshal,
|
|
.crrefresh = unx_refresh,
|
|
.crvalidate = unx_validate,
|
|
};
|