]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ksmbd: sign rejected SMB2.1 session binding responses
authorNamjae Jeon <linkinjeon@kernel.org>
Thu, 2 Jul 2026 11:27:43 +0000 (20:27 +0900)
committerSteve French <stfrench@microsoft.com>
Mon, 6 Jul 2026 12:55:41 +0000 (07:55 -0500)
SMB2_SESSION_REQ_FLAG_BINDING is not supported before SMB 3.0. ksmbd maps
such a request to STATUS_REQUEST_NOT_ACCEPTED, but it rejects the request
without looking up the referenced session. The response is then sent
unsigned. A client requiring signing reports
STATUS_ACCESS_DENIED instead of the server status.

Look up the referenced session and verify the binding request with its
signing key. Keep the session reference only after successful verification
so the rejected response can be signed without providing a signing oracle.

A signed SESSION_SETUP without the binding flag can reference a session
that does not belong to the connection. Preserve SMB2_FLAGS_SIGNED on the
STATUS_USER_SESSION_DELETED response. Clients skip signature verification
for this status but still require the signed flag before propagating it.

Also restrict failed binding preauthentication cleanup to SMB 3.1.1, the
only dialect that initializes and uses that context.

This fixes smb2.session.bind_negative_smb210s.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/smb/server/smb2pdu.c

index 1f51b7d68de166f60135fe2ff09247be87412dd6..cd2dab1907cf486f53072996d1d34cb1b1a5ae0e 100644 (file)
@@ -2013,7 +2013,17 @@ int smb2_sess_setup(struct ksmbd_work *work)
        } else if ((conn->dialect < SMB30_PROT_ID ||
                    server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) &&
                   (req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) {
-               sess = NULL;
+               sess = ksmbd_session_lookup_slowpath(le64_to_cpu(req->hdr.SessionId));
+               if (sess) {
+                       work->sess = sess;
+                       if (sess->state != SMB2_SESSION_VALID ||
+                           !(req->hdr.Flags & SMB2_FLAGS_SIGNED) ||
+                           !conn->ops->check_sign_req(work)) {
+                               ksmbd_user_session_put(sess);
+                               work->sess = NULL;
+                               sess = NULL;
+                       }
+               }
                rc = -EACCES;
                goto out_err;
        } else {
@@ -2144,6 +2154,9 @@ out_err:
                rsp->hdr.Status = STATUS_ACCESS_DENIED;
        else if (rc)
                rsp->hdr.Status = STATUS_LOGON_FAILURE;
+       if (rsp->hdr.Status == STATUS_USER_SESSION_DELETED &&
+           (req->hdr.Flags & SMB2_FLAGS_SIGNED))
+               rsp->hdr.Flags |= SMB2_FLAGS_SIGNED;
 
        if (conn->mechToken) {
                kfree(conn->mechToken);
@@ -2151,7 +2164,8 @@ out_err:
        }
 
        if (rc < 0) {
-               if (sess && (req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) {
+               if (sess && conn->dialect == SMB311_PROT_ID &&
+                   (req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) {
                        struct preauth_session *preauth_sess;
 
                        preauth_sess = ksmbd_preauth_session_lookup(conn, sess->id);