Commit b68e4c5c authored by Chuck Lever's avatar Chuck Lever
Browse files

SUNRPC: Convert unwrap_integ_data() to use xdr_stream



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

Reviewed-by: default avatarJeff Layton <jlayton@kernel.org>
Signed-off-by: default avatarChuck Lever <chuck.lever@oracle.com>
parent e14673c9
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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);
+31 −16
Original line number Diff line number Diff line
@@ -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 (len & 3)
	if (xdr_stream_decode_u32(xdr, &len) < 0)
		goto unwrap_failed;
	if (len > buf->len)
	if (len & 3)
		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: */
+15 −0
Original line number Diff line number Diff line
@@ -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