diff options
| author | Andrey Markovytch <andreym@codeaurora.org> | 2016-09-13 18:45:47 +0300 |
|---|---|---|
| committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2016-09-15 00:36:43 -0700 |
| commit | a8059e6d39ecfab6c90abc8a7502652907cdf258 (patch) | |
| tree | 11d54be9862c3e7c0941e53f72a6b9a876319393 /fs/ext4 | |
| parent | 9b82a4c5896632b62140d1ddda55638fea254007 (diff) | |
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 <andreym@codeaurora.org>
Diffstat (limited to 'fs/ext4')
| -rw-r--r-- | fs/ext4/Kconfig | 10 | ||||
| -rw-r--r-- | fs/ext4/Makefile | 2 | ||||
| -rw-r--r-- | fs/ext4/crypto.c | 3 | ||||
| -rw-r--r-- | fs/ext4/crypto_key.c | 72 | ||||
| -rw-r--r-- | fs/ext4/ext4.h | 23 | ||||
| -rw-r--r-- | fs/ext4/ext4_crypto.h | 6 | ||||
| -rw-r--r-- | fs/ext4/ext4_ice.c | 109 | ||||
| -rw-r--r-- | fs/ext4/ext4_ice.h | 104 | ||||
| -rw-r--r-- | fs/ext4/inode.c | 7 | ||||
| -rw-r--r-- | fs/ext4/page-io.c | 7 | ||||
| -rw-r--r-- | fs/ext4/readpage.c | 17 |
11 files changed, 318 insertions, 42 deletions
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 <uapi/linux/keyctl.h> #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 <linux/fs.h> +#include <linux/pfk.h> #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 <trace/events/ext4.h> @@ -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 <linux/cleancache.h> #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; } |
