* FAILED: patch "[PATCH] ksmbd: verify transform SessionId matches the decrypted" failed to apply to 6.18-stable tree
@ 2026-11-03 10:46 gregkh
2026-11-03 21:38 ` [PATCH 6.18.y] ksmbd: verify transform SessionId matches the decrypted header Sasha Levin
0 siblings, 1 reply; 2+ messages in thread
From: gregkh @ 2026-11-03 10:46 UTC (permalink / raw)
To: zhangdairui, linkinjeon; +Cc: stable
The patch below does not apply to the 6.18-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 <stable@vger.kernel.org>.
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-6.18.y
git checkout FETCH_HEAD
git cherry-pick -x 798214e6fafcdcc29f5ba2661a473232b5e70cfb
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<stable@vger.kernel.org>' --in-reply-to '2026110343-kosher-bleach-17a4@gregkh' --subject-prefix 'PATCH 6.18.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 798214e6fafcdcc29f5ba2661a473232b5e70cfb Mon Sep 17 00:00:00 2001
From: Dairui Zhang <zhangdairui@gmail.com>
Date: Tue, 29 Sep 2026 22:30:00 +0800
Subject: [PATCH] ksmbd: verify transform SessionId matches the decrypted
header
When an encrypted request comes in, the decryption key is picked by
the SessionId in the transform header, but the request is then
authorized under whatever session the decrypted inner header names.
Nothing ever compares the two, so on a connection with more than
one session a client can get a request decrypted with one session's
key and run it as another session. Encrypted requests are exempt
from signing too, so a valid AEAD tag is the only proof of session
identity, and it is checked against the wrong session.
MS-SMB2 says the server must check that the transform SessionId
matches the one in the decrypted SMB2 header and treat a mismatch
as a protocol error. Add that check in __handle_ksmbd_work() after
decryption, before dispatching: the message has to be at least one
fixed SMB2 header, the first operation must not set
SMB2_FLAGS_RELATED_OPERATIONS and its SessionId must match the
transform SessionId, and every following non-related operation in
a compound chain must carry the same SessionId. NextCommand
offsets must be 8-byte aligned and stay inside the message; the
bounds check is done by subtraction so it cannot wrap.
When the decrypted payload is a compression transform, the
existing decompression step runs first and the decompressed SMB2
message is checked instead, since a compression transform header
carries no SessionId.
Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3")
Reported-by: Dairui Zhang <zhangdairui@gmail.com>
Assisted-by: LLM
Cc: stable@vger.kernel.org
Signed-off-by: Dairui Zhang <zhangdairui@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c
index 0827c8c51006..eb2952f2debe 100644
--- a/fs/smb/server/server.c
+++ b/fs/smb/server/server.c
@@ -187,6 +187,16 @@ static void __handle_ksmbd_work(struct ksmbd_work *work,
if (conn->ops->is_transform_hdr &&
conn->ops->is_transform_hdr(work->request_buf)) {
+ u64 tr_sess_id;
+
+ if (get_rfc1002_len(work->request_buf) <
+ sizeof(struct smb2_transform_hdr)) {
+ ksmbd_conn_abort(conn);
+ return;
+ }
+ tr_sess_id = le64_to_cpu(((struct smb2_transform_hdr *)
+ smb_get_msg(work->request_buf))->SessionId);
+
rc = conn->ops->decrypt_req(work);
if (rc < 0) {
ksmbd_conn_abort(conn);
@@ -215,6 +225,12 @@ static void __handle_ksmbd_work(struct ksmbd_work *work,
ksmbd_conn_abort(conn);
return;
}
+
+ rc = ksmbd_check_transform_session(work, tr_sess_id);
+ if (rc < 0) {
+ ksmbd_conn_abort(conn);
+ return;
+ }
}
if (conn->ops->allocate_rsp_buf(work))
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index 45dce9c30b6b..0592e2539d1d 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -12256,6 +12256,60 @@ bool smb3_is_transform_hdr(void *buf)
return trhdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM;
}
+/*
+ * The decryption key is selected by the transform SessionId, but
+ * the request is authorized under the session named in the
+ * decrypted SMB2 header. Per MS-SMB2 the two must match, so check
+ * the decrypted message and its compound chain before dispatch.
+ */
+int ksmbd_check_transform_session(struct ksmbd_work *work, u64 tr_sess_id)
+{
+ struct smb2_hdr *hdr = smb_get_msg(work->request_buf);
+ size_t msg_len = get_rfc1002_len(work->request_buf);
+ size_t off = 0;
+ bool first = true;
+
+ if (msg_len < sizeof(*hdr)) {
+ pr_err_ratelimited("Decrypted message is smaller than SMB2 header\n");
+ return -ECONNABORTED;
+ }
+
+ for (;;) {
+ u64 sid = le64_to_cpu(hdr->SessionId);
+ u32 next;
+
+ if (first) {
+ if (hdr->Flags & SMB2_FLAGS_RELATED_OPERATIONS) {
+ pr_err_ratelimited("RELATED_OPERATIONS set on first operation\n");
+ return -ECONNABORTED;
+ }
+ if (sid != tr_sess_id) {
+ pr_err_ratelimited("SessionId mismatch between transform and inner header\n");
+ return -ECONNABORTED;
+ }
+ first = false;
+ } else if (!(hdr->Flags & SMB2_FLAGS_RELATED_OPERATIONS) &&
+ sid != tr_sess_id) {
+ pr_err_ratelimited("SessionId mismatch in compound chain\n");
+ return -ECONNABORTED;
+ }
+
+ next = le32_to_cpu(hdr->NextCommand);
+ if (!next)
+ return 0;
+ if (next % 8) {
+ pr_err_ratelimited("NextCommand %u is not 8-byte aligned\n", next);
+ return -ECONNABORTED;
+ }
+ if (next > msg_len - off - sizeof(*hdr)) {
+ pr_err_ratelimited("NextCommand %u is out of the message\n", next);
+ return -ECONNABORTED;
+ }
+ off += next;
+ hdr = (struct smb2_hdr *)((u8 *)smb_get_msg(work->request_buf) + off);
+ }
+}
+
int smb3_decrypt_req(struct ksmbd_work *work)
{
char *buf = work->request_buf;
diff --git a/fs/smb/server/smb2pdu.h b/fs/smb/server/smb2pdu.h
index 40d61745ef41..c90a1d4e9f11 100644
--- a/fs/smb/server/smb2pdu.h
+++ b/fs/smb/server/smb2pdu.h
@@ -416,6 +416,7 @@ struct channel *lookup_chann_list(struct ksmbd_session *sess,
void smb3_preauth_hash_rsp(struct ksmbd_work *work);
bool smb3_is_transform_hdr(void *buf);
int smb3_decrypt_req(struct ksmbd_work *work);
+int ksmbd_check_transform_session(struct ksmbd_work *work, u64 tr_sess_id);
int smb3_encrypt_resp(struct ksmbd_work *work);
bool smb3_11_final_sess_setup_resp(struct ksmbd_work *work);
int smb2_set_rsp_credits(struct ksmbd_work *work);
^ permalink raw reply related [flat|nested] 2+ messages in thread* [PATCH 6.18.y] ksmbd: verify transform SessionId matches the decrypted header
2026-11-03 10:46 FAILED: patch "[PATCH] ksmbd: verify transform SessionId matches the decrypted" failed to apply to 6.18-stable tree gregkh
@ 2026-11-03 21:38 ` Sasha Levin
0 siblings, 0 replies; 2+ messages in thread
From: Sasha Levin @ 2026-11-03 21:38 UTC (permalink / raw)
To: stable; +Cc: Dairui Zhang, Namjae Jeon, Sasha Levin
From: Dairui Zhang <zhangdairui@gmail.com>
[ Upstream commit 798214e6fafcdcc29f5ba2661a473232b5e70cfb ]
When an encrypted request comes in, the decryption key is picked by
the SessionId in the transform header, but the request is then
authorized under whatever session the decrypted inner header names.
Nothing ever compares the two, so on a connection with more than
one session a client can get a request decrypted with one session's
key and run it as another session. Encrypted requests are exempt
from signing too, so a valid AEAD tag is the only proof of session
identity, and it is checked against the wrong session.
MS-SMB2 says the server must check that the transform SessionId
matches the one in the decrypted SMB2 header and treat a mismatch
as a protocol error. Add that check in __handle_ksmbd_work() after
decryption, before dispatching: the message has to be at least one
fixed SMB2 header, the first operation must not set
SMB2_FLAGS_RELATED_OPERATIONS and its SessionId must match the
transform SessionId, and every following non-related operation in
a compound chain must carry the same SessionId. NextCommand
offsets must be 8-byte aligned and stay inside the message; the
bounds check is done by subtraction so it cannot wrap.
When the decrypted payload is a compression transform, the
existing decompression step runs first and the decompressed SMB2
message is checked instead, since a compression transform header
carries no SessionId.
Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3")
Reported-by: Dairui Zhang <zhangdairui@gmail.com>
Assisted-by: LLM
Cc: stable@vger.kernel.org
Signed-off-by: Dairui Zhang <zhangdairui@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
[ sashal: Placed session validation directly after decryption because the intervening decompression code is absent. ]
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/smb/server/server.c | 16 ++++++++++++
fs/smb/server/smb2pdu.c | 54 +++++++++++++++++++++++++++++++++++++++++
fs/smb/server/smb2pdu.h | 1 +
3 files changed, 71 insertions(+)
diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c
index 85f14d1cc0cd3..ba625b52c6375 100644
--- a/fs/smb/server/server.c
+++ b/fs/smb/server/server.c
@@ -169,12 +169,28 @@ static void __handle_ksmbd_work(struct ksmbd_work *work,
if (conn->ops->is_transform_hdr &&
conn->ops->is_transform_hdr(work->request_buf)) {
+ u64 tr_sess_id;
+
+ if (get_rfc1002_len(work->request_buf) <
+ sizeof(struct smb2_transform_hdr)) {
+ ksmbd_conn_abort(conn);
+ return;
+ }
+ tr_sess_id = le64_to_cpu(((struct smb2_transform_hdr *)
+ smb_get_msg(work->request_buf))->SessionId);
+
rc = conn->ops->decrypt_req(work);
if (rc < 0) {
ksmbd_conn_abort(conn);
return;
}
work->encrypted = true;
+
+ rc = ksmbd_check_transform_session(work, tr_sess_id);
+ if (rc < 0) {
+ ksmbd_conn_abort(conn);
+ return;
+ }
}
if (conn->ops->allocate_rsp_buf(work))
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index 793c219b9a610..80e7b2532f968 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -9414,6 +9414,60 @@ bool smb3_is_transform_hdr(void *buf)
return trhdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM;
}
+/*
+ * The decryption key is selected by the transform SessionId, but
+ * the request is authorized under the session named in the
+ * decrypted SMB2 header. Per MS-SMB2 the two must match, so check
+ * the decrypted message and its compound chain before dispatch.
+ */
+int ksmbd_check_transform_session(struct ksmbd_work *work, u64 tr_sess_id)
+{
+ struct smb2_hdr *hdr = smb_get_msg(work->request_buf);
+ size_t msg_len = get_rfc1002_len(work->request_buf);
+ size_t off = 0;
+ bool first = true;
+
+ if (msg_len < sizeof(*hdr)) {
+ pr_err_ratelimited("Decrypted message is smaller than SMB2 header\n");
+ return -ECONNABORTED;
+ }
+
+ for (;;) {
+ u64 sid = le64_to_cpu(hdr->SessionId);
+ u32 next;
+
+ if (first) {
+ if (hdr->Flags & SMB2_FLAGS_RELATED_OPERATIONS) {
+ pr_err_ratelimited("RELATED_OPERATIONS set on first operation\n");
+ return -ECONNABORTED;
+ }
+ if (sid != tr_sess_id) {
+ pr_err_ratelimited("SessionId mismatch between transform and inner header\n");
+ return -ECONNABORTED;
+ }
+ first = false;
+ } else if (!(hdr->Flags & SMB2_FLAGS_RELATED_OPERATIONS) &&
+ sid != tr_sess_id) {
+ pr_err_ratelimited("SessionId mismatch in compound chain\n");
+ return -ECONNABORTED;
+ }
+
+ next = le32_to_cpu(hdr->NextCommand);
+ if (!next)
+ return 0;
+ if (next % 8) {
+ pr_err_ratelimited("NextCommand %u is not 8-byte aligned\n", next);
+ return -ECONNABORTED;
+ }
+ if (next > msg_len - off - sizeof(*hdr)) {
+ pr_err_ratelimited("NextCommand %u is out of the message\n", next);
+ return -ECONNABORTED;
+ }
+ off += next;
+ hdr = (struct smb2_hdr *)((u8 *)smb_get_msg(work->request_buf) + off);
+ }
+}
+
int smb3_decrypt_req(struct ksmbd_work *work)
{
struct ksmbd_session *sess;
diff --git a/fs/smb/server/smb2pdu.h b/fs/smb/server/smb2pdu.h
index 6833342407013..23abb70ea5d3d 100644
--- a/fs/smb/server/smb2pdu.h
+++ b/fs/smb/server/smb2pdu.h
@@ -441,6 +441,7 @@ struct channel *lookup_chann_list(struct ksmbd_session *sess,
void smb3_preauth_hash_rsp(struct ksmbd_work *work);
bool smb3_is_transform_hdr(void *buf);
int smb3_decrypt_req(struct ksmbd_work *work);
+int ksmbd_check_transform_session(struct ksmbd_work *work, u64 tr_sess_id);
int smb3_encrypt_resp(struct ksmbd_work *work);
bool smb3_11_final_sess_setup_resp(struct ksmbd_work *work);
int smb2_set_rsp_credits(struct ksmbd_work *work);
--
2.53.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-11-03 21:38 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-03 10:46 FAILED: patch "[PATCH] ksmbd: verify transform SessionId matches the decrypted" failed to apply to 6.18-stable tree gregkh
2026-11-03 21:38 ` [PATCH 6.18.y] ksmbd: verify transform SessionId matches the decrypted header Sasha Levin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox