lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
Message-Id: <20211013125652.3578336-1-s.hauer@pengutronix.de>
Date:   Wed, 13 Oct 2021 14:56:52 +0200
From:   Sascha Hauer <s.hauer@...gutronix.de>
To:     ecryptfs@...r.kernel.org
Cc:     Tyler Hicks <code@...icks.com>, linux-kernel@...r.kernel.org,
        kernel@...gutronix.de, Sascha Hauer <s.hauer@...gutronix.de>
Subject: [PATCH v2] eCryptfs: fix setattr on empty lower file

Depending on the synchronization state of the lower filesystem during a
power cut it can happen that a lower file is empty after that power cut.

An inode_operations::setattr operation fails with -EIO on such files,
because ecryptfs_read_metadata() fails. In e3ccaa976120 ("eCryptfs:
Initialize empty lower files when opening them") a similar problem was
solved in the open() path:

| To transparently solve this problem, this patch initializes empty lower
| files in the ecryptfs_open() error path. If the metadata is unreadable
| due to the lower inode size being 0, plaintext passthrough support is
| not in use, and the metadata is stored in the header of the file (as
| opposed to the user.ecryptfs extended attribute), the lower file will be
| initialized.

Do the same in inode_operations::setattr to allow setattr on empty lower
files.

Signed-off-by: Sascha Hauer <s.hauer@...gutronix.de>
---

Changes since v1 (https://www.spinics.net/lists/ecryptfs/msg01397.html):
- In ecryptfs_setattr() ecryptfs_read_or_initialize_metadata() can't be
  called directly as &crypt_stat->cs_mutex would be locked, but in
  ecryptfs_settattr() that mutex is already locked. Create a
  locked/unlocked version of ecryptfs_read_or_initialize_metadata() and
  use the latter one in ecryptfs_setattr().

 fs/ecryptfs/crypto.c          | 61 +++++++++++++++++++++++++++++++++--
 fs/ecryptfs/ecryptfs_kernel.h |  3 +-
 fs/ecryptfs/file.c            | 44 +------------------------
 fs/ecryptfs/inode.c           |  2 +-
 4 files changed, 63 insertions(+), 47 deletions(-)

diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c
index e3f5d7f3c8a0a..a14d3ef40259f 100644
--- a/fs/ecryptfs/crypto.c
+++ b/fs/ecryptfs/crypto.c
@@ -1377,7 +1377,7 @@ int ecryptfs_read_and_validate_xattr_region(struct dentry *dentry,
  *
  * Returns zero if valid headers found and parsed; non-zero otherwise
  */
-int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry)
+static int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry)
 {
 	int rc;
 	char *page_virt;
@@ -1443,7 +1443,64 @@ int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry)
 	return rc;
 }
 
-/*
+int ecryptfs_read_or_initialize_metadata_locked(struct dentry *dentry)
+{
+	struct inode *inode = d_inode(dentry);
+	struct ecryptfs_mount_crypt_stat *mount_crypt_stat;
+	struct ecryptfs_crypt_stat *crypt_stat;
+	int rc;
+
+	crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat;
+	mount_crypt_stat = &ecryptfs_superblock_to_private(
+						inode->i_sb)->mount_crypt_stat;
+
+	if (crypt_stat->flags & ECRYPTFS_POLICY_APPLIED &&
+	    crypt_stat->flags & ECRYPTFS_KEY_VALID) {
+		rc = 0;
+		goto out;
+	}
+
+	rc = ecryptfs_read_metadata(dentry);
+	if (!rc)
+		goto out;
+
+	if (mount_crypt_stat->flags & ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED) {
+		crypt_stat->flags &= ~(ECRYPTFS_I_SIZE_INITIALIZED
+				       | ECRYPTFS_ENCRYPTED);
+		rc = 0;
+		goto out;
+	}
+
+	if (!(mount_crypt_stat->flags & ECRYPTFS_XATTR_METADATA_ENABLED) &&
+	    !i_size_read(ecryptfs_inode_to_lower(inode))) {
+		rc = ecryptfs_initialize_file(dentry, inode);
+		if (!rc)
+			goto out;
+	}
+
+	rc = -EIO;
+out:
+	return rc;
+}
+
+int ecryptfs_read_or_initialize_metadata(struct dentry *dentry)
+{
+	struct inode *inode = d_inode(dentry);
+	struct ecryptfs_crypt_stat *crypt_stat;
+	int rc;
+
+	crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat;
+
+	mutex_lock(&crypt_stat->cs_mutex);
+
+	rc = ecryptfs_read_or_initialize_metadata_locked(dentry);
+
+	mutex_unlock(&crypt_stat->cs_mutex);
+
+	return rc;
+}
+
+/**
  * ecryptfs_encrypt_filename - encrypt filename
  *
  * CBC-encrypts the filename. We do not want to encrypt the same
diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h
index 5f2b49e13731a..af71d6f7da91d 100644
--- a/fs/ecryptfs/ecryptfs_kernel.h
+++ b/fs/ecryptfs/ecryptfs_kernel.h
@@ -573,7 +573,8 @@ int ecryptfs_encrypt_page(struct page *page);
 int ecryptfs_decrypt_page(struct page *page);
 int ecryptfs_write_metadata(struct dentry *ecryptfs_dentry,
 			    struct inode *ecryptfs_inode);
-int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry);
+int ecryptfs_read_or_initialize_metadata(struct dentry *dentry);
+int ecryptfs_read_or_initialize_metadata_locked(struct dentry *dentry);
 int ecryptfs_new_file_context(struct inode *ecryptfs_inode);
 void ecryptfs_write_crypt_stat_flags(char *page_virt,
 				     struct ecryptfs_crypt_stat *crypt_stat,
diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c
index 18d5b91cb573e..4721aba376784 100644
--- a/fs/ecryptfs/file.c
+++ b/fs/ecryptfs/file.c
@@ -124,48 +124,6 @@ static int ecryptfs_readdir(struct file *file, struct dir_context *ctx)
 
 struct kmem_cache *ecryptfs_file_info_cache;
 
-static int read_or_initialize_metadata(struct dentry *dentry)
-{
-	struct inode *inode = d_inode(dentry);
-	struct ecryptfs_mount_crypt_stat *mount_crypt_stat;
-	struct ecryptfs_crypt_stat *crypt_stat;
-	int rc;
-
-	crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat;
-	mount_crypt_stat = &ecryptfs_superblock_to_private(
-						inode->i_sb)->mount_crypt_stat;
-	mutex_lock(&crypt_stat->cs_mutex);
-
-	if (crypt_stat->flags & ECRYPTFS_POLICY_APPLIED &&
-	    crypt_stat->flags & ECRYPTFS_KEY_VALID) {
-		rc = 0;
-		goto out;
-	}
-
-	rc = ecryptfs_read_metadata(dentry);
-	if (!rc)
-		goto out;
-
-	if (mount_crypt_stat->flags & ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED) {
-		crypt_stat->flags &= ~(ECRYPTFS_I_SIZE_INITIALIZED
-				       | ECRYPTFS_ENCRYPTED);
-		rc = 0;
-		goto out;
-	}
-
-	if (!(mount_crypt_stat->flags & ECRYPTFS_XATTR_METADATA_ENABLED) &&
-	    !i_size_read(ecryptfs_inode_to_lower(inode))) {
-		rc = ecryptfs_initialize_file(dentry, inode);
-		if (!rc)
-			goto out;
-	}
-
-	rc = -EIO;
-out:
-	mutex_unlock(&crypt_stat->cs_mutex);
-	return rc;
-}
-
 static int ecryptfs_mmap(struct file *file, struct vm_area_struct *vma)
 {
 	struct file *lower_file = ecryptfs_file_to_lower(file);
@@ -232,7 +190,7 @@ static int ecryptfs_open(struct inode *inode, struct file *file)
 	}
 	ecryptfs_set_file_lower(
 		file, ecryptfs_inode_to_private(inode)->lower_file);
-	rc = read_or_initialize_metadata(ecryptfs_dentry);
+	rc = ecryptfs_read_or_initialize_metadata(ecryptfs_dentry);
 	if (rc)
 		goto out_put;
 	ecryptfs_printk(KERN_DEBUG, "inode w/ addr = [0x%p], i_ino = "
diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index 16d50dface59a..bdeeb89d1dc95 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -916,7 +916,7 @@ static int ecryptfs_setattr(struct user_namespace *mnt_userns,
 			mutex_unlock(&crypt_stat->cs_mutex);
 			goto out;
 		}
-		rc = ecryptfs_read_metadata(dentry);
+		rc = ecryptfs_read_or_initialize_metadata_locked(dentry);
 		ecryptfs_put_lower_file(inode);
 		if (rc) {
 			if (!(mount_crypt_stat->flags
-- 
2.30.2

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ