Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a0584ee9 authored by Chuck Lever's avatar Chuck Lever Committed by Anna Schumaker
Browse files

SUNRPC: Use struct xdr_stream when decoding RPC Reply header



Modernize and harden the code path that parses an RPC Reply
message.

Signed-off-by: default avatarChuck Lever <chuck.lever@oracle.com>
Signed-off-by: default avatarAnna Schumaker <Anna.Schumaker@Netapp.com>
parent 7f5667a5
Loading
Loading
Loading
Loading
+10 −5
Original line number Original line Diff line number Diff line
@@ -134,11 +134,12 @@ struct rpc_credops {
	int			(*crmarshal)(struct rpc_task *task,
	int			(*crmarshal)(struct rpc_task *task,
					     struct xdr_stream *xdr);
					     struct xdr_stream *xdr);
	int			(*crrefresh)(struct rpc_task *);
	int			(*crrefresh)(struct rpc_task *);
	__be32 *		(*crvalidate)(struct rpc_task *, __be32 *);
	int			(*crvalidate)(struct rpc_task *task,
					      struct xdr_stream *xdr);
	int			(*crwrap_req)(struct rpc_task *task,
	int			(*crwrap_req)(struct rpc_task *task,
					      struct xdr_stream *xdr);
					      struct xdr_stream *xdr);
	int			(*crunwrap_resp)(struct rpc_task *, kxdrdproc_t,
	int			(*crunwrap_resp)(struct rpc_task *task,
						void *, __be32 *, void *);
						 struct xdr_stream *xdr);
	int			(*crkey_timeout)(struct rpc_cred *);
	int			(*crkey_timeout)(struct rpc_cred *);
	char *			(*crstringify_acceptor)(struct rpc_cred *);
	char *			(*crstringify_acceptor)(struct rpc_cred *);
	bool			(*crneed_reencode)(struct rpc_task *);
	bool			(*crneed_reencode)(struct rpc_task *);
@@ -168,12 +169,16 @@ struct rpc_cred * rpcauth_lookupcred(struct rpc_auth *, int);
void			put_rpccred(struct rpc_cred *);
void			put_rpccred(struct rpc_cred *);
int			rpcauth_marshcred(struct rpc_task *task,
int			rpcauth_marshcred(struct rpc_task *task,
					  struct xdr_stream *xdr);
					  struct xdr_stream *xdr);
__be32 *		rpcauth_checkverf(struct rpc_task *, __be32 *);
int			rpcauth_checkverf(struct rpc_task *task,
					  struct xdr_stream *xdr);
int			rpcauth_wrap_req_encode(struct rpc_task *task,
int			rpcauth_wrap_req_encode(struct rpc_task *task,
						struct xdr_stream *xdr);
						struct xdr_stream *xdr);
int			rpcauth_wrap_req(struct rpc_task *task,
int			rpcauth_wrap_req(struct rpc_task *task,
					 struct xdr_stream *xdr);
					 struct xdr_stream *xdr);
int			rpcauth_unwrap_resp(struct rpc_task *task, kxdrdproc_t decode, void *rqstp, __be32 *data, void *obj);
int			rpcauth_unwrap_resp_decode(struct rpc_task *task,
						   struct xdr_stream *xdr);
int			rpcauth_unwrap_resp(struct rpc_task *task,
					    struct xdr_stream *xdr);
bool			rpcauth_xmit_need_reencode(struct rpc_task *task);
bool			rpcauth_xmit_need_reencode(struct rpc_task *task);
int			rpcauth_refreshcred(struct rpc_task *);
int			rpcauth_refreshcred(struct rpc_task *);
void			rpcauth_invalcred(struct rpc_task *);
void			rpcauth_invalcred(struct rpc_task *);
+1 −0
Original line number Original line Diff line number Diff line
@@ -89,6 +89,7 @@ xdr_buf_init(struct xdr_buf *buf, void *start, size_t len)


#define	rpc_auth_null	cpu_to_be32(RPC_AUTH_NULL)
#define	rpc_auth_null	cpu_to_be32(RPC_AUTH_NULL)
#define	rpc_auth_unix	cpu_to_be32(RPC_AUTH_UNIX)
#define	rpc_auth_unix	cpu_to_be32(RPC_AUTH_UNIX)
#define	rpc_auth_short	cpu_to_be32(RPC_AUTH_SHORT)
#define	rpc_auth_gss	cpu_to_be32(RPC_AUTH_GSS)
#define	rpc_auth_gss	cpu_to_be32(RPC_AUTH_GSS)


#define	rpc_call	cpu_to_be32(RPC_CALL)
#define	rpc_call	cpu_to_be32(RPC_CALL)
+41 −22
Original line number Original line Diff line number Diff line
@@ -17,6 +17,8 @@
#include <linux/sunrpc/gss_api.h>
#include <linux/sunrpc/gss_api.h>
#include <linux/spinlock.h>
#include <linux/spinlock.h>


#include <trace/events/sunrpc.h>

#define RPC_CREDCACHE_DEFAULT_HASHBITS	(4)
#define RPC_CREDCACHE_DEFAULT_HASHBITS	(4)
struct rpc_cred_cache {
struct rpc_cred_cache {
	struct hlist_head	*hashtable;
	struct hlist_head	*hashtable;
@@ -773,14 +775,6 @@ int rpcauth_marshcred(struct rpc_task *task, struct xdr_stream *xdr)
	return ops->crmarshal(task, xdr);
	return ops->crmarshal(task, xdr);
}
}


__be32 *
rpcauth_checkverf(struct rpc_task *task, __be32 *p)
{
	struct rpc_cred	*cred = task->tk_rqstp->rq_cred;

	return cred->cr_ops->crvalidate(task, p);
}

/**
/**
 * rpcauth_wrap_req_encode - XDR encode the RPC procedure
 * rpcauth_wrap_req_encode - XDR encode the RPC procedure
 * @task: controlling RPC task
 * @task: controlling RPC task
@@ -814,27 +808,52 @@ int rpcauth_wrap_req(struct rpc_task *task, struct xdr_stream *xdr)
	return ops->crwrap_req(task, xdr);
	return ops->crwrap_req(task, xdr);
}
}


static int
/**
rpcauth_unwrap_req_decode(kxdrdproc_t decode, struct rpc_rqst *rqstp,
 * rpcauth_checkverf - Validate verifier in RPC Reply header
			  __be32 *data, void *obj)
 * @task: controlling RPC task
 * @xdr: xdr_stream containing RPC Reply header
 *
 * On success, @xdr is updated to point past the verifier and
 * zero is returned. Otherwise, @xdr is in an undefined state
 * and a negative errno is returned.
 */
int
rpcauth_checkverf(struct rpc_task *task, struct xdr_stream *xdr)
{
	const struct rpc_credops *ops = task->tk_rqstp->rq_cred->cr_ops;

	return ops->crvalidate(task, xdr);
}

/**
 * rpcauth_unwrap_resp_decode - Invoke XDR decode function
 * @task: controlling RPC task
 * @xdr: stream where the Reply message resides
 *
 * Returns zero on success; otherwise a negative errno is returned.
 */
int
rpcauth_unwrap_resp_decode(struct rpc_task *task, struct xdr_stream *xdr)
{
{
	struct xdr_stream xdr;
	kxdrdproc_t decode = task->tk_msg.rpc_proc->p_decode;


	xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, data, rqstp);
	return decode(task->tk_rqstp, xdr, task->tk_msg.rpc_resp);
	return decode(rqstp, &xdr, obj);
}
}
EXPORT_SYMBOL_GPL(rpcauth_unwrap_resp_decode);


/**
 * rpcauth_unwrap_resp - Invoke unwrap and decode function for the cred
 * @task: controlling RPC task
 * @xdr: stream where the Reply message resides
 *
 * Returns zero on success; otherwise a negative errno is returned.
 */
int
int
rpcauth_unwrap_resp(struct rpc_task *task, kxdrdproc_t decode, void *rqstp,
rpcauth_unwrap_resp(struct rpc_task *task, struct xdr_stream *xdr)
		__be32 *data, void *obj)
{
{
	struct rpc_cred *cred = task->tk_rqstp->rq_cred;
	const struct rpc_credops *ops = task->tk_rqstp->rq_cred->cr_ops;


	if (cred->cr_ops->crunwrap_resp)
	return ops->crunwrap_resp(task, xdr);
		return cred->cr_ops->crunwrap_resp(task, decode, rqstp,
						   data, obj);
	/* By default, we decode the arguments normally. */
	return rpcauth_unwrap_req_decode(decode, rqstp, data, obj);
}
}


bool
bool
+110 −94
Original line number Original line Diff line number Diff line
@@ -1671,59 +1671,62 @@ gss_refresh_null(struct rpc_task *task)
	return 0;
	return 0;
}
}


static __be32 *
static int
gss_validate(struct rpc_task *task, __be32 *p)
gss_validate(struct rpc_task *task, struct xdr_stream *xdr)
{
{
	struct rpc_cred *cred = task->tk_rqstp->rq_cred;
	struct rpc_cred *cred = task->tk_rqstp->rq_cred;
	struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred);
	struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred);
	__be32		*seq = NULL;
	__be32		*p, *seq = NULL;
	struct kvec	iov;
	struct kvec	iov;
	struct xdr_buf	verf_buf;
	struct xdr_buf	verf_buf;
	struct xdr_netobj mic;
	struct xdr_netobj mic;
	u32		flav,len;
	u32		len, maj_stat;
	u32		maj_stat;
	int		status;
	__be32		*ret = ERR_PTR(-EIO);


	dprintk("RPC: %5u %s\n", task->tk_pid, __func__);
	p = xdr_inline_decode(xdr, 2 * sizeof(*p));
	if (!p)
		goto validate_failed;
	if (*p++ != rpc_auth_gss)
		goto validate_failed;
	len = be32_to_cpup(p);
	if (len > RPC_MAX_AUTH_SIZE)
		goto validate_failed;
	p = xdr_inline_decode(xdr, len);
	if (!p)
		goto validate_failed;


	flav = ntohl(*p++);
	if ((len = ntohl(*p++)) > RPC_MAX_AUTH_SIZE)
		goto out_bad;
	if (flav != RPC_AUTH_GSS)
		goto out_bad;
	seq = kmalloc(4, GFP_NOFS);
	seq = kmalloc(4, GFP_NOFS);
	if (!seq)
	if (!seq)
		goto out_bad;
		goto validate_failed;
	*seq = htonl(task->tk_rqstp->rq_seqno);
	*seq = cpu_to_be32(task->tk_rqstp->rq_seqno);
	iov.iov_base = seq;
	iov.iov_base = seq;
	iov.iov_len = 4;
	iov.iov_len = 4;
	xdr_buf_from_iov(&iov, &verf_buf);
	xdr_buf_from_iov(&iov, &verf_buf);
	mic.data = (u8 *)p;
	mic.data = (u8 *)p;
	mic.len = len;
	mic.len = len;

	ret = ERR_PTR(-EACCES);
	maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &verf_buf, &mic);
	maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &verf_buf, &mic);
	if (maj_stat == GSS_S_CONTEXT_EXPIRED)
	if (maj_stat == GSS_S_CONTEXT_EXPIRED)
		clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
		clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
	if (maj_stat) {
	if (maj_stat)
		dprintk("RPC: %5u %s: gss_verify_mic returned error 0x%08x\n",
		goto bad_mic;
			task->tk_pid, __func__, maj_stat);

		goto out_bad;
	}
	/* We leave it to unwrap to calculate au_rslack. For now we just
	/* We leave it to unwrap to calculate au_rslack. For now we just
	 * calculate the length of the verifier: */
	 * calculate the length of the verifier: */
	cred->cr_auth->au_verfsize = XDR_QUADLEN(len) + 2;
	cred->cr_auth->au_verfsize = XDR_QUADLEN(len) + 2;
	status = 0;
out:
	gss_put_ctx(ctx);
	gss_put_ctx(ctx);
	dprintk("RPC: %5u %s: gss_verify_mic succeeded.\n",
			task->tk_pid, __func__);
	kfree(seq);
	return p + XDR_QUADLEN(len);
out_bad:
	gss_put_ctx(ctx);
	dprintk("RPC: %5u %s failed ret %ld.\n", task->tk_pid, __func__,
		PTR_ERR(ret));
	kfree(seq);
	kfree(seq);
	return ret;
	return status;

validate_failed:
	status = -EIO;
	goto out;
bad_mic:
	dprintk("RPC: %5u %s: gss_verify_mic returned error 0x%08x\n",
		task->tk_pid, __func__, maj_stat);
	status = -EACCES;
	goto out;
}
}


static int gss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
static int gss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
@@ -1921,79 +1924,98 @@ static int gss_wrap_req(struct rpc_task *task, struct xdr_stream *xdr)
	return status;
	return status;
}
}


static inline int
static int
gss_unwrap_resp_auth(struct rpc_cred *cred)
{
	cred->cr_auth->au_rslack = cred->cr_auth->au_verfsize;
	return 0;
}

static int
gss_unwrap_resp_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
gss_unwrap_resp_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
		struct rpc_rqst *rqstp, __be32 **p)
		      struct rpc_rqst *rqstp, struct xdr_stream *xdr)
{
{
	struct xdr_buf	*rcv_buf = &rqstp->rq_rcv_buf;
	struct xdr_buf integ_buf, *rcv_buf = &rqstp->rq_rcv_buf;
	struct xdr_buf integ_buf;
	u32 data_offset, mic_offset, integ_len, maj_stat;
	struct xdr_netobj mic;
	struct xdr_netobj mic;
	u32 data_offset, mic_offset;
	__be32 *p;
	u32 integ_len;
	u32 maj_stat;
	int status = -EIO;


	integ_len = ntohl(*(*p)++);
	p = xdr_inline_decode(xdr, 2 * sizeof(*p));
	if (unlikely(!p))
		goto unwrap_failed;
	integ_len = be32_to_cpup(p++);
	if (integ_len & 3)
	if (integ_len & 3)
		return status;
		goto unwrap_failed;
	data_offset = (u8 *)(*p) - (u8 *)rcv_buf->head[0].iov_base;
	data_offset = (u8 *)(p) - (u8 *)rcv_buf->head[0].iov_base;
	mic_offset = integ_len + data_offset;
	mic_offset = integ_len + data_offset;
	if (mic_offset > rcv_buf->len)
	if (mic_offset > rcv_buf->len)
		return status;
		goto unwrap_failed;
	if (ntohl(*(*p)++) != rqstp->rq_seqno)
	if (be32_to_cpup(p) != rqstp->rq_seqno)
		return status;
		goto unwrap_failed;

	if (xdr_buf_subsegment(rcv_buf, &integ_buf, data_offset,
				mic_offset - data_offset))
		return status;


	if (xdr_buf_subsegment(rcv_buf, &integ_buf, data_offset, integ_len))
		goto unwrap_failed;
	if (xdr_buf_read_netobj(rcv_buf, &mic, mic_offset))
	if (xdr_buf_read_netobj(rcv_buf, &mic, mic_offset))
		return status;
		goto unwrap_failed;

	maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &integ_buf, &mic);
	maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &integ_buf, &mic);
	if (maj_stat == GSS_S_CONTEXT_EXPIRED)
	if (maj_stat == GSS_S_CONTEXT_EXPIRED)
		clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
		clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
	if (maj_stat != GSS_S_COMPLETE)
	if (maj_stat != GSS_S_COMPLETE)
		return status;
		goto bad_mic;

	cred->cr_auth->au_rslack = cred->cr_auth->au_verfsize + 2 +
				   1 + XDR_QUADLEN(mic.len);
	return 0;
	return 0;
unwrap_failed:
	return -EIO;
bad_mic:
	dprintk("RPC:       %s: gss_verify_mic returned error 0x%08x\n",
		__func__, maj_stat);
	return -EIO;
}
}


static inline int
static int
gss_unwrap_resp_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
gss_unwrap_resp_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
		struct rpc_rqst *rqstp, __be32 **p)
		     struct rpc_rqst *rqstp, struct xdr_stream *xdr)
{
{
	struct xdr_buf *rcv_buf = &rqstp->rq_rcv_buf;
	struct xdr_buf *rcv_buf = &rqstp->rq_rcv_buf;
	u32 offset;
	struct kvec *head = rqstp->rq_rcv_buf.head;
	u32 opaque_len;
	unsigned int savedlen = rcv_buf->len;
	u32 maj_stat;
	u32 offset, opaque_len, maj_stat;
	int status = -EIO;
	__be32 *p;


	opaque_len = ntohl(*(*p)++);
	p = xdr_inline_decode(xdr, 2 * sizeof(*p));
	offset = (u8 *)(*p) - (u8 *)rcv_buf->head[0].iov_base;
	if (unlikely(!p))
		goto unwrap_failed;
	opaque_len = be32_to_cpup(p++);
	offset = (u8 *)(p) - (u8 *)head->iov_base;
	if (offset + opaque_len > rcv_buf->len)
	if (offset + opaque_len > rcv_buf->len)
		return status;
		goto unwrap_failed;
	/* remove padding: */
	rcv_buf->len = offset + opaque_len;
	rcv_buf->len = offset + opaque_len;


	maj_stat = gss_unwrap(ctx->gc_gss_ctx, offset, rcv_buf);
	maj_stat = gss_unwrap(ctx->gc_gss_ctx, offset, rcv_buf);
	if (maj_stat == GSS_S_CONTEXT_EXPIRED)
	if (maj_stat == GSS_S_CONTEXT_EXPIRED)
		clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
		clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
	if (maj_stat != GSS_S_COMPLETE)
	if (maj_stat != GSS_S_COMPLETE)
		return status;
		goto bad_unwrap;
	if (ntohl(*(*p)++) != rqstp->rq_seqno)
	/* gss_unwrap decrypted the sequence number */
		return status;
	if (be32_to_cpup(p++) != rqstp->rq_seqno)
		goto unwrap_failed;


	return 0;
	/* gss_unwrap redacts the opaque blob from the head iovec.
}
	 * rcv_buf has changed, thus the stream needs to be reset.

	 */
static int
	xdr_init_decode(xdr, rcv_buf, p, rqstp);
gss_unwrap_req_decode(kxdrdproc_t decode, struct rpc_rqst *rqstp,
		      __be32 *p, void *obj)
{
	struct xdr_stream xdr;


	xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p, rqstp);
	cred->cr_auth->au_rslack = cred->cr_auth->au_verfsize + 2 +
	return decode(rqstp, &xdr, obj);
				   XDR_QUADLEN(savedlen - rcv_buf->len);
	return 0;
unwrap_failed:
	return -EIO;
bad_unwrap:
	dprintk("RPC:       %s: gss_unwrap returned error 0x%08x\n",
		__func__, maj_stat);
	return -EIO;
}
}


static bool
static bool
@@ -2037,39 +2059,33 @@ gss_xmit_need_reencode(struct rpc_task *task)
}
}


static int
static int
gss_unwrap_resp(struct rpc_task *task,
gss_unwrap_resp(struct rpc_task *task, struct xdr_stream *xdr)
		kxdrdproc_t decode, void *rqstp, __be32 *p, void *obj)
{
{
	struct rpc_cred *cred = task->tk_rqstp->rq_cred;
	struct rpc_rqst *rqstp = task->tk_rqstp;
	struct rpc_cred *cred = rqstp->rq_cred;
	struct gss_cred *gss_cred = container_of(cred, struct gss_cred,
	struct gss_cred *gss_cred = container_of(cred, struct gss_cred,
			gc_base);
			gc_base);
	struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred);
	struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred);
	__be32		*savedp = p;
	struct kvec	*head = ((struct rpc_rqst *)rqstp)->rq_rcv_buf.head;
	int		savedlen = head->iov_len;
	int status = -EIO;
	int status = -EIO;


	if (ctx->gc_proc != RPC_GSS_PROC_DATA)
	if (ctx->gc_proc != RPC_GSS_PROC_DATA)
		goto out_decode;
		goto out_decode;
	switch (gss_cred->gc_service) {
	switch (gss_cred->gc_service) {
	case RPC_GSS_SVC_NONE:
	case RPC_GSS_SVC_NONE:
		status = gss_unwrap_resp_auth(cred);
		break;
		break;
	case RPC_GSS_SVC_INTEGRITY:
	case RPC_GSS_SVC_INTEGRITY:
		status = gss_unwrap_resp_integ(cred, ctx, rqstp, &p);
		status = gss_unwrap_resp_integ(cred, ctx, rqstp, xdr);
		if (status)
			goto out;
		break;
		break;
	case RPC_GSS_SVC_PRIVACY:
	case RPC_GSS_SVC_PRIVACY:
		status = gss_unwrap_resp_priv(cred, ctx, rqstp, &p);
		status = gss_unwrap_resp_priv(cred, ctx, rqstp, xdr);
		if (status)
			goto out;
		break;
		break;
	}
	}
	/* take into account extra slack for integrity and privacy cases: */
	if (status)
	cred->cr_auth->au_rslack = cred->cr_auth->au_verfsize + (p - savedp)
		goto out;
						+ (savedlen - head->iov_len);

out_decode:
out_decode:
	status = gss_unwrap_req_decode(decode, rqstp, p, obj);
	status = rpcauth_unwrap_resp_decode(task, xdr);
out:
out:
	gss_put_ctx(ctx);
	gss_put_ctx(ctx);
	dprintk("RPC: %5u %s returning %d\n",
	dprintk("RPC: %5u %s returning %d\n",
+13 −18
Original line number Original line Diff line number Diff line
@@ -86,25 +86,19 @@ nul_refresh(struct rpc_task *task)
	return 0;
	return 0;
}
}


static __be32 *
static int
nul_validate(struct rpc_task *task, __be32 *p)
nul_validate(struct rpc_task *task, struct xdr_stream *xdr)
{
{
	rpc_authflavor_t	flavor;
	__be32 *p;
	u32			size;

	flavor = ntohl(*p++);
	if (flavor != RPC_AUTH_NULL) {
		printk("RPC: bad verf flavor: %u\n", flavor);
		return ERR_PTR(-EIO);
	}

	size = ntohl(*p++);
	if (size != 0) {
		printk("RPC: bad verf size: %u\n", size);
		return ERR_PTR(-EIO);
	}


	return p;
	p = xdr_inline_decode(xdr, 2 * sizeof(*p));
	if (!p)
		return -EIO;
	if (*p++ != rpc_auth_null)
		return -EIO;
	if (*p != xdr_zero)
		return -EIO;
	return 0;
}
}


const struct rpc_authops authnull_ops = {
const struct rpc_authops authnull_ops = {
@@ -134,6 +128,7 @@ const struct rpc_credops null_credops = {
	.crwrap_req	= rpcauth_wrap_req_encode,
	.crwrap_req	= rpcauth_wrap_req_encode,
	.crrefresh	= nul_refresh,
	.crrefresh	= nul_refresh,
	.crvalidate	= nul_validate,
	.crvalidate	= nul_validate,
	.crunwrap_resp	= rpcauth_unwrap_resp_decode,
};
};


static
static
Loading