add patch remove-unused-parameter-newblock
[ext4-patch-queue.git] / crypto-fix-validate-when-key-add-remove
blob81be3ae867699c5d9bf45c24d6ec01e97a515a4a
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>
11 ---
12  fs/ext4/crypto.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
13  fs/ext4/dir.c    |  6 ++++++
14  fs/ext4/ext4.h   |  1 +
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)
23                 return size;
24         return 0;
25  }
27 +/*
28 + * Validate dentries for encrypted directories to make sure we aren't
29 + * potentially caching stale data after a key has been added or
30 + * removed.
31 + */
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))
39 +               return 0;
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))))
45 +               ci = NULL;
47 +       /* this should eventually be an flag in d_flags */
48 +       encrypted_filename = dentry->d_fsdata != NULL;
49 +       inode_has_key = (ci != NULL);
51 +       /*
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
56 +        * check.
57 +        *
58 +        * We also fail the validation if the dentry is for a
59 +        * decrypted filename, but we no longer have the key.
60 +        */
61 +       if ((encrypted_filename && d_is_negative(dentry)) ||
62 +           (!encrypted_filename && !inode_has_key))
63 +               return 0;
64 +       return 1;
67 +const struct dentry_operations ext4_encrypted_d_ops = {
68 +       .d_revalidate = ext4_d_revalidate,
69 +};
70 diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c
71 index 1d1bca7..6d17f31 100644
72 --- a/fs/ext4/dir.c
73 +++ b/fs/ext4/dir.c
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)
81 +                       return err;
82 +       }
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
89 --- a/fs/ext4/ext4.h
90 +++ b/fs/ext4/ext4.h
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);
110 +               /*
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.
116 +                */
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);
123 +       }
125         if (dentry->d_name.len > EXT4_NAME_LEN)
126                 return ERR_PTR(-ENAMETOOLONG);