Skip to content

Commit 4c190e2

Browse files
jtlaytonJ. Bruce Fields
authored andcommitted
sunrpc: trim off trailing checksum before returning decrypted or integrity authenticated buffer
When GSSAPI integrity signatures are in use, or when we're using GSSAPI privacy with the v2 token format, there is a trailing checksum on the xdr_buf that is returned. It's checked during the authentication stage, and afterward nothing cares about it. Ordinarily, it's not a problem since the XDR code generally ignores it, but it will be when we try to compute a checksum over the buffer to help prevent XID collisions in the duplicate reply cache. Fix the code to trim off the checksums after verifying them. Note that in unwrap_integ_data, we must avoid trying to reverify the checksum if the request was deferred since it will no longer be present when it's revisited. Signed-off-by: Jeff Layton <[email protected]>
1 parent de0b65c commit 4c190e2

File tree

4 files changed

+52
-2
lines changed

4 files changed

+52
-2
lines changed

include/linux/sunrpc/xdr.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ xdr_adjust_iovec(struct kvec *iov, __be32 *p)
152152
extern void xdr_shift_buf(struct xdr_buf *, size_t);
153153
extern void xdr_buf_from_iov(struct kvec *, struct xdr_buf *);
154154
extern int xdr_buf_subsegment(struct xdr_buf *, struct xdr_buf *, unsigned int, unsigned int);
155+
extern void xdr_buf_trim(struct xdr_buf *, unsigned int);
155156
extern int xdr_buf_read_netobj(struct xdr_buf *, struct xdr_netobj *, unsigned int);
156157
extern int read_bytes_from_xdr_buf(struct xdr_buf *, unsigned int, void *, unsigned int);
157158
extern int write_bytes_to_xdr_buf(struct xdr_buf *, unsigned int, void *, unsigned int);

net/sunrpc/auth_gss/gss_krb5_wrap.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,8 @@ gss_unwrap_kerberos_v2(struct krb5_ctx *kctx, int offset, struct xdr_buf *buf)
574574
buf->head[0].iov_len -= GSS_KRB5_TOK_HDR_LEN + headskip;
575575
buf->len -= GSS_KRB5_TOK_HDR_LEN + headskip;
576576

577+
/* Trim off the checksum blob */
578+
xdr_buf_trim(buf, GSS_KRB5_TOK_HDR_LEN + tailskip);
577579
return GSS_S_COMPLETE;
578580
}
579581

net/sunrpc/auth_gss/svcauth_gss.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -817,13 +817,17 @@ read_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj)
817817
* The server uses base of head iovec as read pointer, while the
818818
* client uses separate pointer. */
819819
static int
820-
unwrap_integ_data(struct xdr_buf *buf, u32 seq, struct gss_ctx *ctx)
820+
unwrap_integ_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gss_ctx *ctx)
821821
{
822822
int stat = -EINVAL;
823823
u32 integ_len, maj_stat;
824824
struct xdr_netobj mic;
825825
struct xdr_buf integ_buf;
826826

827+
/* Did we already verify the signature on the original pass through? */
828+
if (rqstp->rq_deferred)
829+
return 0;
830+
827831
integ_len = svc_getnl(&buf->head[0]);
828832
if (integ_len & 3)
829833
return stat;
@@ -846,6 +850,8 @@ unwrap_integ_data(struct xdr_buf *buf, u32 seq, struct gss_ctx *ctx)
846850
goto out;
847851
if (svc_getnl(&buf->head[0]) != seq)
848852
goto out;
853+
/* trim off the mic at the end before returning */
854+
xdr_buf_trim(buf, mic.len + 4);
849855
stat = 0;
850856
out:
851857
kfree(mic.data);
@@ -1190,7 +1196,7 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp)
11901196
/* placeholders for length and seq. number: */
11911197
svc_putnl(resv, 0);
11921198
svc_putnl(resv, 0);
1193-
if (unwrap_integ_data(&rqstp->rq_arg,
1199+
if (unwrap_integ_data(rqstp, &rqstp->rq_arg,
11941200
gc->gc_seq, rsci->mechctx))
11951201
goto garbage_args;
11961202
break;

net/sunrpc/xdr.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -879,6 +879,47 @@ xdr_buf_subsegment(struct xdr_buf *buf, struct xdr_buf *subbuf,
879879
}
880880
EXPORT_SYMBOL_GPL(xdr_buf_subsegment);
881881

882+
/**
883+
* xdr_buf_trim - lop at most "len" bytes off the end of "buf"
884+
* @buf: buf to be trimmed
885+
* @len: number of bytes to reduce "buf" by
886+
*
887+
* Trim an xdr_buf by the given number of bytes by fixing up the lengths. Note
888+
* that it's possible that we'll trim less than that amount if the xdr_buf is
889+
* too small, or if (for instance) it's all in the head and the parser has
890+
* already read too far into it.
891+
*/
892+
void xdr_buf_trim(struct xdr_buf *buf, unsigned int len)
893+
{
894+
size_t cur;
895+
unsigned int trim = len;
896+
897+
if (buf->tail[0].iov_len) {
898+
cur = min_t(size_t, buf->tail[0].iov_len, trim);
899+
buf->tail[0].iov_len -= cur;
900+
trim -= cur;
901+
if (!trim)
902+
goto fix_len;
903+
}
904+
905+
if (buf->page_len) {
906+
cur = min_t(unsigned int, buf->page_len, trim);
907+
buf->page_len -= cur;
908+
trim -= cur;
909+
if (!trim)
910+
goto fix_len;
911+
}
912+
913+
if (buf->head[0].iov_len) {
914+
cur = min_t(size_t, buf->head[0].iov_len, trim);
915+
buf->head[0].iov_len -= cur;
916+
trim -= cur;
917+
}
918+
fix_len:
919+
buf->len -= (len - trim);
920+
}
921+
EXPORT_SYMBOL_GPL(xdr_buf_trim);
922+
882923
static void __read_bytes_from_xdr_buf(struct xdr_buf *subbuf, void *obj, unsigned int len)
883924
{
884925
unsigned int this_len;

0 commit comments

Comments
 (0)