[PATCH v4 4/6] ksmbd: check a request before signing STATUS_NETWORK_SESSION_EXPIRED
From: Michail Andreas Michalopoulos
Date: Tue Nov 10 2026 - 12:18:13 EST
When a session's Kerberos ticket has expired, smb2_check_user_session()
returns -EKEYEXPIRED and __handle_ksmbd_work() signs the
STATUS_NETWORK_SESSION_EXPIRED response if the request has
SMB2_FLAGS_SIGNED set. This happens before __process_request(), which
is where the MessageId and the request signature are checked. So the
response is signed with the session's key for a request whose
signature was never verified, at a MessageId that was never checked.
Anyone who can inject requests into the connection can then get
responses signed with that key at MessageIds of their choice: a
signing oracle. Reauthentication keeps the session's keys, so the effect
lasts for the rest of the session.
MS-SMB2 checks the MessageId (3.3.5.2.3) and the signature (3.3.5.2.4)
before the session (3.3.5.2.9). Do the same: sign the response only if
the request passes both checks, and otherwise answer with an unsigned
STATUS_INVALID_PARAMETER or STATUS_ACCESS_DENIED, as
__process_request() does.
For a later command of a compound, the response that is current at this
point is still the previous one, so the old code re-signed that response
instead. Leave the expired response of such a command unsigned.
An AI assistant was used to draft the patch and as an independent
reviewer.
Fixes: a47634cd729b ("ksmbd: expire SMB sessions when Kerberos tickets expire")
Reported-by: Ian Bridges <icb@xxxxxxxxxxxx>
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Michail Andreas Michalopoulos <mike.michalo@xxxxxxxxx>
---
fs/smb/server/server.c | 28 +++++++++++++++++++++++++++-
1 file changed, 27 insertions(+), 1 deletion(-)
diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c
index 064f99427ca6f..276dbbd986503 100644
--- a/fs/smb/server/server.c
+++ b/fs/smb/server/server.c
@@ -107,6 +107,32 @@ static inline int check_conn_state(struct ksmbd_work *work)
return 0;
}
+/*
+ * The keys of a session whose Kerberos ticket has expired are still valid,
+ * so STATUS_NETWORK_SESSION_EXPIRED is signed. Check the MessageId and the
+ * signature first (MS-SMB2 3.3.5.2.3 and 3.3.5.2.4), so that the key never
+ * signs a response to a request it did not sign.
+ */
+static void ksmbd_sign_expired_rsp(struct ksmbd_work *work)
+{
+ struct ksmbd_conn *conn = work->conn;
+
+ /*
+ * For a later command of a compound, the current response is still
+ * the previous one here, so signing would corrupt that response.
+ * Leave this one unsigned.
+ */
+ if (work->next_smb2_rcv_hdr_off)
+ return;
+
+ if (ksmbd_verify_smb_message(work))
+ conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER);
+ else if (!conn->ops->check_sign_req(work))
+ conn->ops->set_rsp_status(work, STATUS_ACCESS_DENIED);
+ else
+ conn->ops->set_sign_rsp(work);
+}
+
#define SERVER_HANDLER_CONTINUE 0
#define SERVER_HANDLER_ABORT 1
@@ -265,7 +291,7 @@ static void __handle_ksmbd_work(struct ksmbd_work *work,
rsp_hdr = ksmbd_resp_buf_curr(work);
if (rc == -EKEYEXPIRED && work->sess &&
conn->ops->set_sign_rsp)
- conn->ops->set_sign_rsp(work);
+ ksmbd_sign_expired_rsp(work);
else
rsp_hdr->Flags |= SMB2_FLAGS_SIGNED;
}
--
2.43.0