From a813e81870096bcfecbe12aeeed8e1b0114cd474 Mon Sep 17 00:00:00 2001 From: Sebastien Buisson Date: Wed, 25 Mar 2020 16:43:33 +0000 Subject: [PATCH] LU-12275 sec: add llcrypt as file encryption library Include fscrypt sources from Linux 5.4 into libcfs kernel module. All fscrypt_ prefixes have been replaced with llcrypt_ to avoid collision. For the exact transformations carried out, please see script contrib/scripts/fscrypt_inclusion.sh, and patches under contrib/scripts/crypto_patches/. This llcrypt library will be built if kernel provides minimum encryption support, ie IS_ENCRYPTED and S_ENCRYPTED. If kernel provides support for encryption policies v2 (included in Linux 5.4), then llcrypt library will not be built, and in-kernel fscrypt will be used instead. To be independent as much as possible from the kernel, llcrypt relies on a new 'lsi_cop' field added to struct lustre_sb_info, to point to struct llcrypt_operations, and makes use of the new 'lsi_master_keys' field to store file system keys. llcrypt also uses i_private field on struct inode to store file-specific enryption info. Test-Parameters: trivial Test-Parameters: clientdistro=ubuntu1804 testgroup=review-ldiskfs Test-Parameters: clientdistro=el8.1 testgroup=review-ldiskfs Signed-off-by: Sebastien Buisson Change-Id: Ide2431b0c3381214aed2e54dd3084132e9545ca8 Reviewed-on: https://review.whamcloud.com/38127 Tested-by: jenkins Reviewed-by: Andreas Dilger Tested-by: Maloo Reviewed-by: Yang Sheng Reviewed-by: Oleg Drokin --- MAINTAINERS | 7 + autoMakefile.am | 5 + config/lustre-build-ldiskfs.m4 | 23 - config/lustre-build.m4 | 8 + .../0001_llcrypt_private_include.patch | 11 + .../0002_dcache_encrypted_name.patch | 16 + .../crypto_patches/0003_keyring_search_4args.patch | 16 + .../scripts/crypto_patches/0004_master_keys.patch | 157 +++ .../0005_crypto_tfm_req_forbid_weak_keys.patch | 16 + .../crypto_patches/0006_bvec_iter_all.patch | 17 + .../scripts/crypto_patches/0007_crypto_init.patch | 28 + .../crypto_patches/0008_key_desc_prefix.patch | 24 + contrib/scripts/crypto_patches/0009_lsi_cop.patch | 198 ++++ .../scripts/crypto_patches/0010_llcrypt_info.patch | 157 +++ .../crypto_patches/0011_llcrypt_cleanup.patch | 69 ++ contrib/scripts/crypto_patches/0012_igrab.patch | 19 + .../crypto_patches/0013_dentry_path_raw.patch | 13 + contrib/scripts/crypto_patches/0014_cdebug.patch | 45 + .../crypto_patches/0015_llcrypt_include.patch | 34 + contrib/scripts/crypto_patches/series | 15 + contrib/scripts/fscrypt_inclusion.sh | 46 + debian/rules | 10 + libcfs/autoconf/lustre-libcfs.m4 | 4 + libcfs/include/Makefile.am | 2 +- libcfs/include/libcfs/Makefile.am | 2 + libcfs/include/libcfs/crypto/.gitignore | 1 + libcfs/include/libcfs/crypto/Makefile.am | 1 + libcfs/include/libcfs/crypto/llcrypt.h | 796 +++++++++++++++ libcfs/include/uapi/.gitignore | 1 + libcfs/include/uapi/Makefile.am | 1 + libcfs/include/uapi/linux/.gitignore | 1 + libcfs/include/uapi/linux/Makefile.am | 1 + libcfs/include/uapi/linux/llcrypt.h | 185 ++++ libcfs/libcfs/Makefile.in | 5 + libcfs/libcfs/autoMakefile.am | 4 +- libcfs/libcfs/crypto/.gitignore | 1 + libcfs/libcfs/crypto/ChangeLog | 2 + libcfs/libcfs/crypto/Makefile.am | 4 + libcfs/libcfs/crypto/bio.c | 128 +++ libcfs/libcfs/crypto/crypto.c | 542 +++++++++++ libcfs/libcfs/crypto/fname.c | 410 ++++++++ libcfs/libcfs/crypto/hkdf.c | 185 ++++ libcfs/libcfs/crypto/hooks.c | 311 ++++++ libcfs/libcfs/crypto/keyring.c | 1011 ++++++++++++++++++++ libcfs/libcfs/crypto/keysetup.c | 608 ++++++++++++ libcfs/libcfs/crypto/keysetup_v1.c | 350 +++++++ libcfs/libcfs/crypto/llcrypt_private.h | 496 ++++++++++ libcfs/libcfs/crypto/policy.c | 511 ++++++++++ libcfs/libcfs/module.c | 9 + lustre/autoconf/lustre-core.m4 | 112 +++ lustre/include/lustre_disk.h | 4 + lustre/obdclass/obd_mount.c | 2 + 52 files changed, 6599 insertions(+), 25 deletions(-) create mode 100644 contrib/scripts/crypto_patches/0001_llcrypt_private_include.patch create mode 100644 contrib/scripts/crypto_patches/0002_dcache_encrypted_name.patch create mode 100644 contrib/scripts/crypto_patches/0003_keyring_search_4args.patch create mode 100644 contrib/scripts/crypto_patches/0004_master_keys.patch create mode 100644 contrib/scripts/crypto_patches/0005_crypto_tfm_req_forbid_weak_keys.patch create mode 100644 contrib/scripts/crypto_patches/0006_bvec_iter_all.patch create mode 100644 contrib/scripts/crypto_patches/0007_crypto_init.patch create mode 100644 contrib/scripts/crypto_patches/0008_key_desc_prefix.patch create mode 100644 contrib/scripts/crypto_patches/0009_lsi_cop.patch create mode 100644 contrib/scripts/crypto_patches/0010_llcrypt_info.patch create mode 100644 contrib/scripts/crypto_patches/0011_llcrypt_cleanup.patch create mode 100644 contrib/scripts/crypto_patches/0012_igrab.patch create mode 100644 contrib/scripts/crypto_patches/0013_dentry_path_raw.patch create mode 100644 contrib/scripts/crypto_patches/0014_cdebug.patch create mode 100644 contrib/scripts/crypto_patches/0015_llcrypt_include.patch create mode 100644 contrib/scripts/crypto_patches/series create mode 100755 contrib/scripts/fscrypt_inclusion.sh create mode 100644 libcfs/include/libcfs/crypto/.gitignore create mode 100644 libcfs/include/libcfs/crypto/Makefile.am create mode 100644 libcfs/include/libcfs/crypto/llcrypt.h create mode 100644 libcfs/include/uapi/.gitignore create mode 100644 libcfs/include/uapi/Makefile.am create mode 100644 libcfs/include/uapi/linux/.gitignore create mode 100644 libcfs/include/uapi/linux/Makefile.am create mode 100644 libcfs/include/uapi/linux/llcrypt.h create mode 100644 libcfs/libcfs/crypto/.gitignore create mode 100644 libcfs/libcfs/crypto/ChangeLog create mode 100644 libcfs/libcfs/crypto/Makefile.am create mode 100644 libcfs/libcfs/crypto/bio.c create mode 100644 libcfs/libcfs/crypto/crypto.c create mode 100644 libcfs/libcfs/crypto/fname.c create mode 100644 libcfs/libcfs/crypto/hkdf.c create mode 100644 libcfs/libcfs/crypto/hooks.c create mode 100644 libcfs/libcfs/crypto/keyring.c create mode 100644 libcfs/libcfs/crypto/keysetup.c create mode 100644 libcfs/libcfs/crypto/keysetup_v1.c create mode 100644 libcfs/libcfs/crypto/llcrypt_private.h create mode 100644 libcfs/libcfs/crypto/policy.c diff --git a/MAINTAINERS b/MAINTAINERS index e0eac46..a268e98 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -129,6 +129,13 @@ S: Supported F: lustre/include/lustre_mdc.h F: lustre/mdc/ +Lustre client side encryption +M: Sebastien Buisson +S: Maintained +F: libcfs/libcfs/crypto/*.[ch] +F: libcfs/include/libcfs/crypto/*.h +F: libcfs/include/uapi/linux/llcrypt.h + Lustre Client VFS Interface R: Oleg Drokin M: Lai Siyao diff --git a/autoMakefile.am b/autoMakefile.am index 51c27d0..7a1e355 100644 --- a/autoMakefile.am +++ b/autoMakefile.am @@ -263,6 +263,11 @@ debs: undef.h elif test "x@ENABLE_GSS@" = "xno"; then \ export DEB_BUILD_PROFILES="$${DEB_BUILD_PROFILES} nogss"; \ fi; \ + if test "x@ENABLE_CRYPTO@" = "xyes"; then \ + export DEB_BUILD_PROFILES="$${DEB_BUILD_PROFILES} crypto"; \ + elif test "x@ENABLE_CRYPTO@" = "xno"; then \ + export DEB_BUILD_PROFILES="$${DEB_BUILD_PROFILES} nocrypto"; \ + fi; \ if test "x@systemdsystemunitdir@" != "x"; then \ export DEB_BUILD_PROFILES="$${DEB_BUILD_PROFILES} systemd"; \ fi; \ diff --git a/config/lustre-build-ldiskfs.m4 b/config/lustre-build-ldiskfs.m4 index 2ddb1e9..4b52570 100644 --- a/config/lustre-build-ldiskfs.m4 +++ b/config/lustre-build-ldiskfs.m4 @@ -275,28 +275,6 @@ AC_DEFUN([LDISKFS_AC_PATCH_PROGRAM], [ ]) # LDISKFS_AC_PATCH_PROGRAM # -# LB_HAVE_BVEC_ITER_ALL -# -# kernel 5.1 commit 6dc4f100c175dd0511ae8674786e7c9006cdfbfa -# block: allow bio_for_each_segment_all() to iterate over multi-page bvec -# -AC_DEFUN([LB_HAVE_BVEC_ITER_ALL], [ -tmp_flags="$EXTRA_KCFLAGS" -EXTRA_KCFLAGS="-Werror" -LB_CHECK_COMPILE([if bvec_iter_all exists for multi-page bvec iternation], -ext4fs_dirhash, [ - #include -],[ - struct bvec_iter_all iter; - (void)iter; -],[ - AC_DEFINE(HAVE_BVEC_ITER_ALL, 1, - [if bvec_iter_all exists for multi-page bvec iternation]) -]) -EXTRA_KCFLAGS="$tmp_flags" -]) # LB_HAVE_BVEC_ITER_ALL - -# # LB_LDISKFS_FIND_ENTRY_LOCKED_EXISTS # # kernel 5.2 commit 8a363970d1dc38c4ec4ad575c862f776f468d057 @@ -411,7 +389,6 @@ AS_IF([test x$enable_ldiskfs != xno],[ LB_EXT4_HAVE_INFO_DQUOT LB_EXT4_HAVE_I_CRYPT_INFO LB_LDISKFS_IGET_HAS_FLAGS_ARG - LB_HAVE_BVEC_ITER_ALL LB_LDISKFS_FIND_ENTRY_LOCKED_EXISTS LB_LDISKFSFS_DIRHASH_WANTS_DIR AC_DEFINE(CONFIG_LDISKFS_FS_POSIX_ACL, 1, [posix acls for ldiskfs]) diff --git a/config/lustre-build.m4 b/config/lustre-build.m4 index ed23616..947b632 100644 --- a/config/lustre-build.m4 +++ b/config/lustre-build.m4 @@ -561,6 +561,13 @@ elif test x$enable_gss = xno ; then RPMBINARGS="$RPMBINARGS --without gss" AC_SUBST(ENABLE_GSS, no) fi +if test x$enable_crypto = xyes ; then + RPMBINARGS="$RPMBINARGS --with crypto" + AC_SUBST(ENABLE_CRYPTO, yes) +elif test x$enable_crypto = xno ; then + RPMBINARGS="$RPMBINARGS --without crypto" + AC_SUBST(ENABLE_CRYPTO, no) +fi if test x$enable_iokit != xyes ; then RPMBINARGS="$RPMBINARGS --without lustre_iokit" fi @@ -618,6 +625,7 @@ LB_CONFIG_TESTS LC_CONFIG_CLIENT LB_CONFIG_MPITESTS LB_CONFIG_SERVERS +LC_CONFIG_CRYPTO # Tests depends from utils (multiop from liblustreapi) AS_IF([test "x$enable_utils" = xno], [enable_tests="no"]) diff --git a/contrib/scripts/crypto_patches/0001_llcrypt_private_include.patch b/contrib/scripts/crypto_patches/0001_llcrypt_private_include.patch new file mode 100644 index 0000000..8ad3f18 --- /dev/null +++ b/contrib/scripts/crypto_patches/0001_llcrypt_private_include.patch @@ -0,0 +1,11 @@ +--- a/libcfs/libcfs/crypto/llcrypt_private.h ++++ b/libcfs/libcfs/crypto/llcrypt_private.h +@@ -11,7 +11,7 @@ + #ifndef _LLCRYPT_PRIVATE_H + #define _LLCRYPT_PRIVATE_H + +-#include ++#include + #include + + #define CONST_STRLEN(str) (sizeof(str) - 1) diff --git a/contrib/scripts/crypto_patches/0002_dcache_encrypted_name.patch b/contrib/scripts/crypto_patches/0002_dcache_encrypted_name.patch new file mode 100644 index 0000000..bb62166 --- /dev/null +++ b/contrib/scripts/crypto_patches/0002_dcache_encrypted_name.patch @@ -0,0 +1,16 @@ +Linux 5.1 (commit 6cc248684d3d) renames DCACHE_ENCRYPTED_WITH_KEY to +DCACHE_ENCRYPTED_NAME. + +--- a/libcfs/include/libcfs/crypto/llcrypt.h ++++ b/libcfs/include/libcfs/crypto/llcrypt.h +@@ -13,6 +13,10 @@ + #ifndef _LINUX_LLCRYPT_H + #define _LINUX_LLCRYPT_H + ++#ifndef DCACHE_ENCRYPTED_NAME ++#define DCACHE_ENCRYPTED_NAME 0x02000000 ++#endif ++ + #include + #include + #include diff --git a/contrib/scripts/crypto_patches/0003_keyring_search_4args.patch b/contrib/scripts/crypto_patches/0003_keyring_search_4args.patch new file mode 100644 index 0000000..2f85b0b --- /dev/null +++ b/contrib/scripts/crypto_patches/0003_keyring_search_4args.patch @@ -0,0 +1,16 @@ +Linux 5.2 commit dcf49dbc8077) adds a 'recurse' flag for keyring searches. + +--- a/libcfs/libcfs/crypto/keyring.c ++++ b/libcfs/libcfs/crypto/keyring.c +@@ -138,7 +138,11 @@ static struct key *search_llcrypt_keyrin + */ + key_ref_t keyref = make_key_ref(keyring, true /* possessed */); + ++#ifdef HAVE_KEYRING_SEARCH_4ARGS + keyref = keyring_search(keyref, type, description, false); ++#else ++ keyref = keyring_search(keyref, type, description); ++#endif + if (IS_ERR(keyref)) { + if (PTR_ERR(keyref) == -EAGAIN || /* not found */ + PTR_ERR(keyref) == -EKEYREVOKED) /* recently invalidated */ diff --git a/contrib/scripts/crypto_patches/0004_master_keys.patch b/contrib/scripts/crypto_patches/0004_master_keys.patch new file mode 100644 index 0000000..c713875 --- /dev/null +++ b/contrib/scripts/crypto_patches/0004_master_keys.patch @@ -0,0 +1,157 @@ +Linux 5.3 (commit 22d94f493bfb) introduces s_master_keys to struct super_block. +As we need encryption support for older kernels, replace this with +new lsi_master_keys field in struct lustre_sb_info. + +--- a/libcfs/libcfs/crypto/keyring.c ++++ b/libcfs/libcfs/crypto/keyring.c +@@ -82,7 +82,7 @@ static void llcrypt_key_describe(const s + } + + /* +- * Type of key in ->s_master_keys. Each key of this type represents a master ++ * Type of key in ->lsi_master_keys. Each key of this type represents a master + * key which has been added to the filesystem. Its payload is a + * 'struct llcrypt_master_key'. The "." prefix in the key type name prevents + * users from adding keys of this type via the keyrings syscalls rather than via +@@ -127,7 +127,7 @@ static struct key_type key_type_llcrypt_ + .describe = llcrypt_user_key_describe, + }; + +-/* Search ->s_master_keys or ->mk_users */ ++/* Search ->lsi_master_keys or ->mk_users */ + static struct key *search_llcrypt_keyring(struct key *keyring, + struct key_type *type, + const char *description) +@@ -196,13 +196,17 @@ static void format_mk_user_description( + mk_identifier, __kuid_val(current_fsuid())); + } + +-/* Create ->s_master_keys if needed. Synchronized by llcrypt_add_key_mutex. */ ++/* Create ->lsi_master_keys if needed. Synchronized by llcrypt_add_key_mutex. */ + static int allocate_filesystem_keyring(struct super_block *sb) + { + char description[LLCRYPT_FS_KEYRING_DESCRIPTION_SIZE]; + struct key *keyring; ++ struct lustre_sb_info *lsi = s2lsi(sb); + +- if (sb->s_master_keys) ++ if (!lsi) ++ return -EINVAL; ++ ++ if (lsi->lsi_master_keys) + return 0; + + format_fs_keyring_description(description, sb); +@@ -214,18 +218,22 @@ static int allocate_filesystem_keyring(s + return PTR_ERR(keyring); + + /* Pairs with READ_ONCE() in llcrypt_find_master_key() */ +- smp_store_release(&sb->s_master_keys, keyring); ++ smp_store_release(&lsi->lsi_master_keys, keyring); + return 0; + } + + void llcrypt_sb_free(struct super_block *sb) + { +- key_put(sb->s_master_keys); +- sb->s_master_keys = NULL; ++ struct lustre_sb_info *lsi = s2lsi(sb); ++ ++ if (lsi != NULL) { ++ key_put(lsi->lsi_master_keys); ++ lsi->lsi_master_keys = NULL; ++ } + } + + /* +- * Find the specified master key in ->s_master_keys. ++ * Find the specified master key in ->lsi_master_keys. + * Returns ERR_PTR(-ENOKEY) if not found. + */ + struct key *llcrypt_find_master_key(struct super_block *sb, +@@ -233,9 +241,13 @@ struct key *llcrypt_find_master_key(stru + { + struct key *keyring; + char description[LLCRYPT_MK_DESCRIPTION_SIZE]; ++ struct lustre_sb_info *lsi = s2lsi(sb); ++ ++ if (!lsi) ++ return ERR_PTR(-EINVAL); + + /* pairs with smp_store_release() in allocate_filesystem_keyring() */ +- keyring = READ_ONCE(sb->s_master_keys); ++ keyring = READ_ONCE(lsi->lsi_master_keys); + if (keyring == NULL) + return ERR_PTR(-ENOKEY); /* No keyring yet, so no keys yet. */ + +@@ -432,8 +444,12 @@ static int add_master_key(struct super_b + { + static DEFINE_MUTEX(llcrypt_add_key_mutex); + struct key *key; ++ struct lustre_sb_info *lsi = s2lsi(sb); + int err; + ++ if (!lsi) ++ return -EINVAL; ++ + mutex_lock(&llcrypt_add_key_mutex); /* serialize find + link */ + retry: + key = llcrypt_find_master_key(sb, mk_spec); +@@ -441,14 +457,15 @@ retry: + err = PTR_ERR(key); + if (err != -ENOKEY) + goto out_unlock; +- /* Didn't find the key in ->s_master_keys. Add it. */ ++ /* Didn't find the key in ->lsi_master_keys. Add it. */ + err = allocate_filesystem_keyring(sb); + if (err) + goto out_unlock; +- err = add_new_master_key(secret, mk_spec, sb->s_master_keys); ++ err = add_new_master_key(secret, mk_spec, ++ lsi->lsi_master_keys); + } else { + /* +- * Found the key in ->s_master_keys. Re-add the secret if ++ * Found the key in ->lsi_master_keys. Re-add the secret if + * needed, and add the user to ->mk_users if needed. + */ + down_write(&key->sem); +--- a/libcfs/libcfs/crypto/keysetup.c ++++ b/libcfs/libcfs/crypto/keysetup.c +@@ -326,7 +326,7 @@ static int setup_file_encryption_key(str + /* + * As a legacy fallback for v1 policies, search for the key in + * the current task's subscribed keyrings too. Don't move this +- * to before the search of ->s_master_keys, since users ++ * to before the search of ->lsi_master_keys, since users + * shouldn't be able to override filesystem-level keys. + */ + return llcrypt_setup_v1_file_key_via_subscribed_keyrings(ci); +@@ -406,7 +406,7 @@ static void put_crypt_info(struct llcryp + * + * In addition, if we're removing the last inode from a key that + * already had its secret removed, invalidate the key so that it +- * gets removed from ->s_master_keys. ++ * gets removed from ->lsi_master_keys. + */ + spin_lock(&mk->mk_decrypted_inodes_lock); + list_del(&ci->ci_master_key_link); +--- a/libcfs/libcfs/crypto/llcrypt_private.h ++++ b/libcfs/libcfs/crypto/llcrypt_private.h +@@ -13,6 +13,7 @@ + + #include + #include ++#include + + #define CONST_STRLEN(str) (sizeof(str) - 1) + +@@ -372,7 +373,7 @@ struct llcrypt_master_key { + + /* + * Length of ->mk_decrypted_inodes, plus one if mk_secret is present. +- * Once this goes to 0, the master key is removed from ->s_master_keys. ++ * Once this goes to 0, the master key is removed from ->lsi_master_keys. + * The 'struct llcrypt_master_key' will continue to live as long as the + * 'struct key' whose payload it is, but we won't let this reference + * count rise again. diff --git a/contrib/scripts/crypto_patches/0005_crypto_tfm_req_forbid_weak_keys.patch b/contrib/scripts/crypto_patches/0005_crypto_tfm_req_forbid_weak_keys.patch new file mode 100644 index 0000000..ebcef57 --- /dev/null +++ b/contrib/scripts/crypto_patches/0005_crypto_tfm_req_forbid_weak_keys.patch @@ -0,0 +1,16 @@ +Linux 5.0 (commit 231baecdef7a) renames CRYPTO_TFM_REQ_WEAK_KEY to +CRYPTO_TFM_REQ_FORBID_WEAK_KEYS. + +--- a/libcfs/libcfs/crypto/llcrypt_private.h ++++ b/libcfs/libcfs/crypto/llcrypt_private.h +@@ -15,6 +15,10 @@ + #include + #include + ++#ifndef CRYPTO_TFM_REQ_FORBID_WEAK_KEYS ++#define CRYPTO_TFM_REQ_FORBID_WEAK_KEYS CRYPTO_TFM_REQ_WEAK_KEY ++#endif ++ + #define CONST_STRLEN(str) (sizeof(str) - 1) + + #define FS_KEY_DERIVATION_NONCE_SIZE 16 diff --git a/contrib/scripts/crypto_patches/0006_bvec_iter_all.patch b/contrib/scripts/crypto_patches/0006_bvec_iter_all.patch new file mode 100644 index 0000000..a6968c2 --- /dev/null +++ b/contrib/scripts/crypto_patches/0006_bvec_iter_all.patch @@ -0,0 +1,17 @@ +Linux 5.1 (commit 6dc4f100c175dd0511ae8674786e7c9006cdfbfa) introduces +bvec_iter_all. + +--- a/libcfs/libcfs/crypto/bio.c ++++ b/libcfs/libcfs/crypto/bio.c +@@ -29,7 +29,11 @@ + static void __llcrypt_decrypt_bio(struct bio *bio, bool done) + { + struct bio_vec *bv; ++#ifdef HAVE_BVEC_ITER_ALL + struct bvec_iter_all iter_all; ++#else ++ int iter_all; ++#endif + + bio_for_each_segment_all(bv, bio, iter_all) { + struct page *page = bv->bv_page; diff --git a/contrib/scripts/crypto_patches/0007_crypto_init.patch b/contrib/scripts/crypto_patches/0007_crypto_init.patch new file mode 100644 index 0000000..a5f95e1 --- /dev/null +++ b/contrib/scripts/crypto_patches/0007_crypto_init.patch @@ -0,0 +1,28 @@ +Make llcrypt_init visible outside of llcrypt. + +--- a/libcfs/include/libcfs/crypto/llcrypt.h ++++ b/libcfs/include/libcfs/crypto/llcrypt.h +@@ -105,6 +105,7 @@ static inline void llcrypt_handle_d_move + } + + /* crypto.c */ ++extern int __init llcrypt_init(void); + extern void llcrypt_enqueue_decrypt_work(struct work_struct *); + extern struct llcrypt_ctx *llcrypt_get_ctx(gfp_t); + extern void llcrypt_release_ctx(struct llcrypt_ctx *); +--- a/libcfs/libcfs/crypto/crypto.c ++++ b/libcfs/libcfs/crypto/crypto.c +@@ -476,7 +476,7 @@ void llcrypt_msg(const struct inode *ino + /** + * llcrypt_init() - Set up for fs encryption. + */ +-static int __init llcrypt_init(void) ++int __init llcrypt_init(void) + { + int err = -ENOMEM; + +@@ -517,4 +517,3 @@ fail_free_queue: + fail: + return err; + } +-late_initcall(llcrypt_init) diff --git a/contrib/scripts/crypto_patches/0008_key_desc_prefix.patch b/contrib/scripts/crypto_patches/0008_key_desc_prefix.patch new file mode 100644 index 0000000..eb256df --- /dev/null +++ b/contrib/scripts/crypto_patches/0008_key_desc_prefix.patch @@ -0,0 +1,24 @@ +Keep 'fscrypt' as key desc prefix, for compatibility with third party tools. + +--- a/libcfs/include/uapi/linux/llcrypt.h ++++ b/libcfs/include/uapi/linux/llcrypt.h +@@ -48,7 +48,7 @@ struct llcrypt_policy_v1 { + * Process-subscribed "logon" key description prefix and payload format. + * Deprecated; prefer LL_IOC_ADD_ENCRYPTION_KEY instead. + */ +-#define LLCRYPT_KEY_DESC_PREFIX "llcrypt:" ++#define LLCRYPT_KEY_DESC_PREFIX "fscrypt:" + #define LLCRYPT_KEY_DESC_PREFIX_SIZE 8 + #define LLCRYPT_MAX_KEY_SIZE 64 + struct llcrypt_key { +--- a/libcfs/libcfs/crypto/hkdf.c ++++ b/libcfs/libcfs/crypto/hkdf.c +@@ -129,7 +129,7 @@ int llcrypt_hkdf_expand(struct llcrypt_h + + desc->tfm = hkdf->hmac_tfm; + +- memcpy(prefix, "llcrypt\0", 8); ++ memcpy(prefix, "fscrypt\0", 8); + prefix[8] = context; + + for (i = 0; i < okmlen; i += HKDF_HASHLEN) { diff --git a/contrib/scripts/crypto_patches/0009_lsi_cop.patch b/contrib/scripts/crypto_patches/0009_lsi_cop.patch new file mode 100644 index 0000000..2da28ff --- /dev/null +++ b/contrib/scripts/crypto_patches/0009_lsi_cop.patch @@ -0,0 +1,198 @@ +Replace (struct super_block *)->s_cop with (struct lustre_sb_info *)->lsi_cop. +This is needed so that we can use llcrypt on kernels that do not have +(struct super_block *)->s_cop. + +--- a/libcfs/include/libcfs/crypto/llcrypt.h ++++ b/libcfs/include/libcfs/crypto/llcrypt.h +@@ -20,6 +20,7 @@ + #include + #include + #include ++#include + #include + + #define LL_CRYPTO_BLOCK_SIZE 16 +@@ -88,8 +89,13 @@ static inline bool llcrypt_has_encryptio + + static inline bool llcrypt_dummy_context_enabled(struct inode *inode) + { +- return inode->i_sb->s_cop->dummy_context && +- inode->i_sb->s_cop->dummy_context(inode); ++ struct lustre_sb_info *lsi = s2lsi(inode->i_sb); ++ ++ if (unlikely(!lsi)) ++ return false; ++ ++ return lsi->lsi_cop->dummy_context && ++ lsi->lsi_cop->dummy_context(inode); + } + + /* +@@ -275,9 +281,12 @@ extern const char *llcrypt_get_symlink(s + unsigned int max_size, + struct delayed_call *done); + static inline void llcrypt_set_ops(struct super_block *sb, +- const struct llcrypt_operations *s_cop) ++ const struct llcrypt_operations *lsi_cop) + { +- sb->s_cop = s_cop; ++ struct lustre_sb_info *lsi = s2lsi(sb); ++ ++ if (lsi) ++ lsi->lsi_cop = lsi_cop; + } + #else /* !CONFIG_LL_ENCRYPTION */ + +@@ -557,7 +566,7 @@ static inline const char *llcrypt_get_sy + } + + static inline void llcrypt_set_ops(struct super_block *sb, +- const struct llcrypt_operations *s_cop) ++ const struct llcrypt_operations *lsi_cop) + { + } + +--- a/libcfs/libcfs/crypto/fname.c ++++ b/libcfs/libcfs/crypto/fname.c +@@ -333,8 +333,12 @@ int llcrypt_setup_filename(struct inode + return ret; + + if (llcrypt_has_encryption_key(dir)) { ++ struct lustre_sb_info *lsi = s2lsi(dir->i_sb); ++ + if (!llcrypt_fname_encrypted_size(dir, iname->len, +- dir->i_sb->s_cop->max_namelen, ++ lsi ? ++ lsi->lsi_cop->max_namelen : ++ NAME_MAX, + &fname->crypto_buf.len)) + return -ENAMETOOLONG; + fname->crypto_buf.name = kmalloc(fname->crypto_buf.len, +--- a/libcfs/libcfs/crypto/keysetup.c ++++ b/libcfs/libcfs/crypto/keysetup.c +@@ -424,16 +424,20 @@ int llcrypt_get_encryption_info(struct i + union llcrypt_context ctx; + struct llcrypt_mode *mode; + struct key *master_key = NULL; ++ struct lustre_sb_info *lsi = s2lsi(inode->i_sb); + int res; + + if (llcrypt_has_encryption_key(inode)) + return 0; + +- res = llcrypt_initialize(inode->i_sb->s_cop->flags); ++ if (!lsi) ++ return -ENOKEY; ++ ++ res = llcrypt_initialize(lsi->lsi_cop->flags); + if (res) + return res; + +- res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx)); ++ res = lsi->lsi_cop->get_context(inode, &ctx, sizeof(ctx)); + if (res < 0) { + if (!llcrypt_dummy_context_enabled(inode) || + IS_ENCRYPTED(inode)) { +--- a/libcfs/libcfs/crypto/keysetup_v1.c ++++ b/libcfs/libcfs/crypto/keysetup_v1.c +@@ -325,10 +325,16 @@ int llcrypt_setup_v1_file_key_via_subscr + key = find_and_lock_process_key(LLCRYPT_KEY_DESC_PREFIX, + ci->ci_policy.v1.master_key_descriptor, + ci->ci_mode->keysize, &payload); +- if (key == ERR_PTR(-ENOKEY) && ci->ci_inode->i_sb->s_cop->key_prefix) { +- key = find_and_lock_process_key(ci->ci_inode->i_sb->s_cop->key_prefix, +- ci->ci_policy.v1.master_key_descriptor, +- ci->ci_mode->keysize, &payload); ++ if (key == ERR_PTR(-ENOKEY)) { ++ struct lustre_sb_info *lsi = s2lsi(ci->ci_inode->i_sb); ++ ++ if (lsi && lsi->lsi_cop->key_prefix) { ++ key = ++ find_and_lock_process_key(lsi->lsi_cop->key_prefix, ++ ci->ci_policy.v1.master_key_descriptor, ++ ci->ci_mode->keysize, ++ &payload); ++ } + } + if (IS_ERR(key)) + return PTR_ERR(key); +--- a/libcfs/libcfs/crypto/policy.c ++++ b/libcfs/libcfs/crypto/policy.c +@@ -209,6 +209,7 @@ static int llcrypt_get_policy(struct ino + { + const struct llcrypt_info *ci; + union llcrypt_context ctx; ++ struct lustre_sb_info *lsi = s2lsi(inode->i_sb); + int ret; + + ci = READ_ONCE(inode->i_crypt_info); +@@ -221,7 +222,10 @@ static int llcrypt_get_policy(struct ino + if (!IS_ENCRYPTED(inode)) + return -ENODATA; + +- ret = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx)); ++ if (!lsi) ++ return -ENODATA; ++ ++ ret = lsi->lsi_cop->get_context(inode, &ctx, sizeof(ctx)); + if (ret < 0) + return (ret == -ERANGE) ? -EINVAL : ret; + +@@ -233,6 +237,7 @@ static int set_encryption_policy(struct + { + union llcrypt_context ctx; + int ctxsize; ++ struct lustre_sb_info *lsi = s2lsi(inode->i_sb); + int err; + + if (!llcrypt_supported_policy(policy, inode)) +@@ -267,7 +272,10 @@ static int set_encryption_policy(struct + + ctxsize = llcrypt_new_context_from_policy(&ctx, policy); + +- return inode->i_sb->s_cop->set_context(inode, &ctx, ctxsize, NULL); ++ if (!lsi) ++ return -EINVAL; ++ ++ return lsi->lsi_cop->set_context(inode, &ctx, ctxsize, NULL); + } + + int llcrypt_ioctl_set_policy(struct file *filp, const void __user *arg) +@@ -313,11 +321,13 @@ int llcrypt_ioctl_set_policy(struct file + + ret = llcrypt_get_policy(inode, &existing_policy); + if (ret == -ENODATA) { ++ struct lustre_sb_info *lsi = s2lsi(inode->i_sb); ++ + if (!S_ISDIR(inode->i_mode)) + ret = -ENOTDIR; + else if (IS_DEADDIR(inode)) + ret = -ENOENT; +- else if (!inode->i_sb->s_cop->empty_dir(inode)) ++ else if (lsi && !lsi->lsi_cop->empty_dir(inode)) + ret = -ENOTEMPTY; + else + ret = set_encryption_policy(inode, &policy); +@@ -472,6 +482,7 @@ int llcrypt_inherit_context(struct inode + union llcrypt_context ctx; + int ctxsize; + struct llcrypt_info *ci; ++ struct lustre_sb_info *lsi = s2lsi(parent->i_sb); + int res; + + res = llcrypt_get_encryption_info(parent); +@@ -482,10 +493,13 @@ int llcrypt_inherit_context(struct inode + if (ci == NULL) + return -ENOKEY; + ++ if (!lsi) ++ return -ENOKEY; ++ + ctxsize = llcrypt_new_context_from_policy(&ctx, &ci->ci_policy); + + BUILD_BUG_ON(sizeof(ctx) != LLCRYPT_SET_CONTEXT_MAX_SIZE); +- res = parent->i_sb->s_cop->set_context(child, &ctx, ctxsize, fs_data); ++ res = lsi->lsi_cop->set_context(child, &ctx, ctxsize, fs_data); + if (res) + return res; + return preload ? llcrypt_get_encryption_info(child): 0; diff --git a/contrib/scripts/crypto_patches/0010_llcrypt_info.patch b/contrib/scripts/crypto_patches/0010_llcrypt_info.patch new file mode 100644 index 0000000..30d3a7b --- /dev/null +++ b/contrib/scripts/crypto_patches/0010_llcrypt_info.patch @@ -0,0 +1,157 @@ +Replace use of inode->i_crypt_info with inode->i_private, cast as a struct llcrypt_info. +This is required in order to be able to support encryption on kernels that lack +inode->i_crypt_info field. + +--- a/libcfs/include/libcfs/crypto/llcrypt.h ++++ b/libcfs/include/libcfs/crypto/llcrypt.h +@@ -81,11 +81,7 @@ struct llcrypt_ctx { + u8 flags; /* Flags */ + }; + +-static inline bool llcrypt_has_encryption_key(const struct inode *inode) +-{ +- /* pairs with cmpxchg_release() in llcrypt_get_encryption_info() */ +- return READ_ONCE(inode->i_crypt_info) != NULL; +-} ++extern bool llcrypt_has_encryption_key(const struct inode *inode); + + static inline bool llcrypt_dummy_context_enabled(struct inode *inode) + { +--- a/libcfs/libcfs/crypto/crypto.c ++++ b/libcfs/libcfs/crypto/crypto.c +@@ -158,7 +158,7 @@ int llcrypt_crypt_block(const struct ino + struct skcipher_request *req = NULL; + DECLARE_CRYPTO_WAIT(wait); + struct scatterlist dst, src; +- struct llcrypt_info *ci = inode->i_crypt_info; ++ struct llcrypt_info *ci = llcrypt_info(inode); + struct crypto_skcipher *tfm = ci->ci_ctfm; + int res = 0; + +--- a/libcfs/libcfs/crypto/fname.c ++++ b/libcfs/libcfs/crypto/fname.c +@@ -39,7 +39,7 @@ int fname_encrypt(struct inode *inode, c + { + struct skcipher_request *req = NULL; + DECLARE_CRYPTO_WAIT(wait); +- struct llcrypt_info *ci = inode->i_crypt_info; ++ struct llcrypt_info *ci = llcrypt_info(inode); + struct crypto_skcipher *tfm = ci->ci_ctfm; + union llcrypt_iv iv; + struct scatterlist sg; +@@ -92,7 +92,7 @@ static int fname_decrypt(struct inode *i + struct skcipher_request *req = NULL; + DECLARE_CRYPTO_WAIT(wait); + struct scatterlist src_sg, dst_sg; +- struct llcrypt_info *ci = inode->i_crypt_info; ++ struct llcrypt_info *ci = llcrypt_info(inode); + struct crypto_skcipher *tfm = ci->ci_ctfm; + union llcrypt_iv iv; + int res; +@@ -181,7 +181,7 @@ static int base64_decode(const char *src + bool llcrypt_fname_encrypted_size(const struct inode *inode, u32 orig_len, + u32 max_len, u32 *encrypted_len_ret) + { +- const struct llcrypt_info *ci = inode->i_crypt_info; ++ const struct llcrypt_info *ci = llcrypt_info(inode); + int padding = 4 << (llcrypt_policy_flags(&ci->ci_policy) & + LLCRYPT_POLICY_FLAGS_PAD_MASK); + u32 encrypted_len; +--- a/libcfs/libcfs/crypto/keysetup.c ++++ b/libcfs/libcfs/crypto/keysetup.c +@@ -501,7 +501,8 @@ int llcrypt_get_encryption_info(struct i + if (res) + goto out; + +- if (cmpxchg_release(&inode->i_crypt_info, NULL, crypt_info) == NULL) { ++ if (cmpxchg_release(&(llcrypt_info_nocast(inode)), NULL, ++ crypt_info) == NULL) { + if (master_key) { + struct llcrypt_master_key *mk = + master_key->payload.data[0]; +@@ -538,8 +539,8 @@ EXPORT_SYMBOL(llcrypt_get_encryption_inf + */ + void llcrypt_put_encryption_info(struct inode *inode) + { +- put_crypt_info(inode->i_crypt_info); +- inode->i_crypt_info = NULL; ++ put_crypt_info(llcrypt_info(inode)); ++ llcrypt_info_nocast(inode) = NULL; + } + EXPORT_SYMBOL(llcrypt_put_encryption_info); + +@@ -569,9 +570,10 @@ EXPORT_SYMBOL(llcrypt_free_inode); + */ + int llcrypt_drop_inode(struct inode *inode) + { +- const struct llcrypt_info *ci = READ_ONCE(inode->i_crypt_info); ++ const struct llcrypt_info *ci; + const struct llcrypt_master_key *mk; + ++ ci = (struct llcrypt_info *)READ_ONCE(llcrypt_info_nocast(inode)); + /* + * If ci is NULL, then the inode doesn't have an encryption key set up + * so it's irrelevant. If ci_master_key is NULL, then the master key +@@ -593,3 +595,10 @@ int llcrypt_drop_inode(struct inode *ino + return !is_master_key_secret_present(&mk->mk_secret); + } + EXPORT_SYMBOL_GPL(llcrypt_drop_inode); ++ ++inline bool llcrypt_has_encryption_key(const struct inode *inode) ++{ ++ /* pairs with cmpxchg_release() in llcrypt_get_encryption_info() */ ++ return READ_ONCE(llcrypt_info_nocast(inode)) != NULL; ++} ++EXPORT_SYMBOL_GPL(llcrypt_has_encryption_key); +--- a/libcfs/libcfs/crypto/llcrypt_private.h ++++ b/libcfs/libcfs/crypto/llcrypt_private.h +@@ -19,6 +19,9 @@ + #define CRYPTO_TFM_REQ_FORBID_WEAK_KEYS CRYPTO_TFM_REQ_WEAK_KEY + #endif + ++#define llcrypt_info(inode) ((struct llcrypt_info *)(inode)->i_private) ++#define llcrypt_info_nocast(inode) ((inode)->i_private) ++ + #define CONST_STRLEN(str) (sizeof(str) - 1) + + #define FS_KEY_DERIVATION_NONCE_SIZE 16 +@@ -160,8 +163,8 @@ struct llcrypt_symlink_data { + * llcrypt_info - the "encryption key" for an inode + * + * When an encrypted file's key is made available, an instance of this struct is +- * allocated and stored in ->i_crypt_info. Once created, it remains until the +- * inode is evicted. ++ * allocated and stored in '(struct llcrypt_info *)inode->i_private'. ++ * Once created, it remains until the inode is evicted. + */ + struct llcrypt_info { + +--- a/libcfs/libcfs/crypto/policy.c ++++ b/libcfs/libcfs/crypto/policy.c +@@ -212,7 +212,7 @@ static int llcrypt_get_policy(struct ino + struct lustre_sb_info *lsi = s2lsi(inode->i_sb); + int ret; + +- ci = READ_ONCE(inode->i_crypt_info); ++ ci = (struct llcrypt_info *)READ_ONCE(llcrypt_info_nocast(inode)); + if (ci) { + /* key available, use the cached policy */ + *policy = ci->ci_policy; +@@ -472,7 +472,7 @@ EXPORT_SYMBOL(llcrypt_has_permitted_cont + * @parent: Parent inode from which the context is inherited. + * @child: Child inode that inherits the context from @parent. + * @fs_data: private data given by FS. +- * @preload: preload child i_crypt_info if true ++ * @preload: preload child crypt info if true + * + * Return: 0 on success, -errno on failure + */ +@@ -489,7 +489,7 @@ int llcrypt_inherit_context(struct inode + if (res < 0) + return res; + +- ci = READ_ONCE(parent->i_crypt_info); ++ ci = (struct llcrypt_info *)READ_ONCE(llcrypt_info_nocast(parent)); + if (ci == NULL) + return -ENOKEY; + diff --git a/contrib/scripts/crypto_patches/0011_llcrypt_cleanup.patch b/contrib/scripts/crypto_patches/0011_llcrypt_cleanup.patch new file mode 100644 index 0000000..c40595a --- /dev/null +++ b/contrib/scripts/crypto_patches/0011_llcrypt_cleanup.patch @@ -0,0 +1,69 @@ +Add __exit functions for llcrypt. +Initial fscrypt implementation lacks this because fscrypt is not a kernel module. + +--- a/libcfs/include/libcfs/crypto/llcrypt.h ++++ b/libcfs/include/libcfs/crypto/llcrypt.h +@@ -108,6 +108,7 @@ static inline void llcrypt_handle_d_move + + /* crypto.c */ + extern int __init llcrypt_init(void); ++extern void __exit llcrypt_exit(void); + extern void llcrypt_enqueue_decrypt_work(struct work_struct *); + extern struct llcrypt_ctx *llcrypt_get_ctx(gfp_t); + extern void llcrypt_release_ctx(struct llcrypt_ctx *); +--- a/libcfs/libcfs/crypto/crypto.c ++++ b/libcfs/libcfs/crypto/crypto.c +@@ -517,3 +517,22 @@ fail_free_queue: + fail: + return err; + } ++ ++/** ++ * llcrypt_exit() - Clean up for fs encryption. ++ */ ++void __exit llcrypt_exit(void) ++{ ++ llcrypt_exit_keyring(); ++ ++ llcrypt_destroy(); ++ /* ++ * Make sure all delayed rcu free inodes are flushed before we ++ * destroy cache. ++ */ ++ rcu_barrier(); ++ ++ kmem_cache_destroy(llcrypt_info_cachep); ++ kmem_cache_destroy(llcrypt_ctx_cachep); ++ destroy_workqueue(llcrypt_read_workqueue); ++} +--- a/libcfs/libcfs/crypto/keyring.c ++++ b/libcfs/libcfs/crypto/keyring.c +@@ -231,6 +231,7 @@ void llcrypt_sb_free(struct super_block + lsi->lsi_master_keys = NULL; + } + } ++EXPORT_SYMBOL(llcrypt_sb_free); + + /* + * Find the specified master key in ->lsi_master_keys. +@@ -1003,3 +1004,9 @@ err_unregister_llcrypt: + unregister_key_type(&key_type_llcrypt); + return err; + } ++ ++void __exit llcrypt_exit_keyring(void) ++{ ++ unregister_key_type(&key_type_llcrypt_user); ++ unregister_key_type(&key_type_llcrypt); ++} +--- a/libcfs/libcfs/crypto/llcrypt_private.h ++++ b/libcfs/libcfs/crypto/llcrypt_private.h +@@ -444,6 +444,8 @@ extern int llcrypt_verify_key_added(stru + + extern int __init llcrypt_init_keyring(void); + ++extern void __exit llcrypt_exit_keyring(void); ++ + /* keysetup.c */ + + struct llcrypt_mode { diff --git a/contrib/scripts/crypto_patches/0012_igrab.patch b/contrib/scripts/crypto_patches/0012_igrab.patch new file mode 100644 index 0000000..501abda --- /dev/null +++ b/contrib/scripts/crypto_patches/0012_igrab.patch @@ -0,0 +1,19 @@ +As __iget is not exported by the kernel, replace it with igrab. + +--- a/libcfs/libcfs/crypto/keyring.c ++++ b/libcfs/libcfs/crypto/keyring.c +@@ -660,13 +660,8 @@ static void evict_dentries_for_decrypted + + list_for_each_entry(ci, &mk->mk_decrypted_inodes, ci_master_key_link) { + inode = ci->ci_inode; +- spin_lock(&inode->i_lock); +- if (inode->i_state & (I_FREEING | I_WILL_FREE | I_NEW)) { +- spin_unlock(&inode->i_lock); ++ if (igrab(inode) == NULL) + continue; +- } +- __iget(inode); +- spin_unlock(&inode->i_lock); + spin_unlock(&mk->mk_decrypted_inodes_lock); + + shrink_dcache_inode(inode); diff --git a/contrib/scripts/crypto_patches/0013_dentry_path_raw.patch b/contrib/scripts/crypto_patches/0013_dentry_path_raw.patch new file mode 100644 index 0000000..b3e3d98 --- /dev/null +++ b/contrib/scripts/crypto_patches/0013_dentry_path_raw.patch @@ -0,0 +1,13 @@ +As dentry_path is not exported by the kernel, replace with dentry_path_raw. + +--- a/libcfs/libcfs/crypto/keyring.c ++++ b/libcfs/libcfs/crypto/keyring.c +@@ -707,7 +707,7 @@ static int check_for_busy_inodes(struct + spin_unlock(&mk->mk_decrypted_inodes_lock); + + if (dentry) { +- path = dentry_path(dentry, _path, sizeof(_path)); ++ path = dentry_path_raw(dentry, _path, sizeof(_path)); + dput(dentry); + } + if (IS_ERR_OR_NULL(path)) diff --git a/contrib/scripts/crypto_patches/0014_cdebug.patch b/contrib/scripts/crypto_patches/0014_cdebug.patch new file mode 100644 index 0000000..b3c9d9e --- /dev/null +++ b/contrib/scripts/crypto_patches/0014_cdebug.patch @@ -0,0 +1,45 @@ +Make llcrypt_msg use CDEBUG. + +--- a/libcfs/libcfs/crypto/crypto.c ++++ b/libcfs/libcfs/crypto/crypto.c +@@ -451,7 +451,7 @@ fail: + return res; + } + +-void llcrypt_msg(const struct inode *inode, const char *level, ++void llcrypt_msg(const struct inode *inode, int mask, + const char *fmt, ...) + { + static DEFINE_RATELIMIT_STATE(rs, DEFAULT_RATELIMIT_INTERVAL, +@@ -466,10 +466,10 @@ void llcrypt_msg(const struct inode *ino + vaf.fmt = fmt; + vaf.va = &args; + if (inode) +- printk("%sllcrypt (%s, inode %lu): %pV\n", +- level, inode->i_sb->s_id, inode->i_ino, &vaf); ++ CDEBUG(mask, "llcrypt (%s, inode %lu): %pV\n", ++ inode->i_sb->s_id, inode->i_ino, &vaf); + else +- printk("%sllcrypt: %pV\n", level, &vaf); ++ CDEBUG(mask, "llcrypt: %pV\n", &vaf); + va_end(args); + } + +--- a/libcfs/libcfs/crypto/llcrypt_private.h ++++ b/libcfs/libcfs/crypto/llcrypt_private.h +@@ -249,12 +249,12 @@ extern struct page *llcrypt_alloc_bounce + extern const struct dentry_operations llcrypt_d_ops; + + extern void __printf(3, 4) __cold +-llcrypt_msg(const struct inode *inode, const char *level, const char *fmt, ...); ++llcrypt_msg(const struct inode *inode, int mask, const char *fmt, ...); + + #define llcrypt_warn(inode, fmt, ...) \ +- llcrypt_msg((inode), KERN_WARNING, fmt, ##__VA_ARGS__) ++ llcrypt_msg((inode), D_SEC, fmt, ##__VA_ARGS__) + #define llcrypt_err(inode, fmt, ...) \ +- llcrypt_msg((inode), KERN_ERR, fmt, ##__VA_ARGS__) ++ llcrypt_msg((inode), D_ERROR, fmt, ##__VA_ARGS__) + + #define LLCRYPT_MAX_IV_SIZE 32 + diff --git a/contrib/scripts/crypto_patches/0015_llcrypt_include.patch b/contrib/scripts/crypto_patches/0015_llcrypt_include.patch new file mode 100644 index 0000000..2ec4552 --- /dev/null +++ b/contrib/scripts/crypto_patches/0015_llcrypt_include.patch @@ -0,0 +1,34 @@ +In case llcrypt is not built (CONFIG_LL_ENCRYPTION undefined), +we need to have stubs for llcrypt init/exit functions. + +--- a/libcfs/include/libcfs/crypto/llcrypt.h ++++ b/libcfs/include/libcfs/crypto/llcrypt.h +@@ -287,6 +287,13 @@ static inline void llcrypt_set_ops(struc + } + #else /* !CONFIG_LL_ENCRYPTION */ + ++struct llcrypt_operations; ++#define llcrypt_init() 0 ++#define llcrypt_exit() {} ++ ++#undef IS_ENCRYPTED ++#define IS_ENCRYPTED(x) 0 ++ + static inline bool llcrypt_has_encryption_key(const struct inode *inode) + { + return false; +@@ -554,13 +561,7 @@ static inline int __llcrypt_encrypt_syml + return -EOPNOTSUPP; + } + +-static inline const char *llcrypt_get_symlink(struct inode *inode, +- const void *caddr, +- unsigned int max_size, +- struct delayed_call *done) +-{ +- return ERR_PTR(-EOPNOTSUPP); +-} ++#define llcrypt_get_symlink(inode, caddr, max_size, done) ERR_PTR(-EOPNOTSUPP) + + static inline void llcrypt_set_ops(struct super_block *sb, + const struct llcrypt_operations *lsi_cop) diff --git a/contrib/scripts/crypto_patches/series b/contrib/scripts/crypto_patches/series new file mode 100644 index 0000000..e5b06d0 --- /dev/null +++ b/contrib/scripts/crypto_patches/series @@ -0,0 +1,15 @@ +contrib/scripts/crypto_patches/0001_llcrypt_private_include.patch +contrib/scripts/crypto_patches/0002_dcache_encrypted_name.patch +contrib/scripts/crypto_patches/0003_keyring_search_4args.patch +contrib/scripts/crypto_patches/0004_master_keys.patch +contrib/scripts/crypto_patches/0005_crypto_tfm_req_forbid_weak_keys.patch +contrib/scripts/crypto_patches/0006_bvec_iter_all.patch +contrib/scripts/crypto_patches/0007_crypto_init.patch +contrib/scripts/crypto_patches/0008_key_desc_prefix.patch +contrib/scripts/crypto_patches/0009_lsi_cop.patch +contrib/scripts/crypto_patches/0010_llcrypt_info.patch +contrib/scripts/crypto_patches/0011_llcrypt_cleanup.patch +contrib/scripts/crypto_patches/0012_igrab.patch +contrib/scripts/crypto_patches/0013_dentry_path_raw.patch +contrib/scripts/crypto_patches/0014_cdebug.patch +contrib/scripts/crypto_patches/0015_llcrypt_include.patch diff --git a/contrib/scripts/fscrypt_inclusion.sh b/contrib/scripts/fscrypt_inclusion.sh new file mode 100755 index 0000000..4f7ddf9 --- /dev/null +++ b/contrib/scripts/fscrypt_inclusion.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +# +# Purpose of this script is to show the method used to integrate fscrypt +# sources from the Linux kernel, and the transformations required. +# +# All C files from $LINUX/fs/crypto/ are put under libcfs/libcfs/crypto/. +# File $LINUX/include/linux/fscrypt.h is put in libcfs/include/libcfs/crypto/. +# File $LINUX/include/uapi/linux/fscrypt.h is placed under +# libcfs/include/uapi/linux/. +# + +mv libcfs/libcfs/crypto/fscrypt_private.h libcfs/libcfs/crypto/llcrypt_private.h +mv libcfs/include/libcfs/crypto/fscrypt.h libcfs/include/libcfs/crypto/llcrypt.h +mv libcfs/include/uapi/linux/fscrypt.h libcfs/include/uapi/linux/llcrypt.h + +file_list=$(find libcfs/libcfs/crypto/ -type f) +file_list+=" libcfs/include/libcfs/crypto/Makefile.am" +file_list+=" libcfs/include/libcfs/crypto/llcrypt.h" +file_list+=" libcfs/include/libcfs/crypto/Makefile.in" +file_list+=" libcfs/include/uapi/linux/llcrypt.h" + +udef_list=$(grep -n "#define FS_" libcfs/include/uapi/linux/llcrypt.h | awk '{print $2}') + +for file in $file_list; do + cp $file ${file}.bkp + sed -i s+fscrypt+llcrypt+g $file + sed -i s+FSCRYPT+LLCRYPT+g $file + sed -i s+FS_CRYPTO_BLOCK_SIZE+LL_CRYPTO_BLOCK_SIZE+g $file + sed -i s+FSTR_INIT+LLTR_INIT+g $file + sed -i s+FSTR_TO_QSTR+LLTR_TO_QSTR+g $file + sed -i s+CONFIG_FS_ENCRYPTION+CONFIG_LL_ENCRYPTION+g $file + sed -i s+FS_CFLG_OWN_PAGES+LL_CFLG_OWN_PAGES+g $file + sed -i s+fname_name+lname_name+g $file + sed -i s+fname_len+lname_len+g $file + for def in $udef_list; do + newdef=$(echo $def | sed s+^FS_+LL_+) + sed -i s+$def+$newdef+g $file + done +done + +for patch in $(cat contrib/scripts/crypto_patches/series); do + patch -p1 < $patch +done + +exit 0 diff --git a/debian/rules b/debian/rules index 33f7878..cbb4a5b 100755 --- a/debian/rules +++ b/debian/rules @@ -194,6 +194,11 @@ configure-stamp: autogen-stamp debian/control.main debian/control.modules.in elif echo "$${DEB_BUILD_PROFILES}" | grep -qw "nogss"; then \ export EXTRAFLAGS="$${EXTRAFLAGS} --disable-gss"; \ fi; \ + if echo "$${DEB_BUILD_PROFILES}" | grep -qw "crypto"; then \ + export EXTRAFLAGS="$${EXTRAFLAGS} --enable-crypto"; \ + elif echo "$${DEB_BUILD_PROFILES}" | grep -qw "nocrypto"; then \ + export EXTRAFLAGS="$${EXTRAFLAGS} --disable-crypto"; \ + fi; \ echo "Final value of EXTRAFLAGS: $${EXTRAFLAGS}"; \ ( cd $(BUILDDIR) && \ $(SRCDIR)/configure --disable-dependency-tracking \ @@ -475,6 +480,11 @@ kdist_config: prep-deb-files patch-stamp if echo "$${DEB_BUILD_PROFILES}" | grep -q "o2ib"; then \ export EXTRAFLAGS="$${EXTRAFLAGS} --with-o2ib=$${O2IB_SRC}"; \ fi; \ + if echo "$${DEB_BUILD_PROFILES}" | grep -qw "crypto"; then \ + export EXTRAFLAGS="$${EXTRAFLAGS} --enable-crypto"; \ + elif echo "$${DEB_BUILD_PROFILES}" | grep -qw "nocrypto"; then \ + export EXTRAFLAGS="$${EXTRAFLAGS} --disable-crypto"; \ + fi; \ echo "Final value of EXTRAFLAGS: $${EXTRAFLAGS}"; \ ./configure --with-linux=$(KSRC_TREE) \ --with-linux-obj=$(KSRC) \ diff --git a/libcfs/autoconf/lustre-libcfs.m4 b/libcfs/autoconf/lustre-libcfs.m4 index 8db36ae..76775ad 100644 --- a/libcfs/autoconf/lustre-libcfs.m4 +++ b/libcfs/autoconf/lustre-libcfs.m4 @@ -1368,11 +1368,15 @@ libcfs/autoMakefile libcfs/autoconf/Makefile libcfs/include/Makefile libcfs/include/libcfs/Makefile +libcfs/include/uapi/Makefile libcfs/include/libcfs/linux/Makefile libcfs/include/libcfs/util/Makefile +libcfs/include/libcfs/crypto/Makefile +libcfs/include/uapi/linux/Makefile libcfs/libcfs/Makefile libcfs/libcfs/autoMakefile libcfs/libcfs/linux/Makefile libcfs/libcfs/util/Makefile +libcfs/libcfs/crypto/Makefile ]) ]) # LIBCFS_CONFIG_FILES diff --git a/libcfs/include/Makefile.am b/libcfs/include/Makefile.am index 8289f5f..9152b79 100644 --- a/libcfs/include/Makefile.am +++ b/libcfs/include/Makefile.am @@ -1 +1 @@ -SUBDIRS = libcfs +SUBDIRS = libcfs uapi diff --git a/libcfs/include/libcfs/Makefile.am b/libcfs/include/libcfs/Makefile.am index 7164dbb..40b998a 100644 --- a/libcfs/include/libcfs/Makefile.am +++ b/libcfs/include/libcfs/Makefile.am @@ -1,5 +1,7 @@ SUBDIRS = linux util DIST_SUBDIRS = linux util +SUBDIRS += crypto +DIST_SUBDIRS += crypto libcfsdir = $(includedir)/libcfs diff --git a/libcfs/include/libcfs/crypto/.gitignore b/libcfs/include/libcfs/crypto/.gitignore new file mode 100644 index 0000000..10a7e8d --- /dev/null +++ b/libcfs/include/libcfs/crypto/.gitignore @@ -0,0 +1 @@ +/Makefile.in diff --git a/libcfs/include/libcfs/crypto/Makefile.am b/libcfs/include/libcfs/crypto/Makefile.am new file mode 100644 index 0000000..e8cdd97 --- /dev/null +++ b/libcfs/include/libcfs/crypto/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST = llcrypt.h diff --git a/libcfs/include/libcfs/crypto/llcrypt.h b/libcfs/include/libcfs/crypto/llcrypt.h new file mode 100644 index 0000000..038ed4b --- /dev/null +++ b/libcfs/include/libcfs/crypto/llcrypt.h @@ -0,0 +1,796 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * llcrypt.h: declarations for per-file encryption + * + * Filesystems that implement per-file encryption must include this header + * file. + * + * Copyright (C) 2015, Google, Inc. + * + * Written by Michael Halcrow, 2015. + * Modified by Jaegeuk Kim, 2015. + */ +/* + * Linux commit 219d54332a09 + * tags/v5.4 + */ +#ifndef _LINUX_LLCRYPT_H +#define _LINUX_LLCRYPT_H + +#ifndef DCACHE_ENCRYPTED_NAME +#define DCACHE_ENCRYPTED_NAME 0x02000000 +#endif + +#include +#include +#include +#include +#include + +#define LL_CRYPTO_BLOCK_SIZE 16 + +struct llcrypt_ctx; +struct llcrypt_info; + +struct llcrypt_str { + unsigned char *name; + u32 len; +}; + +struct llcrypt_name { + const struct qstr *usr_fname; + struct llcrypt_str disk_name; + u32 hash; + u32 minor_hash; + struct llcrypt_str crypto_buf; + bool is_ciphertext_name; +}; + +#define LLTR_INIT(n, l) { .name = n, .len = l } +#define LLTR_TO_QSTR(f) QSTR_INIT((f)->name, (f)->len) +#define lname_name(p) ((p)->disk_name.name) +#define lname_len(p) ((p)->disk_name.len) + +/* Maximum value for the third parameter of llcrypt_operations.set_context(). */ +#define LLCRYPT_SET_CONTEXT_MAX_SIZE 40 + +#ifdef CONFIG_LL_ENCRYPTION +/* + * llcrypt superblock flags + */ +#define LL_CFLG_OWN_PAGES (1U << 1) + +/* + * crypto operations for filesystems + */ +struct llcrypt_operations { + unsigned int flags; + const char *key_prefix; + int (*get_context)(struct inode *, void *, size_t); + int (*set_context)(struct inode *, const void *, size_t, void *); + bool (*dummy_context)(struct inode *); + bool (*empty_dir)(struct inode *); + unsigned int max_namelen; +}; + +/* Decryption work */ +struct llcrypt_ctx { + union { + struct { + struct bio *bio; + struct work_struct work; + }; + struct list_head free_list; /* Free list */ + }; + u8 flags; /* Flags */ +}; + +extern bool llcrypt_has_encryption_key(const struct inode *inode); + +static inline bool llcrypt_dummy_context_enabled(struct inode *inode) +{ + struct lustre_sb_info *lsi = s2lsi(inode->i_sb); + + if (unlikely(!lsi)) + return false; + + return lsi->lsi_cop->dummy_context && + lsi->lsi_cop->dummy_context(inode); +} + +/* + * When d_splice_alias() moves a directory's encrypted alias to its decrypted + * alias as a result of the encryption key being added, DCACHE_ENCRYPTED_NAME + * must be cleared. Note that we don't have to support arbitrary moves of this + * flag because llcrypt doesn't allow encrypted aliases to be the source or + * target of a rename(). + */ +static inline void llcrypt_handle_d_move(struct dentry *dentry) +{ + dentry->d_flags &= ~DCACHE_ENCRYPTED_NAME; +} + +/* crypto.c */ +extern int __init llcrypt_init(void); +extern void __exit llcrypt_exit(void); +extern void llcrypt_enqueue_decrypt_work(struct work_struct *); +extern struct llcrypt_ctx *llcrypt_get_ctx(gfp_t); +extern void llcrypt_release_ctx(struct llcrypt_ctx *); + +extern struct page *llcrypt_encrypt_pagecache_blocks(struct page *page, + unsigned int len, + unsigned int offs, + gfp_t gfp_flags); +extern int llcrypt_encrypt_block_inplace(const struct inode *inode, + struct page *page, unsigned int len, + unsigned int offs, u64 lblk_num, + gfp_t gfp_flags); + +extern int llcrypt_decrypt_pagecache_blocks(struct page *page, unsigned int len, + unsigned int offs); +extern int llcrypt_decrypt_block_inplace(const struct inode *inode, + struct page *page, unsigned int len, + unsigned int offs, u64 lblk_num); + +static inline bool llcrypt_is_bounce_page(struct page *page) +{ + return page->mapping == NULL; +} + +static inline struct page *llcrypt_pagecache_page(struct page *bounce_page) +{ + return (struct page *)page_private(bounce_page); +} + +extern void llcrypt_free_bounce_page(struct page *bounce_page); + +/* policy.c */ +extern int llcrypt_ioctl_set_policy(struct file *, const void __user *); +extern int llcrypt_ioctl_get_policy(struct file *, void __user *); +extern int llcrypt_ioctl_get_policy_ex(struct file *, void __user *); +extern int llcrypt_has_permitted_context(struct inode *, struct inode *); +extern int llcrypt_inherit_context(struct inode *, struct inode *, + void *, bool); +/* keyring.c */ +extern void llcrypt_sb_free(struct super_block *sb); +extern int llcrypt_ioctl_add_key(struct file *filp, void __user *arg); +extern int llcrypt_ioctl_remove_key(struct file *filp, void __user *arg); +extern int llcrypt_ioctl_remove_key_all_users(struct file *filp, + void __user *arg); +extern int llcrypt_ioctl_get_key_status(struct file *filp, void __user *arg); + +/* keysetup.c */ +extern int llcrypt_get_encryption_info(struct inode *); +extern void llcrypt_put_encryption_info(struct inode *); +extern void llcrypt_free_inode(struct inode *); +extern int llcrypt_drop_inode(struct inode *inode); + +/* fname.c */ +extern int llcrypt_setup_filename(struct inode *, const struct qstr *, + int lookup, struct llcrypt_name *); + +static inline void llcrypt_free_filename(struct llcrypt_name *fname) +{ + kfree(fname->crypto_buf.name); +} + +extern int llcrypt_fname_alloc_buffer(const struct inode *, u32, + struct llcrypt_str *); +extern void llcrypt_fname_free_buffer(struct llcrypt_str *); +extern int llcrypt_fname_disk_to_usr(struct inode *, u32, u32, + const struct llcrypt_str *, struct llcrypt_str *); + +#define LLCRYPT_FNAME_MAX_UNDIGESTED_SIZE 32 + +/* Extracts the second-to-last ciphertext block; see explanation below */ +#define LLCRYPT_FNAME_DIGEST(name, len) \ + ((name) + round_down((len) - LL_CRYPTO_BLOCK_SIZE - 1, \ + LL_CRYPTO_BLOCK_SIZE)) + +#define LLCRYPT_FNAME_DIGEST_SIZE LL_CRYPTO_BLOCK_SIZE + +/** + * llcrypt_digested_name - alternate identifier for an on-disk filename + * + * When userspace lists an encrypted directory without access to the key, + * filenames whose ciphertext is longer than LLCRYPT_FNAME_MAX_UNDIGESTED_SIZE + * bytes are shown in this abbreviated form (base64-encoded) rather than as the + * full ciphertext (base64-encoded). This is necessary to allow supporting + * filenames up to NAME_MAX bytes, since base64 encoding expands the length. + * + * To make it possible for filesystems to still find the correct directory entry + * despite not knowing the full on-disk name, we encode any filesystem-specific + * 'hash' and/or 'minor_hash' which the filesystem may need for its lookups, + * followed by the second-to-last ciphertext block of the filename. Due to the + * use of the CBC-CTS encryption mode, the second-to-last ciphertext block + * depends on the full plaintext. (Note that ciphertext stealing causes the + * last two blocks to appear "flipped".) This makes accidental collisions very + * unlikely: just a 1 in 2^128 chance for two filenames to collide even if they + * share the same filesystem-specific hashes. + * + * However, this scheme isn't immune to intentional collisions, which can be + * created by anyone able to create arbitrary plaintext filenames and view them + * without the key. Making the "digest" be a real cryptographic hash like + * SHA-256 over the full ciphertext would prevent this, although it would be + * less efficient and harder to implement, especially since the filesystem would + * need to calculate it for each directory entry examined during a search. + */ +struct llcrypt_digested_name { + u32 hash; + u32 minor_hash; + u8 digest[LLCRYPT_FNAME_DIGEST_SIZE]; +}; + +/** + * llcrypt_match_name() - test whether the given name matches a directory entry + * @fname: the name being searched for + * @de_name: the name from the directory entry + * @de_name_len: the length of @de_name in bytes + * + * Normally @fname->disk_name will be set, and in that case we simply compare + * that to the name stored in the directory entry. The only exception is that + * if we don't have the key for an encrypted directory and a filename in it is + * very long, then we won't have the full disk_name and we'll instead need to + * match against the llcrypt_digested_name. + * + * Return: %true if the name matches, otherwise %false. + */ +static inline bool llcrypt_match_name(const struct llcrypt_name *fname, + const u8 *de_name, u32 de_name_len) +{ + if (unlikely(!fname->disk_name.name)) { + const struct llcrypt_digested_name *n = + (const void *)fname->crypto_buf.name; + if (WARN_ON_ONCE(fname->usr_fname->name[0] != '_')) + return false; + if (de_name_len <= LLCRYPT_FNAME_MAX_UNDIGESTED_SIZE) + return false; + return !memcmp(LLCRYPT_FNAME_DIGEST(de_name, de_name_len), + n->digest, LLCRYPT_FNAME_DIGEST_SIZE); + } + + if (de_name_len != fname->disk_name.len) + return false; + return !memcmp(de_name, fname->disk_name.name, fname->disk_name.len); +} + +/* bio.c */ +extern void llcrypt_decrypt_bio(struct bio *); +extern void llcrypt_enqueue_decrypt_bio(struct llcrypt_ctx *ctx, + struct bio *bio); +extern int llcrypt_zeroout_range(const struct inode *, pgoff_t, sector_t, + unsigned int); + +/* hooks.c */ +extern int llcrypt_file_open(struct inode *inode, struct file *filp); +extern int __llcrypt_prepare_link(struct inode *inode, struct inode *dir, + struct dentry *dentry); +extern int __llcrypt_prepare_rename(struct inode *old_dir, + struct dentry *old_dentry, + struct inode *new_dir, + struct dentry *new_dentry, + unsigned int flags); +extern int __llcrypt_prepare_lookup(struct inode *dir, struct dentry *dentry, + struct llcrypt_name *fname); +extern int __llcrypt_prepare_symlink(struct inode *dir, unsigned int len, + unsigned int max_len, + struct llcrypt_str *disk_link); +extern int __llcrypt_encrypt_symlink(struct inode *inode, const char *target, + unsigned int len, + struct llcrypt_str *disk_link); +extern const char *llcrypt_get_symlink(struct inode *inode, const void *caddr, + unsigned int max_size, + struct delayed_call *done); +static inline void llcrypt_set_ops(struct super_block *sb, + const struct llcrypt_operations *lsi_cop) +{ + struct lustre_sb_info *lsi = s2lsi(sb); + + if (lsi) + lsi->lsi_cop = lsi_cop; +} +#else /* !CONFIG_LL_ENCRYPTION */ + +struct llcrypt_operations; +#define llcrypt_init() 0 +#define llcrypt_exit() {} + +#undef IS_ENCRYPTED +#define IS_ENCRYPTED(x) 0 + +static inline bool llcrypt_has_encryption_key(const struct inode *inode) +{ + return false; +} + +static inline bool llcrypt_dummy_context_enabled(struct inode *inode) +{ + return false; +} + +static inline void llcrypt_handle_d_move(struct dentry *dentry) +{ +} + +/* crypto.c */ +static inline void llcrypt_enqueue_decrypt_work(struct work_struct *work) +{ +} + +static inline struct llcrypt_ctx *llcrypt_get_ctx(gfp_t gfp_flags) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +static inline void llcrypt_release_ctx(struct llcrypt_ctx *ctx) +{ + return; +} + +static inline struct page *llcrypt_encrypt_pagecache_blocks(struct page *page, + unsigned int len, + unsigned int offs, + gfp_t gfp_flags) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +static inline int llcrypt_encrypt_block_inplace(const struct inode *inode, + struct page *page, + unsigned int len, + unsigned int offs, u64 lblk_num, + gfp_t gfp_flags) +{ + return -EOPNOTSUPP; +} + +static inline int llcrypt_decrypt_pagecache_blocks(struct page *page, + unsigned int len, + unsigned int offs) +{ + return -EOPNOTSUPP; +} + +static inline int llcrypt_decrypt_block_inplace(const struct inode *inode, + struct page *page, + unsigned int len, + unsigned int offs, u64 lblk_num) +{ + return -EOPNOTSUPP; +} + +static inline bool llcrypt_is_bounce_page(struct page *page) +{ + return false; +} + +static inline struct page *llcrypt_pagecache_page(struct page *bounce_page) +{ + WARN_ON_ONCE(1); + return ERR_PTR(-EINVAL); +} + +static inline void llcrypt_free_bounce_page(struct page *bounce_page) +{ +} + +/* policy.c */ +static inline int llcrypt_ioctl_set_policy(struct file *filp, + const void __user *arg) +{ + return -EOPNOTSUPP; +} + +static inline int llcrypt_ioctl_get_policy(struct file *filp, void __user *arg) +{ + return -EOPNOTSUPP; +} + +static inline int llcrypt_ioctl_get_policy_ex(struct file *filp, + void __user *arg) +{ + return -EOPNOTSUPP; +} + +static inline int llcrypt_has_permitted_context(struct inode *parent, + struct inode *child) +{ + return 0; +} + +static inline int llcrypt_inherit_context(struct inode *parent, + struct inode *child, + void *fs_data, bool preload) +{ + return -EOPNOTSUPP; +} + +/* keyring.c */ +static inline void llcrypt_sb_free(struct super_block *sb) +{ +} + +static inline int llcrypt_ioctl_add_key(struct file *filp, void __user *arg) +{ + return -EOPNOTSUPP; +} + +static inline int llcrypt_ioctl_remove_key(struct file *filp, void __user *arg) +{ + return -EOPNOTSUPP; +} + +static inline int llcrypt_ioctl_remove_key_all_users(struct file *filp, + void __user *arg) +{ + return -EOPNOTSUPP; +} + +static inline int llcrypt_ioctl_get_key_status(struct file *filp, + void __user *arg) +{ + return -EOPNOTSUPP; +} + +/* keysetup.c */ +static inline int llcrypt_get_encryption_info(struct inode *inode) +{ + return -EOPNOTSUPP; +} + +static inline void llcrypt_put_encryption_info(struct inode *inode) +{ + return; +} + +static inline void llcrypt_free_inode(struct inode *inode) +{ +} + +static inline int llcrypt_drop_inode(struct inode *inode) +{ + return 0; +} + + /* fname.c */ +static inline int llcrypt_setup_filename(struct inode *dir, + const struct qstr *iname, + int lookup, struct llcrypt_name *fname) +{ + if (IS_ENCRYPTED(dir)) + return -EOPNOTSUPP; + + memset(fname, 0, sizeof(*fname)); + fname->usr_fname = iname; + fname->disk_name.name = (unsigned char *)iname->name; + fname->disk_name.len = iname->len; + return 0; +} + +static inline void llcrypt_free_filename(struct llcrypt_name *fname) +{ + return; +} + +static inline int llcrypt_fname_alloc_buffer(const struct inode *inode, + u32 max_encrypted_len, + struct llcrypt_str *crypto_str) +{ + return -EOPNOTSUPP; +} + +static inline void llcrypt_fname_free_buffer(struct llcrypt_str *crypto_str) +{ + return; +} + +static inline int llcrypt_fname_disk_to_usr(struct inode *inode, + u32 hash, u32 minor_hash, + const struct llcrypt_str *iname, + struct llcrypt_str *oname) +{ + return -EOPNOTSUPP; +} + +static inline bool llcrypt_match_name(const struct llcrypt_name *fname, + const u8 *de_name, u32 de_name_len) +{ + /* Encryption support disabled; use standard comparison */ + if (de_name_len != fname->disk_name.len) + return false; + return !memcmp(de_name, fname->disk_name.name, fname->disk_name.len); +} + +/* bio.c */ +static inline void llcrypt_decrypt_bio(struct bio *bio) +{ +} + +static inline void llcrypt_enqueue_decrypt_bio(struct llcrypt_ctx *ctx, + struct bio *bio) +{ +} + +static inline int llcrypt_zeroout_range(const struct inode *inode, pgoff_t lblk, + sector_t pblk, unsigned int len) +{ + return -EOPNOTSUPP; +} + +/* hooks.c */ + +static inline int llcrypt_file_open(struct inode *inode, struct file *filp) +{ + if (IS_ENCRYPTED(inode)) + return -EOPNOTSUPP; + return 0; +} + +static inline int __llcrypt_prepare_link(struct inode *inode, struct inode *dir, + struct dentry *dentry) +{ + return -EOPNOTSUPP; +} + +static inline int __llcrypt_prepare_rename(struct inode *old_dir, + struct dentry *old_dentry, + struct inode *new_dir, + struct dentry *new_dentry, + unsigned int flags) +{ + return -EOPNOTSUPP; +} + +static inline int __llcrypt_prepare_lookup(struct inode *dir, + struct dentry *dentry, + struct llcrypt_name *fname) +{ + return -EOPNOTSUPP; +} + +static inline int __llcrypt_prepare_symlink(struct inode *dir, + unsigned int len, + unsigned int max_len, + struct llcrypt_str *disk_link) +{ + return -EOPNOTSUPP; +} + + +static inline int __llcrypt_encrypt_symlink(struct inode *inode, + const char *target, + unsigned int len, + struct llcrypt_str *disk_link) +{ + return -EOPNOTSUPP; +} + +#define llcrypt_get_symlink(inode, caddr, max_size, done) ERR_PTR(-EOPNOTSUPP) + +static inline void llcrypt_set_ops(struct super_block *sb, + const struct llcrypt_operations *lsi_cop) +{ +} + +#endif /* !CONFIG_LL_ENCRYPTION */ + +/** + * llcrypt_require_key - require an inode's encryption key + * @inode: the inode we need the key for + * + * If the inode is encrypted, set up its encryption key if not already done. + * Then require that the key be present and return -ENOKEY otherwise. + * + * No locks are needed, and the key will live as long as the struct inode --- so + * it won't go away from under you. + * + * Return: 0 on success, -ENOKEY if the key is missing, or another -errno code + * if a problem occurred while setting up the encryption key. + */ +static inline int llcrypt_require_key(struct inode *inode) +{ + if (IS_ENCRYPTED(inode)) { + int err = llcrypt_get_encryption_info(inode); + + if (err) + return err; + if (!llcrypt_has_encryption_key(inode)) + return -ENOKEY; + } + return 0; +} + +/** + * llcrypt_prepare_link - prepare to link an inode into a possibly-encrypted directory + * @old_dentry: an existing dentry for the inode being linked + * @dir: the target directory + * @dentry: negative dentry for the target filename + * + * A new link can only be added to an encrypted directory if the directory's + * encryption key is available --- since otherwise we'd have no way to encrypt + * the filename. Therefore, we first set up the directory's encryption key (if + * not already done) and return an error if it's unavailable. + * + * We also verify that the link will not violate the constraint that all files + * in an encrypted directory tree use the same encryption policy. + * + * Return: 0 on success, -ENOKEY if the directory's encryption key is missing, + * -EXDEV if the link would result in an inconsistent encryption policy, or + * another -errno code. + */ +static inline int llcrypt_prepare_link(struct dentry *old_dentry, + struct inode *dir, + struct dentry *dentry) +{ + if (IS_ENCRYPTED(dir)) + return __llcrypt_prepare_link(d_inode(old_dentry), dir, dentry); + return 0; +} + +/** + * llcrypt_prepare_rename - prepare for a rename between possibly-encrypted directories + * @old_dir: source directory + * @old_dentry: dentry for source file + * @new_dir: target directory + * @new_dentry: dentry for target location (may be negative unless exchanging) + * @flags: rename flags (we care at least about %RENAME_EXCHANGE) + * + * Prepare for ->rename() where the source and/or target directories may be + * encrypted. A new link can only be added to an encrypted directory if the + * directory's encryption key is available --- since otherwise we'd have no way + * to encrypt the filename. A rename to an existing name, on the other hand, + * *is* cryptographically possible without the key. However, we take the more + * conservative approach and just forbid all no-key renames. + * + * We also verify that the rename will not violate the constraint that all files + * in an encrypted directory tree use the same encryption policy. + * + * Return: 0 on success, -ENOKEY if an encryption key is missing, -EXDEV if the + * rename would cause inconsistent encryption policies, or another -errno code. + */ +static inline int llcrypt_prepare_rename(struct inode *old_dir, + struct dentry *old_dentry, + struct inode *new_dir, + struct dentry *new_dentry, + unsigned int flags) +{ + if (IS_ENCRYPTED(old_dir) || IS_ENCRYPTED(new_dir)) + return __llcrypt_prepare_rename(old_dir, old_dentry, + new_dir, new_dentry, flags); + return 0; +} + +/** + * llcrypt_prepare_lookup - prepare to lookup a name in a possibly-encrypted directory + * @dir: directory being searched + * @dentry: filename being looked up + * @fname: (output) the name to use to search the on-disk directory + * + * Prepare for ->lookup() in a directory which may be encrypted by determining + * the name that will actually be used to search the directory on-disk. Lookups + * can be done with or without the directory's encryption key; without the key, + * filenames are presented in encrypted form. Therefore, we'll try to set up + * the directory's encryption key, but even without it the lookup can continue. + * + * This also installs a custom ->d_revalidate() method which will invalidate the + * dentry if it was created without the key and the key is later added. + * + * Return: 0 on success; -ENOENT if key is unavailable but the filename isn't a + * correctly formed encoded ciphertext name, so a negative dentry should be + * created; or another -errno code. + */ +static inline int llcrypt_prepare_lookup(struct inode *dir, + struct dentry *dentry, + struct llcrypt_name *fname) +{ + if (IS_ENCRYPTED(dir)) + return __llcrypt_prepare_lookup(dir, dentry, fname); + + memset(fname, 0, sizeof(*fname)); + fname->usr_fname = &dentry->d_name; + fname->disk_name.name = (unsigned char *)dentry->d_name.name; + fname->disk_name.len = dentry->d_name.len; + return 0; +} + +/** + * llcrypt_prepare_setattr - prepare to change a possibly-encrypted inode's attributes + * @dentry: dentry through which the inode is being changed + * @attr: attributes to change + * + * Prepare for ->setattr() on a possibly-encrypted inode. On an encrypted file, + * most attribute changes are allowed even without the encryption key. However, + * without the encryption key we do have to forbid truncates. This is needed + * because the size being truncated to may not be a multiple of the filesystem + * block size, and in that case we'd have to decrypt the final block, zero the + * portion past i_size, and re-encrypt it. (We *could* allow truncating to a + * filesystem block boundary, but it's simpler to just forbid all truncates --- + * and we already forbid all other contents modifications without the key.) + * + * Return: 0 on success, -ENOKEY if the key is missing, or another -errno code + * if a problem occurred while setting up the encryption key. + */ +static inline int llcrypt_prepare_setattr(struct dentry *dentry, + struct iattr *attr) +{ + if (attr->ia_valid & ATTR_SIZE) + return llcrypt_require_key(d_inode(dentry)); + return 0; +} + +/** + * llcrypt_prepare_symlink - prepare to create a possibly-encrypted symlink + * @dir: directory in which the symlink is being created + * @target: plaintext symlink target + * @len: length of @target excluding null terminator + * @max_len: space the filesystem has available to store the symlink target + * @disk_link: (out) the on-disk symlink target being prepared + * + * This function computes the size the symlink target will require on-disk, + * stores it in @disk_link->len, and validates it against @max_len. An + * encrypted symlink may be longer than the original. + * + * Additionally, @disk_link->name is set to @target if the symlink will be + * unencrypted, but left NULL if the symlink will be encrypted. For encrypted + * symlinks, the filesystem must call llcrypt_encrypt_symlink() to create the + * on-disk target later. (The reason for the two-step process is that some + * filesystems need to know the size of the symlink target before creating the + * inode, e.g. to determine whether it will be a "fast" or "slow" symlink.) + * + * Return: 0 on success, -ENAMETOOLONG if the symlink target is too long, + * -ENOKEY if the encryption key is missing, or another -errno code if a problem + * occurred while setting up the encryption key. + */ +static inline int llcrypt_prepare_symlink(struct inode *dir, + const char *target, + unsigned int len, + unsigned int max_len, + struct llcrypt_str *disk_link) +{ + if (IS_ENCRYPTED(dir) || llcrypt_dummy_context_enabled(dir)) + return __llcrypt_prepare_symlink(dir, len, max_len, disk_link); + + disk_link->name = (unsigned char *)target; + disk_link->len = len + 1; + if (disk_link->len > max_len) + return -ENAMETOOLONG; + return 0; +} + +/** + * llcrypt_encrypt_symlink - encrypt the symlink target if needed + * @inode: symlink inode + * @target: plaintext symlink target + * @len: length of @target excluding null terminator + * @disk_link: (in/out) the on-disk symlink target being prepared + * + * If the symlink target needs to be encrypted, then this function encrypts it + * into @disk_link->name. llcrypt_prepare_symlink() must have been called + * previously to compute @disk_link->len. If the filesystem did not allocate a + * buffer for @disk_link->name after calling llcrypt_prepare_link(), then one + * will be kmalloc()'ed and the filesystem will be responsible for freeing it. + * + * Return: 0 on success, -errno on failure + */ +static inline int llcrypt_encrypt_symlink(struct inode *inode, + const char *target, + unsigned int len, + struct llcrypt_str *disk_link) +{ + if (IS_ENCRYPTED(inode)) + return __llcrypt_encrypt_symlink(inode, target, len, disk_link); + return 0; +} + +/* If *pagep is a bounce page, free it and set *pagep to the pagecache page */ +static inline void llcrypt_finalize_bounce_page(struct page **pagep) +{ + struct page *page = *pagep; + + if (llcrypt_is_bounce_page(page)) { + *pagep = llcrypt_pagecache_page(page); + llcrypt_free_bounce_page(page); + } +} + +#endif /* _LINUX_LLCRYPT_H */ diff --git a/libcfs/include/uapi/.gitignore b/libcfs/include/uapi/.gitignore new file mode 100644 index 0000000..10a7e8d --- /dev/null +++ b/libcfs/include/uapi/.gitignore @@ -0,0 +1 @@ +/Makefile.in diff --git a/libcfs/include/uapi/Makefile.am b/libcfs/include/uapi/Makefile.am new file mode 100644 index 0000000..08b2f5f --- /dev/null +++ b/libcfs/include/uapi/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = linux diff --git a/libcfs/include/uapi/linux/.gitignore b/libcfs/include/uapi/linux/.gitignore new file mode 100644 index 0000000..10a7e8d --- /dev/null +++ b/libcfs/include/uapi/linux/.gitignore @@ -0,0 +1 @@ +/Makefile.in diff --git a/libcfs/include/uapi/linux/Makefile.am b/libcfs/include/uapi/linux/Makefile.am new file mode 100644 index 0000000..e8cdd97 --- /dev/null +++ b/libcfs/include/uapi/linux/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST = llcrypt.h diff --git a/libcfs/include/uapi/linux/llcrypt.h b/libcfs/include/uapi/linux/llcrypt.h new file mode 100644 index 0000000..dac16c0 --- /dev/null +++ b/libcfs/include/uapi/linux/llcrypt.h @@ -0,0 +1,185 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * llcrypt user API + * + * These ioctls can be used on filesystems that support llcrypt. See the + * "User API" section of Documentation/filesystems/llcrypt.rst. + */ +/* + * Linux commit 219d54332a09 + * tags/v5.4 + */ +#ifndef _UAPI_LINUX_LLCRYPT_H +#define _UAPI_LINUX_LLCRYPT_H + +#include + +/* Encryption policy flags */ +#define LLCRYPT_POLICY_FLAGS_PAD_4 0x00 +#define LLCRYPT_POLICY_FLAGS_PAD_8 0x01 +#define LLCRYPT_POLICY_FLAGS_PAD_16 0x02 +#define LLCRYPT_POLICY_FLAGS_PAD_32 0x03 +#define LLCRYPT_POLICY_FLAGS_PAD_MASK 0x03 +#define LLCRYPT_POLICY_FLAG_DIRECT_KEY 0x04 +#define LLCRYPT_POLICY_FLAGS_VALID 0x07 + +/* Encryption algorithms */ +#define LLCRYPT_MODE_AES_256_XTS 1 +#define LLCRYPT_MODE_AES_256_CTS 4 +#define LLCRYPT_MODE_AES_128_CBC 5 +#define LLCRYPT_MODE_AES_128_CTS 6 +#define LLCRYPT_MODE_ADIANTUM 9 +#define __LLCRYPT_MODE_MAX 9 + +/* + * Legacy policy version; ad-hoc KDF and no key verification. + * For new encrypted directories, use llcrypt_policy_v2 instead. + * + * Careful: the .version field for this is actually 0, not 1. + */ +#define LLCRYPT_POLICY_V1 0 +#define LLCRYPT_KEY_DESCRIPTOR_SIZE 8 +struct llcrypt_policy_v1 { + __u8 version; + __u8 contents_encryption_mode; + __u8 filenames_encryption_mode; + __u8 flags; + __u8 master_key_descriptor[LLCRYPT_KEY_DESCRIPTOR_SIZE]; +}; +#define llcrypt_policy llcrypt_policy_v1 + +/* + * Process-subscribed "logon" key description prefix and payload format. + * Deprecated; prefer LL_IOC_ADD_ENCRYPTION_KEY instead. + */ +#define LLCRYPT_KEY_DESC_PREFIX "fscrypt:" +#define LLCRYPT_KEY_DESC_PREFIX_SIZE 8 +#define LLCRYPT_MAX_KEY_SIZE 64 +struct llcrypt_key { + __u32 mode; + __u8 raw[LLCRYPT_MAX_KEY_SIZE]; + __u32 size; +}; + +/* + * New policy version with HKDF and key verification (recommended). + */ +#define LLCRYPT_POLICY_V2 2 +#define LLCRYPT_KEY_IDENTIFIER_SIZE 16 +struct llcrypt_policy_v2 { + __u8 version; + __u8 contents_encryption_mode; + __u8 filenames_encryption_mode; + __u8 flags; + __u8 __reserved[4]; + __u8 master_key_identifier[LLCRYPT_KEY_IDENTIFIER_SIZE]; +}; + +/* Struct passed to LL_IOC_GET_ENCRYPTION_POLICY_EX */ +struct llcrypt_get_policy_ex_arg { + __u64 policy_size; /* input/output */ + union { + __u8 version; + struct llcrypt_policy_v1 v1; + struct llcrypt_policy_v2 v2; + } policy; /* output */ +}; + +/* + * v1 policy keys are specified by an arbitrary 8-byte key "descriptor", + * matching llcrypt_policy_v1::master_key_descriptor. + */ +#define LLCRYPT_KEY_SPEC_TYPE_DESCRIPTOR 1 + +/* + * v2 policy keys are specified by a 16-byte key "identifier" which the kernel + * calculates as a cryptographic hash of the key itself, + * matching llcrypt_policy_v2::master_key_identifier. + */ +#define LLCRYPT_KEY_SPEC_TYPE_IDENTIFIER 2 + +/* + * Specifies a key, either for v1 or v2 policies. This doesn't contain the + * actual key itself; this is just the "name" of the key. + */ +struct llcrypt_key_specifier { + __u32 type; /* one of LLCRYPT_KEY_SPEC_TYPE_* */ + __u32 __reserved; + union { + __u8 __reserved[32]; /* reserve some extra space */ + __u8 descriptor[LLCRYPT_KEY_DESCRIPTOR_SIZE]; + __u8 identifier[LLCRYPT_KEY_IDENTIFIER_SIZE]; + } u; +}; + +/* Struct passed to LL_IOC_ADD_ENCRYPTION_KEY */ +struct llcrypt_add_key_arg { + struct llcrypt_key_specifier key_spec; + __u32 raw_size; + __u32 __reserved[9]; + __u8 raw[]; +}; + +/* Struct passed to LL_IOC_REMOVE_ENCRYPTION_KEY */ +struct llcrypt_remove_key_arg { + struct llcrypt_key_specifier key_spec; +#define LLCRYPT_KEY_REMOVAL_STATUS_FLAG_FILES_BUSY 0x00000001 +#define LLCRYPT_KEY_REMOVAL_STATUS_FLAG_OTHER_USERS 0x00000002 + __u32 removal_status_flags; /* output */ + __u32 __reserved[5]; +}; + +/* Struct passed to LL_IOC_GET_ENCRYPTION_KEY_STATUS */ +struct llcrypt_get_key_status_arg { + /* input */ + struct llcrypt_key_specifier key_spec; + __u32 __reserved[6]; + + /* output */ +#define LLCRYPT_KEY_STATUS_ABSENT 1 +#define LLCRYPT_KEY_STATUS_PRESENT 2 +#define LLCRYPT_KEY_STATUS_INCOMPLETELY_REMOVED 3 + __u32 status; +#define LLCRYPT_KEY_STATUS_FLAG_ADDED_BY_SELF 0x00000001 + __u32 status_flags; + __u32 user_count; + __u32 __out_reserved[13]; +}; + +#define LL_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct llcrypt_policy) +#define LL_IOC_GET_ENCRYPTION_PWSALT _IOW('f', 20, __u8[16]) +#define LL_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct llcrypt_policy) +#define LL_IOC_GET_ENCRYPTION_POLICY_EX _IOWR('f', 22, __u8[9]) /* size + version */ +#define LL_IOC_ADD_ENCRYPTION_KEY _IOWR('f', 23, struct llcrypt_add_key_arg) +#define LL_IOC_REMOVE_ENCRYPTION_KEY _IOWR('f', 24, struct llcrypt_remove_key_arg) +#define LL_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS _IOWR('f', 25, struct llcrypt_remove_key_arg) +#define LL_IOC_GET_ENCRYPTION_KEY_STATUS _IOWR('f', 26, struct llcrypt_get_key_status_arg) + +/**********************************************************************/ + +/* old names; don't add anything new here! */ +#ifndef __KERNEL__ +#define LL_KEY_DESCRIPTOR_SIZE LLCRYPT_KEY_DESCRIPTOR_SIZE +#define LL_POLICY_FLAGS_PAD_4 LLCRYPT_POLICY_FLAGS_PAD_4 +#define LL_POLICY_FLAGS_PAD_8 LLCRYPT_POLICY_FLAGS_PAD_8 +#define LL_POLICY_FLAGS_PAD_16 LLCRYPT_POLICY_FLAGS_PAD_16 +#define LL_POLICY_FLAGS_PAD_32 LLCRYPT_POLICY_FLAGS_PAD_32 +#define LL_POLICY_FLAGS_PAD_MASK LLCRYPT_POLICY_FLAGS_PAD_MASK +#define LL_POLICY_FLAG_DIRECT_KEY LLCRYPT_POLICY_FLAG_DIRECT_KEY +#define LL_POLICY_FLAGS_VALID LLCRYPT_POLICY_FLAGS_VALID +#define LL_ENCRYPTION_MODE_INVALID 0 /* never used */ +#define LL_ENCRYPTION_MODE_AES_256_XTS LLCRYPT_MODE_AES_256_XTS +#define LL_ENCRYPTION_MODE_AES_256_GCM 2 /* never used */ +#define LL_ENCRYPTION_MODE_AES_256_CBC 3 /* never used */ +#define LL_ENCRYPTION_MODE_AES_256_CTS LLCRYPT_MODE_AES_256_CTS +#define LL_ENCRYPTION_MODE_AES_128_CBC LLCRYPT_MODE_AES_128_CBC +#define LL_ENCRYPTION_MODE_AES_128_CTS LLCRYPT_MODE_AES_128_CTS +#define LL_ENCRYPTION_MODE_SPECK128_256_XTS 7 /* removed */ +#define LL_ENCRYPTION_MODE_SPECK128_256_CTS 8 /* removed */ +#define LL_ENCRYPTION_MODE_ADIANTUM LLCRYPT_MODE_ADIANTUM +#define LL_KEY_DESC_PREFIX LLCRYPT_KEY_DESC_PREFIX +#define LL_KEY_DESC_PREFIX_SIZE LLCRYPT_KEY_DESC_PREFIX_SIZE +#define LL_MAX_KEY_SIZE LLCRYPT_MAX_KEY_SIZE +#endif /* !__KERNEL__ */ + +#endif /* _UAPI_LINUX_LLCRYPT_H */ diff --git a/libcfs/libcfs/Makefile.in b/libcfs/libcfs/Makefile.in index f4c2d2f..ad9ac74 100644 --- a/libcfs/libcfs/Makefile.in +++ b/libcfs/libcfs/Makefile.in @@ -5,9 +5,13 @@ libcfs-linux-objs += linux-curproc.o libcfs-linux-objs += linux-hash.o libcfs-linux-objs += linux-wait.o +libcfs-crypto-objs := crypto.o fname.o hkdf.o hooks.o keyring.o +libcfs-crypto-objs += keysetup.o keysetup_v1.o policy.o bio.o + default: all libcfs-linux-objs := $(addprefix linux/,$(libcfs-linux-objs)) +libcfs-crypto-objs := $(addprefix crypto/,$(libcfs-crypto-objs)) libcfs-all-objs := debug.o fail.o module.o tracefile.o \ libcfs_string.o hash.o heap.o \ @@ -17,6 +21,7 @@ libcfs-all-objs := debug.o fail.o module.o tracefile.o \ linux-debug.o linux-tracefile.o libcfs-objs := $(libcfs-linux-objs) $(libcfs-all-objs) +@LLCRYPT_TRUE@libcfs-objs += $(libcfs-crypto-objs) EXTRA_PRE_CFLAGS := -I@LUSTRE@/../libcfs/libcfs diff --git a/libcfs/libcfs/autoMakefile.am b/libcfs/libcfs/autoMakefile.am index 92c7564..7d6553c 100644 --- a/libcfs/libcfs/autoMakefile.am +++ b/libcfs/libcfs/autoMakefile.am @@ -34,6 +34,8 @@ SUBDIRS = linux util DIST_SUBDIRS = linux util +SUBDIRS += crypto +DIST_SUBDIRS += crypto noinst_LTLIBRARIES = libcfs.la libcfs_la_SOURCES := util/string.c util/nidstrings.c util/param.c @@ -55,7 +57,7 @@ endif # LINUX endif # MODULES -MOSTLYCLEANFILES := @MOSTLYCLEANFILES@ linux/*.o libcfs +MOSTLYCLEANFILES := @MOSTLYCLEANFILES@ linux/*.o libcfs crypto/*.o EXTRA_DIST := $(libcfs-all-objs:%.o=%.c) tracefile.h \ workitem.c fail.c libcfs_cpu.c \ heap.c libcfs_mem.c libcfs_lock.c \ diff --git a/libcfs/libcfs/crypto/.gitignore b/libcfs/libcfs/crypto/.gitignore new file mode 100644 index 0000000..10a7e8d --- /dev/null +++ b/libcfs/libcfs/crypto/.gitignore @@ -0,0 +1 @@ +/Makefile.in diff --git a/libcfs/libcfs/crypto/ChangeLog b/libcfs/libcfs/crypto/ChangeLog new file mode 100644 index 0000000..cc16ee3 --- /dev/null +++ b/libcfs/libcfs/crypto/ChangeLog @@ -0,0 +1,2 @@ +04-02-2020 Whamcloud, Inc. + * fscrypt sources taken from Linux kernel v5.4 diff --git a/libcfs/libcfs/crypto/Makefile.am b/libcfs/libcfs/crypto/Makefile.am new file mode 100644 index 0000000..2eaeb83 --- /dev/null +++ b/libcfs/libcfs/crypto/Makefile.am @@ -0,0 +1,4 @@ +if LLCRYPT +EXTRA_DIST = crypto.c fname.c hkdf.c hooks.c keyring.c \ + keysetup.c keysetup_v1.c policy.c bio.c llcrypt_private.h +endif diff --git a/libcfs/libcfs/crypto/bio.c b/libcfs/libcfs/crypto/bio.c new file mode 100644 index 0000000..a0e96db --- /dev/null +++ b/libcfs/libcfs/crypto/bio.c @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This contains encryption functions for per-file encryption. + * + * Copyright (C) 2015, Google, Inc. + * Copyright (C) 2015, Motorola Mobility + * + * Written by Michael Halcrow, 2014. + * + * Filename encryption additions + * Uday Savagaonkar, 2014 + * Encryption policy handling additions + * Ildar Muslukhov, 2014 + * Add llcrypt_pullback_bio_page() + * Jaegeuk Kim, 2015. + * + * This has not yet undergone a rigorous security audit. + * + * The usage of AES-XTS should conform to recommendations in NIST + * Special Publication 800-38E and IEEE P1619/D16. + */ +/* + * Linux commit 219d54332a09 + * tags/v5.4 + */ + +#include +#include +#include +#include +#include "llcrypt_private.h" + +static void __llcrypt_decrypt_bio(struct bio *bio, bool done) +{ + struct bio_vec *bv; +#ifdef HAVE_BVEC_ITER_ALL + struct bvec_iter_all iter_all; +#else + int iter_all; +#endif + + bio_for_each_segment_all(bv, bio, iter_all) { + struct page *page = bv->bv_page; + int ret = llcrypt_decrypt_pagecache_blocks(page, bv->bv_len, + bv->bv_offset); + if (ret) + SetPageError(page); + else if (done) + SetPageUptodate(page); + if (done) + unlock_page(page); + } +} + +void llcrypt_decrypt_bio(struct bio *bio) +{ + __llcrypt_decrypt_bio(bio, false); +} +EXPORT_SYMBOL(llcrypt_decrypt_bio); + +static void completion_pages(struct work_struct *work) +{ + struct llcrypt_ctx *ctx = container_of(work, struct llcrypt_ctx, work); + struct bio *bio = ctx->bio; + + __llcrypt_decrypt_bio(bio, true); + llcrypt_release_ctx(ctx); + bio_put(bio); +} + +void llcrypt_enqueue_decrypt_bio(struct llcrypt_ctx *ctx, struct bio *bio) +{ + INIT_WORK(&ctx->work, completion_pages); + ctx->bio = bio; + llcrypt_enqueue_decrypt_work(&ctx->work); +} +EXPORT_SYMBOL(llcrypt_enqueue_decrypt_bio); + +int llcrypt_zeroout_range(const struct inode *inode, pgoff_t lblk, + sector_t pblk, unsigned int len) +{ + const unsigned int blockbits = inode->i_blkbits; + const unsigned int blocksize = 1 << blockbits; + struct page *ciphertext_page; + struct bio *bio; + int ret, err = 0; + + ciphertext_page = llcrypt_alloc_bounce_page(GFP_NOWAIT); + if (!ciphertext_page) + return -ENOMEM; + + while (len--) { + err = llcrypt_crypt_block(inode, FS_ENCRYPT, lblk, + ZERO_PAGE(0), ciphertext_page, + blocksize, 0, GFP_NOFS); + if (err) + goto errout; + + bio = bio_alloc(GFP_NOWAIT, 1); + if (!bio) { + err = -ENOMEM; + goto errout; + } + bio_set_dev(bio, inode->i_sb->s_bdev); + bio->bi_iter.bi_sector = pblk << (blockbits - 9); + bio_set_op_attrs(bio, REQ_OP_WRITE, 0); + ret = bio_add_page(bio, ciphertext_page, blocksize, 0); + if (WARN_ON(ret != blocksize)) { + /* should never happen! */ + bio_put(bio); + err = -EIO; + goto errout; + } + err = submit_bio_wait(bio); + if (err == 0 && bio->bi_status) + err = -EIO; + bio_put(bio); + if (err) + goto errout; + lblk++; + pblk++; + } + err = 0; +errout: + llcrypt_free_bounce_page(ciphertext_page); + return err; +} +EXPORT_SYMBOL(llcrypt_zeroout_range); diff --git a/libcfs/libcfs/crypto/crypto.c b/libcfs/libcfs/crypto/crypto.c new file mode 100644 index 0000000..379affb --- /dev/null +++ b/libcfs/libcfs/crypto/crypto.c @@ -0,0 +1,542 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * This contains encryption functions for per-file encryption. + * + * Copyright (C) 2015, Google, Inc. + * Copyright (C) 2015, Motorola Mobility + * + * Written by Michael Halcrow, 2014. + * + * Filename encryption additions + * Uday Savagaonkar, 2014 + * Encryption policy handling additions + * Ildar Muslukhov, 2014 + * Add llcrypt_pullback_bio_page() + * Jaegeuk Kim, 2015. + * + * This has not yet undergone a rigorous security audit. + * + * The usage of AES-XTS should conform to recommendations in NIST + * Special Publication 800-38E and IEEE P1619/D16. + */ +/* + * Linux commit 219d54332a09 + * tags/v5.4 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "llcrypt_private.h" + +static unsigned int num_prealloc_crypto_pages = 32; +static unsigned int num_prealloc_crypto_ctxs = 128; + +module_param(num_prealloc_crypto_pages, uint, 0444); +MODULE_PARM_DESC(num_prealloc_crypto_pages, + "Number of crypto pages to preallocate"); +module_param(num_prealloc_crypto_ctxs, uint, 0444); +MODULE_PARM_DESC(num_prealloc_crypto_ctxs, + "Number of crypto contexts to preallocate"); + +static mempool_t *llcrypt_bounce_page_pool = NULL; + +static LIST_HEAD(llcrypt_free_ctxs); +static DEFINE_SPINLOCK(llcrypt_ctx_lock); + +static struct workqueue_struct *llcrypt_read_workqueue; +static DEFINE_MUTEX(llcrypt_init_mutex); + +static struct kmem_cache *llcrypt_ctx_cachep; +struct kmem_cache *llcrypt_info_cachep; + +void llcrypt_enqueue_decrypt_work(struct work_struct *work) +{ + queue_work(llcrypt_read_workqueue, work); +} +EXPORT_SYMBOL(llcrypt_enqueue_decrypt_work); + +/** + * llcrypt_release_ctx() - Release a decryption context + * @ctx: The decryption context to release. + * + * If the decryption context was allocated from the pre-allocated pool, return + * it to that pool. Else, free it. + */ +void llcrypt_release_ctx(struct llcrypt_ctx *ctx) +{ + unsigned long flags; + + if (ctx->flags & FS_CTX_REQUIRES_FREE_ENCRYPT_FL) { + kmem_cache_free(llcrypt_ctx_cachep, ctx); + } else { + spin_lock_irqsave(&llcrypt_ctx_lock, flags); + list_add(&ctx->free_list, &llcrypt_free_ctxs); + spin_unlock_irqrestore(&llcrypt_ctx_lock, flags); + } +} +EXPORT_SYMBOL(llcrypt_release_ctx); + +/** + * llcrypt_get_ctx() - Get a decryption context + * @gfp_flags: The gfp flag for memory allocation + * + * Allocate and initialize a decryption context. + * + * Return: A new decryption context on success; an ERR_PTR() otherwise. + */ +struct llcrypt_ctx *llcrypt_get_ctx(gfp_t gfp_flags) +{ + struct llcrypt_ctx *ctx; + unsigned long flags; + + /* + * First try getting a ctx from the free list so that we don't have to + * call into the slab allocator. + */ + spin_lock_irqsave(&llcrypt_ctx_lock, flags); + ctx = list_first_entry_or_null(&llcrypt_free_ctxs, + struct llcrypt_ctx, free_list); + if (ctx) + list_del(&ctx->free_list); + spin_unlock_irqrestore(&llcrypt_ctx_lock, flags); + if (!ctx) { + ctx = kmem_cache_zalloc(llcrypt_ctx_cachep, gfp_flags); + if (!ctx) + return ERR_PTR(-ENOMEM); + ctx->flags |= FS_CTX_REQUIRES_FREE_ENCRYPT_FL; + } else { + ctx->flags &= ~FS_CTX_REQUIRES_FREE_ENCRYPT_FL; + } + return ctx; +} +EXPORT_SYMBOL(llcrypt_get_ctx); + +struct page *llcrypt_alloc_bounce_page(gfp_t gfp_flags) +{ + return mempool_alloc(llcrypt_bounce_page_pool, gfp_flags); +} + +/** + * llcrypt_free_bounce_page() - free a ciphertext bounce page + * + * Free a bounce page that was allocated by llcrypt_encrypt_pagecache_blocks(), + * or by llcrypt_alloc_bounce_page() directly. + */ +void llcrypt_free_bounce_page(struct page *bounce_page) +{ + if (!bounce_page) + return; + set_page_private(bounce_page, (unsigned long)NULL); + ClearPagePrivate(bounce_page); + mempool_free(bounce_page, llcrypt_bounce_page_pool); +} +EXPORT_SYMBOL(llcrypt_free_bounce_page); + +void llcrypt_generate_iv(union llcrypt_iv *iv, u64 lblk_num, + const struct llcrypt_info *ci) +{ + memset(iv, 0, ci->ci_mode->ivsize); + iv->lblk_num = cpu_to_le64(lblk_num); + + if (llcrypt_is_direct_key_policy(&ci->ci_policy)) + memcpy(iv->nonce, ci->ci_nonce, FS_KEY_DERIVATION_NONCE_SIZE); + + if (ci->ci_essiv_tfm != NULL) + crypto_cipher_encrypt_one(ci->ci_essiv_tfm, iv->raw, iv->raw); +} + +/* Encrypt or decrypt a single filesystem block of file contents */ +int llcrypt_crypt_block(const struct inode *inode, llcrypt_direction_t rw, + u64 lblk_num, struct page *src_page, + struct page *dest_page, unsigned int len, + unsigned int offs, gfp_t gfp_flags) +{ + union llcrypt_iv iv; + struct skcipher_request *req = NULL; + DECLARE_CRYPTO_WAIT(wait); + struct scatterlist dst, src; + struct llcrypt_info *ci = llcrypt_info(inode); + struct crypto_skcipher *tfm = ci->ci_ctfm; + int res = 0; + + if (WARN_ON_ONCE(len <= 0)) + return -EINVAL; + if (WARN_ON_ONCE(len % LL_CRYPTO_BLOCK_SIZE != 0)) + return -EINVAL; + + llcrypt_generate_iv(&iv, lblk_num, ci); + + req = skcipher_request_alloc(tfm, gfp_flags); + if (!req) + return -ENOMEM; + + skcipher_request_set_callback( + req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, + crypto_req_done, &wait); + + sg_init_table(&dst, 1); + sg_set_page(&dst, dest_page, len, offs); + sg_init_table(&src, 1); + sg_set_page(&src, src_page, len, offs); + skcipher_request_set_crypt(req, &src, &dst, len, &iv); + if (rw == FS_DECRYPT) + res = crypto_wait_req(crypto_skcipher_decrypt(req), &wait); + else + res = crypto_wait_req(crypto_skcipher_encrypt(req), &wait); + skcipher_request_free(req); + if (res) { + llcrypt_err(inode, "%scryption failed for block %llu: %d", + (rw == FS_DECRYPT ? "De" : "En"), lblk_num, res); + return res; + } + return 0; +} + +/** + * llcrypt_encrypt_pagecache_blocks() - Encrypt filesystem blocks from a pagecache page + * @page: The locked pagecache page containing the block(s) to encrypt + * @len: Total size of the block(s) to encrypt. Must be a nonzero + * multiple of the filesystem's block size. + * @offs: Byte offset within @page of the first block to encrypt. Must be + * a multiple of the filesystem's block size. + * @gfp_flags: Memory allocation flags + * + * A new bounce page is allocated, and the specified block(s) are encrypted into + * it. In the bounce page, the ciphertext block(s) will be located at the same + * offsets at which the plaintext block(s) were located in the source page; any + * other parts of the bounce page will be left uninitialized. However, normally + * blocksize == PAGE_SIZE and the whole page is encrypted at once. + * + * This is for use by the filesystem's ->writepages() method. + * + * Return: the new encrypted bounce page on success; an ERR_PTR() on failure + */ +struct page *llcrypt_encrypt_pagecache_blocks(struct page *page, + unsigned int len, + unsigned int offs, + gfp_t gfp_flags) + +{ + const struct inode *inode = page->mapping->host; + const unsigned int blockbits = inode->i_blkbits; + const unsigned int blocksize = 1 << blockbits; + struct page *ciphertext_page; + u64 lblk_num = ((u64)page->index << (PAGE_SHIFT - blockbits)) + + (offs >> blockbits); + unsigned int i; + int err; + + if (WARN_ON_ONCE(!PageLocked(page))) + return ERR_PTR(-EINVAL); + + if (WARN_ON_ONCE(len <= 0 || !IS_ALIGNED(len | offs, blocksize))) + return ERR_PTR(-EINVAL); + + ciphertext_page = llcrypt_alloc_bounce_page(gfp_flags); + if (!ciphertext_page) + return ERR_PTR(-ENOMEM); + + for (i = offs; i < offs + len; i += blocksize, lblk_num++) { + err = llcrypt_crypt_block(inode, FS_ENCRYPT, lblk_num, + page, ciphertext_page, + blocksize, i, gfp_flags); + if (err) { + llcrypt_free_bounce_page(ciphertext_page); + return ERR_PTR(err); + } + } + SetPagePrivate(ciphertext_page); + set_page_private(ciphertext_page, (unsigned long)page); + return ciphertext_page; +} +EXPORT_SYMBOL(llcrypt_encrypt_pagecache_blocks); + +/** + * llcrypt_encrypt_block_inplace() - Encrypt a filesystem block in-place + * @inode: The inode to which this block belongs + * @page: The page containing the block to encrypt + * @len: Size of block to encrypt. Doesn't need to be a multiple of the + * fs block size, but must be a multiple of LL_CRYPTO_BLOCK_SIZE. + * @offs: Byte offset within @page at which the block to encrypt begins + * @lblk_num: Filesystem logical block number of the block, i.e. the 0-based + * number of the block within the file + * @gfp_flags: Memory allocation flags + * + * Encrypt a possibly-compressed filesystem block that is located in an + * arbitrary page, not necessarily in the original pagecache page. The @inode + * and @lblk_num must be specified, as they can't be determined from @page. + * + * Return: 0 on success; -errno on failure + */ +int llcrypt_encrypt_block_inplace(const struct inode *inode, struct page *page, + unsigned int len, unsigned int offs, + u64 lblk_num, gfp_t gfp_flags) +{ + return llcrypt_crypt_block(inode, FS_ENCRYPT, lblk_num, page, page, + len, offs, gfp_flags); +} +EXPORT_SYMBOL(llcrypt_encrypt_block_inplace); + +/** + * llcrypt_decrypt_pagecache_blocks() - Decrypt filesystem blocks in a pagecache page + * @page: The locked pagecache page containing the block(s) to decrypt + * @len: Total size of the block(s) to decrypt. Must be a nonzero + * multiple of the filesystem's block size. + * @offs: Byte offset within @page of the first block to decrypt. Must be + * a multiple of the filesystem's block size. + * + * The specified block(s) are decrypted in-place within the pagecache page, + * which must still be locked and not uptodate. Normally, blocksize == + * PAGE_SIZE and the whole page is decrypted at once. + * + * This is for use by the filesystem's ->readpages() method. + * + * Return: 0 on success; -errno on failure + */ +int llcrypt_decrypt_pagecache_blocks(struct page *page, unsigned int len, + unsigned int offs) +{ + const struct inode *inode = page->mapping->host; + const unsigned int blockbits = inode->i_blkbits; + const unsigned int blocksize = 1 << blockbits; + u64 lblk_num = ((u64)page->index << (PAGE_SHIFT - blockbits)) + + (offs >> blockbits); + unsigned int i; + int err; + + if (WARN_ON_ONCE(!PageLocked(page))) + return -EINVAL; + + if (WARN_ON_ONCE(len <= 0 || !IS_ALIGNED(len | offs, blocksize))) + return -EINVAL; + + for (i = offs; i < offs + len; i += blocksize, lblk_num++) { + err = llcrypt_crypt_block(inode, FS_DECRYPT, lblk_num, page, + page, blocksize, i, GFP_NOFS); + if (err) + return err; + } + return 0; +} +EXPORT_SYMBOL(llcrypt_decrypt_pagecache_blocks); + +/** + * llcrypt_decrypt_block_inplace() - Decrypt a filesystem block in-place + * @inode: The inode to which this block belongs + * @page: The page containing the block to decrypt + * @len: Size of block to decrypt. Doesn't need to be a multiple of the + * fs block size, but must be a multiple of LL_CRYPTO_BLOCK_SIZE. + * @offs: Byte offset within @page at which the block to decrypt begins + * @lblk_num: Filesystem logical block number of the block, i.e. the 0-based + * number of the block within the file + * + * Decrypt a possibly-compressed filesystem block that is located in an + * arbitrary page, not necessarily in the original pagecache page. The @inode + * and @lblk_num must be specified, as they can't be determined from @page. + * + * Return: 0 on success; -errno on failure + */ +int llcrypt_decrypt_block_inplace(const struct inode *inode, struct page *page, + unsigned int len, unsigned int offs, + u64 lblk_num) +{ + return llcrypt_crypt_block(inode, FS_DECRYPT, lblk_num, page, page, + len, offs, GFP_NOFS); +} +EXPORT_SYMBOL(llcrypt_decrypt_block_inplace); + +/* + * Validate dentries in encrypted directories to make sure we aren't potentially + * caching stale dentries after a key has been added. + */ +static int llcrypt_d_revalidate(struct dentry *dentry, unsigned int flags) +{ + struct dentry *dir; + int err; + int valid; + + /* + * Plaintext names are always valid, since llcrypt doesn't support + * reverting to ciphertext names without evicting the directory's inode + * -- which implies eviction of the dentries in the directory. + */ + if (!(dentry->d_flags & DCACHE_ENCRYPTED_NAME)) + return 1; + + /* + * Ciphertext name; valid if the directory's key is still unavailable. + * + * Although llcrypt forbids rename() on ciphertext names, we still must + * use dget_parent() here rather than use ->d_parent directly. That's + * because a corrupted fs image may contain directory hard links, which + * the VFS handles by moving the directory's dentry tree in the dcache + * each time ->lookup() finds the directory and it already has a dentry + * elsewhere. Thus ->d_parent can be changing, and we must safely grab + * a reference to some ->d_parent to prevent it from being freed. + */ + + if (flags & LOOKUP_RCU) + return -ECHILD; + + dir = dget_parent(dentry); + err = llcrypt_get_encryption_info(d_inode(dir)); + valid = !llcrypt_has_encryption_key(d_inode(dir)); + dput(dir); + + if (err < 0) + return err; + + return valid; +} + +const struct dentry_operations llcrypt_d_ops = { + .d_revalidate = llcrypt_d_revalidate, +}; + +static void llcrypt_destroy(void) +{ + struct llcrypt_ctx *pos, *n; + + list_for_each_entry_safe(pos, n, &llcrypt_free_ctxs, free_list) + kmem_cache_free(llcrypt_ctx_cachep, pos); + INIT_LIST_HEAD(&llcrypt_free_ctxs); + mempool_destroy(llcrypt_bounce_page_pool); + llcrypt_bounce_page_pool = NULL; +} + +/** + * llcrypt_initialize() - allocate major buffers for fs encryption. + * @cop_flags: llcrypt operations flags + * + * We only call this when we start accessing encrypted files, since it + * results in memory getting allocated that wouldn't otherwise be used. + * + * Return: Zero on success, non-zero otherwise. + */ +int llcrypt_initialize(unsigned int cop_flags) +{ + int i, res = -ENOMEM; + + /* No need to allocate a bounce page pool if this FS won't use it. */ + if (cop_flags & LL_CFLG_OWN_PAGES) + return 0; + + mutex_lock(&llcrypt_init_mutex); + if (llcrypt_bounce_page_pool) + goto already_initialized; + + for (i = 0; i < num_prealloc_crypto_ctxs; i++) { + struct llcrypt_ctx *ctx; + + ctx = kmem_cache_zalloc(llcrypt_ctx_cachep, GFP_NOFS); + if (!ctx) + goto fail; + list_add(&ctx->free_list, &llcrypt_free_ctxs); + } + + llcrypt_bounce_page_pool = + mempool_create_page_pool(num_prealloc_crypto_pages, 0); + if (!llcrypt_bounce_page_pool) + goto fail; + +already_initialized: + mutex_unlock(&llcrypt_init_mutex); + return 0; +fail: + llcrypt_destroy(); + mutex_unlock(&llcrypt_init_mutex); + return res; +} + +void llcrypt_msg(const struct inode *inode, int mask, + const char *fmt, ...) +{ + static DEFINE_RATELIMIT_STATE(rs, DEFAULT_RATELIMIT_INTERVAL, + DEFAULT_RATELIMIT_BURST); + struct va_format vaf; + va_list args; + + if (!__ratelimit(&rs)) + return; + + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + if (inode) + CDEBUG(mask, "llcrypt (%s, inode %lu): %pV\n", + inode->i_sb->s_id, inode->i_ino, &vaf); + else + CDEBUG(mask, "llcrypt: %pV\n", &vaf); + va_end(args); +} + +/** + * llcrypt_init() - Set up for fs encryption. + */ +int __init llcrypt_init(void) +{ + int err = -ENOMEM; + + /* + * Use an unbound workqueue to allow bios to be decrypted in parallel + * even when they happen to complete on the same CPU. This sacrifices + * locality, but it's worthwhile since decryption is CPU-intensive. + * + * Also use a high-priority workqueue to prioritize decryption work, + * which blocks reads from completing, over regular application tasks. + */ + llcrypt_read_workqueue = alloc_workqueue("llcrypt_read_queue", + WQ_UNBOUND | WQ_HIGHPRI, + num_online_cpus()); + if (!llcrypt_read_workqueue) + goto fail; + + llcrypt_ctx_cachep = KMEM_CACHE(llcrypt_ctx, SLAB_RECLAIM_ACCOUNT); + if (!llcrypt_ctx_cachep) + goto fail_free_queue; + + llcrypt_info_cachep = KMEM_CACHE(llcrypt_info, SLAB_RECLAIM_ACCOUNT); + if (!llcrypt_info_cachep) + goto fail_free_ctx; + + err = llcrypt_init_keyring(); + if (err) + goto fail_free_info; + + return 0; + +fail_free_info: + kmem_cache_destroy(llcrypt_info_cachep); +fail_free_ctx: + kmem_cache_destroy(llcrypt_ctx_cachep); +fail_free_queue: + destroy_workqueue(llcrypt_read_workqueue); +fail: + return err; +} + +/** + * llcrypt_exit() - Clean up for fs encryption. + */ +void __exit llcrypt_exit(void) +{ + llcrypt_exit_keyring(); + + llcrypt_destroy(); + /* + * Make sure all delayed rcu free inodes are flushed before we + * destroy cache. + */ + rcu_barrier(); + + kmem_cache_destroy(llcrypt_info_cachep); + kmem_cache_destroy(llcrypt_ctx_cachep); + destroy_workqueue(llcrypt_read_workqueue); +} diff --git a/libcfs/libcfs/crypto/fname.c b/libcfs/libcfs/crypto/fname.c new file mode 100644 index 0000000..08accf8 --- /dev/null +++ b/libcfs/libcfs/crypto/fname.c @@ -0,0 +1,410 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This contains functions for filename crypto management + * + * Copyright (C) 2015, Google, Inc. + * Copyright (C) 2015, Motorola Mobility + * + * Written by Uday Savagaonkar, 2014. + * Modified by Jaegeuk Kim, 2015. + * + * This has not yet undergone a rigorous security audit. + */ +/* + * Linux commit 219d54332a09 + * tags/v5.4 + */ + +#include +#include +#include "llcrypt_private.h" + +static inline bool llcrypt_is_dot_dotdot(const struct qstr *str) +{ + if (str->len == 1 && str->name[0] == '.') + return true; + + if (str->len == 2 && str->name[0] == '.' && str->name[1] == '.') + return true; + + return false; +} + +/** + * fname_encrypt() - encrypt a filename + * + * The output buffer must be at least as large as the input buffer. + * Any extra space is filled with NUL padding before encryption. + * + * Return: 0 on success, -errno on failure + */ +int fname_encrypt(struct inode *inode, const struct qstr *iname, + u8 *out, unsigned int olen) +{ + struct skcipher_request *req = NULL; + DECLARE_CRYPTO_WAIT(wait); + struct llcrypt_info *ci = llcrypt_info(inode); + struct crypto_skcipher *tfm = ci->ci_ctfm; + union llcrypt_iv iv; + struct scatterlist sg; + int res; + + /* + * Copy the filename to the output buffer for encrypting in-place and + * pad it with the needed number of NUL bytes. + */ + if (WARN_ON(olen < iname->len)) + return -ENOBUFS; + memcpy(out, iname->name, iname->len); + memset(out + iname->len, 0, olen - iname->len); + + /* Initialize the IV */ + llcrypt_generate_iv(&iv, 0, ci); + + /* Set up the encryption request */ + req = skcipher_request_alloc(tfm, GFP_NOFS); + if (!req) + return -ENOMEM; + skcipher_request_set_callback(req, + CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, + crypto_req_done, &wait); + sg_init_one(&sg, out, olen); + skcipher_request_set_crypt(req, &sg, &sg, olen, &iv); + + /* Do the encryption */ + res = crypto_wait_req(crypto_skcipher_encrypt(req), &wait); + skcipher_request_free(req); + if (res < 0) { + llcrypt_err(inode, "Filename encryption failed: %d", res); + return res; + } + + return 0; +} + +/** + * fname_decrypt() - decrypt a filename + * + * The caller must have allocated sufficient memory for the @oname string. + * + * Return: 0 on success, -errno on failure + */ +static int fname_decrypt(struct inode *inode, + const struct llcrypt_str *iname, + struct llcrypt_str *oname) +{ + struct skcipher_request *req = NULL; + DECLARE_CRYPTO_WAIT(wait); + struct scatterlist src_sg, dst_sg; + struct llcrypt_info *ci = llcrypt_info(inode); + struct crypto_skcipher *tfm = ci->ci_ctfm; + union llcrypt_iv iv; + int res; + + /* Allocate request */ + req = skcipher_request_alloc(tfm, GFP_NOFS); + if (!req) + return -ENOMEM; + skcipher_request_set_callback(req, + CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, + crypto_req_done, &wait); + + /* Initialize IV */ + llcrypt_generate_iv(&iv, 0, ci); + + /* Create decryption request */ + sg_init_one(&src_sg, iname->name, iname->len); + sg_init_one(&dst_sg, oname->name, oname->len); + skcipher_request_set_crypt(req, &src_sg, &dst_sg, iname->len, &iv); + res = crypto_wait_req(crypto_skcipher_decrypt(req), &wait); + skcipher_request_free(req); + if (res < 0) { + llcrypt_err(inode, "Filename decryption failed: %d", res); + return res; + } + + oname->len = strnlen(oname->name, iname->len); + return 0; +} + +static const char lookup_table[65] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,"; + +#define BASE64_CHARS(nbytes) DIV_ROUND_UP((nbytes) * 4, 3) + +/** + * base64_encode() - + * + * Encodes the input string using characters from the set [A-Za-z0-9+,]. + * The encoded string is roughly 4/3 times the size of the input string. + * + * Return: length of the encoded string + */ +static int base64_encode(const u8 *src, int len, char *dst) +{ + int i, bits = 0, ac = 0; + char *cp = dst; + + for (i = 0; i < len; i++) { + ac += src[i] << bits; + bits += 8; + do { + *cp++ = lookup_table[ac & 0x3f]; + ac >>= 6; + bits -= 6; + } while (bits >= 6); + } + if (bits) + *cp++ = lookup_table[ac & 0x3f]; + return cp - dst; +} + +static int base64_decode(const char *src, int len, u8 *dst) +{ + int i, bits = 0, ac = 0; + const char *p; + u8 *cp = dst; + + for (i = 0; i < len; i++) { + p = strchr(lookup_table, src[i]); + if (p == NULL || src[i] == 0) + return -2; + ac += (p - lookup_table) << bits; + bits += 6; + if (bits >= 8) { + *cp++ = ac & 0xff; + ac >>= 8; + bits -= 8; + } + } + if (ac) + return -1; + return cp - dst; +} + +bool llcrypt_fname_encrypted_size(const struct inode *inode, u32 orig_len, + u32 max_len, u32 *encrypted_len_ret) +{ + const struct llcrypt_info *ci = llcrypt_info(inode); + int padding = 4 << (llcrypt_policy_flags(&ci->ci_policy) & + LLCRYPT_POLICY_FLAGS_PAD_MASK); + u32 encrypted_len; + + if (orig_len > max_len) + return false; + encrypted_len = max(orig_len, (u32)LL_CRYPTO_BLOCK_SIZE); + encrypted_len = round_up(encrypted_len, padding); + *encrypted_len_ret = min(encrypted_len, max_len); + return true; +} + +/** + * llcrypt_fname_alloc_buffer - allocate a buffer for presented filenames + * + * Allocate a buffer that is large enough to hold any decrypted or encoded + * filename (null-terminated), for the given maximum encrypted filename length. + * + * Return: 0 on success, -errno on failure + */ +int llcrypt_fname_alloc_buffer(const struct inode *inode, + u32 max_encrypted_len, + struct llcrypt_str *crypto_str) +{ + const u32 max_encoded_len = + max_t(u32, BASE64_CHARS(LLCRYPT_FNAME_MAX_UNDIGESTED_SIZE), + 1 + BASE64_CHARS(sizeof(struct llcrypt_digested_name))); + u32 max_presented_len; + + max_presented_len = max(max_encoded_len, max_encrypted_len); + + crypto_str->name = kmalloc(max_presented_len + 1, GFP_NOFS); + if (!crypto_str->name) + return -ENOMEM; + crypto_str->len = max_presented_len; + return 0; +} +EXPORT_SYMBOL(llcrypt_fname_alloc_buffer); + +/** + * llcrypt_fname_free_buffer - free the buffer for presented filenames + * + * Free the buffer allocated by llcrypt_fname_alloc_buffer(). + */ +void llcrypt_fname_free_buffer(struct llcrypt_str *crypto_str) +{ + if (!crypto_str) + return; + kfree(crypto_str->name); + crypto_str->name = NULL; +} +EXPORT_SYMBOL(llcrypt_fname_free_buffer); + +/** + * llcrypt_fname_disk_to_usr() - converts a filename from disk space to user + * space + * + * The caller must have allocated sufficient memory for the @oname string. + * + * If the key is available, we'll decrypt the disk name; otherwise, we'll encode + * it for presentation. Short names are directly base64-encoded, while long + * names are encoded in llcrypt_digested_name format. + * + * Return: 0 on success, -errno on failure + */ +int llcrypt_fname_disk_to_usr(struct inode *inode, + u32 hash, u32 minor_hash, + const struct llcrypt_str *iname, + struct llcrypt_str *oname) +{ + const struct qstr qname = LLTR_TO_QSTR(iname); + struct llcrypt_digested_name digested_name; + + if (llcrypt_is_dot_dotdot(&qname)) { + oname->name[0] = '.'; + oname->name[iname->len - 1] = '.'; + oname->len = iname->len; + return 0; + } + + if (iname->len < LL_CRYPTO_BLOCK_SIZE) + return -EUCLEAN; + + if (llcrypt_has_encryption_key(inode)) + return fname_decrypt(inode, iname, oname); + + if (iname->len <= LLCRYPT_FNAME_MAX_UNDIGESTED_SIZE) { + oname->len = base64_encode(iname->name, iname->len, + oname->name); + return 0; + } + if (hash) { + digested_name.hash = hash; + digested_name.minor_hash = minor_hash; + } else { + digested_name.hash = 0; + digested_name.minor_hash = 0; + } + memcpy(digested_name.digest, + LLCRYPT_FNAME_DIGEST(iname->name, iname->len), + LLCRYPT_FNAME_DIGEST_SIZE); + oname->name[0] = '_'; + oname->len = 1 + base64_encode((const u8 *)&digested_name, + sizeof(digested_name), oname->name + 1); + return 0; +} +EXPORT_SYMBOL(llcrypt_fname_disk_to_usr); + +/** + * llcrypt_setup_filename() - prepare to search a possibly encrypted directory + * @dir: the directory that will be searched + * @iname: the user-provided filename being searched for + * @lookup: 1 if we're allowed to proceed without the key because it's + * ->lookup() or we're finding the dir_entry for deletion; 0 if we cannot + * proceed without the key because we're going to create the dir_entry. + * @fname: the filename information to be filled in + * + * Given a user-provided filename @iname, this function sets @fname->disk_name + * to the name that would be stored in the on-disk directory entry, if possible. + * If the directory is unencrypted this is simply @iname. Else, if we have the + * directory's encryption key, then @iname is the plaintext, so we encrypt it to + * get the disk_name. + * + * Else, for keyless @lookup operations, @iname is the presented ciphertext, so + * we decode it to get either the ciphertext disk_name (for short names) or the + * llcrypt_digested_name (for long names). Non-@lookup operations will be + * impossible in this case, so we fail them with ENOKEY. + * + * If successful, llcrypt_free_filename() must be called later to clean up. + * + * Return: 0 on success, -errno on failure + */ +int llcrypt_setup_filename(struct inode *dir, const struct qstr *iname, + int lookup, struct llcrypt_name *fname) +{ + int ret; + int digested; + + memset(fname, 0, sizeof(struct llcrypt_name)); + fname->usr_fname = iname; + + if (!IS_ENCRYPTED(dir) || llcrypt_is_dot_dotdot(iname)) { + fname->disk_name.name = (unsigned char *)iname->name; + fname->disk_name.len = iname->len; + return 0; + } + ret = llcrypt_get_encryption_info(dir); + if (ret) + return ret; + + if (llcrypt_has_encryption_key(dir)) { + struct lustre_sb_info *lsi = s2lsi(dir->i_sb); + + if (!llcrypt_fname_encrypted_size(dir, iname->len, + lsi ? + lsi->lsi_cop->max_namelen : + NAME_MAX, + &fname->crypto_buf.len)) + return -ENAMETOOLONG; + fname->crypto_buf.name = kmalloc(fname->crypto_buf.len, + GFP_NOFS); + if (!fname->crypto_buf.name) + return -ENOMEM; + + ret = fname_encrypt(dir, iname, fname->crypto_buf.name, + fname->crypto_buf.len); + if (ret) + goto errout; + fname->disk_name.name = fname->crypto_buf.name; + fname->disk_name.len = fname->crypto_buf.len; + return 0; + } + if (!lookup) + return -ENOKEY; + fname->is_ciphertext_name = true; + + /* + * We don't have the key and we are doing a lookup; decode the + * user-supplied name + */ + if (iname->name[0] == '_') { + if (iname->len != + 1 + BASE64_CHARS(sizeof(struct llcrypt_digested_name))) + return -ENOENT; + digested = 1; + } else { + if (iname->len > + BASE64_CHARS(LLCRYPT_FNAME_MAX_UNDIGESTED_SIZE)) + return -ENOENT; + digested = 0; + } + + fname->crypto_buf.name = + kmalloc(max_t(size_t, LLCRYPT_FNAME_MAX_UNDIGESTED_SIZE, + sizeof(struct llcrypt_digested_name)), + GFP_KERNEL); + if (fname->crypto_buf.name == NULL) + return -ENOMEM; + + ret = base64_decode(iname->name + digested, iname->len - digested, + fname->crypto_buf.name); + if (ret < 0) { + ret = -ENOENT; + goto errout; + } + fname->crypto_buf.len = ret; + if (digested) { + const struct llcrypt_digested_name *n = + (const void *)fname->crypto_buf.name; + fname->hash = n->hash; + fname->minor_hash = n->minor_hash; + } else { + fname->disk_name.name = fname->crypto_buf.name; + fname->disk_name.len = fname->crypto_buf.len; + } + return 0; + +errout: + kfree(fname->crypto_buf.name); + return ret; +} +EXPORT_SYMBOL(llcrypt_setup_filename); diff --git a/libcfs/libcfs/crypto/hkdf.c b/libcfs/libcfs/crypto/hkdf.c new file mode 100644 index 0000000..147f14b --- /dev/null +++ b/libcfs/libcfs/crypto/hkdf.c @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Implementation of HKDF ("HMAC-based Extract-and-Expand Key Derivation + * Function"), aka RFC 5869. See also the original paper (Krawczyk 2010): + * "Cryptographic Extraction and Key Derivation: The HKDF Scheme". + * + * This is used to derive keys from the llcrypt master keys. + * + * Copyright 2019 Google LLC + */ +/* + * Linux commit 219d54332a09 + * tags/v5.4 + */ + +#include +#include + +#include "llcrypt_private.h" + +/* + * HKDF supports any unkeyed cryptographic hash algorithm, but llcrypt uses + * SHA-512 because it is reasonably secure and efficient; and since it produces + * a 64-byte digest, deriving an AES-256-XTS key preserves all 64 bytes of + * entropy from the master key and requires only one iteration of HKDF-Expand. + */ +#define HKDF_HMAC_ALG "hmac(sha512)" +#define HKDF_HASHLEN SHA512_DIGEST_SIZE + +/* + * HKDF consists of two steps: + * + * 1. HKDF-Extract: extract a pseudorandom key of length HKDF_HASHLEN bytes from + * the input keying material and optional salt. + * 2. HKDF-Expand: expand the pseudorandom key into output keying material of + * any length, parameterized by an application-specific info string. + * + * HKDF-Extract can be skipped if the input is already a pseudorandom key of + * length HKDF_HASHLEN bytes. However, cipher modes other than AES-256-XTS take + * shorter keys, and we don't want to force users of those modes to provide + * unnecessarily long master keys. Thus llcrypt still does HKDF-Extract. No + * salt is used, since llcrypt master keys should already be pseudorandom and + * there's no way to persist a random salt per master key from kernel mode. + */ + +/* HKDF-Extract (RFC 5869 section 2.2), unsalted */ +static int hkdf_extract(struct crypto_shash *hmac_tfm, const u8 *ikm, + unsigned int ikmlen, u8 prk[HKDF_HASHLEN]) +{ + static const u8 default_salt[HKDF_HASHLEN]; + SHASH_DESC_ON_STACK(desc, hmac_tfm); + int err; + + err = crypto_shash_setkey(hmac_tfm, default_salt, HKDF_HASHLEN); + if (err) + return err; + + desc->tfm = hmac_tfm; + err = crypto_shash_digest(desc, ikm, ikmlen, prk); + shash_desc_zero(desc); + return err; +} + +/* + * Compute HKDF-Extract using the given master key as the input keying material, + * and prepare an HMAC transform object keyed by the resulting pseudorandom key. + * + * Afterwards, the keyed HMAC transform object can be used for HKDF-Expand many + * times without having to recompute HKDF-Extract each time. + */ +int llcrypt_init_hkdf(struct llcrypt_hkdf *hkdf, const u8 *master_key, + unsigned int master_key_size) +{ + struct crypto_shash *hmac_tfm; + u8 prk[HKDF_HASHLEN]; + int err; + + hmac_tfm = crypto_alloc_shash(HKDF_HMAC_ALG, 0, 0); + if (IS_ERR(hmac_tfm)) { + llcrypt_err(NULL, "Error allocating " HKDF_HMAC_ALG ": %ld", + PTR_ERR(hmac_tfm)); + return PTR_ERR(hmac_tfm); + } + + if (WARN_ON(crypto_shash_digestsize(hmac_tfm) != sizeof(prk))) { + err = -EINVAL; + goto err_free_tfm; + } + + err = hkdf_extract(hmac_tfm, master_key, master_key_size, prk); + if (err) + goto err_free_tfm; + + err = crypto_shash_setkey(hmac_tfm, prk, sizeof(prk)); + if (err) + goto err_free_tfm; + + hkdf->hmac_tfm = hmac_tfm; + goto out; + +err_free_tfm: + crypto_free_shash(hmac_tfm); +out: + memzero_explicit(prk, sizeof(prk)); + return err; +} + +/* + * HKDF-Expand (RFC 5869 section 2.3). This expands the pseudorandom key, which + * was already keyed into 'hkdf->hmac_tfm' by llcrypt_init_hkdf(), into 'okmlen' + * bytes of output keying material parameterized by the application-specific + * 'info' of length 'infolen' bytes, prefixed by "llcrypt\0" and the 'context' + * byte. This is thread-safe and may be called by multiple threads in parallel. + * + * ('context' isn't part of the HKDF specification; it's just a prefix llcrypt + * adds to its application-specific info strings to guarantee that it doesn't + * accidentally repeat an info string when using HKDF for different purposes.) + */ +int llcrypt_hkdf_expand(struct llcrypt_hkdf *hkdf, u8 context, + const u8 *info, unsigned int infolen, + u8 *okm, unsigned int okmlen) +{ + SHASH_DESC_ON_STACK(desc, hkdf->hmac_tfm); + u8 prefix[9]; + unsigned int i; + int err; + const u8 *prev = NULL; + u8 counter = 1; + u8 tmp[HKDF_HASHLEN]; + + if (WARN_ON(okmlen > 255 * HKDF_HASHLEN)) + return -EINVAL; + + desc->tfm = hkdf->hmac_tfm; + + memcpy(prefix, "fscrypt\0", 8); + prefix[8] = context; + + for (i = 0; i < okmlen; i += HKDF_HASHLEN) { + + err = crypto_shash_init(desc); + if (err) + goto out; + + if (prev) { + err = crypto_shash_update(desc, prev, HKDF_HASHLEN); + if (err) + goto out; + } + + err = crypto_shash_update(desc, prefix, sizeof(prefix)); + if (err) + goto out; + + err = crypto_shash_update(desc, info, infolen); + if (err) + goto out; + + BUILD_BUG_ON(sizeof(counter) != 1); + if (okmlen - i < HKDF_HASHLEN) { + err = crypto_shash_finup(desc, &counter, 1, tmp); + if (err) + goto out; + memcpy(&okm[i], tmp, okmlen - i); + memzero_explicit(tmp, sizeof(tmp)); + } else { + err = crypto_shash_finup(desc, &counter, 1, &okm[i]); + if (err) + goto out; + } + counter++; + prev = &okm[i]; + } + err = 0; +out: + if (unlikely(err)) + memzero_explicit(okm, okmlen); /* so caller doesn't need to */ + shash_desc_zero(desc); + return err; +} + +void llcrypt_destroy_hkdf(struct llcrypt_hkdf *hkdf) +{ + crypto_free_shash(hkdf->hmac_tfm); +} diff --git a/libcfs/libcfs/crypto/hooks.c b/libcfs/libcfs/crypto/hooks.c new file mode 100644 index 0000000..10a837f --- /dev/null +++ b/libcfs/libcfs/crypto/hooks.c @@ -0,0 +1,311 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * fs/crypto/hooks.c + * + * Encryption hooks for higher-level filesystem operations. + */ +/* + * Linux commit 219d54332a09 + * tags/v5.4 + */ + +#include "llcrypt_private.h" + +/** + * llcrypt_file_open - prepare to open a possibly-encrypted regular file + * @inode: the inode being opened + * @filp: the struct file being set up + * + * Currently, an encrypted regular file can only be opened if its encryption key + * is available; access to the raw encrypted contents is not supported. + * Therefore, we first set up the inode's encryption key (if not already done) + * and return an error if it's unavailable. + * + * We also verify that if the parent directory (from the path via which the file + * is being opened) is encrypted, then the inode being opened uses the same + * encryption policy. This is needed as part of the enforcement that all files + * in an encrypted directory tree use the same encryption policy, as a + * protection against certain types of offline attacks. Note that this check is + * needed even when opening an *unencrypted* file, since it's forbidden to have + * an unencrypted file in an encrypted directory. + * + * Return: 0 on success, -ENOKEY if the key is missing, or another -errno code + */ +int llcrypt_file_open(struct inode *inode, struct file *filp) +{ + int err; + struct dentry *dir; + + err = llcrypt_require_key(inode); + if (err) + return err; + + dir = dget_parent(file_dentry(filp)); + if (IS_ENCRYPTED(d_inode(dir)) && + !llcrypt_has_permitted_context(d_inode(dir), inode)) { + llcrypt_warn(inode, + "Inconsistent encryption context (parent directory: %lu)", + d_inode(dir)->i_ino); + err = -EPERM; + } + dput(dir); + return err; +} +EXPORT_SYMBOL_GPL(llcrypt_file_open); + +int __llcrypt_prepare_link(struct inode *inode, struct inode *dir, + struct dentry *dentry) +{ + int err; + + err = llcrypt_require_key(dir); + if (err) + return err; + + /* ... in case we looked up ciphertext name before key was added */ + if (dentry->d_flags & DCACHE_ENCRYPTED_NAME) + return -ENOKEY; + + if (!llcrypt_has_permitted_context(dir, inode)) + return -EXDEV; + + return 0; +} +EXPORT_SYMBOL_GPL(__llcrypt_prepare_link); + +int __llcrypt_prepare_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry, + unsigned int flags) +{ + int err; + + err = llcrypt_require_key(old_dir); + if (err) + return err; + + err = llcrypt_require_key(new_dir); + if (err) + return err; + + /* ... in case we looked up ciphertext name(s) before key was added */ + if ((old_dentry->d_flags | new_dentry->d_flags) & + DCACHE_ENCRYPTED_NAME) + return -ENOKEY; + + if (old_dir != new_dir) { + if (IS_ENCRYPTED(new_dir) && + !llcrypt_has_permitted_context(new_dir, + d_inode(old_dentry))) + return -EXDEV; + + if ((flags & RENAME_EXCHANGE) && + IS_ENCRYPTED(old_dir) && + !llcrypt_has_permitted_context(old_dir, + d_inode(new_dentry))) + return -EXDEV; + } + return 0; +} +EXPORT_SYMBOL_GPL(__llcrypt_prepare_rename); + +int __llcrypt_prepare_lookup(struct inode *dir, struct dentry *dentry, + struct llcrypt_name *fname) +{ + int err = llcrypt_setup_filename(dir, &dentry->d_name, 1, fname); + + if (err && err != -ENOENT) + return err; + + if (fname->is_ciphertext_name) { + spin_lock(&dentry->d_lock); + dentry->d_flags |= DCACHE_ENCRYPTED_NAME; + spin_unlock(&dentry->d_lock); + d_set_d_op(dentry, &llcrypt_d_ops); + } + return err; +} +EXPORT_SYMBOL_GPL(__llcrypt_prepare_lookup); + +int __llcrypt_prepare_symlink(struct inode *dir, unsigned int len, + unsigned int max_len, + struct llcrypt_str *disk_link) +{ + int err; + + /* + * To calculate the size of the encrypted symlink target we need to know + * the amount of NUL padding, which is determined by the flags set in + * the encryption policy which will be inherited from the directory. + * The easiest way to get access to this is to just load the directory's + * llcrypt_info, since we'll need it to create the dir_entry anyway. + * + * Note: in test_dummy_encryption mode, @dir may be unencrypted. + */ + err = llcrypt_get_encryption_info(dir); + if (err) + return err; + if (!llcrypt_has_encryption_key(dir)) + return -ENOKEY; + + /* + * Calculate the size of the encrypted symlink and verify it won't + * exceed max_len. Note that for historical reasons, encrypted symlink + * targets are prefixed with the ciphertext length, despite this + * actually being redundant with i_size. This decreases by 2 bytes the + * longest symlink target we can accept. + * + * We could recover 1 byte by not counting a null terminator, but + * counting it (even though it is meaningless for ciphertext) is simpler + * for now since filesystems will assume it is there and subtract it. + */ + if (!llcrypt_fname_encrypted_size(dir, len, + max_len - sizeof(struct llcrypt_symlink_data), + &disk_link->len)) + return -ENAMETOOLONG; + disk_link->len += sizeof(struct llcrypt_symlink_data); + + disk_link->name = NULL; + return 0; +} +EXPORT_SYMBOL_GPL(__llcrypt_prepare_symlink); + +int __llcrypt_encrypt_symlink(struct inode *inode, const char *target, + unsigned int len, struct llcrypt_str *disk_link) +{ + int err; + struct qstr iname = QSTR_INIT(target, len); + struct llcrypt_symlink_data *sd; + unsigned int ciphertext_len; + + err = llcrypt_require_key(inode); + if (err) + return err; + + if (disk_link->name) { + /* filesystem-provided buffer */ + sd = (struct llcrypt_symlink_data *)disk_link->name; + } else { + sd = kmalloc(disk_link->len, GFP_NOFS); + if (!sd) + return -ENOMEM; + } + ciphertext_len = disk_link->len - sizeof(*sd); + sd->len = cpu_to_le16(ciphertext_len); + + err = fname_encrypt(inode, &iname, sd->encrypted_path, ciphertext_len); + if (err) + goto err_free_sd; + + /* + * Null-terminating the ciphertext doesn't make sense, but we still + * count the null terminator in the length, so we might as well + * initialize it just in case the filesystem writes it out. + */ + sd->encrypted_path[ciphertext_len] = '\0'; + + /* Cache the plaintext symlink target for later use by get_link() */ + err = -ENOMEM; + inode->i_link = kmemdup(target, len + 1, GFP_NOFS); + if (!inode->i_link) + goto err_free_sd; + + if (!disk_link->name) + disk_link->name = (unsigned char *)sd; + return 0; + +err_free_sd: + if (!disk_link->name) + kfree(sd); + return err; +} +EXPORT_SYMBOL_GPL(__llcrypt_encrypt_symlink); + +/** + * llcrypt_get_symlink - get the target of an encrypted symlink + * @inode: the symlink inode + * @caddr: the on-disk contents of the symlink + * @max_size: size of @caddr buffer + * @done: if successful, will be set up to free the returned target if needed + * + * If the symlink's encryption key is available, we decrypt its target. + * Otherwise, we encode its target for presentation. + * + * This may sleep, so the filesystem must have dropped out of RCU mode already. + * + * Return: the presentable symlink target or an ERR_PTR() + */ +const char *llcrypt_get_symlink(struct inode *inode, const void *caddr, + unsigned int max_size, + struct delayed_call *done) +{ + const struct llcrypt_symlink_data *sd; + struct llcrypt_str cstr, pstr; + bool has_key; + int err; + + /* This is for encrypted symlinks only */ + if (WARN_ON(!IS_ENCRYPTED(inode))) + return ERR_PTR(-EINVAL); + + /* If the decrypted target is already cached, just return it. */ + pstr.name = READ_ONCE(inode->i_link); + if (pstr.name) + return pstr.name; + + /* + * Try to set up the symlink's encryption key, but we can continue + * regardless of whether the key is available or not. + */ + err = llcrypt_get_encryption_info(inode); + if (err) + return ERR_PTR(err); + has_key = llcrypt_has_encryption_key(inode); + + /* + * For historical reasons, encrypted symlink targets are prefixed with + * the ciphertext length, even though this is redundant with i_size. + */ + + if (max_size < sizeof(*sd)) + return ERR_PTR(-EUCLEAN); + sd = caddr; + cstr.name = (unsigned char *)sd->encrypted_path; + cstr.len = le16_to_cpu(sd->len); + + if (cstr.len == 0) + return ERR_PTR(-EUCLEAN); + + if (cstr.len + sizeof(*sd) - 1 > max_size) + return ERR_PTR(-EUCLEAN); + + err = llcrypt_fname_alloc_buffer(inode, cstr.len, &pstr); + if (err) + return ERR_PTR(err); + + err = llcrypt_fname_disk_to_usr(inode, 0, 0, &cstr, &pstr); + if (err) + goto err_kfree; + + err = -EUCLEAN; + if (pstr.name[0] == '\0') + goto err_kfree; + + pstr.name[pstr.len] = '\0'; + + /* + * Cache decrypted symlink targets in i_link for later use. Don't cache + * symlink targets encoded without the key, since those become outdated + * once the key is added. This pairs with the READ_ONCE() above and in + * the VFS path lookup code. + */ + if (!has_key || + cmpxchg_release(&inode->i_link, NULL, pstr.name) != NULL) + set_delayed_call(done, kfree_link, pstr.name); + + return pstr.name; + +err_kfree: + kfree(pstr.name); + return ERR_PTR(err); +} +EXPORT_SYMBOL_GPL(llcrypt_get_symlink); diff --git a/libcfs/libcfs/crypto/keyring.c b/libcfs/libcfs/crypto/keyring.c new file mode 100644 index 0000000..a9dde26 --- /dev/null +++ b/libcfs/libcfs/crypto/keyring.c @@ -0,0 +1,1011 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Filesystem-level keyring for llcrypt + * + * Copyright 2019 Google LLC + */ + +/* + * This file implements management of llcrypt master keys in the + * filesystem-level keyring, including the ioctls: + * + * - LL_IOC_ADD_ENCRYPTION_KEY + * - LL_IOC_REMOVE_ENCRYPTION_KEY + * - LL_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS + * - LL_IOC_GET_ENCRYPTION_KEY_STATUS + * + * See the "User API" section of Documentation/filesystems/llcrypt.rst for more + * information about these ioctls. + */ +/* + * Linux commit 219d54332a09 + * tags/v5.4 + */ + +#include +#include +#include + +#include "llcrypt_private.h" + +static void wipe_master_key_secret(struct llcrypt_master_key_secret *secret) +{ + llcrypt_destroy_hkdf(&secret->hkdf); + memzero_explicit(secret, sizeof(*secret)); +} + +static void move_master_key_secret(struct llcrypt_master_key_secret *dst, + struct llcrypt_master_key_secret *src) +{ + memcpy(dst, src, sizeof(*dst)); + memzero_explicit(src, sizeof(*src)); +} + +static void free_master_key(struct llcrypt_master_key *mk) +{ + size_t i; + + wipe_master_key_secret(&mk->mk_secret); + + for (i = 0; i < ARRAY_SIZE(mk->mk_mode_keys); i++) + crypto_free_skcipher(mk->mk_mode_keys[i]); + + key_put(mk->mk_users); + kzfree(mk); +} + +static inline bool valid_key_spec(const struct llcrypt_key_specifier *spec) +{ + if (spec->__reserved) + return false; + return master_key_spec_len(spec) != 0; +} + +static int llcrypt_key_instantiate(struct key *key, + struct key_preparsed_payload *prep) +{ + key->payload.data[0] = (struct llcrypt_master_key *)prep->data; + return 0; +} + +static void llcrypt_key_destroy(struct key *key) +{ + free_master_key(key->payload.data[0]); +} + +static void llcrypt_key_describe(const struct key *key, struct seq_file *m) +{ + seq_puts(m, key->description); + + if (key_is_positive(key)) { + const struct llcrypt_master_key *mk = key->payload.data[0]; + + if (!is_master_key_secret_present(&mk->mk_secret)) + seq_puts(m, ": secret removed"); + } +} + +/* + * Type of key in ->lsi_master_keys. Each key of this type represents a master + * key which has been added to the filesystem. Its payload is a + * 'struct llcrypt_master_key'. The "." prefix in the key type name prevents + * users from adding keys of this type via the keyrings syscalls rather than via + * the intended method of LL_IOC_ADD_ENCRYPTION_KEY. + */ +static struct key_type key_type_llcrypt = { + .name = "._llcrypt", + .instantiate = llcrypt_key_instantiate, + .destroy = llcrypt_key_destroy, + .describe = llcrypt_key_describe, +}; + +static int llcrypt_user_key_instantiate(struct key *key, + struct key_preparsed_payload *prep) +{ + /* + * We just charge LLCRYPT_MAX_KEY_SIZE bytes to the user's key quota for + * each key, regardless of the exact key size. The amount of memory + * actually used is greater than the size of the raw key anyway. + */ + return key_payload_reserve(key, LLCRYPT_MAX_KEY_SIZE); +} + +static void llcrypt_user_key_describe(const struct key *key, struct seq_file *m) +{ + seq_puts(m, key->description); +} + +/* + * Type of key in ->mk_users. Each key of this type represents a particular + * user who has added a particular master key. + * + * Note that the name of this key type really should be something like + * ".llcrypt-user" instead of simply ".llcrypt". But the shorter name is chosen + * mainly for simplicity of presentation in /proc/keys when read by a non-root + * user. And it is expected to be rare that a key is actually added by multiple + * users, since users should keep their encryption keys confidential. + */ +static struct key_type key_type_llcrypt_user = { + .name = ".llcrypt", + .instantiate = llcrypt_user_key_instantiate, + .describe = llcrypt_user_key_describe, +}; + +/* Search ->lsi_master_keys or ->mk_users */ +static struct key *search_llcrypt_keyring(struct key *keyring, + struct key_type *type, + const char *description) +{ + /* + * We need to mark the keyring reference as "possessed" so that we + * acquire permission to search it, via the KEY_POS_SEARCH permission. + */ + key_ref_t keyref = make_key_ref(keyring, true /* possessed */); + +#ifdef HAVE_KEYRING_SEARCH_4ARGS + keyref = keyring_search(keyref, type, description, false); +#else + keyref = keyring_search(keyref, type, description); +#endif + if (IS_ERR(keyref)) { + if (PTR_ERR(keyref) == -EAGAIN || /* not found */ + PTR_ERR(keyref) == -EKEYREVOKED) /* recently invalidated */ + keyref = ERR_PTR(-ENOKEY); + return ERR_CAST(keyref); + } + return key_ref_to_ptr(keyref); +} + +#define LLCRYPT_FS_KEYRING_DESCRIPTION_SIZE \ + (CONST_STRLEN("llcrypt-") + FIELD_SIZEOF(struct super_block, s_id)) + +#define LLCRYPT_MK_DESCRIPTION_SIZE (2 * LLCRYPT_KEY_IDENTIFIER_SIZE + 1) + +#define LLCRYPT_MK_USERS_DESCRIPTION_SIZE \ + (CONST_STRLEN("llcrypt-") + 2 * LLCRYPT_KEY_IDENTIFIER_SIZE + \ + CONST_STRLEN("-users") + 1) + +#define LLCRYPT_MK_USER_DESCRIPTION_SIZE \ + (2 * LLCRYPT_KEY_IDENTIFIER_SIZE + CONST_STRLEN(".uid.") + 10 + 1) + +static void format_fs_keyring_description( + char description[LLCRYPT_FS_KEYRING_DESCRIPTION_SIZE], + const struct super_block *sb) +{ + sprintf(description, "llcrypt-%s", sb->s_id); +} + +static void format_mk_description( + char description[LLCRYPT_MK_DESCRIPTION_SIZE], + const struct llcrypt_key_specifier *mk_spec) +{ + sprintf(description, "%*phN", + master_key_spec_len(mk_spec), (u8 *)&mk_spec->u); +} + +static void format_mk_users_keyring_description( + char description[LLCRYPT_MK_USERS_DESCRIPTION_SIZE], + const u8 mk_identifier[LLCRYPT_KEY_IDENTIFIER_SIZE]) +{ + sprintf(description, "llcrypt-%*phN-users", + LLCRYPT_KEY_IDENTIFIER_SIZE, mk_identifier); +} + +static void format_mk_user_description( + char description[LLCRYPT_MK_USER_DESCRIPTION_SIZE], + const u8 mk_identifier[LLCRYPT_KEY_IDENTIFIER_SIZE]) +{ + + sprintf(description, "%*phN.uid.%u", LLCRYPT_KEY_IDENTIFIER_SIZE, + mk_identifier, __kuid_val(current_fsuid())); +} + +/* Create ->lsi_master_keys if needed. Synchronized by llcrypt_add_key_mutex. */ +static int allocate_filesystem_keyring(struct super_block *sb) +{ + char description[LLCRYPT_FS_KEYRING_DESCRIPTION_SIZE]; + struct key *keyring; + struct lustre_sb_info *lsi = s2lsi(sb); + + if (!lsi) + return -EINVAL; + + if (lsi->lsi_master_keys) + return 0; + + format_fs_keyring_description(description, sb); + keyring = keyring_alloc(description, GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, + current_cred(), KEY_POS_SEARCH | + KEY_USR_SEARCH | KEY_USR_READ | KEY_USR_VIEW, + KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); + if (IS_ERR(keyring)) + return PTR_ERR(keyring); + + /* Pairs with READ_ONCE() in llcrypt_find_master_key() */ + smp_store_release(&lsi->lsi_master_keys, keyring); + return 0; +} + +void llcrypt_sb_free(struct super_block *sb) +{ + struct lustre_sb_info *lsi = s2lsi(sb); + + if (lsi != NULL) { + key_put(lsi->lsi_master_keys); + lsi->lsi_master_keys = NULL; + } +} +EXPORT_SYMBOL(llcrypt_sb_free); + +/* + * Find the specified master key in ->lsi_master_keys. + * Returns ERR_PTR(-ENOKEY) if not found. + */ +struct key *llcrypt_find_master_key(struct super_block *sb, + const struct llcrypt_key_specifier *mk_spec) +{ + struct key *keyring; + char description[LLCRYPT_MK_DESCRIPTION_SIZE]; + struct lustre_sb_info *lsi = s2lsi(sb); + + if (!lsi) + return ERR_PTR(-EINVAL); + + /* pairs with smp_store_release() in allocate_filesystem_keyring() */ + keyring = READ_ONCE(lsi->lsi_master_keys); + if (keyring == NULL) + return ERR_PTR(-ENOKEY); /* No keyring yet, so no keys yet. */ + + format_mk_description(description, mk_spec); + return search_llcrypt_keyring(keyring, &key_type_llcrypt, description); +} + +static int allocate_master_key_users_keyring(struct llcrypt_master_key *mk) +{ + char description[LLCRYPT_MK_USERS_DESCRIPTION_SIZE]; + struct key *keyring; + + format_mk_users_keyring_description(description, + mk->mk_spec.u.identifier); + keyring = keyring_alloc(description, GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, + current_cred(), KEY_POS_SEARCH | + KEY_USR_SEARCH | KEY_USR_READ | KEY_USR_VIEW, + KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); + if (IS_ERR(keyring)) + return PTR_ERR(keyring); + + mk->mk_users = keyring; + return 0; +} + +/* + * Find the current user's "key" in the master key's ->mk_users. + * Returns ERR_PTR(-ENOKEY) if not found. + */ +static struct key *find_master_key_user(struct llcrypt_master_key *mk) +{ + char description[LLCRYPT_MK_USER_DESCRIPTION_SIZE]; + + format_mk_user_description(description, mk->mk_spec.u.identifier); + return search_llcrypt_keyring(mk->mk_users, &key_type_llcrypt_user, + description); +} + +/* + * Give the current user a "key" in ->mk_users. This charges the user's quota + * and marks the master key as added by the current user, so that it cannot be + * removed by another user with the key. Either the master key's key->sem must + * be held for write, or the master key must be still undergoing initialization. + */ +static int add_master_key_user(struct llcrypt_master_key *mk) +{ + char description[LLCRYPT_MK_USER_DESCRIPTION_SIZE]; + struct key *mk_user; + int err; + + format_mk_user_description(description, mk->mk_spec.u.identifier); + mk_user = key_alloc(&key_type_llcrypt_user, description, + current_fsuid(), current_gid(), current_cred(), + KEY_POS_SEARCH | KEY_USR_VIEW, 0, NULL); + if (IS_ERR(mk_user)) + return PTR_ERR(mk_user); + + err = key_instantiate_and_link(mk_user, NULL, 0, mk->mk_users, NULL); + key_put(mk_user); + return err; +} + +/* + * Remove the current user's "key" from ->mk_users. + * The master key's key->sem must be held for write. + * + * Returns 0 if removed, -ENOKEY if not found, or another -errno code. + */ +static int remove_master_key_user(struct llcrypt_master_key *mk) +{ + struct key *mk_user; + int err; + + mk_user = find_master_key_user(mk); + if (IS_ERR(mk_user)) + return PTR_ERR(mk_user); + err = key_unlink(mk->mk_users, mk_user); + key_put(mk_user); + return err; +} + +/* + * Allocate a new llcrypt_master_key which contains the given secret, set it as + * the payload of a new 'struct key' of type llcrypt, and link the 'struct key' + * into the given keyring. Synchronized by llcrypt_add_key_mutex. + */ +static int add_new_master_key(struct llcrypt_master_key_secret *secret, + const struct llcrypt_key_specifier *mk_spec, + struct key *keyring) +{ + struct llcrypt_master_key *mk; + char description[LLCRYPT_MK_DESCRIPTION_SIZE]; + struct key *key; + int err; + + mk = kzalloc(sizeof(*mk), GFP_KERNEL); + if (!mk) + return -ENOMEM; + + mk->mk_spec = *mk_spec; + + move_master_key_secret(&mk->mk_secret, secret); + init_rwsem(&mk->mk_secret_sem); + + refcount_set(&mk->mk_refcount, 1); /* secret is present */ + INIT_LIST_HEAD(&mk->mk_decrypted_inodes); + spin_lock_init(&mk->mk_decrypted_inodes_lock); + + if (mk_spec->type == LLCRYPT_KEY_SPEC_TYPE_IDENTIFIER) { + err = allocate_master_key_users_keyring(mk); + if (err) + goto out_free_mk; + err = add_master_key_user(mk); + if (err) + goto out_free_mk; + } + + /* + * Note that we don't charge this key to anyone's quota, since when + * ->mk_users is in use those keys are charged instead, and otherwise + * (when ->mk_users isn't in use) only root can add these keys. + */ + format_mk_description(description, mk_spec); + key = key_alloc(&key_type_llcrypt, description, + GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, current_cred(), + KEY_POS_SEARCH | KEY_USR_SEARCH | KEY_USR_VIEW, + KEY_ALLOC_NOT_IN_QUOTA, NULL); + if (IS_ERR(key)) { + err = PTR_ERR(key); + goto out_free_mk; + } + err = key_instantiate_and_link(key, mk, sizeof(*mk), keyring, NULL); + key_put(key); + if (err) + goto out_free_mk; + + return 0; + +out_free_mk: + free_master_key(mk); + return err; +} + +#define KEY_DEAD 1 + +static int add_existing_master_key(struct llcrypt_master_key *mk, + struct llcrypt_master_key_secret *secret) +{ + struct key *mk_user; + bool rekey; + int err; + + /* + * If the current user is already in ->mk_users, then there's nothing to + * do. (Not applicable for v1 policy keys, which have NULL ->mk_users.) + */ + if (mk->mk_users) { + mk_user = find_master_key_user(mk); + if (mk_user != ERR_PTR(-ENOKEY)) { + if (IS_ERR(mk_user)) + return PTR_ERR(mk_user); + key_put(mk_user); + return 0; + } + } + + /* If we'll be re-adding ->mk_secret, try to take the reference. */ + rekey = !is_master_key_secret_present(&mk->mk_secret); + if (rekey && !refcount_inc_not_zero(&mk->mk_refcount)) + return KEY_DEAD; + + /* Add the current user to ->mk_users, if applicable. */ + if (mk->mk_users) { + err = add_master_key_user(mk); + if (err) { + if (rekey && refcount_dec_and_test(&mk->mk_refcount)) + return KEY_DEAD; + return err; + } + } + + /* Re-add the secret if needed. */ + if (rekey) { + down_write(&mk->mk_secret_sem); + move_master_key_secret(&mk->mk_secret, secret); + up_write(&mk->mk_secret_sem); + } + return 0; +} + +static int add_master_key(struct super_block *sb, + struct llcrypt_master_key_secret *secret, + const struct llcrypt_key_specifier *mk_spec) +{ + static DEFINE_MUTEX(llcrypt_add_key_mutex); + struct key *key; + struct lustre_sb_info *lsi = s2lsi(sb); + int err; + + if (!lsi) + return -EINVAL; + + mutex_lock(&llcrypt_add_key_mutex); /* serialize find + link */ +retry: + key = llcrypt_find_master_key(sb, mk_spec); + if (IS_ERR(key)) { + err = PTR_ERR(key); + if (err != -ENOKEY) + goto out_unlock; + /* Didn't find the key in ->lsi_master_keys. Add it. */ + err = allocate_filesystem_keyring(sb); + if (err) + goto out_unlock; + err = add_new_master_key(secret, mk_spec, + lsi->lsi_master_keys); + } else { + /* + * Found the key in ->lsi_master_keys. Re-add the secret if + * needed, and add the user to ->mk_users if needed. + */ + down_write(&key->sem); + err = add_existing_master_key(key->payload.data[0], secret); + up_write(&key->sem); + if (err == KEY_DEAD) { + /* Key being removed or needs to be removed */ + key_invalidate(key); + key_put(key); + goto retry; + } + key_put(key); + } +out_unlock: + mutex_unlock(&llcrypt_add_key_mutex); + return err; +} + +/* + * Add a master encryption key to the filesystem, causing all files which were + * encrypted with it to appear "unlocked" (decrypted) when accessed. + * + * When adding a key for use by v1 encryption policies, this ioctl is + * privileged, and userspace must provide the 'key_descriptor'. + * + * When adding a key for use by v2+ encryption policies, this ioctl is + * unprivileged. This is needed, in general, to allow non-root users to use + * encryption without encountering the visibility problems of process-subscribed + * keyrings and the inability to properly remove keys. This works by having + * each key identified by its cryptographically secure hash --- the + * 'key_identifier'. The cryptographic hash ensures that a malicious user + * cannot add the wrong key for a given identifier. Furthermore, each added key + * is charged to the appropriate user's quota for the keyrings service, which + * prevents a malicious user from adding too many keys. Finally, we forbid a + * user from removing a key while other users have added it too, which prevents + * a user who knows another user's key from causing a denial-of-service by + * removing it at an inopportune time. (We tolerate that a user who knows a key + * can prevent other users from removing it.) + * + * For more details, see the "LL_IOC_ADD_ENCRYPTION_KEY" section of + * Documentation/filesystems/llcrypt.rst. + */ +int llcrypt_ioctl_add_key(struct file *filp, void __user *_uarg) +{ + struct super_block *sb = file_inode(filp)->i_sb; + struct llcrypt_add_key_arg __user *uarg = _uarg; + struct llcrypt_add_key_arg arg; + struct llcrypt_master_key_secret secret; + int err; + + if (copy_from_user(&arg, uarg, sizeof(arg))) + return -EFAULT; + + if (!valid_key_spec(&arg.key_spec)) + return -EINVAL; + + if (arg.raw_size < LLCRYPT_MIN_KEY_SIZE || + arg.raw_size > LLCRYPT_MAX_KEY_SIZE) + return -EINVAL; + + if (memchr_inv(arg.__reserved, 0, sizeof(arg.__reserved))) + return -EINVAL; + + memset(&secret, 0, sizeof(secret)); + secret.size = arg.raw_size; + err = -EFAULT; + if (copy_from_user(secret.raw, uarg->raw, secret.size)) + goto out_wipe_secret; + + switch (arg.key_spec.type) { + case LLCRYPT_KEY_SPEC_TYPE_DESCRIPTOR: + /* + * Only root can add keys that are identified by an arbitrary + * descriptor rather than by a cryptographic hash --- since + * otherwise a malicious user could add the wrong key. + */ + err = -EACCES; + if (!capable(CAP_SYS_ADMIN)) + goto out_wipe_secret; + break; + case LLCRYPT_KEY_SPEC_TYPE_IDENTIFIER: + err = llcrypt_init_hkdf(&secret.hkdf, secret.raw, secret.size); + if (err) + goto out_wipe_secret; + + /* + * Now that the HKDF context is initialized, the raw key is no + * longer needed. + */ + memzero_explicit(secret.raw, secret.size); + + /* Calculate the key identifier and return it to userspace. */ + err = llcrypt_hkdf_expand(&secret.hkdf, + HKDF_CONTEXT_KEY_IDENTIFIER, + NULL, 0, arg.key_spec.u.identifier, + LLCRYPT_KEY_IDENTIFIER_SIZE); + if (err) + goto out_wipe_secret; + err = -EFAULT; + if (copy_to_user(uarg->key_spec.u.identifier, + arg.key_spec.u.identifier, + LLCRYPT_KEY_IDENTIFIER_SIZE)) + goto out_wipe_secret; + break; + default: + WARN_ON(1); + err = -EINVAL; + goto out_wipe_secret; + } + + err = add_master_key(sb, &secret, &arg.key_spec); +out_wipe_secret: + wipe_master_key_secret(&secret); + return err; +} +EXPORT_SYMBOL_GPL(llcrypt_ioctl_add_key); + +/* + * Verify that the current user has added a master key with the given identifier + * (returns -ENOKEY if not). This is needed to prevent a user from encrypting + * their files using some other user's key which they don't actually know. + * Cryptographically this isn't much of a problem, but the semantics of this + * would be a bit weird, so it's best to just forbid it. + * + * The system administrator (CAP_FOWNER) can override this, which should be + * enough for any use cases where encryption policies are being set using keys + * that were chosen ahead of time but aren't available at the moment. + * + * Note that the key may have already removed by the time this returns, but + * that's okay; we just care whether the key was there at some point. + * + * Return: 0 if the key is added, -ENOKEY if it isn't, or another -errno code + */ +int llcrypt_verify_key_added(struct super_block *sb, + const u8 identifier[LLCRYPT_KEY_IDENTIFIER_SIZE]) +{ + struct llcrypt_key_specifier mk_spec; + struct key *key, *mk_user; + struct llcrypt_master_key *mk; + int err; + + mk_spec.type = LLCRYPT_KEY_SPEC_TYPE_IDENTIFIER; + memcpy(mk_spec.u.identifier, identifier, LLCRYPT_KEY_IDENTIFIER_SIZE); + + key = llcrypt_find_master_key(sb, &mk_spec); + if (IS_ERR(key)) { + err = PTR_ERR(key); + goto out; + } + mk = key->payload.data[0]; + mk_user = find_master_key_user(mk); + if (IS_ERR(mk_user)) { + err = PTR_ERR(mk_user); + } else { + key_put(mk_user); + err = 0; + } + key_put(key); +out: + if (err == -ENOKEY && capable(CAP_FOWNER)) + err = 0; + return err; +} + +/* + * Try to evict the inode's dentries from the dentry cache. If the inode is a + * directory, then it can have at most one dentry; however, that dentry may be + * pinned by child dentries, so first try to evict the children too. + */ +static void shrink_dcache_inode(struct inode *inode) +{ + struct dentry *dentry; + + if (S_ISDIR(inode->i_mode)) { + dentry = d_find_any_alias(inode); + if (dentry) { + shrink_dcache_parent(dentry); + dput(dentry); + } + } + d_prune_aliases(inode); +} + +static void evict_dentries_for_decrypted_inodes(struct llcrypt_master_key *mk) +{ + struct llcrypt_info *ci; + struct inode *inode; + struct inode *toput_inode = NULL; + + spin_lock(&mk->mk_decrypted_inodes_lock); + + list_for_each_entry(ci, &mk->mk_decrypted_inodes, ci_master_key_link) { + inode = ci->ci_inode; + if (igrab(inode) == NULL) + continue; + spin_unlock(&mk->mk_decrypted_inodes_lock); + + shrink_dcache_inode(inode); + iput(toput_inode); + toput_inode = inode; + + spin_lock(&mk->mk_decrypted_inodes_lock); + } + + spin_unlock(&mk->mk_decrypted_inodes_lock); + iput(toput_inode); +} + +static int check_for_busy_inodes(struct super_block *sb, + struct llcrypt_master_key *mk) +{ + struct list_head *pos; + size_t busy_count = 0; + unsigned long ino; + struct dentry *dentry; + char _path[256]; + char *path = NULL; + + spin_lock(&mk->mk_decrypted_inodes_lock); + + list_for_each(pos, &mk->mk_decrypted_inodes) + busy_count++; + + if (busy_count == 0) { + spin_unlock(&mk->mk_decrypted_inodes_lock); + return 0; + } + + { + /* select an example file to show for debugging purposes */ + struct inode *inode = + list_first_entry(&mk->mk_decrypted_inodes, + struct llcrypt_info, + ci_master_key_link)->ci_inode; + ino = inode->i_ino; + dentry = d_find_alias(inode); + } + spin_unlock(&mk->mk_decrypted_inodes_lock); + + if (dentry) { + path = dentry_path_raw(dentry, _path, sizeof(_path)); + dput(dentry); + } + if (IS_ERR_OR_NULL(path)) + path = "(unknown)"; + + llcrypt_warn(NULL, + "%s: %zu inode(s) still busy after removing key with %s %*phN, including ino %lu (%s)", + sb->s_id, busy_count, master_key_spec_type(&mk->mk_spec), + master_key_spec_len(&mk->mk_spec), (u8 *)&mk->mk_spec.u, + ino, path); + return -EBUSY; +} + +static int try_to_lock_encrypted_files(struct super_block *sb, + struct llcrypt_master_key *mk) +{ + int err1; + int err2; + + /* + * An inode can't be evicted while it is dirty or has dirty pages. + * Thus, we first have to clean the inodes in ->mk_decrypted_inodes. + * + * Just do it the easy way: call sync_filesystem(). It's overkill, but + * it works, and it's more important to minimize the amount of caches we + * drop than the amount of data we sync. Also, unprivileged users can + * already call sync_filesystem() via sys_syncfs() or sys_sync(). + */ + down_read(&sb->s_umount); + err1 = sync_filesystem(sb); + up_read(&sb->s_umount); + /* If a sync error occurs, still try to evict as much as possible. */ + + /* + * Inodes are pinned by their dentries, so we have to evict their + * dentries. shrink_dcache_sb() would suffice, but would be overkill + * and inappropriate for use by unprivileged users. So instead go + * through the inodes' alias lists and try to evict each dentry. + */ + evict_dentries_for_decrypted_inodes(mk); + + /* + * evict_dentries_for_decrypted_inodes() already iput() each inode in + * the list; any inodes for which that dropped the last reference will + * have been evicted due to llcrypt_drop_inode() detecting the key + * removal and telling the VFS to evict the inode. So to finish, we + * just need to check whether any inodes couldn't be evicted. + */ + err2 = check_for_busy_inodes(sb, mk); + + return err1 ?: err2; +} + +/* + * Try to remove an llcrypt master encryption key. + * + * LL_IOC_REMOVE_ENCRYPTION_KEY (all_users=false) removes the current user's + * claim to the key, then removes the key itself if no other users have claims. + * LL_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS (all_users=true) always removes the + * key itself. + * + * To "remove the key itself", first we wipe the actual master key secret, so + * that no more inodes can be unlocked with it. Then we try to evict all cached + * inodes that had been unlocked with the key. + * + * If all inodes were evicted, then we unlink the llcrypt_master_key from the + * keyring. Otherwise it remains in the keyring in the "incompletely removed" + * state (without the actual secret key) where it tracks the list of remaining + * inodes. Userspace can execute the ioctl again later to retry eviction, or + * alternatively can re-add the secret key again. + * + * For more details, see the "Removing keys" section of + * Documentation/filesystems/llcrypt.rst. + */ +static int do_remove_key(struct file *filp, void __user *_uarg, bool all_users) +{ + struct super_block *sb = file_inode(filp)->i_sb; + struct llcrypt_remove_key_arg __user *uarg = _uarg; + struct llcrypt_remove_key_arg arg; + struct key *key; + struct llcrypt_master_key *mk; + u32 status_flags = 0; + int err; + bool dead; + + if (copy_from_user(&arg, uarg, sizeof(arg))) + return -EFAULT; + + if (!valid_key_spec(&arg.key_spec)) + return -EINVAL; + + if (memchr_inv(arg.__reserved, 0, sizeof(arg.__reserved))) + return -EINVAL; + + /* + * Only root can add and remove keys that are identified by an arbitrary + * descriptor rather than by a cryptographic hash. + */ + if (arg.key_spec.type == LLCRYPT_KEY_SPEC_TYPE_DESCRIPTOR && + !capable(CAP_SYS_ADMIN)) + return -EACCES; + + /* Find the key being removed. */ + key = llcrypt_find_master_key(sb, &arg.key_spec); + if (IS_ERR(key)) + return PTR_ERR(key); + mk = key->payload.data[0]; + + down_write(&key->sem); + + /* If relevant, remove current user's (or all users) claim to the key */ + if (mk->mk_users && mk->mk_users->keys.nr_leaves_on_tree != 0) { + if (all_users) + err = keyring_clear(mk->mk_users); + else + err = remove_master_key_user(mk); + if (err) { + up_write(&key->sem); + goto out_put_key; + } + if (mk->mk_users->keys.nr_leaves_on_tree != 0) { + /* + * Other users have still added the key too. We removed + * the current user's claim to the key, but we still + * can't remove the key itself. + */ + status_flags |= + LLCRYPT_KEY_REMOVAL_STATUS_FLAG_OTHER_USERS; + err = 0; + up_write(&key->sem); + goto out_put_key; + } + } + + /* No user claims remaining. Go ahead and wipe the secret. */ + dead = false; + if (is_master_key_secret_present(&mk->mk_secret)) { + down_write(&mk->mk_secret_sem); + wipe_master_key_secret(&mk->mk_secret); + dead = refcount_dec_and_test(&mk->mk_refcount); + up_write(&mk->mk_secret_sem); + } + up_write(&key->sem); + if (dead) { + /* + * No inodes reference the key, and we wiped the secret, so the + * key object is free to be removed from the keyring. + */ + key_invalidate(key); + err = 0; + } else { + /* Some inodes still reference this key; try to evict them. */ + err = try_to_lock_encrypted_files(sb, mk); + if (err == -EBUSY) { + status_flags |= + LLCRYPT_KEY_REMOVAL_STATUS_FLAG_FILES_BUSY; + err = 0; + } + } + /* + * We return 0 if we successfully did something: removed a claim to the + * key, wiped the secret, or tried locking the files again. Users need + * to check the informational status flags if they care whether the key + * has been fully removed including all files locked. + */ +out_put_key: + key_put(key); + if (err == 0) + err = put_user(status_flags, &uarg->removal_status_flags); + return err; +} + +int llcrypt_ioctl_remove_key(struct file *filp, void __user *uarg) +{ + return do_remove_key(filp, uarg, false); +} +EXPORT_SYMBOL_GPL(llcrypt_ioctl_remove_key); + +int llcrypt_ioctl_remove_key_all_users(struct file *filp, void __user *uarg) +{ + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + return do_remove_key(filp, uarg, true); +} +EXPORT_SYMBOL_GPL(llcrypt_ioctl_remove_key_all_users); + +/* + * Retrieve the status of an llcrypt master encryption key. + * + * We set ->status to indicate whether the key is absent, present, or + * incompletely removed. "Incompletely removed" means that the master key + * secret has been removed, but some files which had been unlocked with it are + * still in use. This field allows applications to easily determine the state + * of an encrypted directory without using a hack such as trying to open a + * regular file in it (which can confuse the "incompletely removed" state with + * absent or present). + * + * In addition, for v2 policy keys we allow applications to determine, via + * ->status_flags and ->user_count, whether the key has been added by the + * current user, by other users, or by both. Most applications should not need + * this, since ordinarily only one user should know a given key. However, if a + * secret key is shared by multiple users, applications may wish to add an + * already-present key to prevent other users from removing it. This ioctl can + * be used to check whether that really is the case before the work is done to + * add the key --- which might e.g. require prompting the user for a passphrase. + * + * For more details, see the "LL_IOC_GET_ENCRYPTION_KEY_STATUS" section of + * Documentation/filesystems/llcrypt.rst. + */ +int llcrypt_ioctl_get_key_status(struct file *filp, void __user *uarg) +{ + struct super_block *sb = file_inode(filp)->i_sb; + struct llcrypt_get_key_status_arg arg; + struct key *key; + struct llcrypt_master_key *mk; + int err; + + if (copy_from_user(&arg, uarg, sizeof(arg))) + return -EFAULT; + + if (!valid_key_spec(&arg.key_spec)) + return -EINVAL; + + if (memchr_inv(arg.__reserved, 0, sizeof(arg.__reserved))) + return -EINVAL; + + arg.status_flags = 0; + arg.user_count = 0; + memset(arg.__out_reserved, 0, sizeof(arg.__out_reserved)); + + key = llcrypt_find_master_key(sb, &arg.key_spec); + if (IS_ERR(key)) { + if (key != ERR_PTR(-ENOKEY)) + return PTR_ERR(key); + arg.status = LLCRYPT_KEY_STATUS_ABSENT; + err = 0; + goto out; + } + mk = key->payload.data[0]; + down_read(&key->sem); + + if (!is_master_key_secret_present(&mk->mk_secret)) { + arg.status = LLCRYPT_KEY_STATUS_INCOMPLETELY_REMOVED; + err = 0; + goto out_release_key; + } + + arg.status = LLCRYPT_KEY_STATUS_PRESENT; + if (mk->mk_users) { + struct key *mk_user; + + arg.user_count = mk->mk_users->keys.nr_leaves_on_tree; + mk_user = find_master_key_user(mk); + if (!IS_ERR(mk_user)) { + arg.status_flags |= + LLCRYPT_KEY_STATUS_FLAG_ADDED_BY_SELF; + key_put(mk_user); + } else if (mk_user != ERR_PTR(-ENOKEY)) { + err = PTR_ERR(mk_user); + goto out_release_key; + } + } + err = 0; +out_release_key: + up_read(&key->sem); + key_put(key); +out: + if (!err && copy_to_user(uarg, &arg, sizeof(arg))) + err = -EFAULT; + return err; +} +EXPORT_SYMBOL_GPL(llcrypt_ioctl_get_key_status); + +int __init llcrypt_init_keyring(void) +{ + int err; + + err = register_key_type(&key_type_llcrypt); + if (err) + return err; + + err = register_key_type(&key_type_llcrypt_user); + if (err) + goto err_unregister_llcrypt; + + return 0; + +err_unregister_llcrypt: + unregister_key_type(&key_type_llcrypt); + return err; +} + +void __exit llcrypt_exit_keyring(void) +{ + unregister_key_type(&key_type_llcrypt_user); + unregister_key_type(&key_type_llcrypt); +} diff --git a/libcfs/libcfs/crypto/keysetup.c b/libcfs/libcfs/crypto/keysetup.c new file mode 100644 index 0000000..c9ca537 --- /dev/null +++ b/libcfs/libcfs/crypto/keysetup.c @@ -0,0 +1,608 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Key setup facility for FS encryption support. + * + * Copyright (C) 2015, Google, Inc. + * + * Originally written by Michael Halcrow, Ildar Muslukhov, and Uday Savagaonkar. + * Heavily modified since then. + */ +/* + * Linux commit 219d54332a09 + * tags/v5.4 + */ + +#include +#include +#include +#include + +#include "llcrypt_private.h" + +static struct crypto_shash *essiv_hash_tfm; + +static struct llcrypt_mode available_modes[] = { + [LLCRYPT_MODE_AES_256_XTS] = { + .friendly_name = "AES-256-XTS", + .cipher_str = "xts(aes)", + .keysize = 64, + .ivsize = 16, + }, + [LLCRYPT_MODE_AES_256_CTS] = { + .friendly_name = "AES-256-CTS-CBC", + .cipher_str = "cts(cbc(aes))", + .keysize = 32, + .ivsize = 16, + }, + [LLCRYPT_MODE_AES_128_CBC] = { + .friendly_name = "AES-128-CBC", + .cipher_str = "cbc(aes)", + .keysize = 16, + .ivsize = 16, + .needs_essiv = true, + }, + [LLCRYPT_MODE_AES_128_CTS] = { + .friendly_name = "AES-128-CTS-CBC", + .cipher_str = "cts(cbc(aes))", + .keysize = 16, + .ivsize = 16, + }, + [LLCRYPT_MODE_ADIANTUM] = { + .friendly_name = "Adiantum", + .cipher_str = "adiantum(xchacha12,aes)", + .keysize = 32, + .ivsize = 32, + }, +}; + +static struct llcrypt_mode * +select_encryption_mode(const union llcrypt_policy *policy, + const struct inode *inode) +{ + if (S_ISREG(inode->i_mode)) + return &available_modes[llcrypt_policy_contents_mode(policy)]; + + if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) + return &available_modes[llcrypt_policy_fnames_mode(policy)]; + + WARN_ONCE(1, "llcrypt: filesystem tried to load encryption info for inode %lu, which is not encryptable (file type %d)\n", + inode->i_ino, (inode->i_mode & S_IFMT)); + return ERR_PTR(-EINVAL); +} + +/* Create a symmetric cipher object for the given encryption mode and key */ +struct crypto_skcipher *llcrypt_allocate_skcipher(struct llcrypt_mode *mode, + const u8 *raw_key, + const struct inode *inode) +{ + struct crypto_skcipher *tfm; + int err; + + tfm = crypto_alloc_skcipher(mode->cipher_str, 0, 0); + if (IS_ERR(tfm)) { + if (PTR_ERR(tfm) == -ENOENT) { + llcrypt_warn(inode, + "Missing crypto API support for %s (API name: \"%s\")", + mode->friendly_name, mode->cipher_str); + return ERR_PTR(-ENOPKG); + } + llcrypt_err(inode, "Error allocating '%s' transform: %ld", + mode->cipher_str, PTR_ERR(tfm)); + return tfm; + } + if (unlikely(!mode->logged_impl_name)) { + /* + * llcrypt performance can vary greatly depending on which + * crypto algorithm implementation is used. Help people debug + * performance problems by logging the ->cra_driver_name the + * first time a mode is used. Note that multiple threads can + * race here, but it doesn't really matter. + */ + mode->logged_impl_name = true; + pr_info("llcrypt: %s using implementation \"%s\"\n", + mode->friendly_name, + crypto_skcipher_alg(tfm)->base.cra_driver_name); + } + crypto_skcipher_set_flags(tfm, CRYPTO_TFM_REQ_FORBID_WEAK_KEYS); + err = crypto_skcipher_setkey(tfm, raw_key, mode->keysize); + if (err) + goto err_free_tfm; + + return tfm; + +err_free_tfm: + crypto_free_skcipher(tfm); + return ERR_PTR(err); +} + +static int derive_essiv_salt(const u8 *key, int keysize, u8 *salt) +{ + struct crypto_shash *tfm = READ_ONCE(essiv_hash_tfm); + + /* init hash transform on demand */ + if (unlikely(!tfm)) { + struct crypto_shash *prev_tfm; + + tfm = crypto_alloc_shash("sha256", 0, 0); + if (IS_ERR(tfm)) { + if (PTR_ERR(tfm) == -ENOENT) { + llcrypt_warn(NULL, + "Missing crypto API support for SHA-256"); + return -ENOPKG; + } + llcrypt_err(NULL, + "Error allocating SHA-256 transform: %ld", + PTR_ERR(tfm)); + return PTR_ERR(tfm); + } + prev_tfm = cmpxchg(&essiv_hash_tfm, NULL, tfm); + if (prev_tfm) { + crypto_free_shash(tfm); + tfm = prev_tfm; + } + } + + { + SHASH_DESC_ON_STACK(desc, tfm); + desc->tfm = tfm; + + return crypto_shash_digest(desc, key, keysize, salt); + } +} + +static int init_essiv_generator(struct llcrypt_info *ci, const u8 *raw_key, + int keysize) +{ + int err; + struct crypto_cipher *essiv_tfm; + u8 salt[SHA256_DIGEST_SIZE]; + + if (WARN_ON(ci->ci_mode->ivsize != AES_BLOCK_SIZE)) + return -EINVAL; + + essiv_tfm = crypto_alloc_cipher("aes", 0, 0); + if (IS_ERR(essiv_tfm)) + return PTR_ERR(essiv_tfm); + + ci->ci_essiv_tfm = essiv_tfm; + + err = derive_essiv_salt(raw_key, keysize, salt); + if (err) + goto out; + + /* + * Using SHA256 to derive the salt/key will result in AES-256 being + * used for IV generation. File contents encryption will still use the + * configured keysize (AES-128) nevertheless. + */ + err = crypto_cipher_setkey(essiv_tfm, salt, sizeof(salt)); + if (err) + goto out; + +out: + memzero_explicit(salt, sizeof(salt)); + return err; +} + +/* Given the per-file key, set up the file's crypto transform object(s) */ +int llcrypt_set_derived_key(struct llcrypt_info *ci, const u8 *derived_key) +{ + struct llcrypt_mode *mode = ci->ci_mode; + struct crypto_skcipher *ctfm; + int err; + + ctfm = llcrypt_allocate_skcipher(mode, derived_key, ci->ci_inode); + if (IS_ERR(ctfm)) + return PTR_ERR(ctfm); + + ci->ci_ctfm = ctfm; + + if (mode->needs_essiv) { + err = init_essiv_generator(ci, derived_key, mode->keysize); + if (err) { + llcrypt_warn(ci->ci_inode, + "Error initializing ESSIV generator: %d", + err); + return err; + } + } + return 0; +} + +static int setup_per_mode_key(struct llcrypt_info *ci, + struct llcrypt_master_key *mk) +{ + struct llcrypt_mode *mode = ci->ci_mode; + u8 mode_num = mode - available_modes; + struct crypto_skcipher *tfm, *prev_tfm; + u8 mode_key[LLCRYPT_MAX_KEY_SIZE]; + int err; + + if (WARN_ON(mode_num >= ARRAY_SIZE(mk->mk_mode_keys))) + return -EINVAL; + + /* pairs with cmpxchg() below */ + tfm = READ_ONCE(mk->mk_mode_keys[mode_num]); + if (likely(tfm != NULL)) + goto done; + + BUILD_BUG_ON(sizeof(mode_num) != 1); + err = llcrypt_hkdf_expand(&mk->mk_secret.hkdf, + HKDF_CONTEXT_PER_MODE_KEY, + &mode_num, sizeof(mode_num), + mode_key, mode->keysize); + if (err) + return err; + tfm = llcrypt_allocate_skcipher(mode, mode_key, ci->ci_inode); + memzero_explicit(mode_key, mode->keysize); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + /* pairs with READ_ONCE() above */ + prev_tfm = cmpxchg(&mk->mk_mode_keys[mode_num], NULL, tfm); + if (prev_tfm != NULL) { + crypto_free_skcipher(tfm); + tfm = prev_tfm; + } +done: + ci->ci_ctfm = tfm; + return 0; +} + +static int llcrypt_setup_v2_file_key(struct llcrypt_info *ci, + struct llcrypt_master_key *mk) +{ + u8 derived_key[LLCRYPT_MAX_KEY_SIZE]; + int err; + + if (ci->ci_policy.v2.flags & LLCRYPT_POLICY_FLAG_DIRECT_KEY) { + /* + * DIRECT_KEY: instead of deriving per-file keys, the per-file + * nonce will be included in all the IVs. But unlike v1 + * policies, for v2 policies in this case we don't encrypt with + * the master key directly but rather derive a per-mode key. + * This ensures that the master key is consistently used only + * for HKDF, avoiding key reuse issues. + */ + if (!llcrypt_mode_supports_direct_key(ci->ci_mode)) { + llcrypt_warn(ci->ci_inode, + "Direct key flag not allowed with %s", + ci->ci_mode->friendly_name); + return -EINVAL; + } + return setup_per_mode_key(ci, mk); + } + + err = llcrypt_hkdf_expand(&mk->mk_secret.hkdf, + HKDF_CONTEXT_PER_FILE_KEY, + ci->ci_nonce, FS_KEY_DERIVATION_NONCE_SIZE, + derived_key, ci->ci_mode->keysize); + if (err) + return err; + + err = llcrypt_set_derived_key(ci, derived_key); + memzero_explicit(derived_key, ci->ci_mode->keysize); + return err; +} + +/* + * Find the master key, then set up the inode's actual encryption key. + * + * If the master key is found in the filesystem-level keyring, then the + * corresponding 'struct key' is returned in *master_key_ret with + * ->mk_secret_sem read-locked. This is needed to ensure that only one task + * links the llcrypt_info into ->mk_decrypted_inodes (as multiple tasks may race + * to create an llcrypt_info for the same inode), and to synchronize the master + * key being removed with a new inode starting to use it. + */ +static int setup_file_encryption_key(struct llcrypt_info *ci, + struct key **master_key_ret) +{ + struct key *key; + struct llcrypt_master_key *mk = NULL; + struct llcrypt_key_specifier mk_spec; + int err; + + switch (ci->ci_policy.version) { + case LLCRYPT_POLICY_V1: + mk_spec.type = LLCRYPT_KEY_SPEC_TYPE_DESCRIPTOR; + memcpy(mk_spec.u.descriptor, + ci->ci_policy.v1.master_key_descriptor, + LLCRYPT_KEY_DESCRIPTOR_SIZE); + break; + case LLCRYPT_POLICY_V2: + mk_spec.type = LLCRYPT_KEY_SPEC_TYPE_IDENTIFIER; + memcpy(mk_spec.u.identifier, + ci->ci_policy.v2.master_key_identifier, + LLCRYPT_KEY_IDENTIFIER_SIZE); + break; + default: + WARN_ON(1); + return -EINVAL; + } + + key = llcrypt_find_master_key(ci->ci_inode->i_sb, &mk_spec); + if (IS_ERR(key)) { + if (key != ERR_PTR(-ENOKEY) || + ci->ci_policy.version != LLCRYPT_POLICY_V1) + return PTR_ERR(key); + + /* + * As a legacy fallback for v1 policies, search for the key in + * the current task's subscribed keyrings too. Don't move this + * to before the search of ->lsi_master_keys, since users + * shouldn't be able to override filesystem-level keys. + */ + return llcrypt_setup_v1_file_key_via_subscribed_keyrings(ci); + } + + mk = key->payload.data[0]; + down_read(&mk->mk_secret_sem); + + /* Has the secret been removed (via LL_IOC_REMOVE_ENCRYPTION_KEY)? */ + if (!is_master_key_secret_present(&mk->mk_secret)) { + err = -ENOKEY; + goto out_release_key; + } + + /* + * Require that the master key be at least as long as the derived key. + * Otherwise, the derived key cannot possibly contain as much entropy as + * that required by the encryption mode it will be used for. For v1 + * policies it's also required for the KDF to work at all. + */ + if (mk->mk_secret.size < ci->ci_mode->keysize) { + llcrypt_warn(NULL, + "key with %s %*phN is too short (got %u bytes, need %u+ bytes)", + master_key_spec_type(&mk_spec), + master_key_spec_len(&mk_spec), (u8 *)&mk_spec.u, + mk->mk_secret.size, ci->ci_mode->keysize); + err = -ENOKEY; + goto out_release_key; + } + + switch (ci->ci_policy.version) { + case LLCRYPT_POLICY_V1: + err = llcrypt_setup_v1_file_key(ci, mk->mk_secret.raw); + break; + case LLCRYPT_POLICY_V2: + err = llcrypt_setup_v2_file_key(ci, mk); + break; + default: + WARN_ON(1); + err = -EINVAL; + break; + } + if (err) + goto out_release_key; + + *master_key_ret = key; + return 0; + +out_release_key: + up_read(&mk->mk_secret_sem); + key_put(key); + return err; +} + +static void put_crypt_info(struct llcrypt_info *ci) +{ + struct key *key; + + if (!ci) + return; + + if (ci->ci_direct_key) { + llcrypt_put_direct_key(ci->ci_direct_key); + } else if ((ci->ci_ctfm != NULL || ci->ci_essiv_tfm != NULL) && + !llcrypt_is_direct_key_policy(&ci->ci_policy)) { + crypto_free_skcipher(ci->ci_ctfm); + crypto_free_cipher(ci->ci_essiv_tfm); + } + + key = ci->ci_master_key; + if (key) { + struct llcrypt_master_key *mk = key->payload.data[0]; + + /* + * Remove this inode from the list of inodes that were unlocked + * with the master key. + * + * In addition, if we're removing the last inode from a key that + * already had its secret removed, invalidate the key so that it + * gets removed from ->lsi_master_keys. + */ + spin_lock(&mk->mk_decrypted_inodes_lock); + list_del(&ci->ci_master_key_link); + spin_unlock(&mk->mk_decrypted_inodes_lock); + if (refcount_dec_and_test(&mk->mk_refcount)) + key_invalidate(key); + key_put(key); + } + kmem_cache_free(llcrypt_info_cachep, ci); +} + +int llcrypt_get_encryption_info(struct inode *inode) +{ + struct llcrypt_info *crypt_info; + union llcrypt_context ctx; + struct llcrypt_mode *mode; + struct key *master_key = NULL; + struct lustre_sb_info *lsi = s2lsi(inode->i_sb); + int res; + + if (llcrypt_has_encryption_key(inode)) + return 0; + + if (!lsi) + return -ENOKEY; + + res = llcrypt_initialize(lsi->lsi_cop->flags); + if (res) + return res; + + res = lsi->lsi_cop->get_context(inode, &ctx, sizeof(ctx)); + if (res < 0) { + if (!llcrypt_dummy_context_enabled(inode) || + IS_ENCRYPTED(inode)) { + llcrypt_warn(inode, + "Error %d getting encryption context", + res); + return res; + } + /* Fake up a context for an unencrypted directory */ + memset(&ctx, 0, sizeof(ctx)); + ctx.version = LLCRYPT_CONTEXT_V1; + ctx.v1.contents_encryption_mode = LLCRYPT_MODE_AES_256_XTS; + ctx.v1.filenames_encryption_mode = LLCRYPT_MODE_AES_256_CTS; + memset(ctx.v1.master_key_descriptor, 0x42, + LLCRYPT_KEY_DESCRIPTOR_SIZE); + res = sizeof(ctx.v1); + } + + crypt_info = kmem_cache_zalloc(llcrypt_info_cachep, GFP_NOFS); + if (!crypt_info) + return -ENOMEM; + + crypt_info->ci_inode = inode; + + res = llcrypt_policy_from_context(&crypt_info->ci_policy, &ctx, res); + if (res) { + llcrypt_warn(inode, + "Unrecognized or corrupt encryption context"); + goto out; + } + + switch (ctx.version) { + case LLCRYPT_CONTEXT_V1: + memcpy(crypt_info->ci_nonce, ctx.v1.nonce, + FS_KEY_DERIVATION_NONCE_SIZE); + break; + case LLCRYPT_CONTEXT_V2: + memcpy(crypt_info->ci_nonce, ctx.v2.nonce, + FS_KEY_DERIVATION_NONCE_SIZE); + break; + default: + WARN_ON(1); + res = -EINVAL; + goto out; + } + + if (!llcrypt_supported_policy(&crypt_info->ci_policy, inode)) { + res = -EINVAL; + goto out; + } + + mode = select_encryption_mode(&crypt_info->ci_policy, inode); + if (IS_ERR(mode)) { + res = PTR_ERR(mode); + goto out; + } + WARN_ON(mode->ivsize > LLCRYPT_MAX_IV_SIZE); + crypt_info->ci_mode = mode; + + res = setup_file_encryption_key(crypt_info, &master_key); + if (res) + goto out; + + if (cmpxchg_release(&(llcrypt_info_nocast(inode)), NULL, + crypt_info) == NULL) { + if (master_key) { + struct llcrypt_master_key *mk = + master_key->payload.data[0]; + + refcount_inc(&mk->mk_refcount); + crypt_info->ci_master_key = key_get(master_key); + spin_lock(&mk->mk_decrypted_inodes_lock); + list_add(&crypt_info->ci_master_key_link, + &mk->mk_decrypted_inodes); + spin_unlock(&mk->mk_decrypted_inodes_lock); + } + crypt_info = NULL; + } + res = 0; +out: + if (master_key) { + struct llcrypt_master_key *mk = master_key->payload.data[0]; + + up_read(&mk->mk_secret_sem); + key_put(master_key); + } + if (res == -ENOKEY) + res = 0; + put_crypt_info(crypt_info); + return res; +} +EXPORT_SYMBOL(llcrypt_get_encryption_info); + +/** + * llcrypt_put_encryption_info - free most of an inode's llcrypt data + * + * Free the inode's llcrypt_info. Filesystems must call this when the inode is + * being evicted. An RCU grace period need not have elapsed yet. + */ +void llcrypt_put_encryption_info(struct inode *inode) +{ + put_crypt_info(llcrypt_info(inode)); + llcrypt_info_nocast(inode) = NULL; +} +EXPORT_SYMBOL(llcrypt_put_encryption_info); + +/** + * llcrypt_free_inode - free an inode's llcrypt data requiring RCU delay + * + * Free the inode's cached decrypted symlink target, if any. Filesystems must + * call this after an RCU grace period, just before they free the inode. + */ +void llcrypt_free_inode(struct inode *inode) +{ + if (IS_ENCRYPTED(inode) && S_ISLNK(inode->i_mode)) { + kfree(inode->i_link); + inode->i_link = NULL; + } +} +EXPORT_SYMBOL(llcrypt_free_inode); + +/** + * llcrypt_drop_inode - check whether the inode's master key has been removed + * + * Filesystems supporting llcrypt must call this from their ->drop_inode() + * method so that encrypted inodes are evicted as soon as they're no longer in + * use and their master key has been removed. + * + * Return: 1 if llcrypt wants the inode to be evicted now, otherwise 0 + */ +int llcrypt_drop_inode(struct inode *inode) +{ + const struct llcrypt_info *ci; + const struct llcrypt_master_key *mk; + + ci = (struct llcrypt_info *)READ_ONCE(llcrypt_info_nocast(inode)); + /* + * If ci is NULL, then the inode doesn't have an encryption key set up + * so it's irrelevant. If ci_master_key is NULL, then the master key + * was provided via the legacy mechanism of the process-subscribed + * keyrings, so we don't know whether it's been removed or not. + */ + if (!ci || !ci->ci_master_key) + return 0; + mk = ci->ci_master_key->payload.data[0]; + + /* + * Note: since we aren't holding ->mk_secret_sem, the result here can + * immediately become outdated. But there's no correctness problem with + * unnecessarily evicting. Nor is there a correctness problem with not + * evicting while iput() is racing with the key being removed, since + * then the thread removing the key will either evict the inode itself + * or will correctly detect that it wasn't evicted due to the race. + */ + return !is_master_key_secret_present(&mk->mk_secret); +} +EXPORT_SYMBOL_GPL(llcrypt_drop_inode); + +inline bool llcrypt_has_encryption_key(const struct inode *inode) +{ + /* pairs with cmpxchg_release() in llcrypt_get_encryption_info() */ + return READ_ONCE(llcrypt_info_nocast(inode)) != NULL; +} +EXPORT_SYMBOL_GPL(llcrypt_has_encryption_key); diff --git a/libcfs/libcfs/crypto/keysetup_v1.c b/libcfs/libcfs/crypto/keysetup_v1.c new file mode 100644 index 0000000..d26735e --- /dev/null +++ b/libcfs/libcfs/crypto/keysetup_v1.c @@ -0,0 +1,350 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Key setup for v1 encryption policies + * + * Copyright 2015, 2019 Google LLC + */ +/* + * Linux commit 219d54332a09 + * tags/v5.4 + */ + +/* + * This file implements compatibility functions for the original encryption + * policy version ("v1"), including: + * + * - Deriving per-file keys using the AES-128-ECB based KDF + * (rather than the new method of using HKDF-SHA512) + * + * - Retrieving llcrypt master keys from process-subscribed keyrings + * (rather than the new method of using a filesystem-level keyring) + * + * - Handling policies with the DIRECT_KEY flag set using a master key table + * (rather than the new method of implementing DIRECT_KEY with per-mode keys + * managed alongside the master keys in the filesystem-level keyring) + */ + +#include +#include +#include +#include +#include + +#include "llcrypt_private.h" + +/* Table of keys referenced by DIRECT_KEY policies */ +static DEFINE_HASHTABLE(llcrypt_direct_keys, 6); /* 6 bits = 64 buckets */ +static DEFINE_SPINLOCK(llcrypt_direct_keys_lock); + +/* + * v1 key derivation function. This generates the derived key by encrypting the + * master key with AES-128-ECB using the nonce as the AES key. This provides a + * unique derived key with sufficient entropy for each inode. However, it's + * nonstandard, non-extensible, doesn't evenly distribute the entropy from the + * master key, and is trivially reversible: an attacker who compromises a + * derived key can "decrypt" it to get back to the master key, then derive any + * other key. For all new code, use HKDF instead. + * + * The master key must be at least as long as the derived key. If the master + * key is longer, then only the first 'derived_keysize' bytes are used. + */ +static int derive_key_aes(const u8 *master_key, + const u8 nonce[FS_KEY_DERIVATION_NONCE_SIZE], + u8 *derived_key, unsigned int derived_keysize) +{ + int res = 0; + struct skcipher_request *req = NULL; + DECLARE_CRYPTO_WAIT(wait); + struct scatterlist src_sg, dst_sg; + struct crypto_skcipher *tfm = crypto_alloc_skcipher("ecb(aes)", 0, 0); + + if (IS_ERR(tfm)) { + res = PTR_ERR(tfm); + tfm = NULL; + goto out; + } + crypto_skcipher_set_flags(tfm, CRYPTO_TFM_REQ_FORBID_WEAK_KEYS); + req = skcipher_request_alloc(tfm, GFP_NOFS); + if (!req) { + res = -ENOMEM; + goto out; + } + skcipher_request_set_callback(req, + CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, + crypto_req_done, &wait); + res = crypto_skcipher_setkey(tfm, nonce, FS_KEY_DERIVATION_NONCE_SIZE); + if (res < 0) + goto out; + + sg_init_one(&src_sg, master_key, derived_keysize); + sg_init_one(&dst_sg, derived_key, derived_keysize); + skcipher_request_set_crypt(req, &src_sg, &dst_sg, derived_keysize, + NULL); + res = crypto_wait_req(crypto_skcipher_encrypt(req), &wait); +out: + skcipher_request_free(req); + crypto_free_skcipher(tfm); + return res; +} + +/* + * Search the current task's subscribed keyrings for a "logon" key with + * description prefix:descriptor, and if found acquire a read lock on it and + * return a pointer to its validated payload in *payload_ret. + */ +static struct key * +find_and_lock_process_key(const char *prefix, + const u8 descriptor[LLCRYPT_KEY_DESCRIPTOR_SIZE], + unsigned int min_keysize, + const struct llcrypt_key **payload_ret) +{ + char *description; + struct key *key; + const struct user_key_payload *ukp; + const struct llcrypt_key *payload; + + description = kasprintf(GFP_NOFS, "%s%*phN", prefix, + LLCRYPT_KEY_DESCRIPTOR_SIZE, descriptor); + if (!description) + return ERR_PTR(-ENOMEM); + + key = request_key(&key_type_logon, description, NULL); + kfree(description); + if (IS_ERR(key)) + return key; + + down_read(&key->sem); + ukp = user_key_payload_locked(key); + + if (!ukp) /* was the key revoked before we acquired its semaphore? */ + goto invalid; + + payload = (const struct llcrypt_key *)ukp->data; + + if (ukp->datalen != sizeof(struct llcrypt_key) || + payload->size < 1 || payload->size > LLCRYPT_MAX_KEY_SIZE) { + llcrypt_warn(NULL, + "key with description '%s' has invalid payload", + key->description); + goto invalid; + } + + if (payload->size < min_keysize) { + llcrypt_warn(NULL, + "key with description '%s' is too short (got %u bytes, need %u+ bytes)", + key->description, payload->size, min_keysize); + goto invalid; + } + + *payload_ret = payload; + return key; + +invalid: + up_read(&key->sem); + key_put(key); + return ERR_PTR(-ENOKEY); +} + +/* Master key referenced by DIRECT_KEY policy */ +struct llcrypt_direct_key { + struct hlist_node dk_node; + refcount_t dk_refcount; + const struct llcrypt_mode *dk_mode; + struct crypto_skcipher *dk_ctfm; + u8 dk_descriptor[LLCRYPT_KEY_DESCRIPTOR_SIZE]; + u8 dk_raw[LLCRYPT_MAX_KEY_SIZE]; +}; + +static void free_direct_key(struct llcrypt_direct_key *dk) +{ + if (dk) { + crypto_free_skcipher(dk->dk_ctfm); + kzfree(dk); + } +} + +void llcrypt_put_direct_key(struct llcrypt_direct_key *dk) +{ + if (!refcount_dec_and_lock(&dk->dk_refcount, &llcrypt_direct_keys_lock)) + return; + hash_del(&dk->dk_node); + spin_unlock(&llcrypt_direct_keys_lock); + + free_direct_key(dk); +} + +/* + * Find/insert the given key into the llcrypt_direct_keys table. If found, it + * is returned with elevated refcount, and 'to_insert' is freed if non-NULL. If + * not found, 'to_insert' is inserted and returned if it's non-NULL; otherwise + * NULL is returned. + */ +static struct llcrypt_direct_key * +find_or_insert_direct_key(struct llcrypt_direct_key *to_insert, + const u8 *raw_key, const struct llcrypt_info *ci) +{ + unsigned long hash_key; + struct llcrypt_direct_key *dk; + + /* + * Careful: to avoid potentially leaking secret key bytes via timing + * information, we must key the hash table by descriptor rather than by + * raw key, and use crypto_memneq() when comparing raw keys. + */ + + BUILD_BUG_ON(sizeof(hash_key) > LLCRYPT_KEY_DESCRIPTOR_SIZE); + memcpy(&hash_key, ci->ci_policy.v1.master_key_descriptor, + sizeof(hash_key)); + + spin_lock(&llcrypt_direct_keys_lock); + hash_for_each_possible(llcrypt_direct_keys, dk, dk_node, hash_key) { + if (memcmp(ci->ci_policy.v1.master_key_descriptor, + dk->dk_descriptor, LLCRYPT_KEY_DESCRIPTOR_SIZE) != 0) + continue; + if (ci->ci_mode != dk->dk_mode) + continue; + if (crypto_memneq(raw_key, dk->dk_raw, ci->ci_mode->keysize)) + continue; + /* using existing tfm with same (descriptor, mode, raw_key) */ + refcount_inc(&dk->dk_refcount); + spin_unlock(&llcrypt_direct_keys_lock); + free_direct_key(to_insert); + return dk; + } + if (to_insert) + hash_add(llcrypt_direct_keys, &to_insert->dk_node, hash_key); + spin_unlock(&llcrypt_direct_keys_lock); + return to_insert; +} + +/* Prepare to encrypt directly using the master key in the given mode */ +static struct llcrypt_direct_key * +llcrypt_get_direct_key(const struct llcrypt_info *ci, const u8 *raw_key) +{ + struct llcrypt_direct_key *dk; + int err; + + /* Is there already a tfm for this key? */ + dk = find_or_insert_direct_key(NULL, raw_key, ci); + if (dk) + return dk; + + /* Nope, allocate one. */ + dk = kzalloc(sizeof(*dk), GFP_NOFS); + if (!dk) + return ERR_PTR(-ENOMEM); + refcount_set(&dk->dk_refcount, 1); + dk->dk_mode = ci->ci_mode; + dk->dk_ctfm = llcrypt_allocate_skcipher(ci->ci_mode, raw_key, + ci->ci_inode); + if (IS_ERR(dk->dk_ctfm)) { + err = PTR_ERR(dk->dk_ctfm); + dk->dk_ctfm = NULL; + goto err_free_dk; + } + memcpy(dk->dk_descriptor, ci->ci_policy.v1.master_key_descriptor, + LLCRYPT_KEY_DESCRIPTOR_SIZE); + memcpy(dk->dk_raw, raw_key, ci->ci_mode->keysize); + + return find_or_insert_direct_key(dk, raw_key, ci); + +err_free_dk: + free_direct_key(dk); + return ERR_PTR(err); +} + +/* v1 policy, DIRECT_KEY: use the master key directly */ +static int setup_v1_file_key_direct(struct llcrypt_info *ci, + const u8 *raw_master_key) +{ + const struct llcrypt_mode *mode = ci->ci_mode; + struct llcrypt_direct_key *dk; + + if (!llcrypt_mode_supports_direct_key(mode)) { + llcrypt_warn(ci->ci_inode, + "Direct key mode not allowed with %s", + mode->friendly_name); + return -EINVAL; + } + + if (ci->ci_policy.v1.contents_encryption_mode != + ci->ci_policy.v1.filenames_encryption_mode) { + llcrypt_warn(ci->ci_inode, + "Direct key mode not allowed with different contents and filenames modes"); + return -EINVAL; + } + + /* ESSIV implies 16-byte IVs which implies !DIRECT_KEY */ + if (WARN_ON(mode->needs_essiv)) + return -EINVAL; + + dk = llcrypt_get_direct_key(ci, raw_master_key); + if (IS_ERR(dk)) + return PTR_ERR(dk); + ci->ci_direct_key = dk; + ci->ci_ctfm = dk->dk_ctfm; + return 0; +} + +/* v1 policy, !DIRECT_KEY: derive the file's encryption key */ +static int setup_v1_file_key_derived(struct llcrypt_info *ci, + const u8 *raw_master_key) +{ + u8 *derived_key; + int err; + + /* + * This cannot be a stack buffer because it will be passed to the + * scatterlist crypto API during derive_key_aes(). + */ + derived_key = kmalloc(ci->ci_mode->keysize, GFP_NOFS); + if (!derived_key) + return -ENOMEM; + + err = derive_key_aes(raw_master_key, ci->ci_nonce, + derived_key, ci->ci_mode->keysize); + if (err) + goto out; + + err = llcrypt_set_derived_key(ci, derived_key); +out: + kzfree(derived_key); + return err; +} + +int llcrypt_setup_v1_file_key(struct llcrypt_info *ci, const u8 *raw_master_key) +{ + if (ci->ci_policy.v1.flags & LLCRYPT_POLICY_FLAG_DIRECT_KEY) + return setup_v1_file_key_direct(ci, raw_master_key); + else + return setup_v1_file_key_derived(ci, raw_master_key); +} + +int llcrypt_setup_v1_file_key_via_subscribed_keyrings(struct llcrypt_info *ci) +{ + struct key *key; + const struct llcrypt_key *payload; + int err; + + key = find_and_lock_process_key(LLCRYPT_KEY_DESC_PREFIX, + ci->ci_policy.v1.master_key_descriptor, + ci->ci_mode->keysize, &payload); + if (key == ERR_PTR(-ENOKEY)) { + struct lustre_sb_info *lsi = s2lsi(ci->ci_inode->i_sb); + + if (lsi && lsi->lsi_cop->key_prefix) { + key = + find_and_lock_process_key(lsi->lsi_cop->key_prefix, + ci->ci_policy.v1.master_key_descriptor, + ci->ci_mode->keysize, + &payload); + } + } + if (IS_ERR(key)) + return PTR_ERR(key); + + err = llcrypt_setup_v1_file_key(ci, payload->raw); + up_read(&key->sem); + key_put(key); + return err; +} diff --git a/libcfs/libcfs/crypto/llcrypt_private.h b/libcfs/libcfs/crypto/llcrypt_private.h new file mode 100644 index 0000000..fbf7c73 --- /dev/null +++ b/libcfs/libcfs/crypto/llcrypt_private.h @@ -0,0 +1,496 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * llcrypt_private.h + * + * Copyright (C) 2015, Google, Inc. + * + * Originally written by Michael Halcrow, Ildar Muslukhov, and Uday Savagaonkar. + * Heavily modified since then. + */ +/* + * Linux commit 219d54332a09 + * tags/v5.4 + */ + +#ifndef _LLCRYPT_PRIVATE_H +#define _LLCRYPT_PRIVATE_H + +#include +#include +#include + +#ifndef CRYPTO_TFM_REQ_FORBID_WEAK_KEYS +#define CRYPTO_TFM_REQ_FORBID_WEAK_KEYS CRYPTO_TFM_REQ_WEAK_KEY +#endif + +#define llcrypt_info(inode) ((struct llcrypt_info *)(inode)->i_private) +#define llcrypt_info_nocast(inode) ((inode)->i_private) + +#define CONST_STRLEN(str) (sizeof(str) - 1) + +#define FS_KEY_DERIVATION_NONCE_SIZE 16 + +#define LLCRYPT_MIN_KEY_SIZE 16 + +#define LLCRYPT_CONTEXT_V1 1 +#define LLCRYPT_CONTEXT_V2 2 + +struct llcrypt_context_v1 { + u8 version; /* LLCRYPT_CONTEXT_V1 */ + u8 contents_encryption_mode; + u8 filenames_encryption_mode; + u8 flags; + u8 master_key_descriptor[LLCRYPT_KEY_DESCRIPTOR_SIZE]; + u8 nonce[FS_KEY_DERIVATION_NONCE_SIZE]; +}; + +struct llcrypt_context_v2 { + u8 version; /* LLCRYPT_CONTEXT_V2 */ + u8 contents_encryption_mode; + u8 filenames_encryption_mode; + u8 flags; + u8 __reserved[4]; + u8 master_key_identifier[LLCRYPT_KEY_IDENTIFIER_SIZE]; + u8 nonce[FS_KEY_DERIVATION_NONCE_SIZE]; +}; + +/** + * llcrypt_context - the encryption context of an inode + * + * This is the on-disk equivalent of an llcrypt_policy, stored alongside each + * encrypted file usually in a hidden extended attribute. It contains the + * fields from the llcrypt_policy, in order to identify the encryption algorithm + * and key with which the file is encrypted. It also contains a nonce that was + * randomly generated by llcrypt itself; this is used as KDF input or as a tweak + * to cause different files to be encrypted differently. + */ +union llcrypt_context { + u8 version; + struct llcrypt_context_v1 v1; + struct llcrypt_context_v2 v2; +}; + +/* + * Return the size expected for the given llcrypt_context based on its version + * number, or 0 if the context version is unrecognized. + */ +static inline int llcrypt_context_size(const union llcrypt_context *ctx) +{ + switch (ctx->version) { + case LLCRYPT_CONTEXT_V1: + BUILD_BUG_ON(sizeof(ctx->v1) != 28); + return sizeof(ctx->v1); + case LLCRYPT_CONTEXT_V2: + BUILD_BUG_ON(sizeof(ctx->v2) != 40); + return sizeof(ctx->v2); + } + return 0; +} + +#undef llcrypt_policy +union llcrypt_policy { + u8 version; + struct llcrypt_policy_v1 v1; + struct llcrypt_policy_v2 v2; +}; + +/* + * Return the size expected for the given llcrypt_policy based on its version + * number, or 0 if the policy version is unrecognized. + */ +static inline int llcrypt_policy_size(const union llcrypt_policy *policy) +{ + switch (policy->version) { + case LLCRYPT_POLICY_V1: + return sizeof(policy->v1); + case LLCRYPT_POLICY_V2: + return sizeof(policy->v2); + } + return 0; +} + +/* Return the contents encryption mode of a valid encryption policy */ +static inline u8 +llcrypt_policy_contents_mode(const union llcrypt_policy *policy) +{ + switch (policy->version) { + case LLCRYPT_POLICY_V1: + return policy->v1.contents_encryption_mode; + case LLCRYPT_POLICY_V2: + return policy->v2.contents_encryption_mode; + } + BUG(); +} + +/* Return the filenames encryption mode of a valid encryption policy */ +static inline u8 +llcrypt_policy_fnames_mode(const union llcrypt_policy *policy) +{ + switch (policy->version) { + case LLCRYPT_POLICY_V1: + return policy->v1.filenames_encryption_mode; + case LLCRYPT_POLICY_V2: + return policy->v2.filenames_encryption_mode; + } + BUG(); +} + +/* Return the flags (LLCRYPT_POLICY_FLAG*) of a valid encryption policy */ +static inline u8 +llcrypt_policy_flags(const union llcrypt_policy *policy) +{ + switch (policy->version) { + case LLCRYPT_POLICY_V1: + return policy->v1.flags; + case LLCRYPT_POLICY_V2: + return policy->v2.flags; + } + BUG(); +} + +static inline bool +llcrypt_is_direct_key_policy(const union llcrypt_policy *policy) +{ + return llcrypt_policy_flags(policy) & LLCRYPT_POLICY_FLAG_DIRECT_KEY; +} + +/** + * For encrypted symlinks, the ciphertext length is stored at the beginning + * of the string in little-endian format. + */ +struct llcrypt_symlink_data { + __le16 len; + char encrypted_path[1]; +} __packed; + +/* + * llcrypt_info - the "encryption key" for an inode + * + * When an encrypted file's key is made available, an instance of this struct is + * allocated and stored in '(struct llcrypt_info *)inode->i_private'. + * Once created, it remains until the inode is evicted. + */ +struct llcrypt_info { + + /* The actual crypto transform used for encryption and decryption */ + struct crypto_skcipher *ci_ctfm; + + /* + * Cipher for ESSIV IV generation. Only set for CBC contents + * encryption, otherwise is NULL. + */ + struct crypto_cipher *ci_essiv_tfm; + + /* + * Encryption mode used for this inode. It corresponds to either the + * contents or filenames encryption mode, depending on the inode type. + */ + struct llcrypt_mode *ci_mode; + + /* Back-pointer to the inode */ + struct inode *ci_inode; + + /* + * The master key with which this inode was unlocked (decrypted). This + * will be NULL if the master key was found in a process-subscribed + * keyring rather than in the filesystem-level keyring. + */ + struct key *ci_master_key; + + /* + * Link in list of inodes that were unlocked with the master key. + * Only used when ->ci_master_key is set. + */ + struct list_head ci_master_key_link; + + /* + * If non-NULL, then encryption is done using the master key directly + * and ci_ctfm will equal ci_direct_key->dk_ctfm. + */ + struct llcrypt_direct_key *ci_direct_key; + + /* The encryption policy used by this inode */ + union llcrypt_policy ci_policy; + + /* This inode's nonce, copied from the llcrypt_context */ + u8 ci_nonce[FS_KEY_DERIVATION_NONCE_SIZE]; +}; + +typedef enum { + FS_DECRYPT = 0, + FS_ENCRYPT, +} llcrypt_direction_t; + +#define FS_CTX_REQUIRES_FREE_ENCRYPT_FL 0x00000001 + +static inline bool llcrypt_valid_enc_modes(u32 contents_mode, + u32 filenames_mode) +{ + if (contents_mode == LLCRYPT_MODE_AES_128_CBC && + filenames_mode == LLCRYPT_MODE_AES_128_CTS) + return true; + + if (contents_mode == LLCRYPT_MODE_AES_256_XTS && + filenames_mode == LLCRYPT_MODE_AES_256_CTS) + return true; + + if (contents_mode == LLCRYPT_MODE_ADIANTUM && + filenames_mode == LLCRYPT_MODE_ADIANTUM) + return true; + + return false; +} + +/* crypto.c */ +extern struct kmem_cache *llcrypt_info_cachep; +extern int llcrypt_initialize(unsigned int cop_flags); +extern int llcrypt_crypt_block(const struct inode *inode, + llcrypt_direction_t rw, u64 lblk_num, + struct page *src_page, struct page *dest_page, + unsigned int len, unsigned int offs, + gfp_t gfp_flags); +extern struct page *llcrypt_alloc_bounce_page(gfp_t gfp_flags); +extern const struct dentry_operations llcrypt_d_ops; + +extern void __printf(3, 4) __cold +llcrypt_msg(const struct inode *inode, int mask, const char *fmt, ...); + +#define llcrypt_warn(inode, fmt, ...) \ + llcrypt_msg((inode), D_SEC, fmt, ##__VA_ARGS__) +#define llcrypt_err(inode, fmt, ...) \ + llcrypt_msg((inode), D_ERROR, fmt, ##__VA_ARGS__) + +#define LLCRYPT_MAX_IV_SIZE 32 + +union llcrypt_iv { + struct { + /* logical block number within the file */ + __le64 lblk_num; + + /* per-file nonce; only set in DIRECT_KEY mode */ + u8 nonce[FS_KEY_DERIVATION_NONCE_SIZE]; + }; + u8 raw[LLCRYPT_MAX_IV_SIZE]; +}; + +void llcrypt_generate_iv(union llcrypt_iv *iv, u64 lblk_num, + const struct llcrypt_info *ci); + +/* fname.c */ +extern int fname_encrypt(struct inode *inode, const struct qstr *iname, + u8 *out, unsigned int olen); +extern bool llcrypt_fname_encrypted_size(const struct inode *inode, + u32 orig_len, u32 max_len, + u32 *encrypted_len_ret); + +/* hkdf.c */ + +struct llcrypt_hkdf { + struct crypto_shash *hmac_tfm; +}; + +extern int llcrypt_init_hkdf(struct llcrypt_hkdf *hkdf, const u8 *master_key, + unsigned int master_key_size); + +/* + * The list of contexts in which llcrypt uses HKDF. These values are used as + * the first byte of the HKDF application-specific info string to guarantee that + * info strings are never repeated between contexts. This ensures that all HKDF + * outputs are unique and cryptographically isolated, i.e. knowledge of one + * output doesn't reveal another. + */ +#define HKDF_CONTEXT_KEY_IDENTIFIER 1 +#define HKDF_CONTEXT_PER_FILE_KEY 2 +#define HKDF_CONTEXT_PER_MODE_KEY 3 + +extern int llcrypt_hkdf_expand(struct llcrypt_hkdf *hkdf, u8 context, + const u8 *info, unsigned int infolen, + u8 *okm, unsigned int okmlen); + +extern void llcrypt_destroy_hkdf(struct llcrypt_hkdf *hkdf); + +/* keyring.c */ + +/* + * llcrypt_master_key_secret - secret key material of an in-use master key + */ +struct llcrypt_master_key_secret { + + /* + * For v2 policy keys: HKDF context keyed by this master key. + * For v1 policy keys: not set (hkdf.hmac_tfm == NULL). + */ + struct llcrypt_hkdf hkdf; + + /* Size of the raw key in bytes. Set even if ->raw isn't set. */ + u32 size; + + /* For v1 policy keys: the raw key. Wiped for v2 policy keys. */ + u8 raw[LLCRYPT_MAX_KEY_SIZE]; + +} __randomize_layout; + +/* + * llcrypt_master_key - an in-use master key + * + * This represents a master encryption key which has been added to the + * filesystem and can be used to "unlock" the encrypted files which were + * encrypted with it. + */ +struct llcrypt_master_key { + + /* + * The secret key material. After LL_IOC_REMOVE_ENCRYPTION_KEY is + * executed, this is wiped and no new inodes can be unlocked with this + * key; however, there may still be inodes in ->mk_decrypted_inodes + * which could not be evicted. As long as some inodes still remain, + * LL_IOC_REMOVE_ENCRYPTION_KEY can be retried, or + * LL_IOC_ADD_ENCRYPTION_KEY can add the secret again. + * + * Locking: protected by key->sem (outer) and mk_secret_sem (inner). + * The reason for two locks is that key->sem also protects modifying + * mk_users, which ranks it above the semaphore for the keyring key + * type, which is in turn above page faults (via keyring_read). But + * sometimes filesystems call llcrypt_get_encryption_info() from within + * a transaction, which ranks it below page faults. So we need a + * separate lock which protects mk_secret but not also mk_users. + */ + struct llcrypt_master_key_secret mk_secret; + struct rw_semaphore mk_secret_sem; + + /* + * For v1 policy keys: an arbitrary key descriptor which was assigned by + * userspace (->descriptor). + * + * For v2 policy keys: a cryptographic hash of this key (->identifier). + */ + struct llcrypt_key_specifier mk_spec; + + /* + * Keyring which contains a key of type 'key_type_llcrypt_user' for each + * user who has added this key. Normally each key will be added by just + * one user, but it's possible that multiple users share a key, and in + * that case we need to keep track of those users so that one user can't + * remove the key before the others want it removed too. + * + * This is NULL for v1 policy keys; those can only be added by root. + * + * Locking: in addition to this keyrings own semaphore, this is + * protected by the master key's key->sem, so we can do atomic + * search+insert. It can also be searched without taking any locks, but + * in that case the returned key may have already been removed. + */ + struct key *mk_users; + + /* + * Length of ->mk_decrypted_inodes, plus one if mk_secret is present. + * Once this goes to 0, the master key is removed from ->lsi_master_keys. + * The 'struct llcrypt_master_key' will continue to live as long as the + * 'struct key' whose payload it is, but we won't let this reference + * count rise again. + */ + refcount_t mk_refcount; + + /* + * List of inodes that were unlocked using this key. This allows the + * inodes to be evicted efficiently if the key is removed. + */ + struct list_head mk_decrypted_inodes; + spinlock_t mk_decrypted_inodes_lock; + + /* Per-mode tfms for DIRECT_KEY policies, allocated on-demand */ + struct crypto_skcipher *mk_mode_keys[__LLCRYPT_MODE_MAX + 1]; + +} __randomize_layout; + +static inline bool +is_master_key_secret_present(const struct llcrypt_master_key_secret *secret) +{ + /* + * The READ_ONCE() is only necessary for llcrypt_drop_inode() and + * llcrypt_key_describe(). These run in atomic context, so they can't + * take ->mk_secret_sem and thus 'secret' can change concurrently which + * would be a data race. But they only need to know whether the secret + * *was* present at the time of check, so READ_ONCE() suffices. + */ + return READ_ONCE(secret->size) != 0; +} + +static inline const char *master_key_spec_type( + const struct llcrypt_key_specifier *spec) +{ + switch (spec->type) { + case LLCRYPT_KEY_SPEC_TYPE_DESCRIPTOR: + return "descriptor"; + case LLCRYPT_KEY_SPEC_TYPE_IDENTIFIER: + return "identifier"; + } + return "[unknown]"; +} + +static inline int master_key_spec_len(const struct llcrypt_key_specifier *spec) +{ + switch (spec->type) { + case LLCRYPT_KEY_SPEC_TYPE_DESCRIPTOR: + return LLCRYPT_KEY_DESCRIPTOR_SIZE; + case LLCRYPT_KEY_SPEC_TYPE_IDENTIFIER: + return LLCRYPT_KEY_IDENTIFIER_SIZE; + } + return 0; +} + +extern struct key * +llcrypt_find_master_key(struct super_block *sb, + const struct llcrypt_key_specifier *mk_spec); + +extern int llcrypt_verify_key_added(struct super_block *sb, + const u8 identifier[LLCRYPT_KEY_IDENTIFIER_SIZE]); + +extern int __init llcrypt_init_keyring(void); + +extern void __exit llcrypt_exit_keyring(void); + +/* keysetup.c */ + +struct llcrypt_mode { + const char *friendly_name; + const char *cipher_str; + int keysize; + int ivsize; + bool logged_impl_name; + bool needs_essiv; +}; + +static inline bool +llcrypt_mode_supports_direct_key(const struct llcrypt_mode *mode) +{ + return mode->ivsize >= offsetofend(union llcrypt_iv, nonce); +} + +extern struct crypto_skcipher * +llcrypt_allocate_skcipher(struct llcrypt_mode *mode, const u8 *raw_key, + const struct inode *inode); + +extern int llcrypt_set_derived_key(struct llcrypt_info *ci, + const u8 *derived_key); + +/* keysetup_v1.c */ + +extern void llcrypt_put_direct_key(struct llcrypt_direct_key *dk); + +extern int llcrypt_setup_v1_file_key(struct llcrypt_info *ci, + const u8 *raw_master_key); + +extern int llcrypt_setup_v1_file_key_via_subscribed_keyrings( + struct llcrypt_info *ci); +/* policy.c */ + +extern bool llcrypt_policies_equal(const union llcrypt_policy *policy1, + const union llcrypt_policy *policy2); +extern bool llcrypt_supported_policy(const union llcrypt_policy *policy_u, + const struct inode *inode); +extern int llcrypt_policy_from_context(union llcrypt_policy *policy_u, + const union llcrypt_context *ctx_u, + int ctx_size); + +#endif /* _LLCRYPT_PRIVATE_H */ diff --git a/libcfs/libcfs/crypto/policy.c b/libcfs/libcfs/crypto/policy.c new file mode 100644 index 0000000..bdfc683 --- /dev/null +++ b/libcfs/libcfs/crypto/policy.c @@ -0,0 +1,511 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Encryption policy functions for per-file encryption support. + * + * Copyright (C) 2015, Google, Inc. + * Copyright (C) 2015, Motorola Mobility. + * + * Originally written by Michael Halcrow, 2015. + * Modified by Jaegeuk Kim, 2015. + * Modified by Eric Biggers, 2019 for v2 policy support. + */ +/* + * Linux commit 219d54332a09 + * tags/v5.4 + */ + +#include +#include +#include +#include "llcrypt_private.h" + +/** + * llcrypt_policies_equal - check whether two encryption policies are the same + * + * Return: %true if equal, else %false + */ +bool llcrypt_policies_equal(const union llcrypt_policy *policy1, + const union llcrypt_policy *policy2) +{ + if (policy1->version != policy2->version) + return false; + + return !memcmp(policy1, policy2, llcrypt_policy_size(policy1)); +} + +/** + * llcrypt_supported_policy - check whether an encryption policy is supported + * + * Given an encryption policy, check whether all its encryption modes and other + * settings are supported by this kernel. (But we don't currently don't check + * for crypto API support here, so attempting to use an algorithm not configured + * into the crypto API will still fail later.) + * + * Return: %true if supported, else %false + */ +bool llcrypt_supported_policy(const union llcrypt_policy *policy_u, + const struct inode *inode) +{ + switch (policy_u->version) { + case LLCRYPT_POLICY_V1: { + const struct llcrypt_policy_v1 *policy = &policy_u->v1; + + if (!llcrypt_valid_enc_modes(policy->contents_encryption_mode, + policy->filenames_encryption_mode)) { + llcrypt_warn(inode, + "Unsupported encryption modes (contents %d, filenames %d)", + policy->contents_encryption_mode, + policy->filenames_encryption_mode); + return false; + } + + if (policy->flags & ~LLCRYPT_POLICY_FLAGS_VALID) { + llcrypt_warn(inode, + "Unsupported encryption flags (0x%02x)", + policy->flags); + return false; + } + + return true; + } + case LLCRYPT_POLICY_V2: { + const struct llcrypt_policy_v2 *policy = &policy_u->v2; + + if (!llcrypt_valid_enc_modes(policy->contents_encryption_mode, + policy->filenames_encryption_mode)) { + llcrypt_warn(inode, + "Unsupported encryption modes (contents %d, filenames %d)", + policy->contents_encryption_mode, + policy->filenames_encryption_mode); + return false; + } + + if (policy->flags & ~LLCRYPT_POLICY_FLAGS_VALID) { + llcrypt_warn(inode, + "Unsupported encryption flags (0x%02x)", + policy->flags); + return false; + } + + if (memchr_inv(policy->__reserved, 0, + sizeof(policy->__reserved))) { + llcrypt_warn(inode, + "Reserved bits set in encryption policy"); + return false; + } + + return true; + } + } + return false; +} + +/** + * llcrypt_new_context_from_policy - create a new llcrypt_context from a policy + * + * Create an llcrypt_context for an inode that is being assigned the given + * encryption policy. A new nonce is randomly generated. + * + * Return: the size of the new context in bytes. + */ +static int llcrypt_new_context_from_policy(union llcrypt_context *ctx_u, + const union llcrypt_policy *policy_u) +{ + memset(ctx_u, 0, sizeof(*ctx_u)); + + switch (policy_u->version) { + case LLCRYPT_POLICY_V1: { + const struct llcrypt_policy_v1 *policy = &policy_u->v1; + struct llcrypt_context_v1 *ctx = &ctx_u->v1; + + ctx->version = LLCRYPT_CONTEXT_V1; + ctx->contents_encryption_mode = + policy->contents_encryption_mode; + ctx->filenames_encryption_mode = + policy->filenames_encryption_mode; + ctx->flags = policy->flags; + memcpy(ctx->master_key_descriptor, + policy->master_key_descriptor, + sizeof(ctx->master_key_descriptor)); + get_random_bytes(ctx->nonce, sizeof(ctx->nonce)); + return sizeof(*ctx); + } + case LLCRYPT_POLICY_V2: { + const struct llcrypt_policy_v2 *policy = &policy_u->v2; + struct llcrypt_context_v2 *ctx = &ctx_u->v2; + + ctx->version = LLCRYPT_CONTEXT_V2; + ctx->contents_encryption_mode = + policy->contents_encryption_mode; + ctx->filenames_encryption_mode = + policy->filenames_encryption_mode; + ctx->flags = policy->flags; + memcpy(ctx->master_key_identifier, + policy->master_key_identifier, + sizeof(ctx->master_key_identifier)); + get_random_bytes(ctx->nonce, sizeof(ctx->nonce)); + return sizeof(*ctx); + } + } + BUG(); +} + +/** + * llcrypt_policy_from_context - convert an llcrypt_context to an llcrypt_policy + * + * Given an llcrypt_context, build the corresponding llcrypt_policy. + * + * Return: 0 on success, or -EINVAL if the llcrypt_context has an unrecognized + * version number or size. + * + * This does *not* validate the settings within the policy itself, e.g. the + * modes, flags, and reserved bits. Use llcrypt_supported_policy() for that. + */ +int llcrypt_policy_from_context(union llcrypt_policy *policy_u, + const union llcrypt_context *ctx_u, + int ctx_size) +{ + memset(policy_u, 0, sizeof(*policy_u)); + + if (ctx_size <= 0 || ctx_size != llcrypt_context_size(ctx_u)) + return -EINVAL; + + switch (ctx_u->version) { + case LLCRYPT_CONTEXT_V1: { + const struct llcrypt_context_v1 *ctx = &ctx_u->v1; + struct llcrypt_policy_v1 *policy = &policy_u->v1; + + policy->version = LLCRYPT_POLICY_V1; + policy->contents_encryption_mode = + ctx->contents_encryption_mode; + policy->filenames_encryption_mode = + ctx->filenames_encryption_mode; + policy->flags = ctx->flags; + memcpy(policy->master_key_descriptor, + ctx->master_key_descriptor, + sizeof(policy->master_key_descriptor)); + return 0; + } + case LLCRYPT_CONTEXT_V2: { + const struct llcrypt_context_v2 *ctx = &ctx_u->v2; + struct llcrypt_policy_v2 *policy = &policy_u->v2; + + policy->version = LLCRYPT_POLICY_V2; + policy->contents_encryption_mode = + ctx->contents_encryption_mode; + policy->filenames_encryption_mode = + ctx->filenames_encryption_mode; + policy->flags = ctx->flags; + memcpy(policy->__reserved, ctx->__reserved, + sizeof(policy->__reserved)); + memcpy(policy->master_key_identifier, + ctx->master_key_identifier, + sizeof(policy->master_key_identifier)); + return 0; + } + } + /* unreachable */ + return -EINVAL; +} + +/* Retrieve an inode's encryption policy */ +static int llcrypt_get_policy(struct inode *inode, union llcrypt_policy *policy) +{ + const struct llcrypt_info *ci; + union llcrypt_context ctx; + struct lustre_sb_info *lsi = s2lsi(inode->i_sb); + int ret; + + ci = (struct llcrypt_info *)READ_ONCE(llcrypt_info_nocast(inode)); + if (ci) { + /* key available, use the cached policy */ + *policy = ci->ci_policy; + return 0; + } + + if (!IS_ENCRYPTED(inode)) + return -ENODATA; + + if (!lsi) + return -ENODATA; + + ret = lsi->lsi_cop->get_context(inode, &ctx, sizeof(ctx)); + if (ret < 0) + return (ret == -ERANGE) ? -EINVAL : ret; + + return llcrypt_policy_from_context(policy, &ctx, ret); +} + +static int set_encryption_policy(struct inode *inode, + const union llcrypt_policy *policy) +{ + union llcrypt_context ctx; + int ctxsize; + struct lustre_sb_info *lsi = s2lsi(inode->i_sb); + int err; + + if (!llcrypt_supported_policy(policy, inode)) + return -EINVAL; + + switch (policy->version) { + case LLCRYPT_POLICY_V1: + /* + * The original encryption policy version provided no way of + * verifying that the correct master key was supplied, which was + * insecure in scenarios where multiple users have access to the + * same encrypted files (even just read-only access). The new + * encryption policy version fixes this and also implies use of + * an improved key derivation function and allows non-root users + * to securely remove keys. So as long as compatibility with + * old kernels isn't required, it is recommended to use the new + * policy version for all new encrypted directories. + */ + pr_warn_once("%s (pid %d) is setting deprecated v1 encryption policy; recommend upgrading to v2.\n", + current->comm, current->pid); + break; + case LLCRYPT_POLICY_V2: + err = llcrypt_verify_key_added(inode->i_sb, + policy->v2.master_key_identifier); + if (err) + return err; + break; + default: + WARN_ON(1); + return -EINVAL; + } + + ctxsize = llcrypt_new_context_from_policy(&ctx, policy); + + if (!lsi) + return -EINVAL; + + return lsi->lsi_cop->set_context(inode, &ctx, ctxsize, NULL); +} + +int llcrypt_ioctl_set_policy(struct file *filp, const void __user *arg) +{ + union llcrypt_policy policy; + union llcrypt_policy existing_policy; + struct inode *inode = file_inode(filp); + u8 version; + int size; + int ret; + + if (get_user(policy.version, (const u8 __user *)arg)) + return -EFAULT; + + size = llcrypt_policy_size(&policy); + if (size <= 0) + return -EINVAL; + + /* + * We should just copy the remaining 'size - 1' bytes here, but a + * bizarre bug in gcc 7 and earlier (fixed by gcc r255731) causes gcc to + * think that size can be 0 here (despite the check above!) *and* that + * it's a compile-time constant. Thus it would think copy_from_user() + * is passed compile-time constant ULONG_MAX, causing the compile-time + * buffer overflow check to fail, breaking the build. This only occurred + * when building an i386 kernel with -Os and branch profiling enabled. + * + * Work around it by just copying the first byte again... + */ + version = policy.version; + if (copy_from_user(&policy, arg, size)) + return -EFAULT; + policy.version = version; + + if (!inode_owner_or_capable(inode)) + return -EACCES; + + ret = mnt_want_write_file(filp); + if (ret) + return ret; + + inode_lock(inode); + + ret = llcrypt_get_policy(inode, &existing_policy); + if (ret == -ENODATA) { + struct lustre_sb_info *lsi = s2lsi(inode->i_sb); + + if (!S_ISDIR(inode->i_mode)) + ret = -ENOTDIR; + else if (IS_DEADDIR(inode)) + ret = -ENOENT; + else if (lsi && !lsi->lsi_cop->empty_dir(inode)) + ret = -ENOTEMPTY; + else + ret = set_encryption_policy(inode, &policy); + } else if (ret == -EINVAL || + (ret == 0 && !llcrypt_policies_equal(&policy, + &existing_policy))) { + /* The file already uses a different encryption policy. */ + ret = -EEXIST; + } + + inode_unlock(inode); + + mnt_drop_write_file(filp); + return ret; +} +EXPORT_SYMBOL(llcrypt_ioctl_set_policy); + +/* Original ioctl version; can only get the original policy version */ +int llcrypt_ioctl_get_policy(struct file *filp, void __user *arg) +{ + union llcrypt_policy policy; + int err; + + err = llcrypt_get_policy(file_inode(filp), &policy); + if (err) + return err; + + if (policy.version != LLCRYPT_POLICY_V1) + return -EINVAL; + + if (copy_to_user(arg, &policy, sizeof(policy.v1))) + return -EFAULT; + return 0; +} +EXPORT_SYMBOL(llcrypt_ioctl_get_policy); + +/* Extended ioctl version; can get policies of any version */ +int llcrypt_ioctl_get_policy_ex(struct file *filp, void __user *uarg) +{ + struct llcrypt_get_policy_ex_arg arg; + union llcrypt_policy *policy = (union llcrypt_policy *)&arg.policy; + size_t policy_size; + int err; + + /* arg is policy_size, then policy */ + BUILD_BUG_ON(offsetof(typeof(arg), policy_size) != 0); + BUILD_BUG_ON(offsetofend(typeof(arg), policy_size) != + offsetof(typeof(arg), policy)); + BUILD_BUG_ON(sizeof(arg.policy) != sizeof(*policy)); + + err = llcrypt_get_policy(file_inode(filp), policy); + if (err) + return err; + policy_size = llcrypt_policy_size(policy); + + if (copy_from_user(&arg, uarg, sizeof(arg.policy_size))) + return -EFAULT; + + if (policy_size > arg.policy_size) + return -EOVERFLOW; + arg.policy_size = policy_size; + + if (copy_to_user(uarg, &arg, sizeof(arg.policy_size) + policy_size)) + return -EFAULT; + return 0; +} +EXPORT_SYMBOL_GPL(llcrypt_ioctl_get_policy_ex); + +/** + * llcrypt_has_permitted_context() - is a file's encryption policy permitted + * within its directory? + * + * @parent: inode for parent directory + * @child: inode for file being looked up, opened, or linked into @parent + * + * Filesystems must call this before permitting access to an inode in a + * situation where the parent directory is encrypted (either before allowing + * ->lookup() to succeed, or for a regular file before allowing it to be opened) + * and before any operation that involves linking an inode into an encrypted + * directory, including link, rename, and cross rename. It enforces the + * constraint that within a given encrypted directory tree, all files use the + * same encryption policy. The pre-access check is needed to detect potentially + * malicious offline violations of this constraint, while the link and rename + * checks are needed to prevent online violations of this constraint. + * + * Return: 1 if permitted, 0 if forbidden. + */ +int llcrypt_has_permitted_context(struct inode *parent, struct inode *child) +{ + union llcrypt_policy parent_policy, child_policy; + int err; + + /* No restrictions on file types which are never encrypted */ + if (!S_ISREG(child->i_mode) && !S_ISDIR(child->i_mode) && + !S_ISLNK(child->i_mode)) + return 1; + + /* No restrictions if the parent directory is unencrypted */ + if (!IS_ENCRYPTED(parent)) + return 1; + + /* Encrypted directories must not contain unencrypted files */ + if (!IS_ENCRYPTED(child)) + return 0; + + /* + * Both parent and child are encrypted, so verify they use the same + * encryption policy. Compare the llcrypt_info structs if the keys are + * available, otherwise retrieve and compare the llcrypt_contexts. + * + * Note that the llcrypt_context retrieval will be required frequently + * when accessing an encrypted directory tree without the key. + * Performance-wise this is not a big deal because we already don't + * really optimize for file access without the key (to the extent that + * such access is even possible), given that any attempted access + * already causes a llcrypt_context retrieval and keyring search. + * + * In any case, if an unexpected error occurs, fall back to "forbidden". + */ + + err = llcrypt_get_encryption_info(parent); + if (err) + return 0; + err = llcrypt_get_encryption_info(child); + if (err) + return 0; + + err = llcrypt_get_policy(parent, &parent_policy); + if (err) + return 0; + + err = llcrypt_get_policy(child, &child_policy); + if (err) + return 0; + + return llcrypt_policies_equal(&parent_policy, &child_policy); +} +EXPORT_SYMBOL(llcrypt_has_permitted_context); + +/** + * llcrypt_inherit_context() - Sets a child context from its parent + * @parent: Parent inode from which the context is inherited. + * @child: Child inode that inherits the context from @parent. + * @fs_data: private data given by FS. + * @preload: preload child crypt info if true + * + * Return: 0 on success, -errno on failure + */ +int llcrypt_inherit_context(struct inode *parent, struct inode *child, + void *fs_data, bool preload) +{ + union llcrypt_context ctx; + int ctxsize; + struct llcrypt_info *ci; + struct lustre_sb_info *lsi = s2lsi(parent->i_sb); + int res; + + res = llcrypt_get_encryption_info(parent); + if (res < 0) + return res; + + ci = (struct llcrypt_info *)READ_ONCE(llcrypt_info_nocast(parent)); + if (ci == NULL) + return -ENOKEY; + + if (!lsi) + return -ENOKEY; + + ctxsize = llcrypt_new_context_from_policy(&ctx, &ci->ci_policy); + + BUILD_BUG_ON(sizeof(ctx) != LLCRYPT_SET_CONTEXT_MAX_SIZE); + res = lsi->lsi_cop->set_context(child, &ctx, ctxsize, fs_data); + if (res) + return res; + return preload ? llcrypt_get_encryption_info(child): 0; +} +EXPORT_SYMBOL(llcrypt_inherit_context); diff --git a/libcfs/libcfs/module.c b/libcfs/libcfs/module.c index fd6f34b..8b165cc 100644 --- a/libcfs/libcfs/module.c +++ b/libcfs/libcfs/module.c @@ -54,6 +54,7 @@ #include #include #include +#include #include "tracefile.h" static struct dentry *lnet_debugfs_root; @@ -763,6 +764,12 @@ static int __init libcfs_init(void) if (!IS_ERR_OR_NULL(lnet_debugfs_root)) lnet_insert_debugfs_links(lnet_debugfs_symlinks); + rc = llcrypt_init(); + if (rc) { + CERROR("llcrypt_init: error %d\n", rc); + goto cleanup_wi; + } + CDEBUG (D_OTHER, "portals setup OK\n"); return 0; cleanup_wi: @@ -787,6 +794,8 @@ static void __exit libcfs_exit(void) CDEBUG(D_MALLOC, "before Portals cleanup: kmem %d\n", atomic_read(&libcfs_kmemory)); + llcrypt_exit(); + if (cfs_rehash_wq) { destroy_workqueue(cfs_rehash_wq); cfs_rehash_wq = NULL; diff --git a/lustre/autoconf/lustre-core.m4 b/lustre/autoconf/lustre-core.m4 index 3ab5094..74d9743 100644 --- a/lustre/autoconf/lustre-core.m4 +++ b/lustre/autoconf/lustre-core.m4 @@ -1878,6 +1878,22 @@ bi_bdev, [ ]) # LC_BI_BDEV # +# LC_IS_ENCRYPTED +# +# 4.14 introduced IS_ENCRYPTED and S_ENCRYPTED +# +AC_DEFUN([LC_IS_ENCRYPTED], [ +LB_CHECK_COMPILE([if IS_ENCRYPTED is defined], +is_encrypted, [ + #include +],[ + IS_ENCRYPTED((struct inode *)0); +],[ + has_is_encrypted="yes" +]) +]) # LC_IS_ENCRYPTED + +# # LC_I_PAGES # # kernel 4.17 commit b93b016313b3ba8003c3b8bb71f569af91f19fc7 @@ -2058,6 +2074,28 @@ EXTRA_KCFLAGS="$tmp_flags" ]) # LC_HAS_LINUX_SELINUX_ENABLED # +# LB_HAVE_BVEC_ITER_ALL +# +# kernel 5.1 commit 6dc4f100c175dd0511ae8674786e7c9006cdfbfa +# block: allow bio_for_each_segment_all() to iterate over multi-page bvec +# +AC_DEFUN([LB_HAVE_BVEC_ITER_ALL], [ +tmp_flags="$EXTRA_KCFLAGS" +EXTRA_KCFLAGS="-Werror" +LB_CHECK_COMPILE([if bvec_iter_all exists for multi-page bvec iternation], +ext4fs_dirhash, [ + #include +],[ + struct bvec_iter_all iter; + (void)iter; +],[ + AC_DEFINE(HAVE_BVEC_ITER_ALL, 1, + [if bvec_iter_all exists for multi-page bvec iternation]) +]) +EXTRA_KCFLAGS="$tmp_flags" +]) # LB_HAVE_BVEC_ITER_ALL + +# # LC_ACCOUNT_PAGE_DIRTIED # # After 5.2 kernel page dirtied is not exported @@ -2069,6 +2107,25 @@ LB_CHECK_EXPORT([account_page_dirtied], [mm/page-writeback.c], ]) # LC_ACCOUNT_PAGE_DIRTIED # +# LC_KEYRING_SEARCH_4ARGS +# +# Kernel 5.2 commit dcf49dbc8077 +# keys: Add a 'recurse' flag for keyring searches +# +AC_DEFUN([LC_KEYRING_SEARCH_4ARGS], [ +LB_CHECK_COMPILE([if 'keyring_search' has 4 args], +keyring_search_4args, [ + #include +],[ + key_ref_t keyring; + keyring_search(keyring, NULL, NULL, false); +],[ + AC_DEFINE(HAVE_KEYRING_SEARCH_4ARGS, 1, + [keyring_search has 4 args]) +]) +]) # LC_KEYRING_SEARCH_4ARGS + +# # LC_BIO_BI_PHYS_SEGMENTS # # kernel 5.3-rc1 commit 14ccb66b3f585b2bc21e7256c96090abed5a512c @@ -2114,6 +2171,23 @@ EXTRA_KCFLAGS="$tmp_flags" ]) # LC_LM_COMPARE_OWNER_EXISTS # +# LC_FSCRYPT_SUPPORT +# +# 5.4 introduced fscrypt encryption policies v2 +# +AC_DEFUN([LC_FSCRYPT_SUPPORT], [ +LB_CHECK_COMPILE([for fscrypt in-kernel support], +fscrypt_support, [ + #define __FS_HAS_ENCRYPTION 0 + #include +],[ + fscrypt_ioctl_get_policy_ex(NULL, NULL); +],[ + has_fscrypt_support="yes" +]) +]) # LC_FSCRYPT_SUPPORT + +# # LC_PROG_LINUX # # Lustre linux kernel checks @@ -2279,9 +2353,11 @@ AC_DEFUN([LC_PROG_LINUX], [ # 5.1 LC_HAS_LINUX_SELINUX_ENABLED + LB_HAVE_BVEC_ITER_ALL # 5.2 LC_ACCOUNT_PAGE_DIRTIED + LC_KEYRING_SEARCH_4ARGS # 5.3 LC_BIO_BI_PHYS_SEGMENTS @@ -2434,6 +2510,41 @@ AC_SUBST(OSDADDON) ]) # LC_OSD_ADDON # +# LC_CONFIG_CRYPTO +# +# Check whether to enable Lustre client crypto +# +AC_DEFUN([LC_CONFIG_CRYPTO], [ +AC_MSG_CHECKING([whether to enable Lustre client crypto]) +AC_ARG_ENABLE([crypto], + AC_HELP_STRING([--enable-crypto], + [enable Lustre client crypto]), + [], [enable_crypto="auto"]) +AS_IF([test "x$enable_crypto" != xno -a "x$enable_dist" = xno], [ + AC_MSG_RESULT( + ) + LC_IS_ENCRYPTED + LC_FSCRYPT_SUPPORT]) +AS_IF([test "x$has_fscrypt_support" = xyes], [ + AC_DEFINE(HAVE_LUSTRE_CRYPTO, 1, [Enable Lustre client crypto via in-kernel fscrypt]) + enable_crypto=yes], + [AS_IF([test "x$has_is_encrypted" = xyes], [ + AC_DEFINE(HAVE_LUSTRE_CRYPTO, 1, [Enable Lustre client crypto via embedded llcrypt]) + AC_DEFINE(CONFIG_LL_ENCRYPTION, 1, [embedded llcrypt]) + enable_crypto=yes + enable_llcrypt=yes], [ + AS_IF([test "x$enable_crypto" = xyes], + [AC_MSG_ERROR([Lustre client crypto cannot be enabled because of lack of encryption support in your kernel.])]) + AS_IF([test "x$enable_crypto" != xno -a "x$enable_dist" = xno], + [AC_MSG_WARN(Lustre client crypto cannot be enabled because of lack of encryption support in your kernel.)]) + enable_crypto=no])]) +AS_IF([test "x$enable_dist" != xno], [ + enable_crypto=yes + enable_llcrypt=yes]) +AC_MSG_RESULT([$enable_crypto]) +]) # LC_CONFIG_CRYPTO + +# # LC_CONFIGURE # # other configure checks @@ -2602,6 +2713,7 @@ AM_CONDITIONAL(ENABLE_BASH_COMPLETION, test "x$with_bash_completion_dir" != "xno AM_CONDITIONAL(XATTR_HANDLER, test "x$lb_cv_compile_xattr_handler_flags" = xyes) AM_CONDITIONAL(SELINUX, test "$SELINUX" = "-lselinux") AM_CONDITIONAL(GETSEPOL, test x$enable_getsepol = xyes) +AM_CONDITIONAL(LLCRYPT, test x$enable_llcrypt = xyes) ]) # LC_CONDITIONALS # diff --git a/lustre/include/lustre_disk.h b/lustre/include/lustre_disk.h index 18d5241..45222b0 100644 --- a/lustre/include/lustre_disk.h +++ b/lustre/include/lustre_disk.h @@ -138,6 +138,10 @@ struct lustre_sb_info { struct list_head lsi_lwp_list; unsigned long lsi_lwp_started:1, lsi_server_started:1; +#ifdef CONFIG_LL_ENCRYPTION + const struct llcrypt_operations *lsi_cop; + struct key *lsi_master_keys; /* master crypto keys used */ +#endif }; #define LSI_UMOUNT_FAILOVER 0x00200000 diff --git a/lustre/obdclass/obd_mount.c b/lustre/obdclass/obd_mount.c index de23440..d9122c5 100644 --- a/lustre/obdclass/obd_mount.c +++ b/lustre/obdclass/obd_mount.c @@ -49,6 +49,7 @@ #include #include #include +#include static DEFINE_SPINLOCK(client_lock); static struct module *client_mod; @@ -625,6 +626,7 @@ static int lustre_free_lsi(struct super_block *sb) /* someone didn't call server_put_mount. */ LASSERT(atomic_read(&lsi->lsi_mounts) == 0); + llcrypt_sb_free(sb); if (lsi->lsi_lmd != NULL) { if (lsi->lsi_lmd->lmd_dev != NULL) OBD_FREE(lsi->lsi_lmd->lmd_dev, -- 1.8.3.1