diff --git a/config/config b/config/config index 592cc51..2397d08 100644 --- a/config/config +++ b/config/config @@ -169,6 +169,9 @@ CONFIG_CRYPTO_USER_API_AEAD=m CONFIG_ASYMMETRIC_KEY_TYPE=y CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y CONFIG_PKCS8_PRIVATE_KEY_PARSER=m +CONFIG_FIPS_SIGNATURE_SELFTEST=y +CONFIG_FIPS_SIGNATURE_SELFTEST_RSA=y +CONFIG_FIPS_SIGNATURE_SELFTEST_ECDSA=y ## ## file: drivers/accessibility/Kconfig diff --git a/prepare_source b/prepare_source index 7fa5289..f2985b9 100755 --- a/prepare_source +++ b/prepare_source @@ -1,5 +1,5 @@ pkg=linux -version_orig=6.12.91 +version_orig=6.12.92 version="$version_orig-1" ( @@ -30,4 +30,4 @@ apply_patches fixes_debian import_upstream_patches # Unlike the normal suffix, this needs to stay to indicate the patch level that the kernel has with # respect to the FIPS patches. -version_suffix=gl9~bp1877 +version_suffix=gl10~bp1877 diff --git a/upstream_patches/0007-fips-rng.patch b/upstream_patches/0007-fips-rng.patch new file mode 100644 index 0000000..e005802 --- /dev/null +++ b/upstream_patches/0007-fips-rng.patch @@ -0,0 +1,615 @@ +All patches have been refreshed and adjusted for 6.12 +From b18226696aded5b67567859b19c1391e3c4bf8c6 Mon Sep 17 00:00:00 2001 +From: Jeremy Allison +Date: Tue, 3 Mar 2026 14:42:31 -0800 +Subject: [PATCH] Forward port of: commit + 6892c65de813d311f20443401e528b4dc56872ff + + Author: Herbert Xu + Date: Thu Oct 19 18:43:54 2023 +0800 + + random: Add hook to override device reads and getrandom(2) + + Upstream Status: RHEL only + + Restore the changes to /dev/random which were reverted after 5.18. + + This reverts commit 900f11e054896bae7b0146055698656e3d1e20a6. + + 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 + +Signed-off-by: Jeremy Allison +--- + drivers/char/random.c | 121 +++++++++++++++++++++++++++++++++++++++++ + include/linux/random.h | 10 ++++ + 2 files changed, 131 insertions(+) + +diff --git a/drivers/char/random.c b/drivers/char/random.c +index a23c8b2feab6..305c5c697aa1 100644 +--- a/drivers/char/random.c ++++ b/drivers/char/random.c +@@ -54,6 +54,7 @@ + #include + #include + #include ++#include + #include + #include + #ifdef CONFIG_VDSO_GETRANDOM +@@ -323,6 +324,11 @@ static void crng_fast_key_erasure(u8 key[CHACHA_KEY_SIZE], + memzero_explicit(first_block, sizeof(first_block)); + } + ++/* ++ * Hook for external RNG. ++ */ ++static const struct random_extrng __rcu *extrng; ++ + /* + * This function returns a ChaCha state that you may use for generating + * random data. It also returns up to 32 bytes on its own of random data +@@ -752,6 +758,8 @@ static void __cold _credit_init_bits(size_t bits) + } + } + ++static const struct file_operations extrng_random_fops; ++static const struct file_operations extrng_urandom_fops; + + /********************************************************************** + * +@@ -970,6 +978,19 @@ void __init add_bootloader_randomness(const void *buf, size_t len) + credit_init_bits(len * 8); + } + ++void random_register_extrng(const struct random_extrng *rng) ++{ ++ rcu_assign_pointer(extrng, rng); ++} ++EXPORT_SYMBOL_GPL(random_register_extrng); ++ ++void random_unregister_extrng(void) ++{ ++ RCU_INIT_POINTER(extrng, NULL); ++ synchronize_rcu(); ++} ++EXPORT_SYMBOL_GPL(random_unregister_extrng); ++ + #if IS_ENABLED(CONFIG_VMGENID) + static BLOCKING_NOTIFIER_HEAD(vmfork_chain); + +@@ -1379,6 +1400,7 @@ SYSCALL_DEFINE3(getrandom, char __user *, ubuf, size_t, len, unsigned int, flags + { + struct iov_iter iter; + int ret; ++ const struct random_extrng *rng; + + if (flags & ~(GRND_NONBLOCK | GRND_RANDOM | GRND_INSECURE)) + return -EINVAL; +@@ -1390,6 +1412,21 @@ SYSCALL_DEFINE3(getrandom, char __user *, ubuf, size_t, len, unsigned int, flags + if ((flags & (GRND_INSECURE | GRND_RANDOM)) == (GRND_INSECURE | GRND_RANDOM)) + return -EINVAL; + ++ rcu_read_lock(); ++ rng = rcu_dereference(extrng); ++ if (rng && !try_module_get(rng->owner)) ++ rng = NULL; ++ rcu_read_unlock(); ++ ++ if (rng) { ++ ret = import_ubuf(ITER_DEST, ubuf, len, &iter); ++ if (unlikely(ret)) ++ return ret; ++ ret = rng->extrng_read_iter(&iter, !!(flags & GRND_RANDOM)); ++ module_put(rng->owner); ++ return ret; ++ } ++ + if (!crng_ready() && !(flags & GRND_INSECURE)) { + if (flags & GRND_NONBLOCK) + return -EAGAIN; +@@ -1410,6 +1447,12 @@ static __poll_t random_poll(struct file *file, poll_table *wait) + return crng_ready() ? EPOLLIN | EPOLLRDNORM : EPOLLOUT | EPOLLWRNORM; + } + ++static __poll_t extrng_poll(struct file *file, poll_table * wait) ++{ ++ /* extrng pool is always full, always read, no writes */ ++ return EPOLLIN | EPOLLRDNORM; ++} ++ + static ssize_t write_pool_user(struct iov_iter *iter) + { + u8 block[BLAKE2S_BLOCK_SIZE]; +@@ -1550,7 +1593,58 @@ static int random_fasync(int fd, struct file *filp, int on) + return fasync_helper(fd, filp, on, &fasync); + } + ++static int random_open(struct inode *inode, struct file *filp) ++{ ++ const struct random_extrng *rng; ++ ++ rcu_read_lock(); ++ rng = rcu_dereference(extrng); ++ if (rng && !try_module_get(rng->owner)) ++ rng = NULL; ++ rcu_read_unlock(); ++ ++ if (!rng) ++ return 0; ++ ++ filp->f_op = &extrng_random_fops; ++ filp->private_data = rng->owner; ++ ++ return 0; ++} ++ ++static int urandom_open(struct inode *inode, struct file *filp) ++{ ++ const struct random_extrng *rng; ++ ++ rcu_read_lock(); ++ rng = rcu_dereference(extrng); ++ if (rng && !try_module_get(rng->owner)) ++ rng = NULL; ++ rcu_read_unlock(); ++ ++ if (!rng) ++ return 0; ++ ++ filp->f_op = &extrng_urandom_fops; ++ filp->private_data = rng->owner; ++ ++ return 0; ++} ++ ++static int extrng_release(struct inode *inode, struct file *filp) ++{ ++ module_put(filp->private_data); ++ return 0; ++} ++ ++static ssize_t ++extrng_read_iter(struct kiocb *kiocb, struct iov_iter *iter) ++{ ++ return rcu_dereference_raw(extrng)->extrng_read_iter(iter, false); ++} ++ + const struct file_operations random_fops = { ++ .open = random_open, + .read_iter = random_read_iter, + .write_iter = random_write_iter, + .poll = random_poll, +@@ -1563,6 +1657,7 @@ const struct file_operations random_fops = { + }; + + const struct file_operations urandom_fops = { ++ .open = urandom_open, + .read_iter = urandom_read_iter, + .write_iter = random_write_iter, + .unlocked_ioctl = random_ioctl, +@@ -1573,6 +1668,32 @@ const struct file_operations urandom_fops = { + .splice_write = iter_file_splice_write, + }; + ++static const struct file_operations extrng_random_fops = { ++ .open = random_open, ++ .read_iter = extrng_read_iter, ++ .write_iter = random_write_iter, ++ .poll = extrng_poll, ++ .unlocked_ioctl = random_ioctl, ++ .compat_ioctl = compat_ptr_ioctl, ++ .fasync = random_fasync, ++ .llseek = noop_llseek, ++ .release = extrng_release, ++ .splice_read = copy_splice_read, ++ .splice_write = iter_file_splice_write, ++}; ++ ++static const struct file_operations extrng_urandom_fops = { ++ .open = urandom_open, ++ .read_iter = extrng_read_iter, ++ .write_iter = random_write_iter, ++ .unlocked_ioctl = random_ioctl, ++ .compat_ioctl = compat_ptr_ioctl, ++ .fasync = random_fasync, ++ .llseek = noop_llseek, ++ .release = extrng_release, ++ .splice_read = copy_splice_read, ++ .splice_write = iter_file_splice_write, ++}; + + /******************************************************************** + * +diff --git a/include/linux/random.h b/include/linux/random.h +index b0a940af4fff..8a52424fd0d5 100644 +--- a/include/linux/random.h ++++ b/include/linux/random.h +@@ -9,6 +9,13 @@ + + #include + ++struct iov_iter; ++ ++struct random_extrng { ++ ssize_t (*extrng_read_iter)(struct iov_iter *iter, bool reseed); ++ struct module *owner; ++}; ++ + struct notifier_block; + + void add_device_randomness(const void *buf, size_t len); +@@ -157,6 +164,9 @@ int random_prepare_cpu(unsigned int cpu); + int random_online_cpu(unsigned int cpu); + #endif + ++void random_register_extrng(const struct random_extrng *rng); ++void random_unregister_extrng(void); ++ + #ifndef MODULE + extern const struct file_operations random_fops, urandom_fops; + #endif +From efc289be71524e8b5860917d2aa93964da5165fc Mon Sep 17 00:00:00 2001 +From: Jeremy Allison +Date: Thu, 5 Mar 2026 11:19:36 -0800 +Subject: [PATCH] Forward port of commit + cac1a889124ad5ac5037056c6998450ea7463100 Author: Herbert Xu + Date: Mon Jul 14 16:36:10 2025 -0700 + + 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 + + Commit 8b0beca705b3877e24cccdd672422c66bbd75635 from kernel-ark. + +Signed-off-by: Jeremy Allison +--- + crypto/drbg.c | 18 ++++- + crypto/rng.c | 149 ++++++++++++++++++++++++++++++++++++----- + include/linux/crypto.h | 1 + + 3 files changed, 149 insertions(+), 19 deletions(-) + +diff --git a/crypto/drbg.c b/crypto/drbg.c +index 3addce90930c..730b03de596a 100644 +--- a/crypto/drbg.c ++++ b/crypto/drbg.c +@@ -1494,13 +1494,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; +@@ -1510,6 +1511,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) +@@ -1936,6 +1939,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 */ +@@ -1943,7 +1947,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 72da96fdfb5d..0d18dcb7347d 100644 +--- a/crypto/rng.c ++++ b/crypto/rng.c +@@ -12,10 +12,13 @@ + #include + #include + #include ++#include + #include + #include + #include + #include ++#include ++#include + #include + #include + #include +@@ -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; +@@ -106,31 +111,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; +@@ -146,24 +157,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 + +@@ -225,5 +245,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 b164da5e129e..30ae82ffc9ea 100644 +--- a/include/linux/crypto.h ++++ b/include/linux/crypto.h +@@ -133,6 +133,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 0x00001000 + + /* + * Miscellaneous stuff. +From c5f2b265ddbe2572f63e55eee2c93487ac7978c2 Mon Sep 17 00:00:00 2001 +From: Jeremy Allison +Date: Thu, 5 Mar 2026 15:48:46 -0800 +Subject: [PATCH] Forward port of commit + 07bbeb1c51a90072aabadddcfc36ce6eb06614cb + + Author: Herbert Xu + Date: Thu Aug 8 16:48:50 2024 +0800 + + not upstream: Disable vdso getrandom when FIPS is enabled + + In order to ensure that the FIPS-certified RNG is always used, + disable the vdso getrandom code by always making it fall back + to getrandom(2) when FIPS mode is enabled. + + Signed-off-by: Herbert Xu + +Signed-off-by: Jeremy Allison +--- + drivers/char/random.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/drivers/char/random.c b/drivers/char/random.c +index 305c5c697aa1..c74802574de4 100644 +--- a/drivers/char/random.c ++++ b/drivers/char/random.c +@@ -54,6 +54,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -739,7 +740,8 @@ static void __cold _credit_init_bits(size_t bits) + queue_work(system_unbound_wq, &set_ready); + atomic_notifier_call_chain(&random_ready_notifier, 0, NULL); + #ifdef CONFIG_VDSO_GETRANDOM +- WRITE_ONCE(__arch_get_k_vdso_rng_data()->is_ready, true); ++ if (!fips_enabled) ++ WRITE_ONCE(__arch_get_k_vdso_rng_data()->is_ready, true); + #endif + wake_up_interruptible(&crng_init_wait); + kill_fasync(&fasync, SIGIO, POLL_IN); diff --git a/upstream_patches/series b/upstream_patches/series index f14e45a..21f68a3 100644 --- a/upstream_patches/series +++ b/upstream_patches/series @@ -4,3 +4,4 @@ 0004-flag-instantiations-as-FIPS_compliant.patch 0005-disable-xxhash64.patch 0006_drbg_log.patch +0007-fips-rng.patch