2008-03-02 06:49:23

by Michael Halcrow

[permalink] [raw]
Subject: [PATCH] eCryptfs: Make ecryptfs_prepare_write decrypt the page

When the page is not up to date, ecryptfs_prepare_write() should be
acting much like ecryptfs_readpage(). This includes the painfully
obvious step of actually decrypting the page contents read from the
lower encrypted file.

Note that this patch resolves a bug in eCryptfs in 2.6.24 that one can
produce with these steps:

# mount -t ecryptfs /secret /secret
# echo "abc" > /secret/file.txt
# umount /secret
# mount -t ecryptfs /secret /secret
# echo "def" >> /secret/file.txt
# cat /secret/file.txt

Without this patch, the resulting data returned from cat is likely to
be something other than "abc\ndef\n".

(Thanks to Benedikt Driessen for reporting this.)

Signed-off-by: Michael Halcrow <[email protected]>
---
mmap.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++-----------------
1 file changed, 76 insertions(+), 26 deletions(-)

diff --git a/fs/ecryptfs/mmap.c b/fs/ecryptfs/mmap.c
index dc74b18..6df1deb 100644
--- a/fs/ecryptfs/mmap.c
+++ b/fs/ecryptfs/mmap.c
@@ -263,52 +263,102 @@ out:
return 0;
}

-/* This function must zero any hole we create */
+/**
+ * ecryptfs_prepare_write
+ * @file: The eCryptfs file
+ * @page: The eCryptfs page
+ * @from: The start byte from which we will write
+ * @to: The end byte to which we will write
+ *
+ * This function must zero any hole we create
+ *
+ * Returns zero on success; non-zero otherwise
+ */
static int ecryptfs_prepare_write(struct file *file, struct page *page,
unsigned from, unsigned to)
{
- int rc = 0;
loff_t prev_page_end_size;
+ int rc = 0;

if (!PageUptodate(page)) {
- rc = ecryptfs_read_lower_page_segment(page, page->index, 0,
- PAGE_CACHE_SIZE,
- page->mapping->host);
- if (rc) {
- printk(KERN_ERR "%s: Error attemping to read lower "
- "page segment; rc = [%d]\n", __FUNCTION__, rc);
- ClearPageUptodate(page);
- goto out;
- } else
+ struct ecryptfs_crypt_stat *crypt_stat =
+ &ecryptfs_inode_to_private(
+ file->f_path.dentry->d_inode)->crypt_stat;
+
+ if (!(crypt_stat->flags & ECRYPTFS_ENCRYPTED)
+ || (crypt_stat->flags & ECRYPTFS_NEW_FILE)) {
+ rc = ecryptfs_read_lower_page_segment(
+ page, page->index, 0, PAGE_CACHE_SIZE,
+ page->mapping->host);
+ if (rc) {
+ printk(KERN_ERR "%s: Error attemping to read "
+ "lower page segment; rc = [%d]\n",
+ __FUNCTION__, rc);
+ ClearPageUptodate(page);
+ goto out;
+ } else
+ SetPageUptodate(page);
+ } else if (crypt_stat->flags & ECRYPTFS_VIEW_AS_ENCRYPTED) {
+ if (crypt_stat->flags & ECRYPTFS_METADATA_IN_XATTR) {
+ rc = ecryptfs_copy_up_encrypted_with_header(
+ page, crypt_stat);
+ if (rc) {
+ printk(KERN_ERR "%s: Error attempting "
+ "to copy the encrypted content "
+ "from the lower file whilst "
+ "inserting the metadata from "
+ "the xattr into the header; rc "
+ "= [%d]\n", __FUNCTION__, rc);
+ ClearPageUptodate(page);
+ goto out;
+ }
+ SetPageUptodate(page);
+ } else {
+ rc = ecryptfs_read_lower_page_segment(
+ page, page->index, 0, PAGE_CACHE_SIZE,
+ page->mapping->host);
+ if (rc) {
+ printk(KERN_ERR "%s: Error reading "
+ "page; rc = [%d]\n",
+ __FUNCTION__, rc);
+ ClearPageUptodate(page);
+ goto out;
+ }
+ SetPageUptodate(page);
+ }
+ } else {
+ rc = ecryptfs_decrypt_page(page);
+ if (rc) {
+ printk(KERN_ERR "%s: Error decrypting page "
+ "at index [%ld]; rc = [%d]\n",
+ __FUNCTION__, page->index, rc);
+ ClearPageUptodate(page);
+ goto out;
+ }
SetPageUptodate(page);
+ }
}
-
prev_page_end_size = ((loff_t)page->index << PAGE_CACHE_SHIFT);
-
- /*
- * If creating a page or more of holes, zero them out via truncate.
- * Note, this will increase i_size.
- */
+ /* If creating a page or more of holes, zero them out via truncate.
+ * Note, this will increase i_size. */
if (page->index != 0) {
if (prev_page_end_size > i_size_read(page->mapping->host)) {
rc = ecryptfs_truncate(file->f_path.dentry,
prev_page_end_size);
if (rc) {
- printk(KERN_ERR "Error on attempt to "
+ printk(KERN_ERR "%s: Error on attempt to "
"truncate to (higher) offset [%lld];"
- " rc = [%d]\n", prev_page_end_size, rc);
+ " rc = [%d]\n", __FUNCTION__,
+ prev_page_end_size, rc);
goto out;
}
}
}
- /*
- * Writing to a new page, and creating a small hole from start of page?
- * Zero it out.
- */
- if ((i_size_read(page->mapping->host) == prev_page_end_size) &&
- (from != 0)) {
+ /* Writing to a new page, and creating a small hole from start
+ * of page? Zero it out. */
+ if ((i_size_read(page->mapping->host) == prev_page_end_size)
+ && (from != 0))
zero_user(page, 0, PAGE_CACHE_SIZE);
- }
out:
return rc;
}


2008-03-02 14:07:30

by Josh Boyer

[permalink] [raw]
Subject: Re: [PATCH] eCryptfs: Make ecryptfs_prepare_write decrypt the page

On Sun, 2 Mar 2008 00:44:52 -0600
Michael Halcrow <[email protected]> wrote:

> When the page is not up to date, ecryptfs_prepare_write() should be
> acting much like ecryptfs_readpage(). This includes the painfully
> obvious step of actually decrypting the page contents read from the
> lower encrypted file.
>
> Note that this patch resolves a bug in eCryptfs in 2.6.24 that one can
> produce with these steps:

Should it go in -stable then?

josh

2008-03-02 15:52:17

by Michael Halcrow

[permalink] [raw]
Subject: Re: [PATCH] eCryptfs: Make ecryptfs_prepare_write decrypt the page

On Sun, Mar 02, 2008 at 08:04:52AM -0600, Josh Boyer wrote:
> On Sun, 2 Mar 2008 00:44:52 -0600
> Michael Halcrow <[email protected]> wrote:
>
> > When the page is not up to date, ecryptfs_prepare_write() should be
> > acting much like ecryptfs_readpage(). This includes the painfully
> > obvious step of actually decrypting the page contents read from the
> > lower encrypted file.
> >
> > Note that this patch resolves a bug in eCryptfs in 2.6.24 that one can
> > produce with these steps:
>
> Should it go in -stable then?

Yes; I recommend this fix for a future 2.6.24 release.