[PATCH v4 1/6] ksmbd: verify the signature of session binding requests

From: Michail Andreas Michalopoulos

Date: Tue Nov 10 2026 - 15:41:51 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.

smb2_check_sign_req() and smb3_check_sign_req() zero the Signature
field in place to compute the MAC. Restore it in both before they
return, 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 | 29 ++++++++++++++++++++++++++++-
1 file changed, 28 insertions(+), 1 deletion(-)

diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index b084cca8813cc..053f3eb398f5a 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -2558,6 +2558,24 @@ 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.
+ */
+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;
+ int ret;
+
+ work->sess = sess;
+ ret = smb3_check_sign_req(work);
+ work->sess = prev_sess;
+ return ret;
+}
+
int smb2_sess_setup(struct ksmbd_work *work)
{
struct ksmbd_conn *conn = work->conn;
@@ -2640,6 +2658,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 +2698,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) {
@@ -12853,6 +12876,8 @@ int smb2_check_sign_req(struct ksmbd_work *work)

ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, 1,
signature);
+ /* Restore the request as received, for the SMB 3.1.1 preauth hash */
+ memcpy(hdr->Signature, signature_req, SMB2_SIGNATURE_SIZE);

if (crypto_memneq(signature, signature_req, SMB2_SIGNATURE_SIZE)) {
pr_err("bad smb2 signature\n");
@@ -12972,6 +12997,8 @@ int smb3_check_sign_req(struct ksmbd_work *work)
iov[0].iov_len = len;

ksmbd_sign_smb3_pdu(conn, signing_key, iov, 1, signature);
+ /* Restore the request as received, for the SMB 3.1.1 preauth hash */
+ memcpy(hdr->Signature, signature_req, SMB2_SIGNATURE_SIZE);

if (crypto_memneq(signature, signature_req, SMB2_SIGNATURE_SIZE)) {
pr_err("bad smb2 signature\n");
--
2.43.0