From a8059e6d39ecfab6c90abc8a7502652907cdf258 Mon Sep 17 00:00:00 2001 From: Andrey Markovytch Date: Tue, 13 Sep 2016 18:45:47 +0300 Subject: [PATCH] ext4 crypto: enable HW based encryption with ICE Numerous changes were introduced to various layers: Block: removed dependency on selinux module for decision on bio merge EXT4: Added feature controlled support for HW encryption PFK: Major re-factoring, separation to eCryptfs and EXT4 sub-layers Change-Id: I9256c8736e1c16175fe3f94733dda430ccc57980 Signed-off-by: Andrey Markovytch --- block/blk-merge.c | 16 +- fs/ext4/Kconfig | 10 +- fs/ext4/Makefile | 2 + fs/ext4/crypto.c | 3 +- fs/ext4/crypto_key.c | 72 ++-- fs/ext4/ext4.h | 23 +- fs/ext4/ext4_crypto.h | 6 + fs/ext4/ext4_ice.c | 109 ++++++ fs/ext4/ext4_ice.h | 104 +++++ fs/ext4/inode.c | 7 +- fs/ext4/page-io.c | 7 +- fs/ext4/readpage.c | 17 +- include/linux/ecryptfs.h | 53 ++- include/linux/lsm_hooks.h | 2 - include/linux/pfk.h | 6 +- include/linux/pft.h | 5 +- include/linux/security.h | 6 - security/pfe/Kconfig | 1 - security/pfe/Makefile | 3 +- security/pfe/pfk.c | 752 +++++++++--------------------------- security/pfe/pfk_ecryptfs.c | 630 ++++++++++++++++++++++++++++++ security/pfe/pfk_ecryptfs.h | 39 ++ security/pfe/pfk_ext4.c | 212 ++++++++++ security/pfe/pfk_ext4.h | 37 ++ security/pfe/pfk_ice.h | 3 +- security/pfe/pfk_internal.h | 34 ++ security/pfe/pft.c | 4 +- security/security.c | 6 - security/selinux/hooks.c | 7 - 29 files changed, 1532 insertions(+), 644 deletions(-) create mode 100644 fs/ext4/ext4_ice.c create mode 100644 fs/ext4/ext4_ice.h create mode 100644 security/pfe/pfk_ecryptfs.c create mode 100644 security/pfe/pfk_ecryptfs.h create mode 100644 security/pfe/pfk_ext4.c create mode 100644 security/pfe/pfk_ext4.h create mode 100644 security/pfe/pfk_internal.h diff --git a/block/blk-merge.c b/block/blk-merge.c index 6e189a49dca6..2b27747b46d3 100644 --- a/block/blk-merge.c +++ b/block/blk-merge.c @@ -6,7 +6,8 @@ #include #include #include -#include +#include +#include #include "blk.h" @@ -729,6 +730,12 @@ static void blk_account_io_merge(struct request *req) } } +static bool crypto_not_mergeable(const struct bio *bio, const struct bio *nxt) +{ + return (!pft_allow_merge_bio(bio, nxt) || + !pfk_allow_merge_bio(bio, nxt)); +} + /* * Has to be called with the request spinlock acquired */ @@ -756,6 +763,9 @@ static int attempt_merge(struct request_queue *q, struct request *req, !blk_write_same_mergeable(req->bio, next->bio)) return 0; + if (crypto_not_mergeable(req->bio, next->bio)) + return 0; + /* * If we are allowed to merge, then append bio list * from next to rq and release next. merge_requests_fn @@ -860,11 +870,9 @@ bool blk_rq_merge_ok(struct request *rq, struct bio *bio) !blk_write_same_mergeable(rq->bio, bio)) return false; - /* Don't merge bios of files with different encryption */ - if (!security_allow_merge_bio(rq->bio, bio)) + if (crypto_not_mergeable(rq->bio, bio)) return false; - return true; } diff --git a/fs/ext4/Kconfig b/fs/ext4/Kconfig index b46e9fc64196..95a49ef2781a 100644 --- a/fs/ext4/Kconfig +++ b/fs/ext4/Kconfig @@ -117,10 +117,16 @@ config EXT4_ENCRYPTION decrypted pages in the page cache. config EXT4_FS_ENCRYPTION - bool - default y + bool "Ext4 FS Encryption" + default n depends on EXT4_ENCRYPTION +config EXT4_FS_ICE_ENCRYPTION + bool "Ext4 Encryption with ICE support" + default n + depends on EXT4_FS_ENCRYPTION + depends on PFK + config EXT4_DEBUG bool "EXT4 debugging support" depends on EXT4_FS diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile index f52cf54f0cbc..1cabbd9a9229 100644 --- a/fs/ext4/Makefile +++ b/fs/ext4/Makefile @@ -14,3 +14,5 @@ ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acl.o ext4-$(CONFIG_EXT4_FS_SECURITY) += xattr_security.o ext4-$(CONFIG_EXT4_FS_ENCRYPTION) += crypto_policy.o crypto.o \ crypto_key.o crypto_fname.o + +ext4-$(CONFIG_EXT4_FS_ICE_ENCRYPTION) += ext4_ice.o diff --git a/fs/ext4/crypto.c b/fs/ext4/crypto.c index 032d0b9bb324..1bb67391225a 100644 --- a/fs/ext4/crypto.c +++ b/fs/ext4/crypto.c @@ -458,7 +458,8 @@ errout: bool ext4_valid_contents_enc_mode(uint32_t mode) { - return (mode == EXT4_ENCRYPTION_MODE_AES_256_XTS); + return (mode == EXT4_ENCRYPTION_MODE_AES_256_XTS || + mode == EXT4_ENCRYPTION_MODE_PRIVATE); } /** diff --git a/fs/ext4/crypto_key.c b/fs/ext4/crypto_key.c index 9a16d1e75a49..15342bfff70d 100644 --- a/fs/ext4/crypto_key.c +++ b/fs/ext4/crypto_key.c @@ -15,6 +15,7 @@ #include #include "ext4.h" +#include "ext4_ice.h" #include "xattr.h" static void derive_crypt_complete(struct crypto_async_request *req, int rc) @@ -111,6 +112,12 @@ void ext4_free_encryption_info(struct inode *inode, ext4_free_crypt_info(ci); } +static int ext4_default_data_encryption_mode(void) +{ + return ext4_is_ice_enabled() ? EXT4_ENCRYPTION_MODE_PRIVATE : + EXT4_ENCRYPTION_MODE_AES_256_XTS; +} + int _ext4_get_encryption_info(struct inode *inode) { struct ext4_inode_info *ei = EXT4_I(inode); @@ -124,8 +131,8 @@ int _ext4_get_encryption_info(struct inode *inode) struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); struct crypto_ablkcipher *ctfm; const char *cipher_str; - char raw_key[EXT4_MAX_KEY_SIZE]; - char mode; + int for_fname = 0; + int mode; int res; if (!ext4_read_workqueue) { @@ -150,7 +157,8 @@ retry: if (res < 0) { if (!DUMMY_ENCRYPTION_ENABLED(sbi)) return res; - ctx.contents_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_XTS; + ctx.contents_encryption_mode = + ext4_default_data_encryption_mode(); ctx.filenames_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_CTS; ctx.flags = 0; @@ -169,12 +177,12 @@ retry: crypt_info->ci_keyring_key = NULL; memcpy(crypt_info->ci_master_key, ctx.master_key_descriptor, sizeof(crypt_info->ci_master_key)); - if (S_ISREG(inode->i_mode)) - mode = crypt_info->ci_data_mode; - else if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) - mode = crypt_info->ci_filename_mode; - else + if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) + for_fname = 1; + else if (!S_ISREG(inode->i_mode)) BUG(); + mode = for_fname ? crypt_info->ci_filename_mode : + crypt_info->ci_data_mode; switch (mode) { case EXT4_ENCRYPTION_MODE_AES_256_XTS: cipher_str = "xts(aes)"; @@ -182,6 +190,9 @@ retry: case EXT4_ENCRYPTION_MODE_AES_256_CTS: cipher_str = "cts(cbc(aes))"; break; + case EXT4_ENCRYPTION_MODE_PRIVATE: + cipher_str = "bugon"; + break; default: printk_once(KERN_WARNING "ext4: unsupported key mode %d (ino %u)\n", @@ -190,7 +201,7 @@ retry: goto out; } if (DUMMY_ENCRYPTION_ENABLED(sbi)) { - memset(raw_key, 0x42, EXT4_AES_256_XTS_KEY_SIZE); + memset(crypt_info->ci_raw_key, 0x42, EXT4_AES_256_XTS_KEY_SIZE); goto got_key; } memcpy(full_key_descriptor, EXT4_KEY_DESC_PREFIX, @@ -232,28 +243,36 @@ retry: goto out; } res = ext4_derive_key_aes(ctx.nonce, master_key->raw, - raw_key); + crypt_info->ci_raw_key); up_read(&keyring_key->sem); if (res) goto out; got_key: - ctfm = crypto_alloc_ablkcipher(cipher_str, 0, 0); - if (!ctfm || IS_ERR(ctfm)) { - res = ctfm ? PTR_ERR(ctfm) : -ENOMEM; - printk(KERN_DEBUG - "%s: error %d (inode %u) allocating crypto tfm\n", - __func__, res, (unsigned) inode->i_ino); + if (for_fname || + (crypt_info->ci_data_mode != EXT4_ENCRYPTION_MODE_PRIVATE)) { + ctfm = crypto_alloc_ablkcipher(cipher_str, 0, 0); + if (!ctfm || IS_ERR(ctfm)) { + res = ctfm ? PTR_ERR(ctfm) : -ENOMEM; + pr_debug("%s: error %d (inode %u) allocating crypto tfm\n", + __func__, res, (unsigned) inode->i_ino); + goto out; + } + crypt_info->ci_ctfm = ctfm; + crypto_ablkcipher_clear_flags(ctfm, ~0); + crypto_tfm_set_flags(crypto_ablkcipher_tfm(ctfm), + CRYPTO_TFM_REQ_WEAK_KEY); + res = crypto_ablkcipher_setkey(ctfm, crypt_info->ci_raw_key, + ext4_encryption_key_size(mode)); + if (res) + goto out; + memzero_explicit(crypt_info->ci_raw_key, + sizeof(crypt_info->ci_raw_key)); + } else if (!ext4_is_ice_enabled()) { + pr_warn("%s: ICE support not available\n", + __func__); + res = -EINVAL; goto out; } - crypt_info->ci_ctfm = ctfm; - crypto_ablkcipher_clear_flags(ctfm, ~0); - crypto_tfm_set_flags(crypto_ablkcipher_tfm(ctfm), - CRYPTO_TFM_REQ_WEAK_KEY); - res = crypto_ablkcipher_setkey(ctfm, raw_key, - ext4_encryption_key_size(mode)); - if (res) - goto out; - memzero_explicit(raw_key, sizeof(raw_key)); if (cmpxchg(&ei->i_crypt_info, NULL, crypt_info) != NULL) { ext4_free_crypt_info(crypt_info); goto retry; @@ -263,8 +282,9 @@ got_key: out: if (res == -ENOKEY) res = 0; + memzero_explicit(crypt_info->ci_raw_key, + sizeof(crypt_info->ci_raw_key)); ext4_free_crypt_info(crypt_info); - memzero_explicit(raw_key, sizeof(raw_key)); return res; } diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 785bc29e4f14..f287ddfd17f1 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -588,6 +588,7 @@ enum { #define EXT4_ENCRYPTION_MODE_AES_256_GCM 2 #define EXT4_ENCRYPTION_MODE_AES_256_CBC 3 #define EXT4_ENCRYPTION_MODE_AES_256_CTS 4 +#define EXT4_ENCRYPTION_MODE_PRIVATE 127 #include "ext4_crypto.h" @@ -2328,6 +2329,19 @@ int _ext4_get_encryption_info(struct inode *inode); #ifdef CONFIG_EXT4_FS_ENCRYPTION int ext4_has_encryption_key(struct inode *inode); +static inline struct ext4_crypt_info *ext4_encryption_info(struct inode *inode) +{ + return EXT4_I(inode)->i_crypt_info; +} + +static inline int ext4_using_hardware_encryption(struct inode *inode) +{ + struct ext4_crypt_info *ci = ext4_encryption_info(inode); + + return S_ISREG(inode->i_mode) && ci && + ci->ci_data_mode == EXT4_ENCRYPTION_MODE_PRIVATE; +} + static inline int ext4_get_encryption_info(struct inode *inode) { struct ext4_crypt_info *ci = EXT4_I(inode)->i_crypt_info; @@ -2341,11 +2355,6 @@ static inline int ext4_get_encryption_info(struct inode *inode) return 0; } -static inline struct ext4_crypt_info *ext4_encryption_info(struct inode *inode) -{ - return EXT4_I(inode)->i_crypt_info; -} - #else static inline int ext4_has_encryption_key(struct inode *inode) { @@ -2359,6 +2368,10 @@ static inline struct ext4_crypt_info *ext4_encryption_info(struct inode *inode) { return NULL; } +static inline int ext4_using_hardware_encryption(struct inode *inode) +{ + return 0; +} #endif diff --git a/fs/ext4/ext4_crypto.h b/fs/ext4/ext4_crypto.h index ac7d4e813796..95cbc9bc1995 100644 --- a/fs/ext4/ext4_crypto.h +++ b/fs/ext4/ext4_crypto.h @@ -12,6 +12,7 @@ #define _EXT4_CRYPTO_H #include +#include #define EXT4_KEY_DESCRIPTOR_SIZE 8 @@ -61,6 +62,7 @@ struct ext4_encryption_context { #define EXT4_AES_256_CBC_KEY_SIZE 32 #define EXT4_AES_256_CTS_KEY_SIZE 32 #define EXT4_AES_256_XTS_KEY_SIZE 64 +#define EXT4_PRIVATE_KEY_SIZE 64 #define EXT4_MAX_KEY_SIZE 64 #define EXT4_KEY_DESC_PREFIX "ext4:" @@ -80,8 +82,11 @@ struct ext4_crypt_info { struct crypto_ablkcipher *ci_ctfm; struct key *ci_keyring_key; char ci_master_key[EXT4_KEY_DESCRIPTOR_SIZE]; + char ci_raw_key[EXT4_MAX_KEY_SIZE]; }; + + #define EXT4_CTX_REQUIRES_FREE_ENCRYPT_FL 0x00000001 #define EXT4_WRITE_PATH_FL 0x00000002 @@ -114,6 +119,7 @@ static inline int ext4_encryption_key_size(int mode) { switch (mode) { case EXT4_ENCRYPTION_MODE_AES_256_XTS: + case EXT4_ENCRYPTION_MODE_PRIVATE: return EXT4_AES_256_XTS_KEY_SIZE; case EXT4_ENCRYPTION_MODE_AES_256_GCM: return EXT4_AES_256_GCM_KEY_SIZE; diff --git a/fs/ext4/ext4_ice.c b/fs/ext4/ext4_ice.c new file mode 100644 index 000000000000..d85bcb8ea1ba --- /dev/null +++ b/fs/ext4/ext4_ice.c @@ -0,0 +1,109 @@ +/* Copyright (c) 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 "ext4_ice.h" +#include "ext4_crypto.h" + + +/* + * Retrieves encryption key from the inode + */ +char *ext4_get_ice_encryption_key(const struct inode *inode) +{ + struct ext4_crypt_info *ci = NULL; + + if (!inode) + return NULL; + + ci = ext4_encryption_info((struct inode *)inode); + if (!ci) + return NULL; + + return &(ci->ci_raw_key[0]); +} + +/* + * Retrieves encryption salt from the inode + */ +char *ext4_get_ice_encryption_salt(const struct inode *inode) +{ + struct ext4_crypt_info *ci = NULL; + + if (!inode) + return NULL; + + ci = ext4_encryption_info((struct inode *)inode); + if (!ci) + return NULL; + + return &(ci->ci_raw_key[ext4_get_ice_encryption_key_size(inode)]); +} + +/* + * returns true if the cipher mode in inode is AES XTS + */ +int ext4_is_aes_xts_cipher(const struct inode *inode) +{ + struct ext4_crypt_info *ci = NULL; + + ci = ext4_encryption_info((struct inode *)inode); + if (!ci) + return 0; + + return (ci->ci_data_mode == EXT4_ENCRYPTION_MODE_PRIVATE); +} + +/* + * returns true if encryption info in both inodes is equal + */ +int ext4_is_ice_encryption_info_equal(const struct inode *inode1, + const struct inode *inode2) +{ + char *key1 = NULL; + char *key2 = NULL; + char *salt1 = NULL; + char *salt2 = NULL; + + if (!inode1 || !inode2) + return 0; + + if (inode1 == inode2) + return 1; + + /* both do not belong to ice, so we don't care, they are equal for us */ + if (!ext4_should_be_processed_by_ice(inode1) && + !ext4_should_be_processed_by_ice(inode2)) + return 1; + + /* one belongs to ice, the other does not -> not equal */ + if (ext4_should_be_processed_by_ice(inode1) ^ + ext4_should_be_processed_by_ice(inode2)) + return 0; + + key1 = ext4_get_ice_encryption_key(inode1); + key2 = ext4_get_ice_encryption_key(inode2); + salt1 = ext4_get_ice_encryption_salt(inode1); + salt2 = ext4_get_ice_encryption_salt(inode2); + + /* key and salt should not be null by this point */ + if (!key1 || !key2 || !salt1 || !salt2 || + (ext4_get_ice_encryption_key_size(inode1) != + ext4_get_ice_encryption_key_size(inode2)) || + (ext4_get_ice_encryption_salt_size(inode1) != + ext4_get_ice_encryption_salt_size(inode2))) + return 0; + + return ((memcmp(key1, key2, + ext4_get_ice_encryption_key_size(inode1)) == 0) && + (memcmp(salt1, salt2, + ext4_get_ice_encryption_salt_size(inode1)) == 0)); +} diff --git a/fs/ext4/ext4_ice.h b/fs/ext4/ext4_ice.h new file mode 100644 index 000000000000..5257edabd6b2 --- /dev/null +++ b/fs/ext4/ext4_ice.h @@ -0,0 +1,104 @@ +/* Copyright (c) 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. + */ + +#ifndef _EXT4_ICE_H +#define _EXT4_ICE_H + +#include "ext4.h" +#include "ext4_crypto.h" + +#ifdef CONFIG_EXT4_FS_ICE_ENCRYPTION +static inline int ext4_should_be_processed_by_ice(const struct inode *inode) +{ + if (!ext4_encrypted_inode((struct inode *)inode)) + return 0; + + return ext4_using_hardware_encryption((struct inode *)inode); +} + +static inline int ext4_is_ice_enabled(void) +{ + return 1; +} + +int ext4_is_aes_xts_cipher(const struct inode *inode); + +char *ext4_get_ice_encryption_key(const struct inode *inode); +char *ext4_get_ice_encryption_salt(const struct inode *inode); + +int ext4_is_ice_encryption_info_equal(const struct inode *inode1, + const struct inode *inode2); + +static inline size_t ext4_get_ice_encryption_key_size( + const struct inode *inode) +{ + return EXT4_AES_256_XTS_KEY_SIZE / 2; +} + +static inline size_t ext4_get_ice_encryption_salt_size( + const struct inode *inode) +{ + return EXT4_AES_256_XTS_KEY_SIZE / 2; +} + +#else +static inline int ext4_should_be_processed_by_ice(const struct inode *inode) +{ + return 0; +} +static inline int ext4_is_ice_enabled(void) +{ + return 0; +} + +static inline char *ext4_get_ice_encryption_key(const struct inode *inode) +{ + return NULL; +} + +static inline char *ext4_get_ice_encryption_salt(const struct inode *inode) +{ + return NULL; +} + +static inline size_t ext4_get_ice_encryption_key_size( + const struct inode *inode) +{ + return 0; +} + +static inline size_t ext4_get_ice_encryption_salt_size( + const struct inode *inode) +{ + return 0; +} + +static inline int ext4_is_xts_cipher(const struct inode *inode) +{ + return 0; +} + +static inline int ext4_is_ice_encryption_info_equal( + const struct inode *inode1, + const struct inode *inode2) +{ + return 0; +} + +static inline int ext4_is_aes_xts_cipher(const struct inode *inode) +{ + return 0; +} + +#endif + +#endif /* _EXT4_ICE_H */ diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 06bda0361e7c..b15e6edb8f2c 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -42,6 +42,7 @@ #include "xattr.h" #include "acl.h" #include "truncate.h" +#include "ext4_ice.h" #include @@ -979,7 +980,8 @@ static int ext4_block_write_begin(struct page *page, loff_t pos, unsigned len, ll_rw_block(READ, 1, &bh); *wait_bh++ = bh; decrypt = ext4_encrypted_inode(inode) && - S_ISREG(inode->i_mode); + S_ISREG(inode->i_mode) && + !ext4_is_ice_enabled(); } } /* @@ -3459,7 +3461,8 @@ static int __ext4_block_zero_page_range(handle_t *handle, if (!buffer_uptodate(bh)) goto unlock; if (S_ISREG(inode->i_mode) && - ext4_encrypted_inode(inode)) { + ext4_encrypted_inode(inode) && + !ext4_using_hardware_encryption(inode)) { /* We expect the key to be set. */ BUG_ON(!ext4_has_encryption_key(inode)); BUG_ON(blocksize != PAGE_CACHE_SIZE); diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c index 5c72ae5d62a6..1a6a8ca4de3a 100644 --- a/fs/ext4/page-io.c +++ b/fs/ext4/page-io.c @@ -28,6 +28,7 @@ #include "ext4_jbd2.h" #include "xattr.h" #include "acl.h" +#include "ext4_ice.h" static struct kmem_cache *io_end_cachep; @@ -489,7 +490,11 @@ int ext4_bio_write_page(struct ext4_io_submit *io, gfp_t gfp_flags = GFP_NOFS; retry_encrypt: - data_page = ext4_encrypt(inode, page, gfp_flags); + + if (!ext4_using_hardware_encryption(inode)) + data_page = ext4_encrypt(inode, page, gfp_flags); + + if (IS_ERR(data_page)) { ret = PTR_ERR(data_page); if (ret == ENOMEM && wbc->sync_mode == WB_SYNC_ALL) { diff --git a/fs/ext4/readpage.c b/fs/ext4/readpage.c index bc7642f57dc8..dd98270d0b21 100644 --- a/fs/ext4/readpage.c +++ b/fs/ext4/readpage.c @@ -45,6 +45,7 @@ #include #include "ext4.h" +#include "ext4_ice.h" /* * Call ext4_decrypt on every single page, reusing the encryption @@ -62,12 +63,17 @@ static void completion_pages(struct work_struct *work) bio_for_each_segment_all(bv, bio, i) { struct page *page = bv->bv_page; - int ret = ext4_decrypt(page); - if (ret) { - WARN_ON_ONCE(1); - SetPageError(page); - } else + if (ext4_is_ice_enabled()) { SetPageUptodate(page); + } else { + int ret = ext4_decrypt(page); + + if (ret) { + WARN_ON_ONCE(1); + SetPageError(page); + } else + SetPageUptodate(page); + } unlock_page(page); } ext4_release_crypto_ctx(ctx); @@ -324,5 +330,6 @@ int ext4_mpage_readpages(struct address_space *mapping, BUG_ON(pages && !list_empty(pages)); if (bio) submit_bio(READ, bio); + return 0; } diff --git a/include/linux/ecryptfs.h b/include/linux/ecryptfs.h index 33d35ea4f937..489b99e37128 100644 --- a/include/linux/ecryptfs.h +++ b/include/linux/ecryptfs.h @@ -130,7 +130,7 @@ struct ecryptfs_events { size_t (*get_salt_key_size_cb)(const void *ecrytpfs_data); }; - +#ifdef CONFIG_ECRYPT_FS int ecryptfs_register_to_events(const struct ecryptfs_events *ops); int ecryptfs_unregister_from_events(int user_handle); @@ -151,4 +151,55 @@ bool ecryptfs_is_page_in_metadata(const void *ecrytpfs_data, pgoff_t offset); bool ecryptfs_is_data_equal(const void *ecrytpfs_data1, const void *ecrytpfs_data2); +#else +static inline int ecryptfs_register_to_events( + const struct ecryptfs_events *ops) +{ + return 1; /* dummy handle */ +} + +static int ecryptfs_unregister_from_events(int user_handle) +{ + return 0; +} + +static inline const unsigned char *ecryptfs_get_key(const void *ecrytpfs_data) +{ + return NULL; +} + +static inline size_t ecryptfs_get_key_size(const void *ecrytpfs_data) +{ + return 0; +} + +static inline const unsigned char *ecryptfs_get_salt(const void *ecrytpfs_data) +{ + return NULL; +} + +static inline size_t ecryptfs_get_salt_size(const void *ecrytpfs_data) +{ + return 0; +} + +static inline bool ecryptfs_cipher_match(const void *ecrytpfs_data, + const unsigned char *cipher, size_t cipher_size) +{ + return false; +} + +bool ecryptfs_is_page_in_metadata(const void *ecrytpfs_data, pgoff_t offset) +{ + return false; +} + +bool ecryptfs_is_data_equal(const void *ecrytpfs_data1, + const void *ecrytpfs_data2) +{ + return false; +} + +#endif /* CONFIG_ECRYPT_FS */ + #endif /* _LINUX_ECRYPTFS_H */ diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 1919b06f28f4..cf4832db2b29 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -1443,7 +1443,6 @@ union security_list_options { int (*file_receive)(struct file *file); int (*file_open)(struct file *file, const struct cred *cred); int (*file_close)(struct file *file); - bool (*allow_merge_bio)(struct bio *bio1, struct bio *bio2); int (*task_create)(unsigned long clone_flags); void (*task_free)(struct task_struct *task); @@ -1708,7 +1707,6 @@ struct security_hook_heads { struct list_head file_receive; struct list_head file_open; struct list_head file_close; - struct list_head allow_merge_bio; struct list_head task_create; struct list_head task_free; struct list_head cred_alloc_blank; diff --git a/include/linux/pfk.h b/include/linux/pfk.h index a7e8ecbea8f5..2fc64442b8ee 100644 --- a/include/linux/pfk.h +++ b/include/linux/pfk.h @@ -23,7 +23,7 @@ int pfk_load_key_start(const struct bio *bio, struct ice_crypto_setting *ice_setting, bool *is_pfe, bool); int pfk_load_key_end(const struct bio *bio, bool *is_pfe); int pfk_remove_key(const unsigned char *key, size_t key_size); -bool pfk_allow_merge_bio(struct bio *bio1, struct bio *bio2); +bool pfk_allow_merge_bio(const struct bio *bio1, const struct bio *bio2); #else static inline int pfk_load_key_start(const struct bio *bio, @@ -48,10 +48,6 @@ static inline bool pfk_allow_merge_bio(const struct bio *bio1, return true; } -static inline void pfk_remove_all_keys(void) -{ -} - #endif /* CONFIG_PFK */ #endif /* PFK_H */ diff --git a/include/linux/pft.h b/include/linux/pft.h index f2173b89a2a0..818383c73476 100644 --- a/include/linux/pft.h +++ b/include/linux/pft.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 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 @@ -55,7 +55,8 @@ static inline int pft_get_key_index(struct bio *bio, u32 *key_index, bool *is_encrypted, bool *is_inplace) { return -ENODEV; } -static inline bool pft_allow_merge_bio(struct bio *bio1, struct bio *bio2) +static inline bool pft_allow_merge_bio(const struct bio *bio1, + const struct bio *bio2) { return true; } static inline int pft_file_permission(struct file *file, int mask) diff --git a/include/linux/security.h b/include/linux/security.h index 3de0302aecf2..e3b5efc0eb4b 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -293,7 +293,6 @@ int security_file_send_sigiotask(struct task_struct *tsk, int security_file_receive(struct file *file); int security_file_open(struct file *file, const struct cred *cred); int security_file_close(struct file *file); -bool security_allow_merge_bio(struct bio *bio1, struct bio *bio2); int security_task_create(unsigned long clone_flags); void security_task_free(struct task_struct *task); int security_cred_alloc_blank(struct cred *cred, gfp_t gfp); @@ -826,11 +825,6 @@ static inline int security_file_close(struct file *file) return 0; } -static inline int security_allow_merge_bio(struct bio *bio1, struct bio *bio2) -{ - return true; -} - static inline int security_task_create(unsigned long clone_flags) { return 0; diff --git a/security/pfe/Kconfig b/security/pfe/Kconfig index b951861bfc84..207be713f800 100644 --- a/security/pfe/Kconfig +++ b/security/pfe/Kconfig @@ -15,7 +15,6 @@ config PFT config PFK bool "Per-File-Key driver" depends on SECURITY - depends on ECRYPT_FS default n help This driver is used for storing eCryptfs information diff --git a/security/pfe/Makefile b/security/pfe/Makefile index 983eedb86170..f7badf74c73f 100644 --- a/security/pfe/Makefile +++ b/security/pfe/Makefile @@ -3,6 +3,7 @@ # ccflags-y += -Isecurity/selinux -Isecurity/selinux/include -Ifs/ecryptfs +ccflags-y += -Ifs/ext4 obj-$(CONFIG_PFT) += pft.o -obj-$(CONFIG_PFK) += pfk.o pfk_kc.o pfk_ice.o +obj-$(CONFIG_PFK) += pfk.o pfk_kc.o pfk_ice.o pfk_ext4.o pfk_ecryptfs.o diff --git a/security/pfe/pfk.c b/security/pfe/pfk.c index 5cfb3758d3cf..7e38c9fbf171 100644 --- a/security/pfe/pfk.c +++ b/security/pfe/pfk.c @@ -14,24 +14,27 @@ /* * Per-File-Key (PFK). * - * This driver is used for storing eCryptfs information (mainly file - * encryption key) in file node as part of eCryptfs hardware enhanced solution - * provided by Qualcomm Technologies, Inc. + * This driver is responsible for overall management of various + * Per File Encryption variants that work on top of or as part of different + * file systems. * - * The information is stored in node when file is first opened (eCryptfs - * will fire a callback notifying PFK about this event) and will be later - * accessed by Block Device Driver to actually load the key to encryption hw. + * The driver has the following purpose : + * 1) Define priorities between PFE's if more than one is enabled + * 2) Extract key information from inode + * 3) Load and manage various keys in ICE HW engine + * 4) It should be invoked from various layers in FS/BLOCK/STORAGE DRIVER + * that need to take decision on HW encryption management of the data + * Some examples: + * BLOCK LAYER: when it takes decision on whether 2 chunks can be united + * to one encryption / decryption request sent to the HW * - * PFK exposes API's for loading and removing keys from encryption hw - * and also API to determine whether 2 adjacent blocks can be agregated by - * Block Layer in one request to encryption hw. - * PFK is only supposed to be used by eCryptfs, except the below. + * UFS DRIVER: when it need to configure ICE HW with a particular key slot + * to be used for encryption / decryption + * + * PFE variants can differ on particular way of storing the cryptographic info + * inside inode, actions to be taken upon file operations, etc., but the common + * properties are described above * - * Please note, the only API that uses EXPORT_SYMBOL() is pfk_remove_key, - * this is intentionally, as it is the only API that is intended to be used - * by any kernel module, including dynamically loaded ones. All other API's, - * as mentioned above are only supposed to be used by eCryptfs which is - * a static module. */ @@ -45,7 +48,6 @@ #include #include #include -#include #include #include @@ -55,17 +57,99 @@ #include "objsec.h" #include "ecryptfs_kernel.h" #include "pfk_ice.h" +#include "pfk_ext4.h" +#include "pfk_ecryptfs.h" +#include "pfk_internal.h" +#include "ext4.h" -static DEFINE_MUTEX(pfk_lock); static bool pfk_ready; -static int g_events_handle; /* might be replaced by a table when more than one cipher is supported */ -#define PFK_SUPPORTED_CIPHER "aes_xts" #define PFK_SUPPORTED_KEY_SIZE 32 #define PFK_SUPPORTED_SALT_SIZE 32 +/* Various PFE types and function tables to support each one of them */ +enum pfe_type {ECRYPTFS_PFE, EXT4_CRYPT_PFE, INVALID_PFE}; + +typedef int (*pfk_parse_inode_type)(const struct bio *bio, + const struct inode *inode, + struct pfk_key_info *key_info, + enum ice_cryto_algo_mode *algo, + bool *is_pfe); + +typedef bool (*pfk_allow_merge_bio_type)(const struct bio *bio1, + const struct bio *bio2, const struct inode *inode1, + const struct inode *inode2); + +static const pfk_parse_inode_type pfk_parse_inode_ftable[] = { + /* ECRYPTFS_PFE */ &pfk_ecryptfs_parse_inode, + /* EXT4_CRYPT_PFE */ &pfk_ext4_parse_inode, +}; + +static const pfk_allow_merge_bio_type pfk_allow_merge_bio_ftable[] = { + /* ECRYPTFS_PFE */ &pfk_ecryptfs_allow_merge_bio, + /* EXT4_CRYPT_PFE */ &pfk_ext4_allow_merge_bio, +}; + +static void __exit pfk_exit(void) +{ + pfk_ready = false; + pfk_ext4_deinit(); + pfk_ecryptfs_deinit(); + pfk_kc_deinit(); +} + +static int __init pfk_init(void) +{ + + int ret = 0; + + ret = pfk_ecryptfs_init(); + if (ret != 0) + goto fail; + + ret = pfk_ext4_init(); + if (ret != 0) { + pfk_ecryptfs_deinit(); + goto fail; + } + + ret = pfk_kc_init(); + if (ret != 0) { + pr_err("could init pfk key cache, error %d\n", ret); + pfk_ext4_deinit(); + pfk_ecryptfs_deinit(); + goto fail; + } + + pfk_ready = true; + pr_info("Driver initialized successfully\n"); + + return 0; + +fail: + pr_err("Failed to init driver\n"); + return -ENODEV; +} + +/* + * If more than one type is supported simultaneously, this function will also + * set the priority between them + */ +static enum pfe_type pfk_get_pfe_type(const struct inode *inode) +{ + if (!inode) + return INVALID_PFE; + + if (pfk_is_ecryptfs_type(inode)) + return ECRYPTFS_PFE; + + if (pfk_is_ext4_type(inode)) + return EXT4_CRYPT_PFE; + + return INVALID_PFE; +} /** * inode_to_filename() - get the filename from inode pointer. @@ -75,7 +159,7 @@ static int g_events_handle; * * Return: filename string or "unknown". */ -static char *inode_to_filename(struct inode *inode) +char *inode_to_filename(const struct inode *inode) { struct dentry *dentry = NULL; char *filename = NULL; @@ -89,55 +173,6 @@ static char *inode_to_filename(struct inode *inode) return filename; } -static int pfk_inode_alloc_security(struct inode *inode) -{ - struct inode_security_struct *i_sec = NULL; - - if (inode == NULL) - return -EINVAL; - - i_sec = kzalloc(sizeof(*i_sec), GFP_KERNEL); - - if (i_sec == NULL) - return -ENOMEM; - - inode->i_security = i_sec; - - return 0; -} - -static void pfk_inode_free_security(struct inode *inode) -{ - if (inode == NULL) - return; - - kzfree(inode->i_security); -} - -static struct security_hook_list pfk_hooks[] = { - LSM_HOOK_INIT(inode_alloc_security, pfk_inode_alloc_security), - LSM_HOOK_INIT(inode_free_security, pfk_inode_free_security), - LSM_HOOK_INIT(allow_merge_bio, pfk_allow_merge_bio), -}; - -static int __init pfk_lsm_init(void) -{ - /* Check if PFK is the chosen lsm via security_module_enable() */ - if (security_module_enable("pfk")) { - security_add_hooks(pfk_hooks, ARRAY_SIZE(pfk_hooks)); - pr_debug("pfk is the chosen lsm, registered successfully !\n"); - } else { - pr_debug("pfk is not the chosen lsm.\n"); - if (!selinux_is_enabled()) { - pr_err("se linux is not enabled.\n"); - return -ENODEV; - } - - } - - return 0; -} - /** * pfk_is_ready() - driver is initialized and ready. * @@ -148,37 +183,6 @@ static inline bool pfk_is_ready(void) return pfk_ready; } -/** - * pfk_get_page_index() - get the inode from a bio. - * @bio: Pointer to BIO structure. - * - * Walk the bio struct links to get the inode. - * Please note, that in general bio may consist of several pages from - * several files, but in our case we always assume that all pages come - * from the same file, since our logic ensures it. That is why we only - * walk through the first page to look for inode. - * - * Return: pointer to the inode struct if successful, or NULL otherwise. - * - */ -static int pfk_get_page_index(const struct bio *bio, pgoff_t *page_index) -{ - if (!bio || !page_index) - return -EINVAL; - - if (!bio_has_data((struct bio *)bio)) - return -EINVAL; - - if (!bio->bi_io_vec) - return -EINVAL; - if (!bio->bi_io_vec->bv_page) - return -EINVAL; - - *page_index = bio->bi_io_vec->bv_page->index; - - return 0; -} - /** * pfk_bio_get_inode() - get the inode from a bio. * @bio: Pointer to BIO structure. @@ -196,10 +200,8 @@ static struct inode *pfk_bio_get_inode(const struct bio *bio) { if (!bio) return NULL; - if (!bio_has_data((struct bio *)bio)) return NULL; - if (!bio->bi_io_vec) return NULL; if (!bio->bi_io_vec->bv_page) @@ -224,89 +226,6 @@ static struct inode *pfk_bio_get_inode(const struct bio *bio) return bio->bi_io_vec->bv_page->mapping->host; } -/** - * pfk_get_ecryptfs_data() - retrieves ecryptfs data stored inside node - * @inode: inode - * - * Return the data or NULL if there isn't any or in case of error - * Should be invoked under lock - */ -static void *pfk_get_ecryptfs_data(const struct inode *inode) -{ - struct inode_security_struct *isec = NULL; - - if (!inode) - return NULL; - - isec = inode->i_security; - - if (!isec) { - pr_debug("i_security is NULL, could be irrelevant file\n"); - return NULL; - } - - return isec->pfk_data; -} - -/** - * pfk_set_ecryptfs_data() - stores ecryptfs data inside node - * @inode: inode to update - * @data: data to put inside the node - * - * Returns 0 in case of success, error otherwise - * Should be invoked under lock - */ -static int pfk_set_ecryptfs_data(struct inode *inode, void *ecryptfs_data) -{ - struct inode_security_struct *isec = NULL; - - if (!inode) - return -EINVAL; - - isec = inode->i_security; - - if (!isec) { - pr_err("i_security is NULL, not ready yet\n"); - return -EINVAL; - } - - isec->pfk_data = ecryptfs_data; - - return 0; -} - - -/** - * pfk_parse_cipher() - parse cipher from ecryptfs to enum - * @ecryptfs_data: ecrypfs data - * @algo: pointer to store the output enum (can be null) - * - * return 0 in case of success, error otherwise (i.e not supported cipher) - */ -static int pfk_parse_cipher(const void *ecryptfs_data, - enum ice_cryto_algo_mode *algo) -{ - /* - * currently only AES XTS algo is supported - * in the future, table with supported ciphers might - * be introduced - */ - - if (!ecryptfs_data) - return -EINVAL; - - if (!ecryptfs_cipher_match(ecryptfs_data, - PFK_SUPPORTED_CIPHER, sizeof(PFK_SUPPORTED_CIPHER))) { - pr_debug("ecryptfs alghoritm is not supported by pfk\n"); - return -EINVAL; - } - - if (algo) - *algo = ICE_CRYPTO_ALGO_MODE_AES_XTS; - - return 0; -} - /** * pfk_key_size_to_key_type() - translate key size to key size enum * @key_size: key size in bytes @@ -314,7 +233,7 @@ static int pfk_parse_cipher(const void *ecryptfs_data, * * return 0 in case of success, error otherwise (i.e not supported key size) */ -static int pfk_key_size_to_key_type(size_t key_size, +int pfk_key_size_to_key_type(size_t key_size, enum ice_crpto_key_size *key_size_type) { /* @@ -334,100 +253,38 @@ static int pfk_key_size_to_key_type(size_t key_size, return 0; } -static int pfk_bio_to_key(const struct bio *bio, unsigned char const **key, - size_t *key_size, unsigned char const **salt, size_t *salt_size, - bool *is_pfe, bool start) +/* + * Retrieves filesystem type from inode's superblock + */ +bool pfe_is_inode_filesystem_type(const struct inode *inode, + const char *fs_type) { - struct inode *inode = NULL; - int ret = 0; - void *ecryptfs_data = NULL; - pgoff_t offset; - bool is_metadata = false; + if (!inode || !fs_type) + return false; - /* - * only a few errors below can indicate that - * this function was not invoked within PFE context, - * otherwise we will consider it PFE - */ - *is_pfe = true; + if (!inode->i_sb) + return false; + if (!inode->i_sb->s_type) + return false; - if (!bio) - return -EINVAL; - - if (!key || !salt || !key_size || !salt_size) - return -EINVAL; - - inode = pfk_bio_get_inode(bio); - if (!inode) { - *is_pfe = false; - return -EINVAL; - } - - ecryptfs_data = pfk_get_ecryptfs_data(inode); - if (!ecryptfs_data) { - *is_pfe = false; - return -EPERM; - } - - pr_debug("loading key for file %s, start %d\n", - inode_to_filename(inode), start); - - ret = pfk_get_page_index(bio, &offset); - if (ret != 0) { - pr_err("could not get page index from bio, probably bug %d\n", - ret); - return -EINVAL; - } - - is_metadata = ecryptfs_is_page_in_metadata(ecryptfs_data, offset); - if (is_metadata == true) { - pr_debug("ecryptfs metadata, bypassing ICE\n"); - *is_pfe = false; - return -EPERM; - } - - *key = ecryptfs_get_key(ecryptfs_data); - if (!key) { - pr_err("could not parse key from ecryptfs\n"); - return -EINVAL; - } - - *key_size = ecryptfs_get_key_size(ecryptfs_data); - if (!(*key_size)) { - pr_err("could not parse key size from ecryptfs\n"); - return -EINVAL; - } - - *salt = ecryptfs_get_salt(ecryptfs_data); - if (!salt) { - pr_err("could not parse salt from ecryptfs\n"); - return -EINVAL; - } - - *salt_size = ecryptfs_get_salt_size(ecryptfs_data); - if (!(*salt_size)) { - pr_err("could not parse salt size from ecryptfs\n"); - return -EINVAL; - } - - return 0; + return (strcmp(inode->i_sb->s_type->name, fs_type) == 0); } + /** * pfk_load_key_start() - loads PFE encryption key to the ICE - * Can also be invoked from non - * PFE context, than it is not - * relevant and is_pfe flag is - * set to true + * Can also be invoked from non + * PFE context, in this case it + * is not relevant and is_pfe + * flag is set to false + * * @bio: Pointer to the BIO structure * @ice_setting: Pointer to ice setting structure that will be filled with * ice configuration values, including the index to which the key was loaded - * @is_pfe: Pointer to is_pfe flag, which will be true if function was invoked - * from PFE context + * @is_pfe: will be false if inode is not relevant to PFE, in such a case + * it should be treated as non PFE by the block layer * - * Via bio gets access to ecryptfs key stored in auxiliary structure inside - * inode and loads it to encryption hw. * Returns the index where the key is stored in encryption hw and additional * information that will be used later for configuration of the encryption hw. * @@ -439,15 +296,12 @@ int pfk_load_key_start(const struct bio *bio, bool async) { int ret = 0; - const unsigned char *key = NULL; - const unsigned char *salt = NULL; - size_t key_size = 0; - size_t salt_size = 0; - enum ice_cryto_algo_mode algo_mode = 0; + struct pfk_key_info key_info = {0}; + enum ice_cryto_algo_mode algo_mode = ICE_CRYPTO_ALGO_MODE_AES_XTS; enum ice_crpto_key_size key_size_type = 0; - void *ecryptfs_data = NULL; u32 key_index = 0; struct inode *inode = NULL; + enum pfe_type which_pfe = INVALID_PFE; if (!is_pfe) { pr_err("is_pfe is NULL\n"); @@ -469,35 +323,32 @@ int pfk_load_key_start(const struct bio *bio, return -EINVAL; } - ret = pfk_bio_to_key(bio, &key, &key_size, &salt, &salt_size, is_pfe, - true); - if (ret != 0) - return ret; - inode = pfk_bio_get_inode(bio); if (!inode) { *is_pfe = false; return -EINVAL; } - ecryptfs_data = pfk_get_ecryptfs_data(inode); - if (!ecryptfs_data) { + which_pfe = pfk_get_pfe_type(inode); + if (which_pfe == INVALID_PFE) { *is_pfe = false; return -EPERM; } - ret = pfk_parse_cipher(ecryptfs_data, &algo_mode); - if (ret != 0) { - pr_err("not supported cipher\n"); - return ret; - } + pr_debug("parsing file %s with PFE %d\n", + inode_to_filename(inode), which_pfe); - ret = pfk_key_size_to_key_type(key_size, &key_size_type); + ret = (*(pfk_parse_inode_ftable[which_pfe])) + (bio, inode, &key_info, &algo_mode, is_pfe); if (ret != 0) return ret; - ret = pfk_kc_load_key_start(key, key_size, salt, salt_size, &key_index, - async); + ret = pfk_key_size_to_key_type(key_info.key_size, &key_size_type); + if (ret != 0) + return ret; + + ret = pfk_kc_load_key_start(key_info.key, key_info.key_size, + key_info.salt, key_info.salt_size, &key_index, async); if (ret) { if (ret != -EBUSY && ret != -EAGAIN) pr_err("start: could not load key into pfk key cache, error %d\n", @@ -512,30 +363,29 @@ int pfk_load_key_start(const struct bio *bio, ice_setting->key_mode = ICE_CRYPTO_USE_LUT_SW_KEY; ice_setting->key_index = key_index; + pr_debug("loaded key for file %s key_index %d\n", + inode_to_filename(inode), key_index); + return 0; } /** * pfk_load_key_end() - marks the PFE key as no longer used by ICE - * Can also be invoked from non - * PFE context, than it is not - * relevant and is_pfe flag is - * set to true + * Can also be invoked from non + * PFE context, in this case it is not + * relevant and is_pfe flag is + * set to false + * * @bio: Pointer to the BIO structure * @is_pfe: Pointer to is_pfe flag, which will be true if function was invoked * from PFE context - * - * Via bio gets access to ecryptfs key stored in auxiliary structure inside - * inode and loads it to encryption hw. - * */ int pfk_load_key_end(const struct bio *bio, bool *is_pfe) { int ret = 0; - const unsigned char *key = NULL; - const unsigned char *salt = NULL; - size_t key_size = 0; - size_t salt_size = 0; + struct pfk_key_info key_info = {0}; + enum pfe_type which_pfe = INVALID_PFE; + struct inode *inode = NULL; if (!is_pfe) { pr_err("is_pfe is NULL\n"); @@ -551,43 +401,32 @@ int pfk_load_key_end(const struct bio *bio, bool *is_pfe) if (!pfk_is_ready()) return -ENODEV; - ret = pfk_bio_to_key(bio, &key, &key_size, &salt, &salt_size, is_pfe, - false); + inode = pfk_bio_get_inode(bio); + if (!inode) { + *is_pfe = false; + return -EINVAL; + } + + which_pfe = pfk_get_pfe_type(inode); + if (which_pfe == INVALID_PFE) { + *is_pfe = false; + return -EPERM; + } + + ret = (*(pfk_parse_inode_ftable[which_pfe])) + (bio, inode, &key_info, NULL, is_pfe); if (ret != 0) return ret; - pfk_kc_load_key_end(key, key_size, salt, salt_size); + pfk_kc_load_key_end(key_info.key, key_info.key_size, + key_info.salt, key_info.salt_size); + + pr_debug("finished using key for file %s\n", + inode_to_filename(inode)); return 0; } -/** - * pfk_remove_key() - removes key from hw - * @key: pointer to the key - * @key_size: key size - * - * Will be used by external clients to remove a particular key for security - * reasons. - * The only API that can be used by dynamically loaded modules, - * see explanations above at the beginning of this file. - * The key is removed securely (by memsetting the previous value) - */ -int pfk_remove_key(const unsigned char *key, size_t key_size) -{ - int ret = 0; - - if (!pfk_is_ready()) - return -ENODEV; - - if (!key) - return -EINVAL; - - ret = pfk_kc_remove_key(key, key_size); - - return ret; -} -EXPORT_SYMBOL(pfk_remove_key); - /** * pfk_allow_merge_bio() - Check if 2 BIOs can be merged. * @bio1: Pointer to first BIO structure. @@ -603,252 +442,39 @@ EXPORT_SYMBOL(pfk_remove_key); * Return: true if the BIOs allowed to be merged, false * otherwise. */ -bool pfk_allow_merge_bio(struct bio *bio1, struct bio *bio2) +bool pfk_allow_merge_bio(const struct bio *bio1, const struct bio *bio2) { - int ret; - void *ecryptfs_data1 = NULL; - void *ecryptfs_data2 = NULL; - pgoff_t offset1, offset2; - bool res = false; + struct inode *inode1 = NULL; + struct inode *inode2 = NULL; + enum pfe_type which_pfe1 = INVALID_PFE; + enum pfe_type which_pfe2 = INVALID_PFE; - /* if there is no pfk, don't disallow merging blocks */ if (!pfk_is_ready()) - return true; + return false; if (!bio1 || !bio2) return false; - ecryptfs_data1 = pfk_get_ecryptfs_data(pfk_bio_get_inode(bio1)); - ecryptfs_data2 = pfk_get_ecryptfs_data(pfk_bio_get_inode(bio2)); + if (bio1 == bio2) + return true; - /* - * if we have 2 different encrypted files or 1 encrypted and 1 regular, - * merge is forbidden - */ - if (!ecryptfs_is_data_equal(ecryptfs_data1, ecryptfs_data2)) { - res = false; - goto end; - } - - /* - * if both are equall in their NULLINNESS, we have 2 unencrypted files, - * allow merge - */ - if (!ecryptfs_data1) { - res = true; - goto end; - } - - /* - * at this point both bio's are in the same file which is probably - * encrypted, last thing to check is header vs data - * We are assuming that we are not working in O_DIRECT mode, - * since it is not currently supported by eCryptfs - */ - ret = pfk_get_page_index(bio1, &offset1); - if (ret != 0) { - pr_err("could not get page index from bio1, probably bug %d\n", - ret); - res = false; - goto end; - } - - ret = pfk_get_page_index(bio2, &offset2); - if (ret != 0) { - pr_err("could not get page index from bio2, bug %d\n", ret); - res = false; - goto end; - } - - res = (ecryptfs_is_page_in_metadata(ecryptfs_data1, offset1) == - ecryptfs_is_page_in_metadata(ecryptfs_data2, offset2)); - - /* fall through */ - -end: - - return res; -} - -/** - * pfk_open_cb() - callback function for file open event - * @inode: file inode - * @data: data provided by eCryptfs - * - * Will be invoked from eCryptfs in case of file open event - */ -static void pfk_open_cb(struct inode *inode, void *ecryptfs_data) -{ - size_t key_size; - - if (!pfk_is_ready()) - return; - - if (!inode) { - pr_err("inode is null\n"); - return; - } - - key_size = ecryptfs_get_key_size(ecryptfs_data); - if (!(key_size)) { - pr_err("could not parse key size from ecryptfs\n"); - return; - } - - if (0 != pfk_parse_cipher(ecryptfs_data, NULL)) { - pr_debug("open_cb: not supported cipher\n"); - return; - } + inode1 = pfk_bio_get_inode(bio1); + inode2 = pfk_bio_get_inode(bio2); - if (0 != pfk_key_size_to_key_type(key_size, NULL)) - return; + which_pfe1 = pfk_get_pfe_type(inode1); + which_pfe2 = pfk_get_pfe_type(inode2); - mutex_lock(&pfk_lock); - pfk_set_ecryptfs_data(inode, ecryptfs_data); - mutex_unlock(&pfk_lock); -} - -/** - * pfk_release_cb() - callback function for file release event - * @inode: file inode - * - * Will be invoked from eCryptfs in case of file release event - */ -static void pfk_release_cb(struct inode *inode) -{ - const unsigned char *key = NULL; - const unsigned char *salt = NULL; - size_t key_size = 0; - size_t salt_size = 0; - void *data = NULL; - - if (!pfk_is_ready()) - return; - - if (!inode) { - pr_err("inode is null\n"); - return; - } - - data = pfk_get_ecryptfs_data(inode); - if (!data) { - pr_debug("could not get ecryptfs data from inode\n"); - return; - } - - key = ecryptfs_get_key(data); - if (!key) { - pr_err("could not parse key from ecryptfs\n"); - return; - } - - key_size = ecryptfs_get_key_size(data); - if (!(key_size)) { - pr_err("could not parse key size from ecryptfs\n"); - return; - } - - salt = ecryptfs_get_salt(data); - if (!salt) { - pr_err("could not parse salt from ecryptfs\n"); - return; - } - - salt_size = ecryptfs_get_salt_size(data); - if (!salt_size) { - pr_err("could not parse salt size from ecryptfs\n"); - return; - } - - pfk_kc_remove_key_with_salt(key, key_size, salt, salt_size); - - - mutex_lock(&pfk_lock); - pfk_set_ecryptfs_data(inode, NULL); - mutex_unlock(&pfk_lock); -} - -static bool pfk_is_cipher_supported_cb(const void *ecryptfs_data) -{ - if (!pfk_is_ready()) + /* nodes with different encryption, do not merge */ + if (which_pfe1 != which_pfe2) return false; - if (!ecryptfs_data) - return false; + /* both nodes do not have encryption, allow merge */ + if (which_pfe1 == INVALID_PFE) + return true; - return (pfk_parse_cipher(ecryptfs_data, NULL)) == 0; -} - -static bool pfk_is_hw_crypt_cb(void) -{ - if (!pfk_is_ready()) - return false; - - return true; -} - -static size_t pfk_get_salt_key_size_cb(const void *ecryptfs_data) -{ - if (!pfk_is_ready()) - return 0; - - if (!pfk_is_cipher_supported_cb(ecryptfs_data)) - return 0; - - return PFK_SUPPORTED_SALT_SIZE; -} - - -static void __exit pfk_exit(void) -{ - pfk_ready = false; - ecryptfs_unregister_from_events(g_events_handle); - pfk_kc_deinit(); -} - -static int __init pfk_init(void) -{ - - int ret = 0; - struct ecryptfs_events events = {0}; - - events.open_cb = pfk_open_cb; - events.release_cb = pfk_release_cb; - events.is_cipher_supported_cb = pfk_is_cipher_supported_cb; - events.is_hw_crypt_cb = pfk_is_hw_crypt_cb; - events.get_salt_key_size_cb = pfk_get_salt_key_size_cb; - - g_events_handle = ecryptfs_register_to_events(&events); - if (0 == g_events_handle) { - pr_err("could not register with eCryptfs, error %d\n", ret); - goto fail; - } - - ret = pfk_kc_init(); - if (ret != 0) { - pr_err("could init pfk key cache, error %d\n", ret); - ecryptfs_unregister_from_events(g_events_handle); - goto fail; - } - - ret = pfk_lsm_init(); - if (ret != 0) { - pr_debug("neither pfk nor se-linux sec modules are enabled\n"); - pr_debug("not an error, just don't enable pfk\n"); - pfk_kc_deinit(); - ecryptfs_unregister_from_events(g_events_handle); - return 0; - } - - pfk_ready = true; - pr_info("Driver initialized successfully\n"); - - return 0; - -fail: - pr_err("Failed to init driver\n"); - return -ENODEV; + return (*(pfk_allow_merge_bio_ftable[which_pfe1]))(bio1, bio2, + inode1, inode2); } module_init(pfk_init); diff --git a/security/pfe/pfk_ecryptfs.c b/security/pfe/pfk_ecryptfs.c new file mode 100644 index 000000000000..1d6a2eeaf6fc --- /dev/null +++ b/security/pfe/pfk_ecryptfs.c @@ -0,0 +1,630 @@ +/* + * 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. + */ + +/* + * Per-File-Key (PFK) - eCryptfs. + * + * This driver is used for storing eCryptfs information (mainly file + * encryption key) in file node as part of eCryptfs hardware enhanced solution + * provided by Qualcomm Technologies, Inc. + * + * The information is stored in node when file is first opened (eCryptfs + * will fire a callback notifying PFK about this event) and will be later + * accessed by Block Device Driver to actually load the key to encryption hw. + * + * PFK exposes API's for loading and removing keys from encryption hw + * and also API to determine whether 2 adjacent blocks can be agregated by + * Block Layer in one request to encryption hw. + * PFK is only supposed to be used by eCryptfs, except the below. + * + */ + + +/* Uncomment the line below to enable debug messages */ +/* #define DEBUG 1 */ +#define pr_fmt(fmt) "pfk_ecryptfs [%s]: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "pfk_ecryptfs.h" +#include "pfk_kc.h" +#include "objsec.h" +#include "ecryptfs_kernel.h" +#include "pfk_ice.h" + +static DEFINE_MUTEX(pfk_ecryptfs_lock); +static bool pfk_ecryptfs_ready; +static int g_events_handle; + + +/* might be replaced by a table when more than one cipher is supported */ +#define PFK_SUPPORTED_CIPHER "aes_xts" +#define PFK_SUPPORTED_SALT_SIZE 32 + +static void *pfk_ecryptfs_get_data(const struct inode *inode); +static void pfk_ecryptfs_open_cb(struct inode *inode, void *ecryptfs_data); +static void pfk_ecryptfs_release_cb(struct inode *inode); +static bool pfk_ecryptfs_is_cipher_supported_cb(const void *ecryptfs_data); +static size_t pfk_ecryptfs_get_salt_key_size_cb(const void *ecryptfs_data); +static bool pfk_ecryptfs_is_hw_crypt_cb(void); + + +/** + * pfk_is_ecryptfs_type() - return true if inode belongs to ICE ecryptfs PFE + * @inode: inode pointer + */ +bool pfk_is_ecryptfs_type(const struct inode *inode) +{ + void *ecryptfs_data = NULL; + + /* + * the actual filesystem of an inode is still ext4, eCryptfs never + * reaches bio + */ + if (!pfe_is_inode_filesystem_type(inode, "ext4")) + return false; + + ecryptfs_data = pfk_ecryptfs_get_data(inode); + + if (!ecryptfs_data) + return false; + + return true; +} + +static int pfk_ecryptfs_inode_alloc_security(struct inode *inode) +{ + struct inode_security_struct *i_sec = NULL; + + if (inode == NULL) + return -EINVAL; + + i_sec = kzalloc(sizeof(*i_sec), GFP_KERNEL); + + if (i_sec == NULL) + return -ENOMEM; + + inode->i_security = i_sec; + + return 0; +} + +static void pfk_ecryptfs_inode_free_security(struct inode *inode) +{ + if (inode == NULL) + return; + + kzfree(inode->i_security); +} + +static struct security_hook_list pfk_ecryptfs_hooks[] = { + LSM_HOOK_INIT(inode_alloc_security, pfk_ecryptfs_inode_alloc_security), + LSM_HOOK_INIT(inode_free_security, pfk_ecryptfs_inode_free_security), +}; + +/* + * pfk_ecryptfs_lsm_init() - makes sure either se-linux or pfk_ecryptfs are + * registered as security module. + * + * This is required because ecryptfs uses a field inside security struct in + * inode to store its info + */ +static int __init pfk_ecryptfs_lsm_init(void) +{ + /* Check if PFK is the chosen lsm via security_module_enable() */ + if (security_module_enable("pfk_ecryptfs")) { + security_add_hooks(pfk_ecryptfs_hooks, + ARRAY_SIZE(pfk_ecryptfs_hooks)); + pr_debug("pfk_ecryptfs is the chosen lsm, registered successfully !\n"); + } else { + pr_debug("pfk_ecryptfs is not the chosen lsm.\n"); + if (!selinux_is_enabled()) { + pr_err("se linux is not enabled.\n"); + return -ENODEV; + } + } + + return 0; +} + +/* + * pfk_ecryptfs_deinit() - Deinit function, should be invoked by upper PFK layer + */ +void __exit pfk_ecryptfs_deinit(void) +{ + pfk_ecryptfs_ready = false; + ecryptfs_unregister_from_events(g_events_handle); +} + +/* + * pfk_ecryptfs_init() - Init function, should be invoked by upper PFK layer + */ +int __init pfk_ecryptfs_init(void) +{ + int ret = 0; + struct ecryptfs_events events = {0}; + + events.open_cb = pfk_ecryptfs_open_cb; + events.release_cb = pfk_ecryptfs_release_cb; + events.is_cipher_supported_cb = pfk_ecryptfs_is_cipher_supported_cb; + events.is_hw_crypt_cb = pfk_ecryptfs_is_hw_crypt_cb; + events.get_salt_key_size_cb = pfk_ecryptfs_get_salt_key_size_cb; + + g_events_handle = ecryptfs_register_to_events(&events); + if (g_events_handle == 0) { + pr_err("could not register with eCryptfs, error %d\n", ret); + goto fail; + } + + ret = pfk_ecryptfs_lsm_init(); + if (ret != 0) { + pr_debug("neither pfk nor se-linux sec modules are enabled\n"); + pr_debug("not an error, just don't enable PFK ecryptfs\n"); + ecryptfs_unregister_from_events(g_events_handle); + return 0; + } + + pfk_ecryptfs_ready = true; + pr_info("PFK ecryptfs inited successfully\n"); + + return 0; + +fail: + pr_err("Failed to init PFK ecryptfs\n"); + return -ENODEV; +} + +/** + * pfk_ecryptfs_is_ready() - driver is initialized and ready. + * + * Return: true if the driver is ready. + */ +static inline bool pfk_ecryptfs_is_ready(void) +{ + return pfk_ecryptfs_ready; +} + +/** + * pfk_ecryptfs_get_page_index() - get the inode from a bio. + * @bio: Pointer to BIO structure. + * + * Walk the bio struct links to get the inode. + * Please note, that in general bio may consist of several pages from + * several files, but in our case we always assume that all pages come + * from the same file, since our logic ensures it. That is why we only + * walk through the first page to look for inode. + * + * Return: pointer to the inode struct if successful, or NULL otherwise. + * + */ +static int pfk_ecryptfs_get_page_index(const struct bio *bio, + pgoff_t *page_index) +{ + if (!bio || !page_index) + return -EINVAL; + if (!bio_has_data((struct bio *)bio)) + return -EINVAL; + if (!bio->bi_io_vec) + return -EINVAL; + if (!bio->bi_io_vec->bv_page) + return -EINVAL; + + *page_index = bio->bi_io_vec->bv_page->index; + + return 0; +} + +/** + * pfk_ecryptfs_get_data() - retrieves ecryptfs data stored inside node + * @inode: inode + * + * Return the data or NULL if there isn't any or in case of error + * Should be invoked under lock + */ +static void *pfk_ecryptfs_get_data(const struct inode *inode) +{ + struct inode_security_struct *isec = NULL; + + if (!inode) + return NULL; + + isec = inode->i_security; + + if (!isec) { + pr_debug("i_security is NULL, could be irrelevant file\n"); + return NULL; + } + + return isec->pfk_data; +} + +/** + * pfk_ecryptfs_set_data() - stores ecryptfs data inside node + * @inode: inode to update + * @data: data to put inside the node + * + * Returns 0 in case of success, error otherwise + * Should be invoked under lock + */ +static int pfk_ecryptfs_set_data(struct inode *inode, void *ecryptfs_data) +{ + struct inode_security_struct *isec = NULL; + + if (!inode) + return -EINVAL; + + isec = inode->i_security; + + if (!isec) { + pr_err("i_security is NULL, not ready yet\n"); + return -EINVAL; + } + + isec->pfk_data = ecryptfs_data; + + return 0; +} + + +/** + * pfk_ecryptfs_parse_cipher() - parse cipher from ecryptfs to enum + * @ecryptfs_data: ecrypfs data + * @algo: pointer to store the output enum (can be null) + * + * return 0 in case of success, error otherwise (i.e not supported cipher) + */ +static int pfk_ecryptfs_parse_cipher(const void *ecryptfs_data, + enum ice_cryto_algo_mode *algo) +{ + /* + * currently only AES XTS algo is supported + * in the future, table with supported ciphers might + * be introduced + */ + + if (!ecryptfs_data) + return -EINVAL; + + if (!ecryptfs_cipher_match(ecryptfs_data, + PFK_SUPPORTED_CIPHER, sizeof(PFK_SUPPORTED_CIPHER))) { + pr_debug("ecryptfs alghoritm is not supported by pfk\n"); + return -EINVAL; + } + + if (algo) + *algo = ICE_CRYPTO_ALGO_MODE_AES_XTS; + + return 0; +} + +/* + * pfk_ecryptfs_parse_inode() - parses key and algo information from inode + * + * Should be invoked by upper pfk layer + * @bio: bio + * @inode: inode to be parsed + * @key_info: out, key and salt information to be stored + * @algo: out, algorithm to be stored (can be null) + * @is_pfe: out, will be false if inode is not relevant to PFE, in such a case + * it should be treated as non PFE by the block layer + */ +int pfk_ecryptfs_parse_inode(const struct bio *bio, + const struct inode *inode, + struct pfk_key_info *key_info, + enum ice_cryto_algo_mode *algo, + bool *is_pfe) +{ + int ret = 0; + void *ecryptfs_data = NULL; + pgoff_t offset; + bool is_metadata = false; + + if (!is_pfe) + return -EINVAL; + + /* + * only a few errors below can indicate that + * this function was not invoked within PFE context, + * otherwise we will consider it PFE + */ + *is_pfe = true; + + if (!pfk_ecryptfs_is_ready()) + return -ENODEV; + + if (!inode) + return -EINVAL; + + if (!key_info) + return -EINVAL; + + ecryptfs_data = pfk_ecryptfs_get_data(inode); + if (!ecryptfs_data) { + pr_err("internal error, no ecryptfs data\n"); + return -EINVAL; + } + + ret = pfk_ecryptfs_get_page_index(bio, &offset); + if (ret != 0) { + pr_err("could not get page index from bio, probably bug %d\n", + ret); + return -EINVAL; + } + + is_metadata = ecryptfs_is_page_in_metadata(ecryptfs_data, offset); + if (is_metadata == true) { + pr_debug("ecryptfs metadata, bypassing ICE\n"); + *is_pfe = false; + return -EPERM; + } + + key_info->key = ecryptfs_get_key(ecryptfs_data); + if (!key_info->key) { + pr_err("could not parse key from ecryptfs\n"); + return -EINVAL; + } + + key_info->key_size = ecryptfs_get_key_size(ecryptfs_data); + if (!key_info->key_size) { + pr_err("could not parse key size from ecryptfs\n"); + return -EINVAL; + } + + key_info->salt = ecryptfs_get_salt(ecryptfs_data); + if (!key_info->salt) { + pr_err("could not parse salt from ecryptfs\n"); + return -EINVAL; + } + + key_info->salt_size = ecryptfs_get_salt_size(ecryptfs_data); + if (!key_info->salt_size) { + pr_err("could not parse salt size from ecryptfs\n"); + return -EINVAL; + } + + ret = pfk_ecryptfs_parse_cipher(ecryptfs_data, algo); + if (ret != 0) { + pr_err("not supported cipher\n"); + return ret; + } + + return 0; +} + +/** + * pfk_ecryptfs_allow_merge_bio() - Check if 2 bios can be merged. + * + * Should be invoked by upper pfk layer + * + * @bio1: Pointer to first BIO structure. + * @bio2: Pointer to second BIO structure. + * @inode1: Pointer to inode from first bio + * @inode2: Pointer to inode from second bio + * + * Prevent merging of BIOs from encrypted and non-encrypted + * files, or files encrypted with different key. + * Also prevent non encrypted and encrypted data from the same file + * to be merged (ecryptfs header if stored inside file should be non + * encrypted) + * + * Return: true if the BIOs allowed to be merged, false + * otherwise. + */ +bool pfk_ecryptfs_allow_merge_bio(const struct bio *bio1, + const struct bio *bio2, const struct inode *inode1, + const struct inode *inode2) +{ + int ret; + void *ecryptfs_data1 = NULL; + void *ecryptfs_data2 = NULL; + pgoff_t offset1, offset2; + + /* if there is no ecryptfs pfk, don't disallow merging blocks */ + if (!pfk_ecryptfs_is_ready()) + return true; + + if (!inode1 || !inode2) + return false; + + ecryptfs_data1 = pfk_ecryptfs_get_data(inode1); + ecryptfs_data2 = pfk_ecryptfs_get_data(inode2); + + if (!ecryptfs_data1 || !ecryptfs_data2) { + pr_err("internal error, ecryptfs data should not be null"); + return false; + } + + /* + * if we have 2 different encrypted files merge is not allowed + */ + if (!ecryptfs_is_data_equal(ecryptfs_data1, ecryptfs_data2)) + return false; + + /* + * at this point both bio's are in the same file which is probably + * encrypted, last thing to check is header vs data + * We are assuming that we are not working in O_DIRECT mode, + * since it is not currently supported by eCryptfs + */ + ret = pfk_ecryptfs_get_page_index(bio1, &offset1); + if (ret != 0) { + pr_err("could not get page index from bio1, probably bug %d\n", + ret); + return false; + } + + ret = pfk_ecryptfs_get_page_index(bio2, &offset2); + if (ret != 0) { + pr_err("could not get page index from bio2, bug %d\n", ret); + return false; + } + + return (ecryptfs_is_page_in_metadata(ecryptfs_data1, offset1) == + ecryptfs_is_page_in_metadata(ecryptfs_data2, offset2)); +} + +/** + * pfk_ecryptfs_open_cb() - callback function for file open event + * @inode: file inode + * @data: data provided by eCryptfs + * + * Will be invoked from eCryptfs in case of file open event + */ +static void pfk_ecryptfs_open_cb(struct inode *inode, void *ecryptfs_data) +{ + size_t key_size; + + if (!pfk_ecryptfs_is_ready()) + return; + + if (!inode) { + pr_err("inode is null\n"); + return; + } + + key_size = ecryptfs_get_key_size(ecryptfs_data); + if (!(key_size)) { + pr_err("could not parse key size from ecryptfs\n"); + return; + } + + if (pfk_ecryptfs_parse_cipher(ecryptfs_data, NULL) != 0) { + pr_debug("open_cb: not supported cipher\n"); + return; + } + + if (pfk_key_size_to_key_type(key_size, NULL) != 0) + return; + + mutex_lock(&pfk_ecryptfs_lock); + pfk_ecryptfs_set_data(inode, ecryptfs_data); + mutex_unlock(&pfk_ecryptfs_lock); +} + +/** + * pfk_ecryptfs_release_cb() - callback function for file release event + * @inode: file inode + * + * Will be invoked from eCryptfs in case of file release event + */ +static void pfk_ecryptfs_release_cb(struct inode *inode) +{ + const unsigned char *key = NULL; + const unsigned char *salt = NULL; + size_t key_size = 0; + size_t salt_size = 0; + void *data = NULL; + + if (!pfk_ecryptfs_is_ready()) + return; + + if (!inode) { + pr_err("inode is null\n"); + return; + } + + data = pfk_ecryptfs_get_data(inode); + if (!data) { + pr_debug("could not get ecryptfs data from inode\n"); + return; + } + + key = ecryptfs_get_key(data); + if (!key) { + pr_err("could not parse key from ecryptfs\n"); + return; + } + + key_size = ecryptfs_get_key_size(data); + if (!(key_size)) { + pr_err("could not parse key size from ecryptfs\n"); + return; + } + + salt = ecryptfs_get_salt(data); + if (!salt) { + pr_err("could not parse salt from ecryptfs\n"); + return; + } + + salt_size = ecryptfs_get_salt_size(data); + if (!salt_size) { + pr_err("could not parse salt size from ecryptfs\n"); + return; + } + + pfk_kc_remove_key_with_salt(key, key_size, salt, salt_size); + + mutex_lock(&pfk_ecryptfs_lock); + pfk_ecryptfs_set_data(inode, NULL); + mutex_unlock(&pfk_ecryptfs_lock); +} + +/* + * pfk_ecryptfs_is_cipher_supported_cb() - callback function to determine + * whether a particular cipher (stored in ecryptfs_data) is cupported by pfk + * + * Ecryptfs should invoke this callback whenever it needs to determine whether + * pfk supports the particular cipher mode + * + * @ecryptfs_data: ecryptfs data + */ +static bool pfk_ecryptfs_is_cipher_supported_cb(const void *ecryptfs_data) +{ + if (!pfk_ecryptfs_is_ready()) + return false; + + if (!ecryptfs_data) + return false; + + return (pfk_ecryptfs_parse_cipher(ecryptfs_data, NULL)) == 0; +} + +/* + * pfk_ecryptfs_is_hw_crypt_cb() - callback function that implements a query + * by ecryptfs whether PFK supports HW encryption + */ +static bool pfk_ecryptfs_is_hw_crypt_cb(void) +{ + if (!pfk_ecryptfs_is_ready()) + return false; + + return true; +} + +/* + * pfk_ecryptfs_get_salt_key_size_cb() - callback function to determine + * what is the salt size supported by PFK + * + * @ecryptfs_data: ecryptfs data + */ +static size_t pfk_ecryptfs_get_salt_key_size_cb(const void *ecryptfs_data) +{ + if (!pfk_ecryptfs_is_ready()) + return 0; + + if (!pfk_ecryptfs_is_cipher_supported_cb(ecryptfs_data)) + return 0; + + return PFK_SUPPORTED_SALT_SIZE; +} diff --git a/security/pfe/pfk_ecryptfs.h b/security/pfe/pfk_ecryptfs.h new file mode 100644 index 000000000000..1d9942c4c09b --- /dev/null +++ b/security/pfe/pfk_ecryptfs.h @@ -0,0 +1,39 @@ +/* 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. + */ + +#ifndef _PFK_ECRYPTFS_H_ +#define _PFK_ECRYPTFS_H_ + +#include +#include +#include +#include +#include "pfk_internal.h" + + +bool pfk_is_ecryptfs_type(const struct inode *inode); + +int pfk_ecryptfs_parse_inode(const struct bio *bio, + const struct inode *inode, + struct pfk_key_info *key_info, + enum ice_cryto_algo_mode *algo, + bool *is_pfe); + +bool pfk_ecryptfs_allow_merge_bio(const struct bio *bio1, + const struct bio *bio2, const struct inode *inode1, + const struct inode *inode2); + +int __init pfk_ecryptfs_init(void); + +void __exit pfk_ecryptfs_deinit(void); + +#endif /* _PFK_ECRYPTFS_H_ */ diff --git a/security/pfe/pfk_ext4.c b/security/pfe/pfk_ext4.c new file mode 100644 index 000000000000..07e2c7ab829c --- /dev/null +++ b/security/pfe/pfk_ext4.c @@ -0,0 +1,212 @@ +/* + * 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. + */ + +/* + * Per-File-Key (PFK) - EXT4 + * + * This driver is used for working with EXT4 crypt extension + * + * The key information is stored in node by EXT4 when file is first opened + * and will be later accessed by Block Device Driver to actually load the key + * to encryption hw. + * + * PFK exposes API's for loading and removing keys from encryption hw + * and also API to determine whether 2 adjacent blocks can be agregated by + * Block Layer in one request to encryption hw. + * + */ + + +/* Uncomment the line below to enable debug messages */ +/* #define DEBUG 1 */ +#define pr_fmt(fmt) "pfk_ext4 [%s]: " fmt, __func__ + +#include +#include +#include +#include + +#include "ext4_ice.h" +#include "pfk_ext4.h" + +static bool pfk_ext4_ready; + +/* + * pfk_ext4_deinit() - Deinit function, should be invoked by upper PFK layer + */ +void __exit pfk_ext4_deinit(void) +{ + pfk_ext4_ready = false; +} + +/* + * pfk_ecryptfs_init() - Init function, should be invoked by upper PFK layer + */ +int __init pfk_ext4_init(void) +{ + pfk_ext4_ready = true; + pr_info("PFK EXT4 inited successfully\n"); + + return 0; +} + +/** + * pfk_ecryptfs_is_ready() - driver is initialized and ready. + * + * Return: true if the driver is ready. + */ +static inline bool pfk_ext4_is_ready(void) +{ + return pfk_ext4_ready; +} + +/** + * pfk_ext4_dump_inode() - dumps all interesting info about inode to the screen + * + * + */ +/* + * static void pfk_ext4_dump_inode(const struct inode* inode) + * { + * struct ext4_crypt_info *ci = ext4_encryption_info((struct inode*)inode); + * + * pr_debug("dumping inode with address 0x%p\n", inode); + * pr_debug("S_ISREG is %d\n", S_ISREG(inode->i_mode)); + * pr_debug("EXT4_INODE_ENCRYPT flag is %d\n", + * ext4_test_inode_flag((struct inode*)inode, EXT4_INODE_ENCRYPT)); + * if (ci) { + * pr_debug("crypt_info address 0x%p\n", ci); + * pr_debug("ci->ci_data_mode %d\n", ci->ci_data_mode); + * } else { + * pr_debug("crypt_info is NULL\n"); + * } + * } +*/ + +/** + * pfk_is_ext4_type() - return true if inode belongs to ICE EXT4 PFE + * @inode: inode pointer + */ +bool pfk_is_ext4_type(const struct inode *inode) +{ + if (!pfe_is_inode_filesystem_type(inode, "ext4")) + return false; + + return ext4_should_be_processed_by_ice(inode); +} + +/** + * pfk_ext4_parse_cipher() - parse cipher from inode to enum + * @inode: inode + * @algo: pointer to store the output enum (can be null) + * + * return 0 in case of success, error otherwise (i.e not supported cipher) + */ +static int pfk_ext4_parse_cipher(const struct inode *inode, + enum ice_cryto_algo_mode *algo) +{ + /* + * currently only AES XTS algo is supported + * in the future, table with supported ciphers might + * be introduced + */ + + if (!inode) + return -EINVAL; + + if (!ext4_is_aes_xts_cipher(inode)) { + pr_err("ext4 alghoritm is not supported by pfk\n"); + return -EINVAL; + } + + if (algo) + *algo = ICE_CRYPTO_ALGO_MODE_AES_XTS; + + return 0; +} + + +int pfk_ext4_parse_inode(const struct bio *bio, + const struct inode *inode, + struct pfk_key_info *key_info, + enum ice_cryto_algo_mode *algo, + bool *is_pfe) +{ + int ret = 0; + + if (!is_pfe) + return -EINVAL; + + /* + * only a few errors below can indicate that + * this function was not invoked within PFE context, + * otherwise we will consider it PFE + */ + *is_pfe = true; + + if (!pfk_ext4_is_ready()) + return -ENODEV; + + if (!inode) + return -EINVAL; + + if (!key_info) + return -EINVAL; + + key_info->key = ext4_get_ice_encryption_key(inode); + if (!key_info->key) { + pr_err("could not parse key from ext4\n"); + return -EINVAL; + } + + key_info->key_size = ext4_get_ice_encryption_key_size(inode); + if (!key_info->key_size) { + pr_err("could not parse key size from ext4\n"); + return -EINVAL; + } + + key_info->salt = ext4_get_ice_encryption_salt(inode); + if (!key_info->salt) { + pr_err("could not parse salt from ext4\n"); + return -EINVAL; + } + + key_info->salt_size = ext4_get_ice_encryption_salt_size(inode); + if (!key_info->salt_size) { + pr_err("could not parse salt size from ext4\n"); + return -EINVAL; + } + + ret = pfk_ext4_parse_cipher(inode, algo); + if (ret != 0) { + pr_err("not supported cipher\n"); + return ret; + } + + return 0; +} + +bool pfk_ext4_allow_merge_bio(const struct bio *bio1, + const struct bio *bio2, const struct inode *inode1, + const struct inode *inode2) +{ + /* if there is no ext4 pfk, don't disallow merging blocks */ + if (!pfk_ext4_is_ready()) + return true; + + if (!inode1 || !inode2) + return false; + + return ext4_is_ice_encryption_info_equal(inode1, inode2); +} + diff --git a/security/pfe/pfk_ext4.h b/security/pfe/pfk_ext4.h new file mode 100644 index 000000000000..6b367b428e50 --- /dev/null +++ b/security/pfe/pfk_ext4.h @@ -0,0 +1,37 @@ +/* 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. + */ + +#ifndef _PFK_EXT4_H_ +#define _PFK_EXT4_H_ + +#include +#include +#include +#include "pfk_internal.h" + +bool pfk_is_ext4_type(const struct inode *inode); + +int pfk_ext4_parse_inode(const struct bio *bio, + const struct inode *inode, + struct pfk_key_info *key_info, + enum ice_cryto_algo_mode *algo, + bool *is_pfe); + +bool pfk_ext4_allow_merge_bio(const struct bio *bio1, + const struct bio *bio2, const struct inode *inode1, + const struct inode *inode2); + +int __init pfk_ext4_init(void); + +void __exit pfk_ext4_deinit(void); + +#endif /* _PFK_EXT4_H_ */ diff --git a/security/pfe/pfk_ice.h b/security/pfe/pfk_ice.h index 1d6339a575be..fe3f2ad3950c 100644 --- a/security/pfe/pfk_ice.h +++ b/security/pfe/pfk_ice.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 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 @@ -22,7 +22,6 @@ #include - int pfk_ice_init(void); int pfk_ice_deinit(void); diff --git a/security/pfe/pfk_internal.h b/security/pfe/pfk_internal.h new file mode 100644 index 000000000000..abdd0b325b39 --- /dev/null +++ b/security/pfe/pfk_internal.h @@ -0,0 +1,34 @@ +/* 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. + */ + +#ifndef _PFK_INTERNAL_H_ +#define _PFK_INTERNAL_H_ + +#include +#include + +struct pfk_key_info { + const unsigned char *key; + const unsigned char *salt; + size_t key_size; + size_t salt_size; +}; + +int pfk_key_size_to_key_type(size_t key_size, + enum ice_crpto_key_size *key_size_type); + +bool pfe_is_inode_filesystem_type(const struct inode *inode, + const char *fs_type); + +char *inode_to_filename(const struct inode *inode); + +#endif /* _PFK_INTERNAL_H_ */ diff --git a/security/pfe/pft.c b/security/pfe/pft.c index e4bbba7b6295..74433c4fe0ff 100644 --- a/security/pfe/pft.c +++ b/security/pfe/pft.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. + * Copyright (c) 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 @@ -746,7 +746,7 @@ EXPORT_SYMBOL(pft_get_key_index); * Return: true if the BIOs allowed to be merged, false * otherwise. */ -bool pft_allow_merge_bio(struct bio *bio1, struct bio *bio2) +bool pft_allow_merge_bio(const struct bio *bio1, const struct bio *bio2) { u32 key_index1 = 0, key_index2 = 0; bool is_encrypted1 = false, is_encrypted2 = false; diff --git a/security/security.c b/security/security.c index 81a555c14a35..7d1de1f6299f 100644 --- a/security/security.c +++ b/security/security.c @@ -857,11 +857,6 @@ int security_file_close(struct file *file) return call_int_hook(file_close, 0, file); } -bool security_allow_merge_bio(struct bio *bio1, struct bio *bio2) -{ - return call_int_hook(allow_merge_bio, 1, bio1, bio2); -} - int security_task_create(unsigned long clone_flags) { return call_int_hook(task_create, 0, clone_flags); @@ -1693,7 +1688,6 @@ struct security_hook_heads security_hook_heads = { .file_receive = LIST_HEAD_INIT(security_hook_heads.file_receive), .file_open = LIST_HEAD_INIT(security_hook_heads.file_open), .file_close = LIST_HEAD_INIT(security_hook_heads.file_close), - .allow_merge_bio = LIST_HEAD_INIT(security_hook_heads.allow_merge_bio), .task_create = LIST_HEAD_INIT(security_hook_heads.task_create), .task_free = LIST_HEAD_INIT(security_hook_heads.task_free), .cred_alloc_blank = diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 33d1262704ec..89fea87feafb 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3585,12 +3585,6 @@ static int selinux_file_close(struct file *file) return pft_file_close(file); } -static bool selinux_allow_merge_bio(struct bio *bio1, struct bio *bio2) -{ - return pft_allow_merge_bio(bio1, bio2) && - pfk_allow_merge_bio(bio1, bio2); -} - /* task security operations */ static int selinux_task_create(unsigned long clone_flags) @@ -6000,7 +5994,6 @@ static struct security_hook_list selinux_hooks[] = { LSM_HOOK_INIT(file_open, selinux_file_open), LSM_HOOK_INIT(file_close, selinux_file_close), - LSM_HOOK_INIT(allow_merge_bio, selinux_allow_merge_bio), LSM_HOOK_INIT(task_create, selinux_task_create), LSM_HOOK_INIT(cred_alloc_blank, selinux_cred_alloc_blank),