Commit 4ccb3a80 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag '6.18-rc1-smb-client-fixes' of git://git.samba.org/sfrench/cifs-2.6

Pull smb client fixes from Steve French:
 "smb client fixes, security and smbdirect improvements, and some minor cleanup:

   - Important OOB DFS fix

   - Fix various potential tcon refcount leaks

   - smbdirect (RDMA) fixes (following up from test event a few weeks
     ago):

      - Fixes to improve and simplify handling of memory lifetime of
        smbdirect_mr_io structures, when a connection gets disconnected

      - Make sure we really wait to reach SMBDIRECT_SOCKET_DISCONNECTED
        before destroying resources

      - Make sure the send/recv submission/completion queues are large
        enough to avoid ib_post_send() from failing under pressure

   - convert cifs.ko to use the recommended crypto libraries (instead of
     crypto_shash), this also can improve performance

   - Three small cleanup patches"

* tag '6.18-rc1-smb-client-fixes' of git://git.samba.org/sfrench/cifs-2.6: (24 commits)
  smb: client: Consolidate cmac(aes) shash allocation
  smb: client: Remove obsolete crypto_shash allocations
  smb: client: Use HMAC-MD5 library for NTLMv2
  smb: client: Use MD5 library for SMB1 signature calculation
  smb: client: Use MD5 library for M-F symlink hashing
  smb: client: Use HMAC-SHA256 library for SMB2 signature calculation
  smb: client: Use HMAC-SHA256 library for key generation
  smb: client: Use SHA-512 library for SMB3.1.1 preauth hash
  cifs: parse_dfs_referrals: prevent oob on malformed input
  smb: client: Fix refcount leak for cifs_sb_tlink
  smb: client: let smbd_destroy() wait for SMBDIRECT_SOCKET_DISCONNECTED
  smb: move some duplicate definitions to common/cifsglob.h
  smb: client: let destroy_mr_list() keep smbdirect_mr_io memory if registered
  smb: client: let destroy_mr_list() call ib_dereg_mr() before ib_dma_unmap_sg()
  smb: client: call ib_dma_unmap_sg if mr->sgt.nents is not 0
  smb: client: improve logic in smbd_deregister_mr()
  smb: client: improve logic in smbd_register_mr()
  smb: client: improve logic in allocate_mr_list()
  smb: client: let destroy_mr_list() remove locked from the list
  smb: client: let destroy_mr_list() call list_del(&mr->list)
  ...
parents 02e5f74e 3c15a6df
Loading
Loading
Loading
Loading
+3 −4
Original line number Diff line number Diff line
@@ -5,17 +5,16 @@ config CIFS
	select NLS
	select NLS_UCS2_UTILS
	select CRYPTO
	select CRYPTO_MD5
	select CRYPTO_SHA256
	select CRYPTO_SHA512
	select CRYPTO_CMAC
	select CRYPTO_HMAC
	select CRYPTO_AEAD2
	select CRYPTO_CCM
	select CRYPTO_GCM
	select CRYPTO_ECB
	select CRYPTO_AES
	select CRYPTO_LIB_ARC4
	select CRYPTO_LIB_MD5
	select CRYPTO_LIB_SHA256
	select CRYPTO_LIB_SHA512
	select KEYS
	select DNS_RESOLVER
	select ASN1
+2 −3
Original line number Diff line number Diff line
@@ -339,7 +339,6 @@ int
sid_to_id(struct cifs_sb_info *cifs_sb, struct smb_sid *psid,
		struct cifs_fattr *fattr, uint sidtype)
{
	int rc = 0;
	struct key *sidkey;
	char *sidstr;
	const struct cred *saved_cred;
@@ -446,12 +445,12 @@ sid_to_id(struct cifs_sb_info *cifs_sb, struct smb_sid *psid,
	 * fails then we just fall back to using the ctx->linux_uid/linux_gid.
	 */
got_valid_id:
	rc = 0;
	if (sidtype == SIDOWNER)
		fattr->cf_uid = fuid;
	else
		fattr->cf_gid = fgid;
	return rc;

	return 0;
}

int
+74 −127
Original line number Diff line number Diff line
@@ -24,14 +24,43 @@
#include <linux/iov_iter.h>
#include <crypto/aead.h>
#include <crypto/arc4.h>
#include <crypto/md5.h>
#include <crypto/sha2.h>

static size_t cifs_shash_step(void *iter_base, size_t progress, size_t len,
static int cifs_sig_update(struct cifs_calc_sig_ctx *ctx,
			   const u8 *data, size_t len)
{
	if (ctx->md5) {
		md5_update(ctx->md5, data, len);
		return 0;
	}
	if (ctx->hmac) {
		hmac_sha256_update(ctx->hmac, data, len);
		return 0;
	}
	return crypto_shash_update(ctx->shash, data, len);
}

static int cifs_sig_final(struct cifs_calc_sig_ctx *ctx, u8 *out)
{
	if (ctx->md5) {
		md5_final(ctx->md5, out);
		return 0;
	}
	if (ctx->hmac) {
		hmac_sha256_final(ctx->hmac, out);
		return 0;
	}
	return crypto_shash_final(ctx->shash, out);
}

static size_t cifs_sig_step(void *iter_base, size_t progress, size_t len,
			    void *priv, void *priv2)
{
	struct shash_desc *shash = priv;
	struct cifs_calc_sig_ctx *ctx = priv;
	int ret, *pret = priv2;

	ret = crypto_shash_update(shash, iter_base, len);
	ret = cifs_sig_update(ctx, iter_base, len);
	if (ret < 0) {
		*pret = ret;
		return len;
@@ -42,21 +71,20 @@ static size_t cifs_shash_step(void *iter_base, size_t progress, size_t len,
/*
 * Pass the data from an iterator into a hash.
 */
static int cifs_shash_iter(const struct iov_iter *iter, size_t maxsize,
			   struct shash_desc *shash)
static int cifs_sig_iter(const struct iov_iter *iter, size_t maxsize,
			 struct cifs_calc_sig_ctx *ctx)
{
	struct iov_iter tmp_iter = *iter;
	int err = -EIO;

	if (iterate_and_advance_kernel(&tmp_iter, maxsize, shash, &err,
				       cifs_shash_step) != maxsize)
	if (iterate_and_advance_kernel(&tmp_iter, maxsize, ctx, &err,
				       cifs_sig_step) != maxsize)
		return err;
	return 0;
}

int __cifs_calc_signature(struct smb_rqst *rqst,
			  struct TCP_Server_Info *server, char *signature,
			  struct shash_desc *shash)
int __cifs_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,
			  char *signature, struct cifs_calc_sig_ctx *ctx)
{
	int i;
	ssize_t rc;
@@ -82,8 +110,7 @@ int __cifs_calc_signature(struct smb_rqst *rqst,
			return -EIO;
		}

		rc = crypto_shash_update(shash,
					 iov[i].iov_base, iov[i].iov_len);
		rc = cifs_sig_update(ctx, iov[i].iov_base, iov[i].iov_len);
		if (rc) {
			cifs_dbg(VFS, "%s: Could not update with payload\n",
				 __func__);
@@ -91,11 +118,11 @@ int __cifs_calc_signature(struct smb_rqst *rqst,
		}
	}

	rc = cifs_shash_iter(&rqst->rq_iter, iov_iter_count(&rqst->rq_iter), shash);
	rc = cifs_sig_iter(&rqst->rq_iter, iov_iter_count(&rqst->rq_iter), ctx);
	if (rc < 0)
		return rc;

	rc = crypto_shash_final(shash, signature);
	rc = cifs_sig_final(ctx, signature);
	if (rc)
		cifs_dbg(VFS, "%s: Could not generate hash\n", __func__);

@@ -112,29 +139,22 @@ int __cifs_calc_signature(struct smb_rqst *rqst,
static int cifs_calc_signature(struct smb_rqst *rqst,
			struct TCP_Server_Info *server, char *signature)
{
	int rc;
	struct md5_ctx ctx;

	if (!rqst->rq_iov || !signature || !server)
		return -EINVAL;

	rc = cifs_alloc_hash("md5", &server->secmech.md5);
	if (rc)
		return -1;

	rc = crypto_shash_init(server->secmech.md5);
	if (rc) {
		cifs_dbg(VFS, "%s: Could not init md5\n", __func__);
		return rc;
	if (fips_enabled) {
		cifs_dbg(VFS,
			 "MD5 signature support is disabled due to FIPS\n");
		return -EOPNOTSUPP;
	}

	rc = crypto_shash_update(server->secmech.md5,
		server->session_key.response, server->session_key.len);
	if (rc) {
		cifs_dbg(VFS, "%s: Could not update with response\n", __func__);
		return rc;
	}
	md5_init(&ctx);
	md5_update(&ctx, server->session_key.response, server->session_key.len);

	return __cifs_calc_signature(rqst, server, signature, server->secmech.md5);
	return __cifs_calc_signature(
		rqst, server, signature,
		&(struct cifs_calc_sig_ctx){ .md5 = &ctx });
}

/* must be called with server->srv_mutex held */
@@ -405,11 +425,11 @@ static __le64 find_timestamp(struct cifs_ses *ses)
}

static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash,
			    const struct nls_table *nls_cp, struct shash_desc *hmacmd5)
			    const struct nls_table *nls_cp)
{
	int rc = 0;
	int len;
	char nt_hash[CIFS_NTHASH_SIZE];
	struct hmac_md5_ctx hmac_ctx;
	__le16 *user;
	wchar_t *domain;
	wchar_t *server;
@@ -417,17 +437,7 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash,
	/* calculate md4 hash of password */
	E_md4hash(ses->password, nt_hash, nls_cp);

	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, rc=%d\n", __func__, rc);
		return rc;
	}

	rc = crypto_shash_init(hmacmd5);
	if (rc) {
		cifs_dbg(VFS, "%s: Could not init HMAC-MD5, rc=%d\n", __func__, rc);
		return rc;
	}
	hmac_md5_init_usingrawkey(&hmac_ctx, nt_hash, CIFS_NTHASH_SIZE);

	/* convert ses->user_name to unicode */
	len = ses->user_name ? strlen(ses->user_name) : 0;
@@ -442,12 +452,8 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash,
		*(u16 *)user = 0;
	}

	rc = crypto_shash_update(hmacmd5, (char *)user, 2 * len);
	hmac_md5_update(&hmac_ctx, (const u8 *)user, 2 * len);
	kfree(user);
	if (rc) {
		cifs_dbg(VFS, "%s: Could not update with user, rc=%d\n", __func__, rc);
		return rc;
	}

	/* convert ses->domainName to unicode and uppercase */
	if (ses->domainName) {
@@ -459,12 +465,8 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash,

		len = cifs_strtoUTF16((__le16 *)domain, ses->domainName, len,
				      nls_cp);
		rc = crypto_shash_update(hmacmd5, (char *)domain, 2 * len);
		hmac_md5_update(&hmac_ctx, (const u8 *)domain, 2 * len);
		kfree(domain);
		if (rc) {
			cifs_dbg(VFS, "%s: Could not update with domain, rc=%d\n", __func__, rc);
			return rc;
		}
	} else {
		/* We use ses->ip_addr if no domain name available */
		len = strlen(ses->ip_addr);
@@ -474,25 +476,16 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash,
			return -ENOMEM;

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

	rc = crypto_shash_final(hmacmd5, ntlmv2_hash);
	if (rc)
		cifs_dbg(VFS, "%s: Could not generate MD5 hash, rc=%d\n", __func__, rc);

	return rc;
	hmac_md5_final(&hmac_ctx, ntlmv2_hash);
	return 0;
}

static int
CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash, struct shash_desc *hmacmd5)
static void CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash)
{
	int rc;
	struct ntlmv2_resp *ntlmv2 = (struct ntlmv2_resp *)
	    (ses->auth_key.response + CIFS_SESS_KEY_SIZE);
	unsigned int hash_len;
@@ -501,35 +494,15 @@ CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash, struct shash_
	hash_len = ses->auth_key.len - (CIFS_SESS_KEY_SIZE +
		offsetof(struct ntlmv2_resp, challenge.key[0]));

	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, rc=%d\n", __func__, rc);
		return rc;
	}

	rc = crypto_shash_init(hmacmd5);
	if (rc) {
		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);
	else
		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, rc=%d\n", __func__, rc);
		return rc;
	}

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

	return rc;
	/* Note that the HMAC-MD5 value overwrites ntlmv2->challenge.key */
	hmac_md5_usingrawkey(ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE,
			     ntlmv2->challenge.key, hash_len,
			     ntlmv2->ntlmv2_hash);
}

/*
@@ -586,7 +559,6 @@ static int set_auth_key_response(struct cifs_ses *ses)
int
setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
{
	struct shash_desc *hmacmd5 = NULL;
	unsigned char *tiblob = NULL; /* target info blob */
	struct ntlmv2_resp *ntlmv2;
	char ntlmv2_hash[16];
@@ -657,51 +629,29 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
	ntlmv2->client_chal = cc;
	ntlmv2->reserved2 = 0;

	rc = cifs_alloc_hash("hmac(md5)", &hmacmd5);
	if (rc) {
		cifs_dbg(VFS, "Could not allocate HMAC-MD5, rc=%d\n", rc);
	if (fips_enabled) {
		cifs_dbg(VFS, "NTLMv2 support is disabled due to FIPS\n");
		rc = -EOPNOTSUPP;
		goto unlock;
	}

	/* calculate ntlmv2_hash */
	rc = calc_ntlmv2_hash(ses, ntlmv2_hash, nls_cp, hmacmd5);
	rc = calc_ntlmv2_hash(ses, ntlmv2_hash, nls_cp);
	if (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, hmacmd5);
	if (rc) {
		cifs_dbg(VFS, "Could not calculate CR1, rc=%d\n", rc);
		goto unlock;
	}
	CalcNTLMv2_response(ses, ntlmv2_hash);

	/* now calculate the session key for NTLMv2 */
	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, rc=%d\n", __func__, rc);
		goto unlock;
	}

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

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

	rc = crypto_shash_final(hmacmd5, ses->auth_key.response);
	if (rc)
		cifs_dbg(VFS, "%s: Could not generate MD5 hash, rc=%d\n", __func__, rc);
	hmac_md5_usingrawkey(ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE,
			     ntlmv2->ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE,
			     ses->auth_key.response);
	rc = 0;
unlock:
	cifs_server_unlock(ses->server);
	cifs_free_hash(&hmacmd5);
setup_ntlmv2_rsp_ret:
	kfree_sensitive(tiblob);

@@ -743,9 +693,6 @@ void
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);

	if (server->secmech.enc) {
		crypto_free_aead(server->secmech.enc);
+0 −4
Original line number Diff line number Diff line
@@ -2139,13 +2139,9 @@ MODULE_DESCRIPTION
	"also older servers complying with the SNIA CIFS Specification)");
MODULE_VERSION(CIFS_VERSION);
MODULE_SOFTDEP("ecb");
MODULE_SOFTDEP("hmac");
MODULE_SOFTDEP("md5");
MODULE_SOFTDEP("nls");
MODULE_SOFTDEP("aes");
MODULE_SOFTDEP("cmac");
MODULE_SOFTDEP("sha256");
MODULE_SOFTDEP("sha512");
MODULE_SOFTDEP("aead2");
MODULE_SOFTDEP("ccm");
MODULE_SOFTDEP("gcm");
+1 −21
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@
#include "cifsacl.h"
#include <crypto/internal/hash.h>
#include <uapi/linux/cifs/cifs_mount.h>
#include "../common/cifsglob.h"
#include "../common/smb2pdu.h"
#include "smb2pdu.h"
#include <linux/filelock.h>
@@ -221,9 +222,6 @@ struct session_key {

/* crypto hashing related structure/fields, not specific to a sec mech */
struct cifs_secmech {
	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) */
@@ -702,12 +700,6 @@ get_rfc1002_length(void *buf)
	return be32_to_cpu(*((__be32 *)buf)) & 0xffffff;
}

static inline void
inc_rfc1001_len(void *buf, int count)
{
	be32_add_cpu((__be32 *)buf, count);
}

struct TCP_Server_Info {
	struct list_head tcp_ses_list;
	struct list_head smb_ses_list;
@@ -1021,8 +1013,6 @@ compare_mid(__u16 mid, const struct smb_hdr *smb)
#define CIFS_MAX_RFC1002_WSIZE ((1<<17) - 1 - sizeof(WRITE_REQ) + 4)
#define CIFS_MAX_RFC1002_RSIZE ((1<<17) - 1 - sizeof(READ_RSP) + 4)

#define CIFS_DEFAULT_IOSIZE (1024 * 1024)

/*
 * Windows only supports a max of 60kb reads and 65535 byte writes. Default to
 * those values when posix extensions aren't in force. In actuality here, we
@@ -2148,30 +2138,20 @@ extern mempool_t cifs_io_request_pool;
extern mempool_t cifs_io_subrequest_pool;

/* Operations for different SMB versions */
#define SMB1_VERSION_STRING	"1.0"
#define SMB20_VERSION_STRING    "2.0"
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
extern struct smb_version_operations smb1_operations;
extern struct smb_version_values smb1_values;
extern struct smb_version_operations smb20_operations;
extern struct smb_version_values smb20_values;
#endif /* CIFS_ALLOW_INSECURE_LEGACY */
#define SMB21_VERSION_STRING	"2.1"
extern struct smb_version_operations smb21_operations;
extern struct smb_version_values smb21_values;
#define SMBDEFAULT_VERSION_STRING "default"
extern struct smb_version_values smbdefault_values;
#define SMB3ANY_VERSION_STRING "3"
extern struct smb_version_values smb3any_values;
#define SMB30_VERSION_STRING	"3.0"
extern struct smb_version_operations smb30_operations;
extern struct smb_version_values smb30_values;
#define SMB302_VERSION_STRING	"3.02"
#define ALT_SMB302_VERSION_STRING "3.0.2"
/*extern struct smb_version_operations smb302_operations;*/ /* not needed yet */
extern struct smb_version_values smb302_values;
#define SMB311_VERSION_STRING	"3.1.1"
#define ALT_SMB311_VERSION_STRING "3.11"
extern struct smb_version_operations smb311_operations;
extern struct smb_version_values smb311_values;

Loading