mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 20:06:24 +00:00
For the HMAC-MD5 computations in NTLMv2, use the HMAC-MD5 library
instead of a "hmac(md5)" 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-MD5 code is accessed directly without inefficient
indirect calls and other unnecessary API overhead.
To preserve the existing behavior of NTLMv2 support being disabled when
the kernel is booted with "fips=1", make ksmbd_auth_ntlmv2() check
fips_enabled itself. Previously it relied on the error from
crypto_alloc_shash("hmac(md5)") being bubbled up. I don't know for sure
that this is actually needed, but this preserves the existing behavior.
Signed-off-by: Eric Biggers <ebiggers@kernel.org>
Acked-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
235 lines
4.6 KiB
C
235 lines
4.6 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Copyright (C) 2019 Samsung Electronics Co., Ltd.
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/string.h>
|
|
#include <linux/err.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/wait.h>
|
|
#include <linux/sched.h>
|
|
|
|
#include "glob.h"
|
|
#include "crypto_ctx.h"
|
|
|
|
struct crypto_ctx_list {
|
|
spinlock_t ctx_lock;
|
|
int avail_ctx;
|
|
struct list_head idle_ctx;
|
|
wait_queue_head_t ctx_wait;
|
|
};
|
|
|
|
static struct crypto_ctx_list ctx_list;
|
|
|
|
static inline void free_aead(struct crypto_aead *aead)
|
|
{
|
|
if (aead)
|
|
crypto_free_aead(aead);
|
|
}
|
|
|
|
static void free_shash(struct shash_desc *shash)
|
|
{
|
|
if (shash) {
|
|
crypto_free_shash(shash->tfm);
|
|
kfree(shash);
|
|
}
|
|
}
|
|
|
|
static struct crypto_aead *alloc_aead(int id)
|
|
{
|
|
struct crypto_aead *tfm = NULL;
|
|
|
|
switch (id) {
|
|
case CRYPTO_AEAD_AES_GCM:
|
|
tfm = crypto_alloc_aead("gcm(aes)", 0, 0);
|
|
break;
|
|
case CRYPTO_AEAD_AES_CCM:
|
|
tfm = crypto_alloc_aead("ccm(aes)", 0, 0);
|
|
break;
|
|
default:
|
|
pr_err("Does not support encrypt ahead(id : %d)\n", id);
|
|
return NULL;
|
|
}
|
|
|
|
if (IS_ERR(tfm)) {
|
|
pr_err("Failed to alloc encrypt aead : %ld\n", PTR_ERR(tfm));
|
|
return NULL;
|
|
}
|
|
|
|
return tfm;
|
|
}
|
|
|
|
static struct shash_desc *alloc_shash_desc(int id)
|
|
{
|
|
struct crypto_shash *tfm = NULL;
|
|
struct shash_desc *shash;
|
|
|
|
switch (id) {
|
|
case CRYPTO_SHASH_CMACAES:
|
|
tfm = crypto_alloc_shash("cmac(aes)", 0, 0);
|
|
break;
|
|
default:
|
|
return NULL;
|
|
}
|
|
|
|
if (IS_ERR(tfm))
|
|
return NULL;
|
|
|
|
shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(tfm),
|
|
KSMBD_DEFAULT_GFP);
|
|
if (!shash)
|
|
crypto_free_shash(tfm);
|
|
else
|
|
shash->tfm = tfm;
|
|
return shash;
|
|
}
|
|
|
|
static void ctx_free(struct ksmbd_crypto_ctx *ctx)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < CRYPTO_SHASH_MAX; i++)
|
|
free_shash(ctx->desc[i]);
|
|
for (i = 0; i < CRYPTO_AEAD_MAX; i++)
|
|
free_aead(ctx->ccmaes[i]);
|
|
kfree(ctx);
|
|
}
|
|
|
|
static struct ksmbd_crypto_ctx *ksmbd_find_crypto_ctx(void)
|
|
{
|
|
struct ksmbd_crypto_ctx *ctx;
|
|
|
|
while (1) {
|
|
spin_lock(&ctx_list.ctx_lock);
|
|
if (!list_empty(&ctx_list.idle_ctx)) {
|
|
ctx = list_entry(ctx_list.idle_ctx.next,
|
|
struct ksmbd_crypto_ctx,
|
|
list);
|
|
list_del(&ctx->list);
|
|
spin_unlock(&ctx_list.ctx_lock);
|
|
return ctx;
|
|
}
|
|
|
|
if (ctx_list.avail_ctx > num_online_cpus()) {
|
|
spin_unlock(&ctx_list.ctx_lock);
|
|
wait_event(ctx_list.ctx_wait,
|
|
!list_empty(&ctx_list.idle_ctx));
|
|
continue;
|
|
}
|
|
|
|
ctx_list.avail_ctx++;
|
|
spin_unlock(&ctx_list.ctx_lock);
|
|
|
|
ctx = kzalloc(sizeof(struct ksmbd_crypto_ctx), KSMBD_DEFAULT_GFP);
|
|
if (!ctx) {
|
|
spin_lock(&ctx_list.ctx_lock);
|
|
ctx_list.avail_ctx--;
|
|
spin_unlock(&ctx_list.ctx_lock);
|
|
wait_event(ctx_list.ctx_wait,
|
|
!list_empty(&ctx_list.idle_ctx));
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
return ctx;
|
|
}
|
|
|
|
void ksmbd_release_crypto_ctx(struct ksmbd_crypto_ctx *ctx)
|
|
{
|
|
if (!ctx)
|
|
return;
|
|
|
|
spin_lock(&ctx_list.ctx_lock);
|
|
if (ctx_list.avail_ctx <= num_online_cpus()) {
|
|
list_add(&ctx->list, &ctx_list.idle_ctx);
|
|
spin_unlock(&ctx_list.ctx_lock);
|
|
wake_up(&ctx_list.ctx_wait);
|
|
return;
|
|
}
|
|
|
|
ctx_list.avail_ctx--;
|
|
spin_unlock(&ctx_list.ctx_lock);
|
|
ctx_free(ctx);
|
|
}
|
|
|
|
static struct ksmbd_crypto_ctx *____crypto_shash_ctx_find(int id)
|
|
{
|
|
struct ksmbd_crypto_ctx *ctx;
|
|
|
|
if (id >= CRYPTO_SHASH_MAX)
|
|
return NULL;
|
|
|
|
ctx = ksmbd_find_crypto_ctx();
|
|
if (ctx->desc[id])
|
|
return ctx;
|
|
|
|
ctx->desc[id] = alloc_shash_desc(id);
|
|
if (ctx->desc[id])
|
|
return ctx;
|
|
ksmbd_release_crypto_ctx(ctx);
|
|
return NULL;
|
|
}
|
|
|
|
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void)
|
|
{
|
|
return ____crypto_shash_ctx_find(CRYPTO_SHASH_CMACAES);
|
|
}
|
|
|
|
static struct ksmbd_crypto_ctx *____crypto_aead_ctx_find(int id)
|
|
{
|
|
struct ksmbd_crypto_ctx *ctx;
|
|
|
|
if (id >= CRYPTO_AEAD_MAX)
|
|
return NULL;
|
|
|
|
ctx = ksmbd_find_crypto_ctx();
|
|
if (ctx->ccmaes[id])
|
|
return ctx;
|
|
|
|
ctx->ccmaes[id] = alloc_aead(id);
|
|
if (ctx->ccmaes[id])
|
|
return ctx;
|
|
ksmbd_release_crypto_ctx(ctx);
|
|
return NULL;
|
|
}
|
|
|
|
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_gcm(void)
|
|
{
|
|
return ____crypto_aead_ctx_find(CRYPTO_AEAD_AES_GCM);
|
|
}
|
|
|
|
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_ccm(void)
|
|
{
|
|
return ____crypto_aead_ctx_find(CRYPTO_AEAD_AES_CCM);
|
|
}
|
|
|
|
void ksmbd_crypto_destroy(void)
|
|
{
|
|
struct ksmbd_crypto_ctx *ctx;
|
|
|
|
while (!list_empty(&ctx_list.idle_ctx)) {
|
|
ctx = list_entry(ctx_list.idle_ctx.next,
|
|
struct ksmbd_crypto_ctx,
|
|
list);
|
|
list_del(&ctx->list);
|
|
ctx_free(ctx);
|
|
}
|
|
}
|
|
|
|
int ksmbd_crypto_create(void)
|
|
{
|
|
struct ksmbd_crypto_ctx *ctx;
|
|
|
|
spin_lock_init(&ctx_list.ctx_lock);
|
|
INIT_LIST_HEAD(&ctx_list.idle_ctx);
|
|
init_waitqueue_head(&ctx_list.ctx_wait);
|
|
ctx_list.avail_ctx = 1;
|
|
|
|
ctx = kzalloc(sizeof(struct ksmbd_crypto_ctx), KSMBD_DEFAULT_GFP);
|
|
if (!ctx)
|
|
return -ENOMEM;
|
|
list_add(&ctx->list, &ctx_list.idle_ctx);
|
|
return 0;
|
|
}
|