linux-fscrypt.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 00/11] fscrypt: rearrangements preliminary to extent encryption
@ 2023-04-10 19:39 Sweet Tea Dorminy
  2023-04-10 19:39 ` [PATCH v2 01/11] fscrypt: move inline crypt decision to info setup Sweet Tea Dorminy
                   ` (11 more replies)
  0 siblings, 12 replies; 24+ messages in thread
From: Sweet Tea Dorminy @ 2023-04-10 19:39 UTC (permalink / raw)
  To: Eric Biggers, Theodore Y. Ts'o, Jaegeuk Kim, linux-fscrypt,
	kernel-team
  Cc: Sweet Tea Dorminy

As per [1], extent-based encryption needs to split allocating and
preparing crypto_skciphers, since extent infos will be loaded at IO time
and crypto_skciphers cannot be allocated at IO time. 

This changeset undertakes to split the existing code to clearly
distinguish preparation and allocation of fscrypt_prepared_keys,
wrapping crypto_skciphers. Elegance of code is in the eye of the
beholder, but I've tried a decent variety of arrangements here and this
seems like the clearest result to me; happy to adjust as desired, and
more changesets coming soon, this just seemed like the clearest cutoff
point for preliminaries without being pure refactoring.

Patchset should apply cleanly to fscrypt/for-next (as per base-commit
below), and pass ext4/f2fs tests (kvm-xfstests is not currently
succesfully setting up ubifs volumes for me).

[1] https://lore.kernel.org/linux-btrfs/Y7NQ1CvPyJiGRe00@sol.localdomain/ 

Changes from v1:
Included change 1, erroneously dropped, and generated patches using --base.

Sweet Tea Dorminy (11):
  fscrypt: move inline crypt decision to info setup.
  fscrypt: split and rename setup_file_encryption_key()
  fscrypt: split and rename setup_per_mode_enc_key()
  fscrypt: move dirhash key setup away from IO key setup
  fscrypt: reduce special-casing of IV_INO_LBLK_32
  fscrypt: make infos have a pointer to prepared keys
  fscrypt: move all the shared mode key setup deeper
  fscrypt: make ci->ci_direct_key a bool not a pointer
  fscrypt: make prepared keys record their type.
  fscrypt: explicitly track prepared parts of key
  fscrypt: split key alloc and preparation

 fs/crypto/crypto.c          |   2 +-
 fs/crypto/fname.c           |   4 +-
 fs/crypto/fscrypt_private.h |  73 +++++--
 fs/crypto/inline_crypt.c    |  30 +--
 fs/crypto/keysetup.c        | 387 ++++++++++++++++++++++++------------
 fs/crypto/keysetup_v1.c     |  13 +-
 6 files changed, 340 insertions(+), 169 deletions(-)


base-commit: 83e57e47906ce0e99bd61c70fae514e69960d274
-- 
2.40.0


^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v2 01/11] fscrypt: move inline crypt decision to info setup.
  2023-04-10 19:39 [PATCH v2 00/11] fscrypt: rearrangements preliminary to extent encryption Sweet Tea Dorminy
@ 2023-04-10 19:39 ` Sweet Tea Dorminy
  2023-04-10 19:39 ` [PATCH v2 02/11] fscrypt: split and rename setup_file_encryption_key() Sweet Tea Dorminy
                   ` (10 subsequent siblings)
  11 siblings, 0 replies; 24+ messages in thread
From: Sweet Tea Dorminy @ 2023-04-10 19:39 UTC (permalink / raw)
  To: Eric Biggers, Theodore Y. Ts'o, Jaegeuk Kim, linux-fscrypt,
	kernel-team
  Cc: Sweet Tea Dorminy

setup_file_encryption_key() is doing a lot of things at the moment --
setting the crypt_info's inline encryption bit, finding and locking a
master key, and calling the functions to get the appropriate prepared
key for this info. Since setting the inline encryption bit has nothing
to do with finding the master key, it's easy and hopefully clearer to
select the encryption implementation in fscrypt_setup_encryption_info(),
the main fscrypt_info setup function, instead of in
setup_file_encryption_key() which will long-term only deal in setting
up the prepared key for the info.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
 fs/crypto/keysetup.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c
index 361f41ef46c7..b89c32ad19fb 100644
--- a/fs/crypto/keysetup.c
+++ b/fs/crypto/keysetup.c
@@ -443,10 +443,6 @@ static int setup_file_encryption_key(struct fscrypt_info *ci,
 	struct fscrypt_master_key *mk;
 	int err;
 
-	err = fscrypt_select_encryption_impl(ci);
-	if (err)
-		return err;
-
 	err = fscrypt_policy_to_key_spec(&ci->ci_policy, &mk_spec);
 	if (err)
 		return err;
@@ -580,6 +576,10 @@ fscrypt_setup_encryption_info(struct inode *inode,
 	WARN_ON_ONCE(mode->ivsize > FSCRYPT_MAX_IV_SIZE);
 	crypt_info->ci_mode = mode;
 
+	res = fscrypt_select_encryption_impl(crypt_info);
+	if (res)
+		goto out;
+
 	res = setup_file_encryption_key(crypt_info, need_dirhash_key, &mk);
 	if (res)
 		goto out;
-- 
2.40.0


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 02/11] fscrypt: split and rename setup_file_encryption_key()
  2023-04-10 19:39 [PATCH v2 00/11] fscrypt: rearrangements preliminary to extent encryption Sweet Tea Dorminy
  2023-04-10 19:39 ` [PATCH v2 01/11] fscrypt: move inline crypt decision to info setup Sweet Tea Dorminy
@ 2023-04-10 19:39 ` Sweet Tea Dorminy
  2023-04-11  3:24   ` Eric Biggers
  2023-04-10 19:39 ` [PATCH v2 03/11] fscrypt: split and rename setup_per_mode_enc_key() Sweet Tea Dorminy
                   ` (9 subsequent siblings)
  11 siblings, 1 reply; 24+ messages in thread
From: Sweet Tea Dorminy @ 2023-04-10 19:39 UTC (permalink / raw)
  To: Eric Biggers, Theodore Y. Ts'o, Jaegeuk Kim, linux-fscrypt,
	kernel-team
  Cc: Sweet Tea Dorminy

At present, setup_file_encryption_key() does several things: it finds
and locks the master key, and then calls into the appropriate functions
to setup the prepared key for the fscrypt_info. The code is clearer to
follow if these functions are divided.

Thus, move calling the appropriate file key setup function into a new
fscrypt_setup_file_key() function. After the file key setup functions
are moved, the remaining function can take a const fscrypt_info, and is
renamed find_and_lock_master_key() to precisely describe its action.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
 fs/crypto/keysetup.c | 75 ++++++++++++++++++++++++++++----------------
 1 file changed, 48 insertions(+), 27 deletions(-)

diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c
index b89c32ad19fb..5989d53971ca 100644
--- a/fs/crypto/keysetup.c
+++ b/fs/crypto/keysetup.c
@@ -386,6 +386,43 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_info *ci,
 	return 0;
 }
 
+/*
+ * Find or create the appropriate prepared key for an info.
+ */
+static int fscrypt_setup_file_key(struct fscrypt_info *ci,
+				  struct fscrypt_master_key *mk,
+				  bool need_dirhash_key)
+{
+	int err;
+
+	if (!mk) {
+		if (ci->ci_policy.version != FSCRYPT_POLICY_V1)
+			return -ENOKEY;
+
+		/*
+		 * As a legacy fallback for v1 policies, search for the key in
+		 * the current task's subscribed keyrings too.  Don't move this
+		 * to before the search of ->s_master_keys, since users
+		 * shouldn't be able to override filesystem-level keys.
+		 */
+		return fscrypt_setup_v1_file_key_via_subscribed_keyrings(ci);
+	}
+
+	switch (ci->ci_policy.version) {
+	case FSCRYPT_POLICY_V1:
+		err = fscrypt_setup_v1_file_key(ci, mk->mk_secret.raw);
+		break;
+	case FSCRYPT_POLICY_V2:
+		err = fscrypt_setup_v2_file_key(ci, mk, need_dirhash_key);
+		break;
+	default:
+		WARN_ON_ONCE(1);
+		err = -EINVAL;
+		break;
+	}
+	return err;
+}
+
 /*
  * Check whether the size of the given master key (@mk) is appropriate for the
  * encryption settings which a particular file will use (@ci).
@@ -426,7 +463,7 @@ static bool fscrypt_valid_master_key_size(const struct fscrypt_master_key *mk,
 }
 
 /*
- * Find the master key, then set up the inode's actual encryption key.
+ * Find and lock the master key.
  *
  * If the master key is found in the filesystem-level keyring, then it is
  * returned in *mk_ret with its semaphore read-locked.  This is needed to ensure
@@ -434,9 +471,8 @@ static bool fscrypt_valid_master_key_size(const struct fscrypt_master_key *mk,
  * multiple tasks may race to create an fscrypt_info for the same inode), and to
  * synchronize the master key being removed with a new inode starting to use it.
  */
-static int setup_file_encryption_key(struct fscrypt_info *ci,
-				     bool need_dirhash_key,
-				     struct fscrypt_master_key **mk_ret)
+static int find_and_lock_master_key(const struct fscrypt_info *ci,
+				    struct fscrypt_master_key **mk_ret)
 {
 	struct super_block *sb = ci->ci_inode->i_sb;
 	struct fscrypt_key_specifier mk_spec;
@@ -466,17 +502,13 @@ static int setup_file_encryption_key(struct fscrypt_info *ci,
 			mk = fscrypt_find_master_key(sb, &mk_spec);
 		}
 	}
+
 	if (unlikely(!mk)) {
 		if (ci->ci_policy.version != FSCRYPT_POLICY_V1)
 			return -ENOKEY;
 
-		/*
-		 * As a legacy fallback for v1 policies, search for the key in
-		 * the current task's subscribed keyrings too.  Don't move this
-		 * to before the search of ->s_master_keys, since users
-		 * shouldn't be able to override filesystem-level keys.
-		 */
-		return fscrypt_setup_v1_file_key_via_subscribed_keyrings(ci);
+		*mk_ret = NULL;
+		return 0;
 	}
 	down_read(&mk->mk_sem);
 
@@ -491,21 +523,6 @@ static int setup_file_encryption_key(struct fscrypt_info *ci,
 		goto out_release_key;
 	}
 
-	switch (ci->ci_policy.version) {
-	case FSCRYPT_POLICY_V1:
-		err = fscrypt_setup_v1_file_key(ci, mk->mk_secret.raw);
-		break;
-	case FSCRYPT_POLICY_V2:
-		err = fscrypt_setup_v2_file_key(ci, mk, need_dirhash_key);
-		break;
-	default:
-		WARN_ON_ONCE(1);
-		err = -EINVAL;
-		break;
-	}
-	if (err)
-		goto out_release_key;
-
 	*mk_ret = mk;
 	return 0;
 
@@ -580,7 +597,11 @@ fscrypt_setup_encryption_info(struct inode *inode,
 	if (res)
 		goto out;
 
-	res = setup_file_encryption_key(crypt_info, need_dirhash_key, &mk);
+	res = find_and_lock_master_key(crypt_info, &mk);
+	if (res)
+		goto out;
+
+	res = fscrypt_setup_file_key(crypt_info, mk, need_dirhash_key);
 	if (res)
 		goto out;
 
-- 
2.40.0


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 03/11] fscrypt: split and rename setup_per_mode_enc_key()
  2023-04-10 19:39 [PATCH v2 00/11] fscrypt: rearrangements preliminary to extent encryption Sweet Tea Dorminy
  2023-04-10 19:39 ` [PATCH v2 01/11] fscrypt: move inline crypt decision to info setup Sweet Tea Dorminy
  2023-04-10 19:39 ` [PATCH v2 02/11] fscrypt: split and rename setup_file_encryption_key() Sweet Tea Dorminy
@ 2023-04-10 19:39 ` Sweet Tea Dorminy
  2023-04-11  3:29   ` Eric Biggers
  2023-04-10 19:39 ` [PATCH v2 04/11] fscrypt: move dirhash key setup away from IO key setup Sweet Tea Dorminy
                   ` (8 subsequent siblings)
  11 siblings, 1 reply; 24+ messages in thread
From: Sweet Tea Dorminy @ 2023-04-10 19:39 UTC (permalink / raw)
  To: Eric Biggers, Theodore Y. Ts'o, Jaegeuk Kim, linux-fscrypt,
	kernel-team
  Cc: Sweet Tea Dorminy

At present, setup_per_mode_enc_key() tries to find, within an array of
mode keys in the master key, an already prepared key, and if it doesn't
find a pre-prepared key, sets up a new one. This caching is not super
clear, at least to me, and splitting this function makes it clearer.

So, the new find_mode_prepared_key() decides if a pre-prepared key
already exists. If not, the renamed setup_new_mode_prepared_key()
deals with taking the mode setup lock and creating the new prepared key
for the master key.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
 fs/crypto/keysetup.c | 57 ++++++++++++++++++++++++++++----------------
 1 file changed, 36 insertions(+), 21 deletions(-)

diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c
index 5989d53971ca..7a3147382033 100644
--- a/fs/crypto/keysetup.c
+++ b/fs/crypto/keysetup.c
@@ -184,34 +184,24 @@ int fscrypt_set_per_file_enc_key(struct fscrypt_info *ci, const u8 *raw_key)
 	return fscrypt_prepare_key(&ci->ci_enc_key, raw_key, ci);
 }
 
-static int setup_per_mode_enc_key(struct fscrypt_info *ci,
-				  struct fscrypt_master_key *mk,
-				  struct fscrypt_prepared_key *keys,
-				  u8 hkdf_context, bool include_fs_uuid)
+static int setup_new_mode_prepared_key(struct fscrypt_master_key *mk,
+				       struct fscrypt_prepared_key *prep_key,
+				       const struct fscrypt_info *ci,
+				       u8 hkdf_context, bool include_fs_uuid)
 {
 	const struct inode *inode = ci->ci_inode;
 	const struct super_block *sb = inode->i_sb;
 	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_KEY_SIZE];
 	u8 hkdf_info[sizeof(mode_num) + sizeof(sb->s_uuid)];
 	unsigned int hkdf_infolen = 0;
 	int err;
 
-	if (WARN_ON_ONCE(mode_num > FSCRYPT_MODE_MAX))
-		return -EINVAL;
-
-	prep_key = &keys[mode_num];
-	if (fscrypt_is_key_prepared(prep_key, ci)) {
-		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;
+		goto out_unlock;
 
 	BUILD_BUG_ON(sizeof(mode_num) != 1);
 	BUILD_BUG_ON(sizeof(sb->s_uuid) != 16);
@@ -231,14 +221,39 @@ static int setup_per_mode_enc_key(struct fscrypt_info *ci,
 	memzero_explicit(mode_key, mode->keysize);
 	if (err)
 		goto out_unlock;
-done_unlock:
-	ci->ci_enc_key = *prep_key;
-	err = 0;
+
 out_unlock:
 	mutex_unlock(&fscrypt_mode_key_setup_mutex);
 	return err;
 }
 
+static int find_mode_prepared_key(struct fscrypt_info *ci,
+				  struct fscrypt_master_key *mk,
+				  struct fscrypt_prepared_key *keys,
+				  u8 hkdf_context, bool include_fs_uuid)
+{
+	struct fscrypt_mode *mode = ci->ci_mode;
+	const u8 mode_num = mode - fscrypt_modes;
+	struct fscrypt_prepared_key *prep_key;
+	int err;
+
+	if (WARN_ON_ONCE(mode_num > FSCRYPT_MODE_MAX))
+		return -EINVAL;
+
+	prep_key = &keys[mode_num];
+	if (fscrypt_is_key_prepared(prep_key, ci)) {
+		ci->ci_enc_key = *prep_key;
+		return 0;
+	}
+	err = setup_new_mode_prepared_key(mk, prep_key, ci, hkdf_context,
+					  include_fs_uuid);
+	if (err)
+		return err;
+
+	ci->ci_enc_key = *prep_key;
+	return 0;
+}
+
 /*
  * Derive a SipHash key from the given fscrypt master key and the given
  * application-specific information string.
@@ -294,7 +309,7 @@ static int fscrypt_setup_iv_ino_lblk_32_key(struct fscrypt_info *ci,
 {
 	int err;
 
-	err = setup_per_mode_enc_key(ci, mk, mk->mk_iv_ino_lblk_32_keys,
+	err = find_mode_prepared_key(ci, mk, mk->mk_iv_ino_lblk_32_keys,
 				     HKDF_CONTEXT_IV_INO_LBLK_32_KEY, true);
 	if (err)
 		return err;
@@ -344,7 +359,7 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_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,
+		err = find_mode_prepared_key(ci, mk, mk->mk_direct_keys,
 					     HKDF_CONTEXT_DIRECT_KEY, false);
 	} else if (ci->ci_policy.v2.flags &
 		   FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64) {
@@ -354,7 +369,7 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_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,
+		err = find_mode_prepared_key(ci, mk, mk->mk_iv_ino_lblk_64_keys,
 					     HKDF_CONTEXT_IV_INO_LBLK_64_KEY,
 					     true);
 	} else if (ci->ci_policy.v2.flags &
-- 
2.40.0


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 04/11] fscrypt: move dirhash key setup away from IO key setup
  2023-04-10 19:39 [PATCH v2 00/11] fscrypt: rearrangements preliminary to extent encryption Sweet Tea Dorminy
                   ` (2 preceding siblings ...)
  2023-04-10 19:39 ` [PATCH v2 03/11] fscrypt: split and rename setup_per_mode_enc_key() Sweet Tea Dorminy
@ 2023-04-10 19:39 ` Sweet Tea Dorminy
  2023-04-11  3:35   ` Eric Biggers
  2023-04-10 19:39 ` [PATCH v2 05/11] fscrypt: reduce special-casing of IV_INO_LBLK_32 Sweet Tea Dorminy
                   ` (7 subsequent siblings)
  11 siblings, 1 reply; 24+ messages in thread
From: Sweet Tea Dorminy @ 2023-04-10 19:39 UTC (permalink / raw)
  To: Eric Biggers, Theodore Y. Ts'o, Jaegeuk Kim, linux-fscrypt,
	kernel-team
  Cc: Sweet Tea Dorminy

The function named fscrypt_setup_v2_file_key() has as its main focus the
setting up of the fscrypt_info's ci_enc_key member, the prepared key
with which filenames or file contents are encrypted or decrypted.
However, it currently also sets up the dirhash key, used by some
directories, based on a parameter. There are no dependencies on
setting up the dirhash key beyond having the master key locked, and it's
clearer having fscrypt_setup_file_key() be only about setting up the
prepared key for IO.

Thus, move dirhash key setup to fscrypt_setup_encryption_info(), which
calls out to each function setting up parts of the fscrypt_info, and
stop passing the need_dirhash_key parameter around.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
 fs/crypto/keysetup.c | 33 ++++++++++++++++++++-------------
 1 file changed, 20 insertions(+), 13 deletions(-)

diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c
index 7a3147382033..82589c370b14 100644
--- a/fs/crypto/keysetup.c
+++ b/fs/crypto/keysetup.c
@@ -345,8 +345,7 @@ static int fscrypt_setup_iv_ino_lblk_32_key(struct fscrypt_info *ci,
 }
 
 static int fscrypt_setup_v2_file_key(struct fscrypt_info *ci,
-				     struct fscrypt_master_key *mk,
-				     bool need_dirhash_key)
+				     struct fscrypt_master_key *mk)
 {
 	int err;
 
@@ -391,13 +390,6 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_info *ci,
 	if (err)
 		return err;
 
-	/* Derive a secret dirhash key for directories that need it. */
-	if (need_dirhash_key) {
-		err = fscrypt_derive_dirhash_key(ci, mk);
-		if (err)
-			return err;
-	}
-
 	return 0;
 }
 
@@ -405,8 +397,7 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_info *ci,
  * Find or create the appropriate prepared key for an info.
  */
 static int fscrypt_setup_file_key(struct fscrypt_info *ci,
-				  struct fscrypt_master_key *mk,
-				  bool need_dirhash_key)
+				  struct fscrypt_master_key *mk)
 {
 	int err;
 
@@ -428,7 +419,7 @@ static int fscrypt_setup_file_key(struct fscrypt_info *ci,
 		err = fscrypt_setup_v1_file_key(ci, mk->mk_secret.raw);
 		break;
 	case FSCRYPT_POLICY_V2:
-		err = fscrypt_setup_v2_file_key(ci, mk, need_dirhash_key);
+		err = fscrypt_setup_v2_file_key(ci, mk);
 		break;
 	default:
 		WARN_ON_ONCE(1);
@@ -616,10 +607,26 @@ fscrypt_setup_encryption_info(struct inode *inode,
 	if (res)
 		goto out;
 
-	res = fscrypt_setup_file_key(crypt_info, mk, need_dirhash_key);
+	res = fscrypt_setup_file_key(crypt_info, mk);
 	if (res)
 		goto out;
 
+	/*
+	 * Derive a secret dirhash key for directories that need it. It
+	 * should be impossible to set flags such that a v1 policy sets
+	 * need_dirhash_key, but check it anyway.
+	 */
+	if (need_dirhash_key) {
+		if (WARN_ON_ONCE(policy->version == FSCRYPT_POLICY_V1)) {
+			res = -EINVAL;
+			goto out;
+		}
+
+		res = fscrypt_derive_dirhash_key(crypt_info, mk);
+		if (res)
+			goto out;
+	}
+
 	/*
 	 * For existing inodes, multiple tasks may race to set ->i_crypt_info.
 	 * So use cmpxchg_release().  This pairs with the smp_load_acquire() in
-- 
2.40.0


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 05/11] fscrypt: reduce special-casing of IV_INO_LBLK_32
  2023-04-10 19:39 [PATCH v2 00/11] fscrypt: rearrangements preliminary to extent encryption Sweet Tea Dorminy
                   ` (3 preceding siblings ...)
  2023-04-10 19:39 ` [PATCH v2 04/11] fscrypt: move dirhash key setup away from IO key setup Sweet Tea Dorminy
@ 2023-04-10 19:39 ` Sweet Tea Dorminy
  2023-04-11  3:38   ` Eric Biggers
  2023-04-10 19:39 ` [PATCH v2 06/11] fscrypt: make infos have a pointer to prepared keys Sweet Tea Dorminy
                   ` (6 subsequent siblings)
  11 siblings, 1 reply; 24+ messages in thread
From: Sweet Tea Dorminy @ 2023-04-10 19:39 UTC (permalink / raw)
  To: Eric Biggers, Theodore Y. Ts'o, Jaegeuk Kim, linux-fscrypt,
	kernel-team
  Cc: Sweet Tea Dorminy

Right now, the IV_INO_LBLK_32 policy is handled by its own function
called in fscrypt_setup_v2_file_key(), different from all other policies
which just call find_mode_prepared_key() with various parameters. The
function additionally sets up the relevant inode hashing key in the
master key, and uses it to hash the inode number if possible. This is
not particularly relevant to setting up a prepared key, so this change
tries to make it clear that every non-default policy uses basically the
same setup mechanism for its prepared key. The other setup is moved to
be called from the top crypt_info setup function.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
 fs/crypto/keysetup.c | 32 ++++++++++++++++++--------------
 1 file changed, 18 insertions(+), 14 deletions(-)

diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c
index 82589c370b14..8b32200dbbc0 100644
--- a/fs/crypto/keysetup.c
+++ b/fs/crypto/keysetup.c
@@ -304,16 +304,10 @@ void fscrypt_hash_inode_number(struct fscrypt_info *ci,
 					      &mk->mk_ino_hash_key);
 }
 
-static int fscrypt_setup_iv_ino_lblk_32_key(struct fscrypt_info *ci,
-					    struct fscrypt_master_key *mk)
+static int fscrypt_setup_ino_hash_key(struct fscrypt_master_key *mk)
 {
 	int err;
 
-	err = find_mode_prepared_key(ci, mk, mk->mk_iv_ino_lblk_32_keys,
-				     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)) {
 
@@ -335,12 +329,6 @@ static int fscrypt_setup_iv_ino_lblk_32_key(struct fscrypt_info *ci,
 			return err;
 	}
 
-	/*
-	 * New inodes may not have an inode number assigned yet.
-	 * Hashing their inode number is delayed until later.
-	 */
-	if (ci->ci_inode->i_ino)
-		fscrypt_hash_inode_number(ci, mk);
 	return 0;
 }
 
@@ -373,7 +361,9 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_info *ci,
 					     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);
+		err = find_mode_prepared_key(ci, mk, mk->mk_iv_ino_lblk_32_keys,
+					     HKDF_CONTEXT_IV_INO_LBLK_32_KEY,
+					     true);
 	} else {
 		u8 derived_key[FSCRYPT_MAX_KEY_SIZE];
 
@@ -627,6 +617,20 @@ fscrypt_setup_encryption_info(struct inode *inode,
 			goto out;
 	}
 
+	/*
+	 * The IV_INO_LBLK_32 policy needs a hashed inode number, but new
+	 * inodes may not have an inode number assigned yet.
+	 */
+	if (policy->version == FSCRYPT_POLICY_V2 &&
+	    (policy->v2.flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32)) {
+		res = fscrypt_setup_ino_hash_key(mk);
+		if (res)
+			goto out;
+
+		if (inode->i_ino)
+			fscrypt_hash_inode_number(crypt_info, mk);
+	}
+
 	/*
 	 * For existing inodes, multiple tasks may race to set ->i_crypt_info.
 	 * So use cmpxchg_release().  This pairs with the smp_load_acquire() in
-- 
2.40.0


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 06/11] fscrypt: make infos have a pointer to prepared keys
  2023-04-10 19:39 [PATCH v2 00/11] fscrypt: rearrangements preliminary to extent encryption Sweet Tea Dorminy
                   ` (4 preceding siblings ...)
  2023-04-10 19:39 ` [PATCH v2 05/11] fscrypt: reduce special-casing of IV_INO_LBLK_32 Sweet Tea Dorminy
@ 2023-04-10 19:39 ` Sweet Tea Dorminy
  2023-04-11  3:44   ` Eric Biggers
  2023-04-10 19:40 ` [PATCH v2 07/11] fscrypt: move all the shared mode key setup deeper Sweet Tea Dorminy
                   ` (5 subsequent siblings)
  11 siblings, 1 reply; 24+ messages in thread
From: Sweet Tea Dorminy @ 2023-04-10 19:39 UTC (permalink / raw)
  To: Eric Biggers, Theodore Y. Ts'o, Jaegeuk Kim, linux-fscrypt,
	kernel-team
  Cc: Sweet Tea Dorminy

At present, it's not entirely clear who owns a prepared key. Under
default policies, infos own the prepared key; but under any of the
policy flag key policies, or with some v1 policies, the info merely has
a copy of the authoritative prepared key; the authoritative copy of the
prepared key lives in the master key or the direct key, but the info has
no way to get to the authoritative key or get updates from it.

A scenario which could occur is the following:

-A directory tree is set up to use v2 policy DIRECT_KEY, mode adiantum.
-One directory is opened, gets a prepared key with a crypto_skcipher.
-A file within it is opened, sets up and gets the 'same' prepared key,
 but it's set up the blk_crypto_key in the prepared key.
-Another directory in the tree is opened, and gets the 'same' prepared
 key, but it's now got a pointer to the blk_crypto_key too.
-The two directories' ci_enc_key values are different, even though for
 practical purposes they are the same.

While it has no correctness implications, it's confusing for debugging
when two directories with the same mode/policy have different prepared
key contents depending on what else happened.

Adding a layer of indirection makes everything clearer at the cost of
another pointer. Now everyone sharing a prepared key within a direct key
or a master key have the same pointer to the single prepared key.
Followups move information from the crypt_info into the prepared key,
which ends up reducing memory usage slightly. And, it makes using
pooled, pre-allocated objects which could be stolen from a dormant
fscrypt_info much easier.

So this change makes crypt_info->ci_enc_key a pointer and updates all
users thereof.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
 fs/crypto/crypto.c          |  2 +-
 fs/crypto/fname.c           |  4 ++--
 fs/crypto/fscrypt_private.h |  2 +-
 fs/crypto/inline_crypt.c    |  4 ++--
 fs/crypto/keysetup.c        | 16 +++++++++++-----
 fs/crypto/keysetup_v1.c     |  2 +-
 6 files changed, 18 insertions(+), 12 deletions(-)

diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c
index 6a837e4b80dc..9f3bda18c797 100644
--- a/fs/crypto/crypto.c
+++ b/fs/crypto/crypto.c
@@ -108,7 +108,7 @@ int fscrypt_crypt_block(const struct inode *inode, fscrypt_direction_t rw,
 	DECLARE_CRYPTO_WAIT(wait);
 	struct scatterlist dst, src;
 	struct fscrypt_info *ci = inode->i_crypt_info;
-	struct crypto_skcipher *tfm = ci->ci_enc_key.tfm;
+	struct crypto_skcipher *tfm = ci->ci_enc_key->tfm;
 	int res = 0;
 
 	if (WARN_ON_ONCE(len <= 0))
diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c
index 6eae3f12ad50..edb78cd1b0e7 100644
--- a/fs/crypto/fname.c
+++ b/fs/crypto/fname.c
@@ -101,7 +101,7 @@ int fscrypt_fname_encrypt(const struct inode *inode, const struct qstr *iname,
 	struct skcipher_request *req = NULL;
 	DECLARE_CRYPTO_WAIT(wait);
 	const struct fscrypt_info *ci = inode->i_crypt_info;
-	struct crypto_skcipher *tfm = ci->ci_enc_key.tfm;
+	struct crypto_skcipher *tfm = ci->ci_enc_key->tfm;
 	union fscrypt_iv iv;
 	struct scatterlist sg;
 	int res;
@@ -158,7 +158,7 @@ static int fname_decrypt(const struct inode *inode,
 	DECLARE_CRYPTO_WAIT(wait);
 	struct scatterlist src_sg, dst_sg;
 	const struct fscrypt_info *ci = inode->i_crypt_info;
-	struct crypto_skcipher *tfm = ci->ci_enc_key.tfm;
+	struct crypto_skcipher *tfm = ci->ci_enc_key->tfm;
 	union fscrypt_iv iv;
 	int res;
 
diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h
index 7ab5a7b7eef8..5011737b60b3 100644
--- a/fs/crypto/fscrypt_private.h
+++ b/fs/crypto/fscrypt_private.h
@@ -198,7 +198,7 @@ struct fscrypt_prepared_key {
 struct fscrypt_info {
 
 	/* The key in a form prepared for actual encryption/decryption */
-	struct fscrypt_prepared_key ci_enc_key;
+	struct fscrypt_prepared_key *ci_enc_key;
 
 	/* True if ci_enc_key should be freed when this fscrypt_info is freed */
 	bool ci_owns_key;
diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c
index 8bfb3ce86476..2063f7941ce6 100644
--- a/fs/crypto/inline_crypt.c
+++ b/fs/crypto/inline_crypt.c
@@ -273,7 +273,7 @@ void fscrypt_set_bio_crypt_ctx(struct bio *bio, const struct inode *inode,
 	ci = inode->i_crypt_info;
 
 	fscrypt_generate_dun(ci, first_lblk, dun);
-	bio_crypt_set_ctx(bio, ci->ci_enc_key.blk_key, dun, gfp_mask);
+	bio_crypt_set_ctx(bio, ci->ci_enc_key->blk_key, dun, gfp_mask);
 }
 EXPORT_SYMBOL_GPL(fscrypt_set_bio_crypt_ctx);
 
@@ -360,7 +360,7 @@ bool fscrypt_mergeable_bio(struct bio *bio, const struct inode *inode,
 	 * uses the same pointer.  I.e., there's currently no need to support
 	 * merging requests where the keys are the same but the pointers differ.
 	 */
-	if (bc->bc_key != inode->i_crypt_info->ci_enc_key.blk_key)
+	if (bc->bc_key != inode->i_crypt_info->ci_enc_key->blk_key)
 		return false;
 
 	fscrypt_generate_dun(inode->i_crypt_info, next_lblk, next_dun);
diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c
index 8b32200dbbc0..f07e3b9579cf 100644
--- a/fs/crypto/keysetup.c
+++ b/fs/crypto/keysetup.c
@@ -181,7 +181,11 @@ void fscrypt_destroy_prepared_key(struct super_block *sb,
 int fscrypt_set_per_file_enc_key(struct fscrypt_info *ci, const u8 *raw_key)
 {
 	ci->ci_owns_key = true;
-	return fscrypt_prepare_key(&ci->ci_enc_key, raw_key, ci);
+	ci->ci_enc_key = kzalloc(sizeof(*ci->ci_enc_key), GFP_KERNEL);
+	if (!ci->ci_enc_key)
+		return -ENOMEM;
+
+	return fscrypt_prepare_key(ci->ci_enc_key, raw_key, ci);
 }
 
 static int setup_new_mode_prepared_key(struct fscrypt_master_key *mk,
@@ -242,7 +246,7 @@ static int find_mode_prepared_key(struct fscrypt_info *ci,
 
 	prep_key = &keys[mode_num];
 	if (fscrypt_is_key_prepared(prep_key, ci)) {
-		ci->ci_enc_key = *prep_key;
+		ci->ci_enc_key = prep_key;
 		return 0;
 	}
 	err = setup_new_mode_prepared_key(mk, prep_key, ci, hkdf_context,
@@ -250,7 +254,7 @@ static int find_mode_prepared_key(struct fscrypt_info *ci,
 	if (err)
 		return err;
 
-	ci->ci_enc_key = *prep_key;
+	ci->ci_enc_key = prep_key;
 	return 0;
 }
 
@@ -537,9 +541,11 @@ static void put_crypt_info(struct fscrypt_info *ci)
 
 	if (ci->ci_direct_key)
 		fscrypt_put_direct_key(ci->ci_direct_key);
-	else if (ci->ci_owns_key)
+	else if (ci->ci_owns_key) {
 		fscrypt_destroy_prepared_key(ci->ci_inode->i_sb,
-					     &ci->ci_enc_key);
+					     ci->ci_enc_key);
+		kfree(ci->ci_enc_key);
+	}
 
 	mk = ci->ci_master_key;
 	if (mk) {
diff --git a/fs/crypto/keysetup_v1.c b/fs/crypto/keysetup_v1.c
index 75dabd9b27f9..e1d761e8067f 100644
--- a/fs/crypto/keysetup_v1.c
+++ b/fs/crypto/keysetup_v1.c
@@ -259,7 +259,7 @@ static int setup_v1_file_key_direct(struct fscrypt_info *ci,
 	if (IS_ERR(dk))
 		return PTR_ERR(dk);
 	ci->ci_direct_key = dk;
-	ci->ci_enc_key = dk->dk_key;
+	ci->ci_enc_key = &dk->dk_key;
 	return 0;
 }
 
-- 
2.40.0


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 07/11] fscrypt: move all the shared mode key setup deeper
  2023-04-10 19:39 [PATCH v2 00/11] fscrypt: rearrangements preliminary to extent encryption Sweet Tea Dorminy
                   ` (5 preceding siblings ...)
  2023-04-10 19:39 ` [PATCH v2 06/11] fscrypt: make infos have a pointer to prepared keys Sweet Tea Dorminy
@ 2023-04-10 19:40 ` Sweet Tea Dorminy
  2023-04-11  3:56   ` Eric Biggers
  2023-04-10 19:40 ` [PATCH v2 08/11] fscrypt: make ci->ci_direct_key a bool not a pointer Sweet Tea Dorminy
                   ` (4 subsequent siblings)
  11 siblings, 1 reply; 24+ messages in thread
From: Sweet Tea Dorminy @ 2023-04-10 19:40 UTC (permalink / raw)
  To: Eric Biggers, Theodore Y. Ts'o, Jaegeuk Kim, linux-fscrypt,
	kernel-team
  Cc: Sweet Tea Dorminy

Currently, fscrypt_setup_v2_file_key() has a set of ifs which encode
various information about how to set up a new mode key if necessary for
a shared-key policy (DIRECT or IV_INO_LBLK_*). This is somewhat awkward
-- this information is only needed at the point that we need to setup a
new key, which is not the common case; the setup details are recorded as
function parameters relatively far from where they're actually used; and
at the point we use the parameters, we can derive the information
equally well.

So this moves mode and policy checking as deep into the callstack as
possible. mk_prepared_key_for_mode_policy() deals with the array lookup
within a master key. fill_hkdf_info() deals with filling in the hkdf
info as necessary for a particular policy. And hkdf_context_for_policy()
translates policy into hkdf context for key derivation. These seem a
little clearer in broad strokes, emphasizing the similarities between
the policies, but it does spread out the information on how the key is
derived for a particular policy more.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
 fs/crypto/keysetup.c | 120 ++++++++++++++++++++++++++++---------------
 1 file changed, 79 insertions(+), 41 deletions(-)

diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c
index f07e3b9579cf..845a92203c87 100644
--- a/fs/crypto/keysetup.c
+++ b/fs/crypto/keysetup.c
@@ -78,6 +78,11 @@ struct fscrypt_mode fscrypt_modes[] = {
 
 static DEFINE_MUTEX(fscrypt_mode_key_setup_mutex);
 
+static const u8 FSCRYPT_POLICY_FLAGS_KEY_MASK =
+	(FSCRYPT_POLICY_FLAG_DIRECT_KEY
+	 | FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64
+	 | FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32);
+
 static struct fscrypt_mode *
 select_encryption_mode(const union fscrypt_policy *policy,
 		       const struct inode *inode)
@@ -188,10 +193,57 @@ int fscrypt_set_per_file_enc_key(struct fscrypt_info *ci, const u8 *raw_key)
 	return fscrypt_prepare_key(ci->ci_enc_key, raw_key, ci);
 }
 
+static struct fscrypt_prepared_key *
+mk_prepared_key_for_mode_policy(struct fscrypt_master_key *mk,
+				union fscrypt_policy *policy,
+				struct fscrypt_mode *mode)
+{
+	const u8 mode_num = mode - fscrypt_modes;
+
+	switch (policy->v2.flags & FSCRYPT_POLICY_FLAGS_KEY_MASK) {
+	case FSCRYPT_POLICY_FLAG_DIRECT_KEY:
+		return &mk->mk_direct_keys[mode_num];
+	case FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64:
+		return &mk->mk_iv_ino_lblk_64_keys[mode_num];
+	case FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32:
+		return &mk->mk_iv_ino_lblk_32_keys[mode_num];
+	default:
+		return ERR_PTR(-EINVAL);
+	}
+}
+
+static size_t fill_hkdf_info(const struct fscrypt_info *ci, u8 *hkdf_info)
+{
+	const u8 mode_num = ci->ci_mode - fscrypt_modes;
+	const struct super_block *sb = ci->ci_inode->i_sb;
+	u8 hkdf_infolen = 0;
+
+	hkdf_info[hkdf_infolen++] = mode_num;
+	if (!(ci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY)) {
+		memcpy(&hkdf_info[hkdf_infolen], &sb->s_uuid,
+				sizeof(sb->s_uuid));
+		hkdf_infolen += sizeof(sb->s_uuid);
+	}
+	return hkdf_infolen;
+}
+
+static u8 hkdf_context_for_policy(const union fscrypt_policy *policy)
+{
+	switch (fscrypt_policy_flags(policy) & FSCRYPT_POLICY_FLAGS_KEY_MASK) {
+		case FSCRYPT_POLICY_FLAG_DIRECT_KEY:
+			return HKDF_CONTEXT_DIRECT_KEY;
+		case FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64:
+			return HKDF_CONTEXT_IV_INO_LBLK_64_KEY;
+		case FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32:
+			return HKDF_CONTEXT_IV_INO_LBLK_32_KEY;
+		default:
+			return 0;
+	}
+}
+
 static int setup_new_mode_prepared_key(struct fscrypt_master_key *mk,
 				       struct fscrypt_prepared_key *prep_key,
-				       const struct fscrypt_info *ci,
-				       u8 hkdf_context, bool include_fs_uuid)
+				       const struct fscrypt_info *ci)
 {
 	const struct inode *inode = ci->ci_inode;
 	const struct super_block *sb = inode->i_sb;
@@ -200,8 +252,23 @@ static int setup_new_mode_prepared_key(struct fscrypt_master_key *mk,
 	u8 mode_key[FSCRYPT_MAX_KEY_SIZE];
 	u8 hkdf_info[sizeof(mode_num) + sizeof(sb->s_uuid)];
 	unsigned int hkdf_infolen = 0;
+	u8 hkdf_context = hkdf_context_for_policy(&ci->ci_policy);
 	int err;
 
+	/*
+	 * For DIRECT_KEY policies: instead of deriving per-file encryption
+	 * keys, the per-file nonce will be included in all the IVs.  But
+	 * unlike v1 policies, for v2 policies in this case we don't encrypt
+	 * with the master key directly but rather derive a per-mode encryption
+	 * key.  This ensures that the master key is consistently used only for
+	 * HKDF, avoiding key reuse issues.
+	 *
+	 * For IV_INO_LBLK policies: encryption keys are derived from
+	 * (master_key, mode_num, filesystem_uuid), and inode number is
+	 * included in the IVs.  This format is optimized for use with inline
+	 * encryption hardware compliant with the UFS standard.
+	 */
+
 	mutex_lock(&fscrypt_mode_key_setup_mutex);
 
 	if (fscrypt_is_key_prepared(prep_key, ci))
@@ -210,12 +277,8 @@ static int setup_new_mode_prepared_key(struct fscrypt_master_key *mk,
 	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);
-	}
+	hkdf_infolen = fill_hkdf_info(ci, hkdf_info);
+
 	err = fscrypt_hkdf_expand(&mk->mk_secret.hkdf,
 				  hkdf_context, hkdf_info, hkdf_infolen,
 				  mode_key, mode->keysize);
@@ -232,9 +295,7 @@ static int setup_new_mode_prepared_key(struct fscrypt_master_key *mk,
 }
 
 static int find_mode_prepared_key(struct fscrypt_info *ci,
-				  struct fscrypt_master_key *mk,
-				  struct fscrypt_prepared_key *keys,
-				  u8 hkdf_context, bool include_fs_uuid)
+				  struct fscrypt_master_key *mk)
 {
 	struct fscrypt_mode *mode = ci->ci_mode;
 	const u8 mode_num = mode - fscrypt_modes;
@@ -244,13 +305,15 @@ static int find_mode_prepared_key(struct fscrypt_info *ci,
 	if (WARN_ON_ONCE(mode_num > FSCRYPT_MODE_MAX))
 		return -EINVAL;
 
-	prep_key = &keys[mode_num];
+	prep_key = mk_prepared_key_for_mode_policy(mk, &ci->ci_policy, mode);
+	if (IS_ERR(prep_key))
+		return PTR_ERR(prep_key);
+
 	if (fscrypt_is_key_prepared(prep_key, ci)) {
 		ci->ci_enc_key = prep_key;
 		return 0;
 	}
-	err = setup_new_mode_prepared_key(mk, prep_key, ci, hkdf_context,
-					  include_fs_uuid);
+	err = setup_new_mode_prepared_key(mk, prep_key, ci);
 	if (err)
 		return err;
 
@@ -341,33 +404,8 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_info *ci,
 {
 	int err;
 
-	if (ci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY) {
-		/*
-		 * DIRECT_KEY: instead of deriving per-file encryption keys, the
-		 * per-file nonce will be included in all the IVs.  But unlike
-		 * v1 policies, for v2 policies in this case we don't encrypt
-		 * with the master key directly but rather derive a per-mode
-		 * encryption key.  This ensures that the master key is
-		 * consistently used only for HKDF, avoiding key reuse issues.
-		 */
-		err = find_mode_prepared_key(ci, mk, mk->mk_direct_keys,
-					     HKDF_CONTEXT_DIRECT_KEY, false);
-	} else if (ci->ci_policy.v2.flags &
-		   FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64) {
-		/*
-		 * IV_INO_LBLK_64: encryption keys are derived from (master_key,
-		 * mode_num, filesystem_uuid), and inode number is included in
-		 * the IVs.  This format is optimized for use with inline
-		 * encryption hardware compliant with the UFS standard.
-		 */
-		err = find_mode_prepared_key(ci, mk, mk->mk_iv_ino_lblk_64_keys,
-					     HKDF_CONTEXT_IV_INO_LBLK_64_KEY,
-					     true);
-	} else if (ci->ci_policy.v2.flags &
-		   FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32) {
-		err = find_mode_prepared_key(ci, mk, mk->mk_iv_ino_lblk_32_keys,
-					     HKDF_CONTEXT_IV_INO_LBLK_32_KEY,
-					     true);
+	if (ci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAGS_KEY_MASK) {
+		err = find_mode_prepared_key(ci, mk);
 	} else {
 		u8 derived_key[FSCRYPT_MAX_KEY_SIZE];
 
-- 
2.40.0


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 08/11] fscrypt: make ci->ci_direct_key a bool not a pointer
  2023-04-10 19:39 [PATCH v2 00/11] fscrypt: rearrangements preliminary to extent encryption Sweet Tea Dorminy
                   ` (6 preceding siblings ...)
  2023-04-10 19:40 ` [PATCH v2 07/11] fscrypt: move all the shared mode key setup deeper Sweet Tea Dorminy
@ 2023-04-10 19:40 ` Sweet Tea Dorminy
  2023-04-11  3:57   ` Eric Biggers
  2023-04-10 19:40 ` [PATCH v2 09/11] fscrypt: make prepared keys record their type Sweet Tea Dorminy
                   ` (3 subsequent siblings)
  11 siblings, 1 reply; 24+ messages in thread
From: Sweet Tea Dorminy @ 2023-04-10 19:40 UTC (permalink / raw)
  To: Eric Biggers, Theodore Y. Ts'o, Jaegeuk Kim, linux-fscrypt,
	kernel-team
  Cc: Sweet Tea Dorminy

The ci_direct_key field is only used for v1 direct key policies,
recording the direct key that needs to have its refcount reduced when
the crypt_info is freed. However, now that crypt_info->ci_enc_key is a
pointer to the authoritative prepared key -- embedded in the direct key,
in this case, we no longer need to keep a full pointer to the direct key
-- we can use container_of() to go from the prepared key to its
surrounding direct key. Thus we can make ci_direct_key a bool instead of
a pointer, saving a few bytes.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
 fs/crypto/fscrypt_private.h | 7 +++----
 fs/crypto/keysetup.c        | 2 +-
 fs/crypto/keysetup_v1.c     | 7 +++++--
 3 files changed, 9 insertions(+), 7 deletions(-)

diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h
index 5011737b60b3..b575fb58a506 100644
--- a/fs/crypto/fscrypt_private.h
+++ b/fs/crypto/fscrypt_private.h
@@ -234,10 +234,9 @@ struct fscrypt_info {
 	struct list_head ci_master_key_link;
 
 	/*
-	 * If non-NULL, then encryption is done using the master key directly
-	 * and ci_enc_key will equal ci_direct_key->dk_key.
+	 * If true, then encryption is done using the master key directly.
 	 */
-	struct fscrypt_direct_key *ci_direct_key;
+	bool ci_direct_key;
 
 	/*
 	 * This inode's hash key for filenames.  This is a 128-bit SipHash-2-4
@@ -641,7 +640,7 @@ static inline int fscrypt_require_key(struct inode *inode)
 
 /* keysetup_v1.c */
 
-void fscrypt_put_direct_key(struct fscrypt_direct_key *dk);
+void fscrypt_put_direct_key(struct fscrypt_prepared_key *prep_key);
 
 int fscrypt_setup_v1_file_key(struct fscrypt_info *ci,
 			      const u8 *raw_master_key);
diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c
index 845a92203c87..d81001bf0a51 100644
--- a/fs/crypto/keysetup.c
+++ b/fs/crypto/keysetup.c
@@ -578,7 +578,7 @@ static void put_crypt_info(struct fscrypt_info *ci)
 		return;
 
 	if (ci->ci_direct_key)
-		fscrypt_put_direct_key(ci->ci_direct_key);
+		fscrypt_put_direct_key(ci->ci_enc_key);
 	else if (ci->ci_owns_key) {
 		fscrypt_destroy_prepared_key(ci->ci_inode->i_sb,
 					     ci->ci_enc_key);
diff --git a/fs/crypto/keysetup_v1.c b/fs/crypto/keysetup_v1.c
index e1d761e8067f..09de84c65368 100644
--- a/fs/crypto/keysetup_v1.c
+++ b/fs/crypto/keysetup_v1.c
@@ -160,8 +160,11 @@ static void free_direct_key(struct fscrypt_direct_key *dk)
 	}
 }
 
-void fscrypt_put_direct_key(struct fscrypt_direct_key *dk)
+void fscrypt_put_direct_key(struct fscrypt_prepared_key *prep_key)
 {
+	struct fscrypt_direct_key *dk =
+		container_of(prep_key, struct fscrypt_direct_key, dk_key);
+
 	if (!refcount_dec_and_lock(&dk->dk_refcount, &fscrypt_direct_keys_lock))
 		return;
 	hash_del(&dk->dk_node);
@@ -258,7 +261,7 @@ static int setup_v1_file_key_direct(struct fscrypt_info *ci,
 	dk = fscrypt_get_direct_key(ci, raw_master_key);
 	if (IS_ERR(dk))
 		return PTR_ERR(dk);
-	ci->ci_direct_key = dk;
+	ci->ci_direct_key = true;
 	ci->ci_enc_key = &dk->dk_key;
 	return 0;
 }
-- 
2.40.0


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 09/11] fscrypt: make prepared keys record their type.
  2023-04-10 19:39 [PATCH v2 00/11] fscrypt: rearrangements preliminary to extent encryption Sweet Tea Dorminy
                   ` (7 preceding siblings ...)
  2023-04-10 19:40 ` [PATCH v2 08/11] fscrypt: make ci->ci_direct_key a bool not a pointer Sweet Tea Dorminy
@ 2023-04-10 19:40 ` Sweet Tea Dorminy
  2023-04-10 19:40 ` [PATCH v2 10/11] fscrypt: explicitly track prepared parts of key Sweet Tea Dorminy
                   ` (2 subsequent siblings)
  11 siblings, 0 replies; 24+ messages in thread
From: Sweet Tea Dorminy @ 2023-04-10 19:40 UTC (permalink / raw)
  To: Eric Biggers, Theodore Y. Ts'o, Jaegeuk Kim, linux-fscrypt,
	kernel-team
  Cc: Sweet Tea Dorminy

Right now fscrypt_infos have two fields dedicated solely to recording
what type of prepared key the info has: whether it solely owns the
prepared key, or has borrowed it from a master key, or from a direct
key.

This information doesn't change during the lifetime of a prepared key.
Since at worst there's a prepared key per info, and at best many infos
share a single prepared key, it is slightly more efficient to store this
ownership info in the prepared key instead of in the fscrypt_info.
Especially since we can squash both fields down into a single enum. This
will also make it easy to record that a prepared key is part of the
pooled prepared keys when extent-based encryption is used.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
 fs/crypto/fscrypt_private.h | 28 ++++++++++++++++++++++------
 fs/crypto/keysetup.c        | 19 ++++++++++++-------
 fs/crypto/keysetup_v1.c     |  2 +-
 3 files changed, 35 insertions(+), 14 deletions(-)

diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h
index b575fb58a506..e726a1fb9f7e 100644
--- a/fs/crypto/fscrypt_private.h
+++ b/fs/crypto/fscrypt_private.h
@@ -174,18 +174,39 @@ struct fscrypt_symlink_data {
 	char encrypted_path[1];
 } __packed;
 
+/**
+ * enum fscrypt_prepared_key_type - records a prepared key's ownership
+ *
+ * @FSCRYPT_KEY_PER_INFO: this prepared key is allocated for a specific info
+ *		          and is never shared.
+ * @FSCRYPT_KEY_DIRECT_V1: this prepared key is embedded in a fscrypt_direct_key
+ *		           used in v1 direct key policies.
+ * @FSCRYPT_KEY_MASTER_KEY: this prepared key is a per-mode and policy key,
+ *			    part of a fscrypt_master_key, shared between all
+ *			    users of this master key having this mode and
+ *			    policy.
+ */
+enum fscrypt_prepared_key_type {
+	FSCRYPT_KEY_PER_INFO = 1,
+	FSCRYPT_KEY_DIRECT_V1,
+	FSCRYPT_KEY_MASTER_KEY,
+} __packed;
+
 /**
  * struct fscrypt_prepared_key - a key prepared for actual encryption/decryption
  * @tfm: crypto API transform object
  * @blk_key: key for blk-crypto
+ * @type: records the ownership type of the prepared key
  *
- * Normally only one of the fields will be non-NULL.
+ * Normally only one of @tfm and @blk_key will be non-NULL, although it is
+ * possible if @type is FSCRYPT_KEY_MASTER_KEY.
  */
 struct fscrypt_prepared_key {
 	struct crypto_skcipher *tfm;
 #ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT
 	struct blk_crypto_key *blk_key;
 #endif
+	enum fscrypt_prepared_key_type type;
 };
 
 /*
@@ -233,11 +254,6 @@ struct fscrypt_info {
 	 */
 	struct list_head ci_master_key_link;
 
-	/*
-	 * If true, then encryption is done using the master key directly.
-	 */
-	bool ci_direct_key;
-
 	/*
 	 * This inode's hash key for filenames.  This is a 128-bit SipHash-2-4
 	 * key.  This is only set for directories that use a keyed dirhash over
diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c
index d81001bf0a51..f338bb544932 100644
--- a/fs/crypto/keysetup.c
+++ b/fs/crypto/keysetup.c
@@ -185,11 +185,11 @@ void fscrypt_destroy_prepared_key(struct super_block *sb,
 /* Given a per-file encryption key, set up the file's crypto transform object */
 int fscrypt_set_per_file_enc_key(struct fscrypt_info *ci, const u8 *raw_key)
 {
-	ci->ci_owns_key = true;
 	ci->ci_enc_key = kzalloc(sizeof(*ci->ci_enc_key), GFP_KERNEL);
 	if (!ci->ci_enc_key)
 		return -ENOMEM;
 
+	ci->ci_enc_key->type = FSCRYPT_KEY_PER_INFO;
 	return fscrypt_prepare_key(ci->ci_enc_key, raw_key, ci);
 }
 
@@ -284,6 +284,7 @@ static int setup_new_mode_prepared_key(struct fscrypt_master_key *mk,
 				  mode_key, mode->keysize);
 	if (err)
 		goto out_unlock;
+	prep_key->type = FSCRYPT_KEY_MASTER_KEY;
 	err = fscrypt_prepare_key(prep_key, mode_key, ci);
 	memzero_explicit(mode_key, mode->keysize);
 	if (err)
@@ -577,12 +578,16 @@ static void put_crypt_info(struct fscrypt_info *ci)
 	if (!ci)
 		return;
 
-	if (ci->ci_direct_key)
-		fscrypt_put_direct_key(ci->ci_enc_key);
-	else if (ci->ci_owns_key) {
-		fscrypt_destroy_prepared_key(ci->ci_inode->i_sb,
-					     ci->ci_enc_key);
-		kfree(ci->ci_enc_key);
+	if (ci->ci_enc_key) {
+		enum fscrypt_prepared_key_type type = ci->ci_enc_key->type;
+
+		if (type == FSCRYPT_KEY_DIRECT_V1)
+			fscrypt_put_direct_key(ci->ci_enc_key);
+		if (type == FSCRYPT_KEY_PER_INFO) {
+			fscrypt_destroy_prepared_key(ci->ci_inode->i_sb,
+						     ci->ci_enc_key);
+			kfree(ci->ci_enc_key);
+		}
 	}
 
 	mk = ci->ci_master_key;
diff --git a/fs/crypto/keysetup_v1.c b/fs/crypto/keysetup_v1.c
index 09de84c65368..1e785cedead0 100644
--- a/fs/crypto/keysetup_v1.c
+++ b/fs/crypto/keysetup_v1.c
@@ -238,6 +238,7 @@ fscrypt_get_direct_key(const struct fscrypt_info *ci, const u8 *raw_key)
 	dk->dk_sb = ci->ci_inode->i_sb;
 	refcount_set(&dk->dk_refcount, 1);
 	dk->dk_mode = ci->ci_mode;
+	dk->dk_key.type = FSCRYPT_KEY_DIRECT_V1;
 	err = fscrypt_prepare_key(&dk->dk_key, raw_key, ci);
 	if (err)
 		goto err_free_dk;
@@ -261,7 +262,6 @@ static int setup_v1_file_key_direct(struct fscrypt_info *ci,
 	dk = fscrypt_get_direct_key(ci, raw_master_key);
 	if (IS_ERR(dk))
 		return PTR_ERR(dk);
-	ci->ci_direct_key = true;
 	ci->ci_enc_key = &dk->dk_key;
 	return 0;
 }
-- 
2.40.0


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 10/11] fscrypt: explicitly track prepared parts of key
  2023-04-10 19:39 [PATCH v2 00/11] fscrypt: rearrangements preliminary to extent encryption Sweet Tea Dorminy
                   ` (8 preceding siblings ...)
  2023-04-10 19:40 ` [PATCH v2 09/11] fscrypt: make prepared keys record their type Sweet Tea Dorminy
@ 2023-04-10 19:40 ` Sweet Tea Dorminy
  2023-04-11  4:05   ` Eric Biggers
  2023-04-10 19:40 ` [PATCH v2 11/11] fscrypt: split key alloc and preparation Sweet Tea Dorminy
  2023-04-11  3:18 ` [PATCH v2 00/11] fscrypt: rearrangements preliminary to extent encryption Eric Biggers
  11 siblings, 1 reply; 24+ messages in thread
From: Sweet Tea Dorminy @ 2023-04-10 19:40 UTC (permalink / raw)
  To: Eric Biggers, Theodore Y. Ts'o, Jaegeuk Kim, linux-fscrypt,
	kernel-team
  Cc: Sweet Tea Dorminy

So far, it has sufficed to allocate and prepare the block key or the TFM
completely before ever setting the relevant field in the prepared key.
This is necessary for mode keys -- because multiple inodes could be
trying to set up the same per-mode prepared key at the same time on
different threads, we currently must not set the prepared key's tfm or
block key pointer until that key is completely set up. Otherwise,
another inode could see the key to be present and attempt to use it
before it is fully set up.

But when using pooled prepared keys, we'll have pre-allocated fields,
and if we separate allocating the fields of a prepared key from
preparing the fields, that inherently sets the fields before they're
ready to use. So, either pooled prepared keys must use different
allocation and setup functions, or we can split allocation and
preparation for all prepared keys and use some other mechanism to signal
that the key is fully prepared.

In order to avoid having similar yet different functions, this function
adds a new field to the prepared key to explicitly track which parts of
it are prepared, setting it explicitly. The same acquire/release
semantics are used to check it in the case of shared mode keys; the cost
lies in the extra byte per prepared key recording which members are
fully prepared.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
 fs/crypto/fscrypt_private.h | 26 +++++++++++++++-----------
 fs/crypto/inline_crypt.c    |  8 +-------
 fs/crypto/keysetup.c        | 36 ++++++++++++++++++++++++++----------
 3 files changed, 42 insertions(+), 28 deletions(-)

diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h
index e726a1fb9f7e..7253cdb5e4d8 100644
--- a/fs/crypto/fscrypt_private.h
+++ b/fs/crypto/fscrypt_private.h
@@ -197,6 +197,8 @@ enum fscrypt_prepared_key_type {
  * @tfm: crypto API transform object
  * @blk_key: key for blk-crypto
  * @type: records the ownership type of the prepared key
+ * @prepared_members: records which of @tfm and @blk_key are prepared. tfm
+ *		      corresponds to bit 0; blk_key corresponds to bit 1.
  *
  * Normally only one of @tfm and @blk_key will be non-NULL, although it is
  * possible if @type is FSCRYPT_KEY_MASTER_KEY.
@@ -207,6 +209,7 @@ struct fscrypt_prepared_key {
 	struct blk_crypto_key *blk_key;
 #endif
 	enum fscrypt_prepared_key_type type;
+	u8 prepared_members;
 };
 
 /*
@@ -363,24 +366,25 @@ void fscrypt_destroy_inline_crypt_key(struct super_block *sb,
 				      struct fscrypt_prepared_key *prep_key);
 
 /*
- * Check whether the crypto transform or blk-crypto key has been allocated in
+ * Check whether the crypto transform or blk-crypto key has been prepared in
  * @prep_key, depending on which encryption implementation the file will use.
  */
 static inline bool
 fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key,
 			const struct fscrypt_info *ci)
 {
+	u8 prepared_members = smp_load_acquire(&prep_key->prepared_members);
+	bool inlinecrypt = fscrypt_using_inline_encryption(ci);
+
 	/*
-	 * The two smp_load_acquire()'s here pair with the smp_store_release()'s
-	 * in fscrypt_prepare_inline_crypt_key() and fscrypt_prepare_key().
-	 * I.e., in some cases (namely, if this prep_key is a per-mode
-	 * encryption key) another task can publish blk_key or tfm concurrently,
-	 * executing a RELEASE barrier.  We need to use smp_load_acquire() here
-	 * to safely ACQUIRE the memory the other task published.
+	 * The smp_load_acquire() here pairs with the smp_store_release()
+	 * in fscrypt_prepare_key().  I.e., in some cases (namely, if this
+	 * prep_key is a per-mode encryption key) another task can publish
+	 * blk_key or tfm concurrently, executing a RELEASE barrier.  We need
+	 * to use smp_load_acquire() here to safely ACQUIRE the memory the
+	 * other task published.
 	 */
-	if (fscrypt_using_inline_encryption(ci))
-		return smp_load_acquire(&prep_key->blk_key) != NULL;
-	return smp_load_acquire(&prep_key->tfm) != NULL;
+	return prepared_members & (1U << inlinecrypt);
 }
 
 #else /* CONFIG_FS_ENCRYPTION_INLINE_CRYPT */
@@ -415,7 +419,7 @@ static inline bool
 fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key,
 			const struct fscrypt_info *ci)
 {
-	return smp_load_acquire(&prep_key->tfm) != NULL;
+	return smp_load_acquire(&prep_key->prepared_members);
 }
 #endif /* !CONFIG_FS_ENCRYPTION_INLINE_CRYPT */
 
diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c
index 2063f7941ce6..ce952dedba77 100644
--- a/fs/crypto/inline_crypt.c
+++ b/fs/crypto/inline_crypt.c
@@ -191,13 +191,7 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
 		goto fail;
 	}
 
-	/*
-	 * Pairs with the smp_load_acquire() in fscrypt_is_key_prepared().
-	 * I.e., here we publish ->blk_key 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->blk_key, blk_key);
+	prep_key->blk_key = blk_key;
 	return 0;
 
 fail:
diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c
index f338bb544932..6efac89d49ec 100644
--- a/fs/crypto/keysetup.c
+++ b/fs/crypto/keysetup.c
@@ -155,21 +155,37 @@ fscrypt_allocate_skcipher(struct fscrypt_mode *mode, const u8 *raw_key,
 int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key,
 			const u8 *raw_key, const struct fscrypt_info *ci)
 {
-	struct crypto_skcipher *tfm;
+	int err;
+	bool inlinecrypt = fscrypt_using_inline_encryption(ci);
+	u8 prepared_member = (1 << inlinecrypt);
 
-	if (fscrypt_using_inline_encryption(ci))
-		return fscrypt_prepare_inline_crypt_key(prep_key, raw_key, ci);
+	if (inlinecrypt) {
+		err = fscrypt_prepare_inline_crypt_key(prep_key, raw_key, ci);
+	} else {
+		struct crypto_skcipher *tfm;
+
+		tfm = fscrypt_allocate_skcipher(ci->ci_mode, raw_key, ci->ci_inode);
+		if (IS_ERR(tfm))
+			return PTR_ERR(tfm);
+		}
+
+		prep_key->tfm = tfm;
+	}
 
-	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.
+	 * I.e., here we publish ->prepared_members 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. For per-mode keys, we have smp_load_acquire'd
+	 * the value of ->prepared_members after taking a lock serializing
+	 * preparing this key, so the value is stable and no other thread can
+	 * have modified it since the read. So another thread can't be trying
+	 * to run this same code in parallel, and we don't need to use cmpxchg.
 	 */
-	smp_store_release(&prep_key->tfm, tfm);
+	smp_store_release(&prep_key->prepared_members,
+			  prep_key->prepared_members | prepared_member);
 	return 0;
 }
 
-- 
2.40.0


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 11/11] fscrypt: split key alloc and preparation
  2023-04-10 19:39 [PATCH v2 00/11] fscrypt: rearrangements preliminary to extent encryption Sweet Tea Dorminy
                   ` (9 preceding siblings ...)
  2023-04-10 19:40 ` [PATCH v2 10/11] fscrypt: explicitly track prepared parts of key Sweet Tea Dorminy
@ 2023-04-10 19:40 ` Sweet Tea Dorminy
  2023-04-11  3:18 ` [PATCH v2 00/11] fscrypt: rearrangements preliminary to extent encryption Eric Biggers
  11 siblings, 0 replies; 24+ messages in thread
From: Sweet Tea Dorminy @ 2023-04-10 19:40 UTC (permalink / raw)
  To: Eric Biggers, Theodore Y. Ts'o, Jaegeuk Kim, linux-fscrypt,
	kernel-team
  Cc: Sweet Tea Dorminy

For extent-based encryption, we plan to use pooled prepared keys, since
it's unsafe to allocate a new crypto_skcipher when performing IO. This
will require being able to set up a pre-allocated prepared key, while
the current code requires allocating and setting up simultaneously.

This pulls apart fscrypt_allocate_skcipher() to only allocate; pulls
allocation out of fscrypt_prepare_inline_crypt_key(); creates a new
function fscrypt_allocate_key_member() that allocates the appropriate
member of a prepared key; and reflects these changes throughout.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
 fs/crypto/fscrypt_private.h | 14 +++++++++++
 fs/crypto/inline_crypt.c    | 20 ++++++++++------
 fs/crypto/keysetup.c        | 47 ++++++++++++++++++++++++++-----------
 fs/crypto/keysetup_v1.c     |  4 ++++
 4 files changed, 64 insertions(+), 21 deletions(-)

diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h
index 7253cdb5e4d8..97323b1e71e7 100644
--- a/fs/crypto/fscrypt_private.h
+++ b/fs/crypto/fscrypt_private.h
@@ -358,6 +358,9 @@ fscrypt_using_inline_encryption(const struct fscrypt_info *ci)
 	return ci->ci_inlinecrypt;
 }
 
+int fscrypt_allocate_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
+				      const struct fscrypt_info *ci);
+
 int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
 				     const u8 *raw_key,
 				     const struct fscrypt_info *ci);
@@ -400,6 +403,14 @@ fscrypt_using_inline_encryption(const struct fscrypt_info *ci)
 	return false;
 }
 
+static inline int
+fscrypt_allocate_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
+				  const struct fscrypt_info *ci)
+{
+	WARN_ON(1);
+	return -EOPNOTSUPP;
+}
+
 static inline int
 fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
 				 const u8 *raw_key,
@@ -616,6 +627,9 @@ struct fscrypt_mode {
 
 extern struct fscrypt_mode fscrypt_modes[];
 
+int fscrypt_allocate_key_member(struct fscrypt_prepared_key *prep_key,
+				const struct fscrypt_info *ci);
+
 int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key,
 			const u8 *raw_key, const struct fscrypt_info *ci);
 
diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c
index ce952dedba77..7b3b96b8a916 100644
--- a/fs/crypto/inline_crypt.c
+++ b/fs/crypto/inline_crypt.c
@@ -157,16 +157,12 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
 	const struct inode *inode = ci->ci_inode;
 	struct super_block *sb = inode->i_sb;
 	enum blk_crypto_mode_num crypto_mode = ci->ci_mode->blk_crypto_mode;
-	struct blk_crypto_key *blk_key;
+	struct blk_crypto_key *blk_key = prep_key->blk_key;
 	struct block_device **devs;
 	unsigned int num_devs;
 	unsigned int i;
 	int err;
 
-	blk_key = kmalloc(sizeof(*blk_key), GFP_KERNEL);
-	if (!blk_key)
-		return -ENOMEM;
-
 	err = blk_crypto_init_key(blk_key, raw_key, crypto_mode,
 				  fscrypt_get_dun_bytes(ci), sb->s_blocksize);
 	if (err) {
@@ -190,8 +186,6 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
 		fscrypt_err(inode, "error %d starting to use blk-crypto", err);
 		goto fail;
 	}
-
-	prep_key->blk_key = blk_key;
 	return 0;
 
 fail:
@@ -199,6 +193,18 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
 	return err;
 }
 
+int fscrypt_allocate_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
+				     const struct fscrypt_info *ci)
+{
+	struct blk_crypto_key *blk_key = kmalloc(sizeof(*blk_key), GFP_KERNEL);
+
+	if (!blk_key)
+		return -ENOMEM;
+
+	prep_key->blk_key = blk_key;
+	return 0;
+}
+
 void fscrypt_destroy_inline_crypt_key(struct super_block *sb,
 				      struct fscrypt_prepared_key *prep_key)
 {
diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c
index 6efac89d49ec..7fc7dc632b3e 100644
--- a/fs/crypto/keysetup.c
+++ b/fs/crypto/keysetup.c
@@ -100,9 +100,9 @@ select_encryption_mode(const union fscrypt_policy *policy,
 	return ERR_PTR(-EINVAL);
 }
 
-/* Create a symmetric cipher object for the given encryption mode and key */
+/* Create a symmetric cipher object for the given encryption mode */
 static struct crypto_skcipher *
-fscrypt_allocate_skcipher(struct fscrypt_mode *mode, const u8 *raw_key,
+fscrypt_allocate_skcipher(struct fscrypt_mode *mode,
 			  const struct inode *inode)
 {
 	struct crypto_skcipher *tfm;
@@ -135,10 +135,6 @@ fscrypt_allocate_skcipher(struct fscrypt_mode *mode, const u8 *raw_key,
 		goto err_free_tfm;
 	}
 	crypto_skcipher_set_flags(tfm, CRYPTO_TFM_REQ_FORBID_WEAK_KEYS);
-	err = crypto_skcipher_setkey(tfm, raw_key, mode->keysize);
-	if (err)
-		goto err_free_tfm;
-
 	return tfm;
 
 err_free_tfm:
@@ -146,11 +142,28 @@ fscrypt_allocate_skcipher(struct fscrypt_mode *mode, const u8 *raw_key,
 	return ERR_PTR(err);
 }
 
+/* Allocate the relevant encryption member for the prepared key */
+int fscrypt_allocate_key_member(struct fscrypt_prepared_key *prep_key,
+				const struct fscrypt_info *ci)
+{
+	struct crypto_skcipher *tfm;
+
+	if (fscrypt_using_inline_encryption(ci))
+		return fscrypt_allocate_inline_crypt_key(prep_key, ci);
+
+	tfm = fscrypt_allocate_skcipher(ci->ci_mode, ci->ci_inode);
+	if (IS_ERR(tfm))
+		return PTR_ERR(tfm);
+	prep_key->tfm = tfm;
+	return 0;
+}
+
 /*
  * 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).
+ * and IV generation method (@ci->ci_policy.flags). The relevant member must
+ * already be allocated and set in @prep_key.
  */
 int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key,
 			const u8 *raw_key, const struct fscrypt_info *ci)
@@ -162,14 +175,10 @@ int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key,
 	if (inlinecrypt) {
 		err = fscrypt_prepare_inline_crypt_key(prep_key, raw_key, ci);
 	} else {
-		struct crypto_skcipher *tfm;
+		err = crypto_skcipher_setkey(prep_key->tfm, raw_key,
+					     ci->ci_mode->keysize);
 
-		tfm = fscrypt_allocate_skcipher(ci->ci_mode, raw_key, ci->ci_inode);
-		if (IS_ERR(tfm))
-			return PTR_ERR(tfm);
-		}
 
-		prep_key->tfm = tfm;
 	}
 
 	/*
@@ -186,7 +195,7 @@ int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key,
 	 */
 	smp_store_release(&prep_key->prepared_members,
 			  prep_key->prepared_members | prepared_member);
-	return 0;
+	return err;
 }
 
 /* Destroy a crypto transform object and/or blk-crypto key. */
@@ -201,11 +210,17 @@ void fscrypt_destroy_prepared_key(struct super_block *sb,
 /* Given a per-file encryption key, set up the file's crypto transform object */
 int fscrypt_set_per_file_enc_key(struct fscrypt_info *ci, const u8 *raw_key)
 {
+	int err;
+
 	ci->ci_enc_key = kzalloc(sizeof(*ci->ci_enc_key), GFP_KERNEL);
 	if (!ci->ci_enc_key)
 		return -ENOMEM;
 
 	ci->ci_enc_key->type = FSCRYPT_KEY_PER_INFO;
+	err = fscrypt_allocate_key_member(ci->ci_enc_key, ci);
+	if (err)
+		return err;
+
 	return fscrypt_prepare_key(ci->ci_enc_key, raw_key, ci);
 }
 
@@ -290,6 +305,10 @@ static int setup_new_mode_prepared_key(struct fscrypt_master_key *mk,
 	if (fscrypt_is_key_prepared(prep_key, ci))
 		goto out_unlock;
 
+	err = fscrypt_allocate_key_member(prep_key, ci);
+	if (err)
+		goto out_unlock;
+
 	BUILD_BUG_ON(sizeof(mode_num) != 1);
 	BUILD_BUG_ON(sizeof(sb->s_uuid) != 16);
 	BUILD_BUG_ON(sizeof(hkdf_info) != 17);
diff --git a/fs/crypto/keysetup_v1.c b/fs/crypto/keysetup_v1.c
index 1e785cedead0..760efa8eeb3a 100644
--- a/fs/crypto/keysetup_v1.c
+++ b/fs/crypto/keysetup_v1.c
@@ -239,6 +239,10 @@ fscrypt_get_direct_key(const struct fscrypt_info *ci, const u8 *raw_key)
 	refcount_set(&dk->dk_refcount, 1);
 	dk->dk_mode = ci->ci_mode;
 	dk->dk_key.type = FSCRYPT_KEY_DIRECT_V1;
+	err = fscrypt_allocate_key_member(&dk->dk_key, ci);
+	if (err)
+		goto err_free_dk;
+
 	err = fscrypt_prepare_key(&dk->dk_key, raw_key, ci);
 	if (err)
 		goto err_free_dk;
-- 
2.40.0


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 00/11] fscrypt: rearrangements preliminary to extent encryption
  2023-04-10 19:39 [PATCH v2 00/11] fscrypt: rearrangements preliminary to extent encryption Sweet Tea Dorminy
                   ` (10 preceding siblings ...)
  2023-04-10 19:40 ` [PATCH v2 11/11] fscrypt: split key alloc and preparation Sweet Tea Dorminy
@ 2023-04-11  3:18 ` Eric Biggers
  11 siblings, 0 replies; 24+ messages in thread
From: Eric Biggers @ 2023-04-11  3:18 UTC (permalink / raw)
  To: Sweet Tea Dorminy
  Cc: Theodore Y. Ts'o, Jaegeuk Kim, linux-fscrypt, kernel-team

Hi Sweet Tea,

On Mon, Apr 10, 2023 at 03:39:53PM -0400, Sweet Tea Dorminy wrote:
> As per [1], extent-based encryption needs to split allocating and
> preparing crypto_skciphers, since extent infos will be loaded at IO time
> and crypto_skciphers cannot be allocated at IO time. 
> 
> This changeset undertakes to split the existing code to clearly
> distinguish preparation and allocation of fscrypt_prepared_keys,
> wrapping crypto_skciphers. Elegance of code is in the eye of the
> beholder, but I've tried a decent variety of arrangements here and this
> seems like the clearest result to me; happy to adjust as desired, and
> more changesets coming soon, this just seemed like the clearest cutoff
> point for preliminaries without being pure refactoring.
> 
> Patchset should apply cleanly to fscrypt/for-next (as per base-commit
> below), and pass ext4/f2fs tests (kvm-xfstests is not currently
> succesfully setting up ubifs volumes for me).
> 
> [1] https://lore.kernel.org/linux-btrfs/Y7NQ1CvPyJiGRe00@sol.localdomain/ 
> 
> Changes from v1:
> Included change 1, erroneously dropped, and generated patches using --base.
> 
> Sweet Tea Dorminy (11):
>   fscrypt: move inline crypt decision to info setup.
>   fscrypt: split and rename setup_file_encryption_key()
>   fscrypt: split and rename setup_per_mode_enc_key()
>   fscrypt: move dirhash key setup away from IO key setup
>   fscrypt: reduce special-casing of IV_INO_LBLK_32
>   fscrypt: make infos have a pointer to prepared keys
>   fscrypt: move all the shared mode key setup deeper
>   fscrypt: make ci->ci_direct_key a bool not a pointer
>   fscrypt: make prepared keys record their type.
>   fscrypt: explicitly track prepared parts of key
>   fscrypt: split key alloc and preparation
> 
>  fs/crypto/crypto.c          |   2 +-
>  fs/crypto/fname.c           |   4 +-
>  fs/crypto/fscrypt_private.h |  73 +++++--
>  fs/crypto/inline_crypt.c    |  30 +--
>  fs/crypto/keysetup.c        | 387 ++++++++++++++++++++++++------------
>  fs/crypto/keysetup_v1.c     |  13 +-
>  6 files changed, 340 insertions(+), 169 deletions(-)
> 

Thanks for the patchset!  I don't see any major issues with these cleanups; I'll
leave some comments on individual patches.  But, I also feel that most of them
aren't too convincing on their own.  So, I'm very interested in seeing where you
go with this.  It seems that your goal is to allow filesystems to more directly
create and manage fscrypt_prepared_key structs.  Do you know when you'll have a
proof of concept ready that includes the changes/additions to the interface
between fs/crypto/ and filesystems (include/linux/fscrypt.h)?

- Eric

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 02/11] fscrypt: split and rename setup_file_encryption_key()
  2023-04-10 19:39 ` [PATCH v2 02/11] fscrypt: split and rename setup_file_encryption_key() Sweet Tea Dorminy
@ 2023-04-11  3:24   ` Eric Biggers
  0 siblings, 0 replies; 24+ messages in thread
From: Eric Biggers @ 2023-04-11  3:24 UTC (permalink / raw)
  To: Sweet Tea Dorminy
  Cc: Theodore Y. Ts'o, Jaegeuk Kim, linux-fscrypt, kernel-team

On Mon, Apr 10, 2023 at 03:39:55PM -0400, Sweet Tea Dorminy wrote:
>  /*
> - * Find the master key, then set up the inode's actual encryption key.
> + * Find and lock the master key.
>   *
>   * If the master key is found in the filesystem-level keyring, then it is
>   * returned in *mk_ret with its semaphore read-locked.  This is needed to ensure
> @@ -434,9 +471,8 @@ static bool fscrypt_valid_master_key_size(const struct fscrypt_master_key *mk,
>   * multiple tasks may race to create an fscrypt_info for the same inode), and to
>   * synchronize the master key being removed with a new inode starting to use it.
>   */
> -static int setup_file_encryption_key(struct fscrypt_info *ci,
> -				     bool need_dirhash_key,
> -				     struct fscrypt_master_key **mk_ret)
> +static int find_and_lock_master_key(const struct fscrypt_info *ci,
> +				    struct fscrypt_master_key **mk_ret)
>  {
>  	struct super_block *sb = ci->ci_inode->i_sb;
>  	struct fscrypt_key_specifier mk_spec;
> @@ -466,17 +502,13 @@ static int setup_file_encryption_key(struct fscrypt_info *ci,
>  			mk = fscrypt_find_master_key(sb, &mk_spec);
>  		}
>  	}
> +
>  	if (unlikely(!mk)) {
>  		if (ci->ci_policy.version != FSCRYPT_POLICY_V1)
>  			return -ENOKEY;
>  
> -		/*
> -		 * As a legacy fallback for v1 policies, search for the key in
> -		 * the current task's subscribed keyrings too.  Don't move this
> -		 * to before the search of ->s_master_keys, since users
> -		 * shouldn't be able to override filesystem-level keys.
> -		 */
> -		return fscrypt_setup_v1_file_key_via_subscribed_keyrings(ci);
> +		*mk_ret = NULL;
> +		return 0;

While this change may be a benefit overall, it does split the code that handles
the legacy case of "v1 policy using process-subscribed keyrings" into two
places.  That makes it a little more difficult to understand.  I think a comment
would be helpful here, at least?

- Eric

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 03/11] fscrypt: split and rename setup_per_mode_enc_key()
  2023-04-10 19:39 ` [PATCH v2 03/11] fscrypt: split and rename setup_per_mode_enc_key() Sweet Tea Dorminy
@ 2023-04-11  3:29   ` Eric Biggers
  0 siblings, 0 replies; 24+ messages in thread
From: Eric Biggers @ 2023-04-11  3:29 UTC (permalink / raw)
  To: Sweet Tea Dorminy
  Cc: Theodore Y. Ts'o, Jaegeuk Kim, linux-fscrypt, kernel-team

On Mon, Apr 10, 2023 at 03:39:56PM -0400, Sweet Tea Dorminy wrote:
> @@ -231,14 +221,39 @@ static int setup_per_mode_enc_key(struct fscrypt_info *ci,
>  	memzero_explicit(mode_key, mode->keysize);
>  	if (err)
>  		goto out_unlock;
> -done_unlock:
> -	ci->ci_enc_key = *prep_key;
> -	err = 0;
> +
>  out_unlock:

The 'if (err)' block above is no longer needed.

> +static int find_mode_prepared_key(struct fscrypt_info *ci,
> +				  struct fscrypt_master_key *mk,
> +				  struct fscrypt_prepared_key *keys,
> +				  u8 hkdf_context, bool include_fs_uuid)
> +{
> +	struct fscrypt_mode *mode = ci->ci_mode;
> +	const u8 mode_num = mode - fscrypt_modes;
> +	struct fscrypt_prepared_key *prep_key;
> +	int err;
> +
> +	if (WARN_ON_ONCE(mode_num > FSCRYPT_MODE_MAX))
> +		return -EINVAL;
> +
> +	prep_key = &keys[mode_num];
> +	if (fscrypt_is_key_prepared(prep_key, ci)) {
> +		ci->ci_enc_key = *prep_key;
> +		return 0;
> +	}
> +	err = setup_new_mode_prepared_key(mk, prep_key, ci, hkdf_context,
> +					  include_fs_uuid);
> +	if (err)
> +		return err;
> +
> +	ci->ci_enc_key = *prep_key;
> +	return 0;
> +}

It actually should be find_or_create_mode_prepared_key(), right?  It's confusing
to have a function that just says "find" actually create the thing it is looking
for.

But, with how long these function names would get, maybe we should just stick
with setup_mode_prepared_key()?  Note that it has the same semantics (find or
create) as fscrypt_setup_ino_hash_key(), which this patchset doesn't change.

- Eric

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 04/11] fscrypt: move dirhash key setup away from IO key setup
  2023-04-10 19:39 ` [PATCH v2 04/11] fscrypt: move dirhash key setup away from IO key setup Sweet Tea Dorminy
@ 2023-04-11  3:35   ` Eric Biggers
  0 siblings, 0 replies; 24+ messages in thread
From: Eric Biggers @ 2023-04-11  3:35 UTC (permalink / raw)
  To: Sweet Tea Dorminy
  Cc: Theodore Y. Ts'o, Jaegeuk Kim, linux-fscrypt, kernel-team

On Mon, Apr 10, 2023 at 03:39:57PM -0400, Sweet Tea Dorminy wrote:
> @@ -391,13 +390,6 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_info *ci,
>  	if (err)
>  		return err;
>  
> -	/* Derive a secret dirhash key for directories that need it. */
> -	if (need_dirhash_key) {
> -		err = fscrypt_derive_dirhash_key(ci, mk);
> -		if (err)
> -			return err;
> -	}
> -
>  	return 0;
>  }

Instead of:

        if (err)
                return err;
        return 0;

Just do:

        return err;

- Eric

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 05/11] fscrypt: reduce special-casing of IV_INO_LBLK_32
  2023-04-10 19:39 ` [PATCH v2 05/11] fscrypt: reduce special-casing of IV_INO_LBLK_32 Sweet Tea Dorminy
@ 2023-04-11  3:38   ` Eric Biggers
  0 siblings, 0 replies; 24+ messages in thread
From: Eric Biggers @ 2023-04-11  3:38 UTC (permalink / raw)
  To: Sweet Tea Dorminy
  Cc: Theodore Y. Ts'o, Jaegeuk Kim, linux-fscrypt, kernel-team

On Mon, Apr 10, 2023 at 03:39:58PM -0400, Sweet Tea Dorminy wrote:
> +static int fscrypt_setup_ino_hash_key(struct fscrypt_master_key *mk)
>  {
>  	int err;
>  
> -	err = find_mode_prepared_key(ci, mk, mk->mk_iv_ino_lblk_32_keys,
> -				     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)) {
>  
> @@ -335,12 +329,6 @@ static int fscrypt_setup_iv_ino_lblk_32_key(struct fscrypt_info *ci,
>  			return err;
>  	}
>  
> -	/*
> -	 * New inodes may not have an inode number assigned yet.
> -	 * Hashing their inode number is delayed until later.
> -	 */
> -	if (ci->ci_inode->i_ino)
> -		fscrypt_hash_inode_number(ci, mk);
>  	return 0;
>  }

Now that this function just does one thing, maybe change it to use an early
return and remove a level of indentation?

        if (smp_load_acquire(&mk->mk_ino_hash_key_initialized))
                return 0;

- Eric

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 06/11] fscrypt: make infos have a pointer to prepared keys
  2023-04-10 19:39 ` [PATCH v2 06/11] fscrypt: make infos have a pointer to prepared keys Sweet Tea Dorminy
@ 2023-04-11  3:44   ` Eric Biggers
  2023-04-11 16:26     ` Sweet Tea Dorminy
  0 siblings, 1 reply; 24+ messages in thread
From: Eric Biggers @ 2023-04-11  3:44 UTC (permalink / raw)
  To: Sweet Tea Dorminy
  Cc: Theodore Y. Ts'o, Jaegeuk Kim, linux-fscrypt, kernel-team

On Mon, Apr 10, 2023 at 03:39:59PM -0400, Sweet Tea Dorminy wrote:
> diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c
> index 8b32200dbbc0..f07e3b9579cf 100644
> --- a/fs/crypto/keysetup.c
> +++ b/fs/crypto/keysetup.c
> @@ -181,7 +181,11 @@ void fscrypt_destroy_prepared_key(struct super_block *sb,
>  int fscrypt_set_per_file_enc_key(struct fscrypt_info *ci, const u8 *raw_key)
>  {
>  	ci->ci_owns_key = true;
> -	return fscrypt_prepare_key(&ci->ci_enc_key, raw_key, ci);
> +	ci->ci_enc_key = kzalloc(sizeof(*ci->ci_enc_key), GFP_KERNEL);
> +	if (!ci->ci_enc_key)
> +		return -ENOMEM;
> +
> +	return fscrypt_prepare_key(ci->ci_enc_key, raw_key, ci);
>  }

Any idea how much this will increase the per-inode memory usage by, in the
per-file keys case?  (Counting the overhead of the slab allocator.)

> -	else if (ci->ci_owns_key)
> +	else if (ci->ci_owns_key) {
>  		fscrypt_destroy_prepared_key(ci->ci_inode->i_sb,
> -					     &ci->ci_enc_key);
> +					     ci->ci_enc_key);
> +		kfree(ci->ci_enc_key);

Use kfree_sensitive() here, please.  Yes, it's not actually needed here because
the allocation doesn't contain the keys themselves.  But I want to code
defensively here.

- Eric

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 07/11] fscrypt: move all the shared mode key setup deeper
  2023-04-10 19:40 ` [PATCH v2 07/11] fscrypt: move all the shared mode key setup deeper Sweet Tea Dorminy
@ 2023-04-11  3:56   ` Eric Biggers
  0 siblings, 0 replies; 24+ messages in thread
From: Eric Biggers @ 2023-04-11  3:56 UTC (permalink / raw)
  To: Sweet Tea Dorminy
  Cc: Theodore Y. Ts'o, Jaegeuk Kim, linux-fscrypt, kernel-team

On Mon, Apr 10, 2023 at 03:40:00PM -0400, Sweet Tea Dorminy wrote:
> +static const u8 FSCRYPT_POLICY_FLAGS_KEY_MASK =
> +	(FSCRYPT_POLICY_FLAG_DIRECT_KEY
> +	 | FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64
> +	 | FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32);

A comment describing the meaning of the above constant would be helpful.

> +static size_t fill_hkdf_info(const struct fscrypt_info *ci, u8 *hkdf_info)

Maybe call this fill_hkdf_info_for_mode_key() to avoid ambiguity with other uses
of HKDF?  Also, maybe add an explicit array size to hkdf_info?  E.g.
hkdf_info[MAX_MODE_KEY_HKDF_INFO_SIZE]

> +static u8 hkdf_context_for_policy(const union fscrypt_policy *policy)
> +{
> +	switch (fscrypt_policy_flags(policy) & FSCRYPT_POLICY_FLAGS_KEY_MASK) {
> +		case FSCRYPT_POLICY_FLAG_DIRECT_KEY:
> +			return HKDF_CONTEXT_DIRECT_KEY;
> +		case FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64:
> +			return HKDF_CONTEXT_IV_INO_LBLK_64_KEY;
> +		case FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32:
> +			return HKDF_CONTEXT_IV_INO_LBLK_32_KEY;
> +		default:
> +			return 0;
> +	}
> +}

There's an extra level of indentation above.

Also, more importantly, since fill_hkdf_info() checks the policy flags anyway,
maybe just handle the HKDF context bytes directly in there?  E.g.:

	if (ci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY) {
		hkdf_info[0] = HKDF_CONTEXT_DIRECT_KEY;
		return 1;
	}
	if (ci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32)
		hkdf_info[0] = HKDF_CONTEXT_IV_INO_LBLK_32_KEY;
	else
		hkdf_info[0] = HKDF_CONTEXT_IV_INO_LBLK_64_KEY;
	memcpy(&hkdf_info[1], &sb->s_uuid, sizeof(sb->s_uuid));
	return 1 + sizeof(sb->s_uuid);

- Eric

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 08/11] fscrypt: make ci->ci_direct_key a bool not a pointer
  2023-04-10 19:40 ` [PATCH v2 08/11] fscrypt: make ci->ci_direct_key a bool not a pointer Sweet Tea Dorminy
@ 2023-04-11  3:57   ` Eric Biggers
  0 siblings, 0 replies; 24+ messages in thread
From: Eric Biggers @ 2023-04-11  3:57 UTC (permalink / raw)
  To: Sweet Tea Dorminy
  Cc: Theodore Y. Ts'o, Jaegeuk Kim, linux-fscrypt, kernel-team

On Mon, Apr 10, 2023 at 03:40:01PM -0400, Sweet Tea Dorminy wrote:
> The ci_direct_key field is only used for v1 direct key policies,
> recording the direct key that needs to have its refcount reduced when
> the crypt_info is freed. However, now that crypt_info->ci_enc_key is a
> pointer to the authoritative prepared key -- embedded in the direct key,
> in this case, we no longer need to keep a full pointer to the direct key
> -- we can use container_of() to go from the prepared key to its
> surrounding direct key. Thus we can make ci_direct_key a bool instead of
> a pointer, saving a few bytes.
> 
> Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
> ---
>  fs/crypto/fscrypt_private.h | 7 +++----
>  fs/crypto/keysetup.c        | 2 +-
>  fs/crypto/keysetup_v1.c     | 7 +++++--
>  3 files changed, 9 insertions(+), 7 deletions(-)
> 
> diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h
> index 5011737b60b3..b575fb58a506 100644
> --- a/fs/crypto/fscrypt_private.h
> +++ b/fs/crypto/fscrypt_private.h
> @@ -234,10 +234,9 @@ struct fscrypt_info {
>  	struct list_head ci_master_key_link;
>  
>  	/*
> -	 * If non-NULL, then encryption is done using the master key directly
> -	 * and ci_enc_key will equal ci_direct_key->dk_key.
> +	 * If true, then encryption is done using the master key directly.
>  	 */
> -	struct fscrypt_direct_key *ci_direct_key;
> +	bool ci_direct_key;

This just gets deleted by the next patch.  Should they be folded together?

- Eric

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 10/11] fscrypt: explicitly track prepared parts of key
  2023-04-10 19:40 ` [PATCH v2 10/11] fscrypt: explicitly track prepared parts of key Sweet Tea Dorminy
@ 2023-04-11  4:05   ` Eric Biggers
  2023-04-11 16:45     ` Sweet Tea Dorminy
  0 siblings, 1 reply; 24+ messages in thread
From: Eric Biggers @ 2023-04-11  4:05 UTC (permalink / raw)
  To: Sweet Tea Dorminy
  Cc: Theodore Y. Ts'o, Jaegeuk Kim, linux-fscrypt, kernel-team

On Mon, Apr 10, 2023 at 03:40:03PM -0400, Sweet Tea Dorminy wrote:
> So far, it has sufficed to allocate and prepare the block key or the TFM
> completely before ever setting the relevant field in the prepared key.
> This is necessary for mode keys -- because multiple inodes could be
> trying to set up the same per-mode prepared key at the same time on
> different threads, we currently must not set the prepared key's tfm or
> block key pointer until that key is completely set up. Otherwise,
> another inode could see the key to be present and attempt to use it
> before it is fully set up.
> 
> But when using pooled prepared keys, we'll have pre-allocated fields,
> and if we separate allocating the fields of a prepared key from
> preparing the fields, that inherently sets the fields before they're
> ready to use. So, either pooled prepared keys must use different
> allocation and setup functions, or we can split allocation and
> preparation for all prepared keys and use some other mechanism to signal
> that the key is fully prepared.
> 
> In order to avoid having similar yet different functions, this function
> adds a new field to the prepared key to explicitly track which parts of
> it are prepared, setting it explicitly. The same acquire/release
> semantics are used to check it in the case of shared mode keys; the cost
> lies in the extra byte per prepared key recording which members are
> fully prepared.
> 
> Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
> ---
>  fs/crypto/fscrypt_private.h | 26 +++++++++++++++-----------
>  fs/crypto/inline_crypt.c    |  8 +-------
>  fs/crypto/keysetup.c        | 36 ++++++++++++++++++++++++++----------
>  3 files changed, 42 insertions(+), 28 deletions(-)

I wonder if this is overcomplicating things and we should simply add a new
rw_semaphore to struct fscrypt_master_key and use it to protect the per-mode key
preparation, instead of trying to keep the fast path lockless?

So the flow (for setting up a file that uses a per-mode key) would look like:

        down_read(&mk->mk_mode_key_prep_sem);
        if key already prepared, unlock and return
        up_read(&mk->mk_mode_key_prep_sem);

        down_write(&mk->mk_mode_key_prep_sem);
        if key already prepared, unlock and return
        prepare the key
        up_write(&mk->mk_mode_key_prep_sem);

Lockless algorithms are nice, but we shouldn't take them too far if they cause
too much trouble...

- Eric

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 06/11] fscrypt: make infos have a pointer to prepared keys
  2023-04-11  3:44   ` Eric Biggers
@ 2023-04-11 16:26     ` Sweet Tea Dorminy
  0 siblings, 0 replies; 24+ messages in thread
From: Sweet Tea Dorminy @ 2023-04-11 16:26 UTC (permalink / raw)
  To: Eric Biggers
  Cc: Theodore Y. Ts'o, Jaegeuk Kim, linux-fscrypt, kernel-team



On 4/10/23 23:44, Eric Biggers wrote:
> On Mon, Apr 10, 2023 at 03:39:59PM -0400, Sweet Tea Dorminy wrote:
>> diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c
>> index 8b32200dbbc0..f07e3b9579cf 100644
>> --- a/fs/crypto/keysetup.c
>> +++ b/fs/crypto/keysetup.c
>> @@ -181,7 +181,11 @@ void fscrypt_destroy_prepared_key(struct super_block *sb,
>>   int fscrypt_set_per_file_enc_key(struct fscrypt_info *ci, const u8 *raw_key)
>>   {
>>   	ci->ci_owns_key = true;
>> -	return fscrypt_prepare_key(&ci->ci_enc_key, raw_key, ci);
>> +	ci->ci_enc_key = kzalloc(sizeof(*ci->ci_enc_key), GFP_KERNEL);
>> +	if (!ci->ci_enc_key)
>> +		return -ENOMEM;
>> +
>> +	return fscrypt_prepare_key(ci->ci_enc_key, raw_key, ci);
>>   }
> 
> Any idea how much this will increase the per-inode memory usage by, in the
> per-file keys case?  (Counting the overhead of the slab allocator.)

For just this change, the object gets allocated from the 8 or 16 byte 
slab cache, so it's just the 8 bytes of pointer, plus the slab cache 
overhead. The slab allocator, as far as I understand it, has 64-byte 
'struct slab's, and for 8/16/32 byte allocations, those come out of page 
size slabs; so that's amortized over 512/256/128 objects. So that's 8 
1/8th or 8 1/4th extra bytes from this change, I think.

Later changes bump the object up to the next size cache, 16 or 32, 
wasting 6 or 14 more bytes. I should probably add something imitating 
struct bio's bi_inline_vecs, adding a ci_inline_prepkey[] member for 
allocation only by per-file key infos.


> 
>> -	else if (ci->ci_owns_key)
>> +	else if (ci->ci_owns_key) {
>>   		fscrypt_destroy_prepared_key(ci->ci_inode->i_sb,
>> -					     &ci->ci_enc_key);
>> +					     ci->ci_enc_key);
>> +		kfree(ci->ci_enc_key);
> 
> Use kfree_sensitive() here, please.  Yes, it's not actually needed here because
> the allocation doesn't contain the keys themselves.  But I want to code
> defensively here.

Ah, I argued with myself on whether I needed it, happy to do so.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 10/11] fscrypt: explicitly track prepared parts of key
  2023-04-11  4:05   ` Eric Biggers
@ 2023-04-11 16:45     ` Sweet Tea Dorminy
  2023-04-11 21:21       ` Eric Biggers
  0 siblings, 1 reply; 24+ messages in thread
From: Sweet Tea Dorminy @ 2023-04-11 16:45 UTC (permalink / raw)
  To: Eric Biggers
  Cc: Theodore Y. Ts'o, Jaegeuk Kim, linux-fscrypt, kernel-team



On 4/11/23 00:05, Eric Biggers wrote:
> On Mon, Apr 10, 2023 at 03:40:03PM -0400, Sweet Tea Dorminy wrote:
>> So far, it has sufficed to allocate and prepare the block key or the TFM
>> completely before ever setting the relevant field in the prepared key.
>> This is necessary for mode keys -- because multiple inodes could be
>> trying to set up the same per-mode prepared key at the same time on
>> different threads, we currently must not set the prepared key's tfm or
>> block key pointer until that key is completely set up. Otherwise,
>> another inode could see the key to be present and attempt to use it
>> before it is fully set up.
>>
>> But when using pooled prepared keys, we'll have pre-allocated fields,
>> and if we separate allocating the fields of a prepared key from
>> preparing the fields, that inherently sets the fields before they're
>> ready to use. So, either pooled prepared keys must use different
>> allocation and setup functions, or we can split allocation and
>> preparation for all prepared keys and use some other mechanism to signal
>> that the key is fully prepared.
>>
>> In order to avoid having similar yet different functions, this function
>> adds a new field to the prepared key to explicitly track which parts of
>> it are prepared, setting it explicitly. The same acquire/release
>> semantics are used to check it in the case of shared mode keys; the cost
>> lies in the extra byte per prepared key recording which members are
>> fully prepared.
>>
>> Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
>> ---
>>   fs/crypto/fscrypt_private.h | 26 +++++++++++++++-----------
>>   fs/crypto/inline_crypt.c    |  8 +-------
>>   fs/crypto/keysetup.c        | 36 ++++++++++++++++++++++++++----------
>>   3 files changed, 42 insertions(+), 28 deletions(-)
> 
> I wonder if this is overcomplicating things and we should simply add a new
> rw_semaphore to struct fscrypt_master_key and use it to protect the per-mode key
> preparation, instead of trying to keep the fast path lockless?
> 
> So the flow (for setting up a file that uses a per-mode key) would look like:
> 
>          down_read(&mk->mk_mode_key_prep_sem);
>          if key already prepared, unlock and return
>          up_read(&mk->mk_mode_key_prep_sem);
> 
>          down_write(&mk->mk_mode_key_prep_sem);
>          if key already prepared, unlock and return
>          prepare the key
>          up_write(&mk->mk_mode_key_prep_sem);
> 
> Lockless algorithms are nice, but we shouldn't take them too far if they cause
> too much trouble...

You're noting that we only really need preparedness for per-mode keys, 
and that's a point I didn't explicitly grasp before; everywhere else we 
know whether it's merely allocated or fully prepared. Two other thoughts 
on ways we could pull the preparedness out of fscrypt_prepared_key and 
still keep locklessness:

fscrypt_master_key could setup both block key and tfm (if block key is 
applicable) when it sets up a prepared key, so we can use just one bit 
of preparedness information, and keep a bitmap recording which prepared 
keys are ready in fscrypt_master_key.

Or we could have
struct fscrypt_master_key_prepared_key {
	u8 preparedness;
	struct fscrypt_prepared_key prep_key;
}
and similarly isolate the preparedness tracking from per-file keys.

Do either of those sound appealing as alternatives to the semaphore?

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 10/11] fscrypt: explicitly track prepared parts of key
  2023-04-11 16:45     ` Sweet Tea Dorminy
@ 2023-04-11 21:21       ` Eric Biggers
  0 siblings, 0 replies; 24+ messages in thread
From: Eric Biggers @ 2023-04-11 21:21 UTC (permalink / raw)
  To: Sweet Tea Dorminy
  Cc: Theodore Y. Ts'o, Jaegeuk Kim, linux-fscrypt, kernel-team

On Tue, Apr 11, 2023 at 12:45:28PM -0400, Sweet Tea Dorminy wrote:
> You're noting that we only really need preparedness for per-mode keys, and
> that's a point I didn't explicitly grasp before; everywhere else we know
> whether it's merely allocated or fully prepared. Two other thoughts on ways
> we could pull the preparedness out of fscrypt_prepared_key and still keep
> locklessness:
> 
> fscrypt_master_key could setup both block key and tfm (if block key is
> applicable) when it sets up a prepared key, so we can use just one bit of
> preparedness information, and keep a bitmap recording which prepared keys
> are ready in fscrypt_master_key.
> 
> Or we could have
> struct fscrypt_master_key_prepared_key {
> 	u8 preparedness;
> 	struct fscrypt_prepared_key prep_key;
> }
> and similarly isolate the preparedness tracking from per-file keys.
> 
> Do either of those sound appealing as alternatives to the semaphore?

Not really.  The bitmaps add extra complexity.  Also note that the tfm and
blk-crypto key do need to be considered separately, as there can be cases where
blk-crypto supports the algorithm but the crypto API doesn't, and vice versa.

I think that for the per-mode keys, we should either keep the current behavior
where prep_key->tfm and prep_key->blk_key aren't set until they're fully ready
(in which case the lockless check continues to be fairly straightforward), *or*
we should make it no longer lockless.

- Eric

^ permalink raw reply	[flat|nested] 24+ messages in thread

end of thread, other threads:[~2023-04-11 21:21 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-04-10 19:39 [PATCH v2 00/11] fscrypt: rearrangements preliminary to extent encryption Sweet Tea Dorminy
2023-04-10 19:39 ` [PATCH v2 01/11] fscrypt: move inline crypt decision to info setup Sweet Tea Dorminy
2023-04-10 19:39 ` [PATCH v2 02/11] fscrypt: split and rename setup_file_encryption_key() Sweet Tea Dorminy
2023-04-11  3:24   ` Eric Biggers
2023-04-10 19:39 ` [PATCH v2 03/11] fscrypt: split and rename setup_per_mode_enc_key() Sweet Tea Dorminy
2023-04-11  3:29   ` Eric Biggers
2023-04-10 19:39 ` [PATCH v2 04/11] fscrypt: move dirhash key setup away from IO key setup Sweet Tea Dorminy
2023-04-11  3:35   ` Eric Biggers
2023-04-10 19:39 ` [PATCH v2 05/11] fscrypt: reduce special-casing of IV_INO_LBLK_32 Sweet Tea Dorminy
2023-04-11  3:38   ` Eric Biggers
2023-04-10 19:39 ` [PATCH v2 06/11] fscrypt: make infos have a pointer to prepared keys Sweet Tea Dorminy
2023-04-11  3:44   ` Eric Biggers
2023-04-11 16:26     ` Sweet Tea Dorminy
2023-04-10 19:40 ` [PATCH v2 07/11] fscrypt: move all the shared mode key setup deeper Sweet Tea Dorminy
2023-04-11  3:56   ` Eric Biggers
2023-04-10 19:40 ` [PATCH v2 08/11] fscrypt: make ci->ci_direct_key a bool not a pointer Sweet Tea Dorminy
2023-04-11  3:57   ` Eric Biggers
2023-04-10 19:40 ` [PATCH v2 09/11] fscrypt: make prepared keys record their type Sweet Tea Dorminy
2023-04-10 19:40 ` [PATCH v2 10/11] fscrypt: explicitly track prepared parts of key Sweet Tea Dorminy
2023-04-11  4:05   ` Eric Biggers
2023-04-11 16:45     ` Sweet Tea Dorminy
2023-04-11 21:21       ` Eric Biggers
2023-04-10 19:40 ` [PATCH v2 11/11] fscrypt: split key alloc and preparation Sweet Tea Dorminy
2023-04-11  3:18 ` [PATCH v2 00/11] fscrypt: rearrangements preliminary to extent encryption Eric Biggers

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).