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

📄 svcauth_gss.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 2 页
字号:
gss_verify_header(struct svc_rqst *rqstp, struct rsc *rsci,		  u32 *rpcstart, struct rpc_gss_wire_cred *gc, u32 *authp){	struct gss_ctx		*ctx_id = rsci->mechctx;	struct xdr_buf		rpchdr;	struct xdr_netobj	checksum;	u32			flavor = 0;	struct kvec		*argv = &rqstp->rq_arg.head[0];	struct kvec		iov;	/* data to compute the checksum over: */	iov.iov_base = rpcstart;	iov.iov_len = (u8 *)argv->iov_base - (u8 *)rpcstart;	xdr_buf_from_iov(&iov, &rpchdr);	*authp = rpc_autherr_badverf;	if (argv->iov_len < 4)		return SVC_DENIED;	flavor = ntohl(svc_getu32(argv));	if (flavor != RPC_AUTH_GSS)		return SVC_DENIED;	if (svc_safe_getnetobj(argv, &checksum))		return SVC_DENIED;	if (rqstp->rq_deferred) /* skip verification of revisited request */		return SVC_OK;	if (gss_verify_mic(ctx_id, &rpchdr, &checksum) != GSS_S_COMPLETE) {		*authp = rpcsec_gsserr_credproblem;		return SVC_DENIED;	}	if (gc->gc_seq > MAXSEQ) {		dprintk("RPC:      svcauth_gss: discarding request with large sequence number %d\n",				gc->gc_seq);		*authp = rpcsec_gsserr_ctxproblem;		return SVC_DENIED;	}	if (!gss_check_seq_num(rsci, gc->gc_seq)) {		dprintk("RPC:      svcauth_gss: discarding request with old sequence number %d\n",				gc->gc_seq);		return SVC_DROP;	}	return SVC_OK;}static intgss_write_verf(struct svc_rqst *rqstp, struct gss_ctx *ctx_id, u32 seq){	u32			xdr_seq;	u32			maj_stat;	struct xdr_buf		verf_data;	struct xdr_netobj	mic;	u32			*p;	struct kvec		iov;	svc_putu32(rqstp->rq_res.head, htonl(RPC_AUTH_GSS));	xdr_seq = htonl(seq);	iov.iov_base = &xdr_seq;	iov.iov_len = sizeof(xdr_seq);	xdr_buf_from_iov(&iov, &verf_data);	p = rqstp->rq_res.head->iov_base + rqstp->rq_res.head->iov_len;	mic.data = (u8 *)(p + 1);	maj_stat = gss_get_mic(ctx_id, &verf_data, &mic);	if (maj_stat != GSS_S_COMPLETE)		return -1;	*p++ = htonl(mic.len);	memset((u8 *)p + mic.len, 0, round_up_to_quad(mic.len) - mic.len);	p += XDR_QUADLEN(mic.len);	if (!xdr_ressize_check(rqstp, p))		return -1;	return 0;}struct gss_domain {	struct auth_domain	h;	u32			pseudoflavor;};static struct auth_domain *find_gss_auth_domain(struct gss_ctx *ctx, u32 svc){	char *name;	name = gss_service_to_auth_domain_name(ctx->mech_type, svc);	if (!name)		return NULL;	return auth_domain_find(name);}intsvcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name){	struct gss_domain	*new;	struct auth_domain	*test;	int			stat = -ENOMEM;	new = kmalloc(sizeof(*new), GFP_KERNEL);	if (!new)		goto out;	cache_init(&new->h.h);	new->h.name = kmalloc(strlen(name) + 1, GFP_KERNEL);	if (!new->h.name)		goto out_free_dom;	strcpy(new->h.name, name);	new->h.flavour = RPC_AUTH_GSS;	new->pseudoflavor = pseudoflavor;	new->h.h.expiry_time = NEVER;	test = auth_domain_lookup(&new->h, 1);	if (test == &new->h) {		BUG_ON(atomic_dec_and_test(&new->h.h.refcnt));	} else { /* XXX Duplicate registration? */		auth_domain_put(&new->h);		goto out;	}	return 0;out_free_dom:	kfree(new);out:	return stat;}EXPORT_SYMBOL(svcauth_gss_register_pseudoflavor);static inline intread_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj){	u32     raw;	int     status;	status = read_bytes_from_xdr_buf(buf, base, &raw, sizeof(*obj));	if (status)		return status;	*obj = ntohl(raw);	return 0;}/* It would be nice if this bit of code could be shared with the client. * Obstacles: *	The client shouldn't malloc(), would have to pass in own memory. *	The server uses base of head iovec as read pointer, while the *	client uses separate pointer. */static intunwrap_integ_data(struct xdr_buf *buf, u32 seq, struct gss_ctx *ctx){	int stat = -EINVAL;	u32 integ_len, maj_stat;	struct xdr_netobj mic;	struct xdr_buf integ_buf;	integ_len = ntohl(svc_getu32(&buf->head[0]));	if (integ_len & 3)		goto out;	if (integ_len > buf->len)		goto out;	if (xdr_buf_subsegment(buf, &integ_buf, 0, integ_len))		BUG();	/* copy out mic... */	if (read_u32_from_xdr_buf(buf, integ_len, &mic.len))		BUG();	if (mic.len > RPC_MAX_AUTH_SIZE)		goto out;	mic.data = kmalloc(mic.len, GFP_KERNEL);	if (!mic.data)		goto out;	if (read_bytes_from_xdr_buf(buf, integ_len + 4, mic.data, mic.len))		goto out;	maj_stat = gss_verify_mic(ctx, &integ_buf, &mic);	if (maj_stat != GSS_S_COMPLETE)		goto out;	if (ntohl(svc_getu32(&buf->head[0])) != seq)		goto out;	stat = 0;out:	return stat;}struct gss_svc_data {	/* decoded gss client cred: */	struct rpc_gss_wire_cred	clcred;	/* pointer to the beginning of the procedure-specific results,	 * which may be encrypted/checksummed in svcauth_gss_release: */	u32				*body_start;	struct rsc			*rsci;};static intsvcauth_gss_set_client(struct svc_rqst *rqstp){	struct gss_svc_data *svcdata = rqstp->rq_auth_data;	struct rsc *rsci = svcdata->rsci;	struct rpc_gss_wire_cred *gc = &svcdata->clcred;	rqstp->rq_client = find_gss_auth_domain(rsci->mechctx, gc->gc_svc);	if (rqstp->rq_client == NULL)		return SVC_DENIED;	return SVC_OK;}/* * Accept an rpcsec packet. * If context establishment, punt to user space * If data exchange, verify/decrypt * If context destruction, handle here * In the context establishment and destruction case we encode * response here and return SVC_COMPLETE. */static intsvcauth_gss_accept(struct svc_rqst *rqstp, u32 *authp){	struct kvec	*argv = &rqstp->rq_arg.head[0];	struct kvec	*resv = &rqstp->rq_res.head[0];	u32		crlen;	struct xdr_netobj tmpobj;	struct gss_svc_data *svcdata = rqstp->rq_auth_data;	struct rpc_gss_wire_cred *gc;	struct rsc	*rsci = NULL;	struct rsi	*rsip, rsikey;	u32		*rpcstart;	u32		*reject_stat = resv->iov_base + resv->iov_len;	int		ret;	dprintk("RPC:      svcauth_gss: argv->iov_len = %zd\n",argv->iov_len);	*authp = rpc_autherr_badcred;	if (!svcdata)		svcdata = kmalloc(sizeof(*svcdata), GFP_KERNEL);	if (!svcdata)		goto auth_err;	rqstp->rq_auth_data = svcdata;	svcdata->body_start = NULL;	svcdata->rsci = NULL;	gc = &svcdata->clcred;	/* start of rpc packet is 7 u32's back from here:	 * xid direction rpcversion prog vers proc flavour	 */	rpcstart = argv->iov_base;	rpcstart -= 7;	/* credential is:	 *   version(==1), proc(0,1,2,3), seq, service (1,2,3), handle	 * at least 5 u32s, and is preceeded by length, so that makes 6.	 */	if (argv->iov_len < 5 * 4)		goto auth_err;	crlen = ntohl(svc_getu32(argv));	if (ntohl(svc_getu32(argv)) != RPC_GSS_VERSION)		goto auth_err;	gc->gc_proc = ntohl(svc_getu32(argv));	gc->gc_seq = ntohl(svc_getu32(argv));	gc->gc_svc = ntohl(svc_getu32(argv));	if (svc_safe_getnetobj(argv, &gc->gc_ctx))		goto auth_err;	if (crlen != round_up_to_quad(gc->gc_ctx.len) + 5 * 4)		goto auth_err;	if ((gc->gc_proc != RPC_GSS_PROC_DATA) && (rqstp->rq_proc != 0))		goto auth_err;	/*	 * We've successfully parsed the credential. Let's check out the	 * verifier.  An AUTH_NULL verifier is allowed (and required) for	 * INIT and CONTINUE_INIT requests. AUTH_RPCSEC_GSS is required for	 * PROC_DATA and PROC_DESTROY.	 *	 * AUTH_NULL verifier is 0 (AUTH_NULL), 0 (length).	 * AUTH_RPCSEC_GSS verifier is:	 *   6 (AUTH_RPCSEC_GSS), length, checksum.	 * checksum is calculated over rpcheader from xid up to here.	 */	*authp = rpc_autherr_badverf;	switch (gc->gc_proc) {	case RPC_GSS_PROC_INIT:	case RPC_GSS_PROC_CONTINUE_INIT:		if (argv->iov_len < 2 * 4)			goto auth_err;		if (ntohl(svc_getu32(argv)) != RPC_AUTH_NULL)			goto auth_err;		if (ntohl(svc_getu32(argv)) != 0)			goto auth_err;		break;	case RPC_GSS_PROC_DATA:	case RPC_GSS_PROC_DESTROY:		*authp = rpcsec_gsserr_credproblem;		rsci = gss_svc_searchbyctx(&gc->gc_ctx);		if (!rsci)			goto auth_err;		switch (gss_verify_header(rqstp, rsci, rpcstart, gc, authp)) {		case SVC_OK:			break;		case SVC_DENIED:			goto auth_err;		case SVC_DROP:			goto drop;		}		break;	default:		*authp = rpc_autherr_rejectedcred;		goto auth_err;	}	/* now act upon the command: */	switch (gc->gc_proc) {	case RPC_GSS_PROC_INIT:	case RPC_GSS_PROC_CONTINUE_INIT:		*authp = rpc_autherr_badcred;		if (gc->gc_proc == RPC_GSS_PROC_INIT && gc->gc_ctx.len != 0)			goto auth_err;		memset(&rsikey, 0, sizeof(rsikey));		if (dup_netobj(&rsikey.in_handle, &gc->gc_ctx))			goto drop;		*authp = rpc_autherr_badverf;		if (svc_safe_getnetobj(argv, &tmpobj)) {			kfree(rsikey.in_handle.data);			goto auth_err;		}		if (dup_netobj(&rsikey.in_token, &tmpobj)) {			kfree(rsikey.in_handle.data);			goto drop;		}		rsip = rsi_lookup(&rsikey, 0);		rsi_free(&rsikey);		if (!rsip) {			goto drop;		}		switch(cache_check(&rsi_cache, &rsip->h, &rqstp->rq_chandle)) {		case -EAGAIN:			goto drop;		case -ENOENT:			goto drop;		case 0:			rsci = gss_svc_searchbyctx(&rsip->out_handle);			if (!rsci) {				goto drop;			}			if (gss_write_verf(rqstp, rsci->mechctx, GSS_SEQ_WIN))				goto drop;			if (resv->iov_len + 4 > PAGE_SIZE)				goto drop;			svc_putu32(resv, rpc_success);			if (svc_safe_putnetobj(resv, &rsip->out_handle))				goto drop;			if (resv->iov_len + 3 * 4 > PAGE_SIZE)				goto drop;			svc_putu32(resv, htonl(rsip->major_status));			svc_putu32(resv, htonl(rsip->minor_status));			svc_putu32(resv, htonl(GSS_SEQ_WIN));			if (svc_safe_putnetobj(resv, &rsip->out_token))				goto drop;			rqstp->rq_client = NULL;		}		goto complete;	case RPC_GSS_PROC_DESTROY:		set_bit(CACHE_NEGATIVE, &rsci->h.flags);		if (resv->iov_len + 4 > PAGE_SIZE)			goto drop;		svc_putu32(resv, rpc_success);		goto complete;	case RPC_GSS_PROC_DATA:		*authp = rpcsec_gsserr_ctxproblem;		if (gss_write_verf(rqstp, rsci->mechctx, gc->gc_seq))			goto auth_err;		rqstp->rq_cred = rsci->cred;		get_group_info(rsci->cred.cr_group_info);		*authp = rpc_autherr_badcred;		switch (gc->gc_svc) {		case RPC_GSS_SVC_NONE:			break;		case RPC_GSS_SVC_INTEGRITY:			if (unwrap_integ_data(&rqstp->rq_arg,					gc->gc_seq, rsci->mechctx))				goto auth_err;			/* placeholders for length and seq. number: */			svcdata->body_start = resv->iov_base + resv->iov_len;			svc_putu32(resv, 0);			svc_putu32(resv, 0);			break;		case RPC_GSS_SVC_PRIVACY:			/* currently unsupported */		default:			goto auth_err;		}		svcdata->rsci = rsci;		cache_get(&rsci->h);		ret = SVC_OK;		goto out;	}auth_err:	/* Restore write pointer to original value: */	xdr_ressize_check(rqstp, reject_stat);	ret = SVC_DENIED;	goto out;complete:	ret = SVC_COMPLETE;	goto out;drop:	ret = SVC_DROP;out:	if (rsci)		rsc_put(&rsci->h, &rsc_cache);	return ret;}static intsvcauth_gss_release(struct svc_rqst *rqstp){	struct gss_svc_data *gsd = (struct gss_svc_data *)rqstp->rq_auth_data;	struct rpc_gss_wire_cred *gc = &gsd->clcred;	struct xdr_buf *resbuf = &rqstp->rq_res;	struct xdr_buf integ_buf;	struct xdr_netobj mic;	struct kvec *resv;	u32 *p;	int integ_offset, integ_len;	int stat = -EINVAL;	if (gc->gc_proc != RPC_GSS_PROC_DATA)		goto out;	/* Release can be called twice, but we only wrap once. */	if (gsd->body_start == NULL)		goto out;	/* normally not set till svc_send, but we need it here: */	resbuf->len = resbuf->head[0].iov_len		+ resbuf->page_len + resbuf->tail[0].iov_len;	switch (gc->gc_svc) {	case RPC_GSS_SVC_NONE:		break;	case RPC_GSS_SVC_INTEGRITY:		p = gsd->body_start;		gsd->body_start = NULL;		/* move accept_stat to right place: */		memcpy(p, p + 2, 4);		/* don't wrap in failure case: */		/* Note: counting on not getting here if call was not even		 * accepted! */		if (*p != rpc_success) {			resbuf->head[0].iov_len -= 2 * 4;			goto out;		}		p++;		integ_offset = (u8 *)(p + 1) - (u8 *)resbuf->head[0].iov_base;		integ_len = resbuf->len - integ_offset;		BUG_ON(integ_len % 4);		*p++ = htonl(integ_len);		*p++ = htonl(gc->gc_seq);		if (xdr_buf_subsegment(resbuf, &integ_buf, integ_offset,					integ_len))			BUG();		if (resbuf->page_len == 0			&& resbuf->tail[0].iov_len + RPC_MAX_AUTH_SIZE				< PAGE_SIZE) {			BUG_ON(resbuf->tail[0].iov_len);			/* Use head for everything */			resv = &resbuf->head[0];		} else if (resbuf->tail[0].iov_base == NULL) {			/* copied from nfsd4_encode_read */			svc_take_page(rqstp);			resbuf->tail[0].iov_base = page_address(rqstp					->rq_respages[rqstp->rq_resused-1]);			rqstp->rq_restailpage = rqstp->rq_resused-1;			resbuf->tail[0].iov_len = 0;			resv = &resbuf->tail[0];		} else {			resv = &resbuf->tail[0];		}		mic.data = (u8 *)resv->iov_base + resv->iov_len + 4;		if (gss_get_mic(gsd->rsci->mechctx, &integ_buf, &mic))			goto out_err;		svc_putu32(resv, htonl(mic.len));		memset(mic.data + mic.len, 0,				round_up_to_quad(mic.len) - mic.len);		resv->iov_len += XDR_QUADLEN(mic.len) << 2;		/* not strictly required: */		resbuf->len += XDR_QUADLEN(mic.len) << 2;		BUG_ON(resv->iov_len > PAGE_SIZE);		break;	case RPC_GSS_SVC_PRIVACY:	default:		goto out_err;	}out:	stat = 0;out_err:	if (rqstp->rq_client)		auth_domain_put(rqstp->rq_client);	rqstp->rq_client = NULL;	if (rqstp->rq_cred.cr_group_info)		put_group_info(rqstp->rq_cred.cr_group_info);	rqstp->rq_cred.cr_group_info = NULL;	if (gsd->rsci)		rsc_put(&gsd->rsci->h, &rsc_cache);	gsd->rsci = NULL;	return stat;}static voidsvcauth_gss_domain_release(struct auth_domain *dom){	struct gss_domain *gd = container_of(dom, struct gss_domain, h);	kfree(dom->name);	kfree(gd);}static struct auth_ops svcauthops_gss = {	.name		= "rpcsec_gss",	.owner		= THIS_MODULE,	.flavour	= RPC_AUTH_GSS,	.accept		= svcauth_gss_accept,	.release	= svcauth_gss_release,	.domain_release = svcauth_gss_domain_release,	.set_client	= svcauth_gss_set_client,};intgss_svc_init(void){	int rv = svc_auth_register(RPC_AUTH_GSS, &svcauthops_gss);	if (rv == 0) {		cache_register(&rsc_cache);		cache_register(&rsi_cache);	}	return rv;}voidgss_svc_shutdown(void){	if (cache_unregister(&rsc_cache))		printk(KERN_ERR "auth_rpcgss: failed to unregister rsc cache\n");	if (cache_unregister(&rsi_cache))		printk(KERN_ERR "auth_rpcgss: failed to unregister rsi cache\n");	svc_auth_unregister(RPC_AUTH_GSS);}

⌨️ 快捷键说明

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