Commit e77a56dd authored by Michael Halcrow's avatar Michael Halcrow Committed by Linus Torvalds

[PATCH] 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: default avatarMichael Halcrow <mhalcrow@us.ibm.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent dd2a3b7a
...@@ -1256,9 +1256,10 @@ int ecryptfs_read_and_validate_header_region(char *data, struct dentry *dentry, ...@@ -1256,9 +1256,10 @@ int ecryptfs_read_and_validate_header_region(char *data, struct dentry *dentry,
} }
static void void
write_header_metadata(char *virt, struct ecryptfs_crypt_stat *crypt_stat, ecryptfs_write_header_metadata(char *virt,
size_t *written) struct ecryptfs_crypt_stat *crypt_stat,
size_t *written)
{ {
u32 header_extent_size; u32 header_extent_size;
u16 num_header_extents_at_front; u16 num_header_extents_at_front;
...@@ -1320,7 +1321,8 @@ static int ecryptfs_write_headers_virt(char *page_virt, size_t *size, ...@@ -1320,7 +1321,8 @@ static int ecryptfs_write_headers_virt(char *page_virt, size_t *size,
offset += written; offset += written;
write_ecryptfs_flags((page_virt + offset), crypt_stat, &written); write_ecryptfs_flags((page_virt + offset), crypt_stat, &written);
offset += written; offset += written;
write_header_metadata((page_virt + offset), crypt_stat, &written); ecryptfs_write_header_metadata((page_virt + offset), crypt_stat,
&written);
offset += written; offset += written;
rc = ecryptfs_generate_key_packet_set((page_virt + offset), crypt_stat, rc = ecryptfs_generate_key_packet_set((page_virt + offset), crypt_stat,
ecryptfs_dentry, &written, ecryptfs_dentry, &written,
...@@ -1606,7 +1608,12 @@ int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry, ...@@ -1606,7 +1608,12 @@ int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry,
ssize_t bytes_read; ssize_t bytes_read;
struct ecryptfs_crypt_stat *crypt_stat = struct ecryptfs_crypt_stat *crypt_stat =
&ecryptfs_inode_to_private(ecryptfs_dentry->d_inode)->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 */ /* Read the first page from the underlying file */
page_virt = kmem_cache_alloc(ecryptfs_header_cache_1, GFP_USER); page_virt = kmem_cache_alloc(ecryptfs_header_cache_1, GFP_USER);
if (!page_virt) { if (!page_virt) {
......
...@@ -559,7 +559,7 @@ ssize_t ecryptfs_getxattr(struct dentry *dentry, const char *name, void *value, ...@@ -559,7 +559,7 @@ ssize_t ecryptfs_getxattr(struct dentry *dentry, const char *name, void *value,
int int
ecryptfs_setxattr(struct dentry *dentry, const char *name, const void *value, ecryptfs_setxattr(struct dentry *dentry, const char *name, const void *value,
size_t size, int flags); 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_helo(unsigned int transport, uid_t uid, pid_t pid);
int ecryptfs_process_quit(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, int ecryptfs_process_response(struct ecryptfs_message *msg, uid_t uid,
...@@ -582,6 +582,9 @@ int ecryptfs_send_connector(char *data, int data_len, ...@@ -582,6 +582,9 @@ int ecryptfs_send_connector(char *data, int data_len,
u16 msg_flags, pid_t daemon_pid); u16 msg_flags, pid_t daemon_pid);
int ecryptfs_init_connector(void); int ecryptfs_init_connector(void);
void ecryptfs_release_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 */ #endif /* #ifndef ECRYPTFS_KERNEL_H */
...@@ -250,6 +250,17 @@ static int ecryptfs_open(struct inode *inode, struct file *file) ...@@ -250,6 +250,17 @@ static int ecryptfs_open(struct inode *inode, struct file *file)
struct ecryptfs_file_info *file_info; struct ecryptfs_file_info *file_info;
int lower_flags; 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 */ /* Released in ecryptfs_release or end of function if failure */
file_info = kmem_cache_zalloc(ecryptfs_file_info_cache, GFP_KERNEL); file_info = kmem_cache_zalloc(ecryptfs_file_info_cache, GFP_KERNEL);
ecryptfs_set_file_private(file, file_info); ecryptfs_set_file_private(file, file_info);
...@@ -261,8 +272,6 @@ static int ecryptfs_open(struct inode *inode, struct file *file) ...@@ -261,8 +272,6 @@ static int ecryptfs_open(struct inode *inode, struct file *file)
} }
lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry); lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry);
crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat; 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); mutex_lock(&crypt_stat->cs_mutex);
if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_POLICY_APPLIED)) { if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_POLICY_APPLIED)) {
ecryptfs_printk(KERN_DEBUG, "Setting flags for stat...\n"); ecryptfs_printk(KERN_DEBUG, "Setting flags for stat...\n");
......
...@@ -289,6 +289,7 @@ static struct dentry *ecryptfs_lookup(struct inode *dir, struct dentry *dentry, ...@@ -289,6 +289,7 @@ static struct dentry *ecryptfs_lookup(struct inode *dir, struct dentry *dentry,
char *encoded_name; char *encoded_name;
unsigned int encoded_namelen; unsigned int encoded_namelen;
struct ecryptfs_crypt_stat *crypt_stat = NULL; struct ecryptfs_crypt_stat *crypt_stat = NULL;
struct ecryptfs_mount_crypt_stat *mount_crypt_stat;
char *page_virt = NULL; char *page_virt = NULL;
struct inode *lower_inode; struct inode *lower_inode;
u64 file_size; u64 file_size;
...@@ -388,8 +389,18 @@ static struct dentry *ecryptfs_lookup(struct inode *dir, struct dentry *dentry, ...@@ -388,8 +389,18 @@ static struct dentry *ecryptfs_lookup(struct inode *dir, struct dentry *dentry,
} }
crypt_stat->flags |= ECRYPTFS_METADATA_IN_XATTR; crypt_stat->flags |= ECRYPTFS_METADATA_IN_XATTR;
} }
memcpy(&file_size, page_virt, sizeof(file_size)); mount_crypt_stat = &ecryptfs_superblock_to_private(
file_size = be64_to_cpu(file_size); 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); i_size_write(dentry->d_inode, (loff_t)file_size);
kmem_cache_free(ecryptfs_header_cache_2, page_virt); kmem_cache_free(ecryptfs_header_cache_2, page_virt);
goto out; goto out;
......
...@@ -260,6 +260,33 @@ int ecryptfs_do_readpage(struct file *file, struct page *page, ...@@ -260,6 +260,33 @@ int ecryptfs_do_readpage(struct file *file, struct page *page,
ClearPageUptodate(page); ClearPageUptodate(page);
return rc; 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 * ecryptfs_readpage
...@@ -289,10 +316,55 @@ static int ecryptfs_readpage(struct file *file, struct page *page) ...@@ -289,10 +316,55 @@ static int ecryptfs_readpage(struct file *file, struct page *page)
"[%d]\n", rc); "[%d]\n", rc);
goto out; 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 { } else {
rc = ecryptfs_decrypt_page(file, page); rc = ecryptfs_decrypt_page(file, page);
if (rc) { if (rc) {
ecryptfs_printk(KERN_ERR, "Error decrypting page; " ecryptfs_printk(KERN_ERR, "Error decrypting page; "
"rc = [%d]\n", rc); "rc = [%d]\n", rc);
goto out; goto out;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment