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

smb: client: Use AES-CMAC library for SMB3 signature calculation



Convert smb3_calc_signature() to use the AES-CMAC library instead of a
"cmac(aes)" crypto_shash.

The result is simpler and faster code.  With the library there's no need
to allocate memory, no need to handle errors except for key preparation,
and the AES-CMAC code is accessed directly without inefficient indirect
calls and other unnecessary API overhead.

For now a "cmac(aes)" crypto_shash is still being allocated in
'struct cifs_secmech'.  Later commits will remove that, simplifying the
code even further.

Reviewed-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 44ccf416
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@ config CIFS
	select CRYPTO_CCM
	select CRYPTO_GCM
	select CRYPTO_AES
	select CRYPTO_LIB_AES_CBC_MACS
	select CRYPTO_LIB_ARC4
	select CRYPTO_LIB_MD5
	select CRYPTO_LIB_SHA256
+20 −40
Original line number Diff line number Diff line
@@ -22,49 +22,33 @@
#include <linux/fips.h>
#include <linux/iov_iter.h>
#include <crypto/aead.h>
#include <crypto/aes-cbc-macs.h>
#include <crypto/arc4.h>
#include <crypto/md5.h>
#include <crypto/sha2.h>

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 cifs_calc_sig_ctx *ctx = priv;
	int ret, *pret = priv2;

	ret = cifs_sig_update(ctx, iter_base, len);
	if (ret < 0) {
		*pret = ret;
		return len;
	if (ctx->md5)
		md5_update(ctx->md5, iter_base, len);
	else if (ctx->hmac)
		hmac_sha256_update(ctx->hmac, iter_base, len);
	else
		aes_cmac_update(ctx->cmac, iter_base, len);
	return 0; /* Return value is length *not* processed, i.e. 0. */
}
	return 0;

static void cifs_sig_final(struct cifs_calc_sig_ctx *ctx, u8 *out)
{
	if (ctx->md5)
		md5_final(ctx->md5, out);
	else if (ctx->hmac)
		hmac_sha256_final(ctx->hmac, out);
	else
		aes_cmac_final(ctx->cmac, out);
}

/*
@@ -75,9 +59,8 @@ static int cifs_sig_iter(const struct iov_iter *iter, size_t maxsize,
{
	struct iov_iter tmp_iter = *iter;
	size_t did;
	int err;

	did = iterate_and_advance_kernel(&tmp_iter, maxsize, ctx, &err,
	did = iterate_and_advance_kernel(&tmp_iter, maxsize, ctx, NULL,
					 cifs_sig_step);
	if (did != maxsize)
		return smb_EIO2(smb_eio_trace_sig_iter, did, maxsize);
@@ -108,11 +91,8 @@ int __cifs_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,
	if (rc < 0)
		return rc;

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

	return rc;
	cifs_sig_final(ctx, signature);
	return 0;
}

/* Build a proper attribute value/target info pairs blob.
+1 −1
Original line number Diff line number Diff line
@@ -2324,7 +2324,7 @@ static inline void mid_execute_callback(struct TCP_Server_Info *server,
struct cifs_calc_sig_ctx {
	struct md5_ctx *md5;
	struct hmac_sha256_ctx *hmac;
	struct shash_desc *shash;
	struct aes_cmac_ctx *cmac;
};

#define CIFS_RECONN_DELAY_SECS	30
+8 −33
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include <linux/mempool.h>
#include <linux/highmem.h>
#include <crypto/aead.h>
#include <crypto/aes-cbc-macs.h>
#include <crypto/sha2.h>
#include <crypto/utils.h>
#include "cifsglob.h"
@@ -474,7 +475,8 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,
	unsigned char smb3_signature[SMB2_CMACAES_SIZE];
	struct kvec *iov = rqst->rq_iov;
	struct smb2_hdr *shdr = (struct smb2_hdr *)iov[0].iov_base;
	struct shash_desc *shash = NULL;
	struct aes_cmac_key cmac_key;
	struct aes_cmac_ctx cmac_ctx;
	struct smb_rqst drqst;
	u8 key[SMB3_SIGN_KEY_SIZE];

@@ -487,33 +489,16 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,
		return rc;
	}

	if (allocate_crypto) {
		rc = cifs_alloc_hash("cmac(aes)", &shash);
		if (rc)
			return rc;
	} else {
		shash = server->secmech.aes_cmac;
	}

	memset(smb3_signature, 0x0, SMB2_CMACAES_SIZE);
	memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE);

	rc = crypto_shash_setkey(shash->tfm, key, SMB2_CMACAES_SIZE);
	rc = aes_cmac_preparekey(&cmac_key, key, SMB2_CMACAES_SIZE);
	if (rc) {
		cifs_server_dbg(VFS, "%s: Could not set key for cmac aes\n", __func__);
		goto out;
		return rc;
	}

	/*
	 * we already allocate aes_cmac when we init smb3 signing key,
	 * so unlike smb2 case we do not have to check here if secmech are
	 * initialized
	 */
	rc = crypto_shash_init(shash);
	if (rc) {
		cifs_server_dbg(VFS, "%s: Could not init cmac aes\n", __func__);
		goto out;
	}
	aes_cmac_init(&cmac_ctx, &cmac_key);

	/*
	 * For SMB2+, __cifs_calc_signature() expects to sign only the actual
@@ -524,26 +509,16 @@ smb3_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;
		}
		aes_cmac_update(&cmac_ctx, iov[0].iov_base, iov[0].iov_len);
		drqst.rq_iov++;
		drqst.rq_nvec--;
	}

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

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