Loading fs/ext4/crypto.c +56 −0 Original line number Diff line number Diff line Loading @@ -469,3 +469,59 @@ uint32_t ext4_validate_encryption_key_size(uint32_t mode, uint32_t size) return size; return 0; } /* * Validate dentries for encrypted directories to make sure we aren't * potentially caching stale data after a key has been added or * removed. */ static int ext4_d_revalidate(struct dentry *dentry, unsigned int flags) { struct inode *dir = d_inode(dentry->d_parent); struct ext4_crypt_info *ci = EXT4_I(dir)->i_crypt_info; int dir_has_key, cached_with_key; if (!ext4_encrypted_inode(dir)) return 0; if (ci && ci->ci_keyring_key && (ci->ci_keyring_key->flags & ((1 << KEY_FLAG_INVALIDATED) | (1 << KEY_FLAG_REVOKED) | (1 << KEY_FLAG_DEAD)))) ci = NULL; /* this should eventually be an flag in d_flags */ cached_with_key = dentry->d_fsdata != NULL; dir_has_key = (ci != NULL); /* * If the dentry was cached without the key, and it is a * negative dentry, it might be a valid name. We can't check * if the key has since been made available due to locking * reasons, so we fail the validation so ext4_lookup() can do * this check. * * We also fail the validation if the dentry was created with * the key present, but we no longer have the key, or vice versa. */ if ((!cached_with_key && d_is_negative(dentry)) || (!cached_with_key && dir_has_key) || (cached_with_key && !dir_has_key)) { #if 0 /* Revalidation debug */ char buf[80]; char *cp = simple_dname(dentry, buf, sizeof(buf)); if (IS_ERR(cp)) cp = (char *) "???"; pr_err("revalidate: %s %p %d %d %d\n", cp, dentry->d_fsdata, cached_with_key, d_is_negative(dentry), dir_has_key); #endif return 0; } return 1; } const struct dentry_operations ext4_encrypted_d_ops = { .d_revalidate = ext4_d_revalidate, }; fs/ext4/dir.c +11 −2 Original line number Diff line number Diff line Loading @@ -111,6 +111,12 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx) int dir_has_error = 0; struct ext4_str fname_crypto_str = {.name = NULL, .len = 0}; if (ext4_encrypted_inode(inode)) { err = ext4_get_encryption_info(inode); if (err && err != -ENOKEY) return err; } if (is_dx_dir(inode)) { err = ext4_dx_readdir(file, ctx); if (err != ERR_BAD_DX_DIR) { Loading Loading @@ -157,8 +163,11 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx) index, 1); file->f_ra.prev_pos = (loff_t)index << PAGE_CACHE_SHIFT; bh = ext4_bread(NULL, inode, map.m_lblk, 0); if (IS_ERR(bh)) return PTR_ERR(bh); if (IS_ERR(bh)) { err = PTR_ERR(bh); bh = NULL; goto errout; } } if (!bh) { Loading fs/ext4/ext4.h +1 −0 Original line number Diff line number Diff line Loading @@ -2258,6 +2258,7 @@ struct page *ext4_encrypt(struct inode *inode, struct page *plaintext_page); int ext4_decrypt(struct page *page); int ext4_encrypted_zeroout(struct inode *inode, struct ext4_extent *ex); extern const struct dentry_operations ext4_encrypted_d_ops; #ifdef CONFIG_EXT4_FS_ENCRYPTION int ext4_init_crypto(void); Loading fs/ext4/namei.c +18 −0 Original line number Diff line number Diff line Loading @@ -1558,6 +1558,24 @@ static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, unsi struct ext4_dir_entry_2 *de; struct buffer_head *bh; if (ext4_encrypted_inode(dir)) { int res = ext4_get_encryption_info(dir); /* * This should be a properly defined flag for * dentry->d_flags when we uplift this to the VFS. * d_fsdata is set to (void *) 1 if if the dentry is * created while the directory was encrypted and we * don't have access to the key. */ dentry->d_fsdata = NULL; if (ext4_encryption_info(dir)) dentry->d_fsdata = (void *) 1; d_set_d_op(dentry, &ext4_encrypted_d_ops); if (res && res != -ENOKEY) return ERR_PTR(res); } if (dentry->d_name.len > EXT4_NAME_LEN) return ERR_PTR(-ENAMETOOLONG); Loading fs/ext4/xattr.c +28 −4 Original line number Diff line number Diff line Loading @@ -232,6 +232,27 @@ ext4_xattr_check_block(struct inode *inode, struct buffer_head *bh) return error; } static int __xattr_check_inode(struct inode *inode, struct ext4_xattr_ibody_header *header, void *end, const char *function, unsigned int line) { struct ext4_xattr_entry *entry = IFIRST(header); int error = -EFSCORRUPTED; if (((void *) header >= end) || (header->h_magic != le32_to_cpu(EXT4_XATTR_MAGIC))) goto errout; error = ext4_xattr_check_names(entry, end, entry); errout: if (error) __ext4_error_inode(inode, function, line, 0, "corrupted in-inode xattr"); return error; } #define xattr_check_inode(inode, header, end) \ __xattr_check_inode((inode), (header), (end), __func__, __LINE__) static inline int ext4_xattr_check_entry(struct ext4_xattr_entry *entry, size_t size) { Loading Loading @@ -343,7 +364,7 @@ ext4_xattr_ibody_get(struct inode *inode, int name_index, const char *name, header = IHDR(inode, raw_inode); entry = IFIRST(header); end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size; error = ext4_xattr_check_names(entry, end, entry); error = xattr_check_inode(inode, header, end); if (error) goto cleanup; error = ext4_xattr_find_entry(&entry, name_index, name, Loading Loading @@ -474,7 +495,7 @@ ext4_xattr_ibody_list(struct dentry *dentry, char *buffer, size_t buffer_size) raw_inode = ext4_raw_inode(&iloc); header = IHDR(inode, raw_inode); end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size; error = ext4_xattr_check_names(IFIRST(header), end, IFIRST(header)); error = xattr_check_inode(inode, header, end); if (error) goto cleanup; error = ext4_xattr_list_entries(dentry, IFIRST(header), Loading Loading @@ -990,8 +1011,7 @@ int ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i, is->s.here = is->s.first; is->s.end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size; if (ext4_test_inode_state(inode, EXT4_STATE_XATTR)) { error = ext4_xattr_check_names(IFIRST(header), is->s.end, IFIRST(header)); error = xattr_check_inode(inode, header, is->s.end); if (error) return error; /* Find the named attribute. */ Loading Loading @@ -1288,6 +1308,10 @@ retry: last = entry; total_ino = sizeof(struct ext4_xattr_ibody_header); error = xattr_check_inode(inode, header, end); if (error) goto cleanup; free = ext4_xattr_free_space(last, &min_offs, base, &total_ino); if (free >= new_extra_isize) { entry = IFIRST(header); Loading Loading
fs/ext4/crypto.c +56 −0 Original line number Diff line number Diff line Loading @@ -469,3 +469,59 @@ uint32_t ext4_validate_encryption_key_size(uint32_t mode, uint32_t size) return size; return 0; } /* * Validate dentries for encrypted directories to make sure we aren't * potentially caching stale data after a key has been added or * removed. */ static int ext4_d_revalidate(struct dentry *dentry, unsigned int flags) { struct inode *dir = d_inode(dentry->d_parent); struct ext4_crypt_info *ci = EXT4_I(dir)->i_crypt_info; int dir_has_key, cached_with_key; if (!ext4_encrypted_inode(dir)) return 0; if (ci && ci->ci_keyring_key && (ci->ci_keyring_key->flags & ((1 << KEY_FLAG_INVALIDATED) | (1 << KEY_FLAG_REVOKED) | (1 << KEY_FLAG_DEAD)))) ci = NULL; /* this should eventually be an flag in d_flags */ cached_with_key = dentry->d_fsdata != NULL; dir_has_key = (ci != NULL); /* * If the dentry was cached without the key, and it is a * negative dentry, it might be a valid name. We can't check * if the key has since been made available due to locking * reasons, so we fail the validation so ext4_lookup() can do * this check. * * We also fail the validation if the dentry was created with * the key present, but we no longer have the key, or vice versa. */ if ((!cached_with_key && d_is_negative(dentry)) || (!cached_with_key && dir_has_key) || (cached_with_key && !dir_has_key)) { #if 0 /* Revalidation debug */ char buf[80]; char *cp = simple_dname(dentry, buf, sizeof(buf)); if (IS_ERR(cp)) cp = (char *) "???"; pr_err("revalidate: %s %p %d %d %d\n", cp, dentry->d_fsdata, cached_with_key, d_is_negative(dentry), dir_has_key); #endif return 0; } return 1; } const struct dentry_operations ext4_encrypted_d_ops = { .d_revalidate = ext4_d_revalidate, };
fs/ext4/dir.c +11 −2 Original line number Diff line number Diff line Loading @@ -111,6 +111,12 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx) int dir_has_error = 0; struct ext4_str fname_crypto_str = {.name = NULL, .len = 0}; if (ext4_encrypted_inode(inode)) { err = ext4_get_encryption_info(inode); if (err && err != -ENOKEY) return err; } if (is_dx_dir(inode)) { err = ext4_dx_readdir(file, ctx); if (err != ERR_BAD_DX_DIR) { Loading Loading @@ -157,8 +163,11 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx) index, 1); file->f_ra.prev_pos = (loff_t)index << PAGE_CACHE_SHIFT; bh = ext4_bread(NULL, inode, map.m_lblk, 0); if (IS_ERR(bh)) return PTR_ERR(bh); if (IS_ERR(bh)) { err = PTR_ERR(bh); bh = NULL; goto errout; } } if (!bh) { Loading
fs/ext4/ext4.h +1 −0 Original line number Diff line number Diff line Loading @@ -2258,6 +2258,7 @@ struct page *ext4_encrypt(struct inode *inode, struct page *plaintext_page); int ext4_decrypt(struct page *page); int ext4_encrypted_zeroout(struct inode *inode, struct ext4_extent *ex); extern const struct dentry_operations ext4_encrypted_d_ops; #ifdef CONFIG_EXT4_FS_ENCRYPTION int ext4_init_crypto(void); Loading
fs/ext4/namei.c +18 −0 Original line number Diff line number Diff line Loading @@ -1558,6 +1558,24 @@ static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, unsi struct ext4_dir_entry_2 *de; struct buffer_head *bh; if (ext4_encrypted_inode(dir)) { int res = ext4_get_encryption_info(dir); /* * This should be a properly defined flag for * dentry->d_flags when we uplift this to the VFS. * d_fsdata is set to (void *) 1 if if the dentry is * created while the directory was encrypted and we * don't have access to the key. */ dentry->d_fsdata = NULL; if (ext4_encryption_info(dir)) dentry->d_fsdata = (void *) 1; d_set_d_op(dentry, &ext4_encrypted_d_ops); if (res && res != -ENOKEY) return ERR_PTR(res); } if (dentry->d_name.len > EXT4_NAME_LEN) return ERR_PTR(-ENAMETOOLONG); Loading
fs/ext4/xattr.c +28 −4 Original line number Diff line number Diff line Loading @@ -232,6 +232,27 @@ ext4_xattr_check_block(struct inode *inode, struct buffer_head *bh) return error; } static int __xattr_check_inode(struct inode *inode, struct ext4_xattr_ibody_header *header, void *end, const char *function, unsigned int line) { struct ext4_xattr_entry *entry = IFIRST(header); int error = -EFSCORRUPTED; if (((void *) header >= end) || (header->h_magic != le32_to_cpu(EXT4_XATTR_MAGIC))) goto errout; error = ext4_xattr_check_names(entry, end, entry); errout: if (error) __ext4_error_inode(inode, function, line, 0, "corrupted in-inode xattr"); return error; } #define xattr_check_inode(inode, header, end) \ __xattr_check_inode((inode), (header), (end), __func__, __LINE__) static inline int ext4_xattr_check_entry(struct ext4_xattr_entry *entry, size_t size) { Loading Loading @@ -343,7 +364,7 @@ ext4_xattr_ibody_get(struct inode *inode, int name_index, const char *name, header = IHDR(inode, raw_inode); entry = IFIRST(header); end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size; error = ext4_xattr_check_names(entry, end, entry); error = xattr_check_inode(inode, header, end); if (error) goto cleanup; error = ext4_xattr_find_entry(&entry, name_index, name, Loading Loading @@ -474,7 +495,7 @@ ext4_xattr_ibody_list(struct dentry *dentry, char *buffer, size_t buffer_size) raw_inode = ext4_raw_inode(&iloc); header = IHDR(inode, raw_inode); end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size; error = ext4_xattr_check_names(IFIRST(header), end, IFIRST(header)); error = xattr_check_inode(inode, header, end); if (error) goto cleanup; error = ext4_xattr_list_entries(dentry, IFIRST(header), Loading Loading @@ -990,8 +1011,7 @@ int ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i, is->s.here = is->s.first; is->s.end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size; if (ext4_test_inode_state(inode, EXT4_STATE_XATTR)) { error = ext4_xattr_check_names(IFIRST(header), is->s.end, IFIRST(header)); error = xattr_check_inode(inode, header, is->s.end); if (error) return error; /* Find the named attribute. */ Loading Loading @@ -1288,6 +1308,10 @@ retry: last = entry; total_ino = sizeof(struct ext4_xattr_ibody_header); error = xattr_check_inode(inode, header, end); if (error) goto cleanup; free = ext4_xattr_free_space(last, &min_offs, base, &total_ino); if (free >= new_extra_isize) { entry = IFIRST(header); Loading