/** * eCryptfs: Linux filesystem encryption layer * * Copyright (C) 1997-2004 Erez Zadok * Copyright (C) 2001-2004 Stony Brook University * Copyright (C) 2004-2007 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 intecryptfs_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 intecryptfs_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){ struct scatterlist sg; struct hash_desc desc = { .tfm = crypt_stat->hash_tfm, .flags = CRYPTO_TFM_REQ_MAY_SLEEP }; int rc = 0; mutex_lock(&crypt_stat->cs_hash_tfm_mutex); sg_init_one(&sg, (u8 *)src, len); if (!desc.tfm) { desc.tfm = crypto_alloc_hash(ECRYPTFS_DEFAULT_HASH, 0, CRYPTO_ALG_ASYNC); if (IS_ERR(desc.tfm)) { rc = PTR_ERR(desc.tfm); ecryptfs_printk(KERN_ERR, "Error attempting to " "allocate crypto context; rc = [%d]\n", rc); goto out; } crypt_stat->hash_tfm = desc.tfm; } crypto_hash_init(&desc); crypto_hash_update(&desc, &sg, len); crypto_hash_final(&desc, dst); mutex_unlock(&crypt_stat->cs_hash_tfm_mutex);out: return rc;} int ecryptfs_crypto_api_algify_cipher_name(char **algified_name, char *cipher_name, char *chaining_modifier){ int cipher_name_len = strlen(cipher_name); int chaining_modifier_len = strlen(chaining_modifier); int algified_name_len; int rc; algified_name_len = (chaining_modifier_len + cipher_name_len + 3); (*algified_name) = kmalloc(algified_name_len, GFP_KERNEL); if (!(*algified_name)) { rc = -ENOMEM; goto out; } snprintf((*algified_name), algified_name_len, "%s(%s)", chaining_modifier, cipher_name); rc = 0;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. */voidecryptfs_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_hash_tfm_mutex); 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_blkcipher(crypt_stat->tfm); if (crypt_stat->hash_tfm) crypto_free_hash(crypt_stat->hash_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_blkcipher(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){ struct blkcipher_desc desc = { .tfm = crypt_stat->tfm, .info = iv, .flags = CRYPTO_TFM_REQ_MAY_SLEEP }; int rc = 0; BUG_ON(!crypt_stat || !crypt_stat->tfm || !(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_blkcipher_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_blkcipher_encrypt_iv(&desc, dest_sg, src_sg, size); mutex_unlock(&crypt_stat->cs_tfm_mutex);out: return rc;} static voidecryptfs_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 { *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", 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 (!(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(Lines 1–500 of 1831. Load more
/** * eCryptfs: Linux filesystem encryption layer * * Copyright (C) 1997-2004 Erez Zadok * Copyright (C) 2001-2004 Stony Brook University * Copyright (C) 2004-2007 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) { struct scatterlist sg; struct hash_desc desc = { .tfm = crypt_stat->hash_tfm, .flags = CRYPTO_TFM_REQ_MAY_SLEEP }; int rc = 0; mutex_lock(&crypt_stat->cs_hash_tfm_mutex); sg_init_one(&sg, (u8 *)src, len); if (!desc.tfm) { desc.tfm = crypto_alloc_hash(ECRYPTFS_DEFAULT_HASH, 0, CRYPTO_ALG_ASYNC); if (IS_ERR(desc.tfm)) { rc = PTR_ERR(desc.tfm); ecryptfs_printk(KERN_ERR, "Error attempting to " "allocate crypto context; rc = [%d]\n", rc); goto out; } crypt_stat->hash_tfm = desc.tfm; } crypto_hash_init(&desc); crypto_hash_update(&desc, &sg, len); crypto_hash_final(&desc, dst); mutex_unlock(&crypt_stat->cs_hash_tfm_mutex); out: return rc; } int ecryptfs_crypto_api_algify_cipher_name(char **algified_name, char *cipher_name, char *chaining_modifier) { int cipher_name_len = strlen(cipher_name); int chaining_modifier_len = strlen(chaining_modifier); int algified_name_len; int rc; algified_name_len = (chaining_modifier_len + cipher_name_len + 3); (*algified_name) = kmalloc(algified_name_len, GFP_KERNEL); if (!(*algified_name)) { rc = -ENOMEM; goto out; } snprintf((*algified_name), algified_name_len, "%s(%s)", chaining_modifier, cipher_name); rc = 0; 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_hash_tfm_mutex); 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_blkcipher(crypt_stat->tfm); if (crypt_stat->hash_tfm) crypto_free_hash(crypt_stat->hash_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_blkcipher(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) { struct blkcipher_desc desc = { .tfm = crypt_stat->tfm, .info = iv, .flags = CRYPTO_TFM_REQ_MAY_SLEEP }; int rc = 0; BUG_ON(!crypt_stat || !crypt_stat->tfm || !(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_blkcipher_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_blkcipher_encrypt_iv(&desc, dest_sg, src_sg, size); 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 { *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", 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 (!(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(