]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
smb: client: fix session setup against servers that require SPN
authorPaulo Alcantara <pc@manguebit.org>
Fri, 25 Jul 2025 03:04:44 +0000 (00:04 -0300)
committerSteve French <stfrench@microsoft.com>
Sun, 27 Jul 2025 21:59:59 +0000 (16:59 -0500)
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>
fs/smb/client/cifsencrypt.c

index 6be850d2a346772bb252bbf9e3bdbb37d0486486..3cc68624690876f7f74d2bce2f876e92c86246fd 100644 (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));
 
-       baselen = CIFS_SESS_KEY_SIZE + sizeof(struct ntlmv2_resp);
-       tilen = ses->auth_key.len;
-       tiblob = ses->auth_key.response;
+       cifs_server_lock(ses->server);
 
-       ses->auth_key.response = kmalloc(baselen + tilen, GFP_KERNEL);
-       if (!ses->auth_key.response) {
-               rc = -ENOMEM;
+       tiblob = ses->auth_key.response;
+       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);