Commit f04ff5a0 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag '6.12rc-more-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6

Pull xmb client fixes from Steve French:

 - Noisy log message cleanup

 - Important netfs fix for cifs crash in generic/074

 - Three minor improvements to use of hashing (multichannel and mount
   improvements)

 - Fix decryption crash for large read with small esize

* tag '6.12rc-more-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6:
  smb: client: make SHA-512 TFM ephemeral
  smb: client: make HMAC-MD5 TFM ephemeral
  smb: client: stop flooding dmesg in smb2_calc_signature()
  smb: client: allocate crypto only for primary server
  smb: client: fix UAF in async decryption
  netfs: Fix write oops in generic/346 (9p) and generic/074 (cifs)
parents ad46e8f9 220d83b5
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -58,6 +58,7 @@ static inline void netfs_proc_del_rreq(struct netfs_io_request *rreq) {}
/*
 * misc.c
 */
struct folio_queue *netfs_buffer_make_space(struct netfs_io_request *rreq);
int netfs_buffer_append_folio(struct netfs_io_request *rreq, struct folio *folio,
			      bool needs_put);
struct folio_queue *netfs_delete_buffer_head(struct netfs_io_request *wreq);
+53 −21
Original line number Diff line number Diff line
@@ -9,35 +9,67 @@
#include "internal.h"

/*
 * Append a folio to the rolling queue.
 * Make sure there's space in the rolling queue.
 */
int netfs_buffer_append_folio(struct netfs_io_request *rreq, struct folio *folio,
			      bool needs_put)
struct folio_queue *netfs_buffer_make_space(struct netfs_io_request *rreq)
{
	struct folio_queue *tail = rreq->buffer_tail;
	unsigned int slot, order = folio_order(folio);
	struct folio_queue *tail = rreq->buffer_tail, *prev;
	unsigned int prev_nr_slots = 0;

	if (WARN_ON_ONCE(!rreq->buffer && tail) ||
	    WARN_ON_ONCE(rreq->buffer && !tail))
		return -EIO;
		return ERR_PTR(-EIO);

	prev = tail;
	if (prev) {
		if (!folioq_full(tail))
			return tail;
		prev_nr_slots = folioq_nr_slots(tail);
	}

	if (!tail || folioq_full(tail)) {
	tail = kmalloc(sizeof(*tail), GFP_NOFS);
	if (!tail)
			return -ENOMEM;
		return ERR_PTR(-ENOMEM);
	netfs_stat(&netfs_n_folioq);
	folioq_init(tail);
		tail->prev = rreq->buffer_tail;
		if (tail->prev)
			tail->prev->next = tail;
	tail->prev = prev;
	if (prev)
		/* [!] NOTE: After we set prev->next, the consumer is entirely
		 * at liberty to delete prev.
		 */
		WRITE_ONCE(prev->next, tail);

	rreq->buffer_tail = tail;
	if (!rreq->buffer) {
		rreq->buffer = tail;
		iov_iter_folio_queue(&rreq->io_iter, ITER_SOURCE, tail, 0, 0, 0);
	} else {
		/* Make sure we don't leave the master iterator pointing to a
		 * block that might get immediately consumed.
		 */
		if (rreq->io_iter.folioq == prev &&
		    rreq->io_iter.folioq_slot == prev_nr_slots) {
			rreq->io_iter.folioq = tail;
			rreq->io_iter.folioq_slot = 0;
		}
	}
	rreq->buffer_tail_slot = 0;
	return tail;
}

/*
 * Append a folio to the rolling queue.
 */
int netfs_buffer_append_folio(struct netfs_io_request *rreq, struct folio *folio,
			      bool needs_put)
{
	struct folio_queue *tail;
	unsigned int slot, order = folio_order(folio);

	tail = netfs_buffer_make_space(rreq);
	if (IS_ERR(tail))
		return PTR_ERR(tail);

	rreq->io_iter.count += PAGE_SIZE << order;

	slot = folioq_append(tail, folio);
+11 −1
Original line number Diff line number Diff line
@@ -153,12 +153,22 @@ static void netfs_prepare_write(struct netfs_io_request *wreq,
				loff_t start)
{
	struct netfs_io_subrequest *subreq;
	struct iov_iter *wreq_iter = &wreq->io_iter;

	/* Make sure we don't point the iterator at a used-up folio_queue
	 * struct being used as a placeholder to prevent the queue from
	 * collapsing.  In such a case, extend the queue.
	 */
	if (iov_iter_is_folioq(wreq_iter) &&
	    wreq_iter->folioq_slot >= folioq_nr_slots(wreq_iter->folioq)) {
		netfs_buffer_make_space(wreq);
	}

	subreq = netfs_alloc_subrequest(wreq);
	subreq->source		= stream->source;
	subreq->start		= start;
	subreq->stream_nr	= stream->stream_nr;
	subreq->io_iter		= wreq->io_iter;
	subreq->io_iter		= *wreq_iter;

	_enter("R=%x[%x]", wreq->debug_id, subreq->debug_index);

+61 −90
Original line number Diff line number Diff line
@@ -416,7 +416,7 @@ find_timestamp(struct cifs_ses *ses)
}

static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash,
			    const struct nls_table *nls_cp)
			    const struct nls_table *nls_cp, struct shash_desc *hmacmd5)
{
	int rc = 0;
	int len;
@@ -425,34 +425,26 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash,
	wchar_t *domain;
	wchar_t *server;

	if (!ses->server->secmech.hmacmd5) {
		cifs_dbg(VFS, "%s: can't generate ntlmv2 hash\n", __func__);
		return -1;
	}

	/* calculate md4 hash of password */
	E_md4hash(ses->password, nt_hash, nls_cp);

	rc = crypto_shash_setkey(ses->server->secmech.hmacmd5->tfm, nt_hash,
				CIFS_NTHASH_SIZE);
	rc = crypto_shash_setkey(hmacmd5->tfm, nt_hash, CIFS_NTHASH_SIZE);
	if (rc) {
		cifs_dbg(VFS, "%s: Could not set NT Hash as a key\n", __func__);
		cifs_dbg(VFS, "%s: Could not set NT hash as a key, rc=%d\n", __func__, rc);
		return rc;
	}

	rc = crypto_shash_init(ses->server->secmech.hmacmd5);
	rc = crypto_shash_init(hmacmd5);
	if (rc) {
		cifs_dbg(VFS, "%s: Could not init hmacmd5\n", __func__);
		cifs_dbg(VFS, "%s: Could not init HMAC-MD5, rc=%d\n", __func__, rc);
		return rc;
	}

	/* convert ses->user_name to unicode */
	len = ses->user_name ? strlen(ses->user_name) : 0;
	user = kmalloc(2 + (len * 2), GFP_KERNEL);
	if (user == NULL) {
		rc = -ENOMEM;
		return rc;
	}
	if (user == NULL)
		return -ENOMEM;

	if (len) {
		len = cifs_strtoUTF16(user, ses->user_name, len, nls_cp);
@@ -461,11 +453,10 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash,
		*(u16 *)user = 0;
	}

	rc = crypto_shash_update(ses->server->secmech.hmacmd5,
				(char *)user, 2 * len);
	rc = crypto_shash_update(hmacmd5, (char *)user, 2 * len);
	kfree(user);
	if (rc) {
		cifs_dbg(VFS, "%s: Could not update with user\n", __func__);
		cifs_dbg(VFS, "%s: Could not update with user, rc=%d\n", __func__, rc);
		return rc;
	}

@@ -474,19 +465,15 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash,
		len = strlen(ses->domainName);

		domain = kmalloc(2 + (len * 2), GFP_KERNEL);
		if (domain == NULL) {
			rc = -ENOMEM;
			return rc;
		}
		if (domain == NULL)
			return -ENOMEM;

		len = cifs_strtoUTF16((__le16 *)domain, ses->domainName, len,
				      nls_cp);
		rc =
		crypto_shash_update(ses->server->secmech.hmacmd5,
					(char *)domain, 2 * len);
		rc = crypto_shash_update(hmacmd5, (char *)domain, 2 * len);
		kfree(domain);
		if (rc) {
			cifs_dbg(VFS, "%s: Could not update with domain\n",
				 __func__);
			cifs_dbg(VFS, "%s: Could not update with domain, rc=%d\n", __func__, rc);
			return rc;
		}
	} else {
@@ -494,33 +481,27 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash,
		len = strlen(ses->ip_addr);

		server = kmalloc(2 + (len * 2), GFP_KERNEL);
		if (server == NULL) {
			rc = -ENOMEM;
			return rc;
		}
		len = cifs_strtoUTF16((__le16 *)server, ses->ip_addr, len,
					nls_cp);
		rc =
		crypto_shash_update(ses->server->secmech.hmacmd5,
					(char *)server, 2 * len);
		if (server == NULL)
			return -ENOMEM;

		len = cifs_strtoUTF16((__le16 *)server, ses->ip_addr, len, nls_cp);
		rc = crypto_shash_update(hmacmd5, (char *)server, 2 * len);
		kfree(server);
		if (rc) {
			cifs_dbg(VFS, "%s: Could not update with server\n",
				 __func__);
			cifs_dbg(VFS, "%s: Could not update with server, rc=%d\n", __func__, rc);
			return rc;
		}
	}

	rc = crypto_shash_final(ses->server->secmech.hmacmd5,
					ntlmv2_hash);
	rc = crypto_shash_final(hmacmd5, ntlmv2_hash);
	if (rc)
		cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__);
		cifs_dbg(VFS, "%s: Could not generate MD5 hash, rc=%d\n", __func__, rc);

	return rc;
}

static int
CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash)
CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash, struct shash_desc *hmacmd5)
{
	int rc;
	struct ntlmv2_resp *ntlmv2 = (struct ntlmv2_resp *)
@@ -531,43 +512,33 @@ CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash)
	hash_len = ses->auth_key.len - (CIFS_SESS_KEY_SIZE +
		offsetof(struct ntlmv2_resp, challenge.key[0]));

	if (!ses->server->secmech.hmacmd5) {
		cifs_dbg(VFS, "%s: can't generate ntlmv2 hash\n", __func__);
		return -1;
	}

	rc = crypto_shash_setkey(ses->server->secmech.hmacmd5->tfm,
				 ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE);
	rc = crypto_shash_setkey(hmacmd5->tfm, ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE);
	if (rc) {
		cifs_dbg(VFS, "%s: Could not set NTLMV2 Hash as a key\n",
			 __func__);
		cifs_dbg(VFS, "%s: Could not set NTLMv2 hash as a key, rc=%d\n", __func__, rc);
		return rc;
	}

	rc = crypto_shash_init(ses->server->secmech.hmacmd5);
	rc = crypto_shash_init(hmacmd5);
	if (rc) {
		cifs_dbg(VFS, "%s: Could not init hmacmd5\n", __func__);
		cifs_dbg(VFS, "%s: Could not init HMAC-MD5, rc=%d\n", __func__, rc);
		return rc;
	}

	if (ses->server->negflavor == CIFS_NEGFLAVOR_EXTENDED)
		memcpy(ntlmv2->challenge.key,
		       ses->ntlmssp->cryptkey, CIFS_SERVER_CHALLENGE_SIZE);
		memcpy(ntlmv2->challenge.key, ses->ntlmssp->cryptkey, CIFS_SERVER_CHALLENGE_SIZE);
	else
		memcpy(ntlmv2->challenge.key,
		       ses->server->cryptkey, CIFS_SERVER_CHALLENGE_SIZE);
	rc = crypto_shash_update(ses->server->secmech.hmacmd5,
				 ntlmv2->challenge.key, hash_len);
		memcpy(ntlmv2->challenge.key, ses->server->cryptkey, CIFS_SERVER_CHALLENGE_SIZE);

	rc = crypto_shash_update(hmacmd5, ntlmv2->challenge.key, hash_len);
	if (rc) {
		cifs_dbg(VFS, "%s: Could not update with response\n", __func__);
		cifs_dbg(VFS, "%s: Could not update with response, rc=%d\n", __func__, rc);
		return rc;
	}

	/* Note that the MD5 digest over writes anon.challenge_key.key */
	rc = crypto_shash_final(ses->server->secmech.hmacmd5,
				ntlmv2->ntlmv2_hash);
	rc = crypto_shash_final(hmacmd5, ntlmv2->ntlmv2_hash);
	if (rc)
		cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__);
		cifs_dbg(VFS, "%s: Could not generate MD5 hash, rc=%d\n", __func__, rc);

	return rc;
}
@@ -575,6 +546,7 @@ CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash)
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;
@@ -640,55 +612,51 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)

	cifs_server_lock(ses->server);

	rc = cifs_alloc_hash("hmac(md5)", &ses->server->secmech.hmacmd5);
	rc = cifs_alloc_hash("hmac(md5)", &hmacmd5);
	if (rc) {
		cifs_dbg(VFS, "Could not allocate HMAC-MD5, rc=%d\n", rc);
		goto unlock;
	}

	/* calculate ntlmv2_hash */
	rc = calc_ntlmv2_hash(ses, ntlmv2_hash, nls_cp);
	rc = calc_ntlmv2_hash(ses, ntlmv2_hash, nls_cp, hmacmd5);
	if (rc) {
		cifs_dbg(VFS, "Could not get v2 hash rc %d\n", rc);
		cifs_dbg(VFS, "Could not get NTLMv2 hash, rc=%d\n", rc);
		goto unlock;
	}

	/* calculate first part of the client response (CR1) */
	rc = CalcNTLMv2_response(ses, ntlmv2_hash);
	rc = CalcNTLMv2_response(ses, ntlmv2_hash, hmacmd5);
	if (rc) {
		cifs_dbg(VFS, "Could not calculate CR1 rc: %d\n", rc);
		cifs_dbg(VFS, "Could not calculate CR1, rc=%d\n", rc);
		goto unlock;
	}

	/* now calculate the session key for NTLMv2 */
	rc = crypto_shash_setkey(ses->server->secmech.hmacmd5->tfm,
		ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE);
	rc = crypto_shash_setkey(hmacmd5->tfm, ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE);
	if (rc) {
		cifs_dbg(VFS, "%s: Could not set NTLMV2 Hash as a key\n",
			 __func__);
		cifs_dbg(VFS, "%s: Could not set NTLMv2 hash as a key, rc=%d\n", __func__, rc);
		goto unlock;
	}

	rc = crypto_shash_init(ses->server->secmech.hmacmd5);
	rc = crypto_shash_init(hmacmd5);
	if (rc) {
		cifs_dbg(VFS, "%s: Could not init hmacmd5\n", __func__);
		cifs_dbg(VFS, "%s: Could not init HMAC-MD5, rc=%d\n", __func__, rc);
		goto unlock;
	}

	rc = crypto_shash_update(ses->server->secmech.hmacmd5,
		ntlmv2->ntlmv2_hash,
		CIFS_HMAC_MD5_HASH_SIZE);
	rc = crypto_shash_update(hmacmd5, ntlmv2->ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE);
	if (rc) {
		cifs_dbg(VFS, "%s: Could not update with response\n", __func__);
		cifs_dbg(VFS, "%s: Could not update with response, rc=%d\n", __func__, rc);
		goto unlock;
	}

	rc = crypto_shash_final(ses->server->secmech.hmacmd5,
		ses->auth_key.response);
	rc = crypto_shash_final(hmacmd5, ses->auth_key.response);
	if (rc)
		cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__);

		cifs_dbg(VFS, "%s: Could not generate MD5 hash, rc=%d\n", __func__, rc);
unlock:
	cifs_server_unlock(ses->server);
	cifs_free_hash(&hmacmd5);
setup_ntlmv2_rsp_ret:
	kfree_sensitive(tiblob);

@@ -732,9 +700,8 @@ cifs_crypto_secmech_release(struct TCP_Server_Info *server)
	cifs_free_hash(&server->secmech.aes_cmac);
	cifs_free_hash(&server->secmech.hmacsha256);
	cifs_free_hash(&server->secmech.md5);
	cifs_free_hash(&server->secmech.sha512);
	cifs_free_hash(&server->secmech.hmacmd5);

	if (!SERVER_IS_CHAN(server)) {
		if (server->secmech.enc) {
			crypto_free_aead(server->secmech.enc);
			server->secmech.enc = NULL;
@@ -744,4 +711,8 @@ cifs_crypto_secmech_release(struct TCP_Server_Info *server)
			crypto_free_aead(server->secmech.dec);
			server->secmech.dec = NULL;
		}
	} else {
		server->secmech.enc = NULL;
		server->secmech.dec = NULL;
	}
}
+0 −2
Original line number Diff line number Diff line
@@ -178,10 +178,8 @@ struct session_key {

/* crypto hashing related structure/fields, not specific to a sec mech */
struct cifs_secmech {
	struct shash_desc *hmacmd5; /* hmacmd5 hash function, for NTLMv2/CR1 hashes */
	struct shash_desc *md5; /* md5 hash function, for CIFS/SMB1 signatures */
	struct shash_desc *hmacsha256; /* hmac-sha256 hash function, for SMB2 signatures */
	struct shash_desc *sha512; /* sha512 hash function, for SMB3.1.1 preauth hash */
	struct shash_desc *aes_cmac; /* block-cipher based MAC function, for SMB3 signatures */

	struct crypto_aead *enc; /* smb3 encryption AEAD TFM (AES-CCM and AES-GCM) */
Loading