1 ext4 crypto: revalidate dentry after adding or removing the key
3 Add a validation check for dentries for encrypted directory to make
4 sure we're not caching stale data after a key has been added or removed.
6 Also check to make sure that status of the encryption key is updated
7 when readdir(2) is executed.
9 Signed-off-by: Theodore Ts'o <tytso@mit.edu>
12 fs/ext4/crypto.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
13 fs/ext4/dir.c | 6 ++++++
15 fs/ext4/namei.c | 18 ++++++++++++++++++
16 4 files changed, 69 insertions(+)
18 diff --git a/fs/ext4/crypto.c b/fs/ext4/crypto.c
19 index c802120..ec508af 100644
20 --- a/fs/ext4/crypto.c
21 +++ b/fs/ext4/crypto.c
22 @@ -467,3 +467,47 @@ uint32_t ext4_validate_encryption_key_size(uint32_t mode, uint32_t size)
28 + * Validate dentries for encrypted directories to make sure we aren't
29 + * potentially caching stale data after a key has been added or
32 +static int ext4_d_revalidate(struct dentry *dentry, unsigned int flags)
34 + struct inode *dir = d_inode(dentry->d_parent);
35 + struct ext4_crypt_info *ci = EXT4_I(dir)->i_crypt_info;
36 + int inode_has_key, encrypted_filename;
38 + if (!ext4_encrypted_inode(dir))
41 + if (ci && ci->ci_keyring_key &&
42 + (ci->ci_keyring_key->flags & ((1 << KEY_FLAG_INVALIDATED) |
43 + (1 << KEY_FLAG_REVOKED) |
44 + (1 << KEY_FLAG_DEAD))))
47 + /* this should eventually be an flag in d_flags */
48 + encrypted_filename = dentry->d_fsdata != NULL;
49 + inode_has_key = (ci != NULL);
52 + * If this is an encrypted file name, and it is a negative
53 + * dentry, it might be a valid name. We can't check if the
54 + * key has since been made available due to locking reasons,
55 + * so we fail the validation so ext4_lookup() can do this
58 + * We also fail the validation if the dentry is for a
59 + * decrypted filename, but we no longer have the key.
61 + if ((encrypted_filename && d_is_negative(dentry)) ||
62 + (!encrypted_filename && !inode_has_key))
67 +const struct dentry_operations ext4_encrypted_d_ops = {
68 + .d_revalidate = ext4_d_revalidate,
70 diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c
71 index 1d1bca7..6d17f31 100644
74 @@ -111,6 +111,12 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
75 int dir_has_error = 0;
76 struct ext4_str fname_crypto_str = {.name = NULL, .len = 0};
78 + if (ext4_encrypted_inode(inode)) {
79 + err = ext4_get_encryption_info(inode);
80 + if (err && err != -ENOKEY)
84 if (is_dx_dir(inode)) {
85 err = ext4_dx_readdir(file, ctx);
86 if (err != ERR_BAD_DX_DIR) {
87 diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
88 index 0662b28..157b458a 100644
91 @@ -2302,6 +2302,7 @@ struct page *ext4_encrypt(struct inode *inode,
92 int ext4_decrypt(struct page *page);
93 int ext4_encrypted_zeroout(struct inode *inode, ext4_lblk_t lblk,
94 ext4_fsblk_t pblk, ext4_lblk_t len);
95 +extern const struct dentry_operations ext4_encrypted_d_ops;
97 #ifdef CONFIG_EXT4_FS_ENCRYPTION
98 int ext4_init_crypto(void);
99 diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
100 index 06574dd..5de8483 100644
101 --- a/fs/ext4/namei.c
102 +++ b/fs/ext4/namei.c
103 @@ -1558,6 +1558,24 @@ static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, unsi
104 struct ext4_dir_entry_2 *de;
105 struct buffer_head *bh;
107 + if (ext4_encrypted_inode(dir)) {
108 + int res = ext4_get_encryption_info(dir);
111 + * This should be a properly defined flag for
112 + * dentry->d_flags when we uplift this to the VFS.
113 + * d_fsdata is set to (void *) 1 if if the dentry is
114 + * created while the directory was encrypted and we
115 + * don't have access to the key.
117 + dentry->d_fsdata = NULL;
118 + if (ext4_encryption_info(dir))
119 + dentry->d_fsdata = (void *) 1;
120 + d_set_d_op(dentry, &ext4_encrypted_d_ops);
121 + if (res && res != -ENOKEY)
122 + return ERR_PTR(res);
125 if (dentry->d_name.len > EXT4_NAME_LEN)
126 return ERR_PTR(-ENAMETOOLONG);