diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/Kconfig | 12 | ||||
-rw-r--r-- | fs/Makefile | 1 | ||||
-rw-r--r-- | fs/ecryptfs/Makefile | 7 | ||||
-rw-r--r-- | fs/ecryptfs/crypto.c | 1659 | ||||
-rw-r--r-- | fs/ecryptfs/debug.c | 123 | ||||
-rw-r--r-- | fs/ecryptfs/dentry.c | 87 | ||||
-rw-r--r-- | fs/ecryptfs/ecryptfs_kernel.h | 482 | ||||
-rw-r--r-- | fs/ecryptfs/file.c | 440 | ||||
-rw-r--r-- | fs/ecryptfs/inode.c | 1079 | ||||
-rw-r--r-- | fs/ecryptfs/keystore.c | 1061 | ||||
-rw-r--r-- | fs/ecryptfs/main.c | 831 | ||||
-rw-r--r-- | fs/ecryptfs/mmap.c | 788 | ||||
-rw-r--r-- | fs/ecryptfs/super.c | 198 |
13 files changed, 6768 insertions, 0 deletions
diff --git a/fs/Kconfig b/fs/Kconfig index 68f4561423f..674cfbb83a9 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -995,6 +995,18 @@ config AFFS_FS To compile this file system support as a module, choose M here: the module will be called affs. If unsure, say N. +config ECRYPT_FS + tristate "eCrypt filesystem layer support (EXPERIMENTAL)" + depends on EXPERIMENTAL && KEYS && CRYPTO + help + Encrypted filesystem that operates on the VFS layer. See + <file:Documentation/ecryptfs.txt> to learn more about + eCryptfs. Userspace components are required and can be + obtained from <http://ecryptfs.sf.net>. + + To compile this file system support as a module, choose M here: the + module will be called ecryptfs. + config HFS_FS tristate "Apple Macintosh file system support (EXPERIMENTAL)" depends on BLOCK && EXPERIMENTAL diff --git a/fs/Makefile b/fs/Makefile index 819b2a93beb..fd24d67a7cd 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -75,6 +75,7 @@ obj-$(CONFIG_BFS_FS) += bfs/ obj-$(CONFIG_ISO9660_FS) += isofs/ obj-$(CONFIG_HFSPLUS_FS) += hfsplus/ # Before hfs to find wrapped HFS+ obj-$(CONFIG_HFS_FS) += hfs/ +obj-$(CONFIG_ECRYPT_FS) += ecryptfs/ obj-$(CONFIG_VXFS_FS) += freevxfs/ obj-$(CONFIG_NFS_FS) += nfs/ obj-$(CONFIG_EXPORTFS) += exportfs/ diff --git a/fs/ecryptfs/Makefile b/fs/ecryptfs/Makefile new file mode 100644 index 00000000000..ca6562451ee --- /dev/null +++ b/fs/ecryptfs/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the Linux 2.6 eCryptfs +# + +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 diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c new file mode 100644 index 00000000000..ed35a9712fa --- /dev/null +++ b/fs/ecryptfs/crypto.c @@ -0,0 +1,1659 @@ +/** + * eCryptfs: Linux filesystem encryption layer + * + * Copyright (C) 1997-2004 Erez Zadok + * Copyright (C) 2001-2004 Stony Brook University + * Copyright (C) 2004-2006 International Business Machines Corp. + * Author(s): Michael A. Halcrow <mahalcro@us.ibm.com> + * Michael C. Thompson <mcthomps@us.ibm.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * 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 <linux/fs.h> +#include <linux/mount.h> +#include <linux/pagemap.h> +#include <linux/random.h> +#include <linux/compiler.h> +#include <linux/key.h> +#include <linux/namei.h> +#include <linux/crypto.h> +#include <linux/file.h> +#include <linux/scatterlist.h> +#include "ecryptfs_kernel.h" + +static int +ecryptfs_decrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat, + struct page *dst_page, int dst_offset, + struct page *src_page, int src_offset, int size, + unsigned char *iv); +static int +ecryptfs_encrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat, + struct page *dst_page, int dst_offset, + struct page *src_page, int src_offset, int size, + unsigned char *iv); + +/** + * ecryptfs_to_hex + * @dst: Buffer to take hex character representation of contents of + * src; must be at least of size (src_size * 2) + * @src: Buffer to be converted to a hex string respresentation + * @src_size: number of bytes to convert + */ +void ecryptfs_to_hex(char *dst, char *src, size_t src_size) +{ + int x; + + for (x = 0; x < src_size; x++) + sprintf(&dst[x * 2], "%.2x", (unsigned char)src[x]); +} + +/** + * ecryptfs_from_hex + * @dst: Buffer to take the bytes from src hex; must be at least of + * size (src_size / 2) + * @src: Buffer to be converted from a hex string respresentation to raw value + * @dst_size: size of dst buffer, or number of hex characters pairs to convert + */ +void ecryptfs_from_hex(char *dst, char *src, int dst_size) +{ + int x; + char tmp[3] = { 0, }; + + for (x = 0; x < dst_size; x++) { + tmp[0] = src[x * 2]; + tmp[1] = src[x * 2 + 1]; + dst[x] = (unsigned char)simple_strtol(tmp, NULL, 16); + } +} + +/** + * ecryptfs_calculate_md5 - calculates the md5 of @src + * @dst: Pointer to 16 bytes of allocated memory + * @crypt_stat: Pointer to crypt_stat struct for the current inode + * @src: Data to be md5'd + * @len: Length of @src + * + * Uses the allocated crypto context that crypt_stat references to + * generate the MD5 sum of the contents of src. + */ +static int ecryptfs_calculate_md5(char *dst, + struct ecryptfs_crypt_stat *crypt_stat, + char *src, int len) +{ + int rc = 0; + struct scatterlist sg; + + mutex_lock(&crypt_stat->cs_md5_tfm_mutex); + sg_init_one(&sg, (u8 *)src, len); + if (!crypt_stat->md5_tfm) { + crypt_stat->md5_tfm = + crypto_alloc_tfm("md5", CRYPTO_TFM_REQ_MAY_SLEEP); + if (!crypt_stat->md5_tfm) { + rc = -ENOMEM; + ecryptfs_printk(KERN_ERR, "Error attempting to " + "allocate crypto context\n"); + goto out; + } + } + crypto_digest_init(crypt_stat->md5_tfm); + crypto_digest_update(crypt_stat->md5_tfm, &sg, 1); + crypto_digest_final(crypt_stat->md5_tfm, dst); + mutex_unlock(&crypt_stat->cs_md5_tfm_mutex); +out: + return rc; +} + +/** + * ecryptfs_derive_iv + * @iv: destination for the derived iv vale + * @crypt_stat: Pointer to crypt_stat struct for the current inode + * @offset: Offset of the page whose's iv we are to derive + * + * Generate the initialization vector from the given root IV and page + * offset. + * + * Returns zero on success; non-zero on error. + */ +static int ecryptfs_derive_iv(char *iv, struct ecryptfs_crypt_stat *crypt_stat, + pgoff_t offset) +{ + int rc = 0; + char dst[MD5_DIGEST_SIZE]; + char src[ECRYPTFS_MAX_IV_BYTES + 16]; + + if (unlikely(ecryptfs_verbosity > 0)) { + ecryptfs_printk(KERN_DEBUG, "root iv:\n"); + ecryptfs_dump_hex(crypt_stat->root_iv, crypt_stat->iv_bytes); + } + /* TODO: It is probably secure to just cast the least + * significant bits of the root IV into an unsigned long and + * add the offset to that rather than go through all this + * hashing business. -Halcrow */ + memcpy(src, crypt_stat->root_iv, crypt_stat->iv_bytes); + memset((src + crypt_stat->iv_bytes), 0, 16); + snprintf((src + crypt_stat->iv_bytes), 16, "%ld", offset); + if (unlikely(ecryptfs_verbosity > 0)) { + ecryptfs_printk(KERN_DEBUG, "source:\n"); + ecryptfs_dump_hex(src, (crypt_stat->iv_bytes + 16)); + } + rc = ecryptfs_calculate_md5(dst, crypt_stat, src, + (crypt_stat->iv_bytes + 16)); + if (rc) { + ecryptfs_printk(KERN_WARNING, "Error attempting to compute " + "MD5 while generating IV for a page\n"); + goto out; + } + memcpy(iv, dst, crypt_stat->iv_bytes); + if (unlikely(ecryptfs_verbosity > 0)) { + ecryptfs_printk(KERN_DEBUG, "derived iv:\n"); + ecryptfs_dump_hex(iv, crypt_stat->iv_bytes); + } +out: + return rc; +} + +/** + * ecryptfs_init_crypt_stat + * @crypt_stat: Pointer to the crypt_stat struct to initialize. + * + * Initialize the crypt_stat structure. + */ +void +ecryptfs_init_crypt_stat(struct ecryptfs_crypt_stat *crypt_stat) +{ + memset((void *)crypt_stat, 0, sizeof(struct ecryptfs_crypt_stat)); + mutex_init(&crypt_stat->cs_mutex); + mutex_init(&crypt_stat->cs_tfm_mutex); + mutex_init(&crypt_stat->cs_md5_tfm_mutex); + ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_STRUCT_INITIALIZED); +} + +/** + * ecryptfs_destruct_crypt_stat + * @crypt_stat: Pointer to the crypt_stat struct to initialize. + * + * Releases all memory associated with a crypt_stat struct. + */ +void ecryptfs_destruct_crypt_stat(struct ecryptfs_crypt_stat *crypt_stat) +{ + if (crypt_stat->tfm) + crypto_free_tfm(crypt_stat->tfm); + if (crypt_stat->md5_tfm) + crypto_free_tfm(crypt_stat->md5_tfm); + memset(crypt_stat, 0, sizeof(struct ecryptfs_crypt_stat)); +} + +void ecryptfs_destruct_mount_crypt_stat( + struct ecryptfs_mount_crypt_stat *mount_crypt_stat) +{ + if (mount_crypt_stat->global_auth_tok_key) + key_put(mount_crypt_stat->global_auth_tok_key); + if (mount_crypt_stat->global_key_tfm) + crypto_free_tfm(mount_crypt_stat->global_key_tfm); + memset(mount_crypt_stat, 0, sizeof(struct ecryptfs_mount_crypt_stat)); +} + +/** + * virt_to_scatterlist + * @addr: Virtual address + * @size: Size of data; should be an even multiple of the block size + * @sg: Pointer to scatterlist array; set to NULL to obtain only + * the number of scatterlist structs required in array + * @sg_size: Max array size + * + * Fills in a scatterlist array with page references for a passed + * virtual address. + * + * Returns the number of scatterlist structs in array used + */ +int virt_to_scatterlist(const void *addr, int size, struct scatterlist *sg, + int sg_size) +{ + int i = 0; + struct page *pg; + int offset; + int remainder_of_page; + + while (size > 0 && i < sg_size) { + pg = virt_to_page(addr); + offset = offset_in_page(addr); + if (sg) { + sg[i].page = pg; + sg[i].offset = offset; + } + remainder_of_page = PAGE_CACHE_SIZE - offset; + if (size >= remainder_of_page) { + if (sg) + sg[i].length = remainder_of_page; + addr += remainder_of_page; + size -= remainder_of_page; + } else { + if (sg) + sg[i].length = size; + addr += size; + size = 0; + } + i++; + } + if (size > 0) + return -ENOMEM; + return i; +} + +/** + * encrypt_scatterlist + * @crypt_stat: Pointer to the crypt_stat struct to initialize. + * @dest_sg: Destination of encrypted data + * @src_sg: Data to be encrypted + * @size: Length of data to be encrypted + * @iv: iv to use during encryption + * + * Returns the number of bytes encrypted; negative value on error + */ +static int encrypt_scatterlist(struct ecryptfs_crypt_stat *crypt_stat, + struct scatterlist *dest_sg, + struct scatterlist *src_sg, int size, + unsigned char *iv) +{ + int rc = 0; + + BUG_ON(!crypt_stat || !crypt_stat->tfm + || !ECRYPTFS_CHECK_FLAG(crypt_stat->flags, + ECRYPTFS_STRUCT_INITIALIZED)); + if (unlikely(ecryptfs_verbosity > 0)) { + ecryptfs_printk(KERN_DEBUG, "Key size [%d]; key:\n", + crypt_stat->key_size); + ecryptfs_dump_hex(crypt_stat->key, + crypt_stat->key_size); + } + /* Consider doing this once, when the file is opened */ + mutex_lock(&crypt_stat->cs_tfm_mutex); + rc = crypto_cipher_setkey(crypt_stat->tfm, crypt_stat->key, + crypt_stat->key_size); + if (rc) { + ecryptfs_printk(KERN_ERR, "Error setting key; rc = [%d]\n", + rc); + mutex_unlock(&crypt_stat->cs_tfm_mutex); + rc = -EINVAL; + goto out; + } + ecryptfs_printk(KERN_DEBUG, "Encrypting [%d] bytes.\n", size); + crypto_cipher_encrypt_iv(crypt_stat->tfm, dest_sg, src_sg, size, iv); + mutex_unlock(&crypt_stat->cs_tfm_mutex); +out: + return rc; +} + +static void +ecryptfs_extent_to_lwr_pg_idx_and_offset(unsigned long *lower_page_idx, + int *byte_offset, + struct ecryptfs_crypt_stat *crypt_stat, + unsigned long extent_num) +{ + unsigned long lower_extent_num; + int extents_occupied_by_headers_at_front; + int bytes_occupied_by_headers_at_front; + int extent_offset; + int extents_per_page; + + bytes_occupied_by_headers_at_front = + ( crypt_stat->header_extent_size + * crypt_stat->num_header_extents_at_front ); + extents_occupied_by_headers_at_front = + ( bytes_occupied_by_headers_at_front + / crypt_stat->extent_size ); + lower_extent_num = extents_occupied_by_headers_at_front + extent_num; + extents_per_page = PAGE_CACHE_SIZE / crypt_stat->extent_size; + (*lower_page_idx) = lower_extent_num / extents_per_page; + extent_offset = lower_extent_num % extents_per_page; + (*byte_offset) = extent_offset * crypt_stat->extent_size; + ecryptfs_printk(KERN_DEBUG, " * crypt_stat->header_extent_size = " + "[%d]\n", crypt_stat->header_extent_size); + ecryptfs_printk(KERN_DEBUG, " * crypt_stat->" + "num_header_extents_at_front = [%d]\n", + crypt_stat->num_header_extents_at_front); + ecryptfs_printk(KERN_DEBUG, " * extents_occupied_by_headers_at_" + "front = [%d]\n", extents_occupied_by_headers_at_front); + ecryptfs_printk(KERN_DEBUG, " * lower_extent_num = [0x%.16x]\n", + lower_extent_num); + ecryptfs_printk(KERN_DEBUG, " * extents_per_page = [%d]\n", + extents_per_page); + ecryptfs_printk(KERN_DEBUG, " * (*lower_page_idx) = [0x%.16x]\n", + (*lower_page_idx)); + ecryptfs_printk(KERN_DEBUG, " * extent_offset = [%d]\n", + extent_offset); + ecryptfs_printk(KERN_DEBUG, " * (*byte_offset) = [%d]\n", + (*byte_offset)); +} + +static int ecryptfs_write_out_page(struct ecryptfs_page_crypt_context *ctx, + struct page *lower_page, + struct inode *lower_inode, + int byte_offset_in_page, int bytes_to_write) +{ + int rc = 0; + + if (ctx->mode == ECRYPTFS_PREPARE_COMMIT_MODE) { + rc = ecryptfs_commit_lower_page(lower_page, lower_inode, + ctx->param.lower_file, + byte_offset_in_page, + bytes_to_write); + if (rc) { + ecryptfs_printk(KERN_ERR, "Error calling lower " + "commit; rc = [%d]\n", rc); + goto out; + } + } else { + rc = ecryptfs_writepage_and_release_lower_page(lower_page, + lower_inode, + ctx->param.wbc); + if (rc) { + ecryptfs_printk(KERN_ERR, "Error calling lower " + "writepage(); rc = [%d]\n", rc); + goto out; + } + } +out: + return rc; +} + +static int ecryptfs_read_in_page(struct ecryptfs_page_crypt_context *ctx, + struct page **lower_page, + struct inode *lower_inode, + unsigned long lower_page_idx, + int byte_offset_in_page) +{ + int rc = 0; + + if (ctx->mode == ECRYPTFS_PREPARE_COMMIT_MODE) { + /* TODO: Limit this to only the data extents that are + * needed */ + rc = ecryptfs_get_lower_page(lower_page, lower_inode, + ctx->param.lower_file, + lower_page_idx, + byte_offset_in_page, + (PAGE_CACHE_SIZE + - byte_offset_in_page)); + if (rc) { + ecryptfs_printk( + KERN_ERR, "Error attempting to grab, map, " + "and prepare_write lower page with index " + "[0x%.16x]; rc = [%d]\n", lower_page_idx, rc); + goto out; + } + } else { + rc = ecryptfs_grab_and_map_lower_page(lower_page, NULL, + lower_inode, + lower_page_idx); + if (rc) { + ecryptfs_printk( + KERN_ERR, "Error attempting to grab and map " + "lower page with index [0x%.16x]; rc = [%d]\n", + lower_page_idx, rc); + goto out; + } + } +out: + return rc; +} + +/** + * ecryptfs_encrypt_page + * @ctx: The context of the page + * + * Encrypt an eCryptfs page. This is done on a per-extent basis. Note + * that eCryptfs pages may straddle the lower pages -- for instance, + * if the file was created on a machine with an 8K page size + * (resulting in an 8K header), and then the file is copied onto a + * host with a 32K page size, then when reading page 0 of the eCryptfs + * file, 24K of page 0 of the lower file will be read and decrypted, + * and then 8K of page 1 of the lower file will be read and decrypted. + * + * The actual operations performed on each page depends on the + * contents of the ecryptfs_page_crypt_context struct. + * + * Returns zero on success; negative on error + */ +int ecryptfs_encrypt_page(struct ecryptfs_page_crypt_context *ctx) +{ + char extent_iv[ECRYPTFS_MAX_IV_BYTES]; + unsigned long base_extent; + unsigned long extent_offset = 0; + unsigned long lower_page_idx = 0; + unsigned long prior_lower_page_idx = 0; + struct page *lower_page; + struct inode *lower_inode; + struct ecryptfs_inode_info *inode_info; + struct ecryptfs_crypt_stat *crypt_stat; + int rc = 0; + int lower_byte_offset = 0; + int orig_byte_offset = 0; + int num_extents_per_page; +#define ECRYPTFS_PAGE_STATE_UNREAD 0 +#define ECRYPTFS_PAGE_STATE_READ 1 +#define ECRYPTFS_PAGE_STATE_MODIFIED 2 +#define ECRYPTFS_PAGE_STATE_WRITTEN 3 + int page_state; + + 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)) { + rc = ecryptfs_copy_page_to_lower(ctx->page, lower_inode, + ctx->param.lower_file); + if (rc) + ecryptfs_printk(KERN_ERR, "Error attempting to copy " + "page at index [0x%.16x]\n", + ctx->page->index); + goto out; + } + num_extents_per_page = PAGE_CACHE_SIZE / crypt_stat->extent_size; + base_extent = (ctx->page->index * num_extents_per_page); + page_state = ECRYPTFS_PAGE_STATE_UNREAD; + while (extent_offset < num_extents_per_page) { + ecryptfs_extent_to_lwr_pg_idx_and_offset( + &lower_page_idx, &lower_byte_offset, crypt_stat, + (base_extent + extent_offset)); + if (prior_lower_page_idx != lower_page_idx + && page_state == ECRYPTFS_PAGE_STATE_MODIFIED) { + rc = ecryptfs_write_out_page(ctx, lower_page, + lower_inode, + orig_byte_offset, + (PAGE_CACHE_SIZE + - orig_byte_offset)); + if (rc) { + ecryptfs_printk(KERN_ERR, "Error attempting " + "to write out page; rc = [%d]" + "\n", rc); + goto out; + } + page_state = ECRYPTFS_PAGE_STATE_WRITTEN; + } + if (page_state == ECRYPTFS_PAGE_STATE_UNREAD + || page_state == ECRYPTFS_PAGE_STATE_WRITTEN) { + rc = ecryptfs_read_in_page(ctx, &lower_page, + lower_inode, lower_page_idx, + lower_byte_offset); + if (rc) { + ecryptfs_printk(KERN_ERR, "Error attempting " + "to read in lower page with " + "index [0x%.16x]; rc = [%d]\n", + lower_page_idx, rc); + goto out; + } + orig_byte_offset = lower_byte_offset; + prior_lower_page_idx = lower_page_idx; + page_state = ECRYPTFS_PAGE_STATE_READ; + } + BUG_ON(!(page_state == ECRYPTFS_PAGE_STATE_MODIFIED + || page_state == ECRYPTFS_PAGE_STATE_READ)); + rc = ecryptfs_derive_iv(extent_iv, crypt_stat, + (base_extent + extent_offset)); + if (rc) { + ecryptfs_printk(KERN_ERR, "Error attempting to " + "derive IV for extent [0x%.16x]; " + "rc = [%d]\n", + (base_extent + extent_offset), rc); + goto out; + } + if (unlikely(ecryptfs_verbosity > 0)) { + ecryptfs_printk(KERN_DEBUG, "Encrypting extent " + "with iv:\n"); + ecryptfs_dump_hex(extent_iv, crypt_stat->iv_bytes); + ecryptfs_printk(KERN_DEBUG, "First 8 bytes before " + "encryption:\n"); + ecryptfs_dump_hex((char *) + (page_address(ctx->page) + + (extent_offset + * crypt_stat->extent_size)), 8); + } + rc = ecryptfs_encrypt_page_offset( + crypt_stat, lower_page, lower_byte_offset, ctx->page, + (extent_offset * crypt_stat->extent_size), + crypt_stat->extent_size, extent_iv); + ecryptfs_printk(KERN_DEBUG, "Encrypt extent [0x%.16x]; " + "rc = [%d]\n", + (base_extent + extent_offset), rc); + if (unlikely(ecryptfs_verbosity > 0)) { + ecryptfs_printk(KERN_DEBUG, "First 8 bytes after " + "encryption:\n"); + ecryptfs_dump_hex((char *)(page_address(lower_page) + + lower_byte_offset), 8); + } + page_state = ECRYPTFS_PAGE_STATE_MODIFIED; + extent_offset++; + } + BUG_ON(orig_byte_offset != 0); + rc = ecryptfs_write_out_page(ctx, lower_page, lower_inode, 0, + (lower_byte_offset + + crypt_stat->extent_size)); + if (rc) { + ecryptfs_printk(KERN_ERR, "Error attempting to write out " + "page; rc = [%d]\n", rc); + goto out; + } +out: + return rc; +} + +/** + * ecryptfs_decrypt_page + * @file: The ecryptfs file + * @page: The page in ecryptfs to decrypt + * + * Decrypt an eCryptfs page. This is done on a per-extent basis. Note + * that eCryptfs pages may straddle the lower pages -- for instance, + * if the file was created on a machine with an 8K page size + * (resulting in an 8K header), and then the file is copied onto a + * host with a 32K page size, then when reading page 0 of the eCryptfs + * file, 24K of page 0 of the lower file will be read and decrypted, + * and then 8K of page 1 of the lower file will be read and decrypted. + * + * Returns zero on success; negative on error + */ +int ecryptfs_decrypt_page(struct file *file, struct page *page) +{ + char extent_iv[ECRYPTFS_MAX_IV_BYTES]; + unsigned long base_extent; + unsigned long extent_offset = 0; + unsigned long lower_page_idx = 0; + unsigned long prior_lower_page_idx = 0; + struct page *lower_page; + char *lower_page_virt = NULL; + struct inode *lower_inode; + struct ecryptfs_crypt_stat *crypt_stat; + int rc = 0; + int byte_offset; + int num_extents_per_page; + int page_state; + + 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)) { + rc = ecryptfs_do_readpage(file, page, page->index); + if (rc) + ecryptfs_printk(KERN_ERR, "Error attempting to copy " + "page at index [0x%.16x]\n", + page->index); + goto out; + } + num_extents_per_page = PAGE_CACHE_SIZE / crypt_stat->extent_size; + base_extent = (page->index * num_extents_per_page); + lower_page_virt = kmem_cache_alloc(ecryptfs_lower_page_cache, + SLAB_KERNEL); + if (!lower_page_virt) { + rc = -ENOMEM; + ecryptfs_printk(KERN_ERR, "Error getting page for encrypted " + "lower page(s)\n"); + goto out; + } + lower_page = virt_to_page(lower_page_virt); + page_state = ECRYPTFS_PAGE_STATE_UNREAD; + while (extent_offset < num_extents_per_page) { + ecryptfs_extent_to_lwr_pg_idx_and_offset( + &lower_page_idx, &byte_offset, crypt_stat, + (base_extent + extent_offset)); + if (prior_lower_page_idx != lower_page_idx + || page_state == ECRYPTFS_PAGE_STATE_UNREAD) { + rc = ecryptfs_do_readpage(file, lower_page, + lower_page_idx); + if (rc) { + ecryptfs_printk(KERN_ERR, "Error reading " + "lower encrypted page; rc = " + "[%d]\n", rc); + goto out; + } + prior_lower_page_idx = lower_page_idx; + page_state = ECRYPTFS_PAGE_STATE_READ; + } + rc = ecryptfs_derive_iv(extent_iv, crypt_stat, + (base_extent + extent_offset)); + if (rc) { + ecryptfs_printk(KERN_ERR, "Error attempting to " + "derive IV for extent [0x%.16x]; rc = " + "[%d]\n", + (base_extent + extent_offset), rc); + goto out; + } + if (unlikely(ecryptfs_verbosity > 0)) { + ecryptfs_printk(KERN_DEBUG, "Decrypting extent " + "with iv:\n"); + ecryptfs_dump_hex(extent_iv, crypt_stat->iv_bytes); + ecryptfs_printk(KERN_DEBUG, "First 8 bytes before " + "decryption:\n"); + ecryptfs_dump_hex((lower_page_virt + byte_offset), 8); + } + rc = ecryptfs_decrypt_page_offset(crypt_stat, page, + (extent_offset + * crypt_stat->extent_size), + lower_page, byte_offset, + crypt_stat->extent_size, + extent_iv); + if (rc != crypt_stat->extent_size) { + ecryptfs_printk(KERN_ERR, "Error attempting to " + "decrypt extent [0x%.16x]\n", + (base_extent + extent_offset)); + goto out; + } + rc = 0; + if (unlikely(ecryptfs_verbosity > 0)) { + ecryptfs_printk(KERN_DEBUG, "First 8 bytes after " + "decryption:\n"); + ecryptfs_dump_hex((char *)(page_address(page) + + byte_offset), 8); + } + extent_offset++; + } +out: + if (lower_page_virt) + kmem_cache_free(ecryptfs_lower_page_cache, lower_page_virt); + return rc; +} + +/** + * decrypt_scatterlist + * + * Returns the number of bytes decrypted; negative value on error + */ +static int decrypt_scatterlist(struct ecryptfs_crypt_stat *crypt_stat, + struct scatterlist *dest_sg, + struct scatterlist *src_sg, int size, + unsigned char *iv) +{ + int rc = 0; + + /* Consider doing this once, when the file is opened */ + mutex_lock(&crypt_stat->cs_tfm_mutex); + rc = crypto_cipher_setkey(crypt_stat->tfm, crypt_stat->key, + crypt_stat->key_size); + if (rc) { + ecryptfs_printk(KERN_ERR, "Error setting key; rc = [%d]\n", + rc); + mutex_unlock(&crypt_stat->cs_tfm_mutex); + rc = -EINVAL; + goto out; + } + ecryptfs_printk(KERN_DEBUG, "Decrypting [%d] bytes.\n", size); + rc = crypto_cipher_decrypt_iv(crypt_stat->tfm, dest_sg, src_sg, size, + iv); + mutex_unlock(&crypt_stat->cs_tfm_mutex); + if (rc) { + ecryptfs_printk(KERN_ERR, "Error decrypting; rc = [%d]\n", + rc); + goto out; + } + rc = size; +out: + return rc; +} + +/** + * ecryptfs_encrypt_page_offset + * + * Returns the number of bytes encrypted + */ +static int +ecryptfs_encrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat, + struct page *dst_page, int dst_offset, + struct page *src_page, int src_offset, int size, + unsigned char *iv) +{ + struct scatterlist src_sg, dst_sg; + + src_sg.page = src_page; + src_sg.offset = src_offset; + src_sg.length = size; + dst_sg.page = dst_page; + dst_sg.offset = dst_offset; + dst_sg.length = size; + return encrypt_scatterlist(crypt_stat, &dst_sg, &src_sg, size, iv); +} + +/** + * ecryptfs_decrypt_page_offset + * + * Returns the number of bytes decrypted + */ +static int +ecryptfs_decrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat, + struct page *dst_page, int dst_offset, + struct page *src_page, int src_offset, int size, + unsigned char *iv) +{ + struct scatterlist src_sg, dst_sg; + + src_sg.page = src_page; + src_sg.offset = src_offset; + src_sg.length = size; + dst_sg.page = dst_page; + dst_sg.offset = dst_offset; + dst_sg.length = size; + return decrypt_scatterlist(crypt_stat, &dst_sg, &src_sg, size, iv); +} + +#define ECRYPTFS_MAX_SCATTERLIST_LEN 4 + +/** + * ecryptfs_init_crypt_ctx + * @crypt_stat: Uninitilized crypt stats structure + * + * Initialize the crypto context. + * + * TODO: Performance: Keep a cache of initialized cipher contexts; + * only init if needed + */ +int ecryptfs_init_crypt_ctx(struct ecryptfs_crypt_stat *crypt_stat) +{ + int rc = -EINVAL; + + if (!crypt_stat->cipher) { + ecryptfs_printk(KERN_ERR, "No cipher specified\n"); + goto out; + } + ecryptfs_printk(KERN_DEBUG, + "Initializing cipher [%s]; strlen = [%d]; " + "key_size_bits = [%d]\n", + crypt_stat->cipher, (int)strlen(crypt_stat->cipher), + crypt_stat->key_size << 3); + if (crypt_stat->tfm) { + rc = 0; + goto out; + } + mutex_lock(&crypt_stat->cs_tfm_mutex); + crypt_stat->tfm = crypto_alloc_tfm(crypt_stat->cipher, + ECRYPTFS_DEFAULT_CHAINING_MODE + | CRYPTO_TFM_REQ_WEAK_KEY); + mutex_unlock(&crypt_stat->cs_tfm_mutex); + if (!crypt_stat->tfm) { + ecryptfs_printk(KERN_ERR, "cryptfs: init_crypt_ctx(): " + "Error initializing cipher [%s]\n", + crypt_stat->cipher); + goto out; + } + rc = 0; +out: + return rc; +} + +static void set_extent_mask_and_shift(struct ecryptfs_crypt_stat *crypt_stat) +{ + int extent_size_tmp; + + crypt_stat->extent_mask = 0xFFFFFFFF; + crypt_stat->extent_shift = 0; + if (crypt_stat->extent_size == 0) + return; + extent_size_tmp = crypt_stat->extent_size; + while ((extent_size_tmp & 0x01) == 0) { + extent_size_tmp >>= 1; + crypt_stat->extent_mask <<= 1; + crypt_stat->extent_shift++; + } +} + +void ecryptfs_set_default_sizes(struct ecryptfs_crypt_stat *crypt_stat) +{ + /* Default values; may be overwritten as we are parsing the + * packets. */ + crypt_stat->extent_size = ECRYPTFS_DEFAULT_EXTENT_SIZE; + set_extent_mask_and_shift(crypt_stat); + crypt_stat->iv_bytes = ECRYPTFS_DEFAULT_IV_BYTES; + if (PAGE_CACHE_SIZE <= ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE) { + crypt_stat->header_extent_size = + ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE; + } else + crypt_stat->header_extent_size = PAGE_CACHE_SIZE; + crypt_stat->num_header_extents_at_front = 1; +} + +/** + * ecryptfs_compute_root_iv + * @crypt_stats + * + * On error, sets the root IV to all 0's. + */ +int ecryptfs_compute_root_iv(struct ecryptfs_crypt_stat *crypt_stat) +{ + int rc = 0; + char dst[MD5_DIGEST_SIZE]; + + 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)) { + rc = -EINVAL; + ecryptfs_printk(KERN_WARNING, "Session key not valid; " + "cannot generate root IV\n"); + goto out; + } + rc = ecryptfs_calculate_md5(dst, crypt_stat, crypt_stat->key, + crypt_stat->key_size); + if (rc) { + ecryptfs_printk(KERN_WARNING, "Error attempting to compute " + "MD5 while generating root IV\n"); + goto out; + } + memcpy(crypt_stat->root_iv, dst, crypt_stat->iv_bytes); +out: + if (rc) { + memset(crypt_stat->root_iv, 0, crypt_stat->iv_bytes); + ECRYPTFS_SET_FLAG(crypt_stat->flags, + ECRYPTFS_SECURITY_WARNING); + } + return rc; +} + +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); + ecryptfs_compute_root_iv(crypt_stat); + if (unlikely(ecryptfs_verbosity > 0)) { + ecryptfs_printk(KERN_DEBUG, "Generated new session key:\n"); + ecryptfs_dump_hex(crypt_stat->key, + crypt_stat->key_size); + } +} + +/** + * ecryptfs_set_default_crypt_stat_vals + * @crypt_stat + * + * Default values in the event that policy does not override them. + */ +static void ecryptfs_set_default_crypt_stat_vals( + struct ecryptfs_crypt_stat *crypt_stat, + struct ecryptfs_mount_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->file_version = ECRYPTFS_FILE_VERSION; + crypt_stat->mount_crypt_stat = mount_crypt_stat; +} + +/** + * ecryptfs_new_file_context + * @ecryptfs_dentry + * + * If the crypto context for the file has not yet been established, + * this is where we do that. Establishing a new crypto context + * involves the following decisions: + * - What cipher to use? + * - What set of authentication tokens to use? + * Here we just worry about getting enough information into the + * authentication tokens so that we know that they are available. + * We associate the available authentication tokens with the new file + * via the set of signatures in the crypt_stat struct. Later, when + * the headers are actually written out, we may again defer to + * userspace to perform the encryption of the session key; for the + * foreseeable future, this will be the case with public key packets. + * + * Returns zero on success; non-zero otherwise + */ +/* Associate an authentication token(s) with the file */ +int ecryptfs_new_file_context(struct dentry *ecryptfs_dentry) +{ + int rc = 0; + 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; + int cipher_name_len; + + ecryptfs_set_default_crypt_stat_vals(crypt_stat, mount_crypt_stat); + /* See if there are mount crypt options */ + 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); + memcpy(crypt_stat->keysigs[crypt_stat->num_keysigs++], + mount_crypt_stat->global_auth_tok_sig, + ECRYPTFS_SIG_SIZE_HEX); + cipher_name_len = + strlen(mount_crypt_stat->global_default_cipher_name); + memcpy(crypt_stat->cipher, + mount_crypt_stat->global_default_cipher_name, + cipher_name_len); + crypt_stat->cipher[cipher_name_len] = '\0'; + crypt_stat->key_size = + mount_crypt_stat->global_default_cipher_key_size; + ecryptfs_generate_new_key(crypt_stat); + } else + /* We should not encounter this scenario since we + * should detect lack of global_auth_tok at mount time + * TODO: Applies to 0.1 release only; remove in future + * release */ + BUG(); + rc = ecryptfs_init_crypt_ctx(crypt_stat); + if (rc) + ecryptfs_printk(KERN_ERR, "Error initializing cryptographic " + "context for cipher [%s]: rc = [%d]\n", + crypt_stat->cipher, rc); + return rc; +} |