commitb01531db6cupstream. ->lookup() in an encrypted directory begins as follows: 1. fscrypt_prepare_lookup(): a. Try to load the directory's encryption key. b. If the key is unavailable, mark the dentry as a ciphertext name via d_flags. 2. fscrypt_setup_filename(): a. Try to load the directory's encryption key. b. If the key is available, encrypt the name (treated as a plaintext name) to get the on-disk name. Otherwise decode the name (treated as a ciphertext name) to get the on-disk name. But if the key is concurrently added, it may be found at (2a) but not at (1a). In this case, the dentry will be wrongly marked as a ciphertext name even though it was actually treated as plaintext. This will cause the dentry to be wrongly invalidated on the next lookup, potentially causing problems. For example, if the racy ->lookup() was part of sys_mount(), then the new mount will be detached when anything tries to access it. This is despite the mountpoint having a plaintext path, which should remain valid now that the key was added. Of course, this is only possible if there's a userspace race. Still, the additional kernel-side race is confusing and unexpected. Close the kernel-side race by changing fscrypt_prepare_lookup() to also set the on-disk filename (step 2b), consistent with the d_flags update. Fixes:28b4c26396("ext4 crypto: revalidate dentry after adding or removing the key") Signed-off-by: Eric Biggers <ebiggers@google.com> Signed-off-by: Theodore Ts'o <tytso@mit.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
233 lines
5.0 KiB
C
233 lines
5.0 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
/*
|
|
* fscrypt_notsupp.h
|
|
*
|
|
* This stubs out the fscrypt functions for filesystems configured without
|
|
* encryption support.
|
|
*
|
|
* Do not include this file directly. Use fscrypt.h instead!
|
|
*/
|
|
#ifndef _LINUX_FSCRYPT_H
|
|
#error "Incorrect include of linux/fscrypt_notsupp.h!"
|
|
#endif
|
|
|
|
#ifndef _LINUX_FSCRYPT_NOTSUPP_H
|
|
#define _LINUX_FSCRYPT_NOTSUPP_H
|
|
|
|
static inline bool fscrypt_has_encryption_key(const struct inode *inode)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
static inline bool fscrypt_dummy_context_enabled(struct inode *inode)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/* crypto.c */
|
|
static inline void fscrypt_enqueue_decrypt_work(struct work_struct *work)
|
|
{
|
|
}
|
|
|
|
static inline struct fscrypt_ctx *fscrypt_get_ctx(const struct inode *inode,
|
|
gfp_t gfp_flags)
|
|
{
|
|
return ERR_PTR(-EOPNOTSUPP);
|
|
}
|
|
|
|
static inline void fscrypt_release_ctx(struct fscrypt_ctx *ctx)
|
|
{
|
|
return;
|
|
}
|
|
|
|
static inline struct page *fscrypt_encrypt_page(const struct inode *inode,
|
|
struct page *page,
|
|
unsigned int len,
|
|
unsigned int offs,
|
|
u64 lblk_num, gfp_t gfp_flags)
|
|
{
|
|
return ERR_PTR(-EOPNOTSUPP);
|
|
}
|
|
|
|
static inline int fscrypt_decrypt_page(const struct inode *inode,
|
|
struct page *page,
|
|
unsigned int len, unsigned int offs,
|
|
u64 lblk_num)
|
|
{
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
static inline struct page *fscrypt_control_page(struct page *page)
|
|
{
|
|
WARN_ON_ONCE(1);
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
static inline void fscrypt_restore_control_page(struct page *page)
|
|
{
|
|
return;
|
|
}
|
|
|
|
/* policy.c */
|
|
static inline int fscrypt_ioctl_set_policy(struct file *filp,
|
|
const void __user *arg)
|
|
{
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
static inline int fscrypt_ioctl_get_policy(struct file *filp, void __user *arg)
|
|
{
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
static inline int fscrypt_has_permitted_context(struct inode *parent,
|
|
struct inode *child)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static inline int fscrypt_inherit_context(struct inode *parent,
|
|
struct inode *child,
|
|
void *fs_data, bool preload)
|
|
{
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
/* keyinfo.c */
|
|
static inline int fscrypt_get_encryption_info(struct inode *inode)
|
|
{
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
static inline void fscrypt_put_encryption_info(struct inode *inode)
|
|
{
|
|
return;
|
|
}
|
|
|
|
/* fname.c */
|
|
static inline int fscrypt_setup_filename(struct inode *dir,
|
|
const struct qstr *iname,
|
|
int lookup, struct fscrypt_name *fname)
|
|
{
|
|
if (IS_ENCRYPTED(dir))
|
|
return -EOPNOTSUPP;
|
|
|
|
memset(fname, 0, sizeof(*fname));
|
|
fname->usr_fname = iname;
|
|
fname->disk_name.name = (unsigned char *)iname->name;
|
|
fname->disk_name.len = iname->len;
|
|
return 0;
|
|
}
|
|
|
|
static inline void fscrypt_free_filename(struct fscrypt_name *fname)
|
|
{
|
|
return;
|
|
}
|
|
|
|
static inline int fscrypt_fname_alloc_buffer(const struct inode *inode,
|
|
u32 max_encrypted_len,
|
|
struct fscrypt_str *crypto_str)
|
|
{
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
static inline void fscrypt_fname_free_buffer(struct fscrypt_str *crypto_str)
|
|
{
|
|
return;
|
|
}
|
|
|
|
static inline int fscrypt_fname_disk_to_usr(struct inode *inode,
|
|
u32 hash, u32 minor_hash,
|
|
const struct fscrypt_str *iname,
|
|
struct fscrypt_str *oname)
|
|
{
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
static inline bool fscrypt_match_name(const struct fscrypt_name *fname,
|
|
const u8 *de_name, u32 de_name_len)
|
|
{
|
|
/* Encryption support disabled; use standard comparison */
|
|
if (de_name_len != fname->disk_name.len)
|
|
return false;
|
|
return !memcmp(de_name, fname->disk_name.name, fname->disk_name.len);
|
|
}
|
|
|
|
/* bio.c */
|
|
static inline void fscrypt_decrypt_bio(struct bio *bio)
|
|
{
|
|
}
|
|
|
|
static inline void fscrypt_enqueue_decrypt_bio(struct fscrypt_ctx *ctx,
|
|
struct bio *bio)
|
|
{
|
|
}
|
|
|
|
static inline void fscrypt_pullback_bio_page(struct page **page, bool restore)
|
|
{
|
|
return;
|
|
}
|
|
|
|
static inline int fscrypt_zeroout_range(const struct inode *inode, pgoff_t lblk,
|
|
sector_t pblk, unsigned int len)
|
|
{
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
/* hooks.c */
|
|
|
|
static inline int fscrypt_file_open(struct inode *inode, struct file *filp)
|
|
{
|
|
if (IS_ENCRYPTED(inode))
|
|
return -EOPNOTSUPP;
|
|
return 0;
|
|
}
|
|
|
|
static inline int __fscrypt_prepare_link(struct inode *inode, struct inode *dir,
|
|
struct dentry *dentry)
|
|
{
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
static inline int __fscrypt_prepare_rename(struct inode *old_dir,
|
|
struct dentry *old_dentry,
|
|
struct inode *new_dir,
|
|
struct dentry *new_dentry,
|
|
unsigned int flags)
|
|
{
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
static inline int __fscrypt_prepare_lookup(struct inode *dir,
|
|
struct dentry *dentry,
|
|
struct fscrypt_name *fname)
|
|
{
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
static inline int __fscrypt_prepare_symlink(struct inode *dir,
|
|
unsigned int len,
|
|
unsigned int max_len,
|
|
struct fscrypt_str *disk_link)
|
|
{
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
static inline int __fscrypt_encrypt_symlink(struct inode *inode,
|
|
const char *target,
|
|
unsigned int len,
|
|
struct fscrypt_str *disk_link)
|
|
{
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
static inline const char *fscrypt_get_symlink(struct inode *inode,
|
|
const void *caddr,
|
|
unsigned int max_size,
|
|
struct delayed_call *done)
|
|
{
|
|
return ERR_PTR(-EOPNOTSUPP);
|
|
}
|
|
|
|
#endif /* _LINUX_FSCRYPT_NOTSUPP_H */
|