1831 lines · 54.1 KB · C
RawDownload
/** * 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.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t