diff --git a/crypto/fips140-module.c b/crypto/fips140-module.c index 31df2b1e006f..61b90f6c7b8d 100644 --- a/crypto/fips140-module.c +++ b/crypto/fips140-module.c @@ -130,7 +130,7 @@ static bool __init is_fips140_algo(struct crypto_alg *alg) return false; } -static LIST_HEAD(unchecked_fips140_algos); +static LIST_HEAD(existing_live_algos); /* * Release a list of algorithms which have been removed from crypto_alg_list. @@ -173,38 +173,53 @@ static void __init unregister_existing_fips140_algos(void) down_write(&crypto_alg_sem); /* - * Find all registered algorithms that we care about, and move them to - * a private list so that they are no longer exposed via the algo - * lookup API. Subsequently, we will unregister them if they are not in - * active use. If they are, we cannot simply remove them but we can - * adapt them later to use our integrity checked backing code. + * Find all registered algorithms that we care about, and move them to a + * private list so that they are no longer exposed via the algo lookup + * API. Subsequently, we will unregister them if they are not in active + * use. If they are, we can't fully unregister them but we can ensure + * that new users won't use them. */ list_for_each_entry_safe(alg, tmp, &crypto_alg_list, cra_list) { - if (is_fips140_algo(alg)) { - if (refcount_read(&alg->cra_refcnt) == 1) { - /* - * This algorithm is not currently in use, but - * there may be template instances holding - * references to it via spawns. So let's tear - * it down like crypto_unregister_alg() would, - * but without releasing the lock, to prevent - * races with concurrent TFM allocations. - */ - alg->cra_flags |= CRYPTO_ALG_DEAD; - list_move(&alg->cra_list, &remove_list); - crypto_remove_spawns(alg, &spawns, NULL); - } else { - /* - * This algorithm is live, i.e., there are TFMs - * allocated that rely on it for its crypto - * transformations. We will swap these out - * later with integrity checked versions. - */ - pr_info("found already-live algorithm '%s' ('%s')\n", - alg->cra_name, alg->cra_driver_name); - list_move(&alg->cra_list, - &unchecked_fips140_algos); - } + if (!is_fips140_algo(alg)) + continue; + if (refcount_read(&alg->cra_refcnt) == 1) { + /* + * This algorithm is not currently in use, but there may + * be template instances holding references to it via + * spawns. So let's tear it down like + * crypto_unregister_alg() would, but without releasing + * the lock, to prevent races with concurrent TFM + * allocations. + */ + alg->cra_flags |= CRYPTO_ALG_DEAD; + list_move(&alg->cra_list, &remove_list); + crypto_remove_spawns(alg, &spawns, NULL); + } else { + /* + * This algorithm is live, i.e. it has TFMs allocated, + * so we can't fully unregister it. It's not necessary + * to dynamically redirect existing users to the FIPS + * code, given that they can't be relying on FIPS + * certified crypto in the first place. However, we do + * need to ensure that new users will get the FIPS code. + * + * In most cases, setting alg->cra_priority to 0 + * achieves this. However, that isn't enough for + * algorithms like "hmac(sha256)" that need to be + * instantiated from a template, since existing + * algorithms always take priority over a template being + * instantiated. Therefore, we move the algorithm to + * a private list so that algorithm lookups won't find + * it anymore. To further distinguish it from the FIPS + * algorithms, we also append "+orig" to its name. + */ + pr_info("found already-live algorithm '%s' ('%s')\n", + alg->cra_name, alg->cra_driver_name); + alg->cra_priority = 0; + strlcat(alg->cra_name, "+orig", CRYPTO_MAX_ALG_NAME); + strlcat(alg->cra_driver_name, "+orig", + CRYPTO_MAX_ALG_NAME); + list_move(&alg->cra_list, &existing_live_algos); } } up_write(&crypto_alg_sem); @@ -355,166 +370,6 @@ static bool __init check_fips140_module_hmac(void) return true; } -static bool __init update_live_fips140_algos(void) -{ - struct crypto_alg *alg, *new_alg, *tmp; - - /* - * Find all algorithms that we could not unregister the last time - * around, due to the fact that they were already in use. - */ - down_write(&crypto_alg_sem); - list_for_each_entry_safe(alg, tmp, &unchecked_fips140_algos, cra_list) { - - /* - * Take this algo off the list before releasing the lock. This - * ensures that a concurrent invocation of - * crypto_unregister_alg() observes a consistent state, i.e., - * the algo is still on the list, and crypto_unregister_alg() - * will release it, or it is not, and crypto_unregister_alg() - * will issue a warning but ignore this condition otherwise. - */ - list_del_init(&alg->cra_list); - up_write(&crypto_alg_sem); - - /* - * Grab the algo that will replace the live one. - * Note that this will instantiate template based instances as - * well, as long as their driver name uses the conventional - * pattern of "template(algo)". In this case, we are relying on - * the fact that the templates carried by this module will - * supersede the builtin ones, due to the fact that they were - * registered later, and therefore appear first in the linked - * list. For example, "hmac(sha1-ce)" constructed using the - * builtin hmac template and the builtin SHA1 driver will be - * superseded by the integrity checked versions of HMAC and - * SHA1-ce carried in this module. - * - * Note that this takes a reference to the new algorithm which - * will never get released. This is intentional: once we copy - * the function pointers from the new algo into the old one, we - * cannot drop the new algo unless we are sure that the old one - * has been released, and this is someting we don't keep track - * of at the moment. - */ - new_alg = crypto_alg_mod_lookup(alg->cra_driver_name, - alg->cra_flags & CRYPTO_ALG_TYPE_MASK, - CRYPTO_ALG_TYPE_MASK | CRYPTO_NOLOAD); - - if (IS_ERR(new_alg)) { - pr_crit("Failed to allocate '%s' for updating live algo (%ld)\n", - alg->cra_driver_name, PTR_ERR(new_alg)); - return false; - } - - /* - * The FIPS module's algorithms are expected to be built from - * the same source code as the in-kernel ones so that they are - * fully compatible. In general, there's no way to verify full - * compatibility at runtime, but we can at least verify that - * the algorithm properties match. - */ - if (alg->cra_ctxsize != new_alg->cra_ctxsize || - alg->cra_alignmask != new_alg->cra_alignmask) { - pr_crit("Failed to update live algo '%s' due to mismatch:\n" - "cra_ctxsize : %u vs %u\n" - "cra_alignmask : 0x%x vs 0x%x\n", - alg->cra_driver_name, - alg->cra_ctxsize, new_alg->cra_ctxsize, - alg->cra_alignmask, new_alg->cra_alignmask); - return false; - } - - /* - * Update the name and priority so the algorithm stands out as - * one that was updated in order to comply with FIPS140, and - * that it is not the preferred version for further use. - */ - strlcat(alg->cra_name, "+orig", CRYPTO_MAX_ALG_NAME); - alg->cra_priority = 0; - - switch (alg->cra_flags & CRYPTO_ALG_TYPE_MASK) { - struct aead_alg *old_aead, *new_aead; - struct skcipher_alg *old_skcipher, *new_skcipher; - struct shash_alg *old_shash, *new_shash; - struct rng_alg *old_rng, *new_rng; - - case CRYPTO_ALG_TYPE_CIPHER: - alg->cra_u.cipher = new_alg->cra_u.cipher; - break; - - case CRYPTO_ALG_TYPE_AEAD: - old_aead = container_of(alg, struct aead_alg, base); - new_aead = container_of(new_alg, struct aead_alg, base); - - old_aead->setkey = new_aead->setkey; - old_aead->setauthsize = new_aead->setauthsize; - old_aead->encrypt = new_aead->encrypt; - old_aead->decrypt = new_aead->decrypt; - old_aead->init = new_aead->init; - old_aead->exit = new_aead->exit; - break; - - case CRYPTO_ALG_TYPE_SKCIPHER: - old_skcipher = container_of(alg, struct skcipher_alg, base); - new_skcipher = container_of(new_alg, struct skcipher_alg, base); - - old_skcipher->setkey = new_skcipher->setkey; - old_skcipher->encrypt = new_skcipher->encrypt; - old_skcipher->decrypt = new_skcipher->decrypt; - old_skcipher->init = new_skcipher->init; - old_skcipher->exit = new_skcipher->exit; - break; - - case CRYPTO_ALG_TYPE_SHASH: - old_shash = container_of(alg, struct shash_alg, base); - new_shash = container_of(new_alg, struct shash_alg, base); - - old_shash->init = new_shash->init; - old_shash->update = new_shash->update; - old_shash->final = new_shash->final; - old_shash->finup = new_shash->finup; - old_shash->digest = new_shash->digest; - old_shash->export = new_shash->export; - old_shash->import = new_shash->import; - old_shash->setkey = new_shash->setkey; - old_shash->init_tfm = new_shash->init_tfm; - old_shash->exit_tfm = new_shash->exit_tfm; - break; - - case CRYPTO_ALG_TYPE_RNG: - old_rng = container_of(alg, struct rng_alg, base); - new_rng = container_of(new_alg, struct rng_alg, base); - - old_rng->generate = new_rng->generate; - old_rng->seed = new_rng->seed; - old_rng->set_ent = new_rng->set_ent; - break; - default: - /* - * This should never happen: every item on the - * fips140_algorithms list should match one of the - * cases above, so if we end up here, something is - * definitely wrong. - */ - pr_crit("Unexpected type %u for algo %s, giving up ...\n", - alg->cra_flags & CRYPTO_ALG_TYPE_MASK, - alg->cra_driver_name); - return false; - } - - /* - * Move the algorithm back to the algorithm list, so it is - * visible in /proc/crypto et al. - */ - down_write(&crypto_alg_sem); - list_add_tail(&alg->cra_list, &crypto_alg_list); - } - up_write(&crypto_alg_sem); - - return true; -} - static void fips140_sha256(void *p, const u8 *data, unsigned int len, u8 *out, int *hook_inuse) { @@ -613,19 +468,9 @@ fips140_init(void) complete_all(&fips140_tests_done); - if (!update_live_fips140_algos()) - goto panic; - if (!update_fips140_library_routines()) goto panic; - /* - * Wait until all tasks have at least been scheduled once and preempted - * voluntarily. This ensures that none of the superseded algorithms that - * were already in use will still be live. - */ - synchronize_rcu_tasks(); - pr_info("module successfully loaded\n"); return 0;