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 ed318a6cc0 ("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>
This commit is contained in:
Eric Biggers
2020-09-16 21:11:35 -07:00
parent 31114726b6
commit ac4acb1f4b
10 changed files with 132 additions and 140 deletions

View File

@@ -21,7 +21,7 @@
#define FS_CRYPTO_BLOCK_SIZE 16
union fscrypt_context;
union fscrypt_policy;
struct fscrypt_info;
struct seq_file;
@@ -62,8 +62,7 @@ struct fscrypt_operations {
int (*get_context)(struct inode *inode, void *ctx, size_t len);
int (*set_context)(struct inode *inode, const void *ctx, size_t len,
void *fs_data);
const union fscrypt_context *(*get_dummy_context)(
struct super_block *sb);
const union fscrypt_policy *(*get_dummy_policy)(struct super_block *sb);
bool (*empty_dir)(struct inode *inode);
unsigned int max_namelen;
bool (*has_stable_inodes)(struct super_block *sb);
@@ -101,14 +100,6 @@ static inline bool fscrypt_needs_contents_encryption(const struct inode *inode)
return IS_ENCRYPTED(inode) && S_ISREG(inode->i_mode);
}
static inline const union fscrypt_context *
fscrypt_get_dummy_context(struct super_block *sb)
{
if (!sb->s_cop->get_dummy_context)
return NULL;
return sb->s_cop->get_dummy_context(sb);
}
/*
* When d_splice_alias() moves a directory's encrypted alias to its decrypted
* alias as a result of the encryption key being added, DCACHE_ENCRYPTED_NAME
@@ -158,20 +149,21 @@ int fscrypt_ioctl_get_nonce(struct file *filp, void __user *arg);
int fscrypt_has_permitted_context(struct inode *parent, struct inode *child);
int fscrypt_set_context(struct inode *inode, void *fs_data);
struct fscrypt_dummy_context {
const union fscrypt_context *ctx;
struct fscrypt_dummy_policy {
const union fscrypt_policy *policy;
};
int fscrypt_set_test_dummy_encryption(struct super_block *sb,
const substring_t *arg,
struct fscrypt_dummy_context *dummy_ctx);
int fscrypt_set_test_dummy_encryption(
struct super_block *sb,
const substring_t *arg,
struct fscrypt_dummy_policy *dummy_policy);
void fscrypt_show_test_dummy_encryption(struct seq_file *seq, char sep,
struct super_block *sb);
static inline void
fscrypt_free_dummy_context(struct fscrypt_dummy_context *dummy_ctx)
fscrypt_free_dummy_policy(struct fscrypt_dummy_policy *dummy_policy)
{
kfree(dummy_ctx->ctx);
dummy_ctx->ctx = NULL;
kfree(dummy_policy->policy);
dummy_policy->policy = NULL;
}
/* keyring.c */
@@ -250,12 +242,6 @@ static inline bool fscrypt_needs_contents_encryption(const struct inode *inode)
return false;
}
static inline const union fscrypt_context *
fscrypt_get_dummy_context(struct super_block *sb)
{
return NULL;
}
static inline void fscrypt_handle_d_move(struct dentry *dentry)
{
}
@@ -346,7 +332,7 @@ static inline int fscrypt_set_context(struct inode *inode, void *fs_data)
return -EOPNOTSUPP;
}
struct fscrypt_dummy_context {
struct fscrypt_dummy_policy {
};
static inline void fscrypt_show_test_dummy_encryption(struct seq_file *seq,
@@ -356,7 +342,7 @@ static inline void fscrypt_show_test_dummy_encryption(struct seq_file *seq,
}
static inline void
fscrypt_free_dummy_context(struct fscrypt_dummy_context *dummy_ctx)
fscrypt_free_dummy_policy(struct fscrypt_dummy_policy *dummy_policy)
{
}