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: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
This commit is contained in:
Chuck Lever 2019-02-11 11:24:58 -05:00 committed by Anna Schumaker
parent 7f5667a5f8
commit a0584ee9ae
7 changed files with 241 additions and 199 deletions

View File

@ -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 *);

View File

@ -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)

View File

@ -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)
{ {
struct xdr_stream xdr; const struct rpc_credops *ops = task->tk_rqstp->rq_cred->cr_ops;
xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, data, rqstp); return ops->crvalidate(task, xdr);
return decode(rqstp, &xdr, obj);
} }
/**
* 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 int
rpcauth_unwrap_resp(struct rpc_task *task, kxdrdproc_t decode, void *rqstp, rpcauth_unwrap_resp_decode(struct rpc_task *task, struct xdr_stream *xdr)
__be32 *data, void *obj)
{ {
struct rpc_cred *cred = task->tk_rqstp->rq_cred; kxdrdproc_t decode = task->tk_msg.rpc_proc->p_decode;
if (cred->cr_ops->crunwrap_resp) return decode(task->tk_rqstp, xdr, task->tk_msg.rpc_resp);
return cred->cr_ops->crunwrap_resp(task, decode, rqstp, }
data, obj); EXPORT_SYMBOL_GPL(rpcauth_unwrap_resp_decode);
/* By default, we decode the arguments normally. */
return rpcauth_unwrap_req_decode(decode, rqstp, data, obj); /**
* 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
rpcauth_unwrap_resp(struct rpc_task *task, struct xdr_stream *xdr)
{
const struct rpc_credops *ops = task->tk_rqstp->rq_cred->cr_ops;
return ops->crunwrap_resp(task, xdr);
} }
bool bool

View File

@ -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); kfree(seq);
return p + XDR_QUADLEN(len); return status;
out_bad:
gss_put_ctx(ctx); validate_failed:
dprintk("RPC: %5u %s failed ret %ld.\n", task->tk_pid, __func__, status = -EIO;
PTR_ERR(ret)); goto out;
kfree(seq); bad_mic:
return ret; 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 @@ out:
return status; return status;
} }
static inline int static int
gss_unwrap_resp_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx, gss_unwrap_resp_auth(struct rpc_cred *cred)
struct rpc_rqst *rqstp, __be32 **p)
{ {
struct xdr_buf *rcv_buf = &rqstp->rq_rcv_buf; cred->cr_auth->au_rslack = cred->cr_auth->au_verfsize;
struct xdr_buf integ_buf; return 0;
struct xdr_netobj mic; }
u32 data_offset, mic_offset;
u32 integ_len;
u32 maj_stat;
int status = -EIO;
integ_len = ntohl(*(*p)++); static int
gss_unwrap_resp_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
struct rpc_rqst *rqstp, struct xdr_stream *xdr)
{
struct xdr_buf integ_buf, *rcv_buf = &rqstp->rq_rcv_buf;
u32 data_offset, mic_offset, integ_len, maj_stat;
struct xdr_netobj mic;
__be32 *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;
/* gss_unwrap redacts the opaque blob from the head iovec.
* rcv_buf has changed, thus the stream needs to be reset.
*/
xdr_init_decode(xdr, rcv_buf, p, rqstp);
cred->cr_auth->au_rslack = cred->cr_auth->au_verfsize + 2 +
XDR_QUADLEN(savedlen - rcv_buf->len);
return 0; return 0;
} unwrap_failed:
return -EIO;
static int bad_unwrap:
gss_unwrap_req_decode(kxdrdproc_t decode, struct rpc_rqst *rqstp, dprintk("RPC: %s: gss_unwrap returned error 0x%08x\n",
__be32 *p, void *obj) __func__, maj_stat);
{ return -EIO;
struct xdr_stream xdr;
xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p, rqstp);
return decode(rqstp, &xdr, obj);
} }
static bool static bool
@ -2037,39 +2059,33 @@ out:
} }
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; int status = -EIO;
struct kvec *head = ((struct rpc_rqst *)rqstp)->rq_rcv_buf.head;
int savedlen = head->iov_len;
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",

View File

@ -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++); p = xdr_inline_decode(xdr, 2 * sizeof(*p));
if (flavor != RPC_AUTH_NULL) { if (!p)
printk("RPC: bad verf flavor: %u\n", flavor); return -EIO;
return ERR_PTR(-EIO); if (*p++ != rpc_auth_null)
} return -EIO;
if (*p != xdr_zero)
size = ntohl(*p++); return -EIO;
if (size != 0) { return 0;
printk("RPC: bad verf size: %u\n", size);
return ERR_PTR(-EIO);
}
return p;
} }
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

View File

@ -160,29 +160,32 @@ unx_refresh(struct rpc_task *task)
return 0; return 0;
} }
static __be32 * static int
unx_validate(struct rpc_task *task, __be32 *p) unx_validate(struct rpc_task *task, struct xdr_stream *xdr)
{ {
rpc_authflavor_t flavor; __be32 *p;
u32 size; u32 size;
flavor = ntohl(*p++); p = xdr_inline_decode(xdr, 2 * sizeof(*p));
if (flavor != RPC_AUTH_NULL && if (!p)
flavor != RPC_AUTH_UNIX && return -EIO;
flavor != RPC_AUTH_SHORT) { switch (*p++) {
printk("RPC: bad verf flavor: %u\n", flavor); case rpc_auth_null:
return ERR_PTR(-EIO); case rpc_auth_unix:
case rpc_auth_short:
break;
default:
return -EIO;
} }
size = be32_to_cpup(p);
if (size > RPC_MAX_AUTH_SIZE)
return -EIO;
p = xdr_inline_decode(xdr, size);
if (!p)
return -EIO;
size = ntohl(*p++);
if (size > RPC_MAX_AUTH_SIZE) {
printk("RPC: giant verf size: %u\n", size);
return ERR_PTR(-EIO);
}
task->tk_rqstp->rq_cred->cr_auth->au_rslack = (size >> 2) + 2; task->tk_rqstp->rq_cred->cr_auth->au_rslack = (size >> 2) + 2;
p += (size >> 2); return 0;
return p;
} }
int __init rpc_init_authunix(void) int __init rpc_init_authunix(void)
@ -223,4 +226,5 @@ const struct rpc_credops unix_credops = {
.crwrap_req = rpcauth_wrap_req_encode, .crwrap_req = rpcauth_wrap_req_encode,
.crrefresh = unx_refresh, .crrefresh = unx_refresh,
.crvalidate = unx_validate, .crvalidate = unx_validate,
.crunwrap_resp = rpcauth_unwrap_resp_decode,
}; };

View File

@ -79,7 +79,8 @@ static void call_connect_status(struct rpc_task *task);
static int rpc_encode_header(struct rpc_task *task, static int rpc_encode_header(struct rpc_task *task,
struct xdr_stream *xdr); struct xdr_stream *xdr);
static __be32 *rpc_decode_header(struct rpc_task *task); static int rpc_decode_header(struct rpc_task *task,
struct xdr_stream *xdr);
static int rpc_ping(struct rpc_clnt *clnt); static int rpc_ping(struct rpc_clnt *clnt);
static void rpc_register_client(struct rpc_clnt *clnt) static void rpc_register_client(struct rpc_clnt *clnt)
@ -2251,12 +2252,11 @@ call_decode(struct rpc_task *task)
{ {
struct rpc_clnt *clnt = task->tk_client; struct rpc_clnt *clnt = task->tk_client;
struct rpc_rqst *req = task->tk_rqstp; struct rpc_rqst *req = task->tk_rqstp;
kxdrdproc_t decode = task->tk_msg.rpc_proc->p_decode; struct xdr_stream xdr;
__be32 *p;
dprint_status(task); dprint_status(task);
if (!decode) { if (!task->tk_msg.rpc_proc->p_decode) {
task->tk_action = rpc_exit_task; task->tk_action = rpc_exit_task;
return; return;
} }
@ -2292,29 +2292,27 @@ call_decode(struct rpc_task *task)
goto out_retry; goto out_retry;
} }
p = rpc_decode_header(task); xdr_init_decode(&xdr, &req->rq_rcv_buf,
if (IS_ERR(p)) { req->rq_rcv_buf.head[0].iov_base, req);
if (p == ERR_PTR(-EAGAIN)) switch (rpc_decode_header(task, &xdr)) {
goto out_retry; case 0:
task->tk_action = rpc_exit_task;
task->tk_status = rpcauth_unwrap_resp(task, &xdr);
dprintk("RPC: %5u %s result %d\n",
task->tk_pid, __func__, task->tk_status);
return; return;
} case -EAGAIN:
task->tk_action = rpc_exit_task;
task->tk_status = rpcauth_unwrap_resp(task, decode, req, p,
task->tk_msg.rpc_resp);
dprintk("RPC: %5u call_decode result %d\n", task->tk_pid,
task->tk_status);
return;
out_retry: out_retry:
task->tk_status = 0; task->tk_status = 0;
/* Note: rpc_decode_header() may have freed the RPC slot */ /* Note: rpc_decode_header() may have freed the RPC slot */
if (task->tk_rqstp == req) { if (task->tk_rqstp == req) {
xdr_free_bvec(&req->rq_rcv_buf); xdr_free_bvec(&req->rq_rcv_buf);
req->rq_reply_bytes_recvd = req->rq_rcv_buf.len = 0; req->rq_reply_bytes_recvd = 0;
if (task->tk_client->cl_discrtry) req->rq_rcv_buf.len = 0;
xprt_conditional_disconnect(req->rq_xprt, if (task->tk_client->cl_discrtry)
req->rq_connect_cookie); xprt_conditional_disconnect(req->rq_xprt,
req->rq_connect_cookie);
}
} }
} }
@ -2347,14 +2345,12 @@ out_fail:
return error; return error;
} }
static noinline __be32 * static noinline int
rpc_decode_header(struct rpc_task *task) rpc_decode_header(struct rpc_task *task, struct xdr_stream *xdr)
{ {
struct rpc_clnt *clnt = task->tk_client; struct rpc_clnt *clnt = task->tk_client;
struct kvec *iov = &task->tk_rqstp->rq_rcv_buf.head[0];
int len = task->tk_rqstp->rq_rcv_buf.len >> 2;
__be32 *p = iov->iov_base;
int error = -EACCES; int error = -EACCES;
__be32 *p;
/* RFC-1014 says that the representation of XDR data must be a /* RFC-1014 says that the representation of XDR data must be a
* multiple of four bytes * multiple of four bytes
@ -2363,25 +2359,26 @@ rpc_decode_header(struct rpc_task *task)
*/ */
if (task->tk_rqstp->rq_rcv_buf.len & 3) if (task->tk_rqstp->rq_rcv_buf.len & 3)
goto out_badlen; goto out_badlen;
if ((len -= 3) < 0)
goto out_unparsable;
p = xdr_inline_decode(xdr, 3 * sizeof(*p));
if (!p)
goto out_unparsable;
p++; /* skip XID */ p++; /* skip XID */
if (*p++ != rpc_reply) if (*p++ != rpc_reply)
goto out_unparsable; goto out_unparsable;
if (*p++ != rpc_msg_accepted) if (*p++ != rpc_msg_accepted)
goto out_msg_denied; goto out_msg_denied;
p = rpcauth_checkverf(task, p); error = rpcauth_checkverf(task, xdr);
if (IS_ERR(p)) if (error)
goto out_verifier; goto out_verifier;
len = p - (__be32 *)iov->iov_base - 1; p = xdr_inline_decode(xdr, sizeof(*p));
if (len < 0) if (!p)
goto out_unparsable; goto out_unparsable;
switch (*p++) { switch (*p) {
case rpc_success: case rpc_success:
return p; return 0;
case rpc_prog_unavail: case rpc_prog_unavail:
trace_rpc__prog_unavail(task); trace_rpc__prog_unavail(task);
error = -EPFNOSUPPORT; error = -EPFNOSUPPORT;
@ -2406,11 +2403,11 @@ out_garbage:
if (task->tk_garb_retry) { if (task->tk_garb_retry) {
task->tk_garb_retry--; task->tk_garb_retry--;
task->tk_action = call_encode; task->tk_action = call_encode;
return ERR_PTR(-EAGAIN); return -EAGAIN;
} }
out_err: out_err:
rpc_exit(task, error); rpc_exit(task, error);
return ERR_PTR(error); return error;
out_badlen: out_badlen:
trace_rpc__unparsable(task); trace_rpc__unparsable(task);
@ -2424,10 +2421,12 @@ out_unparsable:
out_verifier: out_verifier:
trace_rpc_bad_verifier(task); trace_rpc_bad_verifier(task);
error = PTR_ERR(p);
goto out_garbage; goto out_garbage;
out_msg_denied: out_msg_denied:
p = xdr_inline_decode(xdr, sizeof(*p));
if (!p)
goto out_unparsable;
switch (*p++) { switch (*p++) {
case rpc_auth_error: case rpc_auth_error:
break; break;
@ -2441,6 +2440,9 @@ out_msg_denied:
goto out_err; goto out_err;
} }
p = xdr_inline_decode(xdr, sizeof(*p));
if (!p)
goto out_unparsable;
switch (*p++) { switch (*p++) {
case rpc_autherr_rejectedcred: case rpc_autherr_rejectedcred:
case rpc_autherr_rejectedverf: case rpc_autherr_rejectedverf:
@ -2454,7 +2456,7 @@ out_msg_denied:
/* Ensure we obtain a new XID! */ /* Ensure we obtain a new XID! */
xprt_release(task); xprt_release(task);
task->tk_action = call_reserve; task->tk_action = call_reserve;
return ERR_PTR(-EAGAIN); return -EAGAIN;
case rpc_autherr_badcred: case rpc_autherr_badcred:
case rpc_autherr_badverf: case rpc_autherr_badverf:
/* possibly garbled cred/verf? */ /* possibly garbled cred/verf? */
@ -2463,7 +2465,7 @@ out_msg_denied:
task->tk_garb_retry--; task->tk_garb_retry--;
trace_rpc__bad_creds(task); trace_rpc__bad_creds(task);
task->tk_action = call_encode; task->tk_action = call_encode;
return ERR_PTR(-EAGAIN); return -EAGAIN;
case rpc_autherr_tooweak: case rpc_autherr_tooweak:
trace_rpc__auth_tooweak(task); trace_rpc__auth_tooweak(task);
pr_warn("RPC: server %s requires stronger authentication.\n", pr_warn("RPC: server %s requires stronger authentication.\n",