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

Commit e4465fda authored by Michael Halcrow's avatar Michael Halcrow Committed by Linus Torvalds
Browse files

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: default avatarMichael Halcrow <mhalcrow@us.ibm.com>
Cc: Benedikt Driessen <bdriessen@escrypt.com>
Cc: <stable@kernel.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 87ffbe67
Loading
Loading
Loading
Loading
+76 −26
Original line number Original line Diff line number Diff line
@@ -263,52 +263,102 @@ static int fill_zeros_to_end_of_page(struct page *page, unsigned int to)
	return 0;
	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,
static int ecryptfs_prepare_write(struct file *file, struct page *page,
				  unsigned from, unsigned to)
				  unsigned from, unsigned to)
{
{
	int rc = 0;
	loff_t prev_page_end_size;
	loff_t prev_page_end_size;
	int rc = 0;


	if (!PageUptodate(page)) {
	if (!PageUptodate(page)) {
		rc = ecryptfs_read_lower_page_segment(page, page->index, 0,
		struct ecryptfs_crypt_stat *crypt_stat =
						      PAGE_CACHE_SIZE,
			&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);
				page->mapping->host);
			if (rc) {
			if (rc) {
			printk(KERN_ERR "%s: Error attemping to read lower "
				printk(KERN_ERR "%s: Error attemping to read "
			       "page segment; rc = [%d]\n", __FUNCTION__, rc);
				       "lower page segment; rc = [%d]\n",
				       __FUNCTION__, rc);
				ClearPageUptodate(page);
				ClearPageUptodate(page);
				goto out;
				goto out;
			} else
			} else
				SetPageUptodate(page);
				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);
	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 (page->index != 0) {
		if (prev_page_end_size > i_size_read(page->mapping->host)) {
		if (prev_page_end_size > i_size_read(page->mapping->host)) {
			rc = ecryptfs_truncate(file->f_path.dentry,
			rc = ecryptfs_truncate(file->f_path.dentry,
					       prev_page_end_size);
					       prev_page_end_size);
			if (rc) {
			if (rc) {
				printk(KERN_ERR "Error on attempt to "
				printk(KERN_ERR "%s: Error on attempt to "
				       "truncate to (higher) offset [%lld];"
				       "truncate to (higher) offset [%lld];"
				       " rc = [%d]\n", prev_page_end_size, rc);
				       " rc = [%d]\n", __FUNCTION__,
				       prev_page_end_size, rc);
				goto out;
				goto out;
			}
			}
		}
		}
	}
	}
	/*
	/* Writing to a new page, and creating a small hole from start
	 * Writing to a new page, and creating a small hole from start of page?
	 * of page?  Zero it out. */
	 * Zero it out.
	if ((i_size_read(page->mapping->host) == prev_page_end_size)
	 */
	    && (from != 0))
	if ((i_size_read(page->mapping->host) == prev_page_end_size) &&
	    (from != 0)) {
		zero_user(page, 0, PAGE_CACHE_SIZE);
		zero_user(page, 0, PAGE_CACHE_SIZE);
	}
out:
out:
	return rc;
	return rc;
}
}