From: Michail Andreas Michalopoulos <mike.michalo@gmail.com>
To: linkinjeon@kernel.org, stfrench@microsoft.com
Cc: icb@fastmail.org, senozhatsky@chromium.org, tom@talpey.com,
chenxiaosong@chenxiaosong.com, ematsumiya@suse.de,
lsahlber@redhat.com, hyc.lee@gmail.com,
linux-cifs@vger.kernel.org, linux-kernel@vger.kernel.org,
Michail Andreas Michalopoulos <mike.michalo@gmail.com>,
stable@vger.kernel.org
Subject: [PATCH v4 3/6] ksmbd: use the channel signing key for session setup on a bound channel
Date: Tue, 10 Nov 2026 23:58:31 +0700 [thread overview]
Message-ID: <20261110165834.28162-4-mike.michalo@gmail.com> (raw)
In-Reply-To: <20261110165834.28162-1-mike.michalo@gmail.com>
smb3_check_sign_req() and smb3_set_sign_rsp() always use the session
signing key for SESSION_SETUP. That is right for a binding request and
its non-final responses (MS-SMB2 3.3.5.2.4, 3.3.5.5.3), but a
reauthentication sent on a channel that is already bound is then also
verified and signed with the session key instead of that channel's
key.
Use the session key for binding requests, for their non-final
responses, and for SESSION_SETUP on a connection that is not yet a
channel of the session. Use the channel key for everything else on a
channel. Samba does the same. MS-SMB2 is ambiguous here: 3.2.4.1.1
has the client sign any session setup with Session.SigningKey, while
3.3.5.2.4 has the server verify non-binding requests with
Channel.SigningKey.
The signature of a reauthentication was not checked at all, because
SESSION_SETUP is exempt from the check in __process_request(). A
reauthentication of a live session with a forged signature therefore
got STATUS_MORE_PROCESSING_REQUIRED signed with the session key, with or
without multichannel: the same kind of signing oracle as an unverified
binding request. Verify it with the same helper as binding requests.
Also refuse an unsigned reauthentication of a session that requires
signing, as smbtorture's bind_negative tests expect from Windows,
unless the request is encrypted. A session that negotiated encryption
has sess->sign cleared even when the connection required signing, so
take that and enforced encryption into account too; merely having
negotiated encryption is not enough, since clients such as the Linux
one always offer it and then send unsigned, unencrypted requests. Do
not expire the session when a check fails, since the request did not
come from its owner.
An AI assistant was used to draft the patch and as an independent
reviewer.
Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3")
Cc: stable@vger.kernel.org
Signed-off-by: Michail Andreas Michalopoulos <mike.michalo@gmail.com>
---
fs/smb/server/smb2pdu.c | 112 ++++++++++++++++++++++++++++++----------
1 file changed, 86 insertions(+), 26 deletions(-)
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index ddaa98055154a..293e660265c95 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -2561,23 +2561,51 @@ 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.
+ * SESSION_SETUP is not covered by the signature check in __process_request(),
+ * because work->sess is not set for it. A binding request must be signed
+ * with the signing key of the session it binds to (MS-SMB2 3.3.5.2.4), and a
+ * signed reauthentication with the key of the channel it arrives on. Check
+ * them 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)
+static bool smb2_check_sess_setup_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);
+ /* The key, and so the way it signs, belongs to the session */
+ if (sess->dialect >= SMB30_PROT_ID)
+ ret = smb3_check_sign_req(work);
+ else
+ ret = smb2_check_sign_req(work);
work->sess = prev_sess;
return ret;
}
+/*
+ * Whether a reauthentication of @sess must be signed or encrypted. A session
+ * that negotiated encryption has sess->sign cleared even when signing was
+ * required on the connection, while encryption itself is only enforced when
+ * configured, so check what the session actually requires. Merely having
+ * negotiated encryption does not count: clients such as the Linux one always
+ * offer it, and then legitimately send unsigned, unencrypted requests.
+ */
+static bool smb2_sess_needs_protection(struct ksmbd_conn *conn,
+ struct ksmbd_session *sess)
+{
+ if (sess->sign)
+ return true;
+ if (!sess->enc)
+ return false;
+ if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION)
+ return true;
+ return sess->user && !user_guest(sess->user) &&
+ (conn->sign || server_conf.enforced_signing);
+}
+
int smb2_sess_setup(struct ksmbd_work *work)
{
struct ksmbd_conn *conn = work->conn;
@@ -2660,7 +2688,7 @@ int smb2_sess_setup(struct ksmbd_work *work)
goto out_err;
}
- if (!smb2_check_binding_sign_req(work, &req->hdr, sess)) {
+ if (!smb2_check_sess_setup_sign_req(work, &req->hdr, sess)) {
rc = -EKEYREJECTED;
goto out_err;
}
@@ -2754,6 +2782,29 @@ int smb2_sess_setup(struct ksmbd_work *work)
goto out_err;
}
+ /*
+ * A reauthentication of a session that has keys must prove it
+ * comes from the session's owner: a signed request must verify,
+ * and a session that requires signing or encryption must not
+ * accept an unsigned request unless it is encrypted, since
+ * encryption has already authenticated it. The session has
+ * keys once this connection is one of its channels, including
+ * while a reauthentication is in progress. Do not expire the
+ * session if the check fails: the request did not come from
+ * its owner.
+ */
+ if ((READ_ONCE(sess->state) != SMB2_SESSION_IN_PROGRESS ||
+ lookup_chann_list(sess, conn)) &&
+ ((req->hdr.Flags & SMB2_FLAGS_SIGNED) ?
+ !smb2_check_sess_setup_sign_req(work, &req->hdr, sess) :
+ (!work->encrypted &&
+ smb2_sess_needs_protection(conn, sess)))) {
+ rc = -EKEYREJECTED;
+ ksmbd_user_session_put(sess);
+ sess = NULL;
+ goto out_err;
+ }
+
if (work->session_setup_reauth)
WRITE_ONCE(sess->state, SMB2_SESSION_IN_PROGRESS);
@@ -12979,22 +13030,24 @@ int smb3_check_sign_req(struct ksmbd_work *work)
* A key must be used with the algorithm it was generated for: the
* session key with the one of the connection that created the
* session, which can differ from this connection's when binding.
+ *
+ * A binding request, or a SESSION_SETUP on a connection that is not
+ * yet a channel of the session, is verified with the session key. A
+ * request on an existing channel, including a reauthentication, is
+ * verified with that channel's key, so that the session key is not
+ * used at the MessageIds of another connection.
*/
- if (le16_to_cpu(hdr->Command) == SMB2_SESSION_SETUP_HE) {
+ chann = lookup_chann_list(work->sess, conn);
+ if (le16_to_cpu(hdr->Command) == SMB2_SESSION_SETUP_HE &&
+ (!chann || (((struct smb2_sess_setup_req *)hdr)->Flags &
+ SMB2_SESSION_REQ_FLAG_BINDING))) {
signing_key = work->sess->smb3signingkey;
signing_algorithm = work->sess->signing_algorithm;
+ } else if (chann) {
+ signing_key = chann->smb3signingkey;
+ signing_algorithm = conn->signing_algorithm;
} else {
- chann = lookup_chann_list(work->sess, conn);
- if (!chann) {
- if (le16_to_cpu(hdr->Command) != SMB2_SESSION_SETUP_HE ||
- !(hdr->Flags & SMB2_FLAGS_SIGNED))
- return 0;
- signing_key = work->sess->smb3signingkey;
- signing_algorithm = work->sess->signing_algorithm;
- } else {
- signing_key = chann->smb3signingkey;
- signing_algorithm = conn->signing_algorithm;
- }
+ return 0;
}
if (!signing_key) {
@@ -13040,17 +13093,24 @@ void smb3_set_sign_rsp(struct ksmbd_work *work)
hdr = ksmbd_resp_buf_curr(work);
command = le16_to_cpu(hdr->Command);
+ /*
+ * Responses to binding requests, other than the final one, are signed
+ * with the session key (MS-SMB2 3.3.5.5.3), as is any SESSION_SETUP
+ * response on a connection that is not yet a channel. Everything else
+ * on a channel, including a reauthentication on a secondary channel,
+ * is signed with that channel's key.
+ */
+ chann = lookup_chann_list(work->sess, work->conn);
if (command == SMB2_SESSION_SETUP_HE &&
- (!work->session_setup_binding || hdr->Status != STATUS_SUCCESS)) {
+ (!chann || (work->session_setup_binding &&
+ hdr->Status != STATUS_SUCCESS))) {
signing_key = work->sess->smb3signingkey;
signing_algorithm = work->sess->signing_algorithm;
- } else {
- chann = lookup_chann_list(work->sess, work->conn);
- if (!chann) {
- return;
- }
+ } else if (chann) {
signing_key = chann->smb3signingkey;
signing_algorithm = conn->signing_algorithm;
+ } else {
+ return;
}
if (!signing_key)
--
2.43.0
next prev parent reply other threads:[~2026-11-10 16:59 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-10 16:58 [PATCH v4 0/6] ksmbd: verify session setup requests before signing with their key Michail Andreas Michalopoulos
2026-11-10 16:58 ` [PATCH v4 1/6] ksmbd: verify the signature of session binding requests Michail Andreas Michalopoulos
2026-11-10 16:58 ` [PATCH v4 2/6] ksmbd: sign with the negotiated SMB 3.1.1 signing algorithm Michail Andreas Michalopoulos
2026-11-10 16:58 ` Michail Andreas Michalopoulos [this message]
2026-11-10 16:58 ` [PATCH v4 4/6] ksmbd: check a request before signing STATUS_NETWORK_SESSION_EXPIRED Michail Andreas Michalopoulos
2026-11-10 16:58 ` [PATCH v4 5/6] ksmbd: use the current command's header for signing in a compound Michail Andreas Michalopoulos
2026-11-10 16:58 ` [PATCH v4 6/6] ksmbd: consume the sequence number of a request on a failed session Michail Andreas Michalopoulos
2026-11-10 18:17 ` [PATCH v4 0/6] ksmbd: verify session setup requests before signing with their key mixalhs
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261110165834.28162-4-mike.michalo@gmail.com \
--to=mike.michalo@gmail.com \
--cc=chenxiaosong@chenxiaosong.com \
--cc=ematsumiya@suse.de \
--cc=hyc.lee@gmail.com \
--cc=icb@fastmail.org \
--cc=linkinjeon@kernel.org \
--cc=linux-cifs@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=lsahlber@redhat.com \
--cc=senozhatsky@chromium.org \
--cc=stable@vger.kernel.org \
--cc=stfrench@microsoft.com \
--cc=tom@talpey.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®