// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Cryptographic API for algorithms (i.e., low-level API).
*
* Copyright (c) 2006 Herbert Xu <herbert@gondor.apana.org.au>
*/
#include <crypto/algapi.h>
#include <crypto/internal/simd.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/fips.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/rtnetlink.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/workqueue.h>
#include "internal.h"
static LIST_HEAD(crypto_template_list);
#ifdef CONFIG_CRYPTO_MANAGER_EXTRA_TESTS
DEFINE_PER_CPU(bool, crypto_simd_disabled_for_test);
EXPORT_PER_CPU_SYMBOL_GPL(crypto_simd_disabled_for_test);
#endif
static inline void crypto_check_module_sig(struct module *mod)
{
if (fips_enabled && mod && !module_sig_ok(mod))
panic("Module %s signature verification failed in FIPS mode\n",
module_name(mod));
}
static int crypto_check_alg(struct crypto_alg *alg)
{
crypto_check_module_sig(alg->cra_module);
if (!alg->cra_name[0] || !alg->cra_driver_name[0])
return -EINVAL;
if (alg->cra_alignmask & (alg->cra_alignmask + 1))
return -EINVAL;
/* General maximums for all algs. */
if (alg->cra_alignmask > MAX_ALGAPI_ALIGNMASK)
return -EINVAL;
if (alg->cra_blocksize > MAX_ALGAPI_BLOCKSIZE)
return -EINVAL;
/* Lower maximums for specific alg types. */
if (!alg->cra_type && (alg->cra_flags & CRYPTO_ALG_TYPE_MASK) ==
CRYPTO_ALG_TYPE_CIPHER) {
if (alg->cra_alignmask > MAX_CIPHER_ALIGNMASK)
return -EINVAL;
if (alg->cra_blocksize > MAX_CIPHER_BLOCKSIZE)
return -EINVAL;
}
if (alg->cra_priority < 0)
return -EINVAL;
refcount_set(&alg->cra_refcnt, 1);
return 0;
}
static void crypto_free_instance(struct crypto_instance *inst)
{
inst->alg.cra_type->free(inst);
}
static void crypto_destroy_instance_workfn(struct work_struct *w)
{
struct crypto_instance *inst = container_of(w, struct crypto_instance,
free_work);
struct crypto_template *tmpl = inst->tmpl;
crypto_free_instance(inst);
crypto_tmpl_put(tmpl);
}
static void crypto_destroy_instance(struct crypto_alg *alg)
{
struct crypto_instance *inst = container_of(alg,
struct crypto_instance,
alg);
INIT_WORK(&inst->free_work, crypto_destroy_instance_workfn);
schedule_work(&inst->free_work);
}
/*
* This function adds a spawn to the list secondary_spawns which
* will be used at the end of crypto_remove_spawns to unregister
* instances, unless the spawn happens to be one that is depended
* on by the new algorithm (nalg in crypto_remove_spawns).
*
* This function is also responsible for resurrecting any algorithms
* in the dependency chain of nalg by unsetting n->dead.
*/
static struct list_head *crypto_more_spawns(struct crypto_alg *alg,
struct list_head *stack,
struct list_head *top,
struct list_head *secondary_spawns)
{
struct crypto_spawn *spawn, *n;
spawn = list_first_entry_or_null(stack, struct crypto_spawn, list);
if (!spawn)
return NULL;
n = list_prev_entry(spawn, list);
list_move(&spawn->list, secondary_spawns);
if (list_is_last(&n->list, stack))
return top;
n = list_next_entry(n, list);
if (!spawn->dead)
n->dead = false;
return &n->inst->alg.cra_users;
}
static void crypto_remove_instance(struct crypto_instance *inst,
struct list_head *list)
{
struct crypto_template *tmpl = inst->tmpl;
if (crypto_is_dead(&inst->alg))
return;
inst->alg.cra_flags |= CRYPTO_ALG_DEAD;
if (!tmpl || !crypto_tmpl_get(tmpl))
return;
list_move(&inst->alg.cra_list, list);
hlist_del(&inst->list);
inst->alg.cra_destroy = crypto_destroy_instance;
BUG_ON(!list_empty(&inst->alg.cra_users));
}
/*
* Given an algorithm alg, remove all algorithms that depend on it
* through spawns. If nalg is not null, then exempt any algorithms
* that is depended on by nalg. This is useful when nalg itself
* depends on alg.
*/
void crypto_remove_spawns(struct crypto_alg *alg, struct list_head *list,
struct crypto_alg *nalg)
{
u32 new_type = (nalg ?: alg)->cra_flags;
struct crypto_spawn *spawn, *