smb: client: fix session setup against servers that require SPN

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>
Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
Paulo Alcantara 2025-07-25 00:04:44 -03:00 committed by Steve French
parent be77ab6b9f
commit 33cfdd7263

View File

@ -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);