FAILED: patch "[PATCH] SUNRPC: svcauth_gss: enforce krb5 token minimum length" failed to apply to 5.10-stable tree
Subsystems (4)
[email protected]
maintainerThe patch below does not apply to the 5.10-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-5.10.y
git checkout FETCH_HEAD
git cherry-pick -x a919c5c88769cf8fb3ec071e6078d830bf512489
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026090353-ogle-atop-5025@gregkh' --subject-prefix 'PATCH 5.10.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From a919c5c88769cf8fb3ec071e6078d830bf512489 Mon Sep 17 00:00:00 2001
From: Chris Mason <[email protected]>
Date: Sat, 23 May 2026 21:02:10 -0400
Subject: [PATCH] SUNRPC: svcauth_gss: enforce krb5 token minimum length
svcauth_gss_unwrap_priv() validates only an upper bound on the wire-supplied opaque length before handing the buffer to gss_unwrap():
if (len > xdr_stream_remaining(xdr))
goto unwrap_failed;
offset = xdr_stream_pos(xdr);
...
maj_stat = gss_unwrap(ctx, offset, offset + len, buf);The wire value len flows unchanged as the upper bound into the krb5 unwrap path, so a len in [0, 16] passes this check and is handed to gss_unwrap(). For a krb5 v2 context that lands in gss_krb5_unwrap_v2(), which reads the 16-byte RFC 4121 token header fields at ptr+4 and ptr+6 and then calls rotate_left() before any integrity check. With a sub-header length the header reads run past the token, and _rotate_left()'s shift %= buf->len path can divide by zero when buf->len has been driven to zero by the truncated token. A header-only token (len == 16) is equally
invalid: with a non-zero RRC field and the opaque blob ending at the XDR buffer boundary, rotate_left() builds a zero-length subbuffer, reaching the same division.
Reject the token at the server entry point before it reaches the krb5 unwrap core. A valid sealed RFC 4121 token must contain the 16-byte header plus at least some encrypted payload.
Fix by adding a minimum-length check immediately after the existing upper-bound check:
if (len <= GSS_KRB5_TOK_HDR_LEN)
goto unwrap_failed;net/sunrpc/auth_gss/svcauth_gss.c +2 -0
@@ -949,6 +949,8 @@ svcauth_gss_unwrap_priv(struct svc_rqst *rqstp, u32 seq, struct gss_ctx *ctx) }if (len >xdr_stream_remaining (xdr)) goto unwrap_failed; +if (len <=GSS_KRB5_TOK_HDR_LEN ) + goto unwrap_failed; offset =xdr_stream_pos (xdr); saved_len = buf->len;
Sasha Levin
maintainerPATCH 1/4 SUNRPC: Fix crasher in unwrap_integ_data()
From: Chuck Lever <[email protected]>
[ Upstream commit 4dd9daa9124aad3c3d4ceca4f1d417cc62d59156 ]
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.
Stable-dep-of: a919c5c88769 ("SUNRPC: svcauth_gss: enforce krb5 token minimum length")
--- net/sunrpc/auth_gss/svcauth_gss.c | 55 ++++++++++++++++++------------- 1 file changed, 32 insertions(+), 23 deletions(-)
net/sunrpc/auth_gss/svcauth_gss.c +32 -23
@@ -49,11 +49,36 @@#include <linux/sunrpc/svcauth.h> #include <linux/sunrpc/svcauth_gss.h> #include <linux/sunrpc/cache.h> +#include <linux/sunrpc/gss_krb5.h> #include <trace/events/rpcgss.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 * into replies.
@@ -885,13 +910,11 @@ read_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj)static int 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; -int stat = -EINVAL ;struct xdr_netobj mic;struct xdr_buf integ_buf; - mic.data =NULL ; -/* NFS READ normally uses splice to send data in-place. However * 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
@@ -915,11 +938,9 @@ unwrap_integ_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct g/* copy out mic... */ if (read_u32_from_xdr_buf (buf, integ_len, &mic.len)) goto unwrap_failed; -if (mic.len >RPC_MAX_AUTH_SIZE ) - goto unwrap_failed; - mic.data =kmalloc (mic.len,GFP_KERNEL ); -if (!mic.data) +if (mic.len >sizeof (gsd->gsd_scratch)) goto unwrap_failed; + mic.data = gsd->gsd_scratch;if (read_bytes_from_xdr_buf (buf, integ_len +4 , mic.data, mic.len)) goto unwrap_failed; maj_stat =gss_verify_mic (ctx, &integ_buf, &mic);
@@ -930,20 +951,17 @@ unwrap_integ_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct g goto bad_seqno;/* trim off the mic and padding at the end before returning */ xdr_buf_trim (buf,round_up_to_quad (mic.len) +4 ); - stat =0 ; -out: -kfree (mic.data); -return stat; +return 0 ; unwrap_failed:trace_rpcgss_svc_unwrap_failed (rqstp); - goto out; +return -EINVAL ; bad_seqno:trace_rpcgss_svc_seqno_bad (rqstp, seq, rseqno); - goto out; +return -EINVAL ; bad_mic:trace_rpcgss_svc_mic (rqstp, maj_stat); - goto out; +return -EINVAL ; } static inline int
@@ -1021,15 +1039,6 @@ unwrap_priv_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gsreturn -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 svcauth_gss_set_client (struct svc_rqst *rqstp) {
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/4 SUNRPC: Rename automatic variables in unwrap_priv_data()
From: Chuck Lever <[email protected]>
[ Upstream commit f4a59e822f8116baf51a9fc86590e4391bd542d2 ]
Clean up: To help orient readers, name the stack variables to match the XDR field names.
Stable-dep-of: a919c5c88769 ("SUNRPC: svcauth_gss: enforce krb5 token minimum length")
--- net/sunrpc/auth_gss/svcauth_gss.c | 38 ++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 13 deletions(-)
net/sunrpc/auth_gss/svcauth_gss.c +25 -13
@@ -981,16 +981,28 @@ fix_priv_head(struct xdr_buf *buf, int pad) } } +/* +* RFC 2203, Section 5.3.2.3 +* +* struct rpc_gss_priv_data { +* opaque databody_priv<> +* }; +* +* struct rpc_gss_data_t { +* unsigned int seq_num; +* proc_req_arg_t arg; +* }; +*/ static int -unwrap_priv_data (struct svc_rqst *rqstp,struct xdr_buf *buf,u32 seq,struct gss_ctx *ctx) +svcauth_gss_unwrap_priv (struct svc_rqst *rqstp,struct xdr_buf *buf,u32 seq, +struct gss_ctx *ctx) { -u32 priv_len, maj_stat; +u32 len, seq_num, maj_stat;int pad, remaining_len, offset; -u32 rseqno;clear_bit (RQ_SPLICE_OK , &rqstp->rq_flags); - priv_len =svc_getnl (&buf->head[0 ]); + len =svc_getnl (&buf->head[0 ]);if (rqstp->rq_deferred) {/* Already decrypted last time through! The sequence number * check at out_seq is unnecessary but harmless: */
@@ -1000,14 +1012,14 @@ unwrap_priv_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gs* request to the end, where head[0].iov_len is just the bytes * not yet read from the head, so these two values are different: */ remaining_len =total_buf_len (buf); -if (priv_len > remaining_len) +if (len > remaining_len) goto unwrap_failed; - pad = remaining_len - priv_len; + pad = remaining_len - len; buf->len -= pad;fix_priv_head (buf, pad); - maj_stat =gss_unwrap (ctx,0 , priv_len, buf); - pad = priv_len - buf->len; + maj_stat =gss_unwrap (ctx,0 , len, buf); + pad = len - buf->len;/* The upper layers assume the buffer is aligned on 4-byte boundaries. * In the krb5p case, at least, the data ends up offset, so we need to * move it around. */
@@ -1023,8 +1035,8 @@ unwrap_priv_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gsif (maj_stat !=GSS_S_COMPLETE ) goto bad_unwrap; out_seq: - rseqno =svc_getnl (&buf->head[0 ]); -if (rseqno != seq) + seq_num =svc_getnl (&buf->head[0 ]); +if (seq_num != seq) goto bad_seqno;return 0 ;
@@ -1032,7 +1044,7 @@ unwrap_priv_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gstrace_rpcgss_svc_unwrap_failed (rqstp);return -EINVAL ; bad_seqno: -trace_rpcgss_svc_seqno_bad (rqstp, seq, rseqno); +trace_rpcgss_svc_seqno_bad (rqstp, seq, seq_num);return -EINVAL ; bad_unwrap:trace_rpcgss_svc_unwrap (rqstp, maj_stat);
@@ -1651,8 +1663,8 @@ svcauth_gss_accept(struct svc_rqst *rqstp)/* placeholders for length and seq. number: */ svc_putnl (resv,0 );svc_putnl (resv,0 ); -if (unwrap_priv_data (rqstp, &rqstp->rq_arg, - gc->gc_seq, rsci->mechctx)) +if (svcauth_gss_unwrap_priv (rqstp, &rqstp->rq_arg, + gc->gc_seq, rsci->mechctx)) goto garbage_args; rqstp->rq_auth_slack =RPC_MAX_AUTH_SIZE *2 ;break ;
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 3/4 SUNRPC: Convert unwrap_priv_data() to use xdr_stream
In reply to Sasha LevinFrom: Chuck Lever <[email protected]>
[ Upstream commit 42140718ea26c47fb06c679451a6aa6703545136 ]
Done as part of hardening the server-side RPC header decoding path.
[ sashal: Reduced backport -- upstream 42140718ea26c touches 3 file(s), this
backport carries 1. Not backported here:
include/linux/sunrpc/xdr.h
net/sunrpc/xdr.c
This note is generated from the file lists only; see the resolution record for the reasoning. ]
Stable-dep-of: a919c5c88769 ("SUNRPC: svcauth_gss: enforce krb5 token minimum length")
--- net/sunrpc/auth_gss/svcauth_gss.c | 64 ++++++++++--------------------- 1 file changed, 21 insertions(+), 43 deletions(-)
net/sunrpc/auth_gss/svcauth_gss.c +21 -43
@@ -970,17 +970,6 @@ total_buf_len(struct xdr_buf *buf)return buf->head[0 ].iov_len + buf->page_len + buf->tail[0 ].iov_len; } -static void -fix_priv_head (struct xdr_buf *buf,int pad) -{ -if (buf->page_len ==0 ) { -/* We need to adjust head and buf->len in tandem in this -* case to make svc_defer() work--it finds the original -* buffer start using buf->len - buf->head[0].iov_len. */ - buf->head[0 ].iov_len -= pad; - } -} -/* * RFC 2203, Section 5.3.2.3 *
@@ -993,49 +982,37 @@ fix_priv_head(struct xdr_buf *buf, int pad)* proc_req_arg_t arg; * }; */ -static int -svcauth_gss_unwrap_priv (struct svc_rqst *rqstp,struct xdr_buf *buf,u32 seq, -struct gss_ctx *ctx) +static noinline_for_stack int +svcauth_gss_unwrap_priv (struct svc_rqst *rqstp,u32 seq,struct gss_ctx *ctx) { -u32 len, seq_num, maj_stat; -int pad, remaining_len, offset; +struct xdr_stream *xdr = &rqstp->rq_arg_stream; +u32 len, maj_stat, seq_num, offset; +struct xdr_buf *buf = xdr->buf; +unsigned int saved_len;clear_bit (RQ_SPLICE_OK , &rqstp->rq_flags); - len =svc_getnl (&buf->head[0 ]); +if (xdr_stream_decode_u32 (xdr, &len) <0 ) + goto unwrap_failed;if (rqstp->rq_deferred) {/* Already decrypted last time through! The sequence number * check at out_seq is unnecessary but harmless: */ goto out_seq; } -/* buf->len is the number of bytes from the original start of the -* request to the end, where head[0].iov_len is just the bytes -* not yet read from the head, so these two values are different: */ - remaining_len =total_buf_len (buf); -if (len > remaining_len) +if (len >xdr_stream_remaining (xdr)) goto unwrap_failed; - pad = remaining_len - len; - buf->len -= pad; -fix_priv_head (buf, pad); - - maj_stat =gss_unwrap (ctx,0 , len, buf); - pad = len - buf->len; -/* The upper layers assume the buffer is aligned on 4-byte boundaries. -* In the krb5p case, at least, the data ends up offset, so we need to -* move it around. */ -/* XXX: This is very inefficient. It would be better to either do -* this while we encrypt, or maybe in the receive code, if we can peak -* ahead and work out the service and mechanism there. */ - offset =xdr_pad_size (buf->head[0 ].iov_len); -if (offset) { - buf->buflen =RPCSVC_MAXPAYLOAD ; -xdr_shift_buf (buf, offset); -fix_priv_head (buf, pad); - } + offset =xdr_stream_pos (xdr); + + saved_len = buf->len; + maj_stat =gss_unwrap (ctx, offset, offset + len, buf);if (maj_stat !=GSS_S_COMPLETE ) goto bad_unwrap; + xdr->nwords -=XDR_QUADLEN (saved_len - buf->len); + out_seq: - seq_num =svc_getnl (&buf->head[0 ]); +/* gss_unwrap() decrypted the sequence number. */ +if (xdr_stream_decode_u32 (xdr, &seq_num) <0 ) + goto unwrap_failed;if (seq_num != seq) goto bad_seqno;return 0 ;
@@ -1663,8 +1640,9 @@ 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_priv (rqstp, &rqstp->rq_arg, - gc->gc_seq, rsci->mechctx)) +svcxdr_init_decode (rqstp); +if (svcauth_gss_unwrap_priv (rqstp, gc->gc_seq, + rsci->mechctx)) goto garbage_args; rqstp->rq_auth_slack =RPC_MAX_AUTH_SIZE *2 ;break ;
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 4/4 SUNRPC: svcauth_gss: enforce krb5 token minimum length
In reply to Sasha LevinFrom: Chris Mason <[email protected]>
[ Upstream commit a919c5c88769cf8fb3ec071e6078d830bf512489 ]
svcauth_gss_unwrap_priv() validates only an upper bound on the wire-supplied opaque length before handing the buffer to gss_unwrap():
if (len > xdr_stream_remaining(xdr))
goto unwrap_failed;
offset = xdr_stream_pos(xdr);
...
maj_stat = gss_unwrap(ctx, offset, offset + len, buf);The wire value len flows unchanged as the upper bound into the krb5 unwrap path, so a len in [0, 16] passes this check and is handed to gss_unwrap(). For a krb5 v2 context that lands in gss_krb5_unwrap_v2(), which reads the 16-byte RFC 4121 token header fields at ptr+4 and ptr+6 and then calls rotate_left() before any integrity check. With a sub-header length the header reads run past the token, and _rotate_left()'s shift %= buf->len path can divide by zero when buf->len has been driven to zero by the truncated token. A header-only token (len == 16) is equally
invalid: with a non-zero RRC field and the opaque blob ending at the XDR buffer boundary, rotate_left() builds a zero-length subbuffer, reaching the same division.
Reject the token at the server entry point before it reaches the krb5 unwrap core. A valid sealed RFC 4121 token must contain the 16-byte header plus at least some encrypted payload.
Fix by adding a minimum-length check immediately after the existing upper-bound check:
if (len <= GSS_KRB5_TOK_HDR_LEN)
goto unwrap_failed;--- net/sunrpc/auth_gss/svcauth_gss.c | 2 ++ 1 file changed, 2 insertions(+)
net/sunrpc/auth_gss/svcauth_gss.c +2 -0
@@ -1001,6 +1001,8 @@ svcauth_gss_unwrap_priv(struct svc_rqst *rqstp, u32 seq, struct gss_ctx *ctx) }if (len >xdr_stream_remaining (xdr)) goto unwrap_failed; +if (len <=GSS_KRB5_TOK_HDR_LEN ) + goto unwrap_failed; offset =xdr_stream_pos (xdr); saved_len = buf->len;
Signature · 1 line
2.53.0