Skip to contentSkip to conversation

cifs: remove rfc1002 length field from all SMB2 response structures

PATCH In review Ronnie Sahlberg 2 participants 4 messages Jan 21, 2018 – Jan 26, 2018 1 message not in the archive

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

cover cifs: remove rfc1002 length field from all SMB2 response structures

Steve, All

Review but do not merge to for-next. This patch still breaks SMBD. I have tested the patch with SMB1/2/3 with and without signing as well as with SMB3 encryption and it works in my tests.

The first patch removes the rfc1002 length from all the response structures hopefully making it easier to implement compounding later. It also gets rid of a lot of magic add/subtract 4 from various computations.

The second patch updates the demultiplex loop so that it will now be able to handle compounded responses we receive from the server. In theory, we will not know it this works until we can actually send compounded requests.

Next steps will be to start implementing the actual compounding. That, I think, will consist of refactoring the SMB2_open/query/close functions into dedicated marshalling functions and then create a new compound_send_recv() function.
The second part will require some additional plumbing.
In smb2_send_recv() we currently create iovectors as
[0] length
[1] smb2 header
[2+] additional data

or, when smb3 encryption is used

[0] length
[1] transform header
[2] smb2 header
[3+] additional data

Instead, in compound_send_recv() we will need to create an array of iovectors, one for each pdu, that would look like
[0] smb2 header
[1+] additional data

and similar for smb3 encryption.

These iovectors would then be propagated down the stack without an initial length field until we are ready to transmit them.
So, for the TCP case, we would not prepend the vectors we want to send with a [0] length field until the point where we are just about to start writing the data to the tcp socket.

That shouldnt be too hard. But we need these patches first.

A message that is not in the archive (sent off-list or not yet received): 20180121220644.7907-1-lsahlber-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org

#2 lore raw

PATCH 1/2 cifs: remove rfc1002 header from all SMB2 response structures

Separate out all the 4 byte rfc1002 headers so that they are no longer part of the SMB2 header structures to prepare for future work to add compounding support.

---
 fs/cifs/cifsglob.h      |   2 +
 fs/cifs/cifssmb.c       |   9 ++-
 fs/cifs/connect.c       |  32 ++++++---
 fs/cifs/misc.c          |   2 +-
 fs/cifs/smb1ops.c       |   1 +
 fs/cifs/smb2glob.h      |   5 --
 fs/cifs/smb2maperror.c  |   8 ++-
 fs/cifs/smb2misc.c      | 111 +++++++++++++++---------------
 fs/cifs/smb2ops.c       | 177 ++++++++++++++++++++++++++++++------------------
 fs/cifs/smb2pdu.c       | 105 ++++++++++++++--------------
 fs/cifs/smb2pdu.h       |  65 ++++++------------
 fs/cifs/smb2proto.h     |   5 +-
 fs/cifs/smb2transport.c |   6 +-
 fs/cifs/transport.c     |   4 +-
 14 files changed, 283 insertions(+), 249 deletions(-)
fs/cifs/cifsglob.h +2 -0
index 3fb1a2fe1ea9..14db722c4b0d 100644
@@ -460,6 +460,7 @@ struct smb_version_values {
 	__u32		exclusive_lock_type;
 	__u32		shared_lock_type;
 	__u32		unlock_lock_type;
+	size_t		header_preamble_size;
 	size_t		header_size;
 	size_t		max_header_size;
 	size_t		read_rsp_size;
@@ -656,6 +657,7 @@ struct TCP_Server_Info {
 	struct delayed_work	echo; /* echo ping workqueue job */
 	char	*smallbuf;	/* pointer to current "small" buffer */
 	char	*bigbuf;	/* pointer to current "big" buffer */
+	unsigned int total_size; /* Total size of this PDU */
 	unsigned int total_read; /* total amount of data read in this pass */
 #ifdef CONFIG_CIFS_FSCACHE
 	struct fscache_cookie   *fscache; /* client index cache cookie */
fs/cifs/cifssmb.c +6 -3
index 35dc5bf01ee2..114e2a7ca77f 100644
@@ -1453,7 +1453,9 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
 	unsigned int data_offset, data_len;
 	struct cifs_readdata *rdata = mid->callback_data;
 	char *buf = server->smallbuf;
-	unsigned int buflen = get_rfc1002_length(buf) + 4;
+	unsigned int buflen;
+
+	buflen = server->total_size + server->vals->header_preamble_size;
 
 	cifs_dbg(FYI, "%s: mid=%llu offset=%llu bytes=%u\n",
 		 __func__, mid->mid, rdata->offset, rdata->bytes);
@@ -1464,7 +1466,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
 	 * the Mid.
 	 */
 	len = min_t(unsigned int, buflen, server->vals->read_rsp_size) -
-							HEADER_SIZE(server) + 1;
+		HEADER_SIZE(server) + 1;
 
 	length = cifs_read_from_socket(server,
 				       buf + HEADER_SIZE(server) - 1, len);
@@ -1502,7 +1504,8 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
 		return cifs_readv_discard(server, mid);
 	}
 
-	data_offset = server->ops->read_data_offset(buf) + 4;
+	data_offset = server->ops->read_data_offset(buf) +
+		server->vals->header_preamble_size;
 	if (data_offset < server->total_read) {
 		/*
 		 * win2k8 sometimes sends an offset of 0 when the read
fs/cifs/connect.c +22 -10
index 64be6f9e54a2..18a1cb4f2330 100644
@@ -763,10 +763,11 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid)
 {
 	int length;
 	char *buf = server->smallbuf;
-	unsigned int pdu_length = get_rfc1002_length(buf);
+	unsigned int pdu_length = server->total_size;
 
 	/* make sure this will fit in a large buffer */
-	if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server) - 4) {
+	if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server) -
+		server->vals->header_preamble_size) {
 		cifs_dbg(VFS, "SMB response too long (%u bytes)\n", pdu_length);
 		cifs_reconnect(server);
 		wake_up(&server->response_q);
@@ -781,8 +782,11 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid)
 	}
 
 	/* now read the rest */
-	length = cifs_read_from_socket(server, buf + HEADER_SIZE(server) - 1,
-				pdu_length - HEADER_SIZE(server) + 1 + 4);
+	length = cifs_read_from_socket(server,
+				       buf + HEADER_SIZE(server) - 1,
+				       pdu_length - HEADER_SIZE(server) + 1
+				       + server->vals->header_preamble_size);
+
 	if (length < 0)
 		return length;
 	server->total_read += length;
@@ -862,20 +866,26 @@ cifs_demultiplex_thread(void *p)
 		length = cifs_read_from_socket(server, buf, pdu_length);
 		if (length < 0)
 			continue;
-		server->total_read = length;
+
+		if (server->vals->header_preamble_size == 0)
+			server->total_read = 0;
+		else
+			server->total_read = length;
 
 		/*
 		 * The right amount was read from socket - 4 bytes,
 		 * so we can now interpret the length field.
 		 */
 		pdu_length = get_rfc1002_length(buf);
+		server->total_size = pdu_length;
 
 		cifs_dbg(FYI, "RFC1002 header 0x%x\n", pdu_length);
 		if (!is_smb_response(server, buf[0]))
 			continue;
 
 		/* make sure we have enough to get to the MID */
-		if (pdu_length < HEADER_SIZE(server) - 1 - 4) {
+		if (pdu_length < HEADER_SIZE(server) - 1 -
+		    server->vals->header_preamble_size) {
 			cifs_dbg(VFS, "SMB response too short (%u bytes)\n",
 				 pdu_length);
 			cifs_reconnect(server);
@@ -884,18 +894,20 @@ cifs_demultiplex_thread(void *p)
 		}
 
 		/* read down to the MID */
-		length = cifs_read_from_socket(server, buf + 4,
-					       HEADER_SIZE(server) - 1 - 4);
+		length = cifs_read_from_socket(server,
+			     buf + server->vals->header_preamble_size,
+			     HEADER_SIZE(server) - 1
+			     - server->vals->header_preamble_size);
 		if (length < 0)
 			continue;
 		server->total_read += length;
 
 		if (server->ops->is_transform_hdr &&
 		    server->ops->receive_transform &&
-		    server->ops->is_transform_hdr(buf)) {
+		    server->ops->is_transform_hdr(buf))
 			length = server->ops->receive_transform(server,
 								&mid_entry);
-		} else {
+		else {
 			mid_entry = server->ops->find_mid(server, buf);
 
 			if (!mid_entry || !mid_entry->receive)
fs/cifs/misc.c +1 -1
index eea93ac15ef0..37022a34982b 100644
@@ -151,7 +151,7 @@ cifs_buf_get(void)
 	 * SMB2 header is bigger than CIFS one - no problems to clean some
 	 * more bytes for CIFS.
 	 */
-	size_t buf_size = sizeof(struct smb2_hdr);
+	size_t buf_size = sizeof(struct smb2_sync_hdr);
 
 	/*
 	 * We could use negotiated size instead of max_msgsize -
fs/cifs/smb1ops.c +1 -0
index a723df3e0197..4c87265050fc 100644
@@ -1120,6 +1120,7 @@ struct smb_version_values smb1_values = {
 	.exclusive_lock_type = 0,
 	.shared_lock_type = LOCKING_ANDX_SHARED_LOCK,
 	.unlock_lock_type = 0,
+	.header_preamble_size = 4,
 	.header_size = sizeof(struct smb_hdr),
 	.max_header_size = MAX_CIFS_HDR_SIZE,
 	.read_rsp_size = sizeof(READ_RSP),
fs/cifs/smb2glob.h +0 -5
index 401a5d856636..0ffa18094335 100644
@@ -61,9 +61,4 @@
 /* Maximum buffer size value we can send with 1 credit */
 #define SMB2_MAX_BUFFER_SIZE 65536
 
-static inline struct smb2_sync_hdr *get_sync_hdr(void *buf)
-{
-	return &(((struct smb2_hdr *)buf)->sync_hdr);
-}
-
 #endif	/* _SMB2_GLOB_H */
fs/cifs/smb2maperror.c +6 -2
index 62c88dfed57b..8484058346cd 100644
@@ -2450,10 +2450,14 @@ smb2_print_status(__le32 status)
 int
 map_smb2_to_linux_error(char *buf, bool log_err)
 {
-	struct smb2_sync_hdr *shdr = get_sync_hdr(buf);
+	struct smb2_sync_hdr *shdr;
 	unsigned int i;
 	int rc = -EIO;
-	__le32 smb2err = shdr->Status;
+	__le32 smb2err;
+
+	shdr = (struct smb2_sync_hdr *)buf;
+
+	smb2err = shdr->Status;
 
 	if (smb2err == 0)
 		return 0;
fs/cifs/smb2misc.c +56 -55

247 lines not on this page · raw message

fs/cifs/smb2ops.c +112 -65

448 lines not on this page · raw message

fs/cifs/smb2pdu.c +49 -56

296 lines not on this page · raw message

fs/cifs/smb2pdu.h +19 -46

189 lines not on this page · raw message

fs/cifs/smb2proto.h +3 -2
index e9ab5227e7a8..089a6ef936b8 100644
@@ -37,7 +37,8 @@ extern int map_smb2_to_linux_error(char *buf, bool log_err);
 extern int smb2_check_message(char *buf, unsigned int length,
 			      struct TCP_Server_Info *server);
 extern unsigned int smb2_calc_size(void *buf);
-extern char *smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr);
+extern char *smb2_get_data_area_len(int *off, int *len,
+				    struct smb2_sync_hdr *shdr);
 extern __le16 *cifs_convert_path_to_utf16(const char *from,
 					  struct cifs_sb_info *cifs_sb);
 
@@ -122,7 +123,7 @@ extern int SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon);
 extern int SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms,
 		     __le16 *path, __u8 *oplock,
 		     struct smb2_file_all_info *buf,
-		     struct smb2_err_rsp **err_buf);
+		     struct kvec *err_iov);
 extern int SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon,
 		     u64 persistent_fid, u64 volatile_fid, u32 opcode,
 		     bool is_fsctl, bool use_ipc,
fs/cifs/smb2transport.c +3 -3
index 99493946e2f9..cf37c6651bf6 100644
@@ -624,10 +624,10 @@ smb2_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
 	struct smb_rqst rqst = { .rq_iov = iov,
 				 .rq_nvec = 2 };
 
-	iov[0].iov_base = (char *)mid->resp_buf;
+	iov[0].iov_base = NULL;
 	iov[0].iov_len = 4;
-	iov[1].iov_base = (char *)mid->resp_buf + 4;
-	iov[1].iov_len = len;
+	iov[1].iov_base = (char *)mid->resp_buf;
+	iov[1].iov_len = server->total_size;
 
 	dump_smb(mid->resp_buf, min_t(u32, 80, len));
 	/* convert the length into a more usable form */
fs/cifs/transport.c +3 -1
index 510f41a435c8..5cecf67af8f1 100644
@@ -779,7 +779,9 @@ cifs_send_recv(const unsigned int xid, struct cifs_ses *ses,
 
 	buf = (char *)midQ->resp_buf;
 	resp_iov->iov_base = buf;
-	resp_iov->iov_len = get_rfc1002_length(buf) + 4;
+	resp_iov->iov_len = ses->server->total_size +
+		ses->server->vals->header_preamble_size;
+
 	if (midQ->large_buf)
 		*resp_buf_type = CIFS_LARGE_BUFFER;
 	else
Signature · 1 line
2.13.3
#3 lore raw

PATCH 2/2 cifs: update multiplex loop to handle compounded responses

In reply to a message not in the archive
---
 fs/cifs/cifsglob.h |  2 +-
 fs/cifs/connect.c  | 18 +++++++++++++++---
 fs/cifs/smb2ops.c  | 26 ++++++++++++++++++++++++--
 3 files changed, 40 insertions(+), 6 deletions(-)
fs/cifs/cifsglob.h +1 -1
index 14db722c4b0d..18a961d32e0c 100644
@@ -449,7 +449,7 @@ struct smb_version_operations {
 				 struct mid_q_entry **);
 	enum securityEnum (*select_sectype)(struct TCP_Server_Info *,
 			    enum securityEnum);
-
+	int (*next_header)(char *);
 };
 
 struct smb_version_values {
fs/cifs/connect.c +15 -3
index 18a1cb4f2330..9bf79e1f0a05 100644
@@ -840,6 +840,7 @@ cifs_demultiplex_thread(void *p)
 	int length;
 	struct TCP_Server_Info *server = p;
 	unsigned int pdu_length;
+	unsigned int next_offset;
 	char *buf = NULL;
 	struct task_struct *task_to_wake = NULL;
 	struct mid_q_entry *mid_entry;
@@ -877,17 +878,18 @@ cifs_demultiplex_thread(void *p)
 		 * so we can now interpret the length field.
 		 */
 		pdu_length = get_rfc1002_length(buf);
+next_pdu:
 		server->total_size = pdu_length;
 
-		cifs_dbg(FYI, "RFC1002 header 0x%x\n", pdu_length);
+		cifs_dbg(FYI, "RFC1002 header 0x%x\n", server->total_size);
 		if (!is_smb_response(server, buf[0]))
 			continue;
 
 		/* make sure we have enough to get to the MID */
-		if (pdu_length < HEADER_SIZE(server) - 1 -
+		if (server->total_size < HEADER_SIZE(server) - 1 -
 		    server->vals->header_preamble_size) {
 			cifs_dbg(VFS, "SMB response too short (%u bytes)\n",
-				 pdu_length);
+				 server->total_size);
 			cifs_reconnect(server);
 			wake_up(&server->response_q);
 			continue;
@@ -902,6 +904,12 @@ cifs_demultiplex_thread(void *p)
 			continue;
 		server->total_read += length;
 
+		if (server->ops->next_header) {
+			next_offset = server->ops->next_header(buf);
+			if (next_offset)
+				server->total_size = next_offset;
+		}
+
 		if (server->ops->is_transform_hdr &&
 		    server->ops->receive_transform &&
 		    server->ops->is_transform_hdr(buf))
@@ -947,6 +955,10 @@ cifs_demultiplex_thread(void *p)
 			cifs_dump_mids(server);
 #endif /* CIFS_DEBUG2 */
 
+			if (pdu_length > server->total_size) {
+				pdu_length -= server->total_size;
+				goto next_pdu;
+			}
 		}
 	} /* end while !EXITING */
 
fs/cifs/smb2ops.c +24 -2
index fef40876a77a..58b12ed106cd 100644
@@ -125,8 +125,8 @@ smb2_get_credits(struct mid_q_entry *mid)
 	char *buf = mid->resp_buf;
 	struct smb2_sync_hdr *shdr;
 
-	if ( *(__u32 *)buf == SMB2_PROTO_NUMBER ||
-	     *(__u32 *)buf == SMB2_TRANSFORM_PROTO_NUM)
+	if ( *(__le32 *)buf == SMB2_PROTO_NUMBER ||
+	     *(__le32 *)buf == SMB2_TRANSFORM_PROTO_NUM)
 		shdr = (struct smb2_sync_hdr *)buf;
 	else
 		shdr = (struct smb2_sync_hdr *)(buf + 4);
@@ -2718,6 +2718,24 @@ smb3_handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid)
 				NULL, 0, 0);
 }
 
+static int
+smb2_next_header(char *buf)
+{
+	struct smb2_sync_hdr *hdr = (struct smb2_sync_hdr *)buf;
+	struct smb2_transform_hdr *t_hdr = (struct smb2_transform_hdr *)buf;
+
+	/* FIXME: compounding.
+	 * How does compounding work with smb3 encryption?
+	 * For now, assume that we will have one transform header for each
+	 * smb2 header (and assume that smb2->NextOffset is 0 for all of them.)
+	 */
+	if (hdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM)
+		return sizeof(struct smb2_transform_hdr) +
+		  le32_to_cpu(t_hdr->OriginalMessageSize);
+
+	return le32_to_cpu(hdr->NextCommand);
+}
+
 struct smb_version_operations smb20_operations = {
 	.compare_fids = smb2_compare_fids,
 	.setup_request = smb2_setup_request,
@@ -2809,6 +2827,7 @@ struct smb_version_operations smb20_operations = {
 	.get_acl_by_fid = get_smb2_acl_by_fid,
 	.set_acl = set_smb2_acl,
 #endif /* CIFS_ACL */
+	.next_header = smb2_next_header,
 };
 
 struct smb_version_operations smb21_operations = {
@@ -2903,6 +2922,7 @@ struct smb_version_operations smb21_operations = {
 	.get_acl_by_fid = get_smb2_acl_by_fid,
 	.set_acl = set_smb2_acl,
 #endif /* CIFS_ACL */
+	.next_header = smb2_next_header,
 };
 
 struct smb_version_operations smb30_operations = {
@@ -3007,6 +3027,7 @@ struct smb_version_operations smb30_operations = {
 	.get_acl_by_fid = get_smb2_acl_by_fid,
 	.set_acl = set_smb2_acl,
 #endif /* CIFS_ACL */
+	.next_header = smb2_next_header,
 };
 
 #ifdef CONFIG_CIFS_SMB311
@@ -3107,6 +3128,7 @@ struct smb_version_operations smb311_operations = {
 	.query_all_EAs = smb2_query_eas,
 	.set_EA = smb2_set_ea,
 #endif /* CIFS_XATTR */
+	.next_header = smb2_next_header,
 };
 #endif /* CIFS_SMB311 */
 
Signature · 1 line
2.13.3
#4 lore raw
In reply to a message not in the archive

Hi Ronnie,

These are dense patches and even though I have an idea of what it is supposed to do I'm not familiar enough with the data structures and their purpose to evaluate all of this right now (well I've put it aside for some time).

Could you guide us throught the added struct members and maybe tell us in which order we should start reading the changes to make this easier to grok?

Cheers,

Signature · 4 lines
Aurélien Aptel / SUSE Labs Samba Team
GPG: 1839 CB5F 9F5B FB9B AA97  8C99 03C8 A49B 521B D5D3
SUSE Linux GmbH, Maxfeldstraße 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)