lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250806203705.2560493-25-dhowells@redhat.com>
Date: Wed,  6 Aug 2025 21:36:45 +0100
From: David Howells <dhowells@...hat.com>
To: Steve French <sfrench@...ba.org>
Cc: David Howells <dhowells@...hat.com>,
	Paulo Alcantara <pc@...guebit.org>,
	Shyam Prasad N <sprasad@...rosoft.com>,
	Tom Talpey <tom@...pey.com>,
	Wang Zhaolong <wangzhaolong@...weicloud.com>,
	Stefan Metzmacher <metze@...ba.org>,
	Mina Almasry <almasrymina@...gle.com>,
	linux-cifs@...r.kernel.org,
	linux-kernel@...r.kernel.org,
	netfs@...ts.linux.dev,
	linux-fsdevel@...r.kernel.org
Subject: [RFC PATCH 24/31] cifs: Convert SMB2 Negotiate Protocol request

Signed-off-by: David Howells <dhowells@...hat.com>
cc: Steve French <sfrench@...ba.org>
cc: Paulo Alcantara <pc@...guebit.org>
cc: Shyam Prasad N <sprasad@...rosoft.com>
cc: Tom Talpey <tom@...pey.com>
cc: linux-cifs@...r.kernel.org
cc: netfs@...ts.linux.dev
cc: linux-fsdevel@...r.kernel.org
---
 fs/smb/client/smb2pdu.c | 509 ++++++++++++++++++++++++----------------
 fs/smb/common/smb2pdu.h |  24 +-
 fs/smb/server/smb2pdu.c |  22 +-
 3 files changed, 319 insertions(+), 236 deletions(-)

diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
index 58a2a4ff3368..f1b6d36fe7cd 100644
--- a/fs/smb/client/smb2pdu.c
+++ b/fs/smb/client/smb2pdu.c
@@ -788,144 +788,200 @@ static int smb2_ioctl_req_init(u32 opcode, struct cifs_tcon *tcon,
 
 /* For explanation of negotiate contexts see MS-SMB2 section 2.2.3.1 */
 
-static void
-build_preauth_ctxt(struct smb2_preauth_neg_context *pneg_ctxt)
+static void *cifs_begin_neg_context(struct smb_message *smb,
+				    __le16 context_type)
 {
-	pneg_ctxt->ContextType = SMB2_PREAUTH_INTEGRITY_CAPABILITIES;
-	pneg_ctxt->DataLength = cpu_to_le16(38);
-	pneg_ctxt->HashAlgorithmCount = cpu_to_le16(1);
-	pneg_ctxt->SaltLength = cpu_to_le16(SMB311_SALT_SIZE);
-	get_random_bytes(pneg_ctxt->Salt, SMB311_SALT_SIZE);
-	pneg_ctxt->HashAlgorithms = SMB2_PREAUTH_INTEGRITY_SHA512;
+	struct smb2_neg_context *neg;
+
+	neg = cifs_begin_extension(smb);
+	neg->ContextType	= context_type;
+	neg->Reserved		= 0;
+	return (void *)neg;
 }
 
-static void
-build_compression_ctxt(struct smb2_compression_capabilities_context *pneg_ctxt)
+static void cifs_end_neg_context(struct smb_message *smb, void *p, size_t size)
+{
+	struct smb2_neg_context *neg = p;
+
+	neg->DataLength = cpu_to_le16(size - sizeof(*neg));
+	cifs_end_extension(smb, size);
+}
+
+static void build_preauth_ctxt(struct smb_message *smb)
 {
-	pneg_ctxt->ContextType = SMB2_COMPRESSION_CAPABILITIES;
-	pneg_ctxt->DataLength =
-		cpu_to_le16(sizeof(struct smb2_compression_capabilities_context)
-			  - sizeof(struct smb2_neg_context));
-	pneg_ctxt->CompressionAlgorithmCount = cpu_to_le16(3);
-	pneg_ctxt->CompressionAlgorithms[0] = SMB3_COMPRESS_LZ77;
-	pneg_ctxt->CompressionAlgorithms[1] = SMB3_COMPRESS_LZ77_HUFF;
-	pneg_ctxt->CompressionAlgorithms[2] = SMB3_COMPRESS_LZNT1;
+	struct smb2_preauth_neg_context *preauth;
+
+	preauth = cifs_begin_neg_context(smb, SMB2_PREAUTH_INTEGRITY_CAPABILITIES);
+	preauth->HashAlgorithmCount	= cpu_to_le16(1);
+	preauth->SaltLength		= cpu_to_le16(SMB311_SALT_SIZE);
+	preauth->HashAlgorithms		= SMB2_PREAUTH_INTEGRITY_SHA512;
+	get_random_bytes(preauth->Salt, SMB311_SALT_SIZE);
+	cifs_end_neg_context(smb, preauth, sizeof(*preauth));
 }
 
-static unsigned int
-build_signing_ctxt(struct smb2_signing_capabilities *pneg_ctxt)
+static void build_compression_ctxt(struct smb_message *smb)
+{
+	struct smb2_compression_capabilities_context *compr;
+
+	compr = cifs_begin_neg_context(smb, SMB2_COMPRESSION_CAPABILITIES);
+	compr->CompressionAlgorithmCount = cpu_to_le16(3);
+	compr->CompressionAlgorithms[0] = SMB3_COMPRESS_LZ77;
+	compr->CompressionAlgorithms[1] = SMB3_COMPRESS_LZ77_HUFF;
+	compr->CompressionAlgorithms[2] = SMB3_COMPRESS_LZNT1;
+	cifs_end_neg_context(smb, compr, sizeof(*compr));
+}
+
+static size_t smb2_size_signing_ctxt(void)
+{
+	size_t ctxt_len = sizeof(struct smb2_signing_capabilities);
+	unsigned short num_algs = 1; /* number of signing algorithms sent */
+
+	ctxt_len += sizeof(__le16) * num_algs;
+	return ALIGN8(ctxt_len);
+}
+
+static void build_signing_ctxt(struct smb_message *smb)
 {
-	unsigned int ctxt_len = sizeof(struct smb2_signing_capabilities);
+	struct smb2_signing_capabilities *scap;
 	unsigned short num_algs = 1; /* number of signing algorithms sent */
 
-	pneg_ctxt->ContextType = SMB2_SIGNING_CAPABILITIES;
+	scap = cifs_begin_neg_context(smb, SMB2_SIGNING_CAPABILITIES);
+	scap->SigningAlgorithmCount = cpu_to_le16(num_algs);
+	scap->SigningAlgorithms[0] = cpu_to_le16(SIGNING_ALG_AES_CMAC);
+	/* TBD add SIGNING_ALG_AES_GMAC and/or SIGNING_ALG_HMAC_SHA256 */
+
 	/*
 	 * Context Data length must be rounded to multiple of 8 for some servers
 	 */
-	pneg_ctxt->DataLength = cpu_to_le16(ALIGN8(sizeof(struct smb2_signing_capabilities) -
-					    sizeof(struct smb2_neg_context) +
-					    (num_algs * sizeof(u16))));
-	pneg_ctxt->SigningAlgorithmCount = cpu_to_le16(num_algs);
-	pneg_ctxt->SigningAlgorithms[0] = cpu_to_le16(SIGNING_ALG_AES_CMAC);
-
-	ctxt_len += sizeof(__le16) * num_algs;
-	ctxt_len = ALIGN8(ctxt_len);
-	return ctxt_len;
-	/* TBD add SIGNING_ALG_AES_GMAC and/or SIGNING_ALG_HMAC_SHA256 */
+	cifs_end_neg_context(smb, scap,
+			     ALIGN8(struct_size(scap, SigningAlgorithms, num_algs)));
 }
 
-static void
-build_encrypt_ctxt(struct smb2_encryption_neg_context *pneg_ctxt)
+static void build_encrypt_ctxt(struct smb_message *smb)
 {
-	pneg_ctxt->ContextType = SMB2_ENCRYPTION_CAPABILITIES;
-	if (require_gcm_256) {
-		pneg_ctxt->DataLength = cpu_to_le16(4); /* Cipher Count + 1 cipher */
-		pneg_ctxt->CipherCount = cpu_to_le16(1);
-		pneg_ctxt->Ciphers[0] = SMB2_ENCRYPTION_AES256_GCM;
-	} else if (enable_gcm_256) {
-		pneg_ctxt->DataLength = cpu_to_le16(8); /* Cipher Count + 3 ciphers */
-		pneg_ctxt->CipherCount = cpu_to_le16(3);
-		pneg_ctxt->Ciphers[0] = SMB2_ENCRYPTION_AES128_GCM;
-		pneg_ctxt->Ciphers[1] = SMB2_ENCRYPTION_AES256_GCM;
-		pneg_ctxt->Ciphers[2] = SMB2_ENCRYPTION_AES128_CCM;
+	struct smb2_encryption_neg_context *ecap;
+	size_t count;
+
+	ecap = cifs_begin_neg_context(smb, SMB2_ENCRYPTION_CAPABILITIES);
+
+	if (READ_ONCE(require_gcm_256)) {
+		ecap->Ciphers[0]  = SMB2_ENCRYPTION_AES256_GCM;
+		count = 1;
+	} else if (READ_ONCE(enable_gcm_256)) {
+		ecap->Ciphers[0]  = SMB2_ENCRYPTION_AES128_GCM;
+		ecap->Ciphers[1]  = SMB2_ENCRYPTION_AES256_GCM;
+		ecap->Ciphers[2]  = SMB2_ENCRYPTION_AES128_CCM;
+		count = 3;
 	} else {
-		pneg_ctxt->DataLength = cpu_to_le16(6); /* Cipher Count + 2 ciphers */
-		pneg_ctxt->CipherCount = cpu_to_le16(2);
-		pneg_ctxt->Ciphers[0] = SMB2_ENCRYPTION_AES128_GCM;
-		pneg_ctxt->Ciphers[1] = SMB2_ENCRYPTION_AES128_CCM;
+		ecap->Ciphers[0]  = SMB2_ENCRYPTION_AES128_GCM;
+		ecap->Ciphers[1]  = SMB2_ENCRYPTION_AES128_CCM;
+		count = 2;
 	}
+	ecap->CipherCount = cpu_to_le16(count);
+	cifs_end_neg_context(smb, ecap, struct_size(ecap, Ciphers, count));
 }
 
-static unsigned int
-build_netname_ctxt(struct smb2_netname_neg_context *pneg_ctxt, char *hostname)
+static size_t smb2_size_netname_ctxt(struct TCP_Server_Info *server)
 {
+	size_t data_len;
+
+#if 0
 	struct nls_table *cp = load_nls_default();
+	const char *hostname;
 
-	pneg_ctxt->ContextType = SMB2_NETNAME_NEGOTIATE_CONTEXT_ID;
+	/* Only include up to first 100 bytes of server name in the NetName
+	 * field.
+	 */
+	cifs_server_lock(pserver);
+	hostname = pserver->hostname;
+	if (hostname && hostname[0])
+		data_len = cifs_size_strtoUTF16(hostname, 100, cp);
+	cifs_server_unlock(pserver);
+#else
+	/* Now, we can't just measure the length of hostname as, unless we hold
+	 * the lock, it may change under us, so allow maximum space for it.
+	 */
+	data_len = 400;
+#endif
+	return ALIGN8(sizeof(struct smb2_neg_context) + data_len);
+}
+
+static void build_netname_ctxt(struct smb_message *smb, const char *hostname)
+{
+	struct smb2_netname_neg_context *name;
+	struct nls_table *cp = load_nls_default();
+	size_t count;
+
+	name = cifs_begin_neg_context(smb, SMB2_NETNAME_NEGOTIATE_CONTEXT_ID);
 
 	/* copy up to max of first 100 bytes of server name to NetName field */
-	pneg_ctxt->DataLength = cpu_to_le16(2 * cifs_strtoUTF16(pneg_ctxt->NetName, hostname, 100, cp));
-	/* context size is DataLength + minimal smb2_neg_context */
-	return ALIGN8(le16_to_cpu(pneg_ctxt->DataLength) + sizeof(struct smb2_neg_context));
+	count = cifs_strtoUTF16(name->NetName, hostname, 100, cp);
+	cifs_end_neg_context(smb, name, struct_size(name, NetName, count));
 }
 
-static void
-build_posix_ctxt(struct smb2_posix_neg_context *pneg_ctxt)
+static void build_posix_ctxt(struct smb_message *smb)
 {
-	pneg_ctxt->ContextType = SMB2_POSIX_EXTENSIONS_AVAILABLE;
-	pneg_ctxt->DataLength = cpu_to_le16(POSIX_CTXT_DATA_LEN);
+	struct smb2_posix_neg_context *posix;
+
+	posix = cifs_begin_neg_context(smb, SMB2_POSIX_EXTENSIONS_AVAILABLE);
 	/* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */
-	pneg_ctxt->Name[0] = 0x93;
-	pneg_ctxt->Name[1] = 0xAD;
-	pneg_ctxt->Name[2] = 0x25;
-	pneg_ctxt->Name[3] = 0x50;
-	pneg_ctxt->Name[4] = 0x9C;
-	pneg_ctxt->Name[5] = 0xB4;
-	pneg_ctxt->Name[6] = 0x11;
-	pneg_ctxt->Name[7] = 0xE7;
-	pneg_ctxt->Name[8] = 0xB4;
-	pneg_ctxt->Name[9] = 0x23;
-	pneg_ctxt->Name[10] = 0x83;
-	pneg_ctxt->Name[11] = 0xDE;
-	pneg_ctxt->Name[12] = 0x96;
-	pneg_ctxt->Name[13] = 0x8B;
-	pneg_ctxt->Name[14] = 0xCD;
-	pneg_ctxt->Name[15] = 0x7C;
+	posix->Name[0] = 0x93;
+	posix->Name[1] = 0xAD;
+	posix->Name[2] = 0x25;
+	posix->Name[3] = 0x50;
+	posix->Name[4] = 0x9C;
+	posix->Name[5] = 0xB4;
+	posix->Name[6] = 0x11;
+	posix->Name[7] = 0xE7;
+	posix->Name[8] = 0xB4;
+	posix->Name[9] = 0x23;
+	posix->Name[10] = 0x83;
+	posix->Name[11] = 0xDE;
+	posix->Name[12] = 0x96;
+	posix->Name[13] = 0x8B;
+	posix->Name[14] = 0xCD;
+	posix->Name[15] = 0x7C;
+	cifs_end_neg_context(smb, posix, sizeof(posix));
 }
 
-static void
-assemble_neg_contexts(struct smb2_negotiate_req *req,
-		      struct TCP_Server_Info *server, unsigned int *total_len)
+static size_t smb2_size_neg_contexts(struct TCP_Server_Info *server,
+				     size_t offset)
 {
-	unsigned int ctxt_len, neg_context_count;
 	struct TCP_Server_Info *pserver;
-	char *pneg_ctxt;
-	char *hostname;
-
-	if (*total_len > 200) {
-		/* In case length corrupted don't want to overrun smb buffer */
-		cifs_server_dbg(VFS, "Bad frame length assembling neg contexts\n");
-		return;
-	}
 
 	/*
 	 * round up total_len of fixed part of SMB3 negotiate request to 8
 	 * byte boundary before adding negotiate contexts
 	 */
-	*total_len = ALIGN8(*total_len);
+	offset = ALIGN8(offset);
+	offset += ALIGN8(sizeof(struct smb2_preauth_neg_context));
+	offset += ALIGN8(sizeof(struct smb2_encryption_neg_context));
 
-	pneg_ctxt = (*total_len) + (char *)req;
-	req->NegotiateContextOffset = cpu_to_le32(*total_len);
+	/*
+	 * secondary channels don't have the hostname field populated
+	 * use the hostname field in the primary channel instead
+	 */
+	pserver = SERVER_IS_CHAN(server) ? server->primary_server : server;
+	offset += smb2_size_netname_ctxt(pserver);
+
+	offset += ALIGN8(sizeof(struct smb2_posix_neg_context));
+	if (server->compression.requested)
+		offset += ALIGN8(sizeof(struct smb2_compression_capabilities_context));
+	if (enable_negotiate_signing)
+		offset += smb2_size_signing_ctxt();
+	return offset;
+}
 
-	build_preauth_ctxt((struct smb2_preauth_neg_context *)pneg_ctxt);
-	ctxt_len = ALIGN8(sizeof(struct smb2_preauth_neg_context));
-	*total_len += ctxt_len;
-	pneg_ctxt += ctxt_len;
+static void
+assemble_neg_contexts(struct smb_message *smb, struct TCP_Server_Info *server)
+{
+	struct smb2_negotiate_req *req;
+	struct TCP_Server_Info *pserver;
+	const char *hostname;
+	unsigned int neg_context_count = 2;
 
-	build_encrypt_ctxt((struct smb2_encryption_neg_context *)pneg_ctxt);
-	ctxt_len = ALIGN8(sizeof(struct smb2_encryption_neg_context));
-	*total_len += ctxt_len;
-	pneg_ctxt += ctxt_len;
+	build_preauth_ctxt(smb);
+	build_encrypt_ctxt(smb);
 
 	/*
 	 * secondary channels don't have the hostname field populated
@@ -934,47 +990,36 @@ assemble_neg_contexts(struct smb2_negotiate_req *req,
 	pserver = SERVER_IS_CHAN(server) ? server->primary_server : server;
 	cifs_server_lock(pserver);
 	hostname = pserver->hostname;
-	if (hostname && (hostname[0] != 0)) {
-		ctxt_len = build_netname_ctxt((struct smb2_netname_neg_context *)pneg_ctxt,
-					      hostname);
-		*total_len += ctxt_len;
-		pneg_ctxt += ctxt_len;
-		neg_context_count = 3;
-	} else
-		neg_context_count = 2;
+	if (hostname && hostname[0]) {
+		build_netname_ctxt(smb, hostname);
+		neg_context_count++;
+	}
 	cifs_server_unlock(pserver);
 
-	build_posix_ctxt((struct smb2_posix_neg_context *)pneg_ctxt);
-	*total_len += sizeof(struct smb2_posix_neg_context);
-	pneg_ctxt += sizeof(struct smb2_posix_neg_context);
+	build_posix_ctxt(smb);
 	neg_context_count++;
 
 	if (server->compression.requested) {
-		build_compression_ctxt((struct smb2_compression_capabilities_context *)
-				pneg_ctxt);
-		ctxt_len = ALIGN8(sizeof(struct smb2_compression_capabilities_context));
-		*total_len += ctxt_len;
-		pneg_ctxt += ctxt_len;
+		build_compression_ctxt(smb);
 		neg_context_count++;
 	}
 
 	if (enable_negotiate_signing) {
-		ctxt_len = build_signing_ctxt((struct smb2_signing_capabilities *)
-				pneg_ctxt);
-		*total_len += ctxt_len;
-		pneg_ctxt += ctxt_len;
+		build_signing_ctxt(smb);
 		neg_context_count++;
 	}
 
 	/* check for and add transport_capabilities and signing capabilities */
+	req = smb->request;
 	req->NegotiateContextCount = cpu_to_le16(neg_context_count);
-
 }
 
 /* If invalid preauth context warn but use what we requested, SHA-512 */
-static void decode_preauth_context(struct smb2_preauth_neg_context *ctxt)
+static void decode_preauth_context(struct smb2_neg_context *neg)
 {
-	unsigned int len = le16_to_cpu(ctxt->DataLength);
+	struct smb2_preauth_neg_context *ctxt =
+		container_of(neg, struct smb2_preauth_neg_context, neg);
+	unsigned int len = le16_to_cpu(ctxt->neg.DataLength);
 
 	/*
 	 * Caller checked that DataLength remains within SMB boundary. We still
@@ -994,9 +1039,11 @@ static void decode_preauth_context(struct smb2_preauth_neg_context *ctxt)
 }
 
 static void decode_compress_ctx(struct TCP_Server_Info *server,
-			 struct smb2_compression_capabilities_context *ctxt)
+				struct smb2_neg_context *neg)
 {
-	unsigned int len = le16_to_cpu(ctxt->DataLength);
+	struct smb2_compression_capabilities_context *ctxt =
+		container_of(neg, struct smb2_compression_capabilities_context, neg);
+	unsigned int len = le16_to_cpu(ctxt->neg.DataLength);
 	__le16 alg;
 
 	server->compression.enabled = false;
@@ -1029,9 +1076,11 @@ static void decode_compress_ctx(struct TCP_Server_Info *server,
 }
 
 static int decode_encrypt_ctx(struct TCP_Server_Info *server,
-			      struct smb2_encryption_neg_context *ctxt)
+			      struct smb2_neg_context *neg)
 {
-	unsigned int len = le16_to_cpu(ctxt->DataLength);
+	struct smb2_encryption_neg_context *ctxt =
+		container_of(neg, struct smb2_encryption_neg_context, neg);
+	unsigned int len = le16_to_cpu(ctxt->neg.DataLength);
 
 	cifs_dbg(FYI, "decode SMB3.11 encryption neg context of len %d\n", len);
 	/*
@@ -1081,9 +1130,11 @@ static int decode_encrypt_ctx(struct TCP_Server_Info *server,
 }
 
 static void decode_signing_ctx(struct TCP_Server_Info *server,
-			       struct smb2_signing_capabilities *pctxt)
+			       struct smb2_neg_context *neg)
 {
-	unsigned int len = le16_to_cpu(pctxt->DataLength);
+	struct smb2_signing_capabilities *pctxt =
+		container_of(neg, struct smb2_signing_capabilities, neg);
+	unsigned int len = le16_to_cpu(pctxt->neg.DataLength);
 
 	/*
 	 * Caller checked that DataLength remains within SMB boundary. We still
@@ -1110,11 +1161,12 @@ static void decode_signing_ctx(struct TCP_Server_Info *server,
 }
 
 
-static int smb311_decode_neg_context(struct smb2_negotiate_rsp *rsp,
-				     struct TCP_Server_Info *server,
-				     unsigned int len_of_smb)
+static int smb311_decode_neg_context(struct smb_message *smb,
+				     struct TCP_Server_Info *server)
 {
+	struct smb2_negotiate_rsp *rsp = smb->response;
 	struct smb2_neg_context *pctx;
+	unsigned int len_of_smb = smb->response_len;
 	unsigned int offset = le32_to_cpu(rsp->NegotiateContextOffset);
 	unsigned int ctxt_cnt = le16_to_cpu(rsp->NegotiateContextCount);
 	unsigned int len_of_ctxts, i;
@@ -1147,23 +1199,26 @@ static int smb311_decode_neg_context(struct smb2_negotiate_rsp *rsp,
 		if (clen > len_of_ctxts)
 			break;
 
-		if (pctx->ContextType == SMB2_PREAUTH_INTEGRITY_CAPABILITIES)
-			decode_preauth_context(
-				(struct smb2_preauth_neg_context *)pctx);
-		else if (pctx->ContextType == SMB2_ENCRYPTION_CAPABILITIES)
-			rc = decode_encrypt_ctx(server,
-				(struct smb2_encryption_neg_context *)pctx);
-		else if (pctx->ContextType == SMB2_COMPRESSION_CAPABILITIES)
-			decode_compress_ctx(server,
-				(struct smb2_compression_capabilities_context *)pctx);
-		else if (pctx->ContextType == SMB2_POSIX_EXTENSIONS_AVAILABLE)
+		switch (pctx->ContextType) {
+		case SMB2_PREAUTH_INTEGRITY_CAPABILITIES:
+			decode_preauth_context(pctx);
+			break;
+		case SMB2_ENCRYPTION_CAPABILITIES:
+			rc = decode_encrypt_ctx(server, pctx);
+			break;
+		case SMB2_COMPRESSION_CAPABILITIES:
+			decode_compress_ctx(server, pctx);
+			break;
+		case SMB2_POSIX_EXTENSIONS_AVAILABLE:
 			server->posix_ext_supported = true;
-		else if (pctx->ContextType == SMB2_SIGNING_CAPABILITIES)
-			decode_signing_ctx(server,
-				(struct smb2_signing_capabilities *)pctx);
-		else
+			break;
+		case SMB2_SIGNING_CAPABILITIES:
+			decode_signing_ctx(server, pctx);
+			break;
+		default:
 			cifs_server_dbg(VFS, "unknown negcontext of type %d ignored\n",
 				le16_to_cpu(pctx->ContextType));
+		}
 		if (rc)
 			break;
 
@@ -1248,17 +1303,16 @@ SMB2_negotiate(const unsigned int xid,
 	       struct cifs_ses *ses,
 	       struct TCP_Server_Info *server)
 {
-	struct smb_rqst rqst;
 	struct smb2_negotiate_req *req;
 	struct smb2_negotiate_rsp *rsp;
-	struct kvec iov[1];
-	struct kvec rsp_iov;
+	struct smb_message *smb;
+	const char *vs;
+	size_t offset, num_dialects;
 	int rc;
-	int resp_buftype;
 	int blob_offset, blob_length;
 	char *security_blob;
 	int flags = CIFS_NEG_OP;
-	unsigned int total_len;
+	enum { DEF, ANY3, SPEC } version;
 
 	cifs_dbg(FYI, "Negotiate protocol\n");
 
@@ -1267,36 +1321,67 @@ SMB2_negotiate(const unsigned int xid,
 		return -EIO;
 	}
 
-	rc = smb2_plain_req_init(SMB2_NEGOTIATE, NULL, server,
-				 (void **) &req, &total_len);
+	rc = smb2_reconnect(SMB2_SESSION_SETUP, NULL, server, false);
 	if (rc)
 		return rc;
 
+	/* Calculate how much space we need for a Negotiate Request message.
+	 * We can't do this exactly as the hostname might change, but we don't
+	 * want to hold the lock across the allocation.
+	 */
+	vs = server->vals->version_string;
+	if (strcmp(vs, SMB3ANY_VERSION_STRING) == 0) {
+		version = ANY3;
+		num_dialects = 3;
+	} else if (strcmp(vs, SMBDEFAULT_VERSION_STRING) == 0) {
+		version = DEF;
+		num_dialects = 4;
+	} else {
+		version = SPEC;
+		num_dialects = 1;
+	}
+
+	offset = sizeof(struct smb2_negotiate_req);
+	offset += sizeof(req->Dialects[0]) * num_dialects;
+
+	if ((server->vals->protocol_id == SMB311_PROT_ID) ||
+	    (version == ANY3) ||
+	    (version == DEF))
+		offset = smb2_size_neg_contexts(server, offset);
+
+	smb = smb2_create_request(SMB2_NEGOTIATE, server, NULL,
+				  sizeof(*req), offset, 0,
+				  SMB2_REQ_HEAD);
+	if (!smb)
+		return -ENOMEM;
+
+	/* Fill in the message. */
+	req = smb->request;
 	req->hdr.SessionId = 0;
+	req->NegotiateContextOffset = cpu_to_be32(smb->ext_offset);
 
 	memset(server->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE);
 	memset(ses->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE);
 
-	if (strcmp(server->vals->version_string,
-		   SMB3ANY_VERSION_STRING) == 0) {
+	switch (version) {
+	case ANY3:
 		req->Dialects[0] = cpu_to_le16(SMB30_PROT_ID);
 		req->Dialects[1] = cpu_to_le16(SMB302_PROT_ID);
 		req->Dialects[2] = cpu_to_le16(SMB311_PROT_ID);
 		req->DialectCount = cpu_to_le16(3);
-		total_len += 6;
-	} else if (strcmp(server->vals->version_string,
-		   SMBDEFAULT_VERSION_STRING) == 0) {
+		break;
+	case DEF:
 		req->Dialects[0] = cpu_to_le16(SMB21_PROT_ID);
 		req->Dialects[1] = cpu_to_le16(SMB30_PROT_ID);
 		req->Dialects[2] = cpu_to_le16(SMB302_PROT_ID);
 		req->Dialects[3] = cpu_to_le16(SMB311_PROT_ID);
 		req->DialectCount = cpu_to_le16(4);
-		total_len += 8;
-	} else {
+		break;
+	case SPEC:
 		/* otherwise send specific dialect */
 		req->Dialects[0] = cpu_to_le16(server->vals->protocol_id);
 		req->DialectCount = cpu_to_le16(1);
-		total_len += 2;
+		break;
 	}
 
 	/* only one of SMB2 signing flags may be set in SMB2 request */
@@ -1312,97 +1397,108 @@ SMB2_negotiate(const unsigned int xid,
 		req->Capabilities |= cpu_to_le32(SMB2_GLOBAL_CAP_MULTI_CHANNEL);
 
 	/* ClientGUID must be zero for SMB2.02 dialect */
-	if (server->vals->protocol_id == SMB20_PROT_ID)
+	if (server->vals->protocol_id == SMB20_PROT_ID) {
 		memset(req->ClientGUID, 0, SMB2_CLIENT_GUID_SIZE);
-	else {
+	} else {
 		memcpy(req->ClientGUID, server->client_guid,
 			SMB2_CLIENT_GUID_SIZE);
 		if ((server->vals->protocol_id == SMB311_PROT_ID) ||
-		    (strcmp(server->vals->version_string,
-		     SMB3ANY_VERSION_STRING) == 0) ||
-		    (strcmp(server->vals->version_string,
-		     SMBDEFAULT_VERSION_STRING) == 0))
-			assemble_neg_contexts(req, server, &total_len);
+		    (version == ANY3) ||
+		    (version == DEF))
+			assemble_neg_contexts(smb, server);
 	}
-	iov[0].iov_base = (char *)req;
-	iov[0].iov_len = total_len;
 
-	memset(&rqst, 0, sizeof(struct smb_rqst));
-	rqst.rq_iov = iov;
-	rqst.rq_nvec = 1;
+	rc = smb_send_recv_messages(xid, ses, server, smb, flags);
+	smb_clear_request(smb);
 
-	rc = cifs_send_recv(xid, ses, server,
-			    &rqst, &resp_buftype, flags, &rsp_iov);
-	cifs_small_buf_release(req);
-	rsp = (struct smb2_negotiate_rsp *)rsp_iov.iov_base;
 	/*
 	 * No tcon so can't do
 	 * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]);
 	 */
-	if (rc == -EOPNOTSUPP) {
-		cifs_server_dbg(VFS, "Dialect not supported by server. Consider  specifying vers=1.0 or vers=2.0 on mount for accessing older servers\n");
-		goto neg_exit;
-	} else if (rc != 0)
+	if (rc != 0) {
+		if (rc == -EOPNOTSUPP)
+			cifs_server_dbg(VFS, "Dialect not supported by server. Consider  specifying vers=1.0 or vers=2.0 on mount for accessing older servers\n");
 		goto neg_exit;
+	}
 
+	/* ________________________________________
+	 * Decode the response.
+	 */
+	rsp = (struct smb2_negotiate_rsp *)smb->response;
+
+	int dialect_revision = le16_to_cpu(rsp->DialectRevision);
 	rc = -EIO;
-	if (strcmp(server->vals->version_string,
-		   SMB3ANY_VERSION_STRING) == 0) {
-		if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) {
+	switch (version) {
+	case ANY3:
+		switch (dialect_revision) {
+		case SMB20_PROT_ID:
 			cifs_server_dbg(VFS,
 				"SMB2 dialect returned but not requested\n");
 			goto neg_exit;
-		} else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) {
+		case SMB21_PROT_ID:
 			cifs_server_dbg(VFS,
 				"SMB2.1 dialect returned but not requested\n");
 			goto neg_exit;
-		} else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) {
+		case SMB311_PROT_ID:
 			/* ops set to 3.0 by default for default so update */
 			server->ops = &smb311_operations;
 			server->vals = &smb311_values;
+			break;
 		}
-	} else if (strcmp(server->vals->version_string,
-		   SMBDEFAULT_VERSION_STRING) == 0) {
-		if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) {
+		break;
+	case DEF:
+		switch (dialect_revision) {
+		case SMB20_PROT_ID:
 			cifs_server_dbg(VFS,
 				"SMB2 dialect returned but not requested\n");
 			goto neg_exit;
-		} else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) {
+		case SMB21_PROT_ID:
 			/* ops set to 3.0 by default for default so update */
 			server->ops = &smb21_operations;
 			server->vals = &smb21_values;
-		} else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) {
+			break;
+		case SMB311_PROT_ID:
 			server->ops = &smb311_operations;
 			server->vals = &smb311_values;
+			break;
 		}
-	} else if (le16_to_cpu(rsp->DialectRevision) !=
-				server->vals->protocol_id) {
-		/* if requested single dialect ensure returned dialect matched */
-		cifs_server_dbg(VFS, "Invalid 0x%x dialect returned: not requested\n",
-				le16_to_cpu(rsp->DialectRevision));
-		goto neg_exit;
+		break;
+	default:
+		if (dialect_revision != server->vals->protocol_id) {
+			/* if requested single dialect ensure returned dialect matched */
+			cifs_server_dbg(VFS, "Invalid 0x%x dialect returned: not requested\n",
+					dialect_revision);
+			goto neg_exit;
+		}
+		break;
 	}
 
 	cifs_dbg(FYI, "mode 0x%x\n", rsp->SecurityMode);
 
-	if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID))
+	switch (dialect_revision) {
+	case SMB20_PROT_ID:
 		cifs_dbg(FYI, "negotiated smb2.0 dialect\n");
-	else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID))
+		break;
+	case SMB21_PROT_ID:
 		cifs_dbg(FYI, "negotiated smb2.1 dialect\n");
-	else if (rsp->DialectRevision == cpu_to_le16(SMB30_PROT_ID))
+		break;
+	case SMB30_PROT_ID:
 		cifs_dbg(FYI, "negotiated smb3.0 dialect\n");
-	else if (rsp->DialectRevision == cpu_to_le16(SMB302_PROT_ID))
+		break;
+	case SMB302_PROT_ID:
 		cifs_dbg(FYI, "negotiated smb3.02 dialect\n");
-	else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID))
+		break;
+	case SMB311_PROT_ID:
 		cifs_dbg(FYI, "negotiated smb3.1.1 dialect\n");
-	else {
+		break;
+	default:
 		cifs_server_dbg(VFS, "Invalid dialect returned by server 0x%x\n",
-				le16_to_cpu(rsp->DialectRevision));
+				dialect_revision);
 		goto neg_exit;
 	}
 
 	rc = 0;
-	server->dialect = le16_to_cpu(rsp->DialectRevision);
+	server->dialect = dialect_revision;
 
 	/*
 	 * Keep a copy of the hash after negprot. This hash will be
@@ -1461,10 +1557,9 @@ SMB2_negotiate(const unsigned int xid,
 			rc = -EIO;
 	}
 
-	if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) {
+	if (server->dialect == SMB311_PROT_ID) {
 		if (rsp->NegotiateContextCount)
-			rc = smb311_decode_neg_context(rsp, server,
-						       rsp_iov.iov_len);
+			rc = smb311_decode_neg_context(smb, server);
 		else
 			cifs_server_dbg(VFS, "Missing expected negotiate contexts\n");
 	}
@@ -1472,7 +1567,7 @@ SMB2_negotiate(const unsigned int xid,
 	if (server->cipher_type && !rc)
 		rc = smb3_crypto_aead_allocate(server);
 neg_exit:
-	free_rsp_buf(resp_buftype, rsp);
+	smb_put_messages(smb);
 	return rc;
 }
 
diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h
index 7da40d229ab5..e3b888140b59 100644
--- a/fs/smb/common/smb2pdu.h
+++ b/fs/smb/common/smb2pdu.h
@@ -455,9 +455,7 @@ struct smb2_neg_context {
 #define MIN_PREAUTH_CTXT_DATA_LEN 6
 
 struct smb2_preauth_neg_context {
-	__le16	ContextType; /* 1 */
-	__le16	DataLength;
-	__le32	Reserved;
+	struct smb2_neg_context neg;
 	__le16	HashAlgorithmCount; /* 1 */
 	__le16	SaltLength;
 	__le16	HashAlgorithms; /* HashAlgorithms[0] since only one defined */
@@ -473,9 +471,7 @@ struct smb2_preauth_neg_context {
 /* Min encrypt context data is one cipher so 2 bytes + 2 byte count field */
 #define MIN_ENCRYPT_CTXT_DATA_LEN	4
 struct smb2_encryption_neg_context {
-	__le16	ContextType; /* 2 */
-	__le16	DataLength;
-	__le32	Reserved;
+	struct smb2_neg_context neg;
 	/* CipherCount usually 2, but can be 3 when AES256-GCM enabled */
 	__le16	CipherCount; /* AES128-GCM and AES128-CCM by default */
 	__le16	Ciphers[];
@@ -495,9 +491,7 @@ struct smb2_encryption_neg_context {
 #define SMB2_COMPRESSION_CAPABILITIES_FLAG_CHAINED	cpu_to_le32(0x00000001)
 
 struct smb2_compression_capabilities_context {
-	__le16	ContextType; /* 3 */
-	__le16  DataLength;
-	__le32	Reserved;
+	struct smb2_neg_context neg;
 	__le16	CompressionAlgorithmCount;
 	__le16	Padding;
 	__le32	Flags;
@@ -511,9 +505,7 @@ struct smb2_compression_capabilities_context {
  * Its struct simply contains NetName, an array of Unicode characters
  */
 struct smb2_netname_neg_context {
-	__le16	ContextType; /* 5 */
-	__le16	DataLength;
-	__le32	Reserved;
+	struct smb2_neg_context neg;
 	__le16	NetName[]; /* hostname of target converted to UCS-2 */
 } __packed;
 
@@ -567,9 +559,7 @@ struct smb2_rdma_transform_capabilities_context {
 #define SIGNING_ALG_AES_GMAC_LE    cpu_to_le16(2)
 
 struct smb2_signing_capabilities {
-	__le16	ContextType; /* 8 */
-	__le16	DataLength;
-	__le32	Reserved;
+	struct smb2_neg_context neg;
 	__le16	SigningAlgorithmCount;
 	__le16	SigningAlgorithms[];
 	/*  Followed by padding to 8 byte boundary (required by some servers) */
@@ -577,9 +567,7 @@ struct smb2_signing_capabilities {
 
 #define POSIX_CTXT_DATA_LEN	16
 struct smb2_posix_neg_context {
-	__le16	ContextType; /* 0x100 */
-	__le16	DataLength;
-	__le32	Reserved;
+	struct smb2_neg_context neg;
 	__u8	Name[16]; /* POSIX ctxt GUID 93AD25509CB411E7B42383DE968BCD7C */
 } __packed;
 
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index 1ed2bcba649f..af5187d1101e 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -774,10 +774,10 @@ static int smb2_get_dos_mode(struct kstat *stat, int attribute)
 static void build_preauth_ctxt(struct smb2_preauth_neg_context *pneg_ctxt,
 			       __le16 hash_id)
 {
-	pneg_ctxt->ContextType = SMB2_PREAUTH_INTEGRITY_CAPABILITIES;
-	pneg_ctxt->DataLength = cpu_to_le16(38);
+	pneg_ctxt->neg.ContextType = SMB2_PREAUTH_INTEGRITY_CAPABILITIES;
+	pneg_ctxt->neg.DataLength = cpu_to_le16(38);
+	pneg_ctxt->neg.Reserved = cpu_to_le32(0);
 	pneg_ctxt->HashAlgorithmCount = cpu_to_le16(1);
-	pneg_ctxt->Reserved = cpu_to_le32(0);
 	pneg_ctxt->SaltLength = cpu_to_le16(SMB311_SALT_SIZE);
 	get_random_bytes(pneg_ctxt->Salt, SMB311_SALT_SIZE);
 	pneg_ctxt->HashAlgorithms = hash_id;
@@ -786,9 +786,9 @@ static void build_preauth_ctxt(struct smb2_preauth_neg_context *pneg_ctxt,
 static void build_encrypt_ctxt(struct smb2_encryption_neg_context *pneg_ctxt,
 			       __le16 cipher_type)
 {
-	pneg_ctxt->ContextType = SMB2_ENCRYPTION_CAPABILITIES;
-	pneg_ctxt->DataLength = cpu_to_le16(4);
-	pneg_ctxt->Reserved = cpu_to_le32(0);
+	pneg_ctxt->neg.ContextType = SMB2_ENCRYPTION_CAPABILITIES;
+	pneg_ctxt->neg.DataLength = cpu_to_le16(4);
+	pneg_ctxt->neg.Reserved = cpu_to_le32(0);
 	pneg_ctxt->CipherCount = cpu_to_le16(1);
 	pneg_ctxt->Ciphers[0] = cipher_type;
 }
@@ -796,19 +796,19 @@ static void build_encrypt_ctxt(struct smb2_encryption_neg_context *pneg_ctxt,
 static void build_sign_cap_ctxt(struct smb2_signing_capabilities *pneg_ctxt,
 				__le16 sign_algo)
 {
-	pneg_ctxt->ContextType = SMB2_SIGNING_CAPABILITIES;
-	pneg_ctxt->DataLength =
+	pneg_ctxt->neg.ContextType = SMB2_SIGNING_CAPABILITIES;
+	pneg_ctxt->neg.DataLength =
 		cpu_to_le16((sizeof(struct smb2_signing_capabilities) + 2)
 			- sizeof(struct smb2_neg_context));
-	pneg_ctxt->Reserved = cpu_to_le32(0);
+	pneg_ctxt->neg.Reserved = cpu_to_le32(0);
 	pneg_ctxt->SigningAlgorithmCount = cpu_to_le16(1);
 	pneg_ctxt->SigningAlgorithms[0] = sign_algo;
 }
 
 static void build_posix_ctxt(struct smb2_posix_neg_context *pneg_ctxt)
 {
-	pneg_ctxt->ContextType = SMB2_POSIX_EXTENSIONS_AVAILABLE;
-	pneg_ctxt->DataLength = cpu_to_le16(POSIX_CTXT_DATA_LEN);
+	pneg_ctxt->neg.ContextType = SMB2_POSIX_EXTENSIONS_AVAILABLE;
+	pneg_ctxt->neg.DataLength = cpu_to_le16(POSIX_CTXT_DATA_LEN);
 	/* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */
 	pneg_ctxt->Name[0] = 0x93;
 	pneg_ctxt->Name[1] = 0xAD;


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ