[PATCH v3 1/6] ksmbd: verify the signature of session binding requests
From: Michail Andreas Michalopoulos
Date: Mon Nov 09 2026 - 14:45:55 EST
When multichannel is enabled, smb2_sess_setup() accepts a binding
request on a new connection once it finds the session and sees
SMB2_FLAGS_SIGNED, but never checks the signature: SESSION_SETUP is
exempt from the generic check in __process_request(), because
work->sess is not set for it. The request then goes through
authentication, and the response is signed with the session's signing
key, since the request claims to be signed.
So anyone who knows a SessionId and ClientGUID can open a connection
and have the server sign STATUS_MORE_PROCESSING_REQUIRED responses with
another client's session key, each with a fresh NTLM challenge and at a
MessageId of their choice. With AES-CMAC this is a signing oracle. With
AES-GMAC the nonce is the MessageId, so it also lets two different
messages be authenticated under the same key and nonce, which reveals
the GHASH key and allows forging signatures for that session.
MS-SMB2 3.3.5.2.4 requires the server to verify a binding request with
Session.SigningKey and to fail it with STATUS_ACCESS_DENIED if that
fails. Do so before the request is processed further. The failure
response is not signed, because work->sess is only set once the
request has been accepted.
smb3_check_sign_req() zeroes the Signature field in place to compute
the MAC. Restore it afterwards, as generate_preauth_hash() later hashes
the request as received into the channel's SMB 3.1.1 preauth integrity
hash.
The other binding branch, which rejects a binding request that is not a
valid multichannel bind, verifies the signature as well. A missing
negation on the multichannel flag made that branch unreachable for an
SMB 3.x request when multichannel is disabled: such a request fell
through to the reauthentication path instead and was answered with an
unsigned STATUS_USER_SESSION_DELETED. Correct the condition so the
request is verified and rejected there.
An AI assistant was used to draft the patch and as an independent
reviewer.
Reported-by: Ian Bridges <icb@xxxxxxxxxxxx>
Closes: https://lore.kernel.org/all/20261103171742.18816-1-icb@xxxxxxxxxxxx/
Fixes: f5a544e3bab7 ("ksmbd: add support for SMB3 multichannel")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Michail Andreas Michalopoulos <mike.michalo@xxxxxxxxx>
---
fs/smb/server/smb2pdu.c | 30 +++++++++++++++++++++++++++++-
1 file changed, 29 insertions(+), 1 deletion(-)
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index b084cca8813cc..129a4c5c74f44 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -2558,6 +2558,29 @@ static int krb5_authenticate(struct ksmbd_work *work,
}
#endif
+/*
+ * A binding request must be signed with the signing key of the session it
+ * binds to (MS-SMB2 3.3.5.2.4). Check it before the request is processed
+ * and before any response is signed with that key. The Signature field is
+ * restored afterwards, because SMB 3.1.1 adds the request as received to
+ * the channel's preauth integrity hash.
+ */
+static bool smb2_check_binding_sign_req(struct ksmbd_work *work,
+ struct smb2_hdr *hdr,
+ struct ksmbd_session *sess)
+{
+ struct ksmbd_session *prev_sess = work->sess;
+ char signature[SMB2_SIGNATURE_SIZE];
+ int ret;
+
+ memcpy(signature, hdr->Signature, SMB2_SIGNATURE_SIZE);
+ work->sess = sess;
+ ret = smb3_check_sign_req(work);
+ work->sess = prev_sess;
+ memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE);
+ return ret;
+}
+
int smb2_sess_setup(struct ksmbd_work *work)
{
struct ksmbd_conn *conn = work->conn;
@@ -2640,6 +2663,11 @@ int smb2_sess_setup(struct ksmbd_work *work)
goto out_err;
}
+ if (!smb2_check_binding_sign_req(work, &req->hdr, sess)) {
+ rc = -EKEYREJECTED;
+ goto out_err;
+ }
+
if (memcmp(conn->ClientGUID, sess->ClientGUID,
SMB2_CLIENT_GUID_SIZE)) {
rc = -ENOENT;
@@ -2675,7 +2703,7 @@ int smb2_sess_setup(struct ksmbd_work *work)
conn->binding = true;
} else if ((conn->dialect < SMB30_PROT_ID ||
- server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) &&
+ !(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL)) &&
(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) {
sess = ksmbd_session_lookup_slowpath(le64_to_cpu(req->hdr.SessionId));
if (sess) {
--
2.43.0