diff options
Diffstat (limited to 'fs/crypto/keysetup.c')
| -rw-r--r-- | fs/crypto/keysetup.c | 177 |
1 files changed, 89 insertions, 88 deletions
diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c index ce327bfdada4..892044ebcaca 100644 --- a/fs/crypto/keysetup.c +++ b/fs/crypto/keysetup.c @@ -83,8 +83,6 @@ static struct fscrypt_mode * select_encryption_mode(const union fscrypt_policy *policy, const struct inode *inode) { - BUILD_BUG_ON(ARRAY_SIZE(fscrypt_modes) != FSCRYPT_MODE_MAX + 1); - if (S_ISREG(inode->i_mode)) return &fscrypt_modes[fscrypt_policy_contents_mode(policy)]; @@ -146,9 +144,9 @@ err_free_tfm: /* * Prepare the crypto transform object or blk-crypto key in @prep_key, given the - * raw key, encryption mode (@ci->ci_mode), flag indicating which encryption - * implementation (fs-layer or blk-crypto) will be used (@ci->ci_inlinecrypt), - * and IV generation method (@ci->ci_policy.flags). + * raw key, encryption mode (@ci->ci_mode), predicate indicating which style of + * key is needed (fscrypt_using_inline_encryption(ci)), IV generation method + * (@ci->ci_policy.flags), and data unit size (@ci->ci_data_unit_bits). */ int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key, const u8 *raw_key, const struct fscrypt_inode_info *ci) @@ -163,13 +161,7 @@ int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key, tfm = fscrypt_allocate_skcipher(ci->ci_mode, raw_key, ci->ci_inode); if (IS_ERR(tfm)) return PTR_ERR(tfm); - /* - * Pairs with the smp_load_acquire() in fscrypt_is_key_prepared(). - * I.e., here we publish ->tfm with a RELEASE barrier so that - * concurrent tasks can ACQUIRE it. Note that this concurrency is only - * possible for per-mode keys, not for per-file keys. - */ - smp_store_release(&prep_key->tfm, tfm); + prep_key->tfm = tfm; return 0; } @@ -190,9 +182,37 @@ int fscrypt_set_per_file_enc_key(struct fscrypt_inode_info *ci, return fscrypt_prepare_key(&ci->ci_enc_key, raw_key, ci); } +/* + * Find the fscrypt_prepared_key (if any) for a particular (mk, hkdf_context, + * mode_num, data_unit_bits, inlinecrypt) combination. + * + * The caller must hold ->mk_sem for reading and ->mk_present must be true, + * ensuring that ->mk_mode_keys is still append-only. + */ +static struct fscrypt_prepared_key * +fscrypt_find_mode_key(struct fscrypt_master_key *mk, u8 hkdf_context, + u8 mode_num, const struct fscrypt_inode_info *ci) +{ + struct fscrypt_mode_key *node; + + /* + * The RCU read lock here is used only to synchronize with concurrent + * list_add_tail_rcu(). Concurrent deletions are impossible here, so + * returning a pointer to a node without taking any refcount is safe. + */ + guard(rcu)(); + list_for_each_entry_rcu(node, &mk->mk_mode_keys, link) { + if (node->hkdf_context == hkdf_context && + node->mode_num == mode_num && + node->data_unit_bits == ci->ci_data_unit_bits && + fscrypt_is_key_prepared(&node->key, ci)) + return &node->key; + } + return NULL; +} + static int setup_per_mode_enc_key(struct fscrypt_inode_info *ci, struct fscrypt_master_key *mk, - struct fscrypt_prepared_key *keys, u8 hkdf_context, bool include_fs_uuid) { const struct inode *inode = ci->ci_inode; @@ -200,71 +220,62 @@ static int setup_per_mode_enc_key(struct fscrypt_inode_info *ci, struct fscrypt_mode *mode = ci->ci_mode; const u8 mode_num = mode - fscrypt_modes; struct fscrypt_prepared_key *prep_key; - u8 mode_key[FSCRYPT_MAX_RAW_KEY_SIZE]; + struct fscrypt_mode_key *new_node; + u8 raw_mode_key[FSCRYPT_MAX_RAW_KEY_SIZE]; u8 hkdf_info[sizeof(mode_num) + sizeof(sb->s_uuid)]; unsigned int hkdf_infolen = 0; - bool use_hw_wrapped_key = false; int err; - if (WARN_ON_ONCE(mode_num > FSCRYPT_MODE_MAX)) - return -EINVAL; - - if (mk->mk_secret.is_hw_wrapped && S_ISREG(inode->i_mode)) { - /* Using a hardware-wrapped key for file contents encryption */ - if (!fscrypt_using_inline_encryption(ci)) { - if (sb->s_flags & SB_INLINECRYPT) - fscrypt_warn(ci->ci_inode, - "Hardware-wrapped key required, but no suitable inline encryption capabilities are available"); - else - fscrypt_warn(ci->ci_inode, - "Hardware-wrapped keys require inline encryption (-o inlinecrypt)"); - return -EINVAL; - } - use_hw_wrapped_key = true; + prep_key = fscrypt_find_mode_key(mk, hkdf_context, mode_num, ci); + if (prep_key) { + ci->ci_enc_key = *prep_key; + return 0; } - prep_key = &keys[mode_num]; - if (fscrypt_is_key_prepared(prep_key, ci)) { + guard(mutex)(&fscrypt_mode_key_setup_mutex); + + prep_key = fscrypt_find_mode_key(mk, hkdf_context, mode_num, ci); + if (prep_key) { ci->ci_enc_key = *prep_key; return 0; } - mutex_lock(&fscrypt_mode_key_setup_mutex); - - if (fscrypt_is_key_prepared(prep_key, ci)) - goto done_unlock; + new_node = kzalloc_obj(*new_node); + if (!new_node) + return -ENOMEM; + new_node->hkdf_context = hkdf_context; + new_node->mode_num = mode_num; + new_node->data_unit_bits = ci->ci_data_unit_bits; + prep_key = &new_node->key; - if (use_hw_wrapped_key) { + if (mk->mk_secret.is_hw_wrapped && S_ISREG(inode->i_mode)) { err = fscrypt_prepare_inline_crypt_key(prep_key, mk->mk_secret.bytes, mk->mk_secret.size, true, ci); - if (err) - goto out_unlock; - goto done_unlock; + } else { + static_assert(sizeof(mode_num) == 1); + static_assert(sizeof(sb->s_uuid) == 16); + static_assert(sizeof(hkdf_info) == 17); + hkdf_info[hkdf_infolen++] = mode_num; + if (include_fs_uuid) { + memcpy(&hkdf_info[hkdf_infolen], &sb->s_uuid, + sizeof(sb->s_uuid)); + hkdf_infolen += sizeof(sb->s_uuid); + } + fscrypt_hkdf_expand(&mk->mk_secret.hkdf, hkdf_context, + hkdf_info, hkdf_infolen, raw_mode_key, + mode->keysize); + err = fscrypt_prepare_key(prep_key, raw_mode_key, ci); + memzero_explicit(raw_mode_key, sizeof(raw_mode_key)); } - - BUILD_BUG_ON(sizeof(mode_num) != 1); - BUILD_BUG_ON(sizeof(sb->s_uuid) != 16); - BUILD_BUG_ON(sizeof(hkdf_info) != 17); - hkdf_info[hkdf_infolen++] = mode_num; - if (include_fs_uuid) { - memcpy(&hkdf_info[hkdf_infolen], &sb->s_uuid, - sizeof(sb->s_uuid)); - hkdf_infolen += sizeof(sb->s_uuid); + if (err) { + kfree(new_node); + return err; } - fscrypt_hkdf_expand(&mk->mk_secret.hkdf, hkdf_context, hkdf_info, - hkdf_infolen, mode_key, mode->keysize); - err = fscrypt_prepare_key(prep_key, mode_key, ci); - memzero_explicit(mode_key, mode->keysize); - if (err) - goto out_unlock; -done_unlock: + list_add_tail_rcu(&new_node->link, &mk->mk_mode_keys); ci->ci_enc_key = *prep_key; - err = 0; -out_unlock: - mutex_unlock(&fscrypt_mode_key_setup_mutex); - return err; + return 0; } /* @@ -311,25 +322,24 @@ static int fscrypt_setup_iv_ino_lblk_32_key(struct fscrypt_inode_info *ci, { int err; - err = setup_per_mode_enc_key(ci, mk, mk->mk_iv_ino_lblk_32_keys, - HKDF_CONTEXT_IV_INO_LBLK_32_KEY, true); + err = setup_per_mode_enc_key(ci, mk, HKDF_CONTEXT_IV_INO_LBLK_32_KEY, + true); if (err) return err; /* pairs with smp_store_release() below */ if (!smp_load_acquire(&mk->mk_ino_hash_key_initialized)) { - - mutex_lock(&fscrypt_mode_key_setup_mutex); - - if (mk->mk_ino_hash_key_initialized) - goto unlock; - - fscrypt_derive_siphash_key(mk, HKDF_CONTEXT_INODE_HASH_KEY, - NULL, 0, &mk->mk_ino_hash_key); - /* pairs with smp_load_acquire() above */ - smp_store_release(&mk->mk_ino_hash_key_initialized, true); -unlock: - mutex_unlock(&fscrypt_mode_key_setup_mutex); + guard(mutex)(&fscrypt_mode_key_setup_mutex); + + if (!mk->mk_ino_hash_key_initialized) { + fscrypt_derive_siphash_key(mk, + HKDF_CONTEXT_INODE_HASH_KEY, + NULL, 0, + &mk->mk_ino_hash_key); + /* pairs with smp_load_acquire() above */ + smp_store_release(&mk->mk_ino_hash_key_initialized, + true); + } } /* @@ -364,8 +374,8 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_inode_info *ci, * encryption key. This ensures that the master key is * consistently used only for HKDF, avoiding key reuse issues. */ - err = setup_per_mode_enc_key(ci, mk, mk->mk_direct_keys, - HKDF_CONTEXT_DIRECT_KEY, false); + err = setup_per_mode_enc_key(ci, mk, HKDF_CONTEXT_DIRECT_KEY, + false); } else if (ci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64) { /* @@ -374,9 +384,8 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_inode_info *ci, * the IVs. This format is optimized for use with inline * encryption hardware compliant with the UFS standard. */ - err = setup_per_mode_enc_key(ci, mk, mk->mk_iv_ino_lblk_64_keys, - HKDF_CONTEXT_IV_INO_LBLK_64_KEY, - true); + err = setup_per_mode_enc_key( + ci, mk, HKDF_CONTEXT_IV_INO_LBLK_64_KEY, true); } else if (ci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32) { err = fscrypt_setup_iv_ino_lblk_32_key(ci, mk); @@ -388,7 +397,7 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_inode_info *ci, ci->ci_nonce, FSCRYPT_FILE_NONCE_SIZE, derived_key, ci->ci_mode->keysize); err = fscrypt_set_per_file_enc_key(ci, derived_key); - memzero_explicit(derived_key, ci->ci_mode->keysize); + memzero_explicit(derived_key, sizeof(derived_key)); } if (err) return err; @@ -485,10 +494,6 @@ static int setup_file_encryption_key(struct fscrypt_inode_info *ci, if (ci->ci_policy.version != FSCRYPT_POLICY_V1) return -ENOKEY; - err = fscrypt_select_encryption_impl(ci, false); - if (err) - return err; - /* * As a legacy fallback for v1 policies, search for the key in * the current task's subscribed keyrings too. Don't move this @@ -510,10 +515,6 @@ static int setup_file_encryption_key(struct fscrypt_inode_info *ci, goto out_release_key; } - err = fscrypt_select_encryption_impl(ci, mk->mk_secret.is_hw_wrapped); - if (err) - goto out_release_key; - switch (ci->ci_policy.version) { case FSCRYPT_POLICY_V1: if (WARN_ON_ONCE(mk->mk_secret.is_hw_wrapped)) { |
