diff options
Diffstat (limited to 'fs/ecryptfs/events.c')
-rw-r--r-- | fs/ecryptfs/events.c | 393 |
1 files changed, 393 insertions, 0 deletions
diff --git a/fs/ecryptfs/events.c b/fs/ecryptfs/events.c new file mode 100644 index 000000000000..12e26c683cf6 --- /dev/null +++ b/fs/ecryptfs/events.c @@ -0,0 +1,393 @@ +/** + * eCryptfs: Linux filesystem encryption layer + * Copyright (c) 2015-2016, 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(const 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; + events_ptr->get_salt_key_size_cb = ops->get_salt_key_size_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(const 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(const void *data1, const void *data2) +{ + /* pointer comparison*/ + return data1 == data2; +} + +/** + * Given ecryptfs data, the function + * returns appropriate key size. + */ +size_t ecryptfs_get_key_size(const 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 salt size. + * + * !!! crypt_stat cipher name and mode must be initialized + */ +size_t ecryptfs_get_salt_size(const void *data) +{ + if (!data) { + ecryptfs_printk(KERN_ERR, + "ecryptfs_get_salt_size: invalid data parameter\n"); + return 0; + } + + return ecryptfs_get_salt_size_for_cipher(data); + +} + +/** + * Given ecryptfs data and cipher string, the function + * returns true if provided cipher and the one in ecryptfs match. + */ +bool ecryptfs_cipher_match(const void *data, + const unsigned char *cipher, size_t cipher_size) +{ + unsigned char final[2*ECRYPTFS_MAX_CIPHER_NAME_SIZE+1]; + const unsigned char *ecryptfs_cipher = NULL; + struct ecryptfs_crypt_stat *stat = NULL; + + if (!data || !cipher) { + ecryptfs_printk(KERN_ERR, + "ecryptfs_get_cipher: invalid data parameter\n"); + return false; + } + + if (!cipher_size || cipher_size > sizeof(final)) { + ecryptfs_printk(KERN_ERR, + "ecryptfs_get_cipher: cipher_size\n"); + return false; + } + + stat = (struct ecryptfs_crypt_stat *)data; + ecryptfs_cipher = ecryptfs_get_full_cipher(stat->cipher, + stat->cipher_mode, + final, sizeof(final)); + + if (!ecryptfs_cipher) { + ecryptfs_printk(KERN_ERR, + "ecryptfs_get_cipher: internal error while parsing cipher\n"); + return false; + } + + if (strcmp(ecryptfs_cipher, cipher)) { + if (ecryptfs_verbosity > 0) + ecryptfs_dump_cipher(stat); + + return false; + } + + return true; +} + +/** + * Given ecryptfs data, the function + * returns file encryption key. + */ +const unsigned char *ecryptfs_get_key(const 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; +} + +/** + * Given ecryptfs data, the function + * returns file encryption salt. + */ +const unsigned char *ecryptfs_get_salt(const void *data) +{ + struct ecryptfs_crypt_stat *stat = NULL; + + if (!data) { + ecryptfs_printk(KERN_ERR, + "ecryptfs_get_salt: invalid data parameter\n"); + return NULL; + } + stat = (struct ecryptfs_crypt_stat *)data; + return stat->key + ecryptfs_get_salt_size(data); +} + +/** + * Returns ecryptfs events pointer + */ +inline struct ecryptfs_events *get_events(void) +{ + return events_ptr; +} + +/** + * If external crypto module requires salt in addition to key, + * we store it as part of key array (if there is enough space) + * Checks whether a salt key can fit into array allocated for + * regular key + */ +bool ecryptfs_check_space_for_salt(const size_t key_size, + const size_t salt_size) +{ + if ((salt_size + key_size) > ECRYPTFS_MAX_KEY_BYTES) + return false; + + return true; +} + +/* + * If there is salt that is used by external crypto module, it is stored + * in the same array where regular key is. Salt is going to be used by + * external crypto module only, so for all internal crypto operations salt + * should be ignored. + * + * Get key size in cases where it is going to be used for data encryption + * or for all other general purposes + */ +size_t ecryptfs_get_key_size_to_enc_data( + const struct ecryptfs_crypt_stat *crypt_stat) +{ + if (!crypt_stat) + return 0; + + return crypt_stat->key_size; +} + +/* + * If there is salt that is used by external crypto module, it is stored + * in the same array where regular key is. Salt is going to be used by + * external crypto module only, but we still need to save and restore it + * (in encrypted form) as part of ecryptfs header along with the regular + * key. + * + * Get key size in cases where it is going to be stored persistently + * + * !!! crypt_stat cipher name and mode must be initialized + */ +size_t ecryptfs_get_key_size_to_store_key( + const struct ecryptfs_crypt_stat *crypt_stat) +{ + size_t salt_size = 0; + + if (!crypt_stat) + return 0; + + salt_size = ecryptfs_get_salt_size(crypt_stat); + + if (!ecryptfs_check_space_for_salt(crypt_stat->key_size, salt_size)) { + ecryptfs_printk(KERN_WARNING, + "ecryptfs_get_key_size_to_store_key: not enough space for salt\n"); + return crypt_stat->key_size; + } + + return crypt_stat->key_size + salt_size; +} + +/* + * If there is salt that is used by external crypto module, it is stored + * in the same array where regular key is. Salt is going to be used by + * external crypto module only, but we still need to save and restore it + * (in encrypted form) as part of ecryptfs header along with the regular + * key. + * + * Get key size in cases where it is going to be restored from storage + * + * !!! crypt_stat cipher name and mode must be initialized + */ +size_t ecryptfs_get_key_size_to_restore_key(size_t stored_key_size, + const struct ecryptfs_crypt_stat *crypt_stat) +{ + size_t salt_size = 0; + + if (!crypt_stat) + return 0; + + salt_size = ecryptfs_get_salt_size_for_cipher(crypt_stat); + + if (salt_size >= stored_key_size) { + ecryptfs_printk(KERN_WARNING, + "ecryptfs_get_key_size_to_restore_key: salt %zu >= stred size %zu\n", + salt_size, stored_key_size); + + return stored_key_size; + } + + return stored_key_size - salt_size; +} + +/** + * Given crypt_stat, the function returns appropriate salt size. + */ +size_t ecryptfs_get_salt_size_for_cipher( + const struct ecryptfs_crypt_stat *crypt_stat) +{ + if (!get_events() || !(get_events()->get_salt_key_size_cb)) + return 0; + + return get_events()->get_salt_key_size_cb(crypt_stat); +} + +/** + * Given mount_crypt_stat, the function returns appropriate salt size. + */ +size_t ecryptfs_get_salt_size_for_cipher_mount( + const struct ecryptfs_mount_crypt_stat *crypt_stat) +{ + if (!get_events() || !(get_events()->get_salt_key_size_cb)) + return 0; + + return get_events()->get_salt_key_size_cb(crypt_stat); +} + |