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 v2 3/4] ksmbd: use the channel signing key for session setup on a bound channel
Date: Fri, 6 Nov 2026 20:06:21 +0100 [thread overview]
Message-ID: <20261106190622.20990-4-mike.michalo@gmail.com> (raw)
In-Reply-To: <20261106190622.20990-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. Do not expire the session
when either check fails, since the request did not come from its
owner.
For a SESSION_SETUP that is not the first command of a compound,
__process_request() already checks the signature and zeroes the
Signature field, so the new reauthentication check then failed. Restore
the field after that check.
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/server.c | 11 +++++
fs/smb/server/smb2pdu.c | 90 ++++++++++++++++++++++++++++-------------
2 files changed, 73 insertions(+), 28 deletions(-)
diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c
index 064f99427ca6..4430c8d6393e 100644
--- a/fs/smb/server/server.c
+++ b/fs/smb/server/server.c
@@ -150,7 +150,18 @@ static int __process_request(struct ksmbd_work *work, struct ksmbd_conn *conn,
}
if (work->sess && signed_req) {
+ struct smb2_hdr *hdr = ksmbd_req_buf_next(work);
+ char signature[SMB2_SIGNATURE_SIZE];
+
+ /*
+ * check_sign_req() zeroes the Signature field. Restore it:
+ * smb2_sess_setup() checks the signature of a SESSION_SETUP
+ * again and adds the request as received to the preauth
+ * integrity hash.
+ */
+ memcpy(signature, hdr->Signature, SMB2_SIGNATURE_SIZE);
ret = conn->ops->check_sign_req(work);
+ memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE);
if (!ret) {
conn->ops->set_rsp_status(work, STATUS_ACCESS_DENIED);
return SERVER_HANDLER_ABORT;
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index f587a76971e9..89cce31f7da6 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -2561,15 +2561,17 @@ 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.
+ * 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. The Signature field is restored afterwards, because SMB
+ * 3.1.1 adds the request as received to the preauth integrity hash.
*/
-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;
char signature[SMB2_SIGNATURE_SIZE];
@@ -2577,7 +2579,11 @@ static bool smb2_check_binding_sign_req(struct ksmbd_work *work,
memcpy(signature, hdr->Signature, SMB2_SIGNATURE_SIZE);
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;
memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE);
return ret;
@@ -2665,7 +2671,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;
}
@@ -2759,6 +2765,25 @@ int smb2_sess_setup(struct ksmbd_work *work)
goto out_err;
}
+ /*
+ * A reauthentication of a session that has keys must be
+ * signed if the session requires signing, and must verify.
+ * 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) :
+ sess->sign)) {
+ 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);
@@ -12982,22 +13007,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) {
@@ -13041,17 +13068,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-06 19:06 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-03 11:14 [PATCH 0/2] ksmbd: AES-GMAC signing support Michail Andreas Michalopoulos
2026-11-03 11:14 ` [PATCH 1/2] ksmbd: sign with the negotiated SMB 3.1.1 signing algorithm Michail Andreas Michalopoulos
2026-11-03 11:14 ` [PATCH 2/2] ksmbd: add AES-GMAC signing support Michail Andreas Michalopoulos
2026-11-03 17:17 ` Ian Bridges
2026-11-03 17:57 ` mixalhs
2026-11-04 4:30 ` Namjae Jeon
2026-11-06 19:06 ` [PATCH v2 0/4] ksmbd: verify session setup requests before signing with their key Michail Andreas Michalopoulos
2026-11-06 19:06 ` [PATCH v2 1/4] ksmbd: verify the signature of session binding requests Michail Andreas Michalopoulos
2026-11-06 19:06 ` [PATCH v2 2/4] ksmbd: sign with the negotiated SMB 3.1.1 signing algorithm Michail Andreas Michalopoulos
2026-11-06 19:06 ` Michail Andreas Michalopoulos [this message]
2026-11-06 19:06 ` [PATCH v2 4/4] ksmbd: check a request before signing STATUS_NETWORK_SESSION_EXPIRED Michail Andreas Michalopoulos
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=20261106190622.20990-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®