Signed-off-by: David Howells <dhowells@redhat.com>
cc: Steve French <sfrench@samba.org>
cc: Paulo Alcantara <pc@manguebit.org>
cc: Shyam Prasad N <sprasad@microsoft.com>
cc: Tom Talpey <tom@talpey.com>
cc: linux-cifs@vger.kernel.org
cc: netfs@lists.linux.dev
cc: linux-fsdevel@vger.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;
On 08/06, David Howells wrote:
> ...
>
>-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);
>+}
Why was this commented out? Your comment implies that you can't hold
the lock anymore there, but I couldn't find out why (with your patches
applied).
>-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);
If you're keeping data_len=400 above, you could just drop
smb2_size_netname_ctxt() altogether and use
"ALIGN8(sizeof(struct smb2_neg_context) + 400)" directly here.
Enzo Matsumiya <ematsumiya@suse.de> wrote:
> On 08/06, David Howells wrote:
> > ...
> > -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);
> >+}
>
> Why was this commented out? Your comment implies that you can't hold
> the lock anymore there, but I couldn't find out why (with your patches
> applied).
The problem is that the hostname may change - and there's a spinlock to
protect it. However, now that I'm working out the message size before the
allocation, I need to find the size of the host name, do the alloc and then
copy the hostname in - but I can't hold the spinlock across the alloc, so the
hostname may change whilst the lock is dropped.
The obvious solution is to just allocate the maximum size for it. It's not
that big and this command isn't used all that often.
Remember that this is a work in progress, so you may find bits like this where
I may need to reconsider what I've chosen.
> >-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);
>
> If you're keeping data_len=400 above, you could just drop
> smb2_size_netname_ctxt() altogether and use
> "ALIGN8(sizeof(struct smb2_neg_context) + 400)" directly here.
Yeah. Probably would make sense to do that with a comment saying why 400.
David
© 2016 - 2025 Red Hat, Inc.