FAILED: patch "[PATCH] fscrypt: add fscrypt_is_nokey_name()" failed to apply to 4.19-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 4.19-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 159e1de201b6fca10bfec50405a3b53a561096a8 Mon Sep 17 00:00:00 2001
From: Eric Biggers <[email protected]>
Date: Tue, 17 Nov 2020 23:56:05 -0800
Subject: [PATCH] fscrypt: add fscrypt_is_nokey_name()
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.
fs/crypto/hooks.c +3 -2
@@ -61,7 +61,7 @@ int __fscrypt_prepare_link(struct inode *inode, struct inode *dir,return err;/* ... in case we looked up no-key name before key was added */ -if (dentry->d_flags &DCACHE_NOKEY_NAME ) +if (fscrypt_is_nokey_name (dentry))return -ENOKEY ;if (!fscrypt_has_permitted_context (dir, inode))
@@ -86,7 +86,8 @@ int __fscrypt_prepare_rename(struct inode *old_dir, struct dentry *old_dentry,return err;/* ... in case we looked up no-key name(s) before key was added */ -if ((old_dentry->d_flags | new_dentry->d_flags) &DCACHE_NOKEY_NAME ) +if (fscrypt_is_nokey_name (old_dentry) || +fscrypt_is_nokey_name (new_dentry))return -ENOKEY ;if (old_dir != new_dir) {
include/linux/fscrypt.h +34 -0
@@ -111,6 +111,35 @@ static inline void fscrypt_handle_d_move(struct dentry *dentry) dentry->d_flags &= ~DCACHE_NOKEY_NAME ; } +/** +* 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_NOKEY_NAME ; +} +/* crypto.c */ void fscrypt_enqueue_decrypt_work (struct work_struct *);
@@ -244,6 +273,11 @@ static inline void fscrypt_handle_d_move(struct dentry *dentry) { } +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) {