summaryrefslogtreecommitdiffstats
path: root/fs/ecryptfs
diff options
context:
space:
mode:
Diffstat (limited to 'fs/ecryptfs')
-rw-r--r--fs/ecryptfs/Makefile2
-rw-r--r--fs/ecryptfs/crypto.c333
-rw-r--r--fs/ecryptfs/debug.c6
-rw-r--r--fs/ecryptfs/ecryptfs_kernel.h160
-rw-r--r--fs/ecryptfs/file.c52
-rw-r--r--fs/ecryptfs/inode.c93
-rw-r--r--fs/ecryptfs/keystore.c802
-rw-r--r--fs/ecryptfs/main.c82
-rw-r--r--fs/ecryptfs/messaging.c515
-rw-r--r--fs/ecryptfs/mmap.c378
-rw-r--r--fs/ecryptfs/netlink.c255
-rw-r--r--fs/ecryptfs/super.c2
12 files changed, 2213 insertions, 467 deletions
diff --git a/fs/ecryptfs/Makefile b/fs/ecryptfs/Makefile
index ca6562451ee..1f1107237ea 100644
--- a/fs/ecryptfs/Makefile
+++ b/fs/ecryptfs/Makefile
@@ -4,4 +4,4 @@
obj-$(CONFIG_ECRYPT_FS) += ecryptfs.o
-ecryptfs-objs := dentry.o file.o inode.o main.o super.o mmap.o crypto.o keystore.o debug.o
+ecryptfs-objs := dentry.o file.o inode.o main.o super.o mmap.o crypto.o keystore.o messaging.o netlink.o debug.o
diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c
index a86a55ccf87..6ac630625b7 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 <mahalcro@us.ibm.com>
* Michael C. Thompson <mcthomps@us.ibm.com>
*
@@ -207,7 +207,7 @@ ecryptfs_init_crypt_stat(struct ecryptfs_crypt_stat *crypt_stat)
mutex_init(&crypt_stat->cs_mutex);
mutex_init(&crypt_stat->cs_tfm_mutex);
mutex_init(&crypt_stat->cs_hash_tfm_mutex);
- ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_STRUCT_INITIALIZED);
+ crypt_stat->flags |= ECRYPTFS_STRUCT_INITIALIZED;
}
/**
@@ -305,8 +305,7 @@ static int encrypt_scatterlist(struct ecryptfs_crypt_stat *crypt_stat,
int rc = 0;
BUG_ON(!crypt_stat || !crypt_stat->tfm
- || !ECRYPTFS_CHECK_FLAG(crypt_stat->flags,
- ECRYPTFS_STRUCT_INITIALIZED));
+ || !(crypt_stat->flags & ECRYPTFS_STRUCT_INITIALIZED));
if (unlikely(ecryptfs_verbosity > 0)) {
ecryptfs_printk(KERN_DEBUG, "Key size [%d]; key:\n",
crypt_stat->key_size);
@@ -429,10 +428,10 @@ static int ecryptfs_read_in_page(struct ecryptfs_page_crypt_context *ctx,
goto out;
}
} else {
- rc = ecryptfs_grab_and_map_lower_page(lower_page, NULL,
- lower_inode,
- lower_page_idx);
- if (rc) {
+ *lower_page = grab_cache_page(lower_inode->i_mapping,
+ lower_page_idx);
+ if (!(*lower_page)) {
+ rc = -EINVAL;
ecryptfs_printk(
KERN_ERR, "Error attempting to grab and map "
"lower page with index [0x%.16x]; rc = [%d]\n",
@@ -485,7 +484,7 @@ int ecryptfs_encrypt_page(struct ecryptfs_page_crypt_context *ctx)
lower_inode = ecryptfs_inode_to_lower(ctx->page->mapping->host);
inode_info = ecryptfs_inode_to_private(ctx->page->mapping->host);
crypt_stat = &inode_info->crypt_stat;
- if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED)) {
+ if (!(crypt_stat->flags & ECRYPTFS_ENCRYPTED)) {
rc = ecryptfs_copy_page_to_lower(ctx->page, lower_inode,
ctx->param.lower_file);
if (rc)
@@ -617,7 +616,7 @@ int ecryptfs_decrypt_page(struct file *file, struct page *page)
crypt_stat = &(ecryptfs_inode_to_private(
page->mapping->host)->crypt_stat);
lower_inode = ecryptfs_inode_to_lower(page->mapping->host);
- if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED)) {
+ if (!(crypt_stat->flags & ECRYPTFS_ENCRYPTED)) {
rc = ecryptfs_do_readpage(file, page, page->index);
if (rc)
ecryptfs_printk(KERN_ERR, "Error attempting to copy "
@@ -863,7 +862,10 @@ void ecryptfs_set_default_sizes(struct ecryptfs_crypt_stat *crypt_stat)
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;
}
/**
@@ -879,7 +881,7 @@ int ecryptfs_compute_root_iv(struct ecryptfs_crypt_stat *crypt_stat)
BUG_ON(crypt_stat->iv_bytes > MD5_DIGEST_SIZE);
BUG_ON(crypt_stat->iv_bytes <= 0);
- if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_KEY_VALID)) {
+ if (!(crypt_stat->flags & ECRYPTFS_KEY_VALID)) {
rc = -EINVAL;
ecryptfs_printk(KERN_WARNING, "Session key not valid; "
"cannot generate root IV\n");
@@ -896,8 +898,7 @@ int ecryptfs_compute_root_iv(struct ecryptfs_crypt_stat *crypt_stat)
out:
if (rc) {
memset(crypt_stat->root_iv, 0, crypt_stat->iv_bytes);
- ECRYPTFS_SET_FLAG(crypt_stat->flags,
- ECRYPTFS_SECURITY_WARNING);
+ crypt_stat->flags |= ECRYPTFS_SECURITY_WARNING;
}
return rc;
}
@@ -905,7 +906,7 @@ out:
static void ecryptfs_generate_new_key(struct ecryptfs_crypt_stat *crypt_stat)
{
get_random_bytes(crypt_stat->key, crypt_stat->key_size);
- ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_KEY_VALID);
+ crypt_stat->flags |= ECRYPTFS_KEY_VALID;
ecryptfs_compute_root_iv(crypt_stat);
if (unlikely(ecryptfs_verbosity > 0)) {
ecryptfs_printk(KERN_DEBUG, "Generated new session key:\n");
@@ -915,6 +916,22 @@ static void ecryptfs_generate_new_key(struct ecryptfs_crypt_stat *crypt_stat)
}
/**
+ * 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
*
@@ -924,10 +941,12 @@ static void ecryptfs_set_default_crypt_stat_vals(
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;
- ECRYPTFS_CLEAR_FLAG(crypt_stat->flags, ECRYPTFS_KEY_VALID);
+ crypt_stat->flags &= ~(ECRYPTFS_KEY_VALID);
crypt_stat->file_version = ECRYPTFS_FILE_VERSION;
crypt_stat->mount_crypt_stat = mount_crypt_stat;
}
@@ -967,8 +986,10 @@ int ecryptfs_new_file_context(struct dentry *ecryptfs_dentry)
if (mount_crypt_stat->global_auth_tok) {
ecryptfs_printk(KERN_DEBUG, "Initializing context for new "
"file using mount_crypt_stat\n");
- ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED);
- ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_KEY_VALID);
+ crypt_stat->flags |= ECRYPTFS_ENCRYPTED;
+ 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);
@@ -1001,7 +1022,7 @@ int ecryptfs_new_file_context(struct dentry *ecryptfs_dentry)
*
* Returns one if marker found; zero if not found
*/
-int contains_ecryptfs_marker(char *data)
+static int contains_ecryptfs_marker(char *data)
{
u32 m_1, m_2;
@@ -1027,7 +1048,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}
};
/**
@@ -1050,11 +1072,9 @@ static int ecryptfs_process_flags(struct ecryptfs_crypt_stat *crypt_stat,
for (i = 0; i < ((sizeof(ecryptfs_flag_map)
/ sizeof(struct ecryptfs_flag_map_elem))); i++)
if (flags & ecryptfs_flag_map[i].file_flag) {
- ECRYPTFS_SET_FLAG(crypt_stat->flags,
- ecryptfs_flag_map[i].local_flag);
+ crypt_stat->flags |= ecryptfs_flag_map[i].local_flag;
} else
- ECRYPTFS_CLEAR_FLAG(crypt_stat->flags,
- ecryptfs_flag_map[i].local_flag);
+ crypt_stat->flags &= ~(ecryptfs_flag_map[i].local_flag);
/* Version is in top 8 bits of the 32-bit flag vector */
crypt_stat->file_version = ((flags >> 24) & 0xFF);
(*bytes_read) = 4;
@@ -1091,8 +1111,7 @@ write_ecryptfs_flags(char *page_virt, struct ecryptfs_crypt_stat *crypt_stat,
for (i = 0; i < ((sizeof(ecryptfs_flag_map)
/ sizeof(struct ecryptfs_flag_map_elem))); i++)
- if (ECRYPTFS_CHECK_FLAG(crypt_stat->flags,
- ecryptfs_flag_map[i].local_flag))
+ if (crypt_stat->flags & ecryptfs_flag_map[i].local_flag)
flags |= ecryptfs_flag_map[i].file_flag;
/* Version is in top 8 bits of the 32-bit flag vector */
flags |= ((((u8)crypt_stat->file_version) << 24) & 0xFF000000);
@@ -1187,8 +1206,8 @@ int ecryptfs_cipher_code_to_string(char *str, u16 cipher_code)
*
* Returns zero on success; non-zero otherwise
*/
-int ecryptfs_read_header_region(char *data, struct dentry *dentry,
- struct vfsmount *mnt)
+static int ecryptfs_read_header_region(char *data, struct dentry *dentry,
+ struct vfsmount *mnt)
{
struct file *lower_file;
mm_segment_t oldfs;
@@ -1217,9 +1236,25 @@ out:
return rc;
}
-static void
-write_header_metadata(char *virt, struct ecryptfs_crypt_stat *crypt_stat,
- size_t *written)
+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;
+}
+
+
+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;
@@ -1268,9 +1303,9 @@ struct kmem_cache *ecryptfs_header_cache_2;
*
* Returns zero on success
*/
-int ecryptfs_write_headers_virt(char *page_virt,
- struct ecryptfs_crypt_stat *crypt_stat,
- struct dentry *ecryptfs_dentry)
+static int ecryptfs_write_headers_virt(char *page_virt, size_t *size,
+ struct ecryptfs_crypt_stat *crypt_stat,
+ struct dentry *ecryptfs_dentry)
{
int rc;
size_t written;
@@ -1281,7 +1316,8 @@ int ecryptfs_write_headers_virt(char *page_virt,
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,
@@ -1289,11 +1325,70 @@ int ecryptfs_write_headers_virt(char *page_virt,
if (rc)
ecryptfs_printk(KERN_WARNING, "Error generating key packet "
"set; rc = [%d]\n", rc);
+ if (size) {
+ offset += written;
+ *size = offset;
+ }
+ return rc;
+}
+
+static 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;
+ ssize_t size;
+ int rc = 0;
+
+ lower_file->f_pos = 0;
+ oldfs = get_fs();
+ set_fs(get_ds());
+ size = vfs_write(lower_file, (char __user *)page_virt, PAGE_CACHE_SIZE,
+ &lower_file->f_pos);
+ if (size < 0) {
+ rc = (int)size;
+ printk(KERN_ERR "Error attempting to write lower page; "
+ "rc = [%d]\n", rc);
+ set_fs(oldfs);
+ goto out;
+ }
+ 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) {
+ size = vfs_write(lower_file, (char __user *)page_virt,
+ PAGE_CACHE_SIZE, &lower_file->f_pos);
+ if (size < 0) {
+ rc = (int)size;
+ printk(KERN_ERR "Error attempting to write lower page; "
+ "rc = [%d]\n", rc);
+ set_fs(oldfs);
+ goto out;
+ }
+ current_header_page++;
+ }
+ set_fs(oldfs);
+out:
+ return rc;
+}
+
+static 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
@@ -1304,22 +1399,18 @@ int ecryptfs_write_headers_virt(char *page_virt,
*
* 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(
ecryptfs_dentry->d_inode)->crypt_stat;
- if (likely(ECRYPTFS_CHECK_FLAG(crypt_stat->flags,
- ECRYPTFS_ENCRYPTED))) {
- if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags,
- ECRYPTFS_KEY_VALID)) {
+ if (likely(crypt_stat->flags & ECRYPTFS_ENCRYPTED)) {
+ if (!(crypt_stat->flags & ECRYPTFS_KEY_VALID)) {
ecryptfs_printk(KERN_DEBUG, "Key is "
"invalid; bailing out\n");
rc = -EINVAL;
@@ -1332,54 +1423,42 @@ int ecryptfs_write_headers(struct dentry *ecryptfs_dentry,
goto out;
}
/* Released in this function */
- page_virt = kmem_cache_alloc(ecryptfs_header_cache_0, GFP_USER);
+ page_virt = kmem_cache_zalloc(ecryptfs_header_cache_0, GFP_USER);
if (!page_virt) {
ecryptfs_printk(KERN_ERR, "Out of memory\n");
rc = -ENOMEM;
goto out;
}
- memset(page_virt, 0, PAGE_CACHE_SIZE);
- 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;
@@ -1394,9 +1473,10 @@ static int parse_header_metadata(struct ecryptfs_crypt_stat *crypt_stat,
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);
@@ -1427,7 +1507,8 @@ static void set_default_header_data(struct ecryptfs_crypt_stat *crypt_stat)
*/
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;
@@ -1461,7 +1542,7 @@ static int ecryptfs_read_headers_virt(char *page_virt,
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);
@@ -1476,12 +1557,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 = [%zd]\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;
@@ -1489,7 +1618,12 @@ int ecryptfs_read_headers(struct dentry *ecryptfs_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) {
@@ -1510,11 +1644,36 @@ int ecryptfs_read_headers(struct dentry *ecryptfs_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/debug.c b/fs/ecryptfs/debug.c
index 61f8e894284..434c7efd80f 100644
--- a/fs/ecryptfs/debug.c
+++ b/fs/ecryptfs/debug.c
@@ -36,7 +36,7 @@ void ecryptfs_dump_auth_tok(struct ecryptfs_auth_tok *auth_tok)
ecryptfs_printk(KERN_DEBUG, "Auth tok at mem loc [%p]:\n",
auth_tok);
- if (ECRYPTFS_CHECK_FLAG(auth_tok->flags, ECRYPTFS_PRIVATE_KEY)) {
+ if (auth_tok->flags & ECRYPTFS_PRIVATE_KEY) {
ecryptfs_printk(KERN_DEBUG, " * private key type\n");
ecryptfs_printk(KERN_DEBUG, " * (NO PRIVATE KEY SUPPORT "
"IN ECRYPTFS VERSION 0.1)\n");
@@ -46,8 +46,8 @@ void ecryptfs_dump_auth_tok(struct ecryptfs_auth_tok *auth_tok)
ECRYPTFS_SALT_SIZE);
salt[ECRYPTFS_SALT_SIZE * 2] = '\0';
ecryptfs_printk(KERN_DEBUG, " * salt = [%s]\n", salt);
- if (ECRYPTFS_CHECK_FLAG(auth_tok->token.password.flags,
- ECRYPTFS_PERSISTENT_PASSWORD)) {
+ if (auth_tok->token.password.flags &
+ ECRYPTFS_PERSISTENT_PASSWORD) {
ecryptfs_printk(KERN_DEBUG, " * persistent\n");
}
memcpy(sig, auth_tok->token.password.signature,
diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h
index 0f897109759..b3609b7cdf1 100644
--- a/fs/ecryptfs/ecryptfs_kernel.h
+++ b/fs/ecryptfs/ecryptfs_kernel.h
@@ -4,8 +4,10 @@
*
* 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 <mahalcro@us.ibm.com>
+ * Trevor S. Highland <trevor.highland@gmail.com>
+ * Tyler Hicks <tyhicks@ou.edu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -31,22 +33,25 @@
#include <linux/fs_stack.h>
#include <linux/namei.h>
#include <linux/scatterlist.h>
+#include <linux/hash.h>
/* Version verification for shared data structures w/ userspace */
#define ECRYPTFS_VERSION_MAJOR 0x00
#define ECRYPTFS_VERSION_MINOR 0x04
-#define ECRYPTFS_SUPPORTED_FILE_VERSION 0x01
+#define ECRYPTFS_SUPPORTED_FILE_VERSION 0x02
/* These flags indicate which features are supported by the kernel
* 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_PLAINTEXT_PASSTHROUGH \
+ | 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
@@ -60,10 +65,25 @@
#define ECRYPTFS_MAX_KEY_BYTES 64
#define ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES 512
#define ECRYPTFS_DEFAULT_IV_BYTES 16
-#define ECRYPTFS_FILE_VERSION 0x01
+#define ECRYPTFS_FILE_VERSION 0x02
#define ECRYPTFS_DEFAULT_HEADER_EXTENT_SIZE 8192
#define ECRYPTFS_DEFAULT_EXTENT_SIZE 4096
#define ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE 8192
+#define ECRYPTFS_DEFAULT_MSG_CTX_ELEMS 32
+#define ECRYPTFS_DEFAULT_SEND_TIMEOUT HZ
+#define ECRYPTFS_MAX_MSG_CTX_TTL (HZ*3)
+#define ECRYPTFS_NLMSG_HELO 100
+#define ECRYPTFS_NLMSG_QUIT 101
+#define ECRYPTFS_NLMSG_REQUEST 102
+#define ECRYPTFS_NLMSG_RESPONSE 103
+#define ECRYPTFS_MAX_PKI_NAME_BYTES 16
+#define ECRYPTFS_DEFAULT_NUM_USERS 4
+#define ECRYPTFS_MAX_NUM_USERS 32768
+#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
@@ -74,9 +94,7 @@
#define RFC2440_CIPHER_TWOFISH 0x0a
#define RFC2440_CIPHER_CAST_6 0x0b
-#define ECRYPTFS_SET_FLAG(flag_bit_vector, flag) (flag_bit_vector |= (flag))
-#define ECRYPTFS_CLEAR_FLAG(flag_bit_vector, flag) (flag_bit_vector &= ~(flag))
-#define ECRYPTFS_CHECK_FLAG(flag_bit_vector, flag) (flag_bit_vector & (flag))
+#define RFC2440_CIPHER_RSA 0x01
/**
* For convenience, we may need to pass around the encrypted session
@@ -114,6 +132,14 @@ struct ecryptfs_password {
enum ecryptfs_token_types {ECRYPTFS_PASSWORD, ECRYPTFS_PRIVATE_KEY};
+struct ecryptfs_private_key {
+ u32 key_size;
+ u32 data_len;
+ u8 signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1];
+ char pki_type[ECRYPTFS_MAX_PKI_NAME_BYTES + 1];
+ u8 data[];
+};
+
/* May be a password or a private key */
struct ecryptfs_auth_tok {
u16 version; /* 8-bit major and 8-bit minor */
@@ -123,7 +149,7 @@ struct ecryptfs_auth_tok {
u8 reserved[32];
union {
struct ecryptfs_password password;
- /* Private key is in future eCryptfs releases */
+ struct ecryptfs_private_key private_key;
} token;
} __attribute__ ((packed));
@@ -177,8 +203,13 @@ ecryptfs_get_key_payload_data(struct key *key)
#define ECRYPTFS_DEFAULT_CIPHER "aes"
#define ECRYPTFS_DEFAULT_KEY_BYTES 16
#define ECRYPTFS_DEFAULT_HASH "md5"
+#define ECRYPTFS_TAG_1_PACKET_TYPE 0x01
#define ECRYPTFS_TAG_3_PACKET_TYPE 0x8C
#define ECRYPTFS_TAG_11_PACKET_TYPE 0xED
+#define ECRYPTFS_TAG_64_PACKET_TYPE 0x40
+#define ECRYPTFS_TAG_65_PACKET_TYPE 0x41
+#define ECRYPTFS_TAG_66_PACKET_TYPE 0x42
+#define ECRYPTFS_TAG_67_PACKET_TYPE 0x43
#define MD5_DIGEST_SIZE 16
/**
@@ -195,6 +226,8 @@ struct ecryptfs_crypt_stat {
#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;
@@ -241,6 +274,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;
@@ -271,6 +306,33 @@ struct ecryptfs_auth_tok_list_item {
struct ecryptfs_auth_tok auth_tok;
};
+struct ecryptfs_message {
+ u32 index;
+ u32 data_len;
+ u8 data[];
+};
+
+struct ecryptfs_msg_ctx {
+#define ECRYPTFS_MSG_CTX_STATE_FREE 0x0001
+#define ECRYPTFS_MSG_CTX_STATE_PENDING 0x0002
+#define ECRYPTFS_MSG_CTX_STATE_DONE 0x0003
+ u32 state;
+ unsigned int index;
+ unsigned int counter;
+ struct ecryptfs_message *msg;
+ struct task_struct *task;
+ struct list_head node;
+ struct mutex mux;
+};
+
+extern unsigned int ecryptfs_transport;
+
+struct ecryptfs_daemon_id {
+ pid_t pid;
+ uid_t uid;
+ struct hlist_node id_chain;
+};
+
static inline struct ecryptfs_file_info *
ecryptfs_file_to_private(struct file *file)
{
@@ -384,13 +446,16 @@ void __ecryptfs_printk(const char *fmt, ...);
extern const struct file_operations ecryptfs_main_fops;
extern const struct file_operations ecryptfs_dir_fops;
-extern struct inode_operations ecryptfs_main_iops;
-extern struct inode_operations ecryptfs_dir_iops;
-extern struct inode_operations ecryptfs_symlink_iops;
-extern struct super_operations ecryptfs_sops;
+extern const struct inode_operations ecryptfs_main_iops;
+extern const struct inode_operations ecryptfs_dir_iops;
+extern const struct inode_operations ecryptfs_symlink_iops;
+extern const struct super_operations ecryptfs_sops;
extern struct dentry_operations ecryptfs_dops;
extern struct address_space_operations ecryptfs_aops;
extern int ecryptfs_verbosity;
+extern unsigned int ecryptfs_message_buf_len;
+extern signed long ecryptfs_message_wait_timeout;
+extern unsigned int ecryptfs_number_of_users;
extern struct kmem_cache *ecryptfs_auth_tok_list_item_cache;
extern struct kmem_cache *ecryptfs_file_info_cache;
@@ -400,6 +465,7 @@ extern struct kmem_cache *ecryptfs_sb_info_cache;
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,
@@ -426,9 +492,13 @@ int ecryptfs_init_crypt_ctx(struct ecryptfs_crypt_stat *crypt_stat);
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,
@@ -441,26 +511,20 @@ int ecryptfs_copy_page_to_lower(struct page *page, struct inode *lower_inode,
struct file *lower_file);
int ecryptfs_do_readpage(struct file *file, struct page *page,
pgoff_t lower_page_index);
-int ecryptfs_grab_and_map_lower_page(struct page **lower_page,
- char **lower_virt,
- struct inode *lower_inode,
- unsigned long lower_page_index);
int ecryptfs_writepage_and_release_lower_page(struct page *lower_page,
struct inode *lower_inode,
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,
+int ecryptfs_write_metadata(struct dentry *ecryptfs_dentry,
+ struct file *lower_file);
+int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry,
struct file *lower_file);
-int ecryptfs_write_headers_virt(char *page_virt,
- struct ecryptfs_crypt_stat *crypt_stat,
- struct dentry *ecryptfs_dentry);
-int ecryptfs_read_headers(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);
@@ -483,5 +547,37 @@ int ecryptfs_open_lower_file(struct file **lower_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_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,
+ pid_t pid, u32 seq);
+int ecryptfs_send_message(unsigned int transport, char *data, int data_len,
+ struct ecryptfs_msg_ctx **msg_ctx);
+int ecryptfs_wait_for_response(struct ecryptfs_msg_ctx *msg_ctx,
+ struct ecryptfs_message **emsg);
+int ecryptfs_init_messaging(unsigned int transport);
+void ecryptfs_release_messaging(unsigned int transport);
+
+int ecryptfs_send_netlink(char *data, int data_len,
+ struct ecryptfs_msg_ctx *msg_ctx, u16 msg_type,
+ u16 msg_flags, pid_t daemon_pid);
+int ecryptfs_init_netlink(void);
+void ecryptfs_release_netlink(void);
+
+int ecryptfs_send_connector(char *data, int data_len,
+ struct ecryptfs_msg_ctx *msg_ctx, u16 msg_type,
+ 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 c5a2e5298f1..bd969adf70d 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 <mhalcrow@us.ibm.com>
* Michael C. Thompson <mcthomps@us.ibm.com>
*
@@ -250,8 +250,19 @@ static int ecryptfs_open(struct inode *inode, struct file *file)
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_alloc(ecryptfs_file_info_cache, GFP_KERNEL);
+ file_info = kmem_cache_zalloc(ecryptfs_file_info_cache, GFP_KERNEL);
ecryptfs_set_file_private(file, file_info);
if (!file_info) {
ecryptfs_printk(KERN_ERR,
@@ -259,17 +270,14 @@ static int ecryptfs_open(struct inode *inode, struct file *file)
rc = -ENOMEM;
goto out;
}
- memset(file_info, 0, sizeof(*file_info));
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)) {
+ if (!(crypt_stat->flags & ECRYPTFS_POLICY_APPLIED)) {
ecryptfs_printk(KERN_DEBUG, "Setting flags for stat...\n");
/* Policy code enabled in future release */
- ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_POLICY_APPLIED);
- ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED);
+ crypt_stat->flags |= ECRYPTFS_POLICY_APPLIED;
+ crypt_stat->flags |= ECRYPTFS_ENCRYPTED;
}
mutex_unlock(&crypt_stat->cs_mutex);
lower_flags = file->f_flags;
@@ -289,31 +297,14 @@ static int ecryptfs_open(struct inode *inode, struct file *file)
lower_inode = lower_dentry->d_inode;
if (S_ISDIR(ecryptfs_dentry->d_inode->i_mode)) {
ecryptfs_printk(KERN_DEBUG, "This is a directory\n");
- ECRYPTFS_CLEAR_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED);
+ crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED);
rc = 0;
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 (!(crypt_stat->flags & ECRYPTFS_POLICY_APPLIED)
+ || !(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");
@@ -327,9 +318,8 @@ static int ecryptfs_open(struct inode *inode, struct file *file)
mutex_unlock(&crypt_stat->cs_mutex);
goto out_puts;
}
- ECRYPTFS_CLEAR_FLAG(crypt_stat->flags,
- ECRYPTFS_ENCRYPTED);
rc = 0;
+ crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED);
mutex_unlock(&crypt_stat->cs_mutex);
goto out;
}
diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index 11f5e5076ae..9fa7e0b27a9 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 <mahalcro@us.ibm.com>
* Michael C. Thompsion <mcthomps@us.ibm.com>
*
@@ -161,17 +161,17 @@ static int grow_file(struct dentry *ecryptfs_dentry, struct file *lower_file,
ecryptfs_set_file_lower(&fake_file, lower_file);
rc = ecryptfs_fill_zeros(&fake_file, 1);
if (rc) {
- ECRYPTFS_SET_FLAG(
- ecryptfs_inode_to_private(inode)->crypt_stat.flags,
- ECRYPTFS_SECURITY_WARNING);
+ ecryptfs_inode_to_private(inode)->crypt_stat.flags |=
+ ECRYPTFS_SECURITY_WARNING;
ecryptfs_printk(KERN_WARNING, "Error attempting to fill zeros "
"in file; rc = [%d]\n", rc);
goto out;
}
i_size_write(inode, 0);
- ecryptfs_write_inode_size_to_header(lower_file, lower_inode, inode);
- ECRYPTFS_SET_FLAG(ecryptfs_inode_to_private(inode)->crypt_stat.flags,
- ECRYPTFS_NEW_FILE);
+ ecryptfs_write_inode_size_to_metadata(lower_file, lower_inode, inode,
+ ecryptfs_dentry,
+ ECRYPTFS_LOWER_I_MUTEX_NOT_HELD);
+ ecryptfs_inode_to_private(inode)->crypt_stat.flags |= ECRYPTFS_NEW_FILE;
out:
return rc;
}
@@ -199,7 +199,7 @@ static int ecryptfs_initialize_file(struct dentry *ecryptfs_dentry)
lower_dentry->d_name.name);
inode = ecryptfs_dentry->d_inode;
crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat;
- lower_flags = ((O_CREAT | O_WRONLY | O_TRUNC) & O_ACCMODE) | O_RDWR;
+ lower_flags = ((O_CREAT | O_TRUNC) & O_ACCMODE) | O_RDWR;
#if BITS_PER_LONG != 32
lower_flags |= O_LARGEFILE;
#endif
@@ -214,10 +214,10 @@ static int ecryptfs_initialize_file(struct dentry *ecryptfs_dentry)
lower_inode = lower_dentry->d_inode;
if (S_ISDIR(ecryptfs_dentry->d_inode->i_mode)) {
ecryptfs_printk(KERN_DEBUG, "This is a directory\n");
- ECRYPTFS_CLEAR_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED);
+ crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED);
goto out_fput;
}
- ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_NEW_FILE);
+ crypt_stat->flags |= ECRYPTFS_NEW_FILE;
ecryptfs_printk(KERN_DEBUG, "Initializing crypto context\n");
rc = ecryptfs_new_file_context(ecryptfs_dentry);
if (rc) {
@@ -225,7 +225,7 @@ static int ecryptfs_initialize_file(struct dentry *ecryptfs_dentry)
"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;
@@ -287,6 +287,7 @@ static struct dentry *ecryptfs_lookup(struct inode *dir, struct dentry *dentry,
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;
@@ -361,34 +362,44 @@ static struct dentry *ecryptfs_lookup(struct inode *dir, struct dentry *dentry,
goto out;
}
/* Released in this function */
- page_virt =
- (char *)kmem_cache_alloc(ecryptfs_header_cache_2,
- GFP_USER);
+ page_virt = kmem_cache_zalloc(ecryptfs_header_cache_2,
+ GFP_USER);
if (!page_virt) {
rc = -ENOMEM;
ecryptfs_printk(KERN_ERR,
"Cannot ecryptfs_kmalloc a page\n");
goto out_dput;
}
- memset(page_virt, 0, PAGE_CACHE_SIZE);
- 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))
+ if (!(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;
}
+ crypt_stat->flags |= ECRYPTFS_METADATA_IN_XATTR;
+ }
+ 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);
}
+ i_size_write(dentry->d_inode, (loff_t)file_size);
kmem_cache_free(ecryptfs_header_cache_2, page_virt);
goto out;
@@ -782,20 +793,26 @@ int ecryptfs_truncate(struct dentry *dentry, loff_t new_length)
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 =
@@ -882,7 +899,7 @@ out:
return rc;
}
-static int
+int
ecryptfs_setxattr(struct dentry *dentry, const char *name, const void *value,
size_t size, int flags)
{
@@ -902,7 +919,7 @@ out:
return rc;
}
-static ssize_t
+ssize_t
ecryptfs_getxattr(struct dentry *dentry, const char *name, void *value,
size_t size)
{
@@ -972,7 +989,7 @@ int ecryptfs_inode_set(struct inode *inode, void *lower_inode)
return 0;
}
-struct inode_operations ecryptfs_symlink_iops = {
+const struct inode_operations ecryptfs_symlink_iops = {
.readlink = ecryptfs_readlink,
.follow_link = ecryptfs_follow_link,
.put_link = ecryptfs_put_link,
@@ -984,7 +1001,7 @@ struct inode_operations ecryptfs_symlink_iops = {
.removexattr = ecryptfs_removexattr
};
-struct inode_operations ecryptfs_dir_iops = {
+const struct inode_operations ecryptfs_dir_iops = {
.create = ecryptfs_create,
.lookup = ecryptfs_lookup,
.link = ecryptfs_link,
@@ -1002,7 +1019,7 @@ struct inode_operations ecryptfs_dir_iops = {
.removexattr = ecryptfs_removexattr
};
-struct inode_operations ecryptfs_main_iops = {
+const struct inode_operations ecryptfs_main_iops = {
.permission = ecryptfs_permission,
.setattr = ecryptfs_setattr,
.setxattr = ecryptfs_setxattr,
diff --git a/fs/ecryptfs/keystore.c b/fs/ecryptfs/keystore.c
index 745c0f1bfbb..c209f67e7a2 100644
--- a/fs/ecryptfs/keystore.c
+++ b/fs/ecryptfs/keystore.c
@@ -7,6 +7,7 @@
* Copyright (C) 2004-2006 International Business Machines Corp.
* Author(s): Michael A. Halcrow <mhalcrow@us.ibm.com>
* Michael C. Thompson <mcthomps@us.ibm.com>
+ * Trevor S. Highland <trevor.highland@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -64,26 +65,6 @@ int process_request_key_err(long err_code)
return rc;
}
-static void wipe_auth_tok_list(struct list_head *auth_tok_list_head)
-{
- struct list_head *walker;
- struct ecryptfs_auth_tok_list_item *auth_tok_list_item;
-
- walker = auth_tok_list_head->next;
- while (walker != auth_tok_list_head) {
- auth_tok_list_item =
- list_entry(walker, struct ecryptfs_auth_tok_list_item,
- list);
- walker = auth_tok_list_item->list.next;
- memset(auth_tok_list_item, 0,
- sizeof(struct ecryptfs_auth_tok_list_item));
- kmem_cache_free(ecryptfs_auth_tok_list_item_cache,
- auth_tok_list_item);
- }
-}
-
-struct kmem_cache *ecryptfs_auth_tok_list_item_cache;
-
/**
* parse_packet_length
* @data: Pointer to memory containing length at offset
@@ -102,12 +83,12 @@ static int parse_packet_length(unsigned char *data, size_t *size,
(*size) = 0;
if (data[0] < 192) {
/* One-byte length */
- (*size) = data[0];
+ (*size) = (unsigned char)data[0];
(*length_size) = 1;
} else if (data[0] < 224) {
/* Two-byte length */
- (*size) = ((data[0] - 192) * 256);
- (*size) += (data[1] + 192);
+ (*size) = (((unsigned char)(data[0]) - 192) * 256);
+ (*size) += ((unsigned char)(data[1]) + 192);
(*length_size) = 2;
} else if (data[0] == 255) {
/* Five-byte length; we're not supposed to see this */
@@ -154,6 +135,499 @@ static int write_packet_length(char *dest, size_t size,
return rc;
}
+static int
+write_tag_64_packet(char *signature, struct ecryptfs_session_key *session_key,
+ char **packet, size_t *packet_len)
+{
+ size_t i = 0;
+ size_t data_len;
+ size_t packet_size_len;
+ char *message;
+ int rc;
+
+ /*
+ * ***** TAG 64 Packet Format *****
+ * | Content Type | 1 byte |
+ * | Key Identifier Size | 1 or 2 bytes |
+ * | Key Identifier | arbitrary |
+ * | Encrypted File Encryption Key Size | 1 or 2 bytes |
+ * | Encrypted File Encryption Key | arbitrary |
+ */
+ data_len = (5 + ECRYPTFS_SIG_SIZE_HEX
+ + session_key->encrypted_key_size);
+ *packet = kmalloc(data_len, GFP_KERNEL);
+ message = *packet;
+ if (!message) {
+ ecryptfs_printk(KERN_ERR, "Unable to allocate memory\n");
+ rc = -ENOMEM;
+ goto out;
+ }
+ message[i++] = ECRYPTFS_TAG_64_PACKET_TYPE;
+ rc = write_packet_length(&message[i], ECRYPTFS_SIG_SIZE_HEX,
+ &packet_size_len);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error generating tag 64 packet "
+ "header; cannot generate packet length\n");
+ goto out;
+ }
+ i += packet_size_len;
+ memcpy(&message[i], signature, ECRYPTFS_SIG_SIZE_HEX);
+ i += ECRYPTFS_SIG_SIZE_HEX;
+ rc = write_packet_length(&message[i], session_key->encrypted_key_size,
+ &packet_size_len);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error generating tag 64 packet "
+ "header; cannot generate packet length\n");
+ goto out;
+ }
+ i += packet_size_len;
+ memcpy(&message[i], session_key->encrypted_key,
+ session_key->encrypted_key_size);
+ i += session_key->encrypted_key_size;
+ *packet_len = i;
+out:
+ return rc;
+}
+
+static int
+parse_tag_65_packet(struct ecryptfs_session_key *session_key, u16 *cipher_code,
+ struct ecryptfs_message *msg)
+{
+ size_t i = 0;
+ char *data;
+ size_t data_len;
+ size_t m_size;
+ size_t message_len;
+ u16 checksum = 0;
+ u16 expected_checksum = 0;
+ int rc;
+
+ /*
+ * ***** TAG 65 Packet Format *****
+ * | Content Type | 1 byte |
+ * | Status Indicator | 1 byte |
+ * | File Encryption Key Size | 1 or 2 bytes |
+ * | File Encryption Key | arbitrary |
+ */
+ message_len = msg->data_len;
+ data = msg->data;
+ if (message_len < 4) {
+ rc = -EIO;
+ goto out;
+ }
+ if (data[i++] != ECRYPTFS_TAG_65_PACKET_TYPE) {
+ ecryptfs_printk(KERN_ERR, "Type should be ECRYPTFS_TAG_65\n");
+ rc = -EIO;
+ goto out;
+ }
+ if (data[i++]) {
+ ecryptfs_printk(KERN_ERR, "Status indicator has non-zero value "
+ "[%d]\n", data[i-1]);
+ rc = -EIO;
+ goto out;
+ }
+ rc = parse_packet_length(&data[i], &m_size, &data_len);
+ if (rc) {
+ ecryptfs_printk(KERN_WARNING, "Error parsing packet length; "
+ "rc = [%d]\n", rc);
+ goto out;
+ }
+ i += data_len;
+ if (message_len < (i + m_size)) {
+ ecryptfs_printk(KERN_ERR, "The received netlink message is "
+ "shorter than expected\n");
+ rc = -EIO;
+ goto out;
+ }
+ if (m_size < 3) {
+ ecryptfs_printk(KERN_ERR,
+ "The decrypted key is not long enough to "
+ "include a cipher code and checksum\n");
+ rc = -EIO;
+ goto out;
+ }
+ *cipher_code = data[i++];
+ /* The decrypted key includes 1 byte cipher code and 2 byte checksum */
+ session_key->decrypted_key_size = m_size - 3;
+ if (session_key->decrypted_key_size > ECRYPTFS_MAX_KEY_BYTES) {
+ ecryptfs_printk(KERN_ERR, "key_size [%d] larger than "
+ "the maximum key size [%d]\n",
+ session_key->decrypted_key_size,
+ ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES);
+ rc = -EIO;
+ goto out;
+ }
+ memcpy(session_key->decrypted_key, &data[i],
+ session_key->decrypted_key_size);
+ i += session_key->decrypted_key_size;
+ expected_checksum += (unsigned char)(data[i++]) << 8;
+ expected_checksum += (unsigned char)(data[i++]);
+ for (i = 0; i < session_key->decrypted_key_size; i++)
+ checksum += session_key->decrypted_key[i];
+ if (expected_checksum != checksum) {
+ ecryptfs_printk(KERN_ERR, "Invalid checksum for file "
+ "encryption key; expected [%x]; calculated "
+ "[%x]\n", expected_checksum, checksum);
+ rc = -EIO;
+ }
+out:
+ return rc;
+}
+
+
+static int
+write_tag_66_packet(char *signature, size_t cipher_code,
+ struct ecryptfs_crypt_stat *crypt_stat, char **packet,
+ size_t *packet_len)
+{
+ size_t i = 0;
+ size_t j;
+ size_t data_len;
+ size_t checksum = 0;
+ size_t packet_size_len;
+ char *message;
+ int rc;
+
+ /*
+ * ***** TAG 66 Packet Format *****
+ * | Content Type | 1 byte |
+ * | Key Identifier Size | 1 or 2 bytes |
+ * | Key Identifier | arbitrary |
+ * | File Encryption Key Size | 1 or 2 bytes |
+ * | File Encryption Key | arbitrary |
+ */
+ data_len = (5 + ECRYPTFS_SIG_SIZE_HEX + crypt_stat->key_size);
+ *packet = kmalloc(data_len, GFP_KERNEL);
+ message = *packet;
+ if (!message) {
+ ecryptfs_printk(KERN_ERR, "Unable to allocate memory\n");
+ rc = -ENOMEM;
+ goto out;
+ }
+ message[i++] = ECRYPTFS_TAG_66_PACKET_TYPE;
+ rc = write_packet_length(&message[i], ECRYPTFS_SIG_SIZE_HEX,
+ &packet_size_len);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error generating tag 66 packet "
+ "header; cannot generate packet length\n");
+ goto out;
+ }
+ i += packet_size_len;
+ memcpy(&message[i], signature, ECRYPTFS_SIG_SIZE_HEX);
+ i += ECRYPTFS_SIG_SIZE_HEX;
+ /* The encrypted key includes 1 byte cipher code and 2 byte checksum */
+ rc = write_packet_length(&message[i], crypt_stat->key_size + 3,
+ &packet_size_len);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error generating tag 66 packet "
+ "header; cannot generate packet length\n");
+ goto out;
+ }
+ i += packet_size_len;
+ message[i++] = cipher_code;
+ memcpy(&message[i], crypt_stat->key, crypt_stat->key_size);
+ i += crypt_stat->key_size;
+ for (j = 0; j < crypt_stat->key_size; j++)
+ checksum += crypt_stat->key[j];
+ message[i++] = (checksum / 256) % 256;
+ message[i++] = (checksum % 256);
+ *packet_len = i;
+out:
+ return rc;
+}
+
+static int
+parse_tag_67_packet(struct ecryptfs_key_record *key_rec,
+ struct ecryptfs_message *msg)
+{
+ size_t i = 0;
+ char *data;
+ size_t data_len;
+ size_t message_len;
+ int rc;
+
+ /*
+ * ***** TAG 65 Packet Format *****
+ * | Content Type | 1 byte |
+ * | Status Indicator | 1 byte |
+ * | Encrypted File Encryption Key Size | 1 or 2 bytes |
+ * | Encrypted File Encryption Key | arbitrary |
+ */
+ message_len = msg->data_len;
+ data = msg->data;
+ /* verify that everything through the encrypted FEK size is present */
+ if (message_len < 4) {
+ rc = -EIO;
+ goto out;
+ }
+ if (data[i++] != ECRYPTFS_TAG_67_PACKET_TYPE) {
+ ecryptfs_printk(KERN_ERR, "Type should be ECRYPTFS_TAG_67\n");
+ rc = -EIO;
+ goto out;
+ }
+ if (data[i++]) {
+ ecryptfs_printk(KERN_ERR, "Status indicator has non zero value"
+ " [%d]\n", data[i-1]);
+ rc = -EIO;
+ goto out;
+ }
+ rc = parse_packet_length(&data[i], &key_rec->enc_key_size, &data_len);
+ if (rc) {
+ ecryptfs_printk(KERN_WARNING, "Error parsing packet length; "
+ "rc = [%d]\n", rc);
+ goto out;
+ }
+ i += data_len;
+ if (message_len < (i + key_rec->enc_key_size)) {
+ ecryptfs_printk(KERN_ERR, "message_len [%d]; max len is [%d]\n",
+ message_len, (i + key_rec->enc_key_size));
+ rc = -EIO;
+ goto out;
+ }
+ if (key_rec->enc_key_size > ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES) {
+ ecryptfs_printk(KERN_ERR, "Encrypted key_size [%d] larger than "
+ "the maximum key size [%d]\n",
+ key_rec->enc_key_size,
+ ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES);
+ rc = -EIO;
+ goto out;
+ }
+ memcpy(key_rec->enc_key, &data[i], key_rec->enc_key_size);
+out:
+ return rc;
+}
+
+/**
+ * decrypt_pki_encrypted_session_key - Decrypt the session key with
+ * the given auth_tok.
+ *
+ * Returns Zero on success; non-zero error otherwise.
+ */
+static int decrypt_pki_encrypted_session_key(
+ struct ecryptfs_mount_crypt_stat *mount_crypt_stat,
+ struct ecryptfs_auth_tok *auth_tok,
+ struct ecryptfs_crypt_stat *crypt_stat)
+{
+ u16 cipher_code = 0;
+ struct ecryptfs_msg_ctx *msg_ctx;
+ struct ecryptfs_message *msg = NULL;
+ char *netlink_message;
+ size_t netlink_message_length;
+ int rc;
+
+ rc = write_tag_64_packet(mount_crypt_stat->global_auth_tok_sig,
+ &(auth_tok->session_key),
+ &netlink_message, &netlink_message_length);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Failed to write tag 64 packet");
+ goto out;
+ }
+ rc = ecryptfs_send_message(ecryptfs_transport, netlink_message,
+ netlink_message_length, &msg_ctx);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error sending netlink message\n");
+ goto out;
+ }
+ rc = ecryptfs_wait_for_response(msg_ctx, &msg);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Failed to receive tag 65 packet "
+ "from the user space daemon\n");
+ rc = -EIO;
+ goto out;
+ }
+ rc = parse_tag_65_packet(&(auth_tok->session_key),
+ &cipher_code, msg);
+ if (rc) {
+ printk(KERN_ERR "Failed to parse tag 65 packet; rc = [%d]\n",
+ rc);
+ goto out;
+ }
+ auth_tok->session_key.flags |= ECRYPTFS_CONTAINS_DECRYPTED_KEY;
+ memcpy(crypt_stat->key, auth_tok->session_key.decrypted_key,
+ auth_tok->session_key.decrypted_key_size);
+ crypt_stat->key_size = auth_tok->session_key.decrypted_key_size;
+ rc = ecryptfs_cipher_code_to_string(crypt_stat->cipher, cipher_code);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Cipher code [%d] is invalid\n",
+ cipher_code)
+ goto out;
+ }
+ crypt_stat->flags |= ECRYPTFS_KEY_VALID;
+ if (ecryptfs_verbosity > 0) {
+ ecryptfs_printk(KERN_DEBUG, "Decrypted session key:\n");
+ ecryptfs_dump_hex(crypt_stat->key,
+ crypt_stat->key_size);
+ }
+out:
+ if (msg)
+ kfree(msg);
+ return rc;
+}
+
+static void wipe_auth_tok_list(struct list_head *auth_tok_list_head)
+{
+ struct list_head *walker;
+ struct ecryptfs_auth_tok_list_item *auth_tok_list_item;
+
+ walker = auth_tok_list_head->next;
+ while (walker != auth_tok_list_head) {
+ auth_tok_list_item =
+ list_entry(walker, struct ecryptfs_auth_tok_list_item,
+ list);
+ walker = auth_tok_list_item->list.next;
+ memset(auth_tok_list_item, 0,
+ sizeof(struct ecryptfs_auth_tok_list_item));
+ kmem_cache_free(ecryptfs_auth_tok_list_item_cache,
+ auth_tok_list_item);
+ }
+ auth_tok_list_head->next = NULL;
+}
+
+struct kmem_cache *ecryptfs_auth_tok_list_item_cache;
+
+
+/**
+ * parse_tag_1_packet
+ * @crypt_stat: The cryptographic context to modify based on packet
+ * contents.
+ * @data: The raw bytes of the packet.
+ * @auth_tok_list: eCryptfs parses packets into authentication tokens;
+ * a new authentication token will be placed at the end
+ * of this list for this packet.
+ * @new_auth_tok: Pointer to a pointer to memory that this function
+ * allocates; sets the memory address of the pointer to
+ * NULL on error. This object is added to the
+ * auth_tok_list.
+ * @packet_size: This function writes the size of the parsed packet
+ * into this memory location; zero on error.
+ *
+ * Returns zero on success; non-zero on error.
+ */
+static int
+parse_tag_1_packet(struct ecryptfs_crypt_stat *crypt_stat,
+ unsigned char *data, struct list_head *auth_tok_list,
+ struct ecryptfs_auth_tok **new_auth_tok,
+ size_t *packet_size, size_t max_packet_size)
+{
+ size_t body_size;
+ struct ecryptfs_auth_tok_list_item *auth_tok_list_item;
+ size_t length_size;
+ int rc = 0;
+
+ (*packet_size) = 0;
+ (*new_auth_tok) = NULL;
+
+ /* we check that:
+ * one byte for the Tag 1 ID flag
+ * two bytes for the body size
+ * do not exceed the maximum_packet_size
+ */
+ if (unlikely((*packet_size) + 3 > max_packet_size)) {
+ ecryptfs_printk(KERN_ERR, "Packet size exceeds max\n");
+ rc = -EINVAL;
+ goto out;
+ }
+ /* check for Tag 1 identifier - one byte */
+ if (data[(*packet_size)++] != ECRYPTFS_TAG_1_PACKET_TYPE) {
+ ecryptfs_printk(KERN_ERR, "Enter w/ first byte != 0x%.2x\n",
+ ECRYPTFS_TAG_1_PACKET_TYPE);
+ rc = -EINVAL;
+ goto out;
+ }
+ /* Released: wipe_auth_tok_list called in ecryptfs_parse_packet_set or
+ * at end of function upon failure */
+ auth_tok_list_item =
+ kmem_cache_alloc(ecryptfs_auth_tok_list_item_cache,
+ GFP_KERNEL);
+ if (!auth_tok_list_item) {
+ ecryptfs_printk(KERN_ERR, "Unable to allocate memory\n");
+ rc = -ENOMEM;
+ goto out;
+ }
+ memset(auth_tok_list_item, 0,
+ sizeof(struct ecryptfs_auth_tok_list_item));
+ (*new_auth_tok) = &auth_tok_list_item->auth_tok;
+ /* check for body size - one to two bytes
+ *
+ * ***** TAG 1 Packet Format *****
+ * | version number | 1 byte |
+ * | key ID | 8 bytes |
+ * | public key algorithm | 1 byte |
+ * | encrypted session key | arbitrary |
+ */
+ rc = parse_packet_length(&data[(*packet_size)], &body_size,
+ &length_size);
+ if (rc) {
+ ecryptfs_printk(KERN_WARNING, "Error parsing packet length; "
+ "rc = [%d]\n", rc);
+ goto out_free;
+ }
+ if (unlikely(body_size < (0x02 + ECRYPTFS_SIG_SIZE))) {
+ ecryptfs_printk(KERN_WARNING, "Invalid body size ([%d])\n",
+ body_size);
+ rc = -EINVAL;
+ goto out_free;
+ }
+ (*packet_size) += length_size;
+ if (unlikely((*packet_size) + body_size > max_packet_size)) {
+ ecryptfs_printk(KERN_ERR, "Packet size exceeds max\n");
+ rc = -EINVAL;
+ goto out_free;
+ }
+ /* Version 3 (from RFC2440) - one byte */
+ if (unlikely(data[(*packet_size)++] != 0x03)) {
+ ecryptfs_printk(KERN_DEBUG, "Unknown version number "
+ "[%d]\n", data[(*packet_size) - 1]);
+ rc = -EINVAL;
+ goto out_free;
+ }
+ /* Read Signature */
+ ecryptfs_to_hex((*new_auth_tok)->token.private_key.signature,
+ &data[(*packet_size)], ECRYPTFS_SIG_SIZE);
+ *packet_size += ECRYPTFS_SIG_SIZE;
+ /* This byte is skipped because the kernel does not need to
+ * know which public key encryption algorithm was used */
+ (*packet_size)++;
+ (*new_auth_tok)->session_key.encrypted_key_size =
+ body_size - (0x02 + ECRYPTFS_SIG_SIZE);
+ if ((*new_auth_tok)->session_key.encrypted_key_size
+ > ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES) {
+ ecryptfs_printk(KERN_ERR, "Tag 1 packet contains key larger "
+ "than ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES");
+ rc = -EINVAL;
+ goto out;
+ }
+ ecryptfs_printk(KERN_DEBUG, "Encrypted key size = [%d]\n",
+ (*new_auth_tok)->session_key.encrypted_key_size);
+ memcpy((*new_auth_tok)->session_key.encrypted_key,
+ &data[(*packet_size)], (body_size - 0x02 - ECRYPTFS_SIG_SIZE));
+ (*packet_size) += (*new_auth_tok)->session_key.encrypted_key_size;
+ (*new_auth_tok)->session_key.flags &=
+ ~ECRYPTFS_CONTAINS_DECRYPTED_KEY;
+ (*new_auth_tok)->session_key.flags |=
+ ECRYPTFS_CONTAINS_ENCRYPTED_KEY;
+ (*new_auth_tok)->token_type = ECRYPTFS_PRIVATE_KEY;
+ (*new_auth_tok)->flags |= ECRYPTFS_PRIVATE_KEY;
+ /* TODO: Why are we setting this flag here? Don't we want the
+ * userspace to decrypt the session key? */
+ (*new_auth_tok)->session_key.flags &=
+ ~(ECRYPTFS_USERSPACE_SHOULD_TRY_TO_DECRYPT);
+ (*new_auth_tok)->session_key.flags &=
+ ~(ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT);
+ list_add(&auth_tok_list_item->list, auth_tok_list);
+ goto out;
+out_free:
+ (*new_auth_tok) = NULL;
+ memset(auth_tok_list_item, 0,
+ sizeof(struct ecryptfs_auth_tok_list_item));
+ kmem_cache_free(ecryptfs_auth_tok_list_item_cache,
+ auth_tok_list_item);
+out:
+ if (rc)
+ (*packet_size) = 0;
+ return rc;
+}
+
/**
* parse_tag_3_packet
* @crypt_stat: The cryptographic context to modify based on packet
@@ -178,10 +652,10 @@ parse_tag_3_packet(struct ecryptfs_crypt_stat *crypt_stat,
struct ecryptfs_auth_tok **new_auth_tok,
size_t *packet_size, size_t max_packet_size)
{
- int rc = 0;
size_t body_size;
struct ecryptfs_auth_tok_list_item *auth_tok_list_item;
size_t length_size;
+ int rc = 0;
(*packet_size) = 0;
(*new_auth_tok) = NULL;
@@ -207,14 +681,12 @@ parse_tag_3_packet(struct ecryptfs_crypt_stat *crypt_stat,
/* Released: wipe_auth_tok_list called in ecryptfs_parse_packet_set or
* at end of function upon failure */
auth_tok_list_item =
- kmem_cache_alloc(ecryptfs_auth_tok_list_item_cache, GFP_KERNEL);
+ kmem_cache_zalloc(ecryptfs_auth_tok_list_item_cache, GFP_KERNEL);
if (!auth_tok_list_item) {
ecryptfs_printk(KERN_ERR, "Unable to allocate memory\n");
rc = -ENOMEM;
goto out;
}
- memset(auth_tok_list_item, 0,
- sizeof(struct ecryptfs_auth_tok_list_item));
(*new_auth_tok) = &auth_tok_list_item->auth_tok;
/* check for body size - one to two bytes */
@@ -321,10 +793,10 @@ parse_tag_3_packet(struct ecryptfs_crypt_stat *crypt_stat,
(*new_auth_tok)->token_type = ECRYPTFS_PASSWORD;
/* TODO: Parametarize; we might actually want userspace to
* decrypt the session key. */
- ECRYPTFS_CLEAR_FLAG((*new_auth_tok)->session_key.flags,
- ECRYPTFS_USERSPACE_SHOULD_TRY_TO_DECRYPT);
- ECRYPTFS_CLEAR_FLAG((*new_auth_tok)->session_key.flags,
- ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT);
+ (*new_auth_tok)->session_key.flags &=
+ ~(ECRYPTFS_USERSPACE_SHOULD_TRY_TO_DECRYPT);
+ (*new_auth_tok)->session_key.flags &=
+ ~(ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT);
list_add(&auth_tok_list_item->list, auth_tok_list);
goto out;
out_free:
@@ -360,9 +832,9 @@ parse_tag_11_packet(unsigned char *data, unsigned char *contents,
size_t max_contents_bytes, size_t *tag_11_contents_size,
size_t *packet_size, size_t max_packet_size)
{
- int rc = 0;
size_t body_size;
size_t length_size;
+ int rc = 0;
(*packet_size) = 0;
(*tag_11_contents_size) = 0;
@@ -461,7 +933,6 @@ static int decrypt_session_key(struct ecryptfs_auth_tok *auth_tok,
struct ecryptfs_password *password_s_ptr;
struct scatterlist src_sg[2], dst_sg[2];
struct mutex *tfm_mutex = NULL;
- /* TODO: Use virt_to_scatterlist for these */
char *encrypted_session_key;
char *session_key;
struct blkcipher_desc desc = {
@@ -470,8 +941,7 @@ static int decrypt_session_key(struct ecryptfs_auth_tok *auth_tok,
int rc = 0;
password_s_ptr = &auth_tok->token.password;
- if (ECRYPTFS_CHECK_FLAG(password_s_ptr->flags,
- ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET))
+ if (password_s_ptr->flags & ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET)
ecryptfs_printk(KERN_DEBUG, "Session key encryption key "
"set; skipping key generation\n");
ecryptfs_printk(KERN_DEBUG, "Session key encryption key (size [%d])"
@@ -553,7 +1023,7 @@ static int decrypt_session_key(struct ecryptfs_auth_tok *auth_tok,
auth_tok->session_key.flags |= ECRYPTFS_CONTAINS_DECRYPTED_KEY;
memcpy(crypt_stat->key, auth_tok->session_key.decrypted_key,
auth_tok->session_key.decrypted_key_size);
- ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_KEY_VALID);
+ crypt_stat->flags |= ECRYPTFS_KEY_VALID;
ecryptfs_printk(KERN_DEBUG, "Decrypted session key:\n");
if (ecryptfs_verbosity > 0)
ecryptfs_dump_hex(crypt_stat->key,
@@ -589,7 +1059,6 @@ int ecryptfs_parse_packet_set(struct ecryptfs_crypt_stat *crypt_stat,
struct dentry *ecryptfs_dentry)
{
size_t i = 0;
- int rc = 0;
size_t found_auth_tok = 0;
size_t next_packet_is_auth_tok_packet;
char sig[ECRYPTFS_SIG_SIZE_HEX];
@@ -605,6 +1074,7 @@ int ecryptfs_parse_packet_set(struct ecryptfs_crypt_stat *crypt_stat,
unsigned char sig_tmp_space[ECRYPTFS_SIG_SIZE];
size_t tag_11_contents_size;
size_t tag_11_packet_size;
+ int rc = 0;
INIT_LIST_HEAD(&auth_tok_list);
/* Parse the header to find as many packets as we can, these will be
@@ -656,8 +1126,21 @@ int ecryptfs_parse_packet_set(struct ecryptfs_crypt_stat *crypt_stat,
sig_tmp_space, tag_11_contents_size);
new_auth_tok->token.password.signature[
ECRYPTFS_PASSWORD_SIG_SIZE] = '\0';
- ECRYPTFS_SET_FLAG(crypt_stat->flags,
- ECRYPTFS_ENCRYPTED);
+ crypt_stat->flags |= ECRYPTFS_ENCRYPTED;
+ break;
+ case ECRYPTFS_TAG_1_PACKET_TYPE:
+ rc = parse_tag_1_packet(crypt_stat,
+ (unsigned char *)&src[i],
+ &auth_tok_list, &new_auth_tok,
+ &packet_size, max_packet_size);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error parsing "
+ "tag 1 packet\n");
+ rc = -EIO;
+ goto out_wipe_list;
+ }
+ i += packet_size;
+ crypt_stat->flags |= ECRYPTFS_ENCRYPTED;
break;
case ECRYPTFS_TAG_11_PACKET_TYPE:
ecryptfs_printk(KERN_WARNING, "Invalid packet set "
@@ -706,31 +1189,46 @@ int ecryptfs_parse_packet_set(struct ecryptfs_crypt_stat *crypt_stat,
goto leave_list;
/* TODO: Transfer the common salt into the
* crypt_stat salt */
+ } else if ((candidate_auth_tok->token_type
+ == ECRYPTFS_PRIVATE_KEY)
+ && !strncmp(candidate_auth_tok->token.private_key.signature,
+ sig, ECRYPTFS_SIG_SIZE_HEX)) {
+ found_auth_tok = 1;
+ goto leave_list;
}
}
-leave_list:
if (!found_auth_tok) {
ecryptfs_printk(KERN_ERR, "Could not find authentication "
"token on temporary list for sig [%.*s]\n",
ECRYPTFS_SIG_SIZE_HEX, sig);
rc = -EIO;
goto out_wipe_list;
- } else {
+ }
+leave_list:
+ rc = -ENOTSUPP;
+ if (candidate_auth_tok->token_type == ECRYPTFS_PRIVATE_KEY) {
+ memcpy(&(candidate_auth_tok->token.private_key),
+ &(chosen_auth_tok->token.private_key),
+ sizeof(struct ecryptfs_private_key));
+ rc = decrypt_pki_encrypted_session_key(mount_crypt_stat,
+ candidate_auth_tok,
+ crypt_stat);
+ } else if (candidate_auth_tok->token_type == ECRYPTFS_PASSWORD) {
memcpy(&(candidate_auth_tok->token.password),
&(chosen_auth_tok->token.password),
sizeof(struct ecryptfs_password));
rc = decrypt_session_key(candidate_auth_tok, crypt_stat);
- if (rc) {
- ecryptfs_printk(KERN_ERR, "Error decrypting the "
- "session key\n");
- goto out_wipe_list;
- }
- rc = ecryptfs_compute_root_iv(crypt_stat);
- if (rc) {
- ecryptfs_printk(KERN_ERR, "Error computing "
- "the root IV\n");
- goto out_wipe_list;
- }
+ }
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error decrypting the "
+ "session key; rc = [%d]\n", rc);
+ goto out_wipe_list;
+ }
+ rc = ecryptfs_compute_root_iv(crypt_stat);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error computing "
+ "the root IV\n");
+ goto out_wipe_list;
}
rc = ecryptfs_init_crypt_ctx(crypt_stat);
if (rc) {
@@ -743,6 +1241,145 @@ out_wipe_list:
out:
return rc;
}
+static int
+pki_encrypt_session_key(struct ecryptfs_auth_tok *auth_tok,
+ struct ecryptfs_crypt_stat *crypt_stat,
+ struct ecryptfs_key_record *key_rec)
+{
+ struct ecryptfs_msg_ctx *msg_ctx = NULL;
+ char *netlink_payload;
+ size_t netlink_payload_length;
+ struct ecryptfs_message *msg;
+ int rc;
+
+ rc = write_tag_66_packet(auth_tok->token.private_key.signature,
+ ecryptfs_code_for_cipher_string(crypt_stat),
+ crypt_stat, &netlink_payload,
+ &netlink_payload_length);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error generating tag 66 packet\n");
+ goto out;
+ }
+ rc = ecryptfs_send_message(ecryptfs_transport, netlink_payload,
+ netlink_payload_length, &msg_ctx);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error sending netlink message\n");
+ goto out;
+ }
+ rc = ecryptfs_wait_for_response(msg_ctx, &msg);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Failed to receive tag 67 packet "
+ "from the user space daemon\n");
+ rc = -EIO;
+ goto out;
+ }
+ rc = parse_tag_67_packet(key_rec, msg);
+ if (rc)
+ ecryptfs_printk(KERN_ERR, "Error parsing tag 67 packet\n");
+ kfree(msg);
+out:
+ if (netlink_payload)
+ kfree(netlink_payload);
+ return rc;
+}
+/**
+ * write_tag_1_packet - Write an RFC2440-compatible tag 1 (public key) packet
+ * @dest: Buffer into which to write the packet
+ * @max: Maximum number of bytes that can be writtn
+ * @packet_size: This function will write the number of bytes that end
+ * up constituting the packet; set to zero on error
+ *
+ * Returns zero on success; non-zero on error.
+ */
+static int
+write_tag_1_packet(char *dest, size_t max, struct ecryptfs_auth_tok *auth_tok,
+ struct ecryptfs_crypt_stat *crypt_stat,
+ struct ecryptfs_mount_crypt_stat *mount_crypt_stat,
+ struct ecryptfs_key_record *key_rec, size_t *packet_size)
+{
+ size_t i;
+ size_t encrypted_session_key_valid = 0;
+ size_t key_rec_size;
+ size_t packet_size_length;
+ int rc = 0;
+
+ (*packet_size) = 0;
+ ecryptfs_from_hex(key_rec->sig, auth_tok->token.private_key.signature,
+ ECRYPTFS_SIG_SIZE);
+ encrypted_session_key_valid = 0;
+ for (i = 0; i < crypt_stat->key_size; i++)
+ encrypted_session_key_valid |=
+ auth_tok->session_key.encrypted_key[i];
+ if (encrypted_session_key_valid) {
+ memcpy(key_rec->enc_key,
+ auth_tok->session_key.encrypted_key,
+ auth_tok->session_key.encrypted_key_size);
+ goto encrypted_session_key_set;
+ }
+ if (auth_tok->session_key.encrypted_key_size == 0)
+ auth_tok->session_key.encrypted_key_size =
+ auth_tok->token.private_key.key_size;
+ rc = pki_encrypt_session_key(auth_tok, crypt_stat, key_rec);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Failed to encrypt session key "
+ "via a pki");
+ goto out;
+ }
+ if (ecryptfs_verbosity > 0) {
+ ecryptfs_printk(KERN_DEBUG, "Encrypted key:\n");
+ ecryptfs_dump_hex(key_rec->enc_key, key_rec->enc_key_size);
+ }
+encrypted_session_key_set:
+ /* Now we have a valid key_rec. Append it to the
+ * key_rec set. */
+ key_rec_size = (sizeof(struct ecryptfs_key_record)
+ - ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES
+ + (key_rec->enc_key_size));
+ /* TODO: Include a packet size limit as a parameter to this
+ * function once we have multi-packet headers (for versions
+ * later than 0.1 */
+ if (key_rec_size >= ECRYPTFS_MAX_KEYSET_SIZE) {
+ ecryptfs_printk(KERN_ERR, "Keyset too large\n");
+ rc = -EINVAL;
+ goto out;
+ }
+ /* ***** TAG 1 Packet Format *****
+ * | version number | 1 byte |
+ * | key ID | 8 bytes |
+ * | public key algorithm | 1 byte |
+ * | encrypted session key | arbitrary |
+ */
+ if ((0x02 + ECRYPTFS_SIG_SIZE + key_rec->enc_key_size) >= max) {
+ ecryptfs_printk(KERN_ERR,
+ "Authentication token is too large\n");
+ rc = -EINVAL;
+ goto out;
+ }
+ dest[(*packet_size)++] = ECRYPTFS_TAG_1_PACKET_TYPE;
+ /* This format is inspired by OpenPGP; see RFC 2440
+ * packet tag 1 */
+ rc = write_packet_length(&dest[(*packet_size)],
+ (0x02 + ECRYPTFS_SIG_SIZE +
+ key_rec->enc_key_size),
+ &packet_size_length);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error generating tag 1 packet "
+ "header; cannot generate packet length\n");
+ goto out;
+ }
+ (*packet_size) += packet_size_length;
+ dest[(*packet_size)++] = 0x03; /* version 3 */
+ memcpy(&dest[(*packet_size)], key_rec->sig, ECRYPTFS_SIG_SIZE);
+ (*packet_size) += ECRYPTFS_SIG_SIZE;
+ dest[(*packet_size)++] = RFC2440_CIPHER_RSA;
+ memcpy(&dest[(*packet_size)], key_rec->enc_key,
+ key_rec->enc_key_size);
+ (*packet_size) += key_rec->enc_key_size;
+out:
+ if (rc)
+ (*packet_size) = 0;
+ return rc;
+}
/**
* write_tag_11_packet
@@ -758,8 +1395,8 @@ static int
write_tag_11_packet(char *dest, int max, char *contents, size_t contents_length,
size_t *packet_length)
{
- int rc = 0;
size_t packet_size_length;
+ int rc = 0;
(*packet_length) = 0;
if ((13 + contents_length) > max) {
@@ -817,7 +1454,6 @@ write_tag_3_packet(char *dest, size_t max, struct ecryptfs_auth_tok *auth_tok,
struct ecryptfs_key_record *key_rec, size_t *packet_size)
{
size_t i;
- size_t signature_is_valid = 0;
size_t encrypted_session_key_valid = 0;
char session_key_encryption_key[ECRYPTFS_MAX_KEY_BYTES];
struct scatterlist dest_sg[2];
@@ -833,19 +1469,14 @@ write_tag_3_packet(char *dest, size_t max, struct ecryptfs_auth_tok *auth_tok,
int rc = 0;
(*packet_size) = 0;
- /* Check for a valid signature on the auth_tok */
- for (i = 0; i < ECRYPTFS_SIG_SIZE_HEX; i++)
- signature_is_valid |= auth_tok->token.password.signature[i];
- if (!signature_is_valid)
- BUG();
- ecryptfs_from_hex((*key_rec).sig, auth_tok->token.password.signature,
+ ecryptfs_from_hex(key_rec->sig, auth_tok->token.password.signature,
ECRYPTFS_SIG_SIZE);
encrypted_session_key_valid = 0;
for (i = 0; i < crypt_stat->key_size; i++)
encrypted_session_key_valid |=
auth_tok->session_key.encrypted_key[i];
if (encrypted_session_key_valid) {
- memcpy((*key_rec).enc_key,
+ memcpy(key_rec->enc_key,
auth_tok->session_key.encrypted_key,
auth_tok->session_key.encrypted_key_size);
goto encrypted_session_key_set;
@@ -858,10 +1489,10 @@ write_tag_3_packet(char *dest, size_t max, struct ecryptfs_auth_tok *auth_tok,
memset((crypt_stat->key + 24), 0, 8);
auth_tok->session_key.encrypted_key_size = 32;
}
- (*key_rec).enc_key_size =
+ key_rec->enc_key_size =
auth_tok->session_key.encrypted_key_size;
- if (ECRYPTFS_CHECK_FLAG(auth_tok->token.password.flags,
- ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET)) {
+ if (auth_tok->token.password.flags &
+ ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET) {
ecryptfs_printk(KERN_DEBUG, "Using previously generated "
"session key encryption key of size [%d]\n",
auth_tok->token.password.
@@ -879,15 +1510,15 @@ write_tag_3_packet(char *dest, size_t max, struct ecryptfs_auth_tok *auth_tok,
ecryptfs_dump_hex(session_key_encryption_key, 16);
}
rc = virt_to_scatterlist(crypt_stat->key,
- (*key_rec).enc_key_size, src_sg, 2);
+ key_rec->enc_key_size, src_sg, 2);
if (!rc) {
ecryptfs_printk(KERN_ERR, "Error generating scatterlist "
"for crypt_stat session key\n");
rc = -ENOMEM;
goto out;
}
- rc = virt_to_scatterlist((*key_rec).enc_key,
- (*key_rec).enc_key_size, dest_sg, 2);
+ rc = virt_to_scatterlist(key_rec->enc_key,
+ key_rec->enc_key_size, dest_sg, 2);
if (!rc) {
ecryptfs_printk(KERN_ERR, "Error generating scatterlist "
"for crypt_stat encrypted session key\n");
@@ -943,14 +1574,14 @@ write_tag_3_packet(char *dest, size_t max, struct ecryptfs_auth_tok *auth_tok,
mutex_unlock(tfm_mutex);
ecryptfs_printk(KERN_DEBUG, "This should be the encrypted key:\n");
if (ecryptfs_verbosity > 0)
- ecryptfs_dump_hex((*key_rec).enc_key,
- (*key_rec).enc_key_size);
+ ecryptfs_dump_hex(key_rec->enc_key,
+ key_rec->enc_key_size);
encrypted_session_key_set:
/* Now we have a valid key_rec. Append it to the
* key_rec set. */
key_rec_size = (sizeof(struct ecryptfs_key_record)
- ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES
- + ((*key_rec).enc_key_size));
+ + (key_rec->enc_key_size));
/* TODO: Include a packet size limit as a parameter to this
* function once we have multi-packet headers (for versions
* later than 0.1 */
@@ -962,7 +1593,7 @@ encrypted_session_key_set:
/* TODO: Packet size limit */
/* We have 5 bytes of surrounding packet data */
if ((0x05 + ECRYPTFS_SALT_SIZE
- + (*key_rec).enc_key_size) >= max) {
+ + key_rec->enc_key_size) >= max) {
ecryptfs_printk(KERN_ERR, "Authentication token is too "
"large\n");
rc = -EINVAL;
@@ -974,7 +1605,7 @@ encrypted_session_key_set:
/* ver+cipher+s2k+hash+salt+iter+enc_key */
rc = write_packet_length(&dest[(*packet_size)],
(0x05 + ECRYPTFS_SALT_SIZE
- + (*key_rec).enc_key_size),
+ + key_rec->enc_key_size),
&packet_size_length);
if (rc) {
ecryptfs_printk(KERN_ERR, "Error generating tag 3 packet "
@@ -997,9 +1628,9 @@ encrypted_session_key_set:
ECRYPTFS_SALT_SIZE);
(*packet_size) += ECRYPTFS_SALT_SIZE; /* salt */
dest[(*packet_size)++] = 0x60; /* hash iterations (65536) */
- memcpy(&dest[(*packet_size)], (*key_rec).enc_key,
- (*key_rec).enc_key_size);
- (*packet_size) += (*key_rec).enc_key_size;
+ memcpy(&dest[(*packet_size)], key_rec->enc_key,
+ key_rec->enc_key_size);
+ (*packet_size) += key_rec->enc_key_size;
out:
if (desc.tfm && !tfm_mutex)
crypto_free_blkcipher(desc.tfm);
@@ -1029,13 +1660,13 @@ ecryptfs_generate_key_packet_set(char *dest_base,
struct dentry *ecryptfs_dentry, size_t *len,
size_t max)
{
- int rc = 0;
struct ecryptfs_auth_tok *auth_tok;
struct ecryptfs_mount_crypt_stat *mount_crypt_stat =
&ecryptfs_superblock_to_private(
ecryptfs_dentry->d_sb)->mount_crypt_stat;
size_t written;
struct ecryptfs_key_record key_rec;
+ int rc = 0;
(*len) = 0;
if (mount_crypt_stat->global_auth_tok) {
@@ -1062,20 +1693,23 @@ ecryptfs_generate_key_packet_set(char *dest_base,
goto out;
}
(*len) += written;
+ } else if (auth_tok->token_type == ECRYPTFS_PRIVATE_KEY) {
+ rc = write_tag_1_packet(dest_base + (*len),
+ max, auth_tok,
+ crypt_stat,mount_crypt_stat,
+ &key_rec, &written);
+ if (rc) {
+ ecryptfs_printk(KERN_WARNING, "Error "
+ "writing tag 1 packet\n");
+ goto out;
+ }
+ (*len) += written;
} else {
ecryptfs_printk(KERN_WARNING, "Unsupported "
"authentication token type\n");
rc = -EINVAL;
goto out;
}
- if (rc) {
- ecryptfs_printk(KERN_WARNING, "Error writing "
- "authentication token packet with sig "
- "= [%s]\n",
- mount_crypt_stat->global_auth_tok_sig);
- rc = -EIO;
- goto out;
- }
} else
BUG();
if (likely((max - (*len)) > 0)) {
diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c
index d0541ae8fab..26fe405a576 100644
--- a/fs/ecryptfs/main.c
+++ b/fs/ecryptfs/main.c
@@ -3,9 +3,10 @@
*
* 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 <mahalcro@us.ibm.com>
* Michael C. Thompson <mcthomps@us.ibm.com>
+ * Tyler Hicks <tyhicks@ou.edu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -48,6 +49,43 @@ MODULE_PARM_DESC(ecryptfs_verbosity,
"Initial verbosity level (0 or 1; defaults to "
"0, which is Quiet)");
+/**
+ * Module parameter that defines the number of netlink message buffer
+ * elements
+ */
+unsigned int ecryptfs_message_buf_len = ECRYPTFS_DEFAULT_MSG_CTX_ELEMS;
+
+module_param(ecryptfs_message_buf_len, uint, 0);
+MODULE_PARM_DESC(ecryptfs_message_buf_len,
+ "Number of message buffer elements");
+
+/**
+ * Module parameter that defines the maximum guaranteed amount of time to wait
+ * for a response through netlink. The actual sleep time will be, more than
+ * likely, a small amount greater than this specified value, but only less if
+ * the netlink message successfully arrives.
+ */
+signed long ecryptfs_message_wait_timeout = ECRYPTFS_MAX_MSG_CTX_TTL / HZ;
+
+module_param(ecryptfs_message_wait_timeout, long, 0);
+MODULE_PARM_DESC(ecryptfs_message_wait_timeout,
+ "Maximum number of seconds that an operation will "
+ "sleep while waiting for a message response from "
+ "userspace");
+
+/**
+ * Module parameter that is an estimate of the maximum number of users
+ * that will be concurrently using eCryptfs. Set this to the right
+ * value to balance performance and memory use.
+ */
+unsigned int ecryptfs_number_of_users = ECRYPTFS_DEFAULT_NUM_USERS;
+
+module_param(ecryptfs_number_of_users, uint, 0);
+MODULE_PARM_DESC(ecryptfs_number_of_users, "An estimate of the number of "
+ "concurrent users of eCryptfs");
+
+unsigned int ecryptfs_transport = ECRYPTFS_DEFAULT_TRANSPORT;
+
void __ecryptfs_printk(const char *fmt, ...)
{
va_list args;
@@ -124,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"},
@@ -135,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}
};
@@ -275,6 +316,16 @@ static int ecryptfs_parse_options(struct super_block *sb, char *options)
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,
@@ -347,9 +398,10 @@ static int ecryptfs_parse_options(struct super_block *sb, char *options)
rc = -EINVAL;
goto out;
}
- if (auth_tok->token_type != ECRYPTFS_PASSWORD) {
+ if (auth_tok->token_type != ECRYPTFS_PASSWORD
+ && auth_tok->token_type != ECRYPTFS_PRIVATE_KEY) {
ecryptfs_printk(KERN_ERR, "Invalid auth_tok structure "
- "returned from key\n");
+ "returned from key query\n");
rc = -EINVAL;
goto out;
}
@@ -378,15 +430,13 @@ ecryptfs_fill_super(struct super_block *sb, void *raw_data, int silent)
/* Released in ecryptfs_put_super() */
ecryptfs_set_superblock_private(sb,
- kmem_cache_alloc(ecryptfs_sb_info_cache,
+ kmem_cache_zalloc(ecryptfs_sb_info_cache,
GFP_KERNEL));
if (!ecryptfs_superblock_to_private(sb)) {
ecryptfs_printk(KERN_WARNING, "Out of memory\n");
rc = -ENOMEM;
goto out;
}
- memset(ecryptfs_superblock_to_private(sb), 0,
- sizeof(struct ecryptfs_sb_info));
sb->s_op = &ecryptfs_sops;
/* Released through deactivate_super(sb) from get_sb_nodev */
sb->s_root = d_alloc(NULL, &(const struct qstr) {
@@ -402,7 +452,7 @@ ecryptfs_fill_super(struct super_block *sb, void *raw_data, int silent)
/* Released in d_release when dput(sb->s_root) is called */
/* through deactivate_super(sb) from get_sb_nodev() */
ecryptfs_set_dentry_private(sb->s_root,
- kmem_cache_alloc(ecryptfs_dentry_info_cache,
+ kmem_cache_zalloc(ecryptfs_dentry_info_cache,
GFP_KERNEL));
if (!ecryptfs_dentry_to_private(sb->s_root)) {
ecryptfs_printk(KERN_ERR,
@@ -410,8 +460,6 @@ ecryptfs_fill_super(struct super_block *sb, void *raw_data, int silent)
rc = -ENOMEM;
goto out;
}
- memset(ecryptfs_dentry_to_private(sb->s_root), 0,
- sizeof(struct ecryptfs_dentry_info));
rc = 0;
out:
/* Should be able to rely on deactivate_super called from
@@ -594,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,
@@ -699,7 +752,8 @@ static struct ecryptfs_version_str_map_elem {
{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)
@@ -798,6 +852,11 @@ static int __init ecryptfs_init(void)
ecryptfs_free_kmem_caches();
goto out;
}
+ rc = ecryptfs_init_messaging(ecryptfs_transport);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Failure occured while attempting to "
+ "initialize the eCryptfs netlink socket\n");
+ }
out:
return rc;
}
@@ -809,6 +868,7 @@ static void __exit ecryptfs_exit(void)
sysfs_remove_file(&ecryptfs_subsys.kset.kobj,
&sysfs_attr_version_str.attr);
subsystem_unregister(&ecryptfs_subsys);
+ ecryptfs_release_messaging(ecryptfs_transport);
unregister_filesystem(&ecryptfs_fs_type);
ecryptfs_free_kmem_caches();
}
diff --git a/fs/ecryptfs/messaging.c b/fs/ecryptfs/messaging.c
new file mode 100644
index 00000000000..47d7e7b611f
--- /dev/null
+++ b/fs/ecryptfs/messaging.c
@@ -0,0 +1,515 @@
+/**
+ * eCryptfs: Linux filesystem encryption layer
+ *
+ * Copyright (C) 2004-2006 International Business Machines Corp.
+ * Author(s): Michael A. Halcrow <mhalcrow@us.ibm.com>
+ * Tyler Hicks <tyhicks@ou.edu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "ecryptfs_kernel.h"
+
+static LIST_HEAD(ecryptfs_msg_ctx_free_list);
+static LIST_HEAD(ecryptfs_msg_ctx_alloc_list);
+static struct mutex ecryptfs_msg_ctx_lists_mux;
+
+static struct hlist_head *ecryptfs_daemon_id_hash;
+static struct mutex ecryptfs_daemon_id_hash_mux;
+static int ecryptfs_hash_buckets;
+#define ecryptfs_uid_hash(uid) \
+ hash_long((unsigned long)uid, ecryptfs_hash_buckets)
+
+static unsigned int ecryptfs_msg_counter;
+static struct ecryptfs_msg_ctx *ecryptfs_msg_ctx_arr;
+
+/**
+ * ecryptfs_acquire_free_msg_ctx
+ * @msg_ctx: The context that was acquired from the free list
+ *
+ * Acquires a context element from the free list and locks the mutex
+ * on the context. Returns zero on success; non-zero on error or upon
+ * failure to acquire a free context element. Be sure to lock the
+ * list mutex before calling.
+ */
+static int ecryptfs_acquire_free_msg_ctx(struct ecryptfs_msg_ctx **msg_ctx)
+{
+ struct list_head *p;
+ int rc;
+
+ if (list_empty(&ecryptfs_msg_ctx_free_list)) {
+ ecryptfs_printk(KERN_WARNING, "The eCryptfs free "
+ "context list is empty. It may be helpful to "
+ "specify the ecryptfs_message_buf_len "
+ "parameter to be greater than the current "
+ "value of [%d]\n", ecryptfs_message_buf_len);
+ rc = -ENOMEM;
+ goto out;
+ }
+ list_for_each(p, &ecryptfs_msg_ctx_free_list) {
+ *msg_ctx = list_entry(p, struct ecryptfs_msg_ctx, node);
+ if (mutex_trylock(&(*msg_ctx)->mux)) {
+ (*msg_ctx)->task = current;
+ rc = 0;
+ goto out;
+ }
+ }
+ rc = -ENOMEM;
+out:
+ return rc;
+}
+
+/**
+ * ecryptfs_msg_ctx_free_to_alloc
+ * @msg_ctx: The context to move from the free list to the alloc list
+ *
+ * Be sure to lock the list mutex and the context mutex before
+ * calling.
+ */
+static void ecryptfs_msg_ctx_free_to_alloc(struct ecryptfs_msg_ctx *msg_ctx)
+{
+ list_move(&msg_ctx->node, &ecryptfs_msg_ctx_alloc_list);
+ msg_ctx->state = ECRYPTFS_MSG_CTX_STATE_PENDING;
+ msg_ctx->counter = ++ecryptfs_msg_counter;
+}
+
+/**
+ * ecryptfs_msg_ctx_alloc_to_free
+ * @msg_ctx: The context to move from the alloc list to the free list
+ *
+ * Be sure to lock the list mutex and the context mutex before
+ * calling.
+ */
+static void ecryptfs_msg_ctx_alloc_to_free(struct ecryptfs_msg_ctx *msg_ctx)
+{
+ list_move(&(msg_ctx->node), &ecryptfs_msg_ctx_free_list);
+ if (msg_ctx->msg)
+ kfree(msg_ctx->msg);
+ msg_ctx->state = ECRYPTFS_MSG_CTX_STATE_FREE;
+}
+
+/**
+ * ecryptfs_find_daemon_id
+ * @uid: The user id which maps to the desired daemon id
+ * @id: If return value is zero, points to the desired daemon id
+ * pointer
+ *
+ * Search the hash list for the given user id. Returns zero if the
+ * user id exists in the list; non-zero otherwise. The daemon id hash
+ * mutex should be held before calling this function.
+ */
+static int ecryptfs_find_daemon_id(uid_t uid, struct ecryptfs_daemon_id **id)
+{
+ struct hlist_node *elem;
+ int rc;
+
+ hlist_for_each_entry(*id, elem,
+ &ecryptfs_daemon_id_hash[ecryptfs_uid_hash(uid)],
+ id_chain) {
+ if ((*id)->uid == uid) {
+ rc = 0;
+ goto out;
+ }
+ }
+ rc = -EINVAL;
+out:
+ return rc;
+}
+
+static int ecryptfs_send_raw_message(unsigned int transport, u16 msg_type,
+ pid_t pid)
+{
+ int rc;
+
+ switch(transport) {
+ case ECRYPTFS_TRANSPORT_NETLINK:
+ rc = ecryptfs_send_netlink(NULL, 0, NULL, msg_type, 0, pid);
+ break;
+ case ECRYPTFS_TRANSPORT_CONNECTOR:
+ case ECRYPTFS_TRANSPORT_RELAYFS:
+ default:
+ rc = -ENOSYS;
+ }
+ return rc;
+}
+
+/**
+ * ecryptfs_process_helo
+ * @transport: The underlying transport (netlink, etc.)
+ * @uid: The user ID owner of the message
+ * @pid: The process ID for the userspace program that sent the
+ * message
+ *
+ * Adds the uid and pid values to the daemon id hash. If a uid
+ * already has a daemon pid registered, the daemon will be
+ * unregistered before the new daemon id is put into the hash list.
+ * Returns zero after adding a new daemon id to the hash list;
+ * non-zero otherwise.
+ */
+int ecryptfs_process_helo(unsigned int transport, uid_t uid, pid_t pid)
+{
+ struct ecryptfs_daemon_id *new_id;
+ struct ecryptfs_daemon_id *old_id;
+ int rc;
+
+ mutex_lock(&ecryptfs_daemon_id_hash_mux);
+ new_id = kmalloc(sizeof(*new_id), GFP_KERNEL);
+ if (!new_id) {
+ rc = -ENOMEM;
+ ecryptfs_printk(KERN_ERR, "Failed to allocate memory; unable "
+ "to register daemon [%d] for user\n", pid, uid);
+ goto unlock;
+ }
+ if (!ecryptfs_find_daemon_id(uid, &old_id)) {
+ printk(KERN_WARNING "Received request from user [%d] "
+ "to register daemon [%d]; unregistering daemon "
+ "[%d]\n", uid, pid, old_id->pid);
+ hlist_del(&old_id->id_chain);
+ rc = ecryptfs_send_raw_message(transport, ECRYPTFS_NLMSG_QUIT,
+ old_id->pid);
+ if (rc)
+ printk(KERN_WARNING "Failed to send QUIT "
+ "message to daemon [%d]; rc = [%d]\n",
+ old_id->pid, rc);
+ kfree(old_id);
+ }
+ new_id->uid = uid;
+ new_id->pid = pid;
+ hlist_add_head(&new_id->id_chain,
+ &ecryptfs_daemon_id_hash[ecryptfs_uid_hash(uid)]);
+ rc = 0;
+unlock:
+ mutex_unlock(&ecryptfs_daemon_id_hash_mux);
+ return rc;
+}
+
+/**
+ * ecryptfs_process_quit
+ * @uid: The user ID owner of the message
+ * @pid: The process ID for the userspace program that sent the
+ * message
+ *
+ * Deletes the corresponding daemon id for the given uid and pid, if
+ * it is the registered that is requesting the deletion. Returns zero
+ * after deleting the desired daemon id; non-zero otherwise.
+ */
+int ecryptfs_process_quit(uid_t uid, pid_t pid)
+{
+ struct ecryptfs_daemon_id *id;
+ int rc;
+
+ mutex_lock(&ecryptfs_daemon_id_hash_mux);
+ if (ecryptfs_find_daemon_id(uid, &id)) {
+ rc = -EINVAL;
+ ecryptfs_printk(KERN_ERR, "Received request from user [%d] to "
+ "unregister unrecognized daemon [%d]\n", uid,
+ pid);
+ goto unlock;
+ }
+ if (id->pid != pid) {
+ rc = -EINVAL;
+ ecryptfs_printk(KERN_WARNING, "Received request from user [%d] "
+ "with pid [%d] to unregister daemon [%d]\n",
+ uid, pid, id->pid);
+ goto unlock;
+ }
+ hlist_del(&id->id_chain);
+ kfree(id);
+ rc = 0;
+unlock:
+ mutex_unlock(&ecryptfs_daemon_id_hash_mux);
+ return rc;
+}
+
+/**
+ * ecryptfs_process_reponse
+ * @msg: The ecryptfs message received; the caller should sanity check
+ * msg->data_len
+ * @pid: The process ID of the userspace application that sent the
+ * message
+ * @seq: The sequence number of the message
+ *
+ * Processes a response message after sending a operation request to
+ * userspace. Returns zero upon delivery to desired context element;
+ * non-zero upon delivery failure or error.
+ */
+int ecryptfs_process_response(struct ecryptfs_message *msg, uid_t uid,
+ pid_t pid, u32 seq)
+{
+ struct ecryptfs_daemon_id *id;
+ struct ecryptfs_msg_ctx *msg_ctx;
+ int msg_size;
+ int rc;
+
+ if (msg->index >= ecryptfs_message_buf_len) {
+ rc = -EINVAL;
+ ecryptfs_printk(KERN_ERR, "Attempt to reference "
+ "context buffer at index [%d]; maximum "
+ "allowable is [%d]\n", msg->index,
+ (ecryptfs_message_buf_len - 1));
+ goto out;
+ }
+ msg_ctx = &ecryptfs_msg_ctx_arr[msg->index];
+ mutex_lock(&msg_ctx->mux);
+ if (ecryptfs_find_daemon_id(msg_ctx->task->euid, &id)) {
+ rc = -EBADMSG;
+ ecryptfs_printk(KERN_WARNING, "User [%d] received a "
+ "message response from process [%d] but does "
+ "not have a registered daemon\n",
+ msg_ctx->task->euid, pid);
+ goto wake_up;
+ }
+ if (msg_ctx->task->euid != uid) {
+ rc = -EBADMSG;
+ ecryptfs_printk(KERN_WARNING, "Received message from user "
+ "[%d]; expected message from user [%d]\n",
+ uid, msg_ctx->task->euid);
+ goto unlock;
+ }
+ if (id->pid != pid) {
+ rc = -EBADMSG;
+ ecryptfs_printk(KERN_ERR, "User [%d] received a "
+ "message response from an unrecognized "
+ "process [%d]\n", msg_ctx->task->euid, pid);
+ goto unlock;
+ }
+ if (msg_ctx->state != ECRYPTFS_MSG_CTX_STATE_PENDING) {
+ rc = -EINVAL;
+ ecryptfs_printk(KERN_WARNING, "Desired context element is not "
+ "pending a response\n");
+ goto unlock;
+ } else if (msg_ctx->counter != seq) {
+ rc = -EINVAL;
+ ecryptfs_printk(KERN_WARNING, "Invalid message sequence; "
+ "expected [%d]; received [%d]\n",
+ msg_ctx->counter, seq);
+ goto unlock;
+ }
+ msg_size = sizeof(*msg) + msg->data_len;
+ msg_ctx->msg = kmalloc(msg_size, GFP_KERNEL);
+ if (!msg_ctx->msg) {
+ rc = -ENOMEM;
+ ecryptfs_printk(KERN_ERR, "Failed to allocate memory\n");
+ goto unlock;
+ }
+ memcpy(msg_ctx->msg, msg, msg_size);
+ msg_ctx->state = ECRYPTFS_MSG_CTX_STATE_DONE;
+ rc = 0;
+wake_up:
+ wake_up_process(msg_ctx->task);
+unlock:
+ mutex_unlock(&msg_ctx->mux);
+out:
+ return rc;
+}
+
+/**
+ * ecryptfs_send_message
+ * @transport: The transport over which to send the message (i.e.,
+ * netlink)
+ * @data: The data to send
+ * @data_len: The length of data
+ * @msg_ctx: The message context allocated for the send
+ */
+int ecryptfs_send_message(unsigned int transport, char *data, int data_len,
+ struct ecryptfs_msg_ctx **msg_ctx)
+{
+ struct ecryptfs_daemon_id *id;
+ int rc;
+
+ mutex_lock(&ecryptfs_daemon_id_hash_mux);
+ if (ecryptfs_find_daemon_id(current->euid, &id)) {
+ mutex_unlock(&ecryptfs_daemon_id_hash_mux);
+ rc = -ENOTCONN;
+ ecryptfs_printk(KERN_ERR, "User [%d] does not have a daemon "
+ "registered\n", current->euid);
+ goto out;
+ }
+ mutex_unlock(&ecryptfs_daemon_id_hash_mux);
+ mutex_lock(&ecryptfs_msg_ctx_lists_mux);
+ rc = ecryptfs_acquire_free_msg_ctx(msg_ctx);
+ if (rc) {
+ mutex_unlock(&ecryptfs_msg_ctx_lists_mux);
+ ecryptfs_printk(KERN_WARNING, "Could not claim a free "
+ "context element\n");
+ goto out;
+ }
+ ecryptfs_msg_ctx_free_to_alloc(*msg_ctx);
+ mutex_unlock(&(*msg_ctx)->mux);
+ mutex_unlock(&ecryptfs_msg_ctx_lists_mux);
+ switch (transport) {
+ case ECRYPTFS_TRANSPORT_NETLINK:
+ rc = ecryptfs_send_netlink(data, data_len, *msg_ctx,
+ ECRYPTFS_NLMSG_REQUEST, 0, id->pid);
+ break;
+ case ECRYPTFS_TRANSPORT_CONNECTOR:
+ case ECRYPTFS_TRANSPORT_RELAYFS:
+ default:
+ rc = -ENOSYS;
+ }
+ if (rc) {
+ printk(KERN_ERR "Error attempting to send message to userspace "
+ "daemon; rc = [%d]\n", rc);
+ }
+out:
+ return rc;
+}
+
+/**
+ * ecryptfs_wait_for_response
+ * @msg_ctx: The context that was assigned when sending a message
+ * @msg: The incoming message from userspace; not set if rc != 0
+ *
+ * Sleeps until awaken by ecryptfs_receive_message or until the amount
+ * of time exceeds ecryptfs_message_wait_timeout. If zero is
+ * returned, msg will point to a valid message from userspace; a
+ * non-zero value is returned upon failure to receive a message or an
+ * error occurs.
+ */
+int ecryptfs_wait_for_response(struct ecryptfs_msg_ctx *msg_ctx,
+ struct ecryptfs_message **msg)
+{
+ signed long timeout = ecryptfs_message_wait_timeout * HZ;
+ int rc = 0;
+
+sleep:
+ timeout = schedule_timeout_interruptible(timeout);
+ mutex_lock(&ecryptfs_msg_ctx_lists_mux);
+ mutex_lock(&msg_ctx->mux);
+ if (msg_ctx->state != ECRYPTFS_MSG_CTX_STATE_DONE) {
+ if (timeout) {
+ mutex_unlock(&msg_ctx->mux);
+ mutex_unlock(&ecryptfs_msg_ctx_lists_mux);
+ goto sleep;
+ }
+ rc = -ENOMSG;
+ } else {
+ *msg = msg_ctx->msg;
+ msg_ctx->msg = NULL;
+ }
+ ecryptfs_msg_ctx_alloc_to_free(msg_ctx);
+ mutex_unlock(&msg_ctx->mux);
+ mutex_unlock(&ecryptfs_msg_ctx_lists_mux);
+ return rc;
+}
+
+int ecryptfs_init_messaging(unsigned int transport)
+{
+ int i;
+ int rc = 0;
+
+ if (ecryptfs_number_of_users > ECRYPTFS_MAX_NUM_USERS) {
+ ecryptfs_number_of_users = ECRYPTFS_MAX_NUM_USERS;
+ ecryptfs_printk(KERN_WARNING, "Specified number of users is "
+ "too large, defaulting to [%d] users\n",
+ ecryptfs_number_of_users);
+ }
+ mutex_init(&ecryptfs_daemon_id_hash_mux);
+ mutex_lock(&ecryptfs_daemon_id_hash_mux);
+ ecryptfs_hash_buckets = 0;
+ while (ecryptfs_number_of_users >> ++ecryptfs_hash_buckets);
+ ecryptfs_daemon_id_hash = kmalloc(sizeof(struct hlist_head)
+ * ecryptfs_hash_buckets, GFP_KERNEL);
+ if (!ecryptfs_daemon_id_hash) {
+ rc = -ENOMEM;
+ ecryptfs_printk(KERN_ERR, "Failed to allocate memory\n");
+ goto out;
+ }
+ for (i = 0; i < ecryptfs_hash_buckets; i++)
+ INIT_HLIST_HEAD(&ecryptfs_daemon_id_hash[i]);
+ mutex_unlock(&ecryptfs_daemon_id_hash_mux);
+
+ ecryptfs_msg_ctx_arr = kmalloc((sizeof(struct ecryptfs_msg_ctx)
+ * ecryptfs_message_buf_len), GFP_KERNEL);
+ if (!ecryptfs_msg_ctx_arr) {
+ rc = -ENOMEM;
+ ecryptfs_printk(KERN_ERR, "Failed to allocate memory\n");
+ goto out;
+ }
+ mutex_init(&ecryptfs_msg_ctx_lists_mux);
+ mutex_lock(&ecryptfs_msg_ctx_lists_mux);
+ ecryptfs_msg_counter = 0;
+ for (i = 0; i < ecryptfs_message_buf_len; i++) {
+ INIT_LIST_HEAD(&ecryptfs_msg_ctx_arr[i].node);
+ mutex_init(&ecryptfs_msg_ctx_arr[i].mux);
+ mutex_lock(&ecryptfs_msg_ctx_arr[i].mux);
+ ecryptfs_msg_ctx_arr[i].index = i;
+ ecryptfs_msg_ctx_arr[i].state = ECRYPTFS_MSG_CTX_STATE_FREE;
+ ecryptfs_msg_ctx_arr[i].counter = 0;
+ ecryptfs_msg_ctx_arr[i].task = NULL;
+ ecryptfs_msg_ctx_arr[i].msg = NULL;
+ list_add_tail(&ecryptfs_msg_ctx_arr[i].node,
+ &ecryptfs_msg_ctx_free_list);
+ mutex_unlock(&ecryptfs_msg_ctx_arr[i].mux);
+ }
+ mutex_unlock(&ecryptfs_msg_ctx_lists_mux);
+ switch(transport) {
+ case ECRYPTFS_TRANSPORT_NETLINK:
+ rc = ecryptfs_init_netlink();
+ if (rc)
+ ecryptfs_release_messaging(transport);
+ break;
+ case ECRYPTFS_TRANSPORT_CONNECTOR:
+ case ECRYPTFS_TRANSPORT_RELAYFS:
+ default:
+ rc = -ENOSYS;
+ }
+out:
+ return rc;
+}
+
+void ecryptfs_release_messaging(unsigned int transport)
+{
+ if (ecryptfs_msg_ctx_arr) {
+ int i;
+
+ mutex_lock(&ecryptfs_msg_ctx_lists_mux);
+ for (i = 0; i < ecryptfs_message_buf_len; i++) {
+ mutex_lock(&ecryptfs_msg_ctx_arr[i].mux);
+ if (ecryptfs_msg_ctx_arr[i].msg)
+ kfree(ecryptfs_msg_ctx_arr[i].msg);
+ mutex_unlock(&ecryptfs_msg_ctx_arr[i].mux);
+ }
+ kfree(ecryptfs_msg_ctx_arr);
+ mutex_unlock(&ecryptfs_msg_ctx_lists_mux);
+ }
+ if (ecryptfs_daemon_id_hash) {
+ struct hlist_node *elem;
+ struct ecryptfs_daemon_id *id;
+ int i;
+
+ mutex_lock(&ecryptfs_daemon_id_hash_mux);
+ for (i = 0; i < ecryptfs_hash_buckets; i++) {
+ hlist_for_each_entry(id, elem,
+ &ecryptfs_daemon_id_hash[i],
+ id_chain) {
+ hlist_del(elem);
+ kfree(id);
+ }
+ }
+ kfree(ecryptfs_daemon_id_hash);
+ mutex_unlock(&ecryptfs_daemon_id_hash_mux);
+ }
+ switch(transport) {
+ case ECRYPTFS_TRANSPORT_NETLINK:
+ ecryptfs_release_netlink();
+ break;
+ case ECRYPTFS_TRANSPORT_CONNECTOR:
+ case ECRYPTFS_TRANSPORT_RELAYFS:
+ default:
+ break;
+ }
+ return;
+}
diff --git a/fs/ecryptfs/mmap.c b/fs/ecryptfs/mmap.c
index 06843d24f23..3a6f65c3f14 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 <mahalcro@us.ibm.com>
*
* This program is free software; you can redistribute it and/or
@@ -234,22 +234,13 @@ int ecryptfs_do_readpage(struct file *file, struct page *page,
goto out;
}
wait_on_page_locked(lower_page);
- page_data = (char *)kmap(page);
- if (!page_data) {
- rc = -ENOMEM;
- ecryptfs_printk(KERN_ERR, "Error mapping page\n");
- goto out;
- }
- lower_page_data = (char *)kmap(lower_page);
- if (!lower_page_data) {
- rc = -ENOMEM;
- ecryptfs_printk(KERN_ERR, "Error mapping page\n");
- kunmap(page);
- goto out;
- }
+ page_data = kmap_atomic(page, KM_USER0);
+ lower_page_data = kmap_atomic(lower_page, KM_USER1);
memcpy(page_data, lower_page_data, PAGE_CACHE_SIZE);
- kunmap(lower_page);
- kunmap(page);
+ kunmap_atomic(lower_page_data, KM_USER1);
+ flush_dcache_page(lower_page);
+ kunmap_atomic(page_data, KM_USER0);
+ flush_dcache_page(page);
rc = 0;
out:
if (likely(lower_page))
@@ -260,6 +251,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
@@ -279,8 +297,8 @@ static int ecryptfs_readpage(struct file *file, struct page *page)
crypt_stat = &ecryptfs_inode_to_private(file->f_path.dentry->d_inode)
->crypt_stat;
if (!crypt_stat
- || !ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED)
- || ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_NEW_FILE)) {
+ || !(crypt_stat->flags & ECRYPTFS_ENCRYPTED)
+ || (crypt_stat->flags & ECRYPTFS_NEW_FILE)) {
ecryptfs_printk(KERN_DEBUG,
"Passing through unencrypted page\n");
rc = ecryptfs_do_readpage(file, page, page->index);
@@ -289,10 +307,51 @@ static int ecryptfs_readpage(struct file *file, struct page *page)
"[%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 = kmap_atomic(page, KM_USER0);
+ 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_atomic(page_virt, KM_USER0);
+ flush_dcache_page(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;
@@ -308,30 +367,27 @@ 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;
int end_byte_in_page;
- int rc = 0;
char *page_virt;
- if ((i_size_read(inode) / PAGE_CACHE_SIZE) == page->index) {
- end_byte_in_page = i_size_read(inode) % PAGE_CACHE_SIZE;
- if (to > end_byte_in_page)
- end_byte_in_page = to;
- page_virt = kmap(page);
- if (!page_virt) {
- rc = -ENOMEM;
- ecryptfs_printk(KERN_WARNING,
- "Could not map page\n");
- goto out;
- }
- memset((page_virt + end_byte_in_page), 0,
- (PAGE_CACHE_SIZE - end_byte_in_page));
- kunmap(page);
- }
+ if ((i_size_read(inode) / PAGE_CACHE_SIZE) != page->index)
+ goto out;
+ end_byte_in_page = i_size_read(inode) % PAGE_CACHE_SIZE;
+ if (to > end_byte_in_page)
+ end_byte_in_page = to;
+ page_virt = kmap_atomic(page, KM_USER0);
+ memset((page_virt + end_byte_in_page), 0,
+ (PAGE_CACHE_SIZE - end_byte_in_page));
+ kunmap_atomic(page_virt, KM_USER0);
+ flush_dcache_page(page);
out:
- return rc;
+ return 0;
}
static int ecryptfs_prepare_write(struct file *file, struct page *page,
@@ -339,7 +395,6 @@ static int ecryptfs_prepare_write(struct file *file, struct page *page,
{
int rc = 0;
- kmap(page);
if (from == 0 && to == PAGE_CACHE_SIZE)
goto out; /* If we are writing a full page, it will be
up to date. */
@@ -349,30 +404,6 @@ out:
return rc;
}
-int ecryptfs_grab_and_map_lower_page(struct page **lower_page,
- char **lower_virt,
- struct inode *lower_inode,
- unsigned long lower_page_index)
-{
- int rc = 0;
-
- (*lower_page) = grab_cache_page(lower_inode->i_mapping,
- lower_page_index);
- if (!(*lower_page)) {
- ecryptfs_printk(KERN_ERR, "grab_cache_page for "
- "lower_page_index = [0x%.16x] failed\n",
- lower_page_index);
- rc = -EINVAL;
- goto out;
- }
- if (lower_virt)
- (*lower_virt) = kmap((*lower_page));
- else
- kmap((*lower_page));
-out:
- return rc;
-}
-
int ecryptfs_writepage_and_release_lower_page(struct page *lower_page,
struct inode *lower_inode,
struct writeback_control *wbc)
@@ -391,11 +422,8 @@ out:
return rc;
}
-static void ecryptfs_unmap_and_release_lower_page(struct page *lower_page)
+static void ecryptfs_release_lower_page(struct page *lower_page)
{
- kunmap(lower_page);
- ecryptfs_printk(KERN_DEBUG, "Unlocking lower page with index = "
- "[0x%.16x]\n", lower_page->index);
unlock_page(lower_page);
page_cache_release(lower_page);
}
@@ -407,10 +435,9 @@ static void ecryptfs_unmap_and_release_lower_page(struct page *lower_page)
*
* 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;
@@ -418,11 +445,11 @@ ecryptfs_write_inode_size_to_header(struct file *lower_file,
const struct address_space_operations *lower_a_ops;
u64 file_size;
- rc = ecryptfs_grab_and_map_lower_page(&header_page, &header_virt,
- lower_inode, 0);
- if (rc) {
- ecryptfs_printk(KERN_ERR, "grab_cache_page for header page "
- "failed\n");
+ header_page = grab_cache_page(lower_inode->i_mapping, 0);
+ if (!header_page) {
+ ecryptfs_printk(KERN_ERR, "grab_cache_page for "
+ "lower_page_index 0 failed\n");
+ rc = -EINVAL;
goto out;
}
lower_a_ops = lower_inode->i_mapping->a_ops;
@@ -430,18 +457,95 @@ ecryptfs_write_inode_size_to_header(struct file *lower_file,
file_size = (u64)i_size_read(inode);
ecryptfs_printk(KERN_DEBUG, "Writing size: [0x%.16x]\n", file_size);
file_size = cpu_to_be64(file_size);
+ header_virt = kmap_atomic(header_page, KM_USER0);
memcpy(header_virt, &file_size, sizeof(u64));
+ kunmap_atomic(header_virt, KM_USER0);
+ flush_dcache_page(header_page);
rc = lower_a_ops->commit_write(lower_file, header_page, 0, 8);
if (rc < 0)
ecryptfs_printk(KERN_ERR, "Error commiting header page "
"write\n");
- ecryptfs_unmap_and_release_lower_page(header_page);
+ ecryptfs_release_lower_page(header_page);
lower_inode->i_mtime = lower_inode->i_ctime = CURRENT_TIME;
mark_inode_dirty_sync(inode);
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,
@@ -449,10 +553,10 @@ int ecryptfs_get_lower_page(struct page **lower_page, struct inode *lower_inode,
{
int rc = 0;
- rc = ecryptfs_grab_and_map_lower_page(lower_page, NULL, lower_inode,
- lower_page_index);
- if (rc) {
- ecryptfs_printk(KERN_ERR, "Error attempting to grab and map "
+ *lower_page = grab_cache_page(lower_inode->i_mapping, lower_page_index);
+ if (!(*lower_page)) {
+ rc = -EINVAL;
+ ecryptfs_printk(KERN_ERR, "Error attempting to grab "
"lower page with index [0x%.16x]\n",
lower_page_index);
goto out;
@@ -468,7 +572,7 @@ int ecryptfs_get_lower_page(struct page **lower_page, struct inode *lower_inode,
}
out:
if (rc && (*lower_page)) {
- ecryptfs_unmap_and_release_lower_page(*lower_page);
+ ecryptfs_release_lower_page(*lower_page);
(*lower_page) = NULL;
}
return rc;
@@ -493,7 +597,7 @@ ecryptfs_commit_lower_page(struct page *lower_page, struct inode *lower_inode,
"Error committing write; rc = [%d]\n", rc);
} else
rc = 0;
- ecryptfs_unmap_and_release_lower_page(lower_page);
+ ecryptfs_release_lower_page(lower_page);
return rc;
}
@@ -528,89 +632,7 @@ out:
return rc;
}
-static int
-process_new_file(struct ecryptfs_crypt_stat *crypt_stat,
- struct file *file, struct inode *inode)
-{
- struct page *header_page;
- const struct address_space_operations *lower_a_ops;
- struct inode *lower_inode;
- struct file *lower_file;
- char *header_virt;
- int rc = 0;
- int current_header_page = 0;
- int header_pages;
- int more_header_data_to_be_written = 1;
-
- lower_inode = ecryptfs_inode_to_lower(inode);
- lower_file = ecryptfs_file_to_lower(file);
- lower_a_ops = lower_inode->i_mapping->a_ops;
- header_pages = ((crypt_stat->header_extent_size
- * crypt_stat->num_header_extents_at_front)
- / PAGE_CACHE_SIZE);
- BUG_ON(header_pages < 1);
- while (current_header_page < header_pages) {
- rc = ecryptfs_grab_and_map_lower_page(&header_page,
- &header_virt,
- lower_inode,
- current_header_page);
- if (rc) {
- ecryptfs_printk(KERN_ERR, "grab_cache_page for "
- "header page [%d] failed; rc = [%d]\n",
- current_header_page, rc);
- goto out;
- }
- rc = lower_a_ops->prepare_write(lower_file, header_page, 0,
- PAGE_CACHE_SIZE);
- if (rc) {
- ecryptfs_printk(KERN_ERR, "Error preparing to write "
- "header page out; rc = [%d]\n", rc);
- goto out;
- }
- memset(header_virt, 0, PAGE_CACHE_SIZE);
- if (more_header_data_to_be_written) {
- rc = ecryptfs_write_headers_virt(header_virt,
- crypt_stat,
- file->f_dentry);
- if (rc) {
- ecryptfs_printk(KERN_WARNING, "Error "
- "generating header; rc = "
- "[%d]\n", rc);
- rc = -EIO;
- memset(header_virt, 0, PAGE_CACHE_SIZE);
- ecryptfs_unmap_and_release_lower_page(
- header_page);
- goto out;
- }
- if (current_header_page == 0)
- memset(header_virt, 0, 8);
- more_header_data_to_be_written = 0;
- }
- rc = lower_a_ops->commit_write(lower_file, header_page, 0,
- PAGE_CACHE_SIZE);
- ecryptfs_unmap_and_release_lower_page(header_page);
- if (rc < 0) {
- ecryptfs_printk(KERN_ERR,
- "Error commiting header page write; "
- "rc = [%d]\n", rc);
- break;
- }
- current_header_page++;
- }
- if (rc >= 0) {
- rc = 0;
- ecryptfs_printk(KERN_DEBUG, "lower_inode->i_blocks = "
- "[0x%.16x]\n", lower_inode->i_blocks);
- i_size_write(inode, 0);
- lower_inode->i_mtime = lower_inode->i_ctime = CURRENT_TIME;
- mark_inode_dirty_sync(inode);
- }
- ecryptfs_printk(KERN_DEBUG, "Clearing ECRYPTFS_NEW_FILE flag in "
- "crypt_stat at memory location [%p]\n", crypt_stat);
- ECRYPTFS_CLEAR_FLAG(crypt_stat->flags, ECRYPTFS_NEW_FILE);
-out:
- return rc;
-}
+struct kmem_cache *ecryptfs_xattr_cache;
/**
* ecryptfs_commit_write
@@ -640,15 +662,10 @@ static int ecryptfs_commit_write(struct file *file, struct page *page,
mutex_lock(&lower_inode->i_mutex);
crypt_stat = &ecryptfs_inode_to_private(file->f_path.dentry->d_inode)
->crypt_stat;
- if (ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_NEW_FILE)) {
+ if (crypt_stat->flags & ECRYPTFS_NEW_FILE) {
ecryptfs_printk(KERN_DEBUG, "ECRYPTFS_NEW_FILE flag set in "
"crypt_stat at memory location [%p]\n", crypt_stat);
- rc = process_new_file(crypt_stat, file, inode);
- if (rc) {
- ecryptfs_printk(KERN_ERR, "Error processing new "
- "file; rc = [%d]\n", rc);
- goto out;
- }
+ crypt_stat->flags &= ~(ECRYPTFS_NEW_FILE);
} else
ecryptfs_printk(KERN_DEBUG, "Not a new file\n");
ecryptfs_printk(KERN_DEBUG, "Calling fill_zeros_to_end_of_page"
@@ -670,7 +687,6 @@ static int ecryptfs_commit_write(struct file *file, struct page *page,
"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)) {
@@ -678,11 +694,15 @@ static int ecryptfs_commit_write(struct file *file, struct page *page,
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:
- kunmap(page); /* mapped in prior call (prepare_write) */
if (rc < 0)
ClearPageUptodate(page);
else
@@ -707,6 +727,7 @@ int write_zeros(struct file *file, pgoff_t index, int start, int num_zeros)
{
int rc = 0;
struct page *tmp_page;
+ char *tmp_page_virt;
tmp_page = ecryptfs_get1page(file, index);
if (IS_ERR(tmp_page)) {
@@ -715,28 +736,27 @@ int write_zeros(struct file *file, pgoff_t index, int start, int num_zeros)
rc = PTR_ERR(tmp_page);
goto out;
}
- kmap(tmp_page);
rc = ecryptfs_prepare_write(file, tmp_page, start, start + num_zeros);
if (rc) {
ecryptfs_printk(KERN_ERR, "Error preparing to write zero's "
"to remainder of page at index [0x%.16x]\n",
index);
- kunmap(tmp_page);
page_cache_release(tmp_page);
goto out;
}
- memset(((char *)page_address(tmp_page) + start), 0, num_zeros);
+ tmp_page_virt = kmap_atomic(tmp_page, KM_USER0);
+ memset(((char *)tmp_page_virt + start), 0, num_zeros);
+ kunmap_atomic(tmp_page_virt, KM_USER0);
+ flush_dcache_page(tmp_page);
rc = ecryptfs_commit_write(file, tmp_page, start, start + num_zeros);
if (rc < 0) {
ecryptfs_printk(KERN_ERR, "Error attempting to write zero's "
"to remainder of page at index [0x%.16x]\n",
index);
- kunmap(tmp_page);
page_cache_release(tmp_page);
goto out;
}
rc = 0;
- kunmap(tmp_page);
page_cache_release(tmp_page);
out:
return rc;
diff --git a/fs/ecryptfs/netlink.c b/fs/ecryptfs/netlink.c
new file mode 100644
index 00000000000..e3aa2253c85
--- /dev/null
+++ b/fs/ecryptfs/netlink.c
@@ -0,0 +1,255 @@
+/**
+ * eCryptfs: Linux filesystem encryption layer
+ *
+ * Copyright (C) 2004-2006 International Business Machines Corp.
+ * Author(s): Michael A. Halcrow <mhalcrow@us.ibm.com>
+ * Tyler Hicks <tyhicks@ou.edu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <net/sock.h>
+#include <linux/hash.h>
+#include <linux/random.h>
+#include "ecryptfs_kernel.h"
+
+static struct sock *ecryptfs_nl_sock;
+
+/**
+ * ecryptfs_send_netlink
+ * @data: The data to include as the payload
+ * @data_len: The byte count of the data
+ * @msg_ctx: The netlink context that will be used to handle the
+ * response message
+ * @msg_type: The type of netlink message to send
+ * @msg_flags: The flags to include in the netlink header
+ * @daemon_pid: The process id of the daemon to send the message to
+ *
+ * Sends the data to the specified daemon pid and uses the netlink
+ * context element to store the data needed for validation upon
+ * receiving the response. The data and the netlink context can be
+ * null if just sending a netlink header is sufficient. Returns zero
+ * upon sending the message; non-zero upon error.
+ */
+int ecryptfs_send_netlink(char *data, int data_len,
+ struct ecryptfs_msg_ctx *msg_ctx, u16 msg_type,
+ u16 msg_flags, pid_t daemon_pid)
+{
+ struct sk_buff *skb;
+ struct nlmsghdr *nlh;
+ struct ecryptfs_message *msg;
+ size_t payload_len;
+ int rc;
+
+ payload_len = ((data && data_len) ? (sizeof(*msg) + data_len) : 0);
+ skb = alloc_skb(NLMSG_SPACE(payload_len), GFP_KERNEL);
+ if (!skb) {
+ rc = -ENOMEM;
+ ecryptfs_printk(KERN_ERR, "Failed to allocate socket buffer\n");
+ goto out;
+ }
+ nlh = NLMSG_PUT(skb, daemon_pid, msg_ctx ? msg_ctx->counter : 0,
+ msg_type, payload_len);
+ nlh->nlmsg_flags = msg_flags;
+ if (msg_ctx && payload_len) {
+ msg = (struct ecryptfs_message *)NLMSG_DATA(nlh);
+ msg->index = msg_ctx->index;
+ msg->data_len = data_len;
+ memcpy(msg->data, data, data_len);
+ }
+ rc = netlink_unicast(ecryptfs_nl_sock, skb, daemon_pid, 0);
+ if (rc < 0) {
+ ecryptfs_printk(KERN_ERR, "Failed to send eCryptfs netlink "
+ "message; rc = [%d]\n", rc);
+ goto out;
+ }
+ rc = 0;
+ goto out;
+nlmsg_failure:
+ rc = -EMSGSIZE;
+ kfree_skb(skb);
+out:
+ return rc;
+}
+
+/**
+ * ecryptfs_process_nl_reponse
+ * @skb: The socket buffer containing the netlink message of state
+ * RESPONSE
+ *
+ * Processes a response message after sending a operation request to
+ * userspace. Attempts to assign the msg to a netlink context element
+ * at the index specified in the msg. The sk_buff and nlmsghdr must
+ * be validated before this function. Returns zero upon delivery to
+ * desired context element; non-zero upon delivery failure or error.
+ */
+static int ecryptfs_process_nl_response(struct sk_buff *skb)
+{
+ struct nlmsghdr *nlh = (struct nlmsghdr*)skb->data;
+ struct ecryptfs_message *msg = NLMSG_DATA(nlh);
+ int rc;
+
+ if (skb->len - NLMSG_HDRLEN - sizeof(*msg) != msg->data_len) {
+ rc = -EINVAL;
+ ecryptfs_printk(KERN_ERR, "Received netlink message with "
+ "incorrectly specified data length\n");
+ goto out;
+ }
+ rc = ecryptfs_process_response(msg, NETLINK_CREDS(skb)->uid,
+ NETLINK_CREDS(skb)->pid, nlh->nlmsg_seq);
+ if (rc)
+ printk(KERN_ERR
+ "Error processing response message; rc = [%d]\n", rc);
+out:
+ return rc;
+}
+
+/**
+ * ecryptfs_process_nl_helo
+ * @skb: The socket buffer containing the nlmsghdr in HELO state
+ *
+ * Gets uid and pid of the skb and adds the values to the daemon id
+ * hash. Returns zero after adding a new daemon id to the hash list;
+ * non-zero otherwise.
+ */
+static int ecryptfs_process_nl_helo(struct sk_buff *skb)
+{
+ int rc;
+
+ rc = ecryptfs_process_helo(ECRYPTFS_TRANSPORT_NETLINK,
+ NETLINK_CREDS(skb)->uid,
+ NETLINK_CREDS(skb)->pid);
+ if (rc)
+ printk(KERN_WARNING "Error processing HELO; rc = [%d]\n", rc);
+ return rc;
+}
+
+/**
+ * ecryptfs_process_nl_quit
+ * @skb: The socket buffer containing the nlmsghdr in QUIT state
+ *
+ * Gets uid and pid of the skb and deletes the corresponding daemon
+ * id, if it is the registered that is requesting the
+ * deletion. Returns zero after deleting the desired daemon id;
+ * non-zero otherwise.
+ */
+static int ecryptfs_process_nl_quit(struct sk_buff *skb)
+{
+ int rc;
+
+ rc = ecryptfs_process_quit(NETLINK_CREDS(skb)->uid,
+ NETLINK_CREDS(skb)->pid);
+ if (rc)
+ printk(KERN_WARNING
+ "Error processing QUIT message; rc = [%d]\n", rc);
+ return rc;
+}
+
+/**
+ * ecryptfs_receive_nl_message
+ *
+ * Callback function called by netlink system when a message arrives.
+ * If the message looks to be valid, then an attempt is made to assign
+ * it to its desired netlink context element and wake up the process
+ * that is waiting for a response.
+ */
+static void ecryptfs_receive_nl_message(struct sock *sk, int len)
+{
+ struct sk_buff *skb;
+ struct nlmsghdr *nlh;
+ int rc = 0; /* skb_recv_datagram requires this */
+
+receive:
+ skb = skb_recv_datagram(sk, 0, 0, &rc);
+ if (rc == -EINTR)
+ goto receive;
+ else if (rc < 0) {
+ ecryptfs_printk(KERN_ERR, "Error occurred while "
+ "receiving eCryptfs netlink message; "
+ "rc = [%d]\n", rc);
+ return;
+ }
+ nlh = (struct nlmsghdr *)skb->data;
+ if (!NLMSG_OK(nlh, skb->len)) {
+ ecryptfs_printk(KERN_ERR, "Received corrupt netlink "
+ "message\n");
+ goto free;
+ }
+ switch (nlh->nlmsg_type) {
+ case ECRYPTFS_NLMSG_RESPONSE:
+ if (ecryptfs_process_nl_response(skb)) {
+ ecryptfs_printk(KERN_WARNING, "Failed to "
+ "deliver netlink response to "
+ "requesting operation\n");
+ }
+ break;
+ case ECRYPTFS_NLMSG_HELO:
+ if (ecryptfs_process_nl_helo(skb)) {
+ ecryptfs_printk(KERN_WARNING, "Failed to "
+ "fulfill HELO request\n");
+ }
+ break;
+ case ECRYPTFS_NLMSG_QUIT:
+ if (ecryptfs_process_nl_quit(skb)) {
+ ecryptfs_printk(KERN_WARNING, "Failed to "
+ "fulfill QUIT request\n");
+ }
+ break;
+ default:
+ ecryptfs_printk(KERN_WARNING, "Dropping netlink "
+ "message of unrecognized type [%d]\n",
+ nlh->nlmsg_type);
+ break;
+ }
+free:
+ kfree_skb(skb);
+}
+
+/**
+ * ecryptfs_init_netlink
+ *
+ * Initializes the daemon id hash list, netlink context array, and
+ * necessary locks. Returns zero upon success; non-zero upon error.
+ */
+int ecryptfs_init_netlink(void)
+{
+ int rc;
+
+ ecryptfs_nl_sock = netlink_kernel_create(NETLINK_ECRYPTFS, 0,
+ ecryptfs_receive_nl_message,
+ THIS_MODULE);
+ if (!ecryptfs_nl_sock) {
+ rc = -EIO;
+ ecryptfs_printk(KERN_ERR, "Failed to create netlink socket\n");
+ goto out;
+ }
+ ecryptfs_nl_sock->sk_sndtimeo = ECRYPTFS_DEFAULT_SEND_TIMEOUT;
+ rc = 0;
+out:
+ return rc;
+}
+
+/**
+ * ecryptfs_release_netlink
+ *
+ * Frees all memory used by the netlink context array and releases the
+ * netlink socket.
+ */
+void ecryptfs_release_netlink(void)
+{
+ if (ecryptfs_nl_sock && ecryptfs_nl_sock->sk_socket)
+ sock_release(ecryptfs_nl_sock->sk_socket);
+ ecryptfs_nl_sock = NULL;
+}
diff --git a/fs/ecryptfs/super.c b/fs/ecryptfs/super.c
index eaa5daaf106..7b3f0cc09a6 100644
--- a/fs/ecryptfs/super.c
+++ b/fs/ecryptfs/super.c
@@ -168,7 +168,7 @@ out:
return rc;
}
-struct super_operations ecryptfs_sops = {
+const struct super_operations ecryptfs_sops = {
.alloc_inode = ecryptfs_alloc_inode,
.destroy_inode = ecryptfs_destroy_inode,
.drop_inode = generic_delete_inode,