416 lines · 9.6 KB · C
RawDownload
/* * linux/net/sunrpc/auth.c * * Generic RPC client authentication API. * * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de> */ #include <linux/types.h>#include <linux/sched.h>#include <linux/module.h>#include <linux/slab.h>#include <linux/errno.h>#include <linux/sunrpc/clnt.h>#include <linux/spinlock.h> #ifdef RPC_DEBUG# define RPCDBG_FACILITY	RPCDBG_AUTH#endif static struct rpc_authops *	auth_flavors[RPC_AUTH_MAXFLAVOR] = {	&authnull_ops,		/* AUTH_NULL */	&authunix_ops,		/* AUTH_UNIX */	NULL,			/* others can be loadable modules */}; static u32pseudoflavor_to_flavor(u32 flavor) {	if (flavor >= RPC_AUTH_MAXFLAVOR)		return RPC_AUTH_GSS;	return flavor;} intrpcauth_register(struct rpc_authops *ops){	rpc_authflavor_t flavor; 	if ((flavor = ops->au_flavor) >= RPC_AUTH_MAXFLAVOR)		return -EINVAL;	if (auth_flavors[flavor] != NULL)		return -EPERM;		/* what else? */	auth_flavors[flavor] = ops;	return 0;} intrpcauth_unregister(struct rpc_authops *ops){	rpc_authflavor_t flavor; 	if ((flavor = ops->au_flavor) >= RPC_AUTH_MAXFLAVOR)		return -EINVAL;	if (auth_flavors[flavor] != ops)		return -EPERM;		/* what else? */	auth_flavors[flavor] = NULL;	return 0;} struct rpc_auth *rpcauth_create(rpc_authflavor_t pseudoflavor, struct rpc_clnt *clnt){	struct rpc_auth		*auth;	struct rpc_authops	*ops;	u32			flavor = pseudoflavor_to_flavor(pseudoflavor); 	auth = ERR_PTR(-EINVAL);	if (flavor >= RPC_AUTH_MAXFLAVOR)		goto out; 	/* FIXME - auth_flavors[] really needs an rw lock,	 * and module refcounting. */#ifdef CONFIG_KMOD	if ((ops = auth_flavors[flavor]) == NULL)		request_module("rpc-auth-%u", flavor);#endif	if ((ops = auth_flavors[flavor]) == NULL)		goto out;	auth = ops->create(clnt, pseudoflavor);	if (IS_ERR(auth))		return auth;	if (clnt->cl_auth)		rpcauth_destroy(clnt->cl_auth);	clnt->cl_auth = auth; out:	return auth;} voidrpcauth_destroy(struct rpc_auth *auth){	if (!atomic_dec_and_test(&auth->au_count))		return;	auth->au_ops->destroy(auth);} static DEFINE_SPINLOCK(rpc_credcache_lock); /* * Initialize RPC credential cache */intrpcauth_init_credcache(struct rpc_auth *auth, unsigned long expire){	struct rpc_cred_cache *new;	int i; 	new = kmalloc(sizeof(*new), GFP_KERNEL);	if (!new)		return -ENOMEM;	for (i = 0; i < RPC_CREDCACHE_NR; i++)		INIT_HLIST_HEAD(&new->hashtable[i]);	new->expire = expire;	new->nextgc = jiffies + (expire >> 1);	auth->au_credcache = new;	return 0;} /* * Destroy a list of credentials */static inlinevoid rpcauth_destroy_credlist(struct hlist_head *head){	struct rpc_cred *cred; 	while (!hlist_empty(head)) {		cred = hlist_entry(head->first, struct rpc_cred, cr_hash);		hlist_del_init(&cred->cr_hash);		put_rpccred(cred);	}} /* * Clear the RPC credential cache, and delete those credentials * that are not referenced. */voidrpcauth_free_credcache(struct rpc_auth *auth){	struct rpc_cred_cache *cache = auth->au_credcache;	HLIST_HEAD(free);	struct hlist_node *pos, *next;	struct rpc_cred	*cred;	int		i; 	spin_lock(&rpc_credcache_lock);	for (i = 0; i < RPC_CREDCACHE_NR; i++) {		hlist_for_each_safe(pos, next, &cache->hashtable[i]) {			cred = hlist_entry(pos, struct rpc_cred, cr_hash);			__hlist_del(&cred->cr_hash);			hlist_add_head(&cred->cr_hash, &free);		}	}	spin_unlock(&rpc_credcache_lock);	rpcauth_destroy_credlist(&free);} static voidrpcauth_prune_expired(struct rpc_auth *auth, struct rpc_cred *cred, struct hlist_head *free){	if (atomic_read(&cred->cr_count) != 1)	       return;	if (time_after(jiffies, cred->cr_expire + auth->au_credcache->expire))		cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE;	if (!(cred->cr_flags & RPCAUTH_CRED_UPTODATE)) {		__hlist_del(&cred->cr_hash);		hlist_add_head(&cred->cr_hash, free);	}} /* * Remove stale credentials. Avoid sleeping inside the loop. */static voidrpcauth_gc_credcache(struct rpc_auth *auth, struct hlist_head *free){	struct rpc_cred_cache *cache = auth->au_credcache;	struct hlist_node *pos, *next;	struct rpc_cred	*cred;	int		i; 	dprintk("RPC: gc'ing RPC credentials for auth %p\n", auth);	for (i = 0; i < RPC_CREDCACHE_NR; i++) {		hlist_for_each_safe(pos, next, &cache->hashtable[i]) {			cred = hlist_entry(pos, struct rpc_cred, cr_hash);			rpcauth_prune_expired(auth, cred, free);		}	}	cache->nextgc = jiffies + cache->expire;} /* * Look up a process' credentials in the authentication cache */struct rpc_cred *rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred,		int flags){	struct rpc_cred_cache *cache = auth->au_credcache;	HLIST_HEAD(free);	struct hlist_node *pos, *next;	struct rpc_cred	*new = NULL,			*cred = NULL;	int		nr = 0; 	if (!(flags & RPCAUTH_LOOKUP_ROOTCREDS))		nr = acred->uid & RPC_CREDCACHE_MASK;retry:	spin_lock(&rpc_credcache_lock);	if (time_before(cache->nextgc, jiffies))		rpcauth_gc_credcache(auth, &free);	hlist_for_each_safe(pos, next, &cache->hashtable[nr]) {		struct rpc_cred *entry;	       	entry = hlist_entry(pos, struct rpc_cred, cr_hash);		if (entry->cr_ops->crmatch(acred, entry, flags)) {			hlist_del(&entry->cr_hash);			cred = entry;			break;		}		rpcauth_prune_expired(auth, entry, &free);	}	if (new) {		if (cred)			hlist_add_head(&new->cr_hash, &free);		else			cred = new;	}	if (cred) {		hlist_add_head(&cred->cr_hash, &cache->hashtable[nr]);		get_rpccred(cred);	}	spin_unlock(&rpc_credcache_lock); 	rpcauth_destroy_credlist(&free); 	if (!cred) {		new = auth->au_ops->crcreate(auth, acred, flags);		if (!IS_ERR(new)) {#ifdef RPC_DEBUG			new->cr_magic = RPCAUTH_CRED_MAGIC;#endif			goto retry;		} else			cred = new;	} else if ((cred->cr_flags & RPCAUTH_CRED_NEW)			&& cred->cr_ops->cr_init != NULL			&& !(flags & RPCAUTH_LOOKUP_NEW)) {		int res = cred->cr_ops->cr_init(auth, cred);		if (res < 0) {			put_rpccred(cred);			cred = ERR_PTR(res);		}	} 	return (struct rpc_cred *) cred;} struct rpc_cred *rpcauth_lookupcred(struct rpc_auth *auth, int flags){	struct auth_cred acred = {		.uid = current->fsuid,		.gid = current->fsgid,		.group_info = current->group_info,	};	struct rpc_cred *ret; 	dprintk("RPC:     looking up %s cred\n",		auth->au_ops->au_name);	get_group_info(acred.group_info);	ret = auth->au_ops->lookup_cred(auth, &acred, flags);	put_group_info(acred.group_info);	return ret;} struct rpc_cred *rpcauth_bindcred(struct rpc_task *task){	struct rpc_auth *auth = task->tk_auth;	struct auth_cred acred = {		.uid = current->fsuid,		.gid = current->fsgid,		.group_info = current->group_info,	};	struct rpc_cred *ret;	int flags = 0; 	dprintk("RPC: %4d looking up %s cred\n",		task->tk_pid, task->tk_auth->au_ops->au_name);	get_group_info(acred.group_info);	if (task->tk_flags & RPC_TASK_ROOTCREDS)		flags |= RPCAUTH_LOOKUP_ROOTCREDS;	ret = auth->au_ops->lookup_cred(auth, &acred, flags);	if (!IS_ERR(ret))		task->tk_msg.rpc_cred = ret;	else		task->tk_status = PTR_ERR(ret);	put_group_info(acred.group_info);	return ret;} voidrpcauth_holdcred(struct rpc_task *task){	dprintk("RPC: %4d holding %s cred %p\n",		task->tk_pid, task->tk_auth->au_ops->au_name, task->tk_msg.rpc_cred);	if (task->tk_msg.rpc_cred)		get_rpccred(task->tk_msg.rpc_cred);} voidput_rpccred(struct rpc_cred *cred){	cred->cr_expire = jiffies;	if (!atomic_dec_and_test(&cred->cr_count))		return;	cred->cr_ops->crdestroy(cred);} voidrpcauth_unbindcred(struct rpc_task *task){	struct rpc_cred	*cred = task->tk_msg.rpc_cred; 	dprintk("RPC: %4d releasing %s cred %p\n",		task->tk_pid, task->tk_auth->au_ops->au_name, cred); 	put_rpccred(cred);	task->tk_msg.rpc_cred = NULL;} u32 *rpcauth_marshcred(struct rpc_task *task, u32 *p){	struct rpc_cred	*cred = task->tk_msg.rpc_cred; 	dprintk("RPC: %4d marshaling %s cred %p\n",		task->tk_pid, task->tk_auth->au_ops->au_name, cred); 	return cred->cr_ops->crmarshal(task, p);} u32 *rpcauth_checkverf(struct rpc_task *task, u32 *p){	struct rpc_cred	*cred = task->tk_msg.rpc_cred; 	dprintk("RPC: %4d validating %s cred %p\n",		task->tk_pid, task->tk_auth->au_ops->au_name, cred); 	return cred->cr_ops->crvalidate(task, p);} intrpcauth_wrap_req(struct rpc_task *task, kxdrproc_t encode, void *rqstp,		u32 *data, void *obj){	struct rpc_cred *cred = task->tk_msg.rpc_cred; 	dprintk("RPC: %4d using %s cred %p to wrap rpc data\n",			task->tk_pid, cred->cr_ops->cr_name, cred);	if (cred->cr_ops->crwrap_req)		return cred->cr_ops->crwrap_req(task, encode, rqstp, data, obj);	/* By default, we encode the arguments normally. */	return encode(rqstp, data, obj);} intrpcauth_unwrap_resp(struct rpc_task *task, kxdrproc_t decode, void *rqstp,		u32 *data, void *obj){	struct rpc_cred *cred = task->tk_msg.rpc_cred; 	dprintk("RPC: %4d using %s cred %p to unwrap rpc data\n",			task->tk_pid, cred->cr_ops->cr_name, cred);	if (cred->cr_ops->crunwrap_resp)		return cred->cr_ops->crunwrap_resp(task, decode, rqstp,						   data, obj);	/* By default, we decode the arguments normally. */	return decode(rqstp, data, obj);} intrpcauth_refreshcred(struct rpc_task *task){	struct rpc_cred	*cred = task->tk_msg.rpc_cred;	int err; 	dprintk("RPC: %4d refreshing %s cred %p\n",		task->tk_pid, task->tk_auth->au_ops->au_name, cred); 	err = cred->cr_ops->crrefresh(task);	if (err < 0)		task->tk_status = err;	return err;} voidrpcauth_invalcred(struct rpc_task *task){	dprintk("RPC: %4d invalidating %s cred %p\n",		task->tk_pid, task->tk_auth->au_ops->au_name, task->tk_msg.rpc_cred);	spin_lock(&rpc_credcache_lock);	if (task->tk_msg.rpc_cred)		task->tk_msg.rpc_cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE;	spin_unlock(&rpc_credcache_lock);} intrpcauth_uptodatecred(struct rpc_task *task){	return !(task->tk_msg.rpc_cred) ||		(task->tk_msg.rpc_cred->cr_flags & RPCAUTH_CRED_UPTODATE);}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t