Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 46e83179 authored by Andrey Markovytch's avatar Andrey Markovytch Committed by Matt Wagantall
Browse files

ecryptfs: enhancing ecryptfs to be configurable with encryption type



enabled eCryptfs for qcom targets. In addition to the usual
options, a special mode 'aes-xts' was added for qcom ICE hw
encryption

Change-Id: I20c01adc46c977b4a5db0be9ff93384cda14bc56
Signed-off-by: default avatarLina Zarivach <linaz@codeaurora.org>
Signed-off-by: default avatarAndrey Markovytch <andreym@codeaurora.org>
parent b1fb21ae
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -4,7 +4,7 @@

obj-$(CONFIG_ECRYPT_FS) += ecryptfs.o

ecryptfs-y := dentry.o file.o inode.o main.o super.o mmap.o read_write.o \
	      crypto.o keystore.o kthread.o debug.o
ecryptfs-y := dentry.o file.o inode.o main.o super.o mmap.o read_write.o events.o \
	      crypto.o keystore.o kthread.o debug.o caches_utils.o

ecryptfs-$(CONFIG_ECRYPT_FS_MESSAGING) += messaging.o miscdev.o
+76 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2015, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only 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.
 */

#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/spinlock.h>
#include <linux/pagemap.h>
#include <linux/pagevec.h>

#include "../internal.h"

void ecryptfs_drop_pagecache_sb(struct super_block *sb, void *unused)
{
	struct inode *inode, *toput_inode = NULL;

	spin_lock(&inode_sb_list_lock);
	list_for_each_entry(inode, &sb->s_inodes, i_sb_list) {
		spin_lock(&inode->i_lock);
		if ((inode->i_state & (I_FREEING|I_WILL_FREE|I_NEW)) ||
		    (inode->i_mapping->nrpages == 0)) {
			spin_unlock(&inode->i_lock);
			continue;
		}
		__iget(inode);
		spin_unlock(&inode->i_lock);
		spin_unlock(&inode_sb_list_lock);
		invalidate_mapping_pages(inode->i_mapping, 0, -1);
		iput(toput_inode);
		toput_inode = inode;
		spin_lock(&inode_sb_list_lock);
	}
	spin_unlock(&inode_sb_list_lock);
	iput(toput_inode);
}

void clean_inode_pages(struct address_space *mapping,
		pgoff_t start, pgoff_t end)
{
	struct pagevec pvec;
	pgoff_t index = start;
	int i;

	pagevec_init(&pvec, 0);
	while (index <= end && pagevec_lookup(&pvec, mapping, index,
		min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1)) {
		for (i = 0; i < pagevec_count(&pvec); i++) {
			struct page *page = pvec.pages[i];

			/**
			 * We rely upon deletion
			 * not changing page->index
			 */
			index = page->index;
			if (index > end)
				break;
			if (!trylock_page(page))
				continue;
			WARN_ON(page->index != index);
			zero_user(page, 0, PAGE_CACHE_SIZE);
			unlock_page(page);
		}
		pagevec_release(&pvec);
		cond_resched();
		index++;
	}
}
+135 −21
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include <asm/unaligned.h>
#include <linux/ecryptfs.h>
#include "ecryptfs_kernel.h"

#define DECRYPT		0
@@ -467,6 +468,30 @@ out:
	return rc;
}

static void init_ecryption_parameters(bool *hw_crypt, bool *cipher_supported,
				struct ecryptfs_crypt_stat *crypt_stat)
{
	if (!hw_crypt || !cipher_supported)
		return;

	*cipher_supported = false;
	*hw_crypt = false;

	if (get_events() && get_events()->is_cipher_supported_cb) {
		*cipher_supported =
			get_events()->is_cipher_supported_cb(
				ecryptfs_get_full_cipher(crypt_stat->cipher,
				crypt_stat->cipher_mode));
		if (*cipher_supported) {
			/**
			 * we should apply external algorythm
			 * assume that is_hw_crypt() cbck is supplied
			 */
			*hw_crypt = get_events()->is_hw_crypt_cb();
		}
	}
}

/**
 * ecryptfs_encrypt_page
 * @page: Page mapped from the eCryptfs inode for the file; contains
@@ -492,11 +517,18 @@ int ecryptfs_encrypt_page(struct page *page)
	loff_t extent_offset;
	loff_t lower_offset;
	int rc = 0;
	bool is_hw_crypt;
	bool is_cipher_supported;


	ecryptfs_inode = page->mapping->host;
	crypt_stat =
		&(ecryptfs_inode_to_private(ecryptfs_inode)->crypt_stat);
	BUG_ON(!(crypt_stat->flags & ECRYPTFS_ENCRYPTED));

	init_ecryption_parameters(&is_hw_crypt,
		&is_cipher_supported, crypt_stat);

	enc_extent_page = alloc_page(GFP_USER);
	if (!enc_extent_page) {
		rc = -ENOMEM;
@@ -504,24 +536,51 @@ int ecryptfs_encrypt_page(struct page *page)
				"encrypted extent\n");
		goto out;
	}

	if (is_hw_crypt) {
		/* no need for encryption */
	} else {
		for (extent_offset = 0;
	     extent_offset < (PAGE_CACHE_SIZE / crypt_stat->extent_size);
			extent_offset <
			(PAGE_CACHE_SIZE / crypt_stat->extent_size);
			extent_offset++) {
		rc = crypt_extent(crypt_stat, enc_extent_page, page,

			if (is_cipher_supported) {
				if (!get_events()->encrypt_cb) {
					rc = -EPERM;
					goto out;
				}
				rc = get_events()->encrypt_cb(page,
					enc_extent_page,
					ecryptfs_inode_to_lower(
						ecryptfs_inode),
						extent_offset);
			} else {
				rc = crypt_extent(crypt_stat,
					enc_extent_page, page,
					extent_offset, ENCRYPT);
			}
			if (rc) {
			printk(KERN_ERR "%s: Error encrypting extent; "
			       "rc = [%d]\n", __func__, rc);
				ecryptfs_printk(KERN_ERR,
				"%s: Error encrypting; rc = [%d]\n",
				__func__, rc);
				goto out;
			}
		}
	}

	lower_offset = lower_offset_for_page(crypt_stat, page);
	if (is_hw_crypt)
		enc_extent_virt = kmap(page);
	else
		enc_extent_virt = kmap(enc_extent_page);

	rc = ecryptfs_write_lower(ecryptfs_inode, enc_extent_virt, lower_offset,
				  PAGE_CACHE_SIZE);
	if (!is_hw_crypt)
			kunmap(enc_extent_page);
	else
			kunmap(page);

	if (rc < 0) {
		ecryptfs_printk(KERN_ERR,
			"Error attempting to write lower page; rc = [%d]\n",
@@ -560,6 +619,8 @@ int ecryptfs_decrypt_page(struct page *page)
	unsigned long extent_offset;
	loff_t lower_offset;
	int rc = 0;
	bool is_cipher_supported;
	bool is_hw_crypt;

	ecryptfs_inode = page->mapping->host;
	crypt_stat =
@@ -578,13 +639,33 @@ int ecryptfs_decrypt_page(struct page *page)
		goto out;
	}

	init_ecryption_parameters(&is_hw_crypt,
		&is_cipher_supported, crypt_stat);

	if (is_hw_crypt) {
		rc = 0;
		return rc;
	}

	for (extent_offset = 0;
	     extent_offset < (PAGE_CACHE_SIZE / crypt_stat->extent_size);
	     extent_offset++) {
		if (is_cipher_supported) {
			if (!get_events()->decrypt_cb) {
				rc = -EPERM;
				goto out;
			}

			rc = get_events()->decrypt_cb(page, page,
				ecryptfs_inode_to_lower(ecryptfs_inode),
				extent_offset);

		} else
			rc = crypt_extent(crypt_stat, page, page,
				  extent_offset, DECRYPT);

		if (rc) {
			printk(KERN_ERR "%s: Error encrypting extent; "
			ecryptfs_printk(KERN_ERR, "%s: Error decrypting extent;"
			       "rc = [%d]\n", __func__, rc);
			goto out;
		}
@@ -824,7 +905,6 @@ int ecryptfs_new_file_context(struct inode *ecryptfs_inode)
	struct ecryptfs_mount_crypt_stat *mount_crypt_stat =
	    &ecryptfs_superblock_to_private(
		    ecryptfs_inode->i_sb)->mount_crypt_stat;
	int cipher_name_len;
	int rc = 0;

	ecryptfs_set_default_crypt_stat_vals(crypt_stat, mount_crypt_stat);
@@ -838,12 +918,14 @@ int ecryptfs_new_file_context(struct inode *ecryptfs_inode)
		       "to the inode key sigs; rc = [%d]\n", rc);
		goto out;
	}
	cipher_name_len =
		strlen(mount_crypt_stat->global_default_cipher_name);
	memcpy(crypt_stat->cipher,
	strlcpy(crypt_stat->cipher,
	       mount_crypt_stat->global_default_cipher_name,
	       cipher_name_len);
	crypt_stat->cipher[cipher_name_len] = '\0';
	       sizeof(crypt_stat->cipher));

	strlcpy(crypt_stat->cipher_mode,
			mount_crypt_stat->global_default_cipher_mode,
			sizeof(crypt_stat->cipher_mode));

	crypt_stat->key_size =
		mount_crypt_stat->global_default_cipher_key_size;
	ecryptfs_generate_new_key(crypt_stat);
@@ -972,7 +1054,8 @@ ecryptfs_cipher_code_str_map[] = {
	{"twofish", RFC2440_CIPHER_TWOFISH},
	{"cast6", RFC2440_CIPHER_CAST_6},
	{"aes", RFC2440_CIPHER_AES_192},
	{"aes", RFC2440_CIPHER_AES_256}
	{"aes", RFC2440_CIPHER_AES_256},
	{"aes_xts", RFC2440_CIPHER_AES_XTS_256}
};

/**
@@ -1000,6 +1083,11 @@ u8 ecryptfs_code_for_cipher_string(char *cipher_name, size_t key_bytes)
		case 32:
			code = RFC2440_CIPHER_AES_256;
		}
	} else if (strcmp(cipher_name, "aes_xts") == 0) {
		switch (key_bytes) {
		case 32:
			code = RFC2440_CIPHER_AES_XTS_256;
		}
	} else {
		for (i = 0; i < ARRAY_SIZE(ecryptfs_cipher_code_str_map); i++)
			if (strcmp(cipher_name, map[i].cipher_str) == 0) {
@@ -1039,9 +1127,24 @@ int ecryptfs_read_and_validate_header_region(struct inode *inode)
	u8 file_size[ECRYPTFS_SIZE_AND_MARKER_BYTES];
	u8 *marker = file_size + ECRYPTFS_FILE_SIZE_BYTES;
	int rc;
	unsigned int ra_pages_org;
	struct file *lower_file = NULL;

	if (!inode)
		return -EIO;
	lower_file = ecryptfs_inode_to_private(inode)->lower_file;
	if (!lower_file)
		return -EIO;

	/*disable read a head mechanism for a while */
	ra_pages_org = lower_file->f_ra.ra_pages;
	lower_file->f_ra.ra_pages = 0;

	rc = ecryptfs_read_lower(file_size, 0, ECRYPTFS_SIZE_AND_MARKER_BYTES,
				 inode);
	lower_file->f_ra.ra_pages = ra_pages_org;
	/* restore read a head mechanism */

	if (rc < ECRYPTFS_SIZE_AND_MARKER_BYTES)
		return rc >= 0 ? -EINVAL : rc;
	rc = ecryptfs_validate_marker(marker);
@@ -1431,6 +1534,11 @@ int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry)
	struct ecryptfs_mount_crypt_stat *mount_crypt_stat =
		&ecryptfs_superblock_to_private(
			ecryptfs_dentry->d_sb)->mount_crypt_stat;
	unsigned int ra_pages_org;
	struct file *lower_file =
		ecryptfs_inode_to_private(ecryptfs_inode)->lower_file;
	if (!lower_file)
		return -EIO;

	ecryptfs_copy_mount_wide_flags_to_inode_flags(crypt_stat,
						      mount_crypt_stat);
@@ -1442,8 +1550,14 @@ int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry)
		       __func__);
		goto out;
	}
	/*disable read a head mechanism */
	ra_pages_org = lower_file->f_ra.ra_pages;
	lower_file->f_ra.ra_pages = 0;

	rc = ecryptfs_read_lower(page_virt, 0, crypt_stat->extent_size,
				 ecryptfs_inode);
	lower_file->f_ra.ra_pages = ra_pages_org; /* restore it back */

	if (rc >= 0)
		rc = ecryptfs_read_headers_virt(page_virt, crypt_stat,
						ecryptfs_dentry,
+60 −0
Original line number Diff line number Diff line
@@ -245,6 +245,7 @@ struct ecryptfs_crypt_stat {
	struct mutex cs_tfm_mutex;
	struct mutex cs_hash_tfm_mutex;
	struct mutex cs_mutex;
	unsigned char cipher_mode[ECRYPTFS_MAX_CIPHER_NAME_SIZE + 1];
};

/* inode private data. */
@@ -345,6 +346,8 @@ struct ecryptfs_mount_crypt_stat {
	unsigned char global_default_fn_cipher_name[
		ECRYPTFS_MAX_CIPHER_NAME_SIZE + 1];
	char global_default_fnek_sig[ECRYPTFS_SIG_SIZE_HEX + 1];
	unsigned char global_default_cipher_mode[ECRYPTFS_MAX_CIPHER_NAME_SIZE
							 + 1];
};

/* superblock private data. */
@@ -527,6 +530,52 @@ ecryptfs_dentry_to_lower_path(struct dentry *dentry)
	return &((struct ecryptfs_dentry_info *)dentry->d_fsdata)->lower_path;
}

/**
 * Given a cipher and mode strings, the function
 * concatenates them to create a new string of
 * <cipher>_<mode> format.
 */
static inline char *ecryptfs_get_full_cipher(
	char *cipher, char *mode)
{
	static char final[2*ECRYPTFS_MAX_CIPHER_NAME_SIZE+1];

	if (strlen(mode) > 0) {
		memset(final, 0, sizeof(final));
		snprintf(final, sizeof(final), "%s_%s", cipher, mode);
		return final;
	}
	return cipher;
}

/**
 * Given a <cipher>[_<mode>] formatted string, the function
 * extracts cipher string and/or mode string.
 * Note: the passed cipher and/or mode strings will be null-terminated.
 */
static inline void ecryptfs_parse_full_cipher(
	char *s, char *cipher, char *mode)
{
	char input[2*ECRYPTFS_MAX_CIPHER_NAME_SIZE+1+1];
			/* +1 for '_'; +1 for '\0' */
	char *p;
	char *input_p = input;

	if (s == NULL || cipher == NULL)
		return;

	memset(input, 0, sizeof(input));
	strlcpy(input, s, sizeof(input));

	p = strsep(&input_p, "_");
	strlcpy(cipher, p, ECRYPTFS_MAX_CIPHER_NAME_SIZE + 1);


	/* check if mode is specified */
	if (input_p != NULL && mode != NULL)
		strlcpy(mode, input_p, ECRYPTFS_MAX_CIPHER_NAME_SIZE + 1);
}

#define ecryptfs_printk(type, fmt, arg...) \
        __ecryptfs_printk(type "%s: " fmt, __func__, ## arg);
__printf(1, 2)
@@ -718,4 +767,15 @@ int ecryptfs_set_f_namelen(long *namelen, long lower_namelen,
int ecryptfs_derive_iv(char *iv, struct ecryptfs_crypt_stat *crypt_stat,
		       loff_t offset);

void clean_inode_pages(struct address_space *mapping,
		pgoff_t start, pgoff_t end);

void ecryptfs_drop_pagecache_sb(struct super_block *sb, void *unused);

void ecryptfs_free_events(void);

void ecryptfs_freepage(struct page *page);

struct ecryptfs_events *get_events(void);

#endif /* #ifndef ECRYPTFS_KERNEL_H */

fs/ecryptfs/events.c

0 → 100644
+212 −0
Original line number Diff line number Diff line
/**
 * eCryptfs: Linux filesystem encryption layer
 * Copyright (c) 2015, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only 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.
 */

#include <linux/string.h>
#include <linux/ecryptfs.h>
#include <linux/mutex.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/pagemap.h>
#include <linux/random.h>
#include "ecryptfs_kernel.h"

static DEFINE_MUTEX(events_mutex);
struct ecryptfs_events *events_ptr = NULL;
static int handle;

void ecryptfs_free_events(void)
{
	mutex_lock(&events_mutex);
	if (events_ptr != NULL) {
		kfree(events_ptr);
		events_ptr = NULL;
	}

	mutex_unlock(&events_mutex);
}

/**
 * Register to ecryptfs events, by passing callback
 * functions to be called upon events occurence.
 * The function returns a handle to be passed
 * to unregister function.
 */
int ecryptfs_register_to_events(struct ecryptfs_events *ops)
{
	int ret_value = 0;

	if (!ops)
		return -EINVAL;

	mutex_lock(&events_mutex);

	if (events_ptr != NULL) {
		ecryptfs_printk(KERN_ERR,
			"already registered!\n");
		ret_value = -EPERM;
		goto out;
	}
	events_ptr =
		kzalloc(sizeof(struct ecryptfs_events), GFP_KERNEL);

	if (!events_ptr) {
		ecryptfs_printk(KERN_ERR, "malloc failure\n");
		ret_value = -ENOMEM;
		goto out;
	}
	/* copy the callbacks */
	events_ptr->open_cb = ops->open_cb;
	events_ptr->release_cb = ops->release_cb;
	events_ptr->encrypt_cb = ops->encrypt_cb;
	events_ptr->decrypt_cb = ops->decrypt_cb;
	events_ptr->is_cipher_supported_cb =
		ops->is_cipher_supported_cb;
	events_ptr->is_hw_crypt_cb = ops->is_hw_crypt_cb;

	get_random_bytes(&handle, sizeof(handle));
	ret_value = handle;

out:
	mutex_unlock(&events_mutex);
	return ret_value;
}

/**
 * Unregister from ecryptfs events.
 */
int ecryptfs_unregister_from_events(int user_handle)
{
	int ret_value = 0;

	mutex_lock(&events_mutex);

	if (!events_ptr) {
		ret_value = -EINVAL;
		goto out;
	}
	if (user_handle != handle) {
		ret_value = ECRYPTFS_INVALID_EVENTS_HANDLE;
		goto out;
	}

	kfree(events_ptr);
	events_ptr = NULL;

out:
	mutex_unlock(&events_mutex);
	return ret_value;
}

/**
 * This function decides whether the passed file offset
 * belongs to ecryptfs metadata or not.
 * The caller must pass ecryptfs data, which was received in one
 * of the callback invocations.
 */
bool ecryptfs_is_page_in_metadata(void *data, pgoff_t offset)
{

	struct ecryptfs_crypt_stat *stat = NULL;
	bool ret = true;

	if (!data) {
		ecryptfs_printk(KERN_ERR, "ecryptfs_is_page_in_metadata: invalid data parameter\n");
		ret = false;
		goto end;
	}
	stat = (struct ecryptfs_crypt_stat *)data;

	if (stat->flags & ECRYPTFS_METADATA_IN_XATTR) {
		ret = false;
		goto end;
	}

	if (offset >= (stat->metadata_size/PAGE_CACHE_SIZE)) {
		ret = false;
		goto end;
	}
end:
	return ret;
}

/**
 * Given two ecryptfs data, the function
 * decides whether they are equal.
 */
inline bool ecryptfs_is_data_equal(void *data1, void *data2)
{
	/* pointer comparison*/
	return data1 == data2;
}

/**
 * Given ecryptfs data, the function
 * returns appropriate key size.
 */
size_t ecryptfs_get_key_size(void *data)
{

	struct ecryptfs_crypt_stat *stat = NULL;

	if (!data)
		return 0;

	stat = (struct ecryptfs_crypt_stat *)data;
	return stat->key_size;
}

/**
 * Given ecryptfs data, the function
 * returns appropriate cipher.
 */
const unsigned char *ecryptfs_get_cipher(void *data)
{

	struct ecryptfs_crypt_stat *stat = NULL;

	if (!data) {
		ecryptfs_printk(KERN_ERR,
			"ecryptfs_get_cipher: invalid data parameter\n");
		return NULL;
	}
	stat = (struct ecryptfs_crypt_stat *)data;
	return ecryptfs_get_full_cipher(stat->cipher, stat->cipher_mode);
}

/**
 * Given ecryptfs data, the function
 * returns file encryption key.
 */
const unsigned char *ecryptfs_get_key(void *data)
{

	struct ecryptfs_crypt_stat *stat = NULL;

	if (!data) {
		ecryptfs_printk(KERN_ERR,
			"ecryptfs_get_key: invalid data parameter\n");
		return NULL;
	}
	stat = (struct ecryptfs_crypt_stat *)data;
	return stat->key;
}

/**
 * Returns ecryptfs events pointer
 */
inline struct ecryptfs_events *get_events(void)
{
	return events_ptr;
}
Loading