2007-01-09 22:21:11

by Michael Halcrow

[permalink] [raw]
Subject: [PATCH 0/3] eCryptfs: Support metadata in xattr

This patch set introduces the ability to store cryptographic metadata
into an lower file extended attribute rather than the lower file
header region.

This patch set implements two new mount options:

ecryptfs_xattr_metadata
- When set, newly created files will have their cryptographic
metadata stored in the extended attribute region of the file rather
than the header.

ecryptfs_encrypted_view
- When set, this option causes eCryptfs to present applications a
view of encrypted files as if the cryptographic metadata were
stored in the file header, whether the metadata is actually stored
in the header or in the extended attributes.


2007-01-09 22:22:05

by Michael Halcrow

[permalink] [raw]
Subject: [PATCH 1/3] eCryptfs: xattr flags and mount options

Add extended attribute support to version bit vector, flags to
indicate when xattr or encrypted view modes are enabled, and support
for the new mount options.

Signed-off-by: Michael Halcrow <[email protected]>

---

fs/ecryptfs/crypto.c | 20 ++++++++++++++++++++
fs/ecryptfs/ecryptfs_kernel.h | 15 ++++++++++-----
fs/ecryptfs/main.c | 18 ++++++++++++++++--
3 files changed, 46 insertions(+), 7 deletions(-)

72a24201af3657aa4e6365b18ba869422fa77cee
diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c
index 33f92d7..c2811b1 100644
--- a/fs/ecryptfs/crypto.c
+++ b/fs/ecryptfs/crypto.c
@@ -917,6 +917,22 @@ static void ecryptfs_generate_new_key(st
}

/**
+ * ecryptfs_copy_mount_wide_flags_to_inode_flags
+ *
+ * This function propagates the mount-wide flags to individual inode
+ * flags.
+ */
+static void ecryptfs_copy_mount_wide_flags_to_inode_flags(
+ struct ecryptfs_crypt_stat *crypt_stat,
+ struct ecryptfs_mount_crypt_stat *mount_crypt_stat)
+{
+ if (mount_crypt_stat->flags & ECRYPTFS_XATTR_METADATA_ENABLED)
+ crypt_stat->flags |= ECRYPTFS_METADATA_IN_XATTR;
+ if (mount_crypt_stat->flags & ECRYPTFS_ENCRYPTED_VIEW_ENABLED)
+ crypt_stat->flags |= ECRYPTFS_VIEW_AS_ENCRYPTED;
+}
+
+/**
* ecryptfs_set_default_crypt_stat_vals
* @crypt_stat
*
@@ -926,6 +942,8 @@ static void ecryptfs_set_default_crypt_s
struct ecryptfs_crypt_stat *crypt_stat,
struct ecryptfs_mount_crypt_stat *mount_crypt_stat)
{
+ ecryptfs_copy_mount_wide_flags_to_inode_flags(crypt_stat,
+ mount_crypt_stat);
ecryptfs_set_default_sizes(crypt_stat);
strcpy(crypt_stat->cipher, ECRYPTFS_DEFAULT_CIPHER);
crypt_stat->key_size = ECRYPTFS_DEFAULT_KEY_BYTES;
@@ -971,6 +989,8 @@ int ecryptfs_new_file_context(struct den
"file using mount_crypt_stat\n");
ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED);
ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_KEY_VALID);
+ ecryptfs_copy_mount_wide_flags_to_inode_flags(crypt_stat,
+ mount_crypt_stat);
memcpy(crypt_stat->keysigs[crypt_stat->num_keysigs++],
mount_crypt_stat->global_auth_tok_sig,
ECRYPTFS_SIG_SIZE_HEX);
diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h
index 51c299a..f74b343 100644
--- a/fs/ecryptfs/ecryptfs_kernel.h
+++ b/fs/ecryptfs/ecryptfs_kernel.h
@@ -43,13 +43,14 @@ #define ECRYPTFS_SUPPORTED_FILE_VERSION
* module; userspace tools such as the mount helper read
* ECRYPTFS_VERSIONING_MASK from a sysfs handle in order to determine
* how to behave. */
-#define ECRYPTFS_VERSIONING_PASSPHRASE 0x00000001
-#define ECRYPTFS_VERSIONING_PUBKEY 0x00000002
+#define ECRYPTFS_VERSIONING_PASSPHRASE 0x00000001
+#define ECRYPTFS_VERSIONING_PUBKEY 0x00000002
#define ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH 0x00000004
-#define ECRYPTFS_VERSIONING_POLICY 0x00000008
+#define ECRYPTFS_VERSIONING_POLICY 0x00000008
+#define ECRYPTFS_VERSIONING_XATTR 0x00000010
#define ECRYPTFS_VERSIONING_MASK (ECRYPTFS_VERSIONING_PASSPHRASE \
- | ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH \
- | ECRYPTFS_VERSIONING_PUBKEY)
+ | ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH \
+ | ECRYPTFS_VERSIONING_PUBKEY)

#define ECRYPTFS_MAX_PASSWORD_LENGTH 64
#define ECRYPTFS_MAX_PASSPHRASE_BYTES ECRYPTFS_MAX_PASSWORD_LENGTH
@@ -228,6 +229,8 @@ #define ECRYPTFS_SECURITY_WARNING 0x00
#define ECRYPTFS_ENABLE_HMAC 0x00000020
#define ECRYPTFS_ENCRYPT_IV_PAGES 0x00000040
#define ECRYPTFS_KEY_VALID 0x00000080
+#define ECRYPTFS_METADATA_IN_XATTR 0x00000100
+#define ECRYPTFS_VIEW_AS_ENCRYPTED 0x00000200
u32 flags;
unsigned int file_version;
size_t iv_bytes;
@@ -274,6 +277,8 @@ struct ecryptfs_dentry_info {
struct ecryptfs_mount_crypt_stat {
/* Pointers to memory we do not own, do not free these */
#define ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED 0x00000001
+#define ECRYPTFS_XATTR_METADATA_ENABLED 0x00000002
+#define ECRYPTFS_ENCRYPTED_VIEW_ENABLED 0x00000004
u32 flags;
struct ecryptfs_auth_tok *global_auth_tok;
struct key *global_auth_tok_key;
diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c
index 87f05c4..a3efdcc 100644
--- a/fs/ecryptfs/main.c
+++ b/fs/ecryptfs/main.c
@@ -162,7 +162,8 @@ out:
enum { ecryptfs_opt_sig, ecryptfs_opt_ecryptfs_sig, ecryptfs_opt_debug,
ecryptfs_opt_ecryptfs_debug, ecryptfs_opt_cipher,
ecryptfs_opt_ecryptfs_cipher, ecryptfs_opt_ecryptfs_key_bytes,
- ecryptfs_opt_passthrough, ecryptfs_opt_err };
+ ecryptfs_opt_passthrough, ecryptfs_opt_xattr_metadata,
+ ecryptfs_opt_encrypted_view, ecryptfs_opt_err };

static match_table_t tokens = {
{ecryptfs_opt_sig, "sig=%s"},
@@ -173,6 +174,8 @@ static match_table_t tokens = {
{ecryptfs_opt_ecryptfs_cipher, "ecryptfs_cipher=%s"},
{ecryptfs_opt_ecryptfs_key_bytes, "ecryptfs_key_bytes=%u"},
{ecryptfs_opt_passthrough, "ecryptfs_passthrough"},
+ {ecryptfs_opt_xattr_metadata, "ecryptfs_xattr_metadata"},
+ {ecryptfs_opt_encrypted_view, "ecryptfs_encrypted_view"},
{ecryptfs_opt_err, NULL}
};

@@ -313,6 +316,16 @@ static int ecryptfs_parse_options(struct
mount_crypt_stat->flags |=
ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED;
break;
+ case ecryptfs_opt_xattr_metadata:
+ mount_crypt_stat->flags |=
+ ECRYPTFS_XATTR_METADATA_ENABLED;
+ break;
+ case ecryptfs_opt_encrypted_view:
+ mount_crypt_stat->flags |=
+ ECRYPTFS_XATTR_METADATA_ENABLED;
+ mount_crypt_stat->flags |=
+ ECRYPTFS_ENCRYPTED_VIEW_ENABLED;
+ break;
case ecryptfs_opt_err:
default:
ecryptfs_printk(KERN_WARNING,
@@ -734,7 +747,8 @@ static struct ecryptfs_version_str_map_e
{ECRYPTFS_VERSIONING_PASSPHRASE, "passphrase"},
{ECRYPTFS_VERSIONING_PUBKEY, "pubkey"},
{ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH, "plaintext passthrough"},
- {ECRYPTFS_VERSIONING_POLICY, "policy"}
+ {ECRYPTFS_VERSIONING_POLICY, "policy"},
+ {ECRYPTFS_VERSIONING_XATTR, "metadata in extended attribute"}
};

static ssize_t version_str_show(struct ecryptfs_obj *obj, char *buff)
--
1.3.3

2007-01-09 22:22:58

by Michael Halcrow

[permalink] [raw]
Subject: [PATCH 2/3] eCryptfs: Generalize metadata read/write

Generalize the metadata reading and writing mechanisms, with two
targets for now: metadata in file header and metadata in the
user.ecryptfs xattr of the lower file.

Signed-off-by: Michael Halcrow <[email protected]>

---

fs/ecryptfs/crypto.c | 224 ++++++++++++++++++++++++++++++++---------
fs/ecryptfs/ecryptfs_kernel.h | 37 +++++--
fs/ecryptfs/file.c | 27 +----
fs/ecryptfs/inode.c | 61 ++++++-----
fs/ecryptfs/main.c | 7 +
fs/ecryptfs/mmap.c | 96 ++++++++++++++++--
6 files changed, 335 insertions(+), 117 deletions(-)

6d11a3df119acb8438e2629d9ac656309ac2a61f
diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c
index c2811b1..e7f5441 100644
--- a/fs/ecryptfs/crypto.c
+++ b/fs/ecryptfs/crypto.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 1997-2004 Erez Zadok
* Copyright (C) 2001-2004 Stony Brook University
- * Copyright (C) 2004-2006 International Business Machines Corp.
+ * Copyright (C) 2004-2007 International Business Machines Corp.
* Author(s): Michael A. Halcrow <[email protected]>
* Michael C. Thompson <[email protected]>
*
@@ -865,7 +865,10 @@ void ecryptfs_set_default_sizes(struct e
ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE;
} else
crypt_stat->header_extent_size = PAGE_CACHE_SIZE;
- crypt_stat->num_header_extents_at_front = 1;
+ if (crypt_stat->flags & ECRYPTFS_METADATA_IN_XATTR)
+ crypt_stat->num_header_extents_at_front = 0;
+ else
+ crypt_stat->num_header_extents_at_front = 1;
}

/**
@@ -1049,7 +1052,8 @@ struct ecryptfs_flag_map_elem {
/* Add support for additional flags by adding elements here. */
static struct ecryptfs_flag_map_elem ecryptfs_flag_map[] = {
{0x00000001, ECRYPTFS_ENABLE_HMAC},
- {0x00000002, ECRYPTFS_ENCRYPTED}
+ {0x00000002, ECRYPTFS_ENCRYPTED},
+ {0x00000004, ECRYPTFS_METADATA_IN_XATTR}
};

/**
@@ -1239,6 +1243,21 @@ out:
return rc;
}

+int ecryptfs_read_and_validate_header_region(char *data, struct dentry *dentry,
+ struct vfsmount *mnt)
+{
+ int rc;
+
+ rc = ecryptfs_read_header_region(data, dentry, mnt);
+ if (rc)
+ goto out;
+ if (!contains_ecryptfs_marker(data + ECRYPTFS_FILE_SIZE_BYTES))
+ rc = -EINVAL;
+out:
+ return rc;
+}
+
+
static void
write_header_metadata(char *virt, struct ecryptfs_crypt_stat *crypt_stat,
size_t *written)
@@ -1290,7 +1309,7 @@ struct kmem_cache *ecryptfs_header_cache
*
* Returns zero on success
*/
-int ecryptfs_write_headers_virt(char *page_virt,
+int ecryptfs_write_headers_virt(char *page_virt, size_t *size,
struct ecryptfs_crypt_stat *crypt_stat,
struct dentry *ecryptfs_dentry)
{
@@ -1311,11 +1330,53 @@ int ecryptfs_write_headers_virt(char *pa
if (rc)
ecryptfs_printk(KERN_WARNING, "Error generating key packet "
"set; rc = [%d]\n", rc);
+ if (size) {
+ offset += written;
+ *size = offset;
+ }
+ return rc;
+}
+
+int ecryptfs_write_metadata_to_contents(struct ecryptfs_crypt_stat *crypt_stat,
+ struct file *lower_file,
+ char *page_virt)
+{
+ mm_segment_t oldfs;
+ int current_header_page;
+ int header_pages;
+
+ lower_file->f_pos = 0;
+ oldfs = get_fs();
+ set_fs(get_ds());
+ lower_file->f_op->write(lower_file, (char __user *)page_virt,
+ PAGE_CACHE_SIZE, &lower_file->f_pos);
+ header_pages = ((crypt_stat->header_extent_size
+ * crypt_stat->num_header_extents_at_front)
+ / PAGE_CACHE_SIZE);
+ memset(page_virt, 0, PAGE_CACHE_SIZE);
+ current_header_page = 1;
+ while (current_header_page < header_pages) {
+ lower_file->f_op->write(lower_file, (char __user *)page_virt,
+ PAGE_CACHE_SIZE, &lower_file->f_pos);
+ current_header_page++;
+ }
+ set_fs(oldfs);
+ return 0;
+}
+
+int ecryptfs_write_metadata_to_xattr(struct dentry *ecryptfs_dentry,
+ struct ecryptfs_crypt_stat *crypt_stat,
+ char *page_virt, size_t size)
+{
+ int rc;
+
+ rc = ecryptfs_setxattr(ecryptfs_dentry, ECRYPTFS_XATTR_NAME, page_virt,
+ size, 0);
return rc;
}

/**
- * ecryptfs_write_headers
+ * ecryptfs_write_metadata
* @lower_file: The lower file struct, which was returned from dentry_open
*
* Write the file headers out. This will likely involve a userspace
@@ -1326,14 +1387,12 @@ int ecryptfs_write_headers_virt(char *pa
*
* Returns zero on success; non-zero on error
*/
-int ecryptfs_write_headers(struct dentry *ecryptfs_dentry,
- struct file *lower_file)
+int ecryptfs_write_metadata(struct dentry *ecryptfs_dentry,
+ struct file *lower_file)
{
- mm_segment_t oldfs;
struct ecryptfs_crypt_stat *crypt_stat;
char *page_virt;
- int current_header_page;
- int header_pages;
+ size_t size;
int rc = 0;

crypt_stat = &ecryptfs_inode_to_private(
@@ -1360,48 +1419,36 @@ int ecryptfs_write_headers(struct dentry
rc = -ENOMEM;
goto out;
}
-
- rc = ecryptfs_write_headers_virt(page_virt, crypt_stat,
- ecryptfs_dentry);
+ rc = ecryptfs_write_headers_virt(page_virt, &size, crypt_stat,
+ ecryptfs_dentry);
if (unlikely(rc)) {
ecryptfs_printk(KERN_ERR, "Error whilst writing headers\n");
memset(page_virt, 0, PAGE_CACHE_SIZE);
goto out_free;
}
- ecryptfs_printk(KERN_DEBUG,
- "Writing key packet set to underlying file\n");
- lower_file->f_pos = 0;
- oldfs = get_fs();
- set_fs(get_ds());
- ecryptfs_printk(KERN_DEBUG, "Calling lower_file->f_op->"
- "write() w/ header page; lower_file->f_pos = "
- "[0x%.16x]\n", lower_file->f_pos);
- lower_file->f_op->write(lower_file, (char __user *)page_virt,
- PAGE_CACHE_SIZE, &lower_file->f_pos);
- header_pages = ((crypt_stat->header_extent_size
- * crypt_stat->num_header_extents_at_front)
- / PAGE_CACHE_SIZE);
- memset(page_virt, 0, PAGE_CACHE_SIZE);
- current_header_page = 1;
- while (current_header_page < header_pages) {
- ecryptfs_printk(KERN_DEBUG, "Calling lower_file->f_op->"
- "write() w/ zero'd page; lower_file->f_pos = "
- "[0x%.16x]\n", lower_file->f_pos);
- lower_file->f_op->write(lower_file, (char __user *)page_virt,
- PAGE_CACHE_SIZE, &lower_file->f_pos);
- current_header_page++;
+ if (crypt_stat->flags & ECRYPTFS_METADATA_IN_XATTR)
+ rc = ecryptfs_write_metadata_to_xattr(ecryptfs_dentry,
+ crypt_stat, page_virt,
+ size);
+ else
+ rc = ecryptfs_write_metadata_to_contents(crypt_stat, lower_file,
+ page_virt);
+ if (rc) {
+ printk(KERN_ERR "Error writing metadata out to lower file; "
+ "rc = [%d]\n", rc);
+ goto out_free;
}
- set_fs(oldfs);
- ecryptfs_printk(KERN_DEBUG,
- "Done writing key packet set to underlying file.\n");
out_free:
kmem_cache_free(ecryptfs_header_cache_0, page_virt);
out:
return rc;
}

+#define ECRYPTFS_DONT_VALIDATE_HEADER_SIZE 0
+#define ECRYPTFS_VALIDATE_HEADER_SIZE 1
static int parse_header_metadata(struct ecryptfs_crypt_stat *crypt_stat,
- char *virt, int *bytes_read)
+ char *virt, int *bytes_read,
+ int validate_header_size)
{
int rc = 0;
u32 header_extent_size;
@@ -1416,9 +1463,10 @@ static int parse_header_metadata(struct
crypt_stat->num_header_extents_at_front =
(int)num_header_extents_at_front;
(*bytes_read) = 6;
- if ((crypt_stat->header_extent_size
- * crypt_stat->num_header_extents_at_front)
- < ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE) {
+ if ((validate_header_size == ECRYPTFS_VALIDATE_HEADER_SIZE)
+ && ((crypt_stat->header_extent_size
+ * crypt_stat->num_header_extents_at_front)
+ < ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE)) {
rc = -EINVAL;
ecryptfs_printk(KERN_WARNING, "Invalid header extent size: "
"[%d]\n", crypt_stat->header_extent_size);
@@ -1449,7 +1497,8 @@ static void set_default_header_data(stru
*/
static int ecryptfs_read_headers_virt(char *page_virt,
struct ecryptfs_crypt_stat *crypt_stat,
- struct dentry *ecryptfs_dentry)
+ struct dentry *ecryptfs_dentry,
+ int validate_header_size)
{
int rc = 0;
int offset;
@@ -1483,7 +1532,7 @@ static int ecryptfs_read_headers_virt(ch
offset += bytes_read;
if (crypt_stat->file_version >= 1) {
rc = parse_header_metadata(crypt_stat, (page_virt + offset),
- &bytes_read);
+ &bytes_read, validate_header_size);
if (rc) {
ecryptfs_printk(KERN_WARNING, "Error reading header "
"metadata; rc = [%d]\n", rc);
@@ -1498,12 +1547,60 @@ out:
}

/**
- * ecryptfs_read_headers
+ * ecryptfs_read_xattr_region
+ *
+ * Attempts to read the crypto metadata from the extended attribute
+ * region of the lower file.
+ */
+int ecryptfs_read_xattr_region(char *page_virt, struct dentry *ecryptfs_dentry)
+{
+ ssize_t size;
+ int rc = 0;
+
+ size = ecryptfs_getxattr(ecryptfs_dentry, ECRYPTFS_XATTR_NAME,
+ page_virt, ECRYPTFS_DEFAULT_EXTENT_SIZE);
+ if (size < 0) {
+ printk(KERN_DEBUG "Error attempting to read the [%s] "
+ "xattr from the lower file; return value = [%d]\n",
+ ECRYPTFS_XATTR_NAME, size);
+ rc = -EINVAL;
+ goto out;
+ }
+out:
+ return rc;
+}
+
+int ecryptfs_read_and_validate_xattr_region(char *page_virt,
+ struct dentry *ecryptfs_dentry)
+{
+ int rc;
+
+ rc = ecryptfs_read_xattr_region(page_virt, ecryptfs_dentry);
+ if (rc)
+ goto out;
+ if (!contains_ecryptfs_marker(page_virt + ECRYPTFS_FILE_SIZE_BYTES)) {
+ printk(KERN_WARNING "Valid data found in [%s] xattr, but "
+ "the marker is invalid\n", ECRYPTFS_XATTR_NAME);
+ rc = -EINVAL;
+ }
+out:
+ return rc;
+}
+
+/**
+ * ecryptfs_read_metadata
+ *
+ * Common entry point for reading file metadata. From here, we could
+ * retrieve the header information from the header region of the file,
+ * the xattr region of the file, or some other repostory that is
+ * stored separately from the file itself. The current implementation
+ * supports retrieving the metadata information from the file contents
+ * and from the xattr region.
*
* Returns zero if valid headers found and parsed; non-zero otherwise
*/
-int ecryptfs_read_headers(struct dentry *ecryptfs_dentry,
- struct file *lower_file)
+int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry,
+ struct file *lower_file)
{
int rc = 0;
char *page_virt = NULL;
@@ -1532,11 +1629,36 @@ int ecryptfs_read_headers(struct dentry
goto out;
}
rc = ecryptfs_read_headers_virt(page_virt, crypt_stat,
- ecryptfs_dentry);
+ ecryptfs_dentry,
+ ECRYPTFS_VALIDATE_HEADER_SIZE);
if (rc) {
- ecryptfs_printk(KERN_DEBUG, "Valid eCryptfs headers not "
- "found\n");
- rc = -EINVAL;
+ rc = ecryptfs_read_xattr_region(page_virt,
+ ecryptfs_dentry);
+ if (rc) {
+ printk(KERN_DEBUG "Valid eCryptfs headers not found in "
+ "file header region or xattr region\n");
+ rc = -EINVAL;
+ goto out;
+ }
+ rc = ecryptfs_read_headers_virt(page_virt, crypt_stat,
+ ecryptfs_dentry,
+ ECRYPTFS_DONT_VALIDATE_HEADER_SIZE);
+ if (rc) {
+ printk(KERN_DEBUG "Valid eCryptfs headers not found in "
+ "file xattr region either\n");
+ rc = -EINVAL;
+ }
+ if (crypt_stat->mount_crypt_stat->flags
+ & ECRYPTFS_XATTR_METADATA_ENABLED) {
+ crypt_stat->flags |= ECRYPTFS_METADATA_IN_XATTR;
+ } else {
+ printk(KERN_WARNING "Attempt to access file with "
+ "crypto metadata only in the extended attribute "
+ "region, but eCryptfs was mounted without "
+ "xattr support enabled. eCryptfs will not treat "
+ "this like an encrypted file.\n");
+ rc = -EINVAL;
+ }
}
out:
if (page_virt) {
diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h
index f74b343..aecca2f 100644
--- a/fs/ecryptfs/ecryptfs_kernel.h
+++ b/fs/ecryptfs/ecryptfs_kernel.h
@@ -4,7 +4,7 @@
*
* Copyright (C) 1997-2003 Erez Zadok
* Copyright (C) 2001-2003 Stony Brook University
- * Copyright (C) 2004-2006 International Business Machines Corp.
+ * Copyright (C) 2004-2007 International Business Machines Corp.
* Author(s): Michael A. Halcrow <[email protected]>
* Trevor S. Highland <[email protected]>
* Tyler Hicks <[email protected]>
@@ -50,8 +50,8 @@ #define ECRYPTFS_VERSIONING_POLICY
#define ECRYPTFS_VERSIONING_XATTR 0x00000010
#define ECRYPTFS_VERSIONING_MASK (ECRYPTFS_VERSIONING_PASSPHRASE \
| ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH \
- | ECRYPTFS_VERSIONING_PUBKEY)
-
+ | ECRYPTFS_VERSIONING_PUBKEY \
+ | ECRYPTFS_VERSIONING_XATTR)
#define ECRYPTFS_MAX_PASSWORD_LENGTH 64
#define ECRYPTFS_MAX_PASSPHRASE_BYTES ECRYPTFS_MAX_PASSWORD_LENGTH
#define ECRYPTFS_SALT_SIZE 8
@@ -83,6 +83,7 @@ #define ECRYPTFS_TRANSPORT_NETLINK 0
#define ECRYPTFS_TRANSPORT_CONNECTOR 1
#define ECRYPTFS_TRANSPORT_RELAYFS 2
#define ECRYPTFS_DEFAULT_TRANSPORT ECRYPTFS_TRANSPORT_NETLINK
+#define ECRYPTFS_XATTR_NAME "user.ecryptfs"

#define RFC2440_CIPHER_DES3_EDE 0x02
#define RFC2440_CIPHER_CAST_5 0x03
@@ -480,6 +481,7 @@ extern struct kmem_cache *ecryptfs_sb_in
extern struct kmem_cache *ecryptfs_header_cache_0;
extern struct kmem_cache *ecryptfs_header_cache_1;
extern struct kmem_cache *ecryptfs_header_cache_2;
+extern struct kmem_cache *ecryptfs_xattr_cache;
extern struct kmem_cache *ecryptfs_lower_page_cache;

int ecryptfs_interpose(struct dentry *hidden_dentry,
@@ -506,9 +508,13 @@ int ecryptfs_init_crypt_ctx(struct ecryp
int ecryptfs_crypto_api_algify_cipher_name(char **algified_name,
char *cipher_name,
char *chaining_modifier);
-int ecryptfs_write_inode_size_to_header(struct file *lower_file,
- struct inode *lower_inode,
- struct inode *inode);
+#define ECRYPTFS_LOWER_I_MUTEX_NOT_HELD 0
+#define ECRYPTFS_LOWER_I_MUTEX_HELD 1
+int ecryptfs_write_inode_size_to_metadata(struct file *lower_file,
+ struct inode *lower_inode,
+ struct inode *inode,
+ struct dentry *ecryptfs_dentry,
+ int lower_i_mutex_held);
int ecryptfs_get_lower_page(struct page **lower_page, struct inode *lower_inode,
struct file *lower_file,
unsigned long lower_page_index, int byte_offset,
@@ -530,17 +536,21 @@ int ecryptfs_writepage_and_release_lower
struct writeback_control *wbc);
int ecryptfs_encrypt_page(struct ecryptfs_page_crypt_context *ctx);
int ecryptfs_decrypt_page(struct file *file, struct page *page);
-int ecryptfs_write_headers(struct dentry *ecryptfs_dentry,
- struct file *lower_file);
-int ecryptfs_write_headers_virt(char *page_virt,
+int ecryptfs_write_metadata(struct dentry *ecryptfs_dentry,
+ struct file *lower_file);
+int ecryptfs_write_headers_virt(char *page_virt, size_t *size,
struct ecryptfs_crypt_stat *crypt_stat,
struct dentry *ecryptfs_dentry);
-int ecryptfs_read_headers(struct dentry *ecryptfs_dentry,
- struct file *lower_file);
+int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry,
+ struct file *lower_file);
int ecryptfs_new_file_context(struct dentry *ecryptfs_dentry);
int contains_ecryptfs_marker(char *data);
int ecryptfs_read_header_region(char *data, struct dentry *dentry,
struct vfsmount *mnt);
+int ecryptfs_read_and_validate_header_region(char *data, struct dentry *dentry,
+ struct vfsmount *mnt);
+int ecryptfs_read_and_validate_xattr_region(char *page_virt,
+ struct dentry *ecryptfs_dentry);
u16 ecryptfs_code_for_cipher_string(struct ecryptfs_crypt_stat *crypt_stat);
int ecryptfs_cipher_code_to_string(char *str, u16 cipher_code);
void ecryptfs_set_default_sizes(struct ecryptfs_crypt_stat *crypt_stat);
@@ -563,6 +573,11 @@ int ecryptfs_open_lower_file(struct file
struct dentry *lower_dentry,
struct vfsmount *lower_mnt, int flags);
int ecryptfs_close_lower_file(struct file *lower_file);
+ssize_t ecryptfs_getxattr(struct dentry *dentry, const char *name, void *value,
+ size_t size);
+int
+ecryptfs_setxattr(struct dentry *dentry, const char *name, const void *value,
+ size_t size, int flags);

int ecryptfs_process_helo(unsigned int transport, uid_t uid, pid_t pid);
int ecryptfs_process_quit(uid_t uid, pid_t pid);
diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c
index 779c347..f22c3a7 100644
--- a/fs/ecryptfs/file.c
+++ b/fs/ecryptfs/file.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 1997-2004 Erez Zadok
* Copyright (C) 2001-2004 Stony Brook University
- * Copyright (C) 2004-2006 International Business Machines Corp.
+ * Copyright (C) 2004-2007 International Business Machines Corp.
* Author(s): Michael A. Halcrow <[email protected]>
* Michael C. Thompson <[email protected]>
*
@@ -293,26 +293,11 @@ static int ecryptfs_open(struct inode *i
goto out;
}
mutex_lock(&crypt_stat->cs_mutex);
- if (i_size_read(lower_inode) < ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE) {
- if (!(mount_crypt_stat->flags
- & ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED)) {
- rc = -EIO;
- printk(KERN_WARNING "Attempt to read file that is "
- "not in a valid eCryptfs format, and plaintext "
- "passthrough mode is not enabled; returning "
- "-EIO\n");
- mutex_unlock(&crypt_stat->cs_mutex);
- goto out_puts;
- }
- crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED);
- rc = 0;
- mutex_unlock(&crypt_stat->cs_mutex);
- goto out;
- } else if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags,
- ECRYPTFS_POLICY_APPLIED)
- || !ECRYPTFS_CHECK_FLAG(crypt_stat->flags,
- ECRYPTFS_KEY_VALID)) {
- rc = ecryptfs_read_headers(ecryptfs_dentry, lower_file);
+ if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags,
+ ECRYPTFS_POLICY_APPLIED)
+ || !ECRYPTFS_CHECK_FLAG(crypt_stat->flags,
+ ECRYPTFS_KEY_VALID)) {
+ rc = ecryptfs_read_metadata(ecryptfs_dentry, lower_file);
if (rc) {
ecryptfs_printk(KERN_DEBUG,
"Valid headers not found\n");
diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index d4f02f3..6b45b29 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 1997-2004 Erez Zadok
* Copyright (C) 2001-2004 Stony Brook University
- * Copyright (C) 2004-2006 International Business Machines Corp.
+ * Copyright (C) 2004-2007 International Business Machines Corp.
* Author(s): Michael A. Halcrow <[email protected]>
* Michael C. Thompsion <[email protected]>
*
@@ -169,7 +169,9 @@ static int grow_file(struct dentry *ecry
goto out;
}
i_size_write(inode, 0);
- ecryptfs_write_inode_size_to_header(lower_file, lower_inode, inode);
+ ecryptfs_write_inode_size_to_metadata(lower_file, lower_inode, inode,
+ ecryptfs_dentry,
+ ECRYPTFS_LOWER_I_MUTEX_NOT_HELD);
ECRYPTFS_SET_FLAG(ecryptfs_inode_to_private(inode)->crypt_stat.flags,
ECRYPTFS_NEW_FILE);
out:
@@ -225,7 +227,7 @@ #endif
"context\n");
goto out_fput;
}
- rc = ecryptfs_write_headers(ecryptfs_dentry, lower_file);
+ rc = ecryptfs_write_metadata(ecryptfs_dentry, lower_file);
if (rc) {
ecryptfs_printk(KERN_DEBUG, "Error writing headers\n");
goto out_fput;
@@ -362,32 +364,33 @@ static struct dentry *ecryptfs_lookup(st
}
/* Released in this function */
page_virt = kmem_cache_zalloc(ecryptfs_header_cache_2,
- GFP_USER);
+ GFP_USER);
if (!page_virt) {
rc = -ENOMEM;
ecryptfs_printk(KERN_ERR,
"Cannot ecryptfs_kmalloc a page\n");
goto out_dput;
}
-
- rc = ecryptfs_read_header_region(page_virt, lower_dentry, nd->mnt);
crypt_stat = &ecryptfs_inode_to_private(dentry->d_inode)->crypt_stat;
if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_POLICY_APPLIED))
ecryptfs_set_default_sizes(crypt_stat);
+ rc = ecryptfs_read_and_validate_header_region(page_virt, lower_dentry,
+ nd->mnt);
if (rc) {
- rc = 0;
- ecryptfs_printk(KERN_WARNING, "Error reading header region;"
- " assuming unencrypted\n");
- } else {
- if (!contains_ecryptfs_marker(page_virt
- + ECRYPTFS_FILE_SIZE_BYTES)) {
+ rc = ecryptfs_read_and_validate_xattr_region(page_virt, dentry);
+ if (rc) {
+ printk(KERN_DEBUG "Valid metadata not found in header "
+ "region or xattr region; treating file as "
+ "unencrypted\n");
+ rc = 0;
kmem_cache_free(ecryptfs_header_cache_2, page_virt);
goto out;
}
- memcpy(&file_size, page_virt, sizeof(file_size));
- file_size = be64_to_cpu(file_size);
- i_size_write(dentry->d_inode, (loff_t)file_size);
+ crypt_stat->flags |= ECRYPTFS_METADATA_IN_XATTR;
}
+ memcpy(&file_size, page_virt, sizeof(file_size));
+ file_size = be64_to_cpu(file_size);
+ i_size_write(dentry->d_inode, (loff_t)file_size);
kmem_cache_free(ecryptfs_header_cache_2, page_virt);
goto out;

@@ -781,20 +784,26 @@ int ecryptfs_truncate(struct dentry *den
goto out_fput;
}
i_size_write(inode, new_length);
- rc = ecryptfs_write_inode_size_to_header(lower_file,
- lower_dentry->d_inode,
- inode);
+ rc = ecryptfs_write_inode_size_to_metadata(
+ lower_file, lower_dentry->d_inode, inode, dentry,
+ ECRYPTFS_LOWER_I_MUTEX_NOT_HELD);
if (rc) {
- ecryptfs_printk(KERN_ERR,
- "Problem with ecryptfs_write"
- "_inode_size\n");
+ printk(KERN_ERR "Problem with "
+ "ecryptfs_write_inode_size_to_metadata; "
+ "rc = [%d]\n", rc);
goto out_fput;
}
} else { /* new_length < i_size_read(inode) */
vmtruncate(inode, new_length);
- ecryptfs_write_inode_size_to_header(lower_file,
- lower_dentry->d_inode,
- inode);
+ rc = ecryptfs_write_inode_size_to_metadata(
+ lower_file, lower_dentry->d_inode, inode, dentry,
+ ECRYPTFS_LOWER_I_MUTEX_NOT_HELD);
+ if (rc) {
+ printk(KERN_ERR "Problem with "
+ "ecryptfs_write_inode_size_to_metadata; "
+ "rc = [%d]\n", rc);
+ goto out_fput;
+ }
/* We are reducing the size of the ecryptfs file, and need to
* know if we need to reduce the size of the lower file. */
lower_size_before_truncate =
@@ -881,7 +890,7 @@ out:
return rc;
}

-static int
+int
ecryptfs_setxattr(struct dentry *dentry, const char *name, const void *value,
size_t size, int flags)
{
@@ -901,7 +910,7 @@ out:
return rc;
}

-static ssize_t
+ssize_t
ecryptfs_getxattr(struct dentry *dentry, const char *name, void *value,
size_t size)
{
diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c
index a3efdcc..26fe405 100644
--- a/fs/ecryptfs/main.c
+++ b/fs/ecryptfs/main.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 1997-2003 Erez Zadok
* Copyright (C) 2001-2003 Stony Brook University
- * Copyright (C) 2004-2006 International Business Machines Corp.
+ * Copyright (C) 2004-2007 International Business Machines Corp.
* Author(s): Michael A. Halcrow <[email protected]>
* Michael C. Thompson <[email protected]>
* Tyler Hicks <[email protected]>
@@ -642,6 +642,11 @@ static struct ecryptfs_cache_info {
.size = PAGE_CACHE_SIZE,
},
{
+ .cache = &ecryptfs_xattr_cache,
+ .name = "ecryptfs_xattr_cache",
+ .size = PAGE_CACHE_SIZE,
+ },
+ {
.cache = &ecryptfs_lower_page_cache,
.name = "ecryptfs_lower_page_cache",
.size = PAGE_CACHE_SIZE,
diff --git a/fs/ecryptfs/mmap.c b/fs/ecryptfs/mmap.c
index 0af3aa3..ba3650d 100644
--- a/fs/ecryptfs/mmap.c
+++ b/fs/ecryptfs/mmap.c
@@ -6,7 +6,7 @@
*
* Copyright (C) 1997-2003 Erez Zadok
* Copyright (C) 2001-2003 Stony Brook University
- * Copyright (C) 2004-2006 International Business Machines Corp.
+ * Copyright (C) 2004-2007 International Business Machines Corp.
* Author(s): Michael A. Halcrow <[email protected]>
*
* This program is free software; you can redistribute it and/or
@@ -308,6 +308,9 @@ out:
return rc;
}

+/**
+ * Called with lower inode mutex held.
+ */
static int fill_zeros_to_end_of_page(struct page *page, unsigned int to)
{
struct inode *inode = page->mapping->host;
@@ -407,10 +410,9 @@ static void ecryptfs_unmap_and_release_l
*
* Returns zero on success; non-zero on error.
*/
-int
-ecryptfs_write_inode_size_to_header(struct file *lower_file,
- struct inode *lower_inode,
- struct inode *inode)
+static int ecryptfs_write_inode_size_to_header(struct file *lower_file,
+ struct inode *lower_inode,
+ struct inode *inode)
{
int rc = 0;
struct page *header_page;
@@ -442,6 +444,80 @@ out:
return rc;
}

+static int ecryptfs_write_inode_size_to_xattr(struct inode *lower_inode,
+ struct inode *inode,
+ struct dentry *ecryptfs_dentry,
+ int lower_i_mutex_held)
+{
+ ssize_t size;
+ void *xattr_virt;
+ struct dentry *lower_dentry;
+ u64 file_size;
+ int rc;
+
+ xattr_virt = kmem_cache_alloc(ecryptfs_xattr_cache, GFP_KERNEL);
+ if (!xattr_virt) {
+ printk(KERN_ERR "Out of memory whilst attempting to write "
+ "inode size to xattr\n");
+ rc = -ENOMEM;
+ goto out;
+ }
+ lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry);
+ if (!lower_dentry->d_inode->i_op->getxattr) {
+ printk(KERN_WARNING
+ "No support for setting xattr in lower filesystem\n");
+ rc = -ENOSYS;
+ kmem_cache_free(ecryptfs_xattr_cache, xattr_virt);
+ goto out;
+ }
+ if (!lower_i_mutex_held)
+ mutex_lock(&lower_dentry->d_inode->i_mutex);
+ size = lower_dentry->d_inode->i_op->getxattr(lower_dentry,
+ ECRYPTFS_XATTR_NAME,
+ xattr_virt,
+ PAGE_CACHE_SIZE);
+ if (!lower_i_mutex_held)
+ mutex_unlock(&lower_dentry->d_inode->i_mutex);
+ if (size < 0)
+ size = 8;
+ file_size = (u64)i_size_read(inode);
+ file_size = cpu_to_be64(file_size);
+ memcpy(xattr_virt, &file_size, sizeof(u64));
+ if (!lower_i_mutex_held)
+ mutex_lock(&lower_dentry->d_inode->i_mutex);
+ rc = lower_dentry->d_inode->i_op->setxattr(lower_dentry,
+ ECRYPTFS_XATTR_NAME,
+ xattr_virt, size, 0);
+ if (!lower_i_mutex_held)
+ mutex_unlock(&lower_dentry->d_inode->i_mutex);
+ if (rc)
+ printk(KERN_ERR "Error whilst attempting to write inode size "
+ "to lower file xattr; rc = [%d]\n", rc);
+ kmem_cache_free(ecryptfs_xattr_cache, xattr_virt);
+out:
+ return rc;
+}
+
+int
+ecryptfs_write_inode_size_to_metadata(struct file *lower_file,
+ struct inode *lower_inode,
+ struct inode *inode,
+ struct dentry *ecryptfs_dentry,
+ int lower_i_mutex_held)
+{
+ struct ecryptfs_crypt_stat *crypt_stat;
+
+ crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat;
+ if (crypt_stat->flags & ECRYPTFS_METADATA_IN_XATTR)
+ return ecryptfs_write_inode_size_to_xattr(lower_inode, inode,
+ ecryptfs_dentry,
+ lower_i_mutex_held);
+ else
+ return ecryptfs_write_inode_size_to_header(lower_file,
+ lower_inode,
+ inode);
+}
+
int ecryptfs_get_lower_page(struct page **lower_page, struct inode *lower_inode,
struct file *lower_file,
unsigned long lower_page_index, int byte_offset,
@@ -528,6 +604,8 @@ out:
return rc;
}

+struct kmem_cache *ecryptfs_xattr_cache;
+
/**
* ecryptfs_commit_write
* @file: The eCryptfs file object
@@ -581,7 +659,6 @@ static int ecryptfs_commit_write(struct
"index [0x%.16x])\n", page->index);
goto out;
}
- rc = 0;
inode->i_blocks = lower_inode->i_blocks;
pos = (page->index << PAGE_CACHE_SHIFT) + to;
if (pos > i_size_read(inode)) {
@@ -589,7 +666,12 @@ static int ecryptfs_commit_write(struct
ecryptfs_printk(KERN_DEBUG, "Expanded file size to "
"[0x%.16x]\n", i_size_read(inode));
}
- ecryptfs_write_inode_size_to_header(lower_file, lower_inode, inode);
+ rc = ecryptfs_write_inode_size_to_metadata(lower_file, lower_inode,
+ inode, file->f_dentry,
+ ECRYPTFS_LOWER_I_MUTEX_HELD);
+ if (rc)
+ printk(KERN_ERR "Error writing inode size to metadata; "
+ "rc = [%d]\n", rc);
lower_inode->i_mtime = lower_inode->i_ctime = CURRENT_TIME;
mark_inode_dirty_sync(inode);
out:
--
1.3.3

2007-01-09 22:23:40

by Michael Halcrow

[permalink] [raw]
Subject: [PATCH 3/3] eCryptfs: Encrypted passthrough

Provide an option to provide a view of the encrypted files such that
the metadata is always in the header of the files, regardless of
whether the metadata is actually in the header or in the extended
attribute. This mode of operation is useful for applications like
incremental backup utilities that do not preserve the extended
attributes when directly accessing the lower files.

With this option enabled, the files under the eCryptfs mount point
will be read-only.

Signed-off-by: Michael Halcrow <[email protected]>

---

fs/ecryptfs/crypto.c | 15 ++++++--
fs/ecryptfs/ecryptfs_kernel.h | 7 +++-
fs/ecryptfs/file.c | 13 ++++++-
fs/ecryptfs/inode.c | 15 +++++++-
fs/ecryptfs/mmap.c | 74 ++++++++++++++++++++++++++++++++++++++++-
5 files changed, 113 insertions(+), 11 deletions(-)

996f24f59d4bf43b67fe5aa4d4686bd75f09a73e
diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c
index e7f5441..8fbc38c 100644
--- a/fs/ecryptfs/crypto.c
+++ b/fs/ecryptfs/crypto.c
@@ -1258,9 +1258,10 @@ out:
}


-static void
-write_header_metadata(char *virt, struct ecryptfs_crypt_stat *crypt_stat,
- size_t *written)
+void
+ecryptfs_write_header_metadata(char *virt,
+ struct ecryptfs_crypt_stat *crypt_stat,
+ size_t *written)
{
u32 header_extent_size;
u16 num_header_extents_at_front;
@@ -1322,7 +1323,8 @@ int ecryptfs_write_headers_virt(char *pa
offset += written;
write_ecryptfs_flags((page_virt + offset), crypt_stat, &written);
offset += written;
- write_header_metadata((page_virt + offset), crypt_stat, &written);
+ ecryptfs_write_header_metadata((page_virt + offset), crypt_stat,
+ &written);
offset += written;
rc = ecryptfs_generate_key_packet_set((page_virt + offset), crypt_stat,
ecryptfs_dentry, &written,
@@ -1608,7 +1610,12 @@ int ecryptfs_read_metadata(struct dentry
ssize_t bytes_read;
struct ecryptfs_crypt_stat *crypt_stat =
&ecryptfs_inode_to_private(ecryptfs_dentry->d_inode)->crypt_stat;
+ struct ecryptfs_mount_crypt_stat *mount_crypt_stat =
+ &ecryptfs_superblock_to_private(
+ ecryptfs_dentry->d_sb)->mount_crypt_stat;

+ ecryptfs_copy_mount_wide_flags_to_inode_flags(crypt_stat,
+ mount_crypt_stat);
/* Read the first page from the underlying file */
page_virt = kmem_cache_alloc(ecryptfs_header_cache_1, GFP_USER);
if (!page_virt) {
diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h
index aecca2f..859d31b 100644
--- a/fs/ecryptfs/ecryptfs_kernel.h
+++ b/fs/ecryptfs/ecryptfs_kernel.h
@@ -578,7 +578,7 @@ ssize_t ecryptfs_getxattr(struct dentry
int
ecryptfs_setxattr(struct dentry *dentry, const char *name, const void *value,
size_t size, int flags);
-
+int ecryptfs_read_xattr_region(char *page_virt, struct dentry *ecryptfs_dentry);
int ecryptfs_process_helo(unsigned int transport, uid_t uid, pid_t pid);
int ecryptfs_process_quit(uid_t uid, pid_t pid);
int ecryptfs_process_response(struct ecryptfs_message *msg, uid_t uid,
@@ -601,6 +601,9 @@ int ecryptfs_send_connector(char *data,
u16 msg_flags, pid_t daemon_pid);
int ecryptfs_init_connector(void);
void ecryptfs_release_connector(void);
-
+void
+ecryptfs_write_header_metadata(char *virt,
+ struct ecryptfs_crypt_stat *crypt_stat,
+ size_t *written);

#endif /* #ifndef ECRYPTFS_KERNEL_H */
diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c
index f22c3a7..652ed77 100644
--- a/fs/ecryptfs/file.c
+++ b/fs/ecryptfs/file.c
@@ -250,6 +250,17 @@ static int ecryptfs_open(struct inode *i
struct ecryptfs_file_info *file_info;
int lower_flags;

+ mount_crypt_stat = &ecryptfs_superblock_to_private(
+ ecryptfs_dentry->d_sb)->mount_crypt_stat;
+ if ((mount_crypt_stat->flags & ECRYPTFS_ENCRYPTED_VIEW_ENABLED)
+ && ((file->f_flags & O_WRONLY) || (file->f_flags & O_RDWR)
+ || (file->f_flags & O_CREAT) || (file->f_flags & O_TRUNC)
+ || (file->f_flags & O_APPEND))) {
+ printk(KERN_WARNING "Mount has encrypted view enabled; "
+ "files may only be read\n");
+ rc = -EPERM;
+ goto out;
+ }
/* Released in ecryptfs_release or end of function if failure */
file_info = kmem_cache_zalloc(ecryptfs_file_info_cache, GFP_KERNEL);
ecryptfs_set_file_private(file, file_info);
@@ -261,8 +272,6 @@ static int ecryptfs_open(struct inode *i
}
lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry);
crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat;
- mount_crypt_stat = &ecryptfs_superblock_to_private(
- ecryptfs_dentry->d_sb)->mount_crypt_stat;
mutex_lock(&crypt_stat->cs_mutex);
if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_POLICY_APPLIED)) {
ecryptfs_printk(KERN_DEBUG, "Setting flags for stat...\n");
diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index 6b45b29..bbc1b4f 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -289,6 +289,7 @@ static struct dentry *ecryptfs_lookup(st
char *encoded_name;
unsigned int encoded_namelen;
struct ecryptfs_crypt_stat *crypt_stat = NULL;
+ struct ecryptfs_mount_crypt_stat *mount_crypt_stat;
char *page_virt = NULL;
struct inode *lower_inode;
u64 file_size;
@@ -388,8 +389,18 @@ static struct dentry *ecryptfs_lookup(st
}
crypt_stat->flags |= ECRYPTFS_METADATA_IN_XATTR;
}
- memcpy(&file_size, page_virt, sizeof(file_size));
- file_size = be64_to_cpu(file_size);
+ mount_crypt_stat = &ecryptfs_superblock_to_private(
+ dentry->d_sb)->mount_crypt_stat;
+ if (mount_crypt_stat->flags & ECRYPTFS_ENCRYPTED_VIEW_ENABLED) {
+ if (crypt_stat->flags & ECRYPTFS_METADATA_IN_XATTR)
+ file_size = (crypt_stat->header_extent_size
+ + i_size_read(lower_dentry->d_inode));
+ else
+ file_size = i_size_read(lower_dentry->d_inode);
+ } else {
+ memcpy(&file_size, page_virt, sizeof(file_size));
+ file_size = be64_to_cpu(file_size);
+ }
i_size_write(dentry->d_inode, (loff_t)file_size);
kmem_cache_free(ecryptfs_header_cache_2, page_virt);
goto out;
diff --git a/fs/ecryptfs/mmap.c b/fs/ecryptfs/mmap.c
index ba3650d..3386014 100644
--- a/fs/ecryptfs/mmap.c
+++ b/fs/ecryptfs/mmap.c
@@ -260,6 +260,33 @@ out:
ClearPageUptodate(page);
return rc;
}
+/**
+ * Header Extent:
+ * Octets 0-7: Unencrypted file size (big-endian)
+ * Octets 8-15: eCryptfs special marker
+ * Octets 16-19: Flags
+ * Octet 16: File format version number (between 0 and 255)
+ * Octets 17-18: Reserved
+ * Octet 19: Bit 1 (lsb): Reserved
+ * Bit 2: Encrypted?
+ * Bits 3-8: Reserved
+ * Octets 20-23: Header extent size (big-endian)
+ * Octets 24-25: Number of header extents at front of file
+ * (big-endian)
+ * Octet 26: Begin RFC 2440 authentication token packet set
+ */
+static void set_header_info(char *page_virt,
+ struct ecryptfs_crypt_stat *crypt_stat)
+{
+ size_t written;
+ int save_num_header_extents_at_front =
+ crypt_stat->num_header_extents_at_front;
+
+ crypt_stat->num_header_extents_at_front = 1;
+ ecryptfs_write_header_metadata(page_virt + 20, crypt_stat, &written);
+ crypt_stat->num_header_extents_at_front =
+ save_num_header_extents_at_front;
+}

/**
* ecryptfs_readpage
@@ -289,10 +316,55 @@ static int ecryptfs_readpage(struct file
"[%d]\n", rc);
goto out;
}
+ } else if (crypt_stat->flags & ECRYPTFS_VIEW_AS_ENCRYPTED) {
+ if (crypt_stat->flags & ECRYPTFS_METADATA_IN_XATTR) {
+ int num_pages_in_header_region =
+ (crypt_stat->header_extent_size
+ / PAGE_CACHE_SIZE);
+
+ if (page->index < num_pages_in_header_region) {
+ char *page_virt;
+
+ page_virt = (char *)kmap(page);
+ if (!page_virt) {
+ rc = -ENOMEM;
+ printk(KERN_ERR "Error mapping page\n");
+ goto out;
+ }
+ memset(page_virt, 0, PAGE_CACHE_SIZE);
+ if (page->index == 0) {
+ rc = ecryptfs_read_xattr_region(
+ page_virt, file->f_path.dentry);
+ set_header_info(page_virt, crypt_stat);
+ }
+ kunmap(page);
+ if (rc) {
+ printk(KERN_ERR "Error reading xattr "
+ "region\n");
+ goto out;
+ }
+ } else {
+ rc = ecryptfs_do_readpage(
+ file, page,
+ (page->index
+ - num_pages_in_header_region));
+ if (rc) {
+ printk(KERN_ERR "Error reading page; "
+ "rc = [%d]\n", rc);
+ goto out;
+ }
+ }
+ } else {
+ rc = ecryptfs_do_readpage(file, page, page->index);
+ if (rc) {
+ printk(KERN_ERR "Error reading page; rc = "
+ "[%d]\n", rc);
+ goto out;
+ }
+ }
} else {
rc = ecryptfs_decrypt_page(file, page);
if (rc) {
-
ecryptfs_printk(KERN_ERR, "Error decrypting page; "
"rc = [%d]\n", rc);
goto out;
--
1.3.3

2007-01-09 22:35:24

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH 0/3] eCryptfs: Support metadata in xattr

On Tue, 9 Jan 2007 16:21:07 -0600
Michael Halcrow <[email protected]> wrote:

> This patch set introduces the ability to store cryptographic metadata
> into an lower file extended attribute rather than the lower file
> header region.
>
> This patch set implements two new mount options:
>
> ecryptfs_xattr_metadata
> - When set, newly created files will have their cryptographic
> metadata stored in the extended attribute region of the file rather
> than the header.

Why is this useful?

> ecryptfs_encrypted_view
> - When set, this option causes eCryptfs to present applications a
> view of encrypted files as if the cryptographic metadata were
> stored in the file header, whether the metadata is actually stored
> in the header or in the extended attributes.

Sounds kludgy. "This mode of operation is useful for applications like
incremental backup utilities that do not preserve the extended attributes
when directly accessing the lower files.". Wouldn't it be better to lean
on people to use better backup tools, and to fix existing ones?


2007-01-09 22:35:38

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH 2/3] eCryptfs: Generalize metadata read/write

On Tue, 9 Jan 2007 16:22:55 -0600
Michael Halcrow <[email protected]> wrote:

> + lower_file->f_op->write(lower_file, (char __user *)page_virt,
> + PAGE_CACHE_SIZE, &lower_file->f_pos);

hm. sys_write() takes a local copy of f_pos and writes that back into the
struct file. It does this so that two concurrent write() callers don't
make a mess of f_pos, and of the file contents.

Perhaps ecryptfs should be calling vfs_write()?

That way we'd also get the fsnotify notifications, which ecryptfs presently
appears to have subverted.

2007-01-09 22:45:21

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH 3/3] eCryptfs: Encrypted passthrough

On Tue, 9 Jan 2007 16:23:37 -0600
Michael Halcrow <[email protected]> wrote:

> + page_virt = (char *)kmap(page);

Do we _have_ to use kmap here? It's slow and theoretically deadlocky.
kmap_atomic() is much preferred.

Can the other kmap() calls in ecryptfs be converted?

We'd actually like to remove kmap() one day. Not much chance of that, but
it's an objective.

> + if (!page_virt) {
> + rc = -ENOMEM;
> + printk(KERN_ERR "Error mapping page\n");
> + goto out;
> + }
> + memset(page_virt, 0, PAGE_CACHE_SIZE);
> + if (page->index == 0) {
> + rc = ecryptfs_read_xattr_region(
> + page_virt, file->f_path.dentry);

Are we assured that ecryptfs_read_xattr_region() cannot overrun the page?

> + set_header_info(page_virt, crypt_stat);
> + }

The kernel must always run flush_dcache_page() after modifying a pagecache
page by hand. Please review all of ecryptfs for this.


> + kunmap(page);

2007-01-09 23:23:41

by Michael Halcrow

[permalink] [raw]
Subject: Re: [PATCH 0/3] eCryptfs: Support metadata in xattr

On Tue, Jan 09, 2007 at 02:35:19PM -0800, Andrew Morton wrote:
> On Tue, 9 Jan 2007 16:21:07 -0600
> Michael Halcrow <[email protected]> wrote:
>
> > This patch set introduces the ability to store cryptographic metadata
> > into an lower file extended attribute rather than the lower file
> > header region.
> >
> > This patch set implements two new mount options:
> >
> > ecryptfs_xattr_metadata
> > - When set, newly created files will have their cryptographic
> > metadata stored in the extended attribute region of the file rather
> > than the header.
>
> Why is this useful?

When storing the data in the file header, there is a minimum of 8KB
reserved for the header information for each file, making each file at
least 12KB in size. This can take up a lot of extra disk space if the
user creates a lot of small files. By storing the data in the extended
attribute, each file will only occupy at least of 4KB of space.

As the eCryptfs metadata set becomes larger with new features such as
multi-key associations, most popular filesystems will not be able to
store all of the information in the xattr region in some cases due to
space constraints. However, the majority of users will only ever
associate one key per file, so most users will be okay with storing
their data in the xattr region.

This option should be used with caution. I want to emphasize that the
xattr must be maintained under all circumstances, or the file will be
rendered permanently unrecoverable. The last thing I want is for a
user to forget to set an xattr flag in a backup utility, only to later
discover that their backups are worthless.

> > ecryptfs_encrypted_view
> > - When set, this option causes eCryptfs to present applications a
> > view of encrypted files as if the cryptographic metadata were
> > stored in the file header, whether the metadata is actually stored
> > in the header or in the extended attributes.
>
> Sounds kludgy. "This mode of operation is useful for applications
> like incremental backup utilities that do not preserve the extended
> attributes when directly accessing the lower files.". Wouldn't it
> be better to lean on people to use better backup tools, and to fix
> existing ones?

No matter what eCryptfs winds up doing in the lower filesystem, I want
to preserve a baseline format compatibility for the encrypted
files. As of right now, the metadata may be in the file header or in
an xattr. There is no reason why the metadata could not be put in a
separate file in future versions.

Without the compatibility mode, backup utilities would have to know to
back up the metadata file along with the files. The semantics of
eCryptfs have always been that the lower files are self-contained
units of encrypted data, and the only additional information required
to decrypt any given eCryptfs file is the key. That is what has always
been emphasized about eCryptfs lower files, and that is what users
expect. Providing the encrypted view option will provide a way to
userspace applications wherein they can always get to the same old
familiar eCryptfs encrypted files, regardless of what eCryptfs winds
up doing with the metadata behind the scenes.

Mike

2007-01-09 23:44:20

by Michael Halcrow

[permalink] [raw]
Subject: Re: [PATCH 3/3] eCryptfs: Encrypted passthrough

On Tue, Jan 09, 2007 at 02:42:03PM -0800, Andrew Morton wrote:
> On Tue, 9 Jan 2007 16:23:37 -0600
> Michael Halcrow <[email protected]> wrote:
>
> > + page_virt = (char *)kmap(page);
>
> Do we _have_ to use kmap here? It's slow and theoretically deadlocky.
> kmap_atomic() is much preferred.
>
> Can the other kmap() calls in ecryptfs be converted?

We will look into doing this.

> We'd actually like to remove kmap() one day. Not much chance of that, but
> it's an objective.
>
> > + if (!page_virt) {
> > + rc = -ENOMEM;
> > + printk(KERN_ERR "Error mapping page\n");
> > + goto out;
> > + }
> > + memset(page_virt, 0, PAGE_CACHE_SIZE);
> > + if (page->index == 0) {
> > + rc = ecryptfs_read_xattr_region(
> > + page_virt, file->f_path.dentry);
>
> Are we assured that ecryptfs_read_xattr_region() cannot overrun the
> page?

Yes:

---
int ecryptfs_read_xattr_region(char *page_virt, struct dentry*ecryptfs_dentry)
{
ssize_t size;
int rc = 0;

size = ecryptfs_getxattr(ecryptfs_dentry, ECRYPTFS_XATTR_NAME,
page_virt, ECRYPTFS_DEFAULT_EXTENT_SIZE);
---

That winds up calling the lower filesystem's getxattr with
ECRYPTFS_DEFAULT_EXTENT_SIZE as the size parameter. eCryptfs validates
this value against PAGE_CACHE_SIZE in main.c::ecryptfs_init().

> > + set_header_info(page_virt, crypt_stat);
> > + }
>
> The kernel must always run flush_dcache_page() after modifying a pagecache
> page by hand. Please review all of ecryptfs for this.

We will work on some patches to address these issues.

Mike