All of lore.kernel.org
 help / color / mirror / Atom feed
From: Namjae Jeon <linkinjeon@kernel.org>
To: linux-cifs@vger.kernel.org
Cc: smfrench@gmail.com, senozhatsky@chromium.org, tom@talpey.com,
	atteh.mailbox@gmail.com, Namjae Jeon <linkinjeon@kernel.org>
Subject: [PATCH] ksmbd: handle encrypted compressed requests
Date: Mon, 17 Aug 2026 21:15:22 +0900	[thread overview]
Message-ID: <20260817121524.8812-10-linkinjeon@kernel.org> (raw)
In-Reply-To: <20260817121524.8812-1-linkinjeon@kernel.org>

SMB3 permits a message to be compressed before it is encrypted. After
decrypting such a request, ksmbd must trim the AEAD tag using
OriginalMessageSize, decompress the nested compression transform, and
validate the resulting SMB2 PDU.

Share the decompression helper between the connection receive path and
the post-decryption work path so unencrypted and encrypted compressed
requests follow the same validation.

Fixes: a08de24c2b85 ("ksmbd: negotiate and decode SMB2 compression")
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
---
 fs/smb/server/compress.c | 76 ++++++++++++++++++++++++++++++----------
 fs/smb/server/compress.h |  1 +
 fs/smb/server/server.c   | 22 ++++++++++++
 fs/smb/server/smb2pdu.c  | 22 ++++++++----
 4 files changed, 97 insertions(+), 24 deletions(-)

diff --git a/fs/smb/server/compress.c b/fs/smb/server/compress.c
index 01d1771ff663..5162fb84c755 100644
--- a/fs/smb/server/compress.c
+++ b/fs/smb/server/compress.c
@@ -14,24 +14,14 @@
 
 #define SMB_COMPRESS_MIN_LEN	PAGE_SIZE
 
-/**
- * ksmbd_decompress_request() - replace a compressed request with its SMB2 PDU
- * @conn: connection which owns the current RFC1002 request buffer
- *
- * Derive the uncompressed size from the transform variant, enforce ksmbd's
- * normal message limits, and ask the common decoder to validate every payload.
- * On success, replace conn->request_buf with a regular RFC1002-framed SMB2
- * message so the rest of the request path needs no compression awareness.
- *
- * Return: 0 on success, otherwise a negative errno.
- */
-int ksmbd_decompress_request(struct ksmbd_conn *conn)
+static int __ksmbd_decompress_request(struct ksmbd_conn *conn,
+					     void *request_buf, void **out_buf)
 {
 	struct smb2_compression_hdr *hdr;
-	unsigned int pdu_size = get_rfc1002_len(conn->request_buf);
+	unsigned int pdu_size = get_rfc1002_len(request_buf);
 	u32 orig_size, offset, out_size;
 	u32 max_allowed_pdu_size;
-	char *buf, *out;
+	char *out;
 	int rc;
 
 	if (pdu_size < sizeof(struct smb2_compression_hdr))
@@ -41,7 +31,7 @@ int ksmbd_decompress_request(struct ksmbd_conn *conn)
 	    conn->compress_algorithm == SMB3_COMPRESS_NONE)
 		return -EINVAL;
 
-	hdr = smb_get_msg(conn->request_buf);
+	hdr = smb_get_msg(request_buf);
 	if (hdr->ProtocolId != SMB2_COMPRESSION_TRANSFORM_ID)
 		return -EINVAL;
 
@@ -74,19 +64,69 @@ int ksmbd_decompress_request(struct ksmbd_conn *conn)
 	if (!out)
 		return -ENOMEM;
 
-	buf = (char *)hdr;
 	*(__be32 *)out = cpu_to_be32(out_size);
 	rc = smb_compression_decompress(conn->compress_algorithm,
 					conn->compress_chained,
 					conn->compress_pattern,
-					buf, pdu_size, out + 4, out_size);
+					(char *)hdr, pdu_size, out + 4, out_size);
 	if (rc) {
 		kvfree(out);
 		return rc;
 	}
 
+	*out_buf = out;
+	return 0;
+}
+
+/**
+ * ksmbd_decompress_request() - replace a compressed request with its SMB2 PDU
+ * @conn: connection which owns the current RFC1002 request buffer
+ *
+ * Derive the uncompressed size from the transform variant, enforce ksmbd's
+ * normal message limits, and ask the common decoder to validate every payload.
+ * On success, replace conn->request_buf with a regular RFC1002-framed SMB2
+ * message so the rest of the request path needs no compression awareness.
+ *
+ * Return: 0 on success, otherwise a negative errno.
+ */
+int ksmbd_decompress_request(struct ksmbd_conn *conn)
+{
+	void *out_buf;
+	int rc;
+
+	rc = __ksmbd_decompress_request(conn, conn->request_buf, &out_buf);
+	if (rc)
+		return rc;
+
 	kvfree(conn->request_buf);
-	conn->request_buf = out;
+	conn->request_buf = out_buf;
+	return 0;
+}
+
+/**
+ * ksmbd_decompress_work_request() - decompress an encrypted work request
+ * @work: work item whose request buffer contains a compression transform
+ *
+ * SMB3 encrypts a compressed message by applying compression first and
+ * encryption second.  The receive loop can therefore only decode the
+ * compression transform before work allocation for an unencrypted request;
+ * an encrypted request must be decompressed after its encryption layer has
+ * been removed.
+ *
+ * Return: 0 on success, otherwise a negative errno.
+ */
+int ksmbd_decompress_work_request(struct ksmbd_work *work)
+{
+	void *out_buf;
+	int rc;
+
+	rc = __ksmbd_decompress_request(work->conn, work->request_buf,
+					&out_buf);
+	if (rc)
+		return rc;
+
+	kvfree(work->request_buf);
+	work->request_buf = out_buf;
 	return 0;
 }
 
diff --git a/fs/smb/server/compress.h b/fs/smb/server/compress.h
index 663c6f44f09b..13df2eb221e8 100644
--- a/fs/smb/server/compress.h
+++ b/fs/smb/server/compress.h
@@ -11,6 +11,7 @@
 #include "../common/compress/compress.h"
 
 int ksmbd_decompress_request(struct ksmbd_conn *conn);
+int ksmbd_decompress_work_request(struct ksmbd_work *work);
 int ksmbd_compress_response(struct ksmbd_work *work);
 
 #endif /* __KSMBD_COMPRESS_H__ */
diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c
index ba44bea9ddc3..6cfe8148da85 100644
--- a/fs/smb/server/server.c
+++ b/fs/smb/server/server.c
@@ -193,6 +193,28 @@ static void __handle_ksmbd_work(struct ksmbd_work *work,
 			return;
 		}
 		work->encrypted = true;
+
+		/*
+		 * SMB3 applies compression before encryption.  The receive loop
+		 * handles a plain compression transform before allocating work, but
+		 * an encrypted request exposes that transform only after decryption.
+		 */
+		if (((struct smb2_hdr *)smb_get_msg(work->request_buf))->ProtocolId ==
+		    SMB2_COMPRESSION_TRANSFORM_ID) {
+			rc = ksmbd_decompress_work_request(work);
+			if (rc < 0) {
+				ksmbd_conn_abort(conn);
+				return;
+			}
+		}
+
+		/* The decrypted payload must now be a complete SMB2 request. */
+		if (((struct smb2_hdr *)smb_get_msg(work->request_buf))->ProtocolId !=
+			SMB2_PROTO_NUMBER ||
+		    get_rfc1002_len(work->request_buf) < sizeof(struct smb2_pdu)) {
+			ksmbd_conn_abort(conn);
+			return;
+		}
 	}
 
 	if (conn->ops->allocate_rsp_buf(work))
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index 8d06c934f24f..a564535132e5 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -11817,18 +11817,27 @@ int smb3_decrypt_req(struct ksmbd_work *work)
 	char *buf = work->request_buf;
 	unsigned int pdu_length = get_rfc1002_len(buf);
 	struct kvec iov[2];
-	int buf_data_size = pdu_length - sizeof(struct smb2_transform_hdr);
+	unsigned int buf_data_size;
 	struct smb2_transform_hdr *tr_hdr = smb_get_msg(buf);
+	unsigned int original_msg_size;
 	int rc = 0;
 
-	if (pdu_length < sizeof(struct smb2_transform_hdr) ||
-	    buf_data_size < sizeof(struct smb2_hdr)) {
+	if (pdu_length < sizeof(struct smb2_transform_hdr)) {
 		pr_err("Transform message is too small (%u)\n",
 		       pdu_length);
 		return -ECONNABORTED;
 	}
 
-	if (buf_data_size < le32_to_cpu(tr_hdr->OriginalMessageSize)) {
+	buf_data_size = pdu_length - sizeof(struct smb2_transform_hdr);
+	original_msg_size = le32_to_cpu(tr_hdr->OriginalMessageSize);
+	if (buf_data_size < sizeof(struct smb2_compression_hdr) ||
+	    original_msg_size < sizeof(struct smb2_compression_hdr)) {
+		pr_err("Transform message is too small (%u)\n",
+		       pdu_length);
+		return -ECONNABORTED;
+	}
+
+	if (buf_data_size < original_msg_size) {
 		pr_err("Transform message is broken\n");
 		return -ECONNABORTED;
 	}
@@ -11841,8 +11850,9 @@ int smb3_decrypt_req(struct ksmbd_work *work)
 	if (rc)
 		return rc;
 
-	memmove(buf + 4, iov[1].iov_base, buf_data_size);
-	*(__be32 *)buf = cpu_to_be32(buf_data_size);
+	/* Drop the AEAD authentication tag from the inner RFC1002 frame. */
+	memmove(buf + 4, iov[1].iov_base, original_msg_size);
+	*(__be32 *)buf = cpu_to_be32(original_msg_size);
 
 	return rc;
 }
-- 
2.25.1


  parent reply	other threads:[~2026-08-17 12:15 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-17 12:15 [PATCH] ksmbd: accept unspecified volatile ID on durable reconnect Namjae Jeon
2026-08-17 12:15 ` [PATCH] ksmbd: implement SMB2 AppInstanceVersion takeover Namjae Jeon
2026-08-17 12:15 ` [PATCH] ksmbd: notify parent directory leases on child create Namjae Jeon
2026-08-17 12:15 ` [PATCH] ksmbd: add per-share SMB3 encryption enforcement Namjae Jeon
2026-08-17 12:15 ` [PATCH] ksmbd: fix encrypted request lookup on bound channels Namjae Jeon
2026-08-17 12:15 ` [PATCH] ksmbd: scope session state changes to bound connections Namjae Jeon
2026-08-17 12:15 ` [PATCH] ksmbd: encrypt interim responses to encrypted requests Namjae Jeon
2026-08-17 12:15 ` [PATCH] ksmbd: disconnect on SMB3 decryption failure Namjae Jeon
2026-08-17 12:15 ` [PATCH] ksmbd: decrypt requests from expired encrypted sessions Namjae Jeon
2026-08-17 12:15 ` Namjae Jeon [this message]
2026-08-17 12:15 ` [PATCH] ksmbd: add SMB Direct RDMA encryption transform Namjae Jeon
2026-08-17 12:15 ` [PATCH] ksmbd: make RDMA encryption diagnostics conditional Namjae Jeon

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=20260817121524.8812-10-linkinjeon@kernel.org \
    --to=linkinjeon@kernel.org \
    --cc=atteh.mailbox@gmail.com \
    --cc=linux-cifs@vger.kernel.org \
    --cc=senozhatsky@chromium.org \
    --cc=smfrench@gmail.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.