Commit 4dd9daa9 authored by Chuck Lever's avatar Chuck Lever Committed by Chuck Lever

SUNRPC: Fix crasher in unwrap_integ_data()

If a zero length is passed to kmalloc() it returns 0x10, which is
not a valid address. gss_verify_mic() subsequently crashes when it
attempts to dereference that pointer.

Instead of allocating this memory on every call based on an
untrusted size value, use a piece of dynamically-allocated scratch
memory that is always available.
Signed-off-by: default avatarChuck Lever <chuck.lever@oracle.com>
Reviewed-by: default avatarJeff Layton <jlayton@kernel.org>
parent c65d9df0
...@@ -49,11 +49,36 @@ ...@@ -49,11 +49,36 @@
#include <linux/sunrpc/svcauth.h> #include <linux/sunrpc/svcauth.h>
#include <linux/sunrpc/svcauth_gss.h> #include <linux/sunrpc/svcauth_gss.h>
#include <linux/sunrpc/cache.h> #include <linux/sunrpc/cache.h>
#include <linux/sunrpc/gss_krb5.h>
#include <trace/events/rpcgss.h> #include <trace/events/rpcgss.h>
#include "gss_rpc_upcall.h" #include "gss_rpc_upcall.h"
/*
* Unfortunately there isn't a maximum checksum size exported via the
* GSS API. Manufacture one based on GSS mechanisms supported by this
* implementation.
*/
#define GSS_MAX_CKSUMSIZE (GSS_KRB5_TOK_HDR_LEN + GSS_KRB5_MAX_CKSUM_LEN)
/*
* This value may be increased in the future to accommodate other
* usage of the scratch buffer.
*/
#define GSS_SCRATCH_SIZE GSS_MAX_CKSUMSIZE
struct gss_svc_data {
/* decoded gss client cred: */
struct rpc_gss_wire_cred clcred;
/* save a pointer to the beginning of the encoded verifier,
* for use in encryption/checksumming in svcauth_gss_release: */
__be32 *verf_start;
struct rsc *rsci;
/* for temporary results */
u8 gsd_scratch[GSS_SCRATCH_SIZE];
};
/* The rpcsec_init cache is used for mapping RPCSEC_GSS_{,CONT_}INIT requests /* The rpcsec_init cache is used for mapping RPCSEC_GSS_{,CONT_}INIT requests
* into replies. * into replies.
...@@ -887,13 +912,11 @@ read_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj) ...@@ -887,13 +912,11 @@ read_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj)
static int static int
unwrap_integ_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gss_ctx *ctx) unwrap_integ_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gss_ctx *ctx)
{ {
struct gss_svc_data *gsd = rqstp->rq_auth_data;
u32 integ_len, rseqno, maj_stat; u32 integ_len, rseqno, maj_stat;
int stat = -EINVAL;
struct xdr_netobj mic; struct xdr_netobj mic;
struct xdr_buf integ_buf; struct xdr_buf integ_buf;
mic.data = NULL;
/* NFS READ normally uses splice to send data in-place. However /* NFS READ normally uses splice to send data in-place. However
* the data in cache can change after the reply's MIC is computed * the data in cache can change after the reply's MIC is computed
* but before the RPC reply is sent. To prevent the client from * but before the RPC reply is sent. To prevent the client from
...@@ -917,11 +940,9 @@ unwrap_integ_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct g ...@@ -917,11 +940,9 @@ unwrap_integ_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct g
/* copy out mic... */ /* copy out mic... */
if (read_u32_from_xdr_buf(buf, integ_len, &mic.len)) if (read_u32_from_xdr_buf(buf, integ_len, &mic.len))
goto unwrap_failed; goto unwrap_failed;
if (mic.len > RPC_MAX_AUTH_SIZE) if (mic.len > sizeof(gsd->gsd_scratch))
goto unwrap_failed;
mic.data = kmalloc(mic.len, GFP_KERNEL);
if (!mic.data)
goto unwrap_failed; goto unwrap_failed;
mic.data = gsd->gsd_scratch;
if (read_bytes_from_xdr_buf(buf, integ_len + 4, mic.data, mic.len)) if (read_bytes_from_xdr_buf(buf, integ_len + 4, mic.data, mic.len))
goto unwrap_failed; goto unwrap_failed;
maj_stat = gss_verify_mic(ctx, &integ_buf, &mic); maj_stat = gss_verify_mic(ctx, &integ_buf, &mic);
...@@ -932,20 +953,17 @@ unwrap_integ_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct g ...@@ -932,20 +953,17 @@ unwrap_integ_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct g
goto bad_seqno; goto bad_seqno;
/* trim off the mic and padding at the end before returning */ /* trim off the mic and padding at the end before returning */
xdr_buf_trim(buf, round_up_to_quad(mic.len) + 4); xdr_buf_trim(buf, round_up_to_quad(mic.len) + 4);
stat = 0; return 0;
out:
kfree(mic.data);
return stat;
unwrap_failed: unwrap_failed:
trace_rpcgss_svc_unwrap_failed(rqstp); trace_rpcgss_svc_unwrap_failed(rqstp);
goto out; return -EINVAL;
bad_seqno: bad_seqno:
trace_rpcgss_svc_seqno_bad(rqstp, seq, rseqno); trace_rpcgss_svc_seqno_bad(rqstp, seq, rseqno);
goto out; return -EINVAL;
bad_mic: bad_mic:
trace_rpcgss_svc_mic(rqstp, maj_stat); trace_rpcgss_svc_mic(rqstp, maj_stat);
goto out; return -EINVAL;
} }
static inline int static inline int
...@@ -1023,15 +1041,6 @@ unwrap_priv_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gs ...@@ -1023,15 +1041,6 @@ unwrap_priv_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gs
return -EINVAL; return -EINVAL;
} }
struct gss_svc_data {
/* decoded gss client cred: */
struct rpc_gss_wire_cred clcred;
/* save a pointer to the beginning of the encoded verifier,
* for use in encryption/checksumming in svcauth_gss_release: */
__be32 *verf_start;
struct rsc *rsci;
};
static int static int
svcauth_gss_set_client(struct svc_rqst *rqstp) svcauth_gss_set_client(struct svc_rqst *rqstp)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment