Commit e05b3115 authored by Eric Biggers's avatar Eric Biggers Committed by Steve French
Browse files

smb: client: Use HMAC-SHA256 library for SMB2 signature calculation



Convert smb2_calc_signature() to use the HMAC-SHA256 library instead of
a "hmac(sha256)" crypto_shash.  This is simpler and faster.  With the
library there's no need to allocate memory, no need to handle errors,
and the HMAC-SHA256 code is accessed directly without inefficient
indirect calls and other unnecessary API overhead.

To make this possible, make __cifs_calc_signature() support both the
HMAC-SHA256 library and crypto_shash.  (crypto_shash is still needed for
HMAC-MD5 and AES-CMAC.  A later commit will switch HMAC-MD5 from shash
to the library.  I'd like to eventually do the same for AES-CMAC, but it
doesn't have a library API yet.  So for now, shash is still needed.)

Also remove the unnecessary 'sigptr' variable.

For now smb3_crypto_shash_allocate() still allocates a "hmac(sha256)"
crypto_shash.  It will be removed in a later commit.

Reviewed-by: default avatarStefan Metzmacher <metze@samba.org>
Acked-by: default avatarArd Biesheuvel <ardb@kernel.org>
Signed-off-by: default avatarEric Biggers <ebiggers@kernel.org>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent 4b4c6fdb
Loading
Loading
Loading
Loading
+36 −16
Original line number Diff line number Diff line
@@ -24,14 +24,34 @@
#include <linux/iov_iter.h>
#include <crypto/aead.h>
#include <crypto/arc4.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->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->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 +62,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 +101,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 +109,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__);

@@ -134,7 +152,9 @@ static int cifs_calc_signature(struct smb_rqst *rqst,
		return rc;
	}

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

/* must be called with server->srv_mutex held */
+6 −3
Original line number Diff line number Diff line
@@ -632,9 +632,12 @@ int cifs_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
			   struct cifs_sb_info *cifs_sb,
			   const unsigned char *path, char *pbuf,
			   unsigned int *pbytes_written);
int __cifs_calc_signature(struct smb_rqst *rqst,
			struct TCP_Server_Info *server, char *signature,
			struct shash_desc *shash);
struct cifs_calc_sig_ctx {
	struct hmac_sha256_ctx *hmac;
	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);
enum securityEnum cifs_select_sectype(struct TCP_Server_Info *,
					enum securityEnum);

+11 −42
Original line number Diff line number Diff line
@@ -254,10 +254,9 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,
{
	int rc;
	unsigned char smb2_signature[SMB2_HMACSHA256_SIZE];
	unsigned char *sigptr = smb2_signature;
	struct kvec *iov = rqst->rq_iov;
	struct smb2_hdr *shdr = (struct smb2_hdr *)iov[0].iov_base;
	struct shash_desc *shash = NULL;
	struct hmac_sha256_ctx hmac_ctx;
	struct smb_rqst drqst;
	__u64 sid = le64_to_cpu(shdr->SessionId);
	u8 key[SMB2_NTLMV2_SESSKEY_SIZE];
@@ -272,30 +271,7 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,
	memset(smb2_signature, 0x0, SMB2_HMACSHA256_SIZE);
	memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE);

	if (allocate_crypto) {
		rc = cifs_alloc_hash("hmac(sha256)", &shash);
		if (rc) {
			cifs_server_dbg(VFS,
					"%s: sha256 alloc failed\n", __func__);
			goto out;
		}
	} else {
		shash = server->secmech.hmacsha256;
	}

	rc = crypto_shash_setkey(shash->tfm, key, sizeof(key));
	if (rc) {
		cifs_server_dbg(VFS,
				"%s: Could not update with response\n",
				__func__);
		goto out;
	}

	rc = crypto_shash_init(shash);
	if (rc) {
		cifs_server_dbg(VFS, "%s: Could not init sha256", __func__);
		goto out;
	}
	hmac_sha256_init_usingrawkey(&hmac_ctx, key, sizeof(key));

	/*
	 * For SMB2+, __cifs_calc_signature() expects to sign only the actual
@@ -306,25 +282,17 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,
	 */
	drqst = *rqst;
	if (drqst.rq_nvec >= 2 && iov[0].iov_len == 4) {
		rc = crypto_shash_update(shash, iov[0].iov_base,
					 iov[0].iov_len);
		if (rc) {
			cifs_server_dbg(VFS,
					"%s: Could not update with payload\n",
					__func__);
			goto out;
		}
		hmac_sha256_update(&hmac_ctx, iov[0].iov_base, iov[0].iov_len);
		drqst.rq_iov++;
		drqst.rq_nvec--;
	}

	rc = __cifs_calc_signature(&drqst, server, sigptr, shash);
	rc = __cifs_calc_signature(
		&drqst, server, smb2_signature,
		&(struct cifs_calc_sig_ctx){ .hmac = &hmac_ctx });
	if (!rc)
		memcpy(shdr->Signature, sigptr, SMB2_SIGNATURE_SIZE);
		memcpy(shdr->Signature, smb2_signature, SMB2_SIGNATURE_SIZE);

out:
	if (allocate_crypto)
		cifs_free_hash(&shash);
	return rc;
}

@@ -542,7 +510,6 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,
{
	int rc;
	unsigned char smb3_signature[SMB2_CMACAES_SIZE];
	unsigned char *sigptr = smb3_signature;
	struct kvec *iov = rqst->rq_iov;
	struct smb2_hdr *shdr = (struct smb2_hdr *)iov[0].iov_base;
	struct shash_desc *shash = NULL;
@@ -603,9 +570,11 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,
		drqst.rq_nvec--;
	}

	rc = __cifs_calc_signature(&drqst, server, sigptr, shash);
	rc = __cifs_calc_signature(
		&drqst, server, smb3_signature,
		&(struct cifs_calc_sig_ctx){ .shash = shash });
	if (!rc)
		memcpy(shdr->Signature, sigptr, SMB2_SIGNATURE_SIZE);
		memcpy(shdr->Signature, smb3_signature, SMB2_SIGNATURE_SIZE);

out:
	if (allocate_crypto)