From: Herbert Xu <herbert.xu@xxxxxxxxxx> crypto: rng - Override drivers/char/random in FIPS mode Upstream Status: RHEL only Restore the changes to use the crypto RNG in drivers/char/random which were reverted after 5.18. This reverts commit 297bcb88233101e8d5062729ff3a5f989bad1c3b. This also brings the code up-to-date with respect to centos-stream commit 9de3a7339793d3c516b9305a8854267156f90c53 so that changes that were made after the kernel-ark revert have been brought in. Signed-off-by: Herbert Xu <herbert.xu@xxxxxxxxxx> diff --git a/crypto/drbg.c b/crypto/drbg.c index blahblah..blahblah 100644 --- a/crypto/drbg.c +++ b/crypto/drbg.c @@ -1510,13 +1510,14 @@ static int drbg_generate(struct drbg_state *drbg, * Wrapper around drbg_generate which can pull arbitrary long strings * from the DRBG without hitting the maximum request limitation. * - * Parameters: see drbg_generate + * Parameters: see drbg_generate, except @reseed, which triggers reseeding * Return codes: see drbg_generate -- if one drbg_generate request fails, * the entire drbg_generate_long request fails */ static int drbg_generate_long(struct drbg_state *drbg, unsigned char *buf, unsigned int buflen, - struct drbg_string *addtl) + struct drbg_string *addtl, + bool reseed) { unsigned int len = 0; unsigned int slice = 0; @@ -1526,6 +1527,8 @@ static int drbg_generate_long(struct drbg_state *drbg, slice = ((buflen - len) / drbg_max_request_bytes(drbg)); chunk = slice ? drbg_max_request_bytes(drbg) : (buflen - len); mutex_lock(&drbg->drbg_mutex); + if (reseed) + drbg->seeded = DRBG_SEED_STATE_UNSEEDED; err = drbg_generate(drbg, buf + len, chunk, addtl); mutex_unlock(&drbg->drbg_mutex); if (0 > err) @@ -1952,6 +1955,7 @@ static int drbg_kcapi_random(struct crypto_rng *tfm, struct drbg_state *drbg = crypto_rng_ctx(tfm); struct drbg_string *addtl = NULL; struct drbg_string string; + int err; if (slen) { /* linked list variable is now local to allow modification */ @@ -1959,7 +1963,15 @@ static int drbg_kcapi_random(struct crypto_rng *tfm, addtl = &string; } - return drbg_generate_long(drbg, dst, dlen, addtl); + err = drbg_generate_long(drbg, dst, dlen, addtl, + (crypto_tfm_get_flags(crypto_rng_tfm(tfm)) & + CRYPTO_TFM_REQ_NEED_RESEED) == + CRYPTO_TFM_REQ_NEED_RESEED); + + crypto_tfm_clear_flags(crypto_rng_tfm(tfm), + CRYPTO_TFM_REQ_NEED_RESEED); + + return err; } /* diff --git a/crypto/rng.c b/crypto/rng.c index blahblah..blahblah 100644 --- a/crypto/rng.c +++ b/crypto/rng.c @@ -12,10 +12,13 @@ #include <linux/atomic.h> #include <linux/cryptouser.h> #include <linux/err.h> +#include <linux/fips.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/random.h> +#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/seq_file.h> #include <linux/slab.h> #include <linux/string.h> @@ -23,7 +26,9 @@ #include "internal.h" -static DEFINE_MUTEX(crypto_default_rng_lock); +static ____cacheline_aligned_in_smp DEFINE_MUTEX(crypto_reseed_rng_lock); +static struct crypto_rng *crypto_reseed_rng; +static ____cacheline_aligned_in_smp DEFINE_MUTEX(crypto_default_rng_lock); struct crypto_rng *crypto_default_rng; EXPORT_SYMBOL_GPL(crypto_default_rng); static int crypto_default_rng_refcnt; @@ -136,31 +141,37 @@ struct crypto_rng *crypto_alloc_rng(const char *alg_name, u32 type, u32 mask) } EXPORT_SYMBOL_GPL(crypto_alloc_rng); -int crypto_get_default_rng(void) +static int crypto_get_rng(struct crypto_rng **rngp) { struct crypto_rng *rng; int err; - mutex_lock(&crypto_default_rng_lock); - if (!crypto_default_rng) { + if (!*rngp) { rng = crypto_alloc_rng("stdrng", 0, 0); err = PTR_ERR(rng); if (IS_ERR(rng)) - goto unlock; + return err; err = crypto_rng_reset(rng, NULL, crypto_rng_seedsize(rng)); if (err) { crypto_free_rng(rng); - goto unlock; + return err; } - crypto_default_rng = rng; + *rngp = rng; } - crypto_default_rng_refcnt++; - err = 0; + return 0; +} + +int crypto_get_default_rng(void) +{ + int err; -unlock: + mutex_lock(&crypto_default_rng_lock); + err = crypto_get_rng(&crypto_default_rng); + if (!err) + crypto_default_rng_refcnt++; mutex_unlock(&crypto_default_rng_lock); return err; @@ -176,24 +187,33 @@ void crypto_put_default_rng(void) EXPORT_SYMBOL_GPL(crypto_put_default_rng); #if defined(CONFIG_CRYPTO_RNG) || defined(CONFIG_CRYPTO_RNG_MODULE) -int crypto_del_default_rng(void) +static int crypto_del_rng(struct crypto_rng **rngp, int *refcntp, + struct mutex *lock) { int err = -EBUSY; - mutex_lock(&crypto_default_rng_lock); - if (crypto_default_rng_refcnt) + mutex_lock(lock); + if (refcntp && *refcntp) goto out; - crypto_free_rng(crypto_default_rng); - crypto_default_rng = NULL; + crypto_free_rng(*rngp); + *rngp = NULL; err = 0; out: - mutex_unlock(&crypto_default_rng_lock); + mutex_unlock(lock); return err; } + +int crypto_del_default_rng(void) +{ + return crypto_del_rng(&crypto_default_rng, &crypto_default_rng_refcnt, + &crypto_default_rng_lock) ?: + crypto_del_rng(&crypto_reseed_rng, NULL, + &crypto_reseed_rng_lock); +} EXPORT_SYMBOL_GPL(crypto_del_default_rng); #endif @@ -251,5 +271,102 @@ void crypto_unregister_rngs(struct rng_alg *algs, int count) } EXPORT_SYMBOL_GPL(crypto_unregister_rngs); +static ssize_t crypto_devrandom_read_iter(struct iov_iter *iter, bool reseed) +{ + struct crypto_rng *rng; + u8 tmp[256]; + ssize_t ret; + + if (unlikely(!iov_iter_count(iter))) + return 0; + + if (reseed) { + u32 flags = 0; + + /* If reseeding is requested, acquire a lock on + * crypto_reseed_rng so it is not swapped out until + * the initial random bytes are generated. + * + * The algorithm implementation is also protected with + * a separate mutex (drbg->drbg_mutex) around the + * reseed-and-generate operation. + */ + mutex_lock(&crypto_reseed_rng_lock); + + /* If crypto_default_rng is not set, it will be seeded + * at creation in __crypto_get_default_rng and thus no + * reseeding is needed. + */ + if (crypto_reseed_rng) + flags |= CRYPTO_TFM_REQ_NEED_RESEED; + + ret = crypto_get_rng(&crypto_reseed_rng); + if (ret) { + mutex_unlock(&crypto_reseed_rng_lock); + return ret; + } + + rng = crypto_reseed_rng; + crypto_tfm_set_flags(crypto_rng_tfm(rng), flags); + } else { + ret = crypto_get_default_rng(); + if (ret) + return ret; + rng = crypto_default_rng; + } + + for (;;) { + size_t i, copied; + int err; + + i = min_t(size_t, iov_iter_count(iter), sizeof(tmp)); + err = crypto_rng_get_bytes(rng, tmp, i); + if (err) { + ret = err; + break; + } + + copied = copy_to_iter(tmp, i, iter); + ret += copied; + + if (!iov_iter_count(iter)) + break; + + if (need_resched()) { + if (signal_pending(current)) + break; + schedule(); + } + } + + if (reseed) + mutex_unlock(&crypto_reseed_rng_lock); + else + crypto_put_default_rng(); + memzero_explicit(tmp, sizeof(tmp)); + + return ret; +} + +static const struct random_extrng crypto_devrandom_rng = { + .extrng_read_iter = crypto_devrandom_read_iter, + .owner = THIS_MODULE, +}; + +static int __init crypto_rng_init(void) +{ + if (fips_enabled) + random_register_extrng(&crypto_devrandom_rng); + return 0; +} + +static void __exit crypto_rng_exit(void) +{ + random_unregister_extrng(); +} + +late_initcall(crypto_rng_init); +module_exit(crypto_rng_exit); + MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Random Number Generator"); diff --git a/include/linux/crypto.h b/include/linux/crypto.h index blahblah..blahblah 100644 --- a/include/linux/crypto.h +++ b/include/linux/crypto.h @@ -135,6 +135,7 @@ #define CRYPTO_TFM_REQ_FORBID_WEAK_KEYS 0x00000100 #define CRYPTO_TFM_REQ_MAY_SLEEP 0x00000200 #define CRYPTO_TFM_REQ_MAY_BACKLOG 0x00000400 +#define CRYPTO_TFM_REQ_NEED_RESEED 0x00000800 /* * Miscellaneous stuff. -- https://gitlab.com/cki-project/kernel-ark/-/merge_requests/2757 _______________________________________________ kernel mailing list -- kernel@xxxxxxxxxxxxxxxxxxxxxxx To unsubscribe send an email to kernel-leave@xxxxxxxxxxxxxxxxxxxxxxx Fedora Code of Conduct: https://docs.fedoraproject.org/en-US/project/code-of-conduct/ List Guidelines: https://fedoraproject.org/wiki/Mailing_list_guidelines List Archives: https://lists.fedoraproject.org/archives/list/kernel@xxxxxxxxxxxxxxxxxxxxxxx Do not reply to spam, report it: https://pagure.io/fedora-infrastructure/new_issue