summary refs log tree commit diff
path: root/fs/crypto/keysetup.c
diff options
context:
space:
mode:
authorEric Biggers <ebiggers@google.com>2020-09-16 21:11:35 -0700
committerEric Biggers <ebiggers@google.com>2020-09-22 06:48:49 -0700
commitac4acb1f4b2b6b7e8d913537cccec8789903e164 (patch)
treee6999a5cae6cb884d844450d19b59da6049186e5 /fs/crypto/keysetup.c
parent31114726b69364f3bf8dd945c600ceed4c430f4f (diff)
downloadlinux-ac4acb1f4b2b6b7e8d913537cccec8789903e164.tar.gz
fscrypt: handle test_dummy_encryption in more logical way
The behavior of the test_dummy_encryption mount option is that when a
new file (or directory or symlink) is created in an unencrypted
directory, it's automatically encrypted using a dummy encryption policy.
That's it; in particular, the encryption (or lack thereof) of existing
files (or directories or symlinks) doesn't change.

Unfortunately the implementation of test_dummy_encryption is a bit weird
and confusing.  When test_dummy_encryption is enabled and a file is
being created in an unencrypted directory, we set up an encryption key
(->i_crypt_info) for the directory.  This isn't actually used to do any
encryption, however, since the directory is still unencrypted!  Instead,
->i_crypt_info is only used for inheriting the encryption policy.

One consequence of this is that the filesystem ends up providing a
"dummy context" (policy + nonce) instead of a "dummy policy".  In
commit ed318a6cc0b6 ("fscrypt: support test_dummy_encryption=v2"), I
mistakenly thought this was required.  However, actually the nonce only
ends up being used to derive a key that is never used.

Another consequence of this implementation is that it allows for
'inode->i_crypt_info != NULL && !IS_ENCRYPTED(inode)', which is an edge
case that can be forgotten about.  For example, currently
FS_IOC_GET_ENCRYPTION_POLICY on an unencrypted directory may return the
dummy encryption policy when the filesystem is mounted with
test_dummy_encryption.  That seems like the wrong thing to do, since
again, the directory itself is not actually encrypted.

Therefore, switch to a more logical and maintainable implementation
where the dummy encryption policy inheritance is done without setting up
keys for unencrypted directories.  This involves:

- Adding a function fscrypt_policy_to_inherit() which returns the
  encryption policy to inherit from a directory.  This can be a real
  policy, a dummy policy, or no policy.

- Replacing struct fscrypt_dummy_context, ->get_dummy_context(), etc.
  with struct fscrypt_dummy_policy, ->get_dummy_policy(), etc.

- Making fscrypt_fname_encrypted_size() take an fscrypt_policy instead
  of an inode.

Acked-by: Jaegeuk Kim <jaegeuk@kernel.org>
Acked-by: Jeff Layton <jlayton@kernel.org>
Link: https://lore.kernel.org/r/20200917041136.178600-13-ebiggers@kernel.org
Signed-off-by: Eric Biggers <ebiggers@google.com>
Diffstat (limited to 'fs/crypto/keysetup.c')
-rw-r--r--fs/crypto/keysetup.c33
1 files changed, 9 insertions, 24 deletions
diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c
index 47f19061ba10..d3c3e5d9b41f 100644
--- a/fs/crypto/keysetup.c
+++ b/fs/crypto/keysetup.c
@@ -547,7 +547,7 @@ out:
 
 /**
  * fscrypt_get_encryption_info() - set up an inode's encryption key
- * @inode: the inode to set up the key for
+ * @inode: the inode to set up the key for.  Must be encrypted.
  *
  * Set up ->i_crypt_info, if it hasn't already been done.
  *
@@ -569,18 +569,8 @@ int fscrypt_get_encryption_info(struct inode *inode)
 
 	res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx));
 	if (res < 0) {
-		const union fscrypt_context *dummy_ctx =
-			fscrypt_get_dummy_context(inode->i_sb);
-
-		if (IS_ENCRYPTED(inode) || !dummy_ctx) {
-			fscrypt_warn(inode,
-				     "Error %d getting encryption context",
-				     res);
-			return res;
-		}
-		/* Fake up a context for an unencrypted directory */
-		res = fscrypt_context_size(dummy_ctx);
-		memcpy(&ctx, dummy_ctx, res);
+		fscrypt_warn(inode, "Error %d getting encryption context", res);
+		return res;
 	}
 
 	res = fscrypt_policy_from_context(&policy, &ctx, res);
@@ -627,17 +617,14 @@ EXPORT_SYMBOL(fscrypt_get_encryption_info);
 int fscrypt_prepare_new_inode(struct inode *dir, struct inode *inode,
 			      bool *encrypt_ret)
 {
-	int err;
+	const union fscrypt_policy *policy;
 	u8 nonce[FSCRYPT_FILE_NONCE_SIZE];
 
-	if (!IS_ENCRYPTED(dir) && fscrypt_get_dummy_context(dir->i_sb) == NULL)
+	policy = fscrypt_policy_to_inherit(dir);
+	if (policy == NULL)
 		return 0;
-
-	err = fscrypt_get_encryption_info(dir);
-	if (err)
-		return err;
-	if (!fscrypt_has_encryption_key(dir))
-		return -ENOKEY;
+	if (IS_ERR(policy))
+		return PTR_ERR(policy);
 
 	if (WARN_ON_ONCE(inode->i_mode == 0))
 		return -EINVAL;
@@ -654,9 +641,7 @@ int fscrypt_prepare_new_inode(struct inode *dir, struct inode *inode,
 	*encrypt_ret = true;
 
 	get_random_bytes(nonce, FSCRYPT_FILE_NONCE_SIZE);
-	return fscrypt_setup_encryption_info(inode,
-					     &dir->i_crypt_info->ci_policy,
-					     nonce,
+	return fscrypt_setup_encryption_info(inode, policy, nonce,
 					     IS_CASEFOLDED(dir) &&
 					     S_ISDIR(inode->i_mode));
 }