* [PATCH 2/2] smb: client: fix session setup against servers that require SPN
2025-07-25 3:04 [PATCH 1/2] smb: client: allow parsing zero-length AV pairs Paulo Alcantara
@ 2025-07-25 3:04 ` Paulo Alcantara
2025-07-25 3:52 ` [PATCH 1/2] smb: client: allow parsing zero-length AV pairs Steve French
1 sibling, 0 replies; 3+ messages in thread
From: Paulo Alcantara @ 2025-07-25 3:04 UTC (permalink / raw)
To: smfrench
Cc: linux-cifs, David Howells, Pierguido Lambri,
Paulo Alcantara (Red Hat)
Some servers might enforce the SPN to be set in the target info
blob (AV pairs) when sending NTLMSSP_AUTH message. In Windows Server,
this could be enforced with SmbServerNameHardeningLevel set to 2.
Fix this by always appending SPN (cifs/<hostname>) to the existing
list of target infos when setting up NTLMv2 response blob.
Cc: linux-cifs@vger.kernel.org
Cc: David Howells <dhowells@redhat.com>
Reported-by: Pierguido Lambri <plambri@redhat.com>
Signed-off-by: Paulo Alcantara (Red Hat) <pc@manguebit.org>
---
fs/smb/client/cifsencrypt.c | 79 ++++++++++++++++++++++++++++---------
1 file changed, 61 insertions(+), 18 deletions(-)
diff --git a/fs/smb/client/cifsencrypt.c b/fs/smb/client/cifsencrypt.c
index 6be850d2a346..3cc686246908 100644
--- a/fs/smb/client/cifsencrypt.c
+++ b/fs/smb/client/cifsencrypt.c
@@ -532,17 +532,67 @@ CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash, struct shash_
return rc;
}
+/*
+ * Set up NTLMv2 response blob with SPN (cifs/<hostname>) appended to the
+ * existing list of AV pairs.
+ */
+static int set_auth_key_response(struct cifs_ses *ses)
+{
+ size_t baselen = CIFS_SESS_KEY_SIZE + sizeof(struct ntlmv2_resp);
+ size_t len, spnlen, tilen = 0, num_avs = 2 /* SPN + EOL */;
+ struct TCP_Server_Info *server = ses->server;
+ char *spn __free(kfree) = NULL;
+ struct ntlmssp2_name *av;
+ char *rsp = NULL;
+ int rc;
+
+ spnlen = strlen(server->hostname);
+ len = sizeof("cifs/") + spnlen;
+ spn = kmalloc(len, GFP_KERNEL);
+ if (!spn) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ spnlen = scnprintf(spn, len, "cifs/%.*s",
+ (int)spnlen, server->hostname);
+
+ av_for_each_entry(ses, av)
+ tilen += sizeof(*av) + AV_LEN(av);
+
+ len = baselen + tilen + spnlen * sizeof(__le16) + num_avs * sizeof(*av);
+ rsp = kmalloc(len, GFP_KERNEL);
+ if (!rsp) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ memcpy(rsp + baselen, ses->auth_key.response, tilen);
+ av = (void *)(rsp + baselen + tilen);
+ av->type = cpu_to_le16(NTLMSSP_AV_TARGET_NAME);
+ av->length = cpu_to_le16(spnlen * sizeof(__le16));
+ cifs_strtoUTF16((__le16 *)av->data, spn, spnlen, ses->local_nls);
+ av = (void *)((__u8 *)av + sizeof(*av) + AV_LEN(av));
+ av->type = cpu_to_le16(NTLMSSP_AV_EOL);
+ av->length = 0;
+
+ rc = 0;
+ ses->auth_key.len = len;
+out:
+ ses->auth_key.response = rsp;
+ return rc;
+}
+
int
setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
{
struct shash_desc *hmacmd5 = NULL;
- int rc;
- int baselen;
- unsigned int tilen;
+ unsigned char *tiblob = NULL; /* target info blob */
struct ntlmv2_resp *ntlmv2;
char ntlmv2_hash[16];
- unsigned char *tiblob = NULL; /* target info blob */
__le64 rsp_timestamp;
+ __u64 cc;
+ int rc;
if (nls_cp == NULL) {
cifs_dbg(VFS, "%s called with nls_cp==NULL\n", __func__);
@@ -588,32 +638,25 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
* (as Windows 7 does)
*/
rsp_timestamp = find_timestamp(ses);
+ get_random_bytes(&cc, sizeof(cc));
+
+ cifs_server_lock(ses->server);
- baselen = CIFS_SESS_KEY_SIZE + sizeof(struct ntlmv2_resp);
- tilen = ses->auth_key.len;
tiblob = ses->auth_key.response;
-
- ses->auth_key.response = kmalloc(baselen + tilen, GFP_KERNEL);
- if (!ses->auth_key.response) {
- rc = -ENOMEM;
+ rc = set_auth_key_response(ses);
+ if (rc) {
ses->auth_key.len = 0;
- goto setup_ntlmv2_rsp_ret;
+ goto unlock;
}
- ses->auth_key.len += baselen;
ntlmv2 = (struct ntlmv2_resp *)
(ses->auth_key.response + CIFS_SESS_KEY_SIZE);
ntlmv2->blob_signature = cpu_to_le32(0x00000101);
ntlmv2->reserved = 0;
ntlmv2->time = rsp_timestamp;
-
- get_random_bytes(&ntlmv2->client_chal, sizeof(ntlmv2->client_chal));
+ ntlmv2->client_chal = cc;
ntlmv2->reserved2 = 0;
- memcpy(ses->auth_key.response + baselen, tiblob, tilen);
-
- cifs_server_lock(ses->server);
-
rc = cifs_alloc_hash("hmac(md5)", &hmacmd5);
if (rc) {
cifs_dbg(VFS, "Could not allocate HMAC-MD5, rc=%d\n", rc);
--
2.50.1
^ permalink raw reply related [flat|nested] 3+ messages in thread* Re: [PATCH 1/2] smb: client: allow parsing zero-length AV pairs
2025-07-25 3:04 [PATCH 1/2] smb: client: allow parsing zero-length AV pairs Paulo Alcantara
2025-07-25 3:04 ` [PATCH 2/2] smb: client: fix session setup against servers that require SPN Paulo Alcantara
@ 2025-07-25 3:52 ` Steve French
1 sibling, 0 replies; 3+ messages in thread
From: Steve French @ 2025-07-25 3:52 UTC (permalink / raw)
To: Paulo Alcantara; +Cc: linux-cifs, David Howells
merged both of these into cifs-2.6.git for-next pending more review and testing
On Thu, Jul 24, 2025 at 10:04 PM Paulo Alcantara <pc@manguebit.org> wrote:
>
> Zero-length AV pairs should be considered as valid target infos.
> Don't skip the next AV pairs that follow them.
>
> Cc: linux-cifs@vger.kernel.org
> Cc: David Howells <dhowells@redhat.com>
> Fixes: 0e8ae9b953bc ("smb: client: parse av pair type 4 in CHALLENGE_MESSAGE")
> Signed-off-by: Paulo Alcantara (Red Hat) <pc@manguebit.org>
> ---
> fs/smb/client/cifsencrypt.c | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/fs/smb/client/cifsencrypt.c b/fs/smb/client/cifsencrypt.c
> index 35892df7335c..6be850d2a346 100644
> --- a/fs/smb/client/cifsencrypt.c
> +++ b/fs/smb/client/cifsencrypt.c
> @@ -343,7 +343,7 @@ static struct ntlmssp2_name *find_next_av(struct cifs_ses *ses,
> len = AV_LEN(av);
> if (AV_TYPE(av) == NTLMSSP_AV_EOL)
> return NULL;
> - if (!len || (u8 *)av + sizeof(*av) + len > end)
> + if ((u8 *)av + sizeof(*av) + len > end)
> return NULL;
> return av;
> }
> @@ -363,7 +363,7 @@ static int find_av_name(struct cifs_ses *ses, u16 type, char **name, u16 maxlen)
>
> av_for_each_entry(ses, av) {
> len = AV_LEN(av);
> - if (AV_TYPE(av) != type)
> + if (AV_TYPE(av) != type || !len)
> continue;
> if (!IS_ALIGNED(len, sizeof(__le16))) {
> cifs_dbg(VFS | ONCE, "%s: bad length(%u) for type %u\n",
> --
> 2.50.1
>
--
Thanks,
Steve
^ permalink raw reply [flat|nested] 3+ messages in thread