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 | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
13 fs/ext4/dir.c | 6 ++++++
15 fs/ext4/namei.c | 18 ++++++++++++++++++
16 4 files changed, 81 insertions(+)
18 diff --git a/fs/ext4/crypto.c b/fs/ext4/crypto.c
19 index c802120..38f7562 100644
20 --- a/fs/ext4/crypto.c
21 +++ b/fs/ext4/crypto.c
22 @@ -467,3 +467,59 @@ 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 dir_has_key, cached_with_key;
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 + cached_with_key = dentry->d_fsdata != NULL;
49 + dir_has_key = (ci != NULL);
52 + * If the dentry was cached without the key, and it is a
53 + * negative dentry, it might be a valid name. We can't check
54 + * if the key has since been made available due to locking
55 + * reasons, so we fail the validation so ext4_lookup() can do
58 + * We also fail the validation if the dentry was created with
59 + * the key present, but we no longer have the key, or vice versa.
61 + if ((!cached_with_key && d_is_negative(dentry)) ||
62 + (!cached_with_key && dir_has_key) ||
63 + (cached_with_key && !dir_has_key)) {
64 +#if 0 /* Revalidation debug */
66 + char *cp = simple_dname(dentry, buf, sizeof(buf));
69 + cp = (char *) "???";
70 + pr_err("revalidate: %s %p %d %d %d\n", cp, dentry->d_fsdata,
71 + cached_with_key, d_is_negative(dentry),
79 +const struct dentry_operations ext4_encrypted_d_ops = {
80 + .d_revalidate = ext4_d_revalidate,
82 diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c
83 index 1d1bca7..6d17f31 100644
86 @@ -111,6 +111,12 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
87 int dir_has_error = 0;
88 struct ext4_str fname_crypto_str = {.name = NULL, .len = 0};
90 + if (ext4_encrypted_inode(inode)) {
91 + err = ext4_get_encryption_info(inode);
92 + if (err && err != -ENOKEY)
96 if (is_dx_dir(inode)) {
97 err = ext4_dx_readdir(file, ctx);
98 if (err != ERR_BAD_DX_DIR) {
99 diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
100 index 0662b28..157b458a 100644
103 @@ -2302,6 +2302,7 @@ struct page *ext4_encrypt(struct inode *inode,
104 int ext4_decrypt(struct page *page);
105 int ext4_encrypted_zeroout(struct inode *inode, ext4_lblk_t lblk,
106 ext4_fsblk_t pblk, ext4_lblk_t len);
107 +extern const struct dentry_operations ext4_encrypted_d_ops;
109 #ifdef CONFIG_EXT4_FS_ENCRYPTION
110 int ext4_init_crypto(void);
111 diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
112 index 06574dd..5de8483 100644
113 --- a/fs/ext4/namei.c
114 +++ b/fs/ext4/namei.c
115 @@ -1558,6 +1558,24 @@ static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, unsi
116 struct ext4_dir_entry_2 *de;
117 struct buffer_head *bh;
119 + if (ext4_encrypted_inode(dir)) {
120 + int res = ext4_get_encryption_info(dir);
123 + * This should be a properly defined flag for
124 + * dentry->d_flags when we uplift this to the VFS.
125 + * d_fsdata is set to (void *) 1 if if the dentry is
126 + * created while the directory was encrypted and we
127 + * don't have access to the key.
129 + dentry->d_fsdata = NULL;
130 + if (ext4_encryption_info(dir))
131 + dentry->d_fsdata = (void *) 1;
132 + d_set_d_op(dentry, &ext4_encrypted_d_ops);
133 + if (res && res != -ENOKEY)
134 + return ERR_PTR(res);
137 if (dentry->d_name.len > EXT4_NAME_LEN)
138 return ERR_PTR(-ENAMETOOLONG);