[PATCH v4 5/6] ksmbd: use the current command's header for signing in a compound

From: Michail Andreas Michalopoulos

Date: Tue Nov 10 2026 - 12:15:56 EST


Three places in the signing path read the wrong command of a compound
request instead of the command being processed:

- smb2_is_sign_req() always inspects smb_get_msg(work->request_buf)->Flags,
so the per-command signing decision in __process_request() follows the
first command's SMB2_FLAGS_SIGNED bit rather than the current one.
smb2_check_sign_req() already uses ksmbd_req_buf_next() for a chained
command; do the same here.

- When check_user_session() fails on a chained command,
set_smb2_rsp_status() writes the error into ksmbd_resp_buf_next(), but
__handle_ksmbd_work() then set SMB2_FLAGS_SIGNED on
ksmbd_resp_buf_curr(), i.e. the previous command's response. Flag the
response that actually holds the error.

- After each command, __handle_ksmbd_work() decides whether to sign its
response only after is_chained_smb2_message() has moved on to the next
command, so smb2_is_sign_req() and smb3_11_final_sess_setup_resp()
would see the next command's request and response. Decide before
moving on, and still sign afterwards, once NextCommand and the padding
of the response are final.

When the session does not require signing, this mattered in three ways:
an unsigned command after a signed one was verified anyway and refused
with STATUS_ACCESS_DENIED; a signed command after an unsigned one was
processed without its signature being checked, so a wrong signature
was accepted, contrary to MS-SMB2 3.3.5.2.4; and the response to that
signed command was not signed. When the session requires signing, an
unsigned first command is still refused and every signed command was
and is verified against its own header, so mandatory signing is not
bypassed.

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@xxxxxxxxxxxxxxx
Signed-off-by: Michail Andreas Michalopoulos <mike.michalo@xxxxxxxxx>
---
fs/smb/server/server.c | 27 +++++++++++++++++++++++----
fs/smb/server/smb2pdu.c | 4 ++++
2 files changed, 27 insertions(+), 4 deletions(-)

diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c
index 276dbbd986503..13341ff38aeeb 100644
--- a/fs/smb/server/server.c
+++ b/fs/smb/server/server.c
@@ -213,6 +213,7 @@ static void __handle_ksmbd_work(struct ksmbd_work *work,
u16 command = 0;
int rc;
bool is_chained = false;
+ bool sign_rsp;

if (conn->ops->is_transform_hdr &&
conn->ops->is_transform_hdr(work->request_buf)) {
@@ -288,7 +289,15 @@ static void __handle_ksmbd_work(struct ksmbd_work *work,
if (conn->ops->is_sign_req(work, conn->ops->get_cmd_val(work))) {
struct smb2_hdr *rsp_hdr;

- rsp_hdr = ksmbd_resp_buf_curr(work);
+ /*
+ * set_smb2_rsp_status() wrote the error
+ * into the next response for a chained
+ * command, so flag that one, not curr.
+ */
+ if (work->next_smb2_rcv_hdr_off)
+ rsp_hdr = ksmbd_resp_buf_next(work);
+ else
+ rsp_hdr = ksmbd_resp_buf_curr(work);
if (rc == -EKEYEXPIRED && work->sess &&
conn->ops->set_sign_rsp)
ksmbd_sign_expired_rsp(work);
@@ -343,11 +352,21 @@ static void __handle_ksmbd_work(struct ksmbd_work *work,

smb2_complete_request_open(work);

+ /*
+ * Decide now whether to sign this command's response:
+ * is_chained_smb2_message() moves on to the next command of a
+ * compound, after which the request and response helpers see
+ * that command. The response is still signed afterwards, once
+ * its NextCommand and padding are final.
+ */
+ sign_rsp = work->sess &&
+ (work->sess->sign ||
+ smb3_11_final_sess_setup_resp(work) ||
+ conn->ops->is_sign_req(work, command));
+
is_chained = is_chained_smb2_message(work);

- if (work->sess &&
- (work->sess->sign || smb3_11_final_sess_setup_resp(work) ||
- conn->ops->is_sign_req(work, command))) {
+ if (sign_rsp) {
if (command == SMB2_SESSION_SETUP_HE &&
work->sess->dialect >= SMB30_PROT_ID &&
conn->dialect < SMB30_PROT_ID)
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index 293e660265c95..f918744870a8f 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -12888,6 +12888,10 @@ bool smb2_is_sign_req(struct ksmbd_work *work, unsigned int command)
{
struct smb2_hdr *rcv_hdr2 = smb_get_msg(work->request_buf);

+ /* In a compound, test the current command, not the first one. */
+ if (work->next_smb2_rcv_hdr_off)
+ rcv_hdr2 = ksmbd_req_buf_next(work);
+
if ((rcv_hdr2->Flags & SMB2_FLAGS_SIGNED) &&
command != SMB2_NEGOTIATE_HE)
return true;
--
2.43.0