SUNRPC: Convert unwrap_integ_data() to use xdr_stream

Done as part of hardening the server-side RPC header decoding path.

Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
This commit is contained in:
Chuck Lever 2023-01-02 12:06:54 -05:00
parent e14673c9c1
commit b68e4c5c32
3 changed files with 47 additions and 16 deletions

View file

@ -247,6 +247,7 @@ extern int xdr_reserve_space_vec(struct xdr_stream *xdr, struct kvec *vec,
size_t nbytes);
extern void __xdr_commit_encode(struct xdr_stream *xdr);
extern void xdr_truncate_encode(struct xdr_stream *xdr, size_t len);
extern void xdr_truncate_decode(struct xdr_stream *xdr, size_t len);
extern int xdr_restrict_buflen(struct xdr_stream *xdr, int newbuflen);
extern void xdr_write_pages(struct xdr_stream *xdr, struct page **pages,
unsigned int base, unsigned int len);

View file

@ -904,13 +904,14 @@ EXPORT_SYMBOL_GPL(svcauth_gss_register_pseudoflavor);
* proc_req_arg_t arg;
* };
*/
static int
svcauth_gss_unwrap_integ(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq,
struct gss_ctx *ctx)
static noinline_for_stack int
svcauth_gss_unwrap_integ(struct svc_rqst *rqstp, u32 seq, struct gss_ctx *ctx)
{
struct gss_svc_data *gsd = rqstp->rq_auth_data;
struct xdr_stream *xdr = &rqstp->rq_arg_stream;
u32 len, offset, seq_num, maj_stat;
struct xdr_buf *buf = xdr->buf;
struct xdr_buf databody_integ;
u32 len, seq_num, maj_stat;
struct xdr_netobj checksum;
/* NFS READ normally uses splice to send data in-place. However
@ -925,29 +926,43 @@ svcauth_gss_unwrap_integ(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq,
if (rqstp->rq_deferred)
return 0;
len = svc_getnl(&buf->head[0]);
if (xdr_stream_decode_u32(xdr, &len) < 0)
goto unwrap_failed;
if (len & 3)
goto unwrap_failed;
if (len > buf->len)
goto unwrap_failed;
if (xdr_buf_subsegment(buf, &databody_integ, 0, len))
offset = xdr_stream_pos(xdr);
if (xdr_buf_subsegment(buf, &databody_integ, offset, len))
goto unwrap_failed;
if (xdr_decode_word(buf, len, &checksum.len))
/*
* The xdr_stream now points to the @seq_num field. The next
* XDR data item is the @arg field, which contains the clear
* text RPC program payload. The checksum, which follows the
* @arg field, is located and decoded without updating the
* xdr_stream.
*/
offset += len;
if (xdr_decode_word(buf, offset, &checksum.len))
goto unwrap_failed;
if (checksum.len > sizeof(gsd->gsd_scratch))
goto unwrap_failed;
checksum.data = gsd->gsd_scratch;
if (read_bytes_from_xdr_buf(buf, len + 4, checksum.data, checksum.len))
if (read_bytes_from_xdr_buf(buf, offset + XDR_UNIT, checksum.data,
checksum.len))
goto unwrap_failed;
maj_stat = gss_verify_mic(ctx, &databody_integ, &checksum);
if (maj_stat != GSS_S_COMPLETE)
goto bad_mic;
seq_num = svc_getnl(&buf->head[0]);
/* The received seqno is protected by the checksum. */
if (xdr_stream_decode_u32(xdr, &seq_num) < 0)
goto unwrap_failed;
if (seq_num != seq)
goto bad_seqno;
/* trim off the mic and padding at the end before returning */
xdr_buf_trim(buf, round_up_to_quad(checksum.len) + 4);
xdr_truncate_decode(xdr, XDR_UNIT + checksum.len);
return 0;
unwrap_failed:
@ -1652,11 +1667,11 @@ svcauth_gss_accept(struct svc_rqst *rqstp)
/* placeholders for length and seq. number: */
svc_putnl(resv, 0);
svc_putnl(resv, 0);
if (svcauth_gss_unwrap_integ(rqstp, &rqstp->rq_arg,
gc->gc_seq, rsci->mechctx))
svcxdr_init_decode(rqstp);
if (svcauth_gss_unwrap_integ(rqstp, gc->gc_seq,
rsci->mechctx))
goto garbage_args;
rqstp->rq_auth_slack = RPC_MAX_AUTH_SIZE;
svcxdr_init_decode(rqstp);
break;
case RPC_GSS_SVC_PRIVACY:
/* placeholders for length and seq. number: */

View file

@ -1192,6 +1192,21 @@ void xdr_truncate_encode(struct xdr_stream *xdr, size_t len)
}
EXPORT_SYMBOL(xdr_truncate_encode);
/**
* xdr_truncate_decode - Truncate a decoding stream
* @xdr: pointer to struct xdr_stream
* @len: Number of bytes to remove
*
*/
void xdr_truncate_decode(struct xdr_stream *xdr, size_t len)
{
unsigned int nbytes = xdr_align_size(len);
xdr->buf->len -= nbytes;
xdr->nwords -= XDR_QUADLEN(nbytes);
}
EXPORT_SYMBOL_GPL(xdr_truncate_decode);
/**
* xdr_restrict_buflen - decrease available buffer space
* @xdr: pointer to xdr_stream