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 <sbuisson@ddn.com>
Change-Id: Ide2431b0c3381214aed2e54dd3084132e9545ca8
Reviewed-on: https://review.whamcloud.com/38127
Tested-by: jenkins <devops@whamcloud.com>
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: Yang Sheng <ys@whamcloud.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
F: lustre/include/lustre_mdc.h
F: lustre/mdc/
+Lustre client side encryption
+M: Sebastien Buisson <sbuisson@whamcloud.com>
+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 <green@whamcloud.com>
M: Lai Siyao <lai.siyao@whamcloud.com>
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; \
]) # 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 <linux/bvec.h>
-],[
- 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
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])
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
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"])
--- /dev/null
+--- 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 <linux/llcrypt.h>
++#include <libcfs/crypto/llcrypt.h>
+ #include <crypto/hash.h>
+
+ #define CONST_STRLEN(str) (sizeof(str) - 1)
--- /dev/null
+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 <linux/fs.h>
+ #include <linux/mm.h>
+ #include <linux/slab.h>
--- /dev/null
+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 */
--- /dev/null
+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 <libcfs/crypto/llcrypt.h>
+ #include <crypto/hash.h>
++#include <lustre_disk.h>
+
+ #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.
--- /dev/null
+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 <crypto/hash.h>
+ #include <lustre_disk.h>
+
++#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
--- /dev/null
+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;
--- /dev/null
+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)
--- /dev/null
+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) {
--- /dev/null
+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 <linux/fs.h>
+ #include <linux/mm.h>
+ #include <linux/slab.h>
++#include <lustre_disk.h>
+ #include <uapi/linux/llcrypt.h>
+
+ #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;
--- /dev/null
+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;
+
--- /dev/null
+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 {
--- /dev/null
+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);
--- /dev/null
+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))
--- /dev/null
+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
+
--- /dev/null
+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)
--- /dev/null
+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
--- /dev/null
+#!/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
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 \
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) \
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
-SUBDIRS = libcfs
+SUBDIRS = libcfs uapi
SUBDIRS = linux util
DIST_SUBDIRS = linux util
+SUBDIRS += crypto
+DIST_SUBDIRS += crypto
libcfsdir = $(includedir)/libcfs
--- /dev/null
+/Makefile.in
--- /dev/null
+EXTRA_DIST = llcrypt.h
--- /dev/null
+/* 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 <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <lustre_disk.h>
+#include <uapi/linux/llcrypt.h>
+
+#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 */
--- /dev/null
+/Makefile.in
--- /dev/null
+SUBDIRS = linux
--- /dev/null
+/Makefile.in
--- /dev/null
+EXTRA_DIST = llcrypt.h
--- /dev/null
+/* 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 <linux/types.h>
+
+/* 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 */
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 \
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
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
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 \
--- /dev/null
+/Makefile.in
--- /dev/null
+04-02-2020 Whamcloud, Inc.
+ * fscrypt sources taken from Linux kernel v5.4
--- /dev/null
+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
--- /dev/null
+// 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 <linux/pagemap.h>
+#include <linux/module.h>
+#include <linux/bio.h>
+#include <linux/namei.h>
+#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);
--- /dev/null
+// 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 <linux/pagemap.h>
+#include <linux/mempool.h>
+#include <linux/module.h>
+#include <linux/scatterlist.h>
+#include <linux/ratelimit.h>
+#include <linux/dcache.h>
+#include <linux/namei.h>
+#include <crypto/aes.h>
+#include <crypto/skcipher.h>
+#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);
+}
--- /dev/null
+// 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 <linux/scatterlist.h>
+#include <crypto/skcipher.h>
+#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);
--- /dev/null
+// 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 <crypto/hash.h>
+#include <crypto/sha.h>
+
+#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);
+}
--- /dev/null
+// 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);
--- /dev/null
+// 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 <crypto/skcipher.h>
+#include <linux/key-type.h>
+#include <linux/seq_file.h>
+
+#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);
+}
--- /dev/null
+// 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 <crypto/aes.h>
+#include <crypto/sha.h>
+#include <crypto/skcipher.h>
+#include <linux/key.h>
+
+#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);
--- /dev/null
+// 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 <crypto/algapi.h>
+#include <crypto/skcipher.h>
+#include <keys/user-type.h>
+#include <linux/hashtable.h>
+#include <linux/scatterlist.h>
+
+#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;
+}
--- /dev/null
+/* 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 <libcfs/crypto/llcrypt.h>
+#include <crypto/hash.h>
+#include <lustre_disk.h>
+
+#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 */
--- /dev/null
+// 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 <linux/random.h>
+#include <linux/string.h>
+#include <linux/mount.h>
+#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);
#include <libcfs/libcfs.h>
#include <libcfs/libcfs_crypto.h>
#include <lnet/lib-lnet.h>
+#include <libcfs/crypto/llcrypt.h>
#include "tracefile.h"
static struct dentry *lnet_debugfs_root;
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:
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;
]) # 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 <linux/fs.h>
+],[
+ IS_ENCRYPTED((struct inode *)0);
+],[
+ has_is_encrypted="yes"
+])
+]) # LC_IS_ENCRYPTED
+
+#
# LC_I_PAGES
#
# kernel 4.17 commit b93b016313b3ba8003c3b8bb71f569af91f19fc7
]) # 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 <linux/bvec.h>
+],[
+ 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
]) # 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 <linux/key.h>
+],[
+ 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
]) # 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 <linux/fscrypt.h>
+],[
+ fscrypt_ioctl_get_policy_ex(NULL, NULL);
+],[
+ has_fscrypt_support="yes"
+])
+]) # LC_FSCRYPT_SUPPORT
+
+#
# LC_PROG_LINUX
#
# Lustre linux kernel checks
# 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
]) # 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
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
#
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
#include <lustre_log.h>
#include <lustre_disk.h>
#include <uapi/linux/lustre/lustre_param.h>
+#include <libcfs/crypto/llcrypt.h>
static DEFINE_SPINLOCK(client_lock);
static struct module *client_mod;
/* 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,