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: Stefan Metzmacher <metze@samba.org>
Acked-by: Ard Biesheuvel <ardb@kernel.org>
Signed-off-by: Eric Biggers <ebiggers@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
Eric Biggers
2025-10-11 18:57:33 -07:00
committed by Steve French
parent 4b4c6fdb25
commit e05b3115e7
3 changed files with 53 additions and 61 deletions
+36 -16
View File
@@ -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,
void *priv, void *priv2)
static int cifs_sig_update(struct cifs_calc_sig_ctx *ctx,
const u8 *data, size_t len)
{
struct shash_desc *shash = priv;
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 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
View File
@@ -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
View File
@@ -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)