Skip to content

Commit

Permalink
Merge tag 'fscrypt_for_linus' of git://git.kernel.org/pub/scm/fs/fscr…
Browse files Browse the repository at this point in the history
…ypt/fscrypt

Pull fscrypt updates from Ted Ts'o:
 "Clean up fscrypt's dcache revalidation support, and other
  miscellaneous cleanups"

* tag 'fscrypt_for_linus' of git://git.kernel.org/pub/scm/fs/fscrypt/fscrypt:
  fscrypt: cache decrypted symlink target in ->i_link
  vfs: use READ_ONCE() to access ->i_link
  fscrypt: fix race where ->lookup() marks plaintext dentry as ciphertext
  fscrypt: only set dentry_operations on ciphertext dentries
  fs, fscrypt: clear DCACHE_ENCRYPTED_NAME when unaliasing directory
  fscrypt: fix race allowing rename() and link() of ciphertext dentries
  fscrypt: clean up and improve dentry revalidation
  fscrypt: use READ_ONCE() to access ->i_crypt_info
  fscrypt: remove WARN_ON_ONCE() when decryption fails
  fscrypt: drop inode argument from fscrypt_get_ctx()
  • Loading branch information
torvalds committed May 8, 2019
2 parents 5abe379 + 2c58d54 commit a9fbcd6
Show file tree
Hide file tree
Showing 18 changed files with 294 additions and 144 deletions.
8 changes: 3 additions & 5 deletions fs/crypto/bio.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,10 @@ static void __fscrypt_decrypt_bio(struct bio *bio, bool done)
int ret = fscrypt_decrypt_page(page->mapping->host, page,
PAGE_SIZE, 0, page->index);

if (ret) {
WARN_ON_ONCE(1);
if (ret)
SetPageError(page);
} else if (done) {
else if (done)
SetPageUptodate(page);
}
if (done)
unlock_page(page);
}
Expand Down Expand Up @@ -103,7 +101,7 @@ int fscrypt_zeroout_range(const struct inode *inode, pgoff_t lblk,

BUG_ON(inode->i_sb->s_blocksize != PAGE_SIZE);

ctx = fscrypt_get_ctx(inode, GFP_NOFS);
ctx = fscrypt_get_ctx(GFP_NOFS);
if (IS_ERR(ctx))
return PTR_ERR(ctx);

Expand Down
74 changes: 35 additions & 39 deletions fs/crypto/crypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -87,23 +87,17 @@ EXPORT_SYMBOL(fscrypt_release_ctx);

/**
* fscrypt_get_ctx() - Gets an encryption context
* @inode: The inode for which we are doing the crypto
* @gfp_flags: The gfp flag for memory allocation
*
* Allocates and initializes an encryption context.
*
* Return: An allocated and initialized encryption context on success; error
* value or NULL otherwise.
* Return: A new encryption context on success; an ERR_PTR() otherwise.
*/
struct fscrypt_ctx *fscrypt_get_ctx(const struct inode *inode, gfp_t gfp_flags)
struct fscrypt_ctx *fscrypt_get_ctx(gfp_t gfp_flags)
{
struct fscrypt_ctx *ctx = NULL;
struct fscrypt_info *ci = inode->i_crypt_info;
struct fscrypt_ctx *ctx;
unsigned long flags;

if (ci == NULL)
return ERR_PTR(-ENOKEY);

/*
* We first try getting the ctx from a free list because in
* the common case the ctx will have an allocated and
Expand Down Expand Up @@ -258,9 +252,9 @@ struct page *fscrypt_encrypt_page(const struct inode *inode,

BUG_ON(!PageLocked(page));

ctx = fscrypt_get_ctx(inode, gfp_flags);
ctx = fscrypt_get_ctx(gfp_flags);
if (IS_ERR(ctx))
return (struct page *)ctx;
return ERR_CAST(ctx);

/* The encryption operation will require a bounce page. */
ciphertext_page = fscrypt_alloc_bounce_page(ctx, gfp_flags);
Expand Down Expand Up @@ -313,45 +307,47 @@ int fscrypt_decrypt_page(const struct inode *inode, struct page *page,
EXPORT_SYMBOL(fscrypt_decrypt_page);

/*
* Validate dentries for encrypted directories to make sure we aren't
* potentially caching stale data after a key has been added or
* removed.
* Validate dentries in encrypted directories to make sure we aren't potentially
* caching stale dentries after a key has been added.
*/
static int fscrypt_d_revalidate(struct dentry *dentry, unsigned int flags)
{
struct dentry *dir;
int dir_has_key, cached_with_key;
int err;
int valid;

/*
* Plaintext names are always valid, since fscrypt doesn't support
* reverting to ciphertext names without evicting the directory's inode
* -- which implies eviction of the dentries in the directory.
*/
if (!(dentry->d_flags & DCACHE_ENCRYPTED_NAME))
return 1;

/*
* Ciphertext name; valid if the directory's key is still unavailable.
*
* Although fscrypt forbids rename() on ciphertext names, we still must
* use dget_parent() here rather than use ->d_parent directly. That's
* because a corrupted fs image may contain directory hard links, which
* the VFS handles by moving the directory's dentry tree in the dcache
* each time ->lookup() finds the directory and it already has a dentry
* elsewhere. Thus ->d_parent can be changing, and we must safely grab
* a reference to some ->d_parent to prevent it from being freed.
*/

if (flags & LOOKUP_RCU)
return -ECHILD;

dir = dget_parent(dentry);
if (!IS_ENCRYPTED(d_inode(dir))) {
dput(dir);
return 0;
}

spin_lock(&dentry->d_lock);
cached_with_key = dentry->d_flags & DCACHE_ENCRYPTED_WITH_KEY;
spin_unlock(&dentry->d_lock);
dir_has_key = (d_inode(dir)->i_crypt_info != NULL);
err = fscrypt_get_encryption_info(d_inode(dir));
valid = !fscrypt_has_encryption_key(d_inode(dir));
dput(dir);

/*
* 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))
return 0;
return 1;
if (err < 0)
return err;

return valid;
}

const struct dentry_operations fscrypt_d_ops = {
Expand Down
5 changes: 3 additions & 2 deletions fs/crypto/fname.c
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ int fscrypt_fname_disk_to_usr(struct inode *inode,
if (iname->len < FS_CRYPTO_BLOCK_SIZE)
return -EUCLEAN;

if (inode->i_crypt_info)
if (fscrypt_has_encryption_key(inode))
return fname_decrypt(inode, iname, oname);

if (iname->len <= FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE) {
Expand Down Expand Up @@ -336,7 +336,7 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname,
if (ret)
return ret;

if (dir->i_crypt_info) {
if (fscrypt_has_encryption_key(dir)) {
if (!fscrypt_fname_encrypted_size(dir, iname->len,
dir->i_sb->s_cop->max_namelen,
&fname->crypto_buf.len))
Expand All @@ -356,6 +356,7 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname,
}
if (!lookup)
return -ENOKEY;
fname->is_ciphertext_name = true;

/*
* We don't have the key and we are doing a lookup; decode the
Expand Down
68 changes: 52 additions & 16 deletions fs/crypto/hooks.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,19 @@ int fscrypt_file_open(struct inode *inode, struct file *filp)
}
EXPORT_SYMBOL_GPL(fscrypt_file_open);

int __fscrypt_prepare_link(struct inode *inode, struct inode *dir)
int __fscrypt_prepare_link(struct inode *inode, struct inode *dir,
struct dentry *dentry)
{
int err;

err = fscrypt_require_key(dir);
if (err)
return err;

/* ... in case we looked up ciphertext name before key was added */
if (dentry->d_flags & DCACHE_ENCRYPTED_NAME)
return -ENOKEY;

if (!fscrypt_has_permitted_context(dir, inode))
return -EXDEV;

Expand All @@ -78,6 +83,11 @@ int __fscrypt_prepare_rename(struct inode *old_dir, struct dentry *old_dentry,
if (err)
return err;

/* ... in case we looked up ciphertext name(s) before key was added */
if ((old_dentry->d_flags | new_dentry->d_flags) &
DCACHE_ENCRYPTED_NAME)
return -ENOKEY;

if (old_dir != new_dir) {
if (IS_ENCRYPTED(new_dir) &&
!fscrypt_has_permitted_context(new_dir,
Expand All @@ -94,21 +104,21 @@ int __fscrypt_prepare_rename(struct inode *old_dir, struct dentry *old_dentry,
}
EXPORT_SYMBOL_GPL(__fscrypt_prepare_rename);

int __fscrypt_prepare_lookup(struct inode *dir, struct dentry *dentry)
int __fscrypt_prepare_lookup(struct inode *dir, struct dentry *dentry,
struct fscrypt_name *fname)
{
int err = fscrypt_get_encryption_info(dir);
int err = fscrypt_setup_filename(dir, &dentry->d_name, 1, fname);

if (err)
if (err && err != -ENOENT)
return err;

if (fscrypt_has_encryption_key(dir)) {
if (fname->is_ciphertext_name) {
spin_lock(&dentry->d_lock);
dentry->d_flags |= DCACHE_ENCRYPTED_WITH_KEY;
dentry->d_flags |= DCACHE_ENCRYPTED_NAME;
spin_unlock(&dentry->d_lock);
d_set_d_op(dentry, &fscrypt_d_ops);
}

d_set_d_op(dentry, &fscrypt_d_ops);
return 0;
return err;
}
EXPORT_SYMBOL_GPL(__fscrypt_prepare_lookup);

Expand Down Expand Up @@ -179,21 +189,30 @@ int __fscrypt_encrypt_symlink(struct inode *inode, const char *target,
sd->len = cpu_to_le16(ciphertext_len);

err = fname_encrypt(inode, &iname, sd->encrypted_path, ciphertext_len);
if (err) {
if (!disk_link->name)
kfree(sd);
return err;
}
if (err)
goto err_free_sd;

/*
* Null-terminating the ciphertext doesn't make sense, but we still
* count the null terminator in the length, so we might as well
* initialize it just in case the filesystem writes it out.
*/
sd->encrypted_path[ciphertext_len] = '\0';

/* Cache the plaintext symlink target for later use by get_link() */
err = -ENOMEM;
inode->i_link = kmemdup(target, len + 1, GFP_NOFS);
if (!inode->i_link)
goto err_free_sd;

if (!disk_link->name)
disk_link->name = (unsigned char *)sd;
return 0;

err_free_sd:
if (!disk_link->name)
kfree(sd);
return err;
}
EXPORT_SYMBOL_GPL(__fscrypt_encrypt_symlink);

Expand All @@ -202,7 +221,7 @@ EXPORT_SYMBOL_GPL(__fscrypt_encrypt_symlink);
* @inode: the symlink inode
* @caddr: the on-disk contents of the symlink
* @max_size: size of @caddr buffer
* @done: if successful, will be set up to free the returned target
* @done: if successful, will be set up to free the returned target if needed
*
* If the symlink's encryption key is available, we decrypt its target.
* Otherwise, we encode its target for presentation.
Expand All @@ -217,19 +236,26 @@ const char *fscrypt_get_symlink(struct inode *inode, const void *caddr,
{
const struct fscrypt_symlink_data *sd;
struct fscrypt_str cstr, pstr;
bool has_key;
int err;

/* This is for encrypted symlinks only */
if (WARN_ON(!IS_ENCRYPTED(inode)))
return ERR_PTR(-EINVAL);

/* If the decrypted target is already cached, just return it. */
pstr.name = READ_ONCE(inode->i_link);
if (pstr.name)
return pstr.name;

/*
* Try to set up the symlink's encryption key, but we can continue
* regardless of whether the key is available or not.
*/
err = fscrypt_get_encryption_info(inode);
if (err)
return ERR_PTR(err);
has_key = fscrypt_has_encryption_key(inode);

/*
* For historical reasons, encrypted symlink targets are prefixed with
Expand Down Expand Up @@ -261,7 +287,17 @@ const char *fscrypt_get_symlink(struct inode *inode, const void *caddr,
goto err_kfree;

pstr.name[pstr.len] = '\0';
set_delayed_call(done, kfree_link, pstr.name);

/*
* Cache decrypted symlink targets in i_link for later use. Don't cache
* symlink targets encoded without the key, since those become outdated
* once the key is added. This pairs with the READ_ONCE() above and in
* the VFS path lookup code.
*/
if (!has_key ||
cmpxchg_release(&inode->i_link, NULL, pstr.name) != NULL)
set_delayed_call(done, kfree_link, pstr.name);

return pstr.name;

err_kfree:
Expand Down
25 changes: 23 additions & 2 deletions fs/crypto/keyinfo.c
Original file line number Diff line number Diff line change
Expand Up @@ -508,7 +508,7 @@ int fscrypt_get_encryption_info(struct inode *inode)
u8 *raw_key = NULL;
int res;

if (inode->i_crypt_info)
if (fscrypt_has_encryption_key(inode))
return 0;

res = fscrypt_initialize(inode->i_sb->s_cop->flags);
Expand Down Expand Up @@ -572,7 +572,7 @@ int fscrypt_get_encryption_info(struct inode *inode)
if (res)
goto out;

if (cmpxchg(&inode->i_crypt_info, NULL, crypt_info) == NULL)
if (cmpxchg_release(&inode->i_crypt_info, NULL, crypt_info) == NULL)
crypt_info = NULL;
out:
if (res == -ENOKEY)
Expand All @@ -583,9 +583,30 @@ int fscrypt_get_encryption_info(struct inode *inode)
}
EXPORT_SYMBOL(fscrypt_get_encryption_info);

/**
* fscrypt_put_encryption_info - free most of an inode's fscrypt data
*
* Free the inode's fscrypt_info. Filesystems must call this when the inode is
* being evicted. An RCU grace period need not have elapsed yet.
*/
void fscrypt_put_encryption_info(struct inode *inode)
{
put_crypt_info(inode->i_crypt_info);
inode->i_crypt_info = NULL;
}
EXPORT_SYMBOL(fscrypt_put_encryption_info);

/**
* fscrypt_free_inode - free an inode's fscrypt data requiring RCU delay
*
* Free the inode's cached decrypted symlink target, if any. Filesystems must
* call this after an RCU grace period, just before they free the inode.
*/
void fscrypt_free_inode(struct inode *inode)
{
if (IS_ENCRYPTED(inode) && S_ISLNK(inode->i_mode)) {
kfree(inode->i_link);
inode->i_link = NULL;
}
}
EXPORT_SYMBOL(fscrypt_free_inode);
6 changes: 3 additions & 3 deletions fs/crypto/policy.c
Original file line number Diff line number Diff line change
Expand Up @@ -194,8 +194,8 @@ int fscrypt_has_permitted_context(struct inode *parent, struct inode *child)
res = fscrypt_get_encryption_info(child);
if (res)
return 0;
parent_ci = parent->i_crypt_info;
child_ci = child->i_crypt_info;
parent_ci = READ_ONCE(parent->i_crypt_info);
child_ci = READ_ONCE(child->i_crypt_info);

if (parent_ci && child_ci) {
return memcmp(parent_ci->ci_master_key_descriptor,
Expand Down Expand Up @@ -246,7 +246,7 @@ int fscrypt_inherit_context(struct inode *parent, struct inode *child,
if (res < 0)
return res;

ci = parent->i_crypt_info;
ci = READ_ONCE(parent->i_crypt_info);
if (ci == NULL)
return -ENOKEY;

Expand Down
Loading

0 comments on commit a9fbcd6

Please sign in to comment.