Skip to contentSkip to conversation

eCryptfs: Support metadata in xattr

PATCH In review Michael Halcrow 2 participants 9 messages Jan 9, 2007 Ecrypt file system

lore
Share & tools
lore
#0 lore raw

cover 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.

#1 lore raw

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.

---

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

72a24201af3657aa4e6365b18ba869422fa77cee

fs/ecryptfs/crypto.c +20 -0
index 33f92d7..c2811b1 100644
@@ -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);
fs/ecryptfs/ecryptfs_kernel.h +10 -5
index 51c299a..f74b343 100644
@@ -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;
fs/ecryptfs/main.c +16 -2
index 87f05c4..a3efdcc 100644
@@ -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)
Signature · 1 line
1.3.3
#2 lore raw

PATCH 2/3 eCryptfs: Generalize metadata read/write

In reply to Michael Halcrow

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.

---

 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

fs/ecryptfs/crypto.c +173 -51
index c2811b1..e7f5441 100644
@@ -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) {
fs/ecryptfs/ecryptfs_kernel.h +26 -11
index f74b343..aecca2f 100644
@@ -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);
fs/ecryptfs/file.c +6 -21
index 779c347..f22c3a7 100644
@@ -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");
fs/ecryptfs/inode.c +35 -26

122 lines not on this page · raw message

fs/ecryptfs/main.c +6 -1
index a3efdcc..26fe405 100644
@@ -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,
fs/ecryptfs/mmap.c +89 -7

138 lines not on this page · raw message

Signature · 1 line
1.3.3
#3 lore raw

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

Review

Quoted 2 lines · On Tue, 9 Jan 2007 16:22:55 -0600 Michael Halcrow <[email protected]> wrote:
(quoted diff) +2 -0
+		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.

#4 lore raw

PATCH 3/3 eCryptfs: Encrypted passthrough

In reply to Michael Halcrow

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.

---

 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

fs/ecryptfs/crypto.c +11 -4
index e7f5441..8fbc38c 100644
@@ -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) {
fs/ecryptfs/ecryptfs_kernel.h +5 -2
index aecca2f..859d31b 100644
@@ -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 */
fs/ecryptfs/file.c +11 -2
index f22c3a7..652ed77 100644
@@ -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");
fs/ecryptfs/inode.c +13 -2
index 6b45b29..bbc1b4f 100644
@@ -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;
fs/ecryptfs/mmap.c +73 -1

89 lines not on this page · raw message

Signature · 1 line
1.3.3
#5 lore raw

Re: [PATCH 3/3] eCryptfs: Encrypted passthrough

fs/ecryptfs/mmap.c static int ecryptfs_readpage(struct file · line 328

Quoted 1 line · On Tue, 9 Jan 2007 16:23:37 -0600 Michael Halcrow <[email protected]> wrote:
fs/ecryptfs/mmap.c +1 -0
+				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.

fs/ecryptfs/mmap.c static int ecryptfs_readpage(struct file · line 337

Quoted 9 lines
fs/ecryptfs/mmap.c +9 -0
+				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?

Review

Quoted 2 lines
(quoted diff) +2 -0
+					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.

Quoted 1 line
fs/ecryptfs/mmap.c +1 -0
+				kunmap(page);
#6 lore raw

Re: [PATCH 3/3] eCryptfs: Encrypted passthrough

Quoted 9 lines · Andrew Morton

fs/ecryptfs/mmap.c static int ecryptfs_readpage(struct file · line 328

Quoted 1 line · On Tue, 9 Jan 2007 16:23:37 -0600 Michael Halcrow <[email protected]> wrote:
fs/ecryptfs/mmap.c +1 -0
+				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.

Quoted 15 lines

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

fs/ecryptfs/mmap.c static int ecryptfs_readpage(struct file · line 337

Quoted 9 lines
fs/ecryptfs/mmap.c +9 -0
+				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().

Quoted 5 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+					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

#7 lore raw
In reply to Michael Halcrow
Quoted 10 lines · 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?

Quoted 5 lines

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?

#8 lore raw
Quoted 7 lines · Andrew Morton
Quoted 4 lines · 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.

Quoted 8 lines
Quoted 6 lines

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.

Quoted 11 lines
Quoted 5 lines

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