⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 auth_gss.c

📁 h内核
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * linux/net/sunrpc/auth_gss.c * * RPCSEC_GSS client authentication. *  *  Copyright (c) 2000 The Regents of the University of Michigan. *  All rights reserved. * *  Dug Song       <dugsong@monkey.org> *  Andy Adamson   <andros@umich.edu> * *  Redistribution and use in source and binary forms, with or without *  modification, are permitted provided that the following conditions *  are met: * *  1. Redistributions of source code must retain the above copyright *     notice, this list of conditions and the following disclaimer. *  2. Redistributions in binary form must reproduce the above copyright *     notice, this list of conditions and the following disclaimer in the *     documentation and/or other materials provided with the distribution. *  3. Neither the name of the University nor the names of its *     contributors may be used to endorse or promote products derived *     from this software without specific prior written permission. * *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $Id$ */#include <linux/module.h>#include <linux/init.h>#include <linux/types.h>#include <linux/slab.h>#include <linux/socket.h>#include <linux/in.h>#include <linux/sched.h>#include <linux/sunrpc/clnt.h>#include <linux/sunrpc/auth.h>#include <linux/sunrpc/auth_gss.h>#include <linux/sunrpc/svcauth_gss.h>#include <linux/sunrpc/gss_err.h>#include <linux/workqueue.h>#include <linux/sunrpc/rpc_pipe_fs.h>#include <linux/sunrpc/gss_api.h>#include <asm/uaccess.h>static struct rpc_authops authgss_ops;static struct rpc_credops gss_credops;#ifdef RPC_DEBUG# define RPCDBG_FACILITY	RPCDBG_AUTH#endif#define NFS_NGROUPS	16#define GSS_CRED_EXPIRE		(60 * HZ)	/* XXX: reasonable? */#define GSS_CRED_SLACK		1024		/* XXX: unused *//* length of a krb5 verifier (48), plus data added before arguments when * using integrity (two 4-byte integers): */#define GSS_VERF_SLACK		56/* XXX this define must match the gssd define* as it is passed to gssd to signal the use of* machine creds should be part of the shared rpc interface */#define CA_RUN_AS_MACHINE  0x00000200 /* dump the buffer in `emacs-hexl' style */#define isprint(c)      ((c > 0x1f) && (c < 0x7f))static DEFINE_RWLOCK(gss_ctx_lock);struct gss_auth {	struct rpc_auth rpc_auth;	struct gss_api_mech *mech;	struct list_head upcalls;	struct dentry *dentry;	char path[48];	spinlock_t lock;};static void gss_destroy_ctx(struct gss_cl_ctx *);static struct rpc_pipe_ops gss_upcall_ops;voidprint_hexl(u32 *p, u_int length, u_int offset){	u_int i, j, jm;	u8 c, *cp;		dprintk("RPC: print_hexl: length %d\n",length);	dprintk("\n");	cp = (u8 *) p;		for (i = 0; i < length; i += 0x10) {		dprintk("  %04x: ", (u_int)(i + offset));		jm = length - i;		jm = jm > 16 ? 16 : jm;				for (j = 0; j < jm; j++) {			if ((j % 2) == 1)				dprintk("%02x ", (u_int)cp[i+j]);			else				dprintk("%02x", (u_int)cp[i+j]);		}		for (; j < 16; j++) {			if ((j % 2) == 1)				dprintk("   ");			else				dprintk("  ");		}		dprintk(" ");				for (j = 0; j < jm; j++) {			c = cp[i+j];			c = isprint(c) ? c : '.';			dprintk("%c", c);		}		dprintk("\n");	}}EXPORT_SYMBOL(print_hexl);static inline struct gss_cl_ctx *gss_get_ctx(struct gss_cl_ctx *ctx){	atomic_inc(&ctx->count);	return ctx;}static inline voidgss_put_ctx(struct gss_cl_ctx *ctx){	if (atomic_dec_and_test(&ctx->count))		gss_destroy_ctx(ctx);}static voidgss_cred_set_ctx(struct rpc_cred *cred, struct gss_cl_ctx *ctx){	struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base);	struct gss_cl_ctx *old;	write_lock(&gss_ctx_lock);	old = gss_cred->gc_ctx;	gss_cred->gc_ctx = ctx;	cred->cr_flags |= RPCAUTH_CRED_UPTODATE;	write_unlock(&gss_ctx_lock);	if (old)		gss_put_ctx(old);}static intgss_cred_is_uptodate_ctx(struct rpc_cred *cred){	struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base);	int res = 0;	read_lock(&gss_ctx_lock);	if ((cred->cr_flags & RPCAUTH_CRED_UPTODATE) && gss_cred->gc_ctx)		res = 1;	read_unlock(&gss_ctx_lock);	return res;}static inline intsimple_get_bytes(char **ptr, const char *end, void *res, int len){	char *p, *q;	p = *ptr;	q = p + len;	if (q > end || q < p)		return -1;	memcpy(res, p, len);	*ptr = q;	return 0;}static inline intsimple_get_netobj(char **ptr, const char *end, struct xdr_netobj *res){	char *p, *q;	p = *ptr;	if (simple_get_bytes(&p, end, &res->len, sizeof(res->len)))		return -1;	q = p + res->len;	if (q > end || q < p)		return -1;	res->data = p;	*ptr = q;	return 0;}static intdup_netobj(struct xdr_netobj *source, struct xdr_netobj *dest){	dest->len = source->len;	if (!(dest->data = kmalloc(dest->len, GFP_KERNEL)))		return -1;	memcpy(dest->data, source->data, dest->len);	return 0;}static struct gss_cl_ctx *gss_cred_get_ctx(struct rpc_cred *cred){	struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base);	struct gss_cl_ctx *ctx = NULL;	read_lock(&gss_ctx_lock);	if (gss_cred->gc_ctx)		ctx = gss_get_ctx(gss_cred->gc_ctx);	read_unlock(&gss_ctx_lock);	return ctx;}static intgss_parse_init_downcall(struct gss_api_mech *gm, struct xdr_netobj *buf,		struct gss_cl_ctx **gc, uid_t *uid, int *gss_err){	char *end = buf->data + buf->len;	char *p = buf->data;	struct gss_cl_ctx *ctx;	struct xdr_netobj tmp_buf;	unsigned int timeout;	int err = -EIO;	if (!(ctx = kmalloc(sizeof(*ctx), GFP_KERNEL))) {		err = -ENOMEM;		goto err;	}	ctx->gc_proc = RPC_GSS_PROC_DATA;	ctx->gc_seq = 1;	/* NetApp 6.4R1 doesn't accept seq. no. 0 */	spin_lock_init(&ctx->gc_seq_lock);	atomic_set(&ctx->count,1);	if (simple_get_bytes(&p, end, uid, sizeof(*uid)))		goto err_free_ctx;	/* FIXME: discarded timeout for now */	if (simple_get_bytes(&p, end, &timeout, sizeof(timeout)))		goto err_free_ctx;	*gss_err = 0;	if (simple_get_bytes(&p, end, &ctx->gc_win, sizeof(ctx->gc_win)))		goto err_free_ctx;	/* gssd signals an error by passing ctx->gc_win = 0: */	if (!ctx->gc_win) {		/* in which case the next int is an error code: */		if (simple_get_bytes(&p, end, gss_err, sizeof(*gss_err)))			goto err_free_ctx;		err = 0;		goto err_free_ctx;	}	if (simple_get_netobj(&p, end, &tmp_buf))		goto err_free_ctx;	if (dup_netobj(&tmp_buf, &ctx->gc_wire_ctx)) {		err = -ENOMEM;		goto err_free_ctx;	}	if (simple_get_netobj(&p, end, &tmp_buf))		goto err_free_wire_ctx;	if (p != end)		goto err_free_wire_ctx;	if (gss_import_sec_context(&tmp_buf, gm, &ctx->gc_gss_ctx))		goto err_free_wire_ctx;	*gc = ctx;	return 0;err_free_wire_ctx:	kfree(ctx->gc_wire_ctx.data);err_free_ctx:	kfree(ctx);err:	*gc = NULL;	dprintk("RPC:      gss_parse_init_downcall returning %d\n", err);	return err;}struct gss_upcall_msg {	struct rpc_pipe_msg msg;	struct list_head list;	struct gss_auth *auth;	struct rpc_wait_queue waitq;	uid_t	uid;	atomic_t count;};static voidgss_release_msg(struct gss_upcall_msg *gss_msg){	if (!atomic_dec_and_test(&gss_msg->count))		return;	BUG_ON(!list_empty(&gss_msg->list));	kfree(gss_msg);}static struct gss_upcall_msg *__gss_find_upcall(struct gss_auth *gss_auth, uid_t uid){	struct gss_upcall_msg *pos;	list_for_each_entry(pos, &gss_auth->upcalls, list) {		if (pos->uid != uid)			continue;		atomic_inc(&pos->count);		dprintk("RPC:      gss_find_upcall found msg %p\n", pos);		return pos;	}	dprintk("RPC:      gss_find_upcall found nothing\n");	return NULL;}static void__gss_unhash_msg(struct gss_upcall_msg *gss_msg){	if (list_empty(&gss_msg->list))		return;	list_del_init(&gss_msg->list);	if (gss_msg->msg.errno < 0)		rpc_wake_up_status(&gss_msg->waitq, gss_msg->msg.errno);	else		rpc_wake_up(&gss_msg->waitq);	atomic_dec(&gss_msg->count);}static voidgss_unhash_msg(struct gss_upcall_msg *gss_msg){	struct gss_auth *gss_auth = gss_msg->auth;	spin_lock(&gss_auth->lock);	__gss_unhash_msg(gss_msg);	spin_unlock(&gss_auth->lock);}static intgss_upcall(struct rpc_clnt *clnt, struct rpc_task *task, struct rpc_cred *cred){	struct gss_auth *gss_auth = container_of(clnt->cl_auth,			struct gss_auth, rpc_auth);	struct gss_upcall_msg *gss_msg, *gss_new = NULL;	struct rpc_pipe_msg *msg;	struct dentry *dentry = gss_auth->dentry;	uid_t uid = cred->cr_uid;	int res = 0;	dprintk("RPC: %4u gss_upcall for uid %u\n", task->tk_pid, uid);retry:	spin_lock(&gss_auth->lock);	gss_msg = __gss_find_upcall(gss_auth, uid);	if (gss_msg)		goto out_sleep;	if (gss_new == NULL) {		spin_unlock(&gss_auth->lock);		gss_new = kmalloc(sizeof(*gss_new), GFP_KERNEL);		if (!gss_new) {			dprintk("RPC: %4u gss_upcall -ENOMEM\n", task->tk_pid);			return -ENOMEM;		}		goto retry;	}	gss_msg = gss_new;	memset(gss_new, 0, sizeof(*gss_new));	INIT_LIST_HEAD(&gss_new->list);	rpc_init_wait_queue(&gss_new->waitq, "RPCSEC_GSS upcall waitq");	atomic_set(&gss_new->count, 2);	msg = &gss_new->msg;	msg->data = &gss_new->uid;	msg->len = sizeof(gss_new->uid);	gss_new->uid = uid;	gss_new->auth = gss_auth;	list_add(&gss_new->list, &gss_auth->upcalls);	gss_new = NULL;	/* Has someone updated the credential behind our back? */	if (!gss_cred_is_uptodate_ctx(cred)) {		/* No, so do upcall and sleep */		task->tk_timeout = 0;		rpc_sleep_on(&gss_msg->waitq, task, NULL, NULL);		spin_unlock(&gss_auth->lock);		res = rpc_queue_upcall(dentry->d_inode, msg);		if (res)			gss_unhash_msg(gss_msg);	} else {		/* Yes, so cancel upcall */		__gss_unhash_msg(gss_msg);		spin_unlock(&gss_auth->lock);	}	gss_release_msg(gss_msg);	dprintk("RPC: %4u gss_upcall for uid %u result %d\n", task->tk_pid,			uid, res);	return res;out_sleep:	task->tk_timeout = 0;	rpc_sleep_on(&gss_msg->waitq, task, NULL, NULL);	spin_unlock(&gss_auth->lock);	dprintk("RPC: %4u gss_upcall  sleeping\n", task->tk_pid);	if (gss_new)		kfree(gss_new);	/* Note: we drop the reference here: we are automatically removed	 * from the queue when we're woken up, and we should in any case	 * have no further responsabilities w.r.t. the upcall.	 */	gss_release_msg(gss_msg);	return 0;}static ssize_tgss_pipe_upcall(struct file *filp, struct rpc_pipe_msg *msg,		char __user *dst, size_t buflen){	char *data = (char *)msg->data + msg->copied;	ssize_t mlen = msg->len;	ssize_t left;	if (mlen > buflen)		mlen = buflen;	left = copy_to_user(dst, data, mlen);	if (left < 0) {		msg->errno = left;		return left;	}	mlen -= left;	msg->copied += mlen;	msg->errno = 0;	return mlen;}#define MSG_BUF_MAXSIZE 1024static ssize_tgss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen){	struct xdr_netobj obj = {		.len	= mlen,	};	struct inode *inode = filp->f_dentry->d_inode;	struct rpc_inode *rpci = RPC_I(inode);	struct rpc_clnt *clnt;	struct rpc_auth *auth;	struct gss_auth *gss_auth;	struct gss_api_mech *mech;	struct auth_cred acred = { 0 };	struct rpc_cred *cred;	struct gss_upcall_msg *gss_msg;	struct gss_cl_ctx *ctx = NULL;	ssize_t left;	int err;	int gss_err;	if (mlen > MSG_BUF_MAXSIZE)		return -EFBIG;	obj.data = kmalloc(mlen, GFP_KERNEL);	if (!obj.data)		return -ENOMEM;	left = copy_from_user(obj.data, src, mlen);	if (left) {		err = -EFAULT;		goto out;	}	clnt = rpci->private;	atomic_inc(&clnt->cl_users);	auth = clnt->cl_auth;	gss_auth = container_of(auth, struct gss_auth, rpc_auth);	mech = gss_auth->mech;	err = gss_parse_init_downcall(mech, &obj, &ctx, &acred.uid, &gss_err);	if (err)		goto err;	cred = rpcauth_lookup_credcache(auth, &acred, 0);	if (!cred)		goto err;	if (gss_err)		cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE;	else		gss_cred_set_ctx(cred, ctx);	spin_lock(&gss_auth->lock);	gss_msg = __gss_find_upcall(gss_auth, acred.uid);	if (gss_msg) {		if (gss_err)			gss_msg->msg.errno = -EACCES;		__gss_unhash_msg(gss_msg);		spin_unlock(&gss_auth->lock);		gss_release_msg(gss_msg);	} else		spin_unlock(&gss_auth->lock);	rpc_release_client(clnt);	kfree(obj.data);	dprintk("RPC:      gss_pipe_downcall returning length %Zu\n", mlen);	return mlen;err:	if (ctx)		gss_destroy_ctx(ctx);	rpc_release_client(clnt);out:	kfree(obj.data);	dprintk("RPC:      gss_pipe_downcall returning %d\n", err);	return err;}static voidgss_pipe_release(struct inode *inode){	struct rpc_inode *rpci = RPC_I(inode);	struct rpc_clnt *clnt;	struct rpc_auth *auth;	struct gss_auth *gss_auth;	clnt = rpci->private;	auth = clnt->cl_auth;	gss_auth = container_of(auth, struct gss_auth, rpc_auth);	spin_lock(&gss_auth->lock);	while (!list_empty(&gss_auth->upcalls)) {		struct gss_upcall_msg *gss_msg;		gss_msg = list_entry(gss_auth->upcalls.next,				struct gss_upcall_msg, list);		gss_msg->msg.errno = -EPIPE;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -