mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-30 23:54:04 +08:00
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:
parent
7f5667a5f8
commit
a0584ee9ae
@ -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 *);
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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",
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
};
|
};
|
||||||
|
@ -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",
|
||||||
|
Loading…
Reference in New Issue
Block a user