Commit 33cfdd72 authored by Paulo Alcantara's avatar Paulo Alcantara Committed by Steve French
Browse files

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: default avatarPierguido Lambri <plambri@redhat.com>
Signed-off-by: default avatarPaulo Alcantara (Red Hat) <pc@manguebit.org>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent be77ab6b
Loading
Loading
Loading
Loading
+61 −18
Original line number Diff line number Diff line
@@ -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);