lkml.org 
[lkml]   [2021]   [Jan]   [5]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 4.19 02/29] fscrypt: add fscrypt_is_nokey_name()
    Date
    From: Eric Biggers <ebiggers@google.com>

    commit 159e1de201b6fca10bfec50405a3b53a561096a8 upstream.

    It's possible to create a duplicate filename in an encrypted directory
    by creating a file concurrently with adding the encryption key.

    Specifically, sys_open(O_CREAT) (or sys_mkdir(), sys_mknod(), or
    sys_symlink()) can lookup the target filename while the directory's
    encryption key hasn't been added yet, resulting in a negative no-key
    dentry. The VFS then calls ->create() (or ->mkdir(), ->mknod(), or
    ->symlink()) because the dentry is negative. Normally, ->create() would
    return -ENOKEY due to the directory's key being unavailable. However,
    if the key was added between the dentry lookup and ->create(), then the
    filesystem will go ahead and try to create the file.

    If the target filename happens to already exist as a normal name (not a
    no-key name), a duplicate filename may be added to the directory.

    In order to fix this, we need to fix the filesystems to prevent
    ->create(), ->mkdir(), ->mknod(), and ->symlink() on no-key names.
    (->rename() and ->link() need it too, but those are already handled
    correctly by fscrypt_prepare_rename() and fscrypt_prepare_link().)

    In preparation for this, add a helper function fscrypt_is_nokey_name()
    that filesystems can use to do this check. Use this helper function for
    the existing checks that fs/crypto/ does for rename and link.

    Cc: stable@vger.kernel.org
    Link: https://lore.kernel.org/r/20201118075609.120337-2-ebiggers@kernel.org
    Signed-off-by: Eric Biggers <ebiggers@google.com>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
    ---
    fs/crypto/hooks.c | 10 +++++-----
    include/linux/fscrypt_notsupp.h | 5 +++++
    include/linux/fscrypt_supp.h | 29 +++++++++++++++++++++++++++++
    3 files changed, 39 insertions(+), 5 deletions(-)

    --- a/fs/crypto/hooks.c
    +++ b/fs/crypto/hooks.c
    @@ -58,8 +58,8 @@ int __fscrypt_prepare_link(struct inode
    if (err)
    return err;

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

    if (!fscrypt_has_permitted_context(dir, inode))
    @@ -83,9 +83,9 @@ int __fscrypt_prepare_rename(struct inod
    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)
    + /* ... in case we looked up no-key name(s) before key was added */
    + if (fscrypt_is_nokey_name(old_dentry) ||
    + fscrypt_is_nokey_name(new_dentry))
    return -ENOKEY;

    if (old_dir != new_dir) {
    --- a/include/linux/fscrypt_notsupp.h
    +++ b/include/linux/fscrypt_notsupp.h
    @@ -24,6 +24,11 @@ static inline bool fscrypt_dummy_context
    return false;
    }

    +static inline bool fscrypt_is_nokey_name(const struct dentry *dentry)
    +{
    + return false;
    +}
    +
    /* crypto.c */
    static inline void fscrypt_enqueue_decrypt_work(struct work_struct *work)
    {
    --- a/include/linux/fscrypt_supp.h
    +++ b/include/linux/fscrypt_supp.h
    @@ -58,6 +58,35 @@ static inline bool fscrypt_dummy_context
    inode->i_sb->s_cop->dummy_context(inode);
    }

    +/**
    + * fscrypt_is_nokey_name() - test whether a dentry is a no-key name
    + * @dentry: the dentry to check
    + *
    + * This returns true if the dentry is a no-key dentry. A no-key dentry is a
    + * dentry that was created in an encrypted directory that hasn't had its
    + * encryption key added yet. Such dentries may be either positive or negative.
    + *
    + * When a filesystem is asked to create a new filename in an encrypted directory
    + * and the new filename's dentry is a no-key dentry, it must fail the operation
    + * with ENOKEY. This includes ->create(), ->mkdir(), ->mknod(), ->symlink(),
    + * ->rename(), and ->link(). (However, ->rename() and ->link() are already
    + * handled by fscrypt_prepare_rename() and fscrypt_prepare_link().)
    + *
    + * This is necessary because creating a filename requires the directory's
    + * encryption key, but just checking for the key on the directory inode during
    + * the final filesystem operation doesn't guarantee that the key was available
    + * during the preceding dentry lookup. And the key must have already been
    + * available during the dentry lookup in order for it to have been checked
    + * whether the filename already exists in the directory and for the new file's
    + * dentry not to be invalidated due to it incorrectly having the no-key flag.
    + *
    + * Return: %true if the dentry is a no-key name
    + */
    +static inline bool fscrypt_is_nokey_name(const struct dentry *dentry)
    +{
    + return dentry->d_flags & DCACHE_ENCRYPTED_NAME;
    +}
    +
    /* crypto.c */
    extern void fscrypt_enqueue_decrypt_work(struct work_struct *);
    extern struct fscrypt_ctx *fscrypt_get_ctx(const struct inode *, gfp_t);

    \
     
     \ /
      Last update: 2021-01-05 10:33    [W:5.264 / U:0.016 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site