Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fa9d8ddc authored by Theodore Ts'o's avatar Theodore Ts'o Committed by Eric Biggers
Browse files

UPSTREAM: ext4 crypto: revalidate dentry after adding or removing the key



commit 28b4c263961c47da84ed8b5be0b5116bad1133eb upstream.

Add a validation check for dentries for encrypted directory to make
sure we're not caching stale data after a key has been added or removed.

Also check to make sure that status of the encryption key is updated
when readdir(2) is executed.

Signed-off-by: default avatarTheodore Ts'o <tytso@mit.edu>
Signed-off-by: default avatarEric Biggers <ebiggers@google.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
(cherry picked from commit 2faff9d1dfc560da3bfba6a8ab0c30246fd6c1cb)
Change-Id: I9b77145c13b70df8dec5a90c977d6dc98d9989ae
Signed-off-by: default avatarEric Biggers <ebiggers@google.com>
parent a6eca816
Loading
Loading
Loading
Loading
+50 −0
Original line number Diff line number Diff line
@@ -470,3 +470,53 @@ 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;

	/* 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,
};
+6 −0
Original line number Diff line number Diff line
@@ -114,6 +114,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) {
+1 −0
Original line number Diff line number Diff line
@@ -2091,6 +2091,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);
+18 −0
Original line number Diff line number Diff line
@@ -1547,6 +1547,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);