From nobody Thu Dec 18 12:32:33 2025 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 16B212D3233 for ; Wed, 6 Aug 2025 20:39:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1754512761; cv=none; b=YxEJBmxfyylvG0YgoTaGW8hr/oQB8+G72rr9ttUA6dZ8DAakFWUcN4PP7VMJhXXJj0SY5x/D2x7GDT+bBBvEdIBKuWDQaLzkR8Asz7SSH4jrZ2eRkUnqoI4UkRI7Z/YOrHfhVc2EPdj3lijbWt8MwzmaYjoP0BoVtL64tuqx89c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1754512761; c=relaxed/simple; bh=T2bSoxrSwOGDQ+OPDN6pZ89/dtL2KAnQ+Cou834QaRs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=koic+83NWch+R0XLoOnvlCA7cpJkszcNz1vbS0Fwwfp8XKQ7lgTE3NAXauNq2HFDmopQb2ZepO7vDoRUR+lxUKfrsmIH2g3ZnhEDovBzvoG+HrvxQZ4rHx1NlRWlK4avfLQECarR3OiNzhGdUXWynjSPEy6dPCGP6sz5Ez7vCgc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=Kn/QR/b9; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="Kn/QR/b9" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1754512757; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=HjeSK8GjGxfDr1EtNwWgacqn2lDSd3SKB7IJ3INJ84c=; b=Kn/QR/b9JzzuKjcCpBUOLe5stefCKtGiSvLkPRdX3xSI/MB+oF0z4C61qYCDPYLs2zkQN1 WSqMhFr5nWFzL5okklm6G5h25nen3h827XfAAtfBVVuR1iEy5WUKjJy58bIoHT8OIIXoGr 0fLnlD9ZP4/TjkIRQY0bmgzEQPrdZFs= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-671-qbmsm-QuPm2cVbAzIxy3XA-1; Wed, 06 Aug 2025 16:39:12 -0400 X-MC-Unique: qbmsm-QuPm2cVbAzIxy3XA-1 X-Mimecast-MFC-AGG-ID: qbmsm-QuPm2cVbAzIxy3XA_1754512750 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id CB54F1956094; Wed, 6 Aug 2025 20:39:10 +0000 (UTC) Received: from warthog.procyon.org.com (unknown [10.42.28.17]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 8238019560AD; Wed, 6 Aug 2025 20:39:07 +0000 (UTC) From: David Howells To: Steve French Cc: David Howells , Paulo Alcantara , Shyam Prasad N , Tom Talpey , Wang Zhaolong , Stefan Metzmacher , Mina Almasry , linux-cifs@vger.kernel.org, linux-kernel@vger.kernel.org, netfs@lists.linux.dev, linux-fsdevel@vger.kernel.org Subject: [RFC PATCH 24/31] cifs: Convert SMB2 Negotiate Protocol request Date: Wed, 6 Aug 2025 21:36:45 +0100 Message-ID: <20250806203705.2560493-25-dhowells@redhat.com> In-Reply-To: <20250806203705.2560493-1-dhowells@redhat.com> References: <20250806203705.2560493-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 Content-Type: text/plain; charset="utf-8" Signed-off-by: David Howells cc: Steve French cc: Paulo Alcantara cc: Shyam Prasad N cc: Tom Talpey 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 c= ifs_tcon *tcon, =20 /* For explanation of negotiate contexts see MS-SMB2 section 2.2.3.1 */ =20 -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 =3D SMB2_PREAUTH_INTEGRITY_CAPABILITIES; - pneg_ctxt->DataLength =3D cpu_to_le16(38); - pneg_ctxt->HashAlgorithmCount =3D cpu_to_le16(1); - pneg_ctxt->SaltLength =3D cpu_to_le16(SMB311_SALT_SIZE); - get_random_bytes(pneg_ctxt->Salt, SMB311_SALT_SIZE); - pneg_ctxt->HashAlgorithms =3D SMB2_PREAUTH_INTEGRITY_SHA512; + struct smb2_neg_context *neg; + + neg =3D cifs_begin_extension(smb); + neg->ContextType =3D context_type; + neg->Reserved =3D 0; + return (void *)neg; } =20 -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 =3D p; + + neg->DataLength =3D cpu_to_le16(size - sizeof(*neg)); + cifs_end_extension(smb, size); +} + +static void build_preauth_ctxt(struct smb_message *smb) { - pneg_ctxt->ContextType =3D SMB2_COMPRESSION_CAPABILITIES; - pneg_ctxt->DataLength =3D - cpu_to_le16(sizeof(struct smb2_compression_capabilities_context) - - sizeof(struct smb2_neg_context)); - pneg_ctxt->CompressionAlgorithmCount =3D cpu_to_le16(3); - pneg_ctxt->CompressionAlgorithms[0] =3D SMB3_COMPRESS_LZ77; - pneg_ctxt->CompressionAlgorithms[1] =3D SMB3_COMPRESS_LZ77_HUFF; - pneg_ctxt->CompressionAlgorithms[2] =3D SMB3_COMPRESS_LZNT1; + struct smb2_preauth_neg_context *preauth; + + preauth =3D cifs_begin_neg_context(smb, SMB2_PREAUTH_INTEGRITY_CAPABILITI= ES); + preauth->HashAlgorithmCount =3D cpu_to_le16(1); + preauth->SaltLength =3D cpu_to_le16(SMB311_SALT_SIZE); + preauth->HashAlgorithms =3D SMB2_PREAUTH_INTEGRITY_SHA512; + get_random_bytes(preauth->Salt, SMB311_SALT_SIZE); + cifs_end_neg_context(smb, preauth, sizeof(*preauth)); } =20 -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 =3D cifs_begin_neg_context(smb, SMB2_COMPRESSION_CAPABILITIES); + compr->CompressionAlgorithmCount =3D cpu_to_le16(3); + compr->CompressionAlgorithms[0] =3D SMB3_COMPRESS_LZ77; + compr->CompressionAlgorithms[1] =3D SMB3_COMPRESS_LZ77_HUFF; + compr->CompressionAlgorithms[2] =3D SMB3_COMPRESS_LZNT1; + cifs_end_neg_context(smb, compr, sizeof(*compr)); +} + +static size_t smb2_size_signing_ctxt(void) +{ + size_t ctxt_len =3D sizeof(struct smb2_signing_capabilities); + unsigned short num_algs =3D 1; /* number of signing algorithms sent */ + + ctxt_len +=3D sizeof(__le16) * num_algs; + return ALIGN8(ctxt_len); +} + +static void build_signing_ctxt(struct smb_message *smb) { - unsigned int ctxt_len =3D sizeof(struct smb2_signing_capabilities); + struct smb2_signing_capabilities *scap; unsigned short num_algs =3D 1; /* number of signing algorithms sent */ =20 - pneg_ctxt->ContextType =3D SMB2_SIGNING_CAPABILITIES; + scap =3D cifs_begin_neg_context(smb, SMB2_SIGNING_CAPABILITIES); + scap->SigningAlgorithmCount =3D cpu_to_le16(num_algs); + scap->SigningAlgorithms[0] =3D 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 =3D cpu_to_le16(ALIGN8(sizeof(struct smb2_signing_c= apabilities) - - sizeof(struct smb2_neg_context) + - (num_algs * sizeof(u16)))); - pneg_ctxt->SigningAlgorithmCount =3D cpu_to_le16(num_algs); - pneg_ctxt->SigningAlgorithms[0] =3D cpu_to_le16(SIGNING_ALG_AES_CMAC); - - ctxt_len +=3D sizeof(__le16) * num_algs; - ctxt_len =3D 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))); } =20 -static void -build_encrypt_ctxt(struct smb2_encryption_neg_context *pneg_ctxt) +static void build_encrypt_ctxt(struct smb_message *smb) { - pneg_ctxt->ContextType =3D SMB2_ENCRYPTION_CAPABILITIES; - if (require_gcm_256) { - pneg_ctxt->DataLength =3D cpu_to_le16(4); /* Cipher Count + 1 cipher */ - pneg_ctxt->CipherCount =3D cpu_to_le16(1); - pneg_ctxt->Ciphers[0] =3D SMB2_ENCRYPTION_AES256_GCM; - } else if (enable_gcm_256) { - pneg_ctxt->DataLength =3D cpu_to_le16(8); /* Cipher Count + 3 ciphers */ - pneg_ctxt->CipherCount =3D cpu_to_le16(3); - pneg_ctxt->Ciphers[0] =3D SMB2_ENCRYPTION_AES128_GCM; - pneg_ctxt->Ciphers[1] =3D SMB2_ENCRYPTION_AES256_GCM; - pneg_ctxt->Ciphers[2] =3D SMB2_ENCRYPTION_AES128_CCM; + struct smb2_encryption_neg_context *ecap; + size_t count; + + ecap =3D cifs_begin_neg_context(smb, SMB2_ENCRYPTION_CAPABILITIES); + + if (READ_ONCE(require_gcm_256)) { + ecap->Ciphers[0] =3D SMB2_ENCRYPTION_AES256_GCM; + count =3D 1; + } else if (READ_ONCE(enable_gcm_256)) { + ecap->Ciphers[0] =3D SMB2_ENCRYPTION_AES128_GCM; + ecap->Ciphers[1] =3D SMB2_ENCRYPTION_AES256_GCM; + ecap->Ciphers[2] =3D SMB2_ENCRYPTION_AES128_CCM; + count =3D 3; } else { - pneg_ctxt->DataLength =3D cpu_to_le16(6); /* Cipher Count + 2 ciphers */ - pneg_ctxt->CipherCount =3D cpu_to_le16(2); - pneg_ctxt->Ciphers[0] =3D SMB2_ENCRYPTION_AES128_GCM; - pneg_ctxt->Ciphers[1] =3D SMB2_ENCRYPTION_AES128_CCM; + ecap->Ciphers[0] =3D SMB2_ENCRYPTION_AES128_GCM; + ecap->Ciphers[1] =3D SMB2_ENCRYPTION_AES128_CCM; + count =3D 2; } + ecap->CipherCount =3D cpu_to_le16(count); + cifs_end_neg_context(smb, ecap, struct_size(ecap, Ciphers, count)); } =20 -static unsigned int -build_netname_ctxt(struct smb2_netname_neg_context *pneg_ctxt, char *hostn= ame) +static size_t smb2_size_netname_ctxt(struct TCP_Server_Info *server) { + size_t data_len; + +#if 0 struct nls_table *cp =3D load_nls_default(); + const char *hostname; =20 - pneg_ctxt->ContextType =3D SMB2_NETNAME_NEGOTIATE_CONTEXT_ID; + /* Only include up to first 100 bytes of server name in the NetName + * field. + */ + cifs_server_lock(pserver); + hostname =3D pserver->hostname; + if (hostname && hostname[0]) + data_len =3D 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 =3D 400; +#endif + return ALIGN8(sizeof(struct smb2_neg_context) + data_len); +} + +static void build_netname_ctxt(struct smb_message *smb, const char *hostna= me) +{ + struct smb2_netname_neg_context *name; + struct nls_table *cp =3D load_nls_default(); + size_t count; + + name =3D cifs_begin_neg_context(smb, SMB2_NETNAME_NEGOTIATE_CONTEXT_ID); =20 /* copy up to max of first 100 bytes of server name to NetName field */ - pneg_ctxt->DataLength =3D cpu_to_le16(2 * cifs_strtoUTF16(pneg_ctxt->NetN= ame, 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 =3D cifs_strtoUTF16(name->NetName, hostname, 100, cp); + cifs_end_neg_context(smb, name, struct_size(name, NetName, count)); } =20 -static void -build_posix_ctxt(struct smb2_posix_neg_context *pneg_ctxt) +static void build_posix_ctxt(struct smb_message *smb) { - pneg_ctxt->ContextType =3D SMB2_POSIX_EXTENSIONS_AVAILABLE; - pneg_ctxt->DataLength =3D cpu_to_le16(POSIX_CTXT_DATA_LEN); + struct smb2_posix_neg_context *posix; + + posix =3D cifs_begin_neg_context(smb, SMB2_POSIX_EXTENSIONS_AVAILABLE); /* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */ - pneg_ctxt->Name[0] =3D 0x93; - pneg_ctxt->Name[1] =3D 0xAD; - pneg_ctxt->Name[2] =3D 0x25; - pneg_ctxt->Name[3] =3D 0x50; - pneg_ctxt->Name[4] =3D 0x9C; - pneg_ctxt->Name[5] =3D 0xB4; - pneg_ctxt->Name[6] =3D 0x11; - pneg_ctxt->Name[7] =3D 0xE7; - pneg_ctxt->Name[8] =3D 0xB4; - pneg_ctxt->Name[9] =3D 0x23; - pneg_ctxt->Name[10] =3D 0x83; - pneg_ctxt->Name[11] =3D 0xDE; - pneg_ctxt->Name[12] =3D 0x96; - pneg_ctxt->Name[13] =3D 0x8B; - pneg_ctxt->Name[14] =3D 0xCD; - pneg_ctxt->Name[15] =3D 0x7C; + posix->Name[0] =3D 0x93; + posix->Name[1] =3D 0xAD; + posix->Name[2] =3D 0x25; + posix->Name[3] =3D 0x50; + posix->Name[4] =3D 0x9C; + posix->Name[5] =3D 0xB4; + posix->Name[6] =3D 0x11; + posix->Name[7] =3D 0xE7; + posix->Name[8] =3D 0xB4; + posix->Name[9] =3D 0x23; + posix->Name[10] =3D 0x83; + posix->Name[11] =3D 0xDE; + posix->Name[12] =3D 0x96; + posix->Name[13] =3D 0x8B; + posix->Name[14] =3D 0xCD; + posix->Name[15] =3D 0x7C; + cifs_end_neg_context(smb, posix, sizeof(posix)); } =20 -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; - } =20 /* * round up total_len of fixed part of SMB3 negotiate request to 8 * byte boundary before adding negotiate contexts */ - *total_len =3D ALIGN8(*total_len); + offset =3D ALIGN8(offset); + offset +=3D ALIGN8(sizeof(struct smb2_preauth_neg_context)); + offset +=3D ALIGN8(sizeof(struct smb2_encryption_neg_context)); =20 - pneg_ctxt =3D (*total_len) + (char *)req; - req->NegotiateContextOffset =3D cpu_to_le32(*total_len); + /* + * secondary channels don't have the hostname field populated + * use the hostname field in the primary channel instead + */ + pserver =3D SERVER_IS_CHAN(server) ? server->primary_server : server; + offset +=3D smb2_size_netname_ctxt(pserver); + + offset +=3D ALIGN8(sizeof(struct smb2_posix_neg_context)); + if (server->compression.requested) + offset +=3D ALIGN8(sizeof(struct smb2_compression_capabilities_context)); + if (enable_negotiate_signing) + offset +=3D smb2_size_signing_ctxt(); + return offset; +} =20 - build_preauth_ctxt((struct smb2_preauth_neg_context *)pneg_ctxt); - ctxt_len =3D ALIGN8(sizeof(struct smb2_preauth_neg_context)); - *total_len +=3D ctxt_len; - pneg_ctxt +=3D ctxt_len; +static void +assemble_neg_contexts(struct smb_message *smb, struct TCP_Server_Info *ser= ver) +{ + struct smb2_negotiate_req *req; + struct TCP_Server_Info *pserver; + const char *hostname; + unsigned int neg_context_count =3D 2; =20 - build_encrypt_ctxt((struct smb2_encryption_neg_context *)pneg_ctxt); - ctxt_len =3D ALIGN8(sizeof(struct smb2_encryption_neg_context)); - *total_len +=3D ctxt_len; - pneg_ctxt +=3D ctxt_len; + build_preauth_ctxt(smb); + build_encrypt_ctxt(smb); =20 /* * secondary channels don't have the hostname field populated @@ -934,47 +990,36 @@ assemble_neg_contexts(struct smb2_negotiate_req *req, pserver =3D SERVER_IS_CHAN(server) ? server->primary_server : server; cifs_server_lock(pserver); hostname =3D pserver->hostname; - if (hostname && (hostname[0] !=3D 0)) { - ctxt_len =3D build_netname_ctxt((struct smb2_netname_neg_context *)pneg_= ctxt, - hostname); - *total_len +=3D ctxt_len; - pneg_ctxt +=3D ctxt_len; - neg_context_count =3D 3; - } else - neg_context_count =3D 2; + if (hostname && hostname[0]) { + build_netname_ctxt(smb, hostname); + neg_context_count++; + } cifs_server_unlock(pserver); =20 - build_posix_ctxt((struct smb2_posix_neg_context *)pneg_ctxt); - *total_len +=3D sizeof(struct smb2_posix_neg_context); - pneg_ctxt +=3D sizeof(struct smb2_posix_neg_context); + build_posix_ctxt(smb); neg_context_count++; =20 if (server->compression.requested) { - build_compression_ctxt((struct smb2_compression_capabilities_context *) - pneg_ctxt); - ctxt_len =3D ALIGN8(sizeof(struct smb2_compression_capabilities_context)= ); - *total_len +=3D ctxt_len; - pneg_ctxt +=3D ctxt_len; + build_compression_ctxt(smb); neg_context_count++; } =20 if (enable_negotiate_signing) { - ctxt_len =3D build_signing_ctxt((struct smb2_signing_capabilities *) - pneg_ctxt); - *total_len +=3D ctxt_len; - pneg_ctxt +=3D ctxt_len; + build_signing_ctxt(smb); neg_context_count++; } =20 /* check for and add transport_capabilities and signing capabilities */ + req =3D smb->request; req->NegotiateContextCount =3D cpu_to_le16(neg_context_count); - } =20 /* 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 =3D le16_to_cpu(ctxt->DataLength); + struct smb2_preauth_neg_context *ctxt =3D + container_of(neg, struct smb2_preauth_neg_context, neg); + unsigned int len =3D le16_to_cpu(ctxt->neg.DataLength); =20 /* * Caller checked that DataLength remains within SMB boundary. We still @@ -994,9 +1039,11 @@ static void decode_preauth_context(struct smb2_preaut= h_neg_context *ctxt) } =20 static void decode_compress_ctx(struct TCP_Server_Info *server, - struct smb2_compression_capabilities_context *ctxt) + struct smb2_neg_context *neg) { - unsigned int len =3D le16_to_cpu(ctxt->DataLength); + struct smb2_compression_capabilities_context *ctxt =3D + container_of(neg, struct smb2_compression_capabilities_context, neg); + unsigned int len =3D le16_to_cpu(ctxt->neg.DataLength); __le16 alg; =20 server->compression.enabled =3D false; @@ -1029,9 +1076,11 @@ static void decode_compress_ctx(struct TCP_Server_In= fo *server, } =20 static int decode_encrypt_ctx(struct TCP_Server_Info *server, - struct smb2_encryption_neg_context *ctxt) + struct smb2_neg_context *neg) { - unsigned int len =3D le16_to_cpu(ctxt->DataLength); + struct smb2_encryption_neg_context *ctxt =3D + container_of(neg, struct smb2_encryption_neg_context, neg); + unsigned int len =3D le16_to_cpu(ctxt->neg.DataLength); =20 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, } =20 static void decode_signing_ctx(struct TCP_Server_Info *server, - struct smb2_signing_capabilities *pctxt) + struct smb2_neg_context *neg) { - unsigned int len =3D le16_to_cpu(pctxt->DataLength); + struct smb2_signing_capabilities *pctxt =3D + container_of(neg, struct smb2_signing_capabilities, neg); + unsigned int len =3D le16_to_cpu(pctxt->neg.DataLength); =20 /* * Caller checked that DataLength remains within SMB boundary. We still @@ -1110,11 +1161,12 @@ static void decode_signing_ctx(struct TCP_Server_In= fo *server, } =20 =20 -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 =3D smb->response; struct smb2_neg_context *pctx; + unsigned int len_of_smb =3D smb->response_len; unsigned int offset =3D le32_to_cpu(rsp->NegotiateContextOffset); unsigned int ctxt_cnt =3D le16_to_cpu(rsp->NegotiateContextCount); unsigned int len_of_ctxts, i; @@ -1147,23 +1199,26 @@ static int smb311_decode_neg_context(struct smb2_ne= gotiate_rsp *rsp, if (clen > len_of_ctxts) break; =20 - if (pctx->ContextType =3D=3D SMB2_PREAUTH_INTEGRITY_CAPABILITIES) - decode_preauth_context( - (struct smb2_preauth_neg_context *)pctx); - else if (pctx->ContextType =3D=3D SMB2_ENCRYPTION_CAPABILITIES) - rc =3D decode_encrypt_ctx(server, - (struct smb2_encryption_neg_context *)pctx); - else if (pctx->ContextType =3D=3D SMB2_COMPRESSION_CAPABILITIES) - decode_compress_ctx(server, - (struct smb2_compression_capabilities_context *)pctx); - else if (pctx->ContextType =3D=3D SMB2_POSIX_EXTENSIONS_AVAILABLE) + switch (pctx->ContextType) { + case SMB2_PREAUTH_INTEGRITY_CAPABILITIES: + decode_preauth_context(pctx); + break; + case SMB2_ENCRYPTION_CAPABILITIES: + rc =3D 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 =3D true; - else if (pctx->ContextType =3D=3D 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; =20 @@ -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 =3D CIFS_NEG_OP; - unsigned int total_len; + enum { DEF, ANY3, SPEC } version; =20 cifs_dbg(FYI, "Negotiate protocol\n"); =20 @@ -1267,36 +1321,67 @@ SMB2_negotiate(const unsigned int xid, return -EIO; } =20 - rc =3D smb2_plain_req_init(SMB2_NEGOTIATE, NULL, server, - (void **) &req, &total_len); + rc =3D smb2_reconnect(SMB2_SESSION_SETUP, NULL, server, false); if (rc) return rc; =20 + /* 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 =3D server->vals->version_string; + if (strcmp(vs, SMB3ANY_VERSION_STRING) =3D=3D 0) { + version =3D ANY3; + num_dialects =3D 3; + } else if (strcmp(vs, SMBDEFAULT_VERSION_STRING) =3D=3D 0) { + version =3D DEF; + num_dialects =3D 4; + } else { + version =3D SPEC; + num_dialects =3D 1; + } + + offset =3D sizeof(struct smb2_negotiate_req); + offset +=3D sizeof(req->Dialects[0]) * num_dialects; + + if ((server->vals->protocol_id =3D=3D SMB311_PROT_ID) || + (version =3D=3D ANY3) || + (version =3D=3D DEF)) + offset =3D smb2_size_neg_contexts(server, offset); + + smb =3D smb2_create_request(SMB2_NEGOTIATE, server, NULL, + sizeof(*req), offset, 0, + SMB2_REQ_HEAD); + if (!smb) + return -ENOMEM; + + /* Fill in the message. */ + req =3D smb->request; req->hdr.SessionId =3D 0; + req->NegotiateContextOffset =3D cpu_to_be32(smb->ext_offset); =20 memset(server->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE); memset(ses->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE); =20 - if (strcmp(server->vals->version_string, - SMB3ANY_VERSION_STRING) =3D=3D 0) { + switch (version) { + case ANY3: req->Dialects[0] =3D cpu_to_le16(SMB30_PROT_ID); req->Dialects[1] =3D cpu_to_le16(SMB302_PROT_ID); req->Dialects[2] =3D cpu_to_le16(SMB311_PROT_ID); req->DialectCount =3D cpu_to_le16(3); - total_len +=3D 6; - } else if (strcmp(server->vals->version_string, - SMBDEFAULT_VERSION_STRING) =3D=3D 0) { + break; + case DEF: req->Dialects[0] =3D cpu_to_le16(SMB21_PROT_ID); req->Dialects[1] =3D cpu_to_le16(SMB30_PROT_ID); req->Dialects[2] =3D cpu_to_le16(SMB302_PROT_ID); req->Dialects[3] =3D cpu_to_le16(SMB311_PROT_ID); req->DialectCount =3D cpu_to_le16(4); - total_len +=3D 8; - } else { + break; + case SPEC: /* otherwise send specific dialect */ req->Dialects[0] =3D cpu_to_le16(server->vals->protocol_id); req->DialectCount =3D cpu_to_le16(1); - total_len +=3D 2; + break; } =20 /* only one of SMB2 signing flags may be set in SMB2 request */ @@ -1312,97 +1397,108 @@ SMB2_negotiate(const unsigned int xid, req->Capabilities |=3D cpu_to_le32(SMB2_GLOBAL_CAP_MULTI_CHANNEL); =20 /* ClientGUID must be zero for SMB2.02 dialect */ - if (server->vals->protocol_id =3D=3D SMB20_PROT_ID) + if (server->vals->protocol_id =3D=3D 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 =3D=3D SMB311_PROT_ID) || - (strcmp(server->vals->version_string, - SMB3ANY_VERSION_STRING) =3D=3D 0) || - (strcmp(server->vals->version_string, - SMBDEFAULT_VERSION_STRING) =3D=3D 0)) - assemble_neg_contexts(req, server, &total_len); + (version =3D=3D ANY3) || + (version =3D=3D DEF)) + assemble_neg_contexts(smb, server); } - iov[0].iov_base =3D (char *)req; - iov[0].iov_len =3D total_len; =20 - memset(&rqst, 0, sizeof(struct smb_rqst)); - rqst.rq_iov =3D iov; - rqst.rq_nvec =3D 1; + rc =3D smb_send_recv_messages(xid, ses, server, smb, flags); + smb_clear_request(smb); =20 - rc =3D cifs_send_recv(xid, ses, server, - &rqst, &resp_buftype, flags, &rsp_iov); - cifs_small_buf_release(req); - rsp =3D (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 =3D=3D -EOPNOTSUPP) { - cifs_server_dbg(VFS, "Dialect not supported by server. Consider specify= ing vers=3D1.0 or vers=3D2.0 on mount for accessing older servers\n"); - goto neg_exit; - } else if (rc !=3D 0) + if (rc !=3D 0) { + if (rc =3D=3D -EOPNOTSUPP) + cifs_server_dbg(VFS, "Dialect not supported by server. Consider specif= ying vers=3D1.0 or vers=3D2.0 on mount for accessing older servers\n"); goto neg_exit; + } =20 + /* ________________________________________ + * Decode the response. + */ + rsp =3D (struct smb2_negotiate_rsp *)smb->response; + + int dialect_revision =3D le16_to_cpu(rsp->DialectRevision); rc =3D -EIO; - if (strcmp(server->vals->version_string, - SMB3ANY_VERSION_STRING) =3D=3D 0) { - if (rsp->DialectRevision =3D=3D 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 =3D=3D 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 =3D=3D cpu_to_le16(SMB311_PROT_ID)) { + case SMB311_PROT_ID: /* ops set to 3.0 by default for default so update */ server->ops =3D &smb311_operations; server->vals =3D &smb311_values; + break; } - } else if (strcmp(server->vals->version_string, - SMBDEFAULT_VERSION_STRING) =3D=3D 0) { - if (rsp->DialectRevision =3D=3D 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 =3D=3D cpu_to_le16(SMB21_PROT_ID)) { + case SMB21_PROT_ID: /* ops set to 3.0 by default for default so update */ server->ops =3D &smb21_operations; server->vals =3D &smb21_values; - } else if (rsp->DialectRevision =3D=3D cpu_to_le16(SMB311_PROT_ID)) { + break; + case SMB311_PROT_ID: server->ops =3D &smb311_operations; server->vals =3D &smb311_values; + break; } - } else if (le16_to_cpu(rsp->DialectRevision) !=3D - 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 !=3D 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; } =20 cifs_dbg(FYI, "mode 0x%x\n", rsp->SecurityMode); =20 - if (rsp->DialectRevision =3D=3D 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 =3D=3D cpu_to_le16(SMB21_PROT_ID)) + break; + case SMB21_PROT_ID: cifs_dbg(FYI, "negotiated smb2.1 dialect\n"); - else if (rsp->DialectRevision =3D=3D cpu_to_le16(SMB30_PROT_ID)) + break; + case SMB30_PROT_ID: cifs_dbg(FYI, "negotiated smb3.0 dialect\n"); - else if (rsp->DialectRevision =3D=3D cpu_to_le16(SMB302_PROT_ID)) + break; + case SMB302_PROT_ID: cifs_dbg(FYI, "negotiated smb3.02 dialect\n"); - else if (rsp->DialectRevision =3D=3D 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; } =20 rc =3D 0; - server->dialect =3D le16_to_cpu(rsp->DialectRevision); + server->dialect =3D dialect_revision; =20 /* * Keep a copy of the hash after negprot. This hash will be @@ -1461,10 +1557,9 @@ SMB2_negotiate(const unsigned int xid, rc =3D -EIO; } =20 - if (rsp->DialectRevision =3D=3D cpu_to_le16(SMB311_PROT_ID)) { + if (server->dialect =3D=3D SMB311_PROT_ID) { if (rsp->NegotiateContextCount) - rc =3D smb311_decode_neg_context(rsp, server, - rsp_iov.iov_len); + rc =3D 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 =3D smb3_crypto_aead_allocate(server); neg_exit: - free_rsp_buf(resp_buftype, rsp); + smb_put_messages(smb); return rc; } =20 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 =20 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) =20 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; =20 @@ -567,9 +559,7 @@ struct smb2_rdma_transform_capabilities_context { #define SIGNING_ALG_AES_GMAC_LE cpu_to_le16(2) =20 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 { =20 #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; =20 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 =3D SMB2_PREAUTH_INTEGRITY_CAPABILITIES; - pneg_ctxt->DataLength =3D cpu_to_le16(38); + pneg_ctxt->neg.ContextType =3D SMB2_PREAUTH_INTEGRITY_CAPABILITIES; + pneg_ctxt->neg.DataLength =3D cpu_to_le16(38); + pneg_ctxt->neg.Reserved =3D cpu_to_le32(0); pneg_ctxt->HashAlgorithmCount =3D cpu_to_le16(1); - pneg_ctxt->Reserved =3D cpu_to_le32(0); pneg_ctxt->SaltLength =3D cpu_to_le16(SMB311_SALT_SIZE); get_random_bytes(pneg_ctxt->Salt, SMB311_SALT_SIZE); pneg_ctxt->HashAlgorithms =3D 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_ct= xt, __le16 cipher_type) { - pneg_ctxt->ContextType =3D SMB2_ENCRYPTION_CAPABILITIES; - pneg_ctxt->DataLength =3D cpu_to_le16(4); - pneg_ctxt->Reserved =3D cpu_to_le32(0); + pneg_ctxt->neg.ContextType =3D SMB2_ENCRYPTION_CAPABILITIES; + pneg_ctxt->neg.DataLength =3D cpu_to_le16(4); + pneg_ctxt->neg.Reserved =3D cpu_to_le32(0); pneg_ctxt->CipherCount =3D cpu_to_le16(1); pneg_ctxt->Ciphers[0] =3D 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_ctx= t, __le16 sign_algo) { - pneg_ctxt->ContextType =3D SMB2_SIGNING_CAPABILITIES; - pneg_ctxt->DataLength =3D + pneg_ctxt->neg.ContextType =3D SMB2_SIGNING_CAPABILITIES; + pneg_ctxt->neg.DataLength =3D cpu_to_le16((sizeof(struct smb2_signing_capabilities) + 2) - sizeof(struct smb2_neg_context)); - pneg_ctxt->Reserved =3D cpu_to_le32(0); + pneg_ctxt->neg.Reserved =3D cpu_to_le32(0); pneg_ctxt->SigningAlgorithmCount =3D cpu_to_le16(1); pneg_ctxt->SigningAlgorithms[0] =3D sign_algo; } =20 static void build_posix_ctxt(struct smb2_posix_neg_context *pneg_ctxt) { - pneg_ctxt->ContextType =3D SMB2_POSIX_EXTENSIONS_AVAILABLE; - pneg_ctxt->DataLength =3D cpu_to_le16(POSIX_CTXT_DATA_LEN); + pneg_ctxt->neg.ContextType =3D SMB2_POSIX_EXTENSIONS_AVAILABLE; + pneg_ctxt->neg.DataLength =3D cpu_to_le16(POSIX_CTXT_DATA_LEN); /* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */ pneg_ctxt->Name[0] =3D 0x93; pneg_ctxt->Name[1] =3D 0xAD;