Whamcloud - gitweb
LU-12275 sec: add llcrypt as file encryption library 27/38127/6
authorSebastien Buisson <sbuisson@ddn.com>
Wed, 25 Mar 2020 16:43:33 +0000 (16:43 +0000)
committerOleg Drokin <green@whamcloud.com>
Fri, 1 May 2020 04:26:36 +0000 (04:26 +0000)
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>
52 files changed:
MAINTAINERS
autoMakefile.am
config/lustre-build-ldiskfs.m4
config/lustre-build.m4
contrib/scripts/crypto_patches/0001_llcrypt_private_include.patch [new file with mode: 0644]
contrib/scripts/crypto_patches/0002_dcache_encrypted_name.patch [new file with mode: 0644]
contrib/scripts/crypto_patches/0003_keyring_search_4args.patch [new file with mode: 0644]
contrib/scripts/crypto_patches/0004_master_keys.patch [new file with mode: 0644]
contrib/scripts/crypto_patches/0005_crypto_tfm_req_forbid_weak_keys.patch [new file with mode: 0644]
contrib/scripts/crypto_patches/0006_bvec_iter_all.patch [new file with mode: 0644]
contrib/scripts/crypto_patches/0007_crypto_init.patch [new file with mode: 0644]
contrib/scripts/crypto_patches/0008_key_desc_prefix.patch [new file with mode: 0644]
contrib/scripts/crypto_patches/0009_lsi_cop.patch [new file with mode: 0644]
contrib/scripts/crypto_patches/0010_llcrypt_info.patch [new file with mode: 0644]
contrib/scripts/crypto_patches/0011_llcrypt_cleanup.patch [new file with mode: 0644]
contrib/scripts/crypto_patches/0012_igrab.patch [new file with mode: 0644]
contrib/scripts/crypto_patches/0013_dentry_path_raw.patch [new file with mode: 0644]
contrib/scripts/crypto_patches/0014_cdebug.patch [new file with mode: 0644]
contrib/scripts/crypto_patches/0015_llcrypt_include.patch [new file with mode: 0644]
contrib/scripts/crypto_patches/series [new file with mode: 0644]
contrib/scripts/fscrypt_inclusion.sh [new file with mode: 0755]
debian/rules
libcfs/autoconf/lustre-libcfs.m4
libcfs/include/Makefile.am
libcfs/include/libcfs/Makefile.am
libcfs/include/libcfs/crypto/.gitignore [new file with mode: 0644]
libcfs/include/libcfs/crypto/Makefile.am [new file with mode: 0644]
libcfs/include/libcfs/crypto/llcrypt.h [new file with mode: 0644]
libcfs/include/uapi/.gitignore [new file with mode: 0644]
libcfs/include/uapi/Makefile.am [new file with mode: 0644]
libcfs/include/uapi/linux/.gitignore [new file with mode: 0644]
libcfs/include/uapi/linux/Makefile.am [new file with mode: 0644]
libcfs/include/uapi/linux/llcrypt.h [new file with mode: 0644]
libcfs/libcfs/Makefile.in
libcfs/libcfs/autoMakefile.am
libcfs/libcfs/crypto/.gitignore [new file with mode: 0644]
libcfs/libcfs/crypto/ChangeLog [new file with mode: 0644]
libcfs/libcfs/crypto/Makefile.am [new file with mode: 0644]
libcfs/libcfs/crypto/bio.c [new file with mode: 0644]
libcfs/libcfs/crypto/crypto.c [new file with mode: 0644]
libcfs/libcfs/crypto/fname.c [new file with mode: 0644]
libcfs/libcfs/crypto/hkdf.c [new file with mode: 0644]
libcfs/libcfs/crypto/hooks.c [new file with mode: 0644]
libcfs/libcfs/crypto/keyring.c [new file with mode: 0644]
libcfs/libcfs/crypto/keysetup.c [new file with mode: 0644]
libcfs/libcfs/crypto/keysetup_v1.c [new file with mode: 0644]
libcfs/libcfs/crypto/llcrypt_private.h [new file with mode: 0644]
libcfs/libcfs/crypto/policy.c [new file with mode: 0644]
libcfs/libcfs/module.c
lustre/autoconf/lustre-core.m4
lustre/include/lustre_disk.h
lustre/obdclass/obd_mount.c

index e0eac46..a268e98 100644 (file)
@@ -129,6 +129,13 @@ S: Supported
 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>
index 51c27d0..7a1e355 100644 (file)
@@ -263,6 +263,11 @@ debs: undef.h
        elif test "x@ENABLE_GSS@" = "xno"; then \
                export DEB_BUILD_PROFILES="$${DEB_BUILD_PROFILES} nogss"; \
        fi; \
+       if test "x@ENABLE_CRYPTO@" = "xyes"; then \
+               export DEB_BUILD_PROFILES="$${DEB_BUILD_PROFILES} crypto"; \
+       elif test "x@ENABLE_CRYPTO@" = "xno"; then \
+               export DEB_BUILD_PROFILES="$${DEB_BUILD_PROFILES} nocrypto"; \
+       fi; \
        if test "x@systemdsystemunitdir@" != "x"; then \
                export DEB_BUILD_PROFILES="$${DEB_BUILD_PROFILES} systemd"; \
        fi; \
index 2ddb1e9..4b52570 100644 (file)
@@ -275,28 +275,6 @@ AC_DEFUN([LDISKFS_AC_PATCH_PROGRAM], [
 ]) # LDISKFS_AC_PATCH_PROGRAM
 
 #
-# LB_HAVE_BVEC_ITER_ALL
-#
-# kernel 5.1 commit 6dc4f100c175dd0511ae8674786e7c9006cdfbfa
-# block: allow bio_for_each_segment_all() to iterate over multi-page bvec
-#
-AC_DEFUN([LB_HAVE_BVEC_ITER_ALL], [
-tmp_flags="$EXTRA_KCFLAGS"
-EXTRA_KCFLAGS="-Werror"
-LB_CHECK_COMPILE([if bvec_iter_all exists for multi-page bvec iternation],
-ext4fs_dirhash, [
-       #include <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
@@ -411,7 +389,6 @@ AS_IF([test x$enable_ldiskfs != xno],[
        LB_EXT4_HAVE_INFO_DQUOT
        LB_EXT4_HAVE_I_CRYPT_INFO
        LB_LDISKFS_IGET_HAS_FLAGS_ARG
-       LB_HAVE_BVEC_ITER_ALL
        LB_LDISKFS_FIND_ENTRY_LOCKED_EXISTS
        LB_LDISKFSFS_DIRHASH_WANTS_DIR
        AC_DEFINE(CONFIG_LDISKFS_FS_POSIX_ACL, 1, [posix acls for ldiskfs])
index ed23616..947b632 100644 (file)
@@ -561,6 +561,13 @@ elif test x$enable_gss = xno ; then
        RPMBINARGS="$RPMBINARGS --without gss"
        AC_SUBST(ENABLE_GSS, no)
 fi
+if test x$enable_crypto = xyes ; then
+       RPMBINARGS="$RPMBINARGS --with crypto"
+       AC_SUBST(ENABLE_CRYPTO, yes)
+elif test x$enable_crypto = xno ; then
+       RPMBINARGS="$RPMBINARGS --without crypto"
+       AC_SUBST(ENABLE_CRYPTO, no)
+fi
 if test x$enable_iokit != xyes ; then
        RPMBINARGS="$RPMBINARGS --without lustre_iokit"
 fi
@@ -618,6 +625,7 @@ LB_CONFIG_TESTS
 LC_CONFIG_CLIENT
 LB_CONFIG_MPITESTS
 LB_CONFIG_SERVERS
+LC_CONFIG_CRYPTO
 
 # Tests depends from utils (multiop from liblustreapi)
 AS_IF([test "x$enable_utils" = xno], [enable_tests="no"])
diff --git a/contrib/scripts/crypto_patches/0001_llcrypt_private_include.patch b/contrib/scripts/crypto_patches/0001_llcrypt_private_include.patch
new file mode 100644 (file)
index 0000000..8ad3f18
--- /dev/null
@@ -0,0 +1,11 @@
+--- a/libcfs/libcfs/crypto/llcrypt_private.h
++++ b/libcfs/libcfs/crypto/llcrypt_private.h
+@@ -11,7 +11,7 @@
+ #ifndef _LLCRYPT_PRIVATE_H
+ #define _LLCRYPT_PRIVATE_H
+-#include <linux/llcrypt.h>
++#include <libcfs/crypto/llcrypt.h>
+ #include <crypto/hash.h>
+ #define CONST_STRLEN(str)     (sizeof(str) - 1)
diff --git a/contrib/scripts/crypto_patches/0002_dcache_encrypted_name.patch b/contrib/scripts/crypto_patches/0002_dcache_encrypted_name.patch
new file mode 100644 (file)
index 0000000..bb62166
--- /dev/null
@@ -0,0 +1,16 @@
+Linux 5.1 (commit 6cc248684d3d) renames DCACHE_ENCRYPTED_WITH_KEY to
+DCACHE_ENCRYPTED_NAME.
+
+--- a/libcfs/include/libcfs/crypto/llcrypt.h
++++ b/libcfs/include/libcfs/crypto/llcrypt.h
+@@ -13,6 +13,10 @@
+ #ifndef _LINUX_LLCRYPT_H
+ #define _LINUX_LLCRYPT_H
++#ifndef DCACHE_ENCRYPTED_NAME
++#define DCACHE_ENCRYPTED_NAME 0x02000000
++#endif
++
+ #include <linux/fs.h>
+ #include <linux/mm.h>
+ #include <linux/slab.h>
diff --git a/contrib/scripts/crypto_patches/0003_keyring_search_4args.patch b/contrib/scripts/crypto_patches/0003_keyring_search_4args.patch
new file mode 100644 (file)
index 0000000..2f85b0b
--- /dev/null
@@ -0,0 +1,16 @@
+Linux 5.2 commit dcf49dbc8077) adds a 'recurse' flag for keyring searches.
+
+--- a/libcfs/libcfs/crypto/keyring.c
++++ b/libcfs/libcfs/crypto/keyring.c
+@@ -138,7 +138,11 @@ static struct key *search_llcrypt_keyrin
+        */
+       key_ref_t keyref = make_key_ref(keyring, true /* possessed */);
++#ifdef HAVE_KEYRING_SEARCH_4ARGS
+       keyref = keyring_search(keyref, type, description, false);
++#else
++      keyref = keyring_search(keyref, type, description);
++#endif
+       if (IS_ERR(keyref)) {
+               if (PTR_ERR(keyref) == -EAGAIN || /* not found */
+                   PTR_ERR(keyref) == -EKEYREVOKED) /* recently invalidated */
diff --git a/contrib/scripts/crypto_patches/0004_master_keys.patch b/contrib/scripts/crypto_patches/0004_master_keys.patch
new file mode 100644 (file)
index 0000000..c713875
--- /dev/null
@@ -0,0 +1,157 @@
+Linux 5.3 (commit 22d94f493bfb) introduces s_master_keys to struct super_block.
+As we need encryption support for older kernels, replace this with
+new lsi_master_keys field in struct lustre_sb_info.
+
+--- a/libcfs/libcfs/crypto/keyring.c
++++ b/libcfs/libcfs/crypto/keyring.c
+@@ -82,7 +82,7 @@ static void llcrypt_key_describe(const s
+ }
+ /*
+- * Type of key in ->s_master_keys.  Each key of this type represents a master
++ * Type of key in ->lsi_master_keys.  Each key of this type represents a master
+  * key which has been added to the filesystem.  Its payload is a
+  * 'struct llcrypt_master_key'.  The "." prefix in the key type name prevents
+  * users from adding keys of this type via the keyrings syscalls rather than via
+@@ -127,7 +127,7 @@ static struct key_type key_type_llcrypt_
+       .describe               = llcrypt_user_key_describe,
+ };
+-/* Search ->s_master_keys or ->mk_users */
++/* Search ->lsi_master_keys or ->mk_users */
+ static struct key *search_llcrypt_keyring(struct key *keyring,
+                                         struct key_type *type,
+                                         const char *description)
+@@ -196,13 +196,17 @@ static void format_mk_user_description(
+               mk_identifier, __kuid_val(current_fsuid()));
+ }
+-/* Create ->s_master_keys if needed.  Synchronized by llcrypt_add_key_mutex. */
++/* Create ->lsi_master_keys if needed.  Synchronized by llcrypt_add_key_mutex. */
+ static int allocate_filesystem_keyring(struct super_block *sb)
+ {
+       char description[LLCRYPT_FS_KEYRING_DESCRIPTION_SIZE];
+       struct key *keyring;
++      struct lustre_sb_info *lsi = s2lsi(sb);
+-      if (sb->s_master_keys)
++      if (!lsi)
++              return -EINVAL;
++
++      if (lsi->lsi_master_keys)
+               return 0;
+       format_fs_keyring_description(description, sb);
+@@ -214,18 +218,22 @@ static int allocate_filesystem_keyring(s
+               return PTR_ERR(keyring);
+       /* Pairs with READ_ONCE() in llcrypt_find_master_key() */
+-      smp_store_release(&sb->s_master_keys, keyring);
++      smp_store_release(&lsi->lsi_master_keys, keyring);
+       return 0;
+ }
+ void llcrypt_sb_free(struct super_block *sb)
+ {
+-      key_put(sb->s_master_keys);
+-      sb->s_master_keys = NULL;
++      struct lustre_sb_info *lsi = s2lsi(sb);
++
++      if (lsi != NULL) {
++              key_put(lsi->lsi_master_keys);
++              lsi->lsi_master_keys = NULL;
++      }
+ }
+ /*
+- * Find the specified master key in ->s_master_keys.
++ * Find the specified master key in ->lsi_master_keys.
+  * Returns ERR_PTR(-ENOKEY) if not found.
+  */
+ struct key *llcrypt_find_master_key(struct super_block *sb,
+@@ -233,9 +241,13 @@ struct key *llcrypt_find_master_key(stru
+ {
+       struct key *keyring;
+       char description[LLCRYPT_MK_DESCRIPTION_SIZE];
++      struct lustre_sb_info *lsi = s2lsi(sb);
++
++      if (!lsi)
++              return ERR_PTR(-EINVAL);
+       /* pairs with smp_store_release() in allocate_filesystem_keyring() */
+-      keyring = READ_ONCE(sb->s_master_keys);
++      keyring = READ_ONCE(lsi->lsi_master_keys);
+       if (keyring == NULL)
+               return ERR_PTR(-ENOKEY); /* No keyring yet, so no keys yet. */
+@@ -432,8 +444,12 @@ static int add_master_key(struct super_b
+ {
+       static DEFINE_MUTEX(llcrypt_add_key_mutex);
+       struct key *key;
++      struct lustre_sb_info *lsi = s2lsi(sb);
+       int err;
++      if (!lsi)
++              return -EINVAL;
++
+       mutex_lock(&llcrypt_add_key_mutex); /* serialize find + link */
+ retry:
+       key = llcrypt_find_master_key(sb, mk_spec);
+@@ -441,14 +457,15 @@ retry:
+               err = PTR_ERR(key);
+               if (err != -ENOKEY)
+                       goto out_unlock;
+-              /* Didn't find the key in ->s_master_keys.  Add it. */
++              /* Didn't find the key in ->lsi_master_keys.  Add it. */
+               err = allocate_filesystem_keyring(sb);
+               if (err)
+                       goto out_unlock;
+-              err = add_new_master_key(secret, mk_spec, sb->s_master_keys);
++              err = add_new_master_key(secret, mk_spec,
++                                       lsi->lsi_master_keys);
+       } else {
+               /*
+-               * Found the key in ->s_master_keys.  Re-add the secret if
++               * Found the key in ->lsi_master_keys.  Re-add the secret if
+                * needed, and add the user to ->mk_users if needed.
+                */
+               down_write(&key->sem);
+--- a/libcfs/libcfs/crypto/keysetup.c
++++ b/libcfs/libcfs/crypto/keysetup.c
+@@ -326,7 +326,7 @@ static int setup_file_encryption_key(str
+               /*
+                * As a legacy fallback for v1 policies, search for the key in
+                * the current task's subscribed keyrings too.  Don't move this
+-               * to before the search of ->s_master_keys, since users
++               * to before the search of ->lsi_master_keys, since users
+                * shouldn't be able to override filesystem-level keys.
+                */
+               return llcrypt_setup_v1_file_key_via_subscribed_keyrings(ci);
+@@ -406,7 +406,7 @@ static void put_crypt_info(struct llcryp
+                *
+                * In addition, if we're removing the last inode from a key that
+                * already had its secret removed, invalidate the key so that it
+-               * gets removed from ->s_master_keys.
++               * gets removed from ->lsi_master_keys.
+                */
+               spin_lock(&mk->mk_decrypted_inodes_lock);
+               list_del(&ci->ci_master_key_link);
+--- a/libcfs/libcfs/crypto/llcrypt_private.h
++++ b/libcfs/libcfs/crypto/llcrypt_private.h
+@@ -13,6 +13,7 @@
+ #include <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.
diff --git a/contrib/scripts/crypto_patches/0005_crypto_tfm_req_forbid_weak_keys.patch b/contrib/scripts/crypto_patches/0005_crypto_tfm_req_forbid_weak_keys.patch
new file mode 100644 (file)
index 0000000..ebcef57
--- /dev/null
@@ -0,0 +1,16 @@
+Linux 5.0 (commit 231baecdef7a) renames CRYPTO_TFM_REQ_WEAK_KEY to
+CRYPTO_TFM_REQ_FORBID_WEAK_KEYS.
+
+--- a/libcfs/libcfs/crypto/llcrypt_private.h
++++ b/libcfs/libcfs/crypto/llcrypt_private.h
+@@ -15,6 +15,10 @@
+ #include <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
diff --git a/contrib/scripts/crypto_patches/0006_bvec_iter_all.patch b/contrib/scripts/crypto_patches/0006_bvec_iter_all.patch
new file mode 100644 (file)
index 0000000..a6968c2
--- /dev/null
@@ -0,0 +1,17 @@
+Linux 5.1 (commit 6dc4f100c175dd0511ae8674786e7c9006cdfbfa) introduces
+bvec_iter_all.
+
+--- a/libcfs/libcfs/crypto/bio.c
++++ b/libcfs/libcfs/crypto/bio.c
+@@ -29,7 +29,11 @@
+ static void __llcrypt_decrypt_bio(struct bio *bio, bool done)
+ {
+       struct bio_vec *bv;
++#ifdef HAVE_BVEC_ITER_ALL
+       struct bvec_iter_all iter_all;
++#else
++      int iter_all;
++#endif
+       bio_for_each_segment_all(bv, bio, iter_all) {
+               struct page *page = bv->bv_page;
diff --git a/contrib/scripts/crypto_patches/0007_crypto_init.patch b/contrib/scripts/crypto_patches/0007_crypto_init.patch
new file mode 100644 (file)
index 0000000..a5f95e1
--- /dev/null
@@ -0,0 +1,28 @@
+Make llcrypt_init visible outside of llcrypt.
+
+--- a/libcfs/include/libcfs/crypto/llcrypt.h
++++ b/libcfs/include/libcfs/crypto/llcrypt.h
+@@ -105,6 +105,7 @@ static inline void llcrypt_handle_d_move
+ }
+ /* crypto.c */
++extern int __init llcrypt_init(void);
+ extern void llcrypt_enqueue_decrypt_work(struct work_struct *);
+ extern struct llcrypt_ctx *llcrypt_get_ctx(gfp_t);
+ extern void llcrypt_release_ctx(struct llcrypt_ctx *);
+--- a/libcfs/libcfs/crypto/crypto.c
++++ b/libcfs/libcfs/crypto/crypto.c
+@@ -476,7 +476,7 @@ void llcrypt_msg(const struct inode *ino
+ /**
+  * llcrypt_init() - Set up for fs encryption.
+  */
+-static int __init llcrypt_init(void)
++int __init llcrypt_init(void)
+ {
+       int err = -ENOMEM;
+@@ -517,4 +517,3 @@ fail_free_queue:
+ fail:
+       return err;
+ }
+-late_initcall(llcrypt_init)
diff --git a/contrib/scripts/crypto_patches/0008_key_desc_prefix.patch b/contrib/scripts/crypto_patches/0008_key_desc_prefix.patch
new file mode 100644 (file)
index 0000000..eb256df
--- /dev/null
@@ -0,0 +1,24 @@
+Keep 'fscrypt' as key desc prefix, for compatibility with third party tools.
+
+--- a/libcfs/include/uapi/linux/llcrypt.h
++++ b/libcfs/include/uapi/linux/llcrypt.h
+@@ -48,7 +48,7 @@ struct llcrypt_policy_v1 {
+  * Process-subscribed "logon" key description prefix and payload format.
+  * Deprecated; prefer LL_IOC_ADD_ENCRYPTION_KEY instead.
+  */
+-#define LLCRYPT_KEY_DESC_PREFIX               "llcrypt:"
++#define LLCRYPT_KEY_DESC_PREFIX               "fscrypt:"
+ #define LLCRYPT_KEY_DESC_PREFIX_SIZE  8
+ #define LLCRYPT_MAX_KEY_SIZE          64
+ struct llcrypt_key {
+--- a/libcfs/libcfs/crypto/hkdf.c
++++ b/libcfs/libcfs/crypto/hkdf.c
+@@ -129,7 +129,7 @@ int llcrypt_hkdf_expand(struct llcrypt_h
+       desc->tfm = hkdf->hmac_tfm;
+-      memcpy(prefix, "llcrypt\0", 8);
++      memcpy(prefix, "fscrypt\0", 8);
+       prefix[8] = context;
+       for (i = 0; i < okmlen; i += HKDF_HASHLEN) {
diff --git a/contrib/scripts/crypto_patches/0009_lsi_cop.patch b/contrib/scripts/crypto_patches/0009_lsi_cop.patch
new file mode 100644 (file)
index 0000000..2da28ff
--- /dev/null
@@ -0,0 +1,198 @@
+Replace (struct super_block *)->s_cop with (struct lustre_sb_info *)->lsi_cop.
+This is needed so that we can use llcrypt on kernels that do not have
+(struct super_block *)->s_cop.
+
+--- a/libcfs/include/libcfs/crypto/llcrypt.h
++++ b/libcfs/include/libcfs/crypto/llcrypt.h
+@@ -20,6 +20,7 @@
+ #include <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;
diff --git a/contrib/scripts/crypto_patches/0010_llcrypt_info.patch b/contrib/scripts/crypto_patches/0010_llcrypt_info.patch
new file mode 100644 (file)
index 0000000..30d3a7b
--- /dev/null
@@ -0,0 +1,157 @@
+Replace use of inode->i_crypt_info with inode->i_private, cast as a struct llcrypt_info.
+This is required in order to be able to support encryption on kernels that lack
+inode->i_crypt_info field.
+
+--- a/libcfs/include/libcfs/crypto/llcrypt.h
++++ b/libcfs/include/libcfs/crypto/llcrypt.h
+@@ -81,11 +81,7 @@ struct llcrypt_ctx {
+       u8 flags;                               /* Flags */
+ };
+-static inline bool llcrypt_has_encryption_key(const struct inode *inode)
+-{
+-      /* pairs with cmpxchg_release() in llcrypt_get_encryption_info() */
+-      return READ_ONCE(inode->i_crypt_info) != NULL;
+-}
++extern bool llcrypt_has_encryption_key(const struct inode *inode);
+ static inline bool llcrypt_dummy_context_enabled(struct inode *inode)
+ {
+--- a/libcfs/libcfs/crypto/crypto.c
++++ b/libcfs/libcfs/crypto/crypto.c
+@@ -158,7 +158,7 @@ int llcrypt_crypt_block(const struct ino
+       struct skcipher_request *req = NULL;
+       DECLARE_CRYPTO_WAIT(wait);
+       struct scatterlist dst, src;
+-      struct llcrypt_info *ci = inode->i_crypt_info;
++      struct llcrypt_info *ci = llcrypt_info(inode);
+       struct crypto_skcipher *tfm = ci->ci_ctfm;
+       int res = 0;
+--- a/libcfs/libcfs/crypto/fname.c
++++ b/libcfs/libcfs/crypto/fname.c
+@@ -39,7 +39,7 @@ int fname_encrypt(struct inode *inode, c
+ {
+       struct skcipher_request *req = NULL;
+       DECLARE_CRYPTO_WAIT(wait);
+-      struct llcrypt_info *ci = inode->i_crypt_info;
++      struct llcrypt_info *ci = llcrypt_info(inode);
+       struct crypto_skcipher *tfm = ci->ci_ctfm;
+       union llcrypt_iv iv;
+       struct scatterlist sg;
+@@ -92,7 +92,7 @@ static int fname_decrypt(struct inode *i
+       struct skcipher_request *req = NULL;
+       DECLARE_CRYPTO_WAIT(wait);
+       struct scatterlist src_sg, dst_sg;
+-      struct llcrypt_info *ci = inode->i_crypt_info;
++      struct llcrypt_info *ci = llcrypt_info(inode);
+       struct crypto_skcipher *tfm = ci->ci_ctfm;
+       union llcrypt_iv iv;
+       int res;
+@@ -181,7 +181,7 @@ static int base64_decode(const char *src
+ bool llcrypt_fname_encrypted_size(const struct inode *inode, u32 orig_len,
+                                 u32 max_len, u32 *encrypted_len_ret)
+ {
+-      const struct llcrypt_info *ci = inode->i_crypt_info;
++      const struct llcrypt_info *ci = llcrypt_info(inode);
+       int padding = 4 << (llcrypt_policy_flags(&ci->ci_policy) &
+                           LLCRYPT_POLICY_FLAGS_PAD_MASK);
+       u32 encrypted_len;
+--- a/libcfs/libcfs/crypto/keysetup.c
++++ b/libcfs/libcfs/crypto/keysetup.c
+@@ -501,7 +501,8 @@ int llcrypt_get_encryption_info(struct i
+       if (res)
+               goto out;
+-      if (cmpxchg_release(&inode->i_crypt_info, NULL, crypt_info) == NULL) {
++      if (cmpxchg_release(&(llcrypt_info_nocast(inode)), NULL,
++                          crypt_info) == NULL) {
+               if (master_key) {
+                       struct llcrypt_master_key *mk =
+                               master_key->payload.data[0];
+@@ -538,8 +539,8 @@ EXPORT_SYMBOL(llcrypt_get_encryption_inf
+  */
+ void llcrypt_put_encryption_info(struct inode *inode)
+ {
+-      put_crypt_info(inode->i_crypt_info);
+-      inode->i_crypt_info = NULL;
++      put_crypt_info(llcrypt_info(inode));
++      llcrypt_info_nocast(inode) = NULL;
+ }
+ EXPORT_SYMBOL(llcrypt_put_encryption_info);
+@@ -569,9 +570,10 @@ EXPORT_SYMBOL(llcrypt_free_inode);
+  */
+ int llcrypt_drop_inode(struct inode *inode)
+ {
+-      const struct llcrypt_info *ci = READ_ONCE(inode->i_crypt_info);
++      const struct llcrypt_info *ci;
+       const struct llcrypt_master_key *mk;
++      ci = (struct llcrypt_info *)READ_ONCE(llcrypt_info_nocast(inode));
+       /*
+        * If ci is NULL, then the inode doesn't have an encryption key set up
+        * so it's irrelevant.  If ci_master_key is NULL, then the master key
+@@ -593,3 +595,10 @@ int llcrypt_drop_inode(struct inode *ino
+       return !is_master_key_secret_present(&mk->mk_secret);
+ }
+ EXPORT_SYMBOL_GPL(llcrypt_drop_inode);
++
++inline bool llcrypt_has_encryption_key(const struct inode *inode)
++{
++      /* pairs with cmpxchg_release() in llcrypt_get_encryption_info() */
++      return READ_ONCE(llcrypt_info_nocast(inode)) != NULL;
++}
++EXPORT_SYMBOL_GPL(llcrypt_has_encryption_key);
+--- a/libcfs/libcfs/crypto/llcrypt_private.h
++++ b/libcfs/libcfs/crypto/llcrypt_private.h
+@@ -19,6 +19,9 @@
+ #define CRYPTO_TFM_REQ_FORBID_WEAK_KEYS CRYPTO_TFM_REQ_WEAK_KEY
+ #endif
++#define llcrypt_info(inode)        ((struct llcrypt_info *)(inode)->i_private)
++#define llcrypt_info_nocast(inode)   ((inode)->i_private)
++
+ #define CONST_STRLEN(str)     (sizeof(str) - 1)
+ #define FS_KEY_DERIVATION_NONCE_SIZE  16
+@@ -160,8 +163,8 @@ struct llcrypt_symlink_data {
+  * llcrypt_info - the "encryption key" for an inode
+  *
+  * When an encrypted file's key is made available, an instance of this struct is
+- * allocated and stored in ->i_crypt_info.  Once created, it remains until the
+- * inode is evicted.
++ * allocated and stored in '(struct llcrypt_info *)inode->i_private'.
++ * Once created, it remains until the inode is evicted.
+  */
+ struct llcrypt_info {
+--- a/libcfs/libcfs/crypto/policy.c
++++ b/libcfs/libcfs/crypto/policy.c
+@@ -212,7 +212,7 @@ static int llcrypt_get_policy(struct ino
+       struct lustre_sb_info *lsi = s2lsi(inode->i_sb);
+       int ret;
+-      ci = READ_ONCE(inode->i_crypt_info);
++      ci = (struct llcrypt_info *)READ_ONCE(llcrypt_info_nocast(inode));
+       if (ci) {
+               /* key available, use the cached policy */
+               *policy = ci->ci_policy;
+@@ -472,7 +472,7 @@ EXPORT_SYMBOL(llcrypt_has_permitted_cont
+  * @parent: Parent inode from which the context is inherited.
+  * @child:  Child inode that inherits the context from @parent.
+  * @fs_data:  private data given by FS.
+- * @preload:  preload child i_crypt_info if true
++ * @preload:  preload child crypt info if true
+  *
+  * Return: 0 on success, -errno on failure
+  */
+@@ -489,7 +489,7 @@ int llcrypt_inherit_context(struct inode
+       if (res < 0)
+               return res;
+-      ci = READ_ONCE(parent->i_crypt_info);
++      ci = (struct llcrypt_info *)READ_ONCE(llcrypt_info_nocast(parent));
+       if (ci == NULL)
+               return -ENOKEY;
diff --git a/contrib/scripts/crypto_patches/0011_llcrypt_cleanup.patch b/contrib/scripts/crypto_patches/0011_llcrypt_cleanup.patch
new file mode 100644 (file)
index 0000000..c40595a
--- /dev/null
@@ -0,0 +1,69 @@
+Add __exit functions for llcrypt.
+Initial fscrypt implementation lacks this because fscrypt is not a kernel module.
+
+--- a/libcfs/include/libcfs/crypto/llcrypt.h
++++ b/libcfs/include/libcfs/crypto/llcrypt.h
+@@ -108,6 +108,7 @@ static inline void llcrypt_handle_d_move
+ /* crypto.c */
+ extern int __init llcrypt_init(void);
++extern void __exit llcrypt_exit(void);
+ extern void llcrypt_enqueue_decrypt_work(struct work_struct *);
+ extern struct llcrypt_ctx *llcrypt_get_ctx(gfp_t);
+ extern void llcrypt_release_ctx(struct llcrypt_ctx *);
+--- a/libcfs/libcfs/crypto/crypto.c
++++ b/libcfs/libcfs/crypto/crypto.c
+@@ -517,3 +517,22 @@ fail_free_queue:
+ fail:
+       return err;
+ }
++
++/**
++ * llcrypt_exit() - Clean up for fs encryption.
++ */
++void __exit llcrypt_exit(void)
++{
++      llcrypt_exit_keyring();
++
++      llcrypt_destroy();
++      /*
++       * Make sure all delayed rcu free inodes are flushed before we
++       * destroy cache.
++       */
++      rcu_barrier();
++
++      kmem_cache_destroy(llcrypt_info_cachep);
++      kmem_cache_destroy(llcrypt_ctx_cachep);
++      destroy_workqueue(llcrypt_read_workqueue);
++}
+--- a/libcfs/libcfs/crypto/keyring.c
++++ b/libcfs/libcfs/crypto/keyring.c
+@@ -231,6 +231,7 @@ void llcrypt_sb_free(struct super_block
+               lsi->lsi_master_keys = NULL;
+       }
+ }
++EXPORT_SYMBOL(llcrypt_sb_free);
+ /*
+  * Find the specified master key in ->lsi_master_keys.
+@@ -1003,3 +1004,9 @@ err_unregister_llcrypt:
+       unregister_key_type(&key_type_llcrypt);
+       return err;
+ }
++
++void __exit llcrypt_exit_keyring(void)
++{
++      unregister_key_type(&key_type_llcrypt_user);
++      unregister_key_type(&key_type_llcrypt);
++}
+--- a/libcfs/libcfs/crypto/llcrypt_private.h
++++ b/libcfs/libcfs/crypto/llcrypt_private.h
+@@ -444,6 +444,8 @@ extern int llcrypt_verify_key_added(stru
+ extern int __init llcrypt_init_keyring(void);
++extern void __exit llcrypt_exit_keyring(void);
++
+ /* keysetup.c */
+ struct llcrypt_mode {
diff --git a/contrib/scripts/crypto_patches/0012_igrab.patch b/contrib/scripts/crypto_patches/0012_igrab.patch
new file mode 100644 (file)
index 0000000..501abda
--- /dev/null
@@ -0,0 +1,19 @@
+As __iget is not exported by the kernel, replace it with igrab.
+
+--- a/libcfs/libcfs/crypto/keyring.c
++++ b/libcfs/libcfs/crypto/keyring.c
+@@ -660,13 +660,8 @@ static void evict_dentries_for_decrypted
+       list_for_each_entry(ci, &mk->mk_decrypted_inodes, ci_master_key_link) {
+               inode = ci->ci_inode;
+-              spin_lock(&inode->i_lock);
+-              if (inode->i_state & (I_FREEING | I_WILL_FREE | I_NEW)) {
+-                      spin_unlock(&inode->i_lock);
++              if (igrab(inode) == NULL)
+                       continue;
+-              }
+-              __iget(inode);
+-              spin_unlock(&inode->i_lock);
+               spin_unlock(&mk->mk_decrypted_inodes_lock);
+               shrink_dcache_inode(inode);
diff --git a/contrib/scripts/crypto_patches/0013_dentry_path_raw.patch b/contrib/scripts/crypto_patches/0013_dentry_path_raw.patch
new file mode 100644 (file)
index 0000000..b3e3d98
--- /dev/null
@@ -0,0 +1,13 @@
+As dentry_path is not exported by the kernel, replace with dentry_path_raw.
+
+--- a/libcfs/libcfs/crypto/keyring.c
++++ b/libcfs/libcfs/crypto/keyring.c
+@@ -707,7 +707,7 @@ static int check_for_busy_inodes(struct
+       spin_unlock(&mk->mk_decrypted_inodes_lock);
+       if (dentry) {
+-              path = dentry_path(dentry, _path, sizeof(_path));
++              path = dentry_path_raw(dentry, _path, sizeof(_path));
+               dput(dentry);
+       }
+       if (IS_ERR_OR_NULL(path))
diff --git a/contrib/scripts/crypto_patches/0014_cdebug.patch b/contrib/scripts/crypto_patches/0014_cdebug.patch
new file mode 100644 (file)
index 0000000..b3c9d9e
--- /dev/null
@@ -0,0 +1,45 @@
+Make llcrypt_msg use CDEBUG.
+
+--- a/libcfs/libcfs/crypto/crypto.c
++++ b/libcfs/libcfs/crypto/crypto.c
+@@ -451,7 +451,7 @@ fail:
+       return res;
+ }
+-void llcrypt_msg(const struct inode *inode, const char *level,
++void llcrypt_msg(const struct inode *inode, int mask,
+                const char *fmt, ...)
+ {
+       static DEFINE_RATELIMIT_STATE(rs, DEFAULT_RATELIMIT_INTERVAL,
+@@ -466,10 +466,10 @@ void llcrypt_msg(const struct inode *ino
+       vaf.fmt = fmt;
+       vaf.va = &args;
+       if (inode)
+-              printk("%sllcrypt (%s, inode %lu): %pV\n",
+-                     level, inode->i_sb->s_id, inode->i_ino, &vaf);
++              CDEBUG(mask, "llcrypt (%s, inode %lu): %pV\n",
++                     inode->i_sb->s_id, inode->i_ino, &vaf);
+       else
+-              printk("%sllcrypt: %pV\n", level, &vaf);
++              CDEBUG(mask, "llcrypt: %pV\n", &vaf);
+       va_end(args);
+ }
+--- a/libcfs/libcfs/crypto/llcrypt_private.h
++++ b/libcfs/libcfs/crypto/llcrypt_private.h
+@@ -249,12 +249,12 @@ extern struct page *llcrypt_alloc_bounce
+ extern const struct dentry_operations llcrypt_d_ops;
+ extern void __printf(3, 4) __cold
+-llcrypt_msg(const struct inode *inode, const char *level, const char *fmt, ...);
++llcrypt_msg(const struct inode *inode, int mask, const char *fmt, ...);
+ #define llcrypt_warn(inode, fmt, ...)         \
+-      llcrypt_msg((inode), KERN_WARNING, fmt, ##__VA_ARGS__)
++      llcrypt_msg((inode), D_SEC, fmt, ##__VA_ARGS__)
+ #define llcrypt_err(inode, fmt, ...)          \
+-      llcrypt_msg((inode), KERN_ERR, fmt, ##__VA_ARGS__)
++      llcrypt_msg((inode), D_ERROR, fmt, ##__VA_ARGS__)
+ #define LLCRYPT_MAX_IV_SIZE   32
diff --git a/contrib/scripts/crypto_patches/0015_llcrypt_include.patch b/contrib/scripts/crypto_patches/0015_llcrypt_include.patch
new file mode 100644 (file)
index 0000000..2ec4552
--- /dev/null
@@ -0,0 +1,34 @@
+In case llcrypt is not built (CONFIG_LL_ENCRYPTION undefined),
+we need to have stubs for llcrypt init/exit functions.
+
+--- a/libcfs/include/libcfs/crypto/llcrypt.h
++++ b/libcfs/include/libcfs/crypto/llcrypt.h
+@@ -287,6 +287,13 @@ static inline void llcrypt_set_ops(struc
+ }
+ #else  /* !CONFIG_LL_ENCRYPTION */
++struct llcrypt_operations;
++#define llcrypt_init()         0
++#define llcrypt_exit()         {}
++
++#undef IS_ENCRYPTED
++#define IS_ENCRYPTED(x)       0
++
+ static inline bool llcrypt_has_encryption_key(const struct inode *inode)
+ {
+       return false;
+@@ -554,13 +561,7 @@ static inline int __llcrypt_encrypt_syml
+       return -EOPNOTSUPP;
+ }
+-static inline const char *llcrypt_get_symlink(struct inode *inode,
+-                                            const void *caddr,
+-                                            unsigned int max_size,
+-                                            struct delayed_call *done)
+-{
+-      return ERR_PTR(-EOPNOTSUPP);
+-}
++#define llcrypt_get_symlink(inode, caddr, max_size, done)   ERR_PTR(-EOPNOTSUPP)
+ static inline void llcrypt_set_ops(struct super_block *sb,
+                                  const struct llcrypt_operations *lsi_cop)
diff --git a/contrib/scripts/crypto_patches/series b/contrib/scripts/crypto_patches/series
new file mode 100644 (file)
index 0000000..e5b06d0
--- /dev/null
@@ -0,0 +1,15 @@
+contrib/scripts/crypto_patches/0001_llcrypt_private_include.patch
+contrib/scripts/crypto_patches/0002_dcache_encrypted_name.patch
+contrib/scripts/crypto_patches/0003_keyring_search_4args.patch
+contrib/scripts/crypto_patches/0004_master_keys.patch
+contrib/scripts/crypto_patches/0005_crypto_tfm_req_forbid_weak_keys.patch
+contrib/scripts/crypto_patches/0006_bvec_iter_all.patch
+contrib/scripts/crypto_patches/0007_crypto_init.patch
+contrib/scripts/crypto_patches/0008_key_desc_prefix.patch
+contrib/scripts/crypto_patches/0009_lsi_cop.patch
+contrib/scripts/crypto_patches/0010_llcrypt_info.patch
+contrib/scripts/crypto_patches/0011_llcrypt_cleanup.patch
+contrib/scripts/crypto_patches/0012_igrab.patch
+contrib/scripts/crypto_patches/0013_dentry_path_raw.patch
+contrib/scripts/crypto_patches/0014_cdebug.patch
+contrib/scripts/crypto_patches/0015_llcrypt_include.patch
diff --git a/contrib/scripts/fscrypt_inclusion.sh b/contrib/scripts/fscrypt_inclusion.sh
new file mode 100755 (executable)
index 0000000..4f7ddf9
--- /dev/null
@@ -0,0 +1,46 @@
+#!/bin/bash
+
+#
+# Purpose of this script is to show the method used to integrate fscrypt
+# sources from the Linux kernel, and the transformations required.
+#
+# All C files from $LINUX/fs/crypto/ are put under libcfs/libcfs/crypto/.
+# File $LINUX/include/linux/fscrypt.h is put in libcfs/include/libcfs/crypto/.
+# File $LINUX/include/uapi/linux/fscrypt.h is placed under
+# libcfs/include/uapi/linux/.
+#
+
+mv libcfs/libcfs/crypto/fscrypt_private.h libcfs/libcfs/crypto/llcrypt_private.h
+mv libcfs/include/libcfs/crypto/fscrypt.h libcfs/include/libcfs/crypto/llcrypt.h
+mv libcfs/include/uapi/linux/fscrypt.h libcfs/include/uapi/linux/llcrypt.h
+
+file_list=$(find libcfs/libcfs/crypto/ -type f)
+file_list+=" libcfs/include/libcfs/crypto/Makefile.am"
+file_list+=" libcfs/include/libcfs/crypto/llcrypt.h"
+file_list+=" libcfs/include/libcfs/crypto/Makefile.in"
+file_list+=" libcfs/include/uapi/linux/llcrypt.h"
+
+udef_list=$(grep -n "#define FS_" libcfs/include/uapi/linux/llcrypt.h | awk '{print $2}')
+
+for file in $file_list; do
+       cp $file ${file}.bkp
+       sed -i s+fscrypt+llcrypt+g $file
+       sed -i s+FSCRYPT+LLCRYPT+g $file
+       sed -i s+FS_CRYPTO_BLOCK_SIZE+LL_CRYPTO_BLOCK_SIZE+g $file
+       sed -i s+FSTR_INIT+LLTR_INIT+g $file
+       sed -i s+FSTR_TO_QSTR+LLTR_TO_QSTR+g $file
+       sed -i s+CONFIG_FS_ENCRYPTION+CONFIG_LL_ENCRYPTION+g $file
+       sed -i s+FS_CFLG_OWN_PAGES+LL_CFLG_OWN_PAGES+g $file
+       sed -i s+fname_name+lname_name+g $file
+       sed -i s+fname_len+lname_len+g $file
+       for def in $udef_list; do
+               newdef=$(echo $def | sed s+^FS_+LL_+)
+               sed -i s+$def+$newdef+g $file
+       done
+done
+
+for patch in $(cat contrib/scripts/crypto_patches/series); do
+       patch -p1 < $patch
+done
+
+exit 0
index 33f7878..cbb4a5b 100755 (executable)
@@ -194,6 +194,11 @@ configure-stamp: autogen-stamp debian/control.main debian/control.modules.in
        elif echo "$${DEB_BUILD_PROFILES}" | grep -qw "nogss"; then \
                export EXTRAFLAGS="$${EXTRAFLAGS} --disable-gss"; \
        fi; \
+       if echo "$${DEB_BUILD_PROFILES}" | grep -qw "crypto"; then \
+               export EXTRAFLAGS="$${EXTRAFLAGS} --enable-crypto"; \
+       elif echo "$${DEB_BUILD_PROFILES}" | grep -qw "nocrypto"; then \
+               export EXTRAFLAGS="$${EXTRAFLAGS} --disable-crypto"; \
+       fi; \
        echo "Final value of EXTRAFLAGS: $${EXTRAFLAGS}"; \
        ( cd $(BUILDDIR) && \
                $(SRCDIR)/configure --disable-dependency-tracking \
@@ -475,6 +480,11 @@ kdist_config: prep-deb-files patch-stamp
        if echo "$${DEB_BUILD_PROFILES}" | grep -q "o2ib"; then \
                export EXTRAFLAGS="$${EXTRAFLAGS} --with-o2ib=$${O2IB_SRC}"; \
        fi; \
+       if echo "$${DEB_BUILD_PROFILES}" | grep -qw "crypto"; then \
+               export EXTRAFLAGS="$${EXTRAFLAGS} --enable-crypto"; \
+       elif echo "$${DEB_BUILD_PROFILES}" | grep -qw "nocrypto"; then \
+               export EXTRAFLAGS="$${EXTRAFLAGS} --disable-crypto"; \
+       fi; \
        echo "Final value of EXTRAFLAGS: $${EXTRAFLAGS}"; \
        ./configure --with-linux=$(KSRC_TREE) \
                --with-linux-obj=$(KSRC) \
index 8db36ae..76775ad 100644 (file)
@@ -1368,11 +1368,15 @@ libcfs/autoMakefile
 libcfs/autoconf/Makefile
 libcfs/include/Makefile
 libcfs/include/libcfs/Makefile
+libcfs/include/uapi/Makefile
 libcfs/include/libcfs/linux/Makefile
 libcfs/include/libcfs/util/Makefile
+libcfs/include/libcfs/crypto/Makefile
+libcfs/include/uapi/linux/Makefile
 libcfs/libcfs/Makefile
 libcfs/libcfs/autoMakefile
 libcfs/libcfs/linux/Makefile
 libcfs/libcfs/util/Makefile
+libcfs/libcfs/crypto/Makefile
 ])
 ]) # LIBCFS_CONFIG_FILES
index 8289f5f..9152b79 100644 (file)
@@ -1 +1 @@
-SUBDIRS = libcfs 
+SUBDIRS = libcfs uapi
index 7164dbb..40b998a 100644 (file)
@@ -1,5 +1,7 @@
 SUBDIRS = linux util
 DIST_SUBDIRS = linux util
+SUBDIRS += crypto
+DIST_SUBDIRS += crypto
 
 libcfsdir = $(includedir)/libcfs
 
diff --git a/libcfs/include/libcfs/crypto/.gitignore b/libcfs/include/libcfs/crypto/.gitignore
new file mode 100644 (file)
index 0000000..10a7e8d
--- /dev/null
@@ -0,0 +1 @@
+/Makefile.in
diff --git a/libcfs/include/libcfs/crypto/Makefile.am b/libcfs/include/libcfs/crypto/Makefile.am
new file mode 100644 (file)
index 0000000..e8cdd97
--- /dev/null
@@ -0,0 +1 @@
+EXTRA_DIST = llcrypt.h
diff --git a/libcfs/include/libcfs/crypto/llcrypt.h b/libcfs/include/libcfs/crypto/llcrypt.h
new file mode 100644 (file)
index 0000000..038ed4b
--- /dev/null
@@ -0,0 +1,796 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * llcrypt.h: declarations for per-file encryption
+ *
+ * Filesystems that implement per-file encryption must include this header
+ * file.
+ *
+ * Copyright (C) 2015, Google, Inc.
+ *
+ * Written by Michael Halcrow, 2015.
+ * Modified by Jaegeuk Kim, 2015.
+ */
+/*
+ * Linux commit 219d54332a09
+ * tags/v5.4
+ */
+#ifndef _LINUX_LLCRYPT_H
+#define _LINUX_LLCRYPT_H
+
+#ifndef DCACHE_ENCRYPTED_NAME
+#define DCACHE_ENCRYPTED_NAME 0x02000000
+#endif
+
+#include <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 */
diff --git a/libcfs/include/uapi/.gitignore b/libcfs/include/uapi/.gitignore
new file mode 100644 (file)
index 0000000..10a7e8d
--- /dev/null
@@ -0,0 +1 @@
+/Makefile.in
diff --git a/libcfs/include/uapi/Makefile.am b/libcfs/include/uapi/Makefile.am
new file mode 100644 (file)
index 0000000..08b2f5f
--- /dev/null
@@ -0,0 +1 @@
+SUBDIRS = linux
diff --git a/libcfs/include/uapi/linux/.gitignore b/libcfs/include/uapi/linux/.gitignore
new file mode 100644 (file)
index 0000000..10a7e8d
--- /dev/null
@@ -0,0 +1 @@
+/Makefile.in
diff --git a/libcfs/include/uapi/linux/Makefile.am b/libcfs/include/uapi/linux/Makefile.am
new file mode 100644 (file)
index 0000000..e8cdd97
--- /dev/null
@@ -0,0 +1 @@
+EXTRA_DIST = llcrypt.h
diff --git a/libcfs/include/uapi/linux/llcrypt.h b/libcfs/include/uapi/linux/llcrypt.h
new file mode 100644 (file)
index 0000000..dac16c0
--- /dev/null
@@ -0,0 +1,185 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * llcrypt user API
+ *
+ * These ioctls can be used on filesystems that support llcrypt.  See the
+ * "User API" section of Documentation/filesystems/llcrypt.rst.
+ */
+/*
+ * Linux commit 219d54332a09
+ * tags/v5.4
+ */
+#ifndef _UAPI_LINUX_LLCRYPT_H
+#define _UAPI_LINUX_LLCRYPT_H
+
+#include <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 */
index f4c2d2f..ad9ac74 100644 (file)
@@ -5,9 +5,13 @@ libcfs-linux-objs += linux-curproc.o
 libcfs-linux-objs += linux-hash.o
 libcfs-linux-objs += linux-wait.o
 
+libcfs-crypto-objs := crypto.o fname.o hkdf.o hooks.o keyring.o
+libcfs-crypto-objs += keysetup.o keysetup_v1.o policy.o bio.o
+
 default: all
 
 libcfs-linux-objs := $(addprefix linux/,$(libcfs-linux-objs))
+libcfs-crypto-objs := $(addprefix crypto/,$(libcfs-crypto-objs))
 
 libcfs-all-objs := debug.o fail.o module.o tracefile.o \
                   libcfs_string.o hash.o heap.o \
@@ -17,6 +21,7 @@ libcfs-all-objs := debug.o fail.o module.o tracefile.o \
                   linux-debug.o linux-tracefile.o
 
 libcfs-objs := $(libcfs-linux-objs) $(libcfs-all-objs)
+@LLCRYPT_TRUE@libcfs-objs += $(libcfs-crypto-objs)
 
 EXTRA_PRE_CFLAGS := -I@LUSTRE@/../libcfs/libcfs
 
index 92c7564..7d6553c 100644 (file)
@@ -34,6 +34,8 @@
 
 SUBDIRS = linux util
 DIST_SUBDIRS = linux util
+SUBDIRS += crypto
+DIST_SUBDIRS += crypto
 
 noinst_LTLIBRARIES = libcfs.la
 libcfs_la_SOURCES  := util/string.c util/nidstrings.c util/param.c
@@ -55,7 +57,7 @@ endif # LINUX
 
 endif # MODULES
 
-MOSTLYCLEANFILES := @MOSTLYCLEANFILES@ linux/*.o libcfs
+MOSTLYCLEANFILES := @MOSTLYCLEANFILES@ linux/*.o libcfs crypto/*.o
 EXTRA_DIST := $(libcfs-all-objs:%.o=%.c) tracefile.h \
              workitem.c fail.c libcfs_cpu.c \
              heap.c libcfs_mem.c libcfs_lock.c \
diff --git a/libcfs/libcfs/crypto/.gitignore b/libcfs/libcfs/crypto/.gitignore
new file mode 100644 (file)
index 0000000..10a7e8d
--- /dev/null
@@ -0,0 +1 @@
+/Makefile.in
diff --git a/libcfs/libcfs/crypto/ChangeLog b/libcfs/libcfs/crypto/ChangeLog
new file mode 100644 (file)
index 0000000..cc16ee3
--- /dev/null
@@ -0,0 +1,2 @@
+04-02-2020 Whamcloud, Inc.
+        * fscrypt sources taken from Linux kernel v5.4
diff --git a/libcfs/libcfs/crypto/Makefile.am b/libcfs/libcfs/crypto/Makefile.am
new file mode 100644 (file)
index 0000000..2eaeb83
--- /dev/null
@@ -0,0 +1,4 @@
+if LLCRYPT
+EXTRA_DIST = crypto.c fname.c hkdf.c hooks.c keyring.c \
+       keysetup.c keysetup_v1.c policy.c bio.c llcrypt_private.h
+endif
diff --git a/libcfs/libcfs/crypto/bio.c b/libcfs/libcfs/crypto/bio.c
new file mode 100644 (file)
index 0000000..a0e96db
--- /dev/null
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This contains encryption functions for per-file encryption.
+ *
+ * Copyright (C) 2015, Google, Inc.
+ * Copyright (C) 2015, Motorola Mobility
+ *
+ * Written by Michael Halcrow, 2014.
+ *
+ * Filename encryption additions
+ *     Uday Savagaonkar, 2014
+ * Encryption policy handling additions
+ *     Ildar Muslukhov, 2014
+ * Add llcrypt_pullback_bio_page()
+ *     Jaegeuk Kim, 2015.
+ *
+ * This has not yet undergone a rigorous security audit.
+ *
+ * The usage of AES-XTS should conform to recommendations in NIST
+ * Special Publication 800-38E and IEEE P1619/D16.
+ */
+/*
+ * Linux commit 219d54332a09
+ * tags/v5.4
+ */
+
+#include <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);
diff --git a/libcfs/libcfs/crypto/crypto.c b/libcfs/libcfs/crypto/crypto.c
new file mode 100644 (file)
index 0000000..379affb
--- /dev/null
@@ -0,0 +1,542 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This contains encryption functions for per-file encryption.
+ *
+ * Copyright (C) 2015, Google, Inc.
+ * Copyright (C) 2015, Motorola Mobility
+ *
+ * Written by Michael Halcrow, 2014.
+ *
+ * Filename encryption additions
+ *     Uday Savagaonkar, 2014
+ * Encryption policy handling additions
+ *     Ildar Muslukhov, 2014
+ * Add llcrypt_pullback_bio_page()
+ *     Jaegeuk Kim, 2015.
+ *
+ * This has not yet undergone a rigorous security audit.
+ *
+ * The usage of AES-XTS should conform to recommendations in NIST
+ * Special Publication 800-38E and IEEE P1619/D16.
+ */
+/*
+ * Linux commit 219d54332a09
+ * tags/v5.4
+ */
+
+#include <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);
+}
diff --git a/libcfs/libcfs/crypto/fname.c b/libcfs/libcfs/crypto/fname.c
new file mode 100644 (file)
index 0000000..08accf8
--- /dev/null
@@ -0,0 +1,410 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This contains functions for filename crypto management
+ *
+ * Copyright (C) 2015, Google, Inc.
+ * Copyright (C) 2015, Motorola Mobility
+ *
+ * Written by Uday Savagaonkar, 2014.
+ * Modified by Jaegeuk Kim, 2015.
+ *
+ * This has not yet undergone a rigorous security audit.
+ */
+/*
+ * Linux commit 219d54332a09
+ * tags/v5.4
+ */
+
+#include <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);
diff --git a/libcfs/libcfs/crypto/hkdf.c b/libcfs/libcfs/crypto/hkdf.c
new file mode 100644 (file)
index 0000000..147f14b
--- /dev/null
@@ -0,0 +1,185 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Implementation of HKDF ("HMAC-based Extract-and-Expand Key Derivation
+ * Function"), aka RFC 5869.  See also the original paper (Krawczyk 2010):
+ * "Cryptographic Extraction and Key Derivation: The HKDF Scheme".
+ *
+ * This is used to derive keys from the llcrypt master keys.
+ *
+ * Copyright 2019 Google LLC
+ */
+/*
+ * Linux commit 219d54332a09
+ * tags/v5.4
+ */
+
+#include <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);
+}
diff --git a/libcfs/libcfs/crypto/hooks.c b/libcfs/libcfs/crypto/hooks.c
new file mode 100644 (file)
index 0000000..10a837f
--- /dev/null
@@ -0,0 +1,311 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * fs/crypto/hooks.c
+ *
+ * Encryption hooks for higher-level filesystem operations.
+ */
+/*
+ * Linux commit 219d54332a09
+ * tags/v5.4
+ */
+
+#include "llcrypt_private.h"
+
+/**
+ * llcrypt_file_open - prepare to open a possibly-encrypted regular file
+ * @inode: the inode being opened
+ * @filp: the struct file being set up
+ *
+ * Currently, an encrypted regular file can only be opened if its encryption key
+ * is available; access to the raw encrypted contents is not supported.
+ * Therefore, we first set up the inode's encryption key (if not already done)
+ * and return an error if it's unavailable.
+ *
+ * We also verify that if the parent directory (from the path via which the file
+ * is being opened) is encrypted, then the inode being opened uses the same
+ * encryption policy.  This is needed as part of the enforcement that all files
+ * in an encrypted directory tree use the same encryption policy, as a
+ * protection against certain types of offline attacks.  Note that this check is
+ * needed even when opening an *unencrypted* file, since it's forbidden to have
+ * an unencrypted file in an encrypted directory.
+ *
+ * Return: 0 on success, -ENOKEY if the key is missing, or another -errno code
+ */
+int llcrypt_file_open(struct inode *inode, struct file *filp)
+{
+       int err;
+       struct dentry *dir;
+
+       err = llcrypt_require_key(inode);
+       if (err)
+               return err;
+
+       dir = dget_parent(file_dentry(filp));
+       if (IS_ENCRYPTED(d_inode(dir)) &&
+           !llcrypt_has_permitted_context(d_inode(dir), inode)) {
+               llcrypt_warn(inode,
+                            "Inconsistent encryption context (parent directory: %lu)",
+                            d_inode(dir)->i_ino);
+               err = -EPERM;
+       }
+       dput(dir);
+       return err;
+}
+EXPORT_SYMBOL_GPL(llcrypt_file_open);
+
+int __llcrypt_prepare_link(struct inode *inode, struct inode *dir,
+                          struct dentry *dentry)
+{
+       int err;
+
+       err = llcrypt_require_key(dir);
+       if (err)
+               return err;
+
+       /* ... in case we looked up ciphertext name before key was added */
+       if (dentry->d_flags & DCACHE_ENCRYPTED_NAME)
+               return -ENOKEY;
+
+       if (!llcrypt_has_permitted_context(dir, inode))
+               return -EXDEV;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(__llcrypt_prepare_link);
+
+int __llcrypt_prepare_rename(struct inode *old_dir, struct dentry *old_dentry,
+                            struct inode *new_dir, struct dentry *new_dentry,
+                            unsigned int flags)
+{
+       int err;
+
+       err = llcrypt_require_key(old_dir);
+       if (err)
+               return err;
+
+       err = llcrypt_require_key(new_dir);
+       if (err)
+               return err;
+
+       /* ... in case we looked up ciphertext name(s) before key was added */
+       if ((old_dentry->d_flags | new_dentry->d_flags) &
+           DCACHE_ENCRYPTED_NAME)
+               return -ENOKEY;
+
+       if (old_dir != new_dir) {
+               if (IS_ENCRYPTED(new_dir) &&
+                   !llcrypt_has_permitted_context(new_dir,
+                                                  d_inode(old_dentry)))
+                       return -EXDEV;
+
+               if ((flags & RENAME_EXCHANGE) &&
+                   IS_ENCRYPTED(old_dir) &&
+                   !llcrypt_has_permitted_context(old_dir,
+                                                  d_inode(new_dentry)))
+                       return -EXDEV;
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(__llcrypt_prepare_rename);
+
+int __llcrypt_prepare_lookup(struct inode *dir, struct dentry *dentry,
+                            struct llcrypt_name *fname)
+{
+       int err = llcrypt_setup_filename(dir, &dentry->d_name, 1, fname);
+
+       if (err && err != -ENOENT)
+               return err;
+
+       if (fname->is_ciphertext_name) {
+               spin_lock(&dentry->d_lock);
+               dentry->d_flags |= DCACHE_ENCRYPTED_NAME;
+               spin_unlock(&dentry->d_lock);
+               d_set_d_op(dentry, &llcrypt_d_ops);
+       }
+       return err;
+}
+EXPORT_SYMBOL_GPL(__llcrypt_prepare_lookup);
+
+int __llcrypt_prepare_symlink(struct inode *dir, unsigned int len,
+                             unsigned int max_len,
+                             struct llcrypt_str *disk_link)
+{
+       int err;
+
+       /*
+        * To calculate the size of the encrypted symlink target we need to know
+        * the amount of NUL padding, which is determined by the flags set in
+        * the encryption policy which will be inherited from the directory.
+        * The easiest way to get access to this is to just load the directory's
+        * llcrypt_info, since we'll need it to create the dir_entry anyway.
+        *
+        * Note: in test_dummy_encryption mode, @dir may be unencrypted.
+        */
+       err = llcrypt_get_encryption_info(dir);
+       if (err)
+               return err;
+       if (!llcrypt_has_encryption_key(dir))
+               return -ENOKEY;
+
+       /*
+        * Calculate the size of the encrypted symlink and verify it won't
+        * exceed max_len.  Note that for historical reasons, encrypted symlink
+        * targets are prefixed with the ciphertext length, despite this
+        * actually being redundant with i_size.  This decreases by 2 bytes the
+        * longest symlink target we can accept.
+        *
+        * We could recover 1 byte by not counting a null terminator, but
+        * counting it (even though it is meaningless for ciphertext) is simpler
+        * for now since filesystems will assume it is there and subtract it.
+        */
+       if (!llcrypt_fname_encrypted_size(dir, len,
+                                         max_len - sizeof(struct llcrypt_symlink_data),
+                                         &disk_link->len))
+               return -ENAMETOOLONG;
+       disk_link->len += sizeof(struct llcrypt_symlink_data);
+
+       disk_link->name = NULL;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(__llcrypt_prepare_symlink);
+
+int __llcrypt_encrypt_symlink(struct inode *inode, const char *target,
+                             unsigned int len, struct llcrypt_str *disk_link)
+{
+       int err;
+       struct qstr iname = QSTR_INIT(target, len);
+       struct llcrypt_symlink_data *sd;
+       unsigned int ciphertext_len;
+
+       err = llcrypt_require_key(inode);
+       if (err)
+               return err;
+
+       if (disk_link->name) {
+               /* filesystem-provided buffer */
+               sd = (struct llcrypt_symlink_data *)disk_link->name;
+       } else {
+               sd = kmalloc(disk_link->len, GFP_NOFS);
+               if (!sd)
+                       return -ENOMEM;
+       }
+       ciphertext_len = disk_link->len - sizeof(*sd);
+       sd->len = cpu_to_le16(ciphertext_len);
+
+       err = fname_encrypt(inode, &iname, sd->encrypted_path, ciphertext_len);
+       if (err)
+               goto err_free_sd;
+
+       /*
+        * Null-terminating the ciphertext doesn't make sense, but we still
+        * count the null terminator in the length, so we might as well
+        * initialize it just in case the filesystem writes it out.
+        */
+       sd->encrypted_path[ciphertext_len] = '\0';
+
+       /* Cache the plaintext symlink target for later use by get_link() */
+       err = -ENOMEM;
+       inode->i_link = kmemdup(target, len + 1, GFP_NOFS);
+       if (!inode->i_link)
+               goto err_free_sd;
+
+       if (!disk_link->name)
+               disk_link->name = (unsigned char *)sd;
+       return 0;
+
+err_free_sd:
+       if (!disk_link->name)
+               kfree(sd);
+       return err;
+}
+EXPORT_SYMBOL_GPL(__llcrypt_encrypt_symlink);
+
+/**
+ * llcrypt_get_symlink - get the target of an encrypted symlink
+ * @inode: the symlink inode
+ * @caddr: the on-disk contents of the symlink
+ * @max_size: size of @caddr buffer
+ * @done: if successful, will be set up to free the returned target if needed
+ *
+ * If the symlink's encryption key is available, we decrypt its target.
+ * Otherwise, we encode its target for presentation.
+ *
+ * This may sleep, so the filesystem must have dropped out of RCU mode already.
+ *
+ * Return: the presentable symlink target or an ERR_PTR()
+ */
+const char *llcrypt_get_symlink(struct inode *inode, const void *caddr,
+                               unsigned int max_size,
+                               struct delayed_call *done)
+{
+       const struct llcrypt_symlink_data *sd;
+       struct llcrypt_str cstr, pstr;
+       bool has_key;
+       int err;
+
+       /* This is for encrypted symlinks only */
+       if (WARN_ON(!IS_ENCRYPTED(inode)))
+               return ERR_PTR(-EINVAL);
+
+       /* If the decrypted target is already cached, just return it. */
+       pstr.name = READ_ONCE(inode->i_link);
+       if (pstr.name)
+               return pstr.name;
+
+       /*
+        * Try to set up the symlink's encryption key, but we can continue
+        * regardless of whether the key is available or not.
+        */
+       err = llcrypt_get_encryption_info(inode);
+       if (err)
+               return ERR_PTR(err);
+       has_key = llcrypt_has_encryption_key(inode);
+
+       /*
+        * For historical reasons, encrypted symlink targets are prefixed with
+        * the ciphertext length, even though this is redundant with i_size.
+        */
+
+       if (max_size < sizeof(*sd))
+               return ERR_PTR(-EUCLEAN);
+       sd = caddr;
+       cstr.name = (unsigned char *)sd->encrypted_path;
+       cstr.len = le16_to_cpu(sd->len);
+
+       if (cstr.len == 0)
+               return ERR_PTR(-EUCLEAN);
+
+       if (cstr.len + sizeof(*sd) - 1 > max_size)
+               return ERR_PTR(-EUCLEAN);
+
+       err = llcrypt_fname_alloc_buffer(inode, cstr.len, &pstr);
+       if (err)
+               return ERR_PTR(err);
+
+       err = llcrypt_fname_disk_to_usr(inode, 0, 0, &cstr, &pstr);
+       if (err)
+               goto err_kfree;
+
+       err = -EUCLEAN;
+       if (pstr.name[0] == '\0')
+               goto err_kfree;
+
+       pstr.name[pstr.len] = '\0';
+
+       /*
+        * Cache decrypted symlink targets in i_link for later use.  Don't cache
+        * symlink targets encoded without the key, since those become outdated
+        * once the key is added.  This pairs with the READ_ONCE() above and in
+        * the VFS path lookup code.
+        */
+       if (!has_key ||
+           cmpxchg_release(&inode->i_link, NULL, pstr.name) != NULL)
+               set_delayed_call(done, kfree_link, pstr.name);
+
+       return pstr.name;
+
+err_kfree:
+       kfree(pstr.name);
+       return ERR_PTR(err);
+}
+EXPORT_SYMBOL_GPL(llcrypt_get_symlink);
diff --git a/libcfs/libcfs/crypto/keyring.c b/libcfs/libcfs/crypto/keyring.c
new file mode 100644 (file)
index 0000000..a9dde26
--- /dev/null
@@ -0,0 +1,1011 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Filesystem-level keyring for llcrypt
+ *
+ * Copyright 2019 Google LLC
+ */
+
+/*
+ * This file implements management of llcrypt master keys in the
+ * filesystem-level keyring, including the ioctls:
+ *
+ * - LL_IOC_ADD_ENCRYPTION_KEY
+ * - LL_IOC_REMOVE_ENCRYPTION_KEY
+ * - LL_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS
+ * - LL_IOC_GET_ENCRYPTION_KEY_STATUS
+ *
+ * See the "User API" section of Documentation/filesystems/llcrypt.rst for more
+ * information about these ioctls.
+ */
+/*
+ * Linux commit 219d54332a09
+ * tags/v5.4
+ */
+
+#include <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);
+}
diff --git a/libcfs/libcfs/crypto/keysetup.c b/libcfs/libcfs/crypto/keysetup.c
new file mode 100644 (file)
index 0000000..c9ca537
--- /dev/null
@@ -0,0 +1,608 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Key setup facility for FS encryption support.
+ *
+ * Copyright (C) 2015, Google, Inc.
+ *
+ * Originally written by Michael Halcrow, Ildar Muslukhov, and Uday Savagaonkar.
+ * Heavily modified since then.
+ */
+/*
+ * Linux commit 219d54332a09
+ * tags/v5.4
+ */
+
+#include <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);
diff --git a/libcfs/libcfs/crypto/keysetup_v1.c b/libcfs/libcfs/crypto/keysetup_v1.c
new file mode 100644 (file)
index 0000000..d26735e
--- /dev/null
@@ -0,0 +1,350 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Key setup for v1 encryption policies
+ *
+ * Copyright 2015, 2019 Google LLC
+ */
+/*
+ * Linux commit 219d54332a09
+ * tags/v5.4
+ */
+
+/*
+ * This file implements compatibility functions for the original encryption
+ * policy version ("v1"), including:
+ *
+ * - Deriving per-file keys using the AES-128-ECB based KDF
+ *   (rather than the new method of using HKDF-SHA512)
+ *
+ * - Retrieving llcrypt master keys from process-subscribed keyrings
+ *   (rather than the new method of using a filesystem-level keyring)
+ *
+ * - Handling policies with the DIRECT_KEY flag set using a master key table
+ *   (rather than the new method of implementing DIRECT_KEY with per-mode keys
+ *    managed alongside the master keys in the filesystem-level keyring)
+ */
+
+#include <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;
+}
diff --git a/libcfs/libcfs/crypto/llcrypt_private.h b/libcfs/libcfs/crypto/llcrypt_private.h
new file mode 100644 (file)
index 0000000..fbf7c73
--- /dev/null
@@ -0,0 +1,496 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * llcrypt_private.h
+ *
+ * Copyright (C) 2015, Google, Inc.
+ *
+ * Originally written by Michael Halcrow, Ildar Muslukhov, and Uday Savagaonkar.
+ * Heavily modified since then.
+ */
+/*
+ * Linux commit 219d54332a09
+ * tags/v5.4
+ */
+
+#ifndef _LLCRYPT_PRIVATE_H
+#define _LLCRYPT_PRIVATE_H
+
+#include <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 */
diff --git a/libcfs/libcfs/crypto/policy.c b/libcfs/libcfs/crypto/policy.c
new file mode 100644 (file)
index 0000000..bdfc683
--- /dev/null
@@ -0,0 +1,511 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Encryption policy functions for per-file encryption support.
+ *
+ * Copyright (C) 2015, Google, Inc.
+ * Copyright (C) 2015, Motorola Mobility.
+ *
+ * Originally written by Michael Halcrow, 2015.
+ * Modified by Jaegeuk Kim, 2015.
+ * Modified by Eric Biggers, 2019 for v2 policy support.
+ */
+/*
+ * Linux commit 219d54332a09
+ * tags/v5.4
+ */
+
+#include <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);
index fd6f34b..8b165cc 100644 (file)
@@ -54,6 +54,7 @@
 #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;
@@ -763,6 +764,12 @@ static int __init libcfs_init(void)
        if (!IS_ERR_OR_NULL(lnet_debugfs_root))
                lnet_insert_debugfs_links(lnet_debugfs_symlinks);
 
+       rc = llcrypt_init();
+       if (rc) {
+               CERROR("llcrypt_init: error %d\n", rc);
+               goto cleanup_wi;
+       }
+
        CDEBUG (D_OTHER, "portals setup OK\n");
        return 0;
 cleanup_wi:
@@ -787,6 +794,8 @@ static void __exit libcfs_exit(void)
        CDEBUG(D_MALLOC, "before Portals cleanup: kmem %d\n",
               atomic_read(&libcfs_kmemory));
 
+       llcrypt_exit();
+
        if (cfs_rehash_wq) {
                destroy_workqueue(cfs_rehash_wq);
                cfs_rehash_wq = NULL;
index 3ab5094..74d9743 100644 (file)
@@ -1878,6 +1878,22 @@ bi_bdev, [
 ]) # LC_BI_BDEV
 
 #
+# LC_IS_ENCRYPTED
+#
+# 4.14 introduced IS_ENCRYPTED and S_ENCRYPTED
+#
+AC_DEFUN([LC_IS_ENCRYPTED], [
+LB_CHECK_COMPILE([if IS_ENCRYPTED is defined],
+is_encrypted, [
+       #include <linux/fs.h>
+],[
+       IS_ENCRYPTED((struct inode *)0);
+],[
+       has_is_encrypted="yes"
+])
+]) # LC_IS_ENCRYPTED
+
+#
 # LC_I_PAGES
 #
 # kernel 4.17 commit b93b016313b3ba8003c3b8bb71f569af91f19fc7
@@ -2058,6 +2074,28 @@ EXTRA_KCFLAGS="$tmp_flags"
 ]) # LC_HAS_LINUX_SELINUX_ENABLED
 
 #
+# LB_HAVE_BVEC_ITER_ALL
+#
+# kernel 5.1 commit 6dc4f100c175dd0511ae8674786e7c9006cdfbfa
+# block: allow bio_for_each_segment_all() to iterate over multi-page bvec
+#
+AC_DEFUN([LB_HAVE_BVEC_ITER_ALL], [
+tmp_flags="$EXTRA_KCFLAGS"
+EXTRA_KCFLAGS="-Werror"
+LB_CHECK_COMPILE([if bvec_iter_all exists for multi-page bvec iternation],
+ext4fs_dirhash, [
+       #include <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
@@ -2069,6 +2107,25 @@ LB_CHECK_EXPORT([account_page_dirtied], [mm/page-writeback.c],
 ]) # LC_ACCOUNT_PAGE_DIRTIED
 
 #
+# LC_KEYRING_SEARCH_4ARGS
+#
+# Kernel 5.2 commit dcf49dbc8077
+# keys: Add a 'recurse' flag for keyring searches
+#
+AC_DEFUN([LC_KEYRING_SEARCH_4ARGS], [
+LB_CHECK_COMPILE([if 'keyring_search' has 4 args],
+keyring_search_4args, [
+       #include <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
@@ -2114,6 +2171,23 @@ EXTRA_KCFLAGS="$tmp_flags"
 ]) # LC_LM_COMPARE_OWNER_EXISTS
 
 #
+# LC_FSCRYPT_SUPPORT
+#
+# 5.4 introduced fscrypt encryption policies v2
+#
+AC_DEFUN([LC_FSCRYPT_SUPPORT], [
+LB_CHECK_COMPILE([for fscrypt in-kernel support],
+fscrypt_support, [
+       #define __FS_HAS_ENCRYPTION 0
+       #include <linux/fscrypt.h>
+],[
+       fscrypt_ioctl_get_policy_ex(NULL, NULL);
+],[
+       has_fscrypt_support="yes"
+])
+]) # LC_FSCRYPT_SUPPORT
+
+#
 # LC_PROG_LINUX
 #
 # Lustre linux kernel checks
@@ -2279,9 +2353,11 @@ AC_DEFUN([LC_PROG_LINUX], [
 
        # 5.1
        LC_HAS_LINUX_SELINUX_ENABLED
+       LB_HAVE_BVEC_ITER_ALL
 
        # 5.2
        LC_ACCOUNT_PAGE_DIRTIED
+       LC_KEYRING_SEARCH_4ARGS
 
        # 5.3
        LC_BIO_BI_PHYS_SEGMENTS
@@ -2434,6 +2510,41 @@ AC_SUBST(OSDADDON)
 ]) # LC_OSD_ADDON
 
 #
+# LC_CONFIG_CRYPTO
+#
+# Check whether to enable Lustre client crypto
+#
+AC_DEFUN([LC_CONFIG_CRYPTO], [
+AC_MSG_CHECKING([whether to enable Lustre client crypto])
+AC_ARG_ENABLE([crypto],
+       AC_HELP_STRING([--enable-crypto],
+               [enable Lustre client crypto]),
+       [], [enable_crypto="auto"])
+AS_IF([test "x$enable_crypto" != xno -a "x$enable_dist" = xno], [
+       AC_MSG_RESULT(
+       )
+       LC_IS_ENCRYPTED
+       LC_FSCRYPT_SUPPORT])
+AS_IF([test "x$has_fscrypt_support" = xyes], [
+       AC_DEFINE(HAVE_LUSTRE_CRYPTO, 1, [Enable Lustre client crypto via in-kernel fscrypt])
+       enable_crypto=yes],
+       [AS_IF([test "x$has_is_encrypted" = xyes], [
+             AC_DEFINE(HAVE_LUSTRE_CRYPTO, 1, [Enable Lustre client crypto via embedded llcrypt])
+             AC_DEFINE(CONFIG_LL_ENCRYPTION, 1, [embedded llcrypt])
+             enable_crypto=yes
+             enable_llcrypt=yes], [
+             AS_IF([test "x$enable_crypto" = xyes],
+                   [AC_MSG_ERROR([Lustre client crypto cannot be enabled because of lack of encryption support in your kernel.])])
+             AS_IF([test "x$enable_crypto" != xno -a "x$enable_dist" = xno],
+                   [AC_MSG_WARN(Lustre client crypto cannot be enabled because of lack of encryption support in your kernel.)])
+             enable_crypto=no])])
+AS_IF([test "x$enable_dist" != xno], [
+       enable_crypto=yes
+       enable_llcrypt=yes])
+AC_MSG_RESULT([$enable_crypto])
+]) # LC_CONFIG_CRYPTO
+
+#
 # LC_CONFIGURE
 #
 # other configure checks
@@ -2602,6 +2713,7 @@ AM_CONDITIONAL(ENABLE_BASH_COMPLETION, test "x$with_bash_completion_dir" != "xno
 AM_CONDITIONAL(XATTR_HANDLER, test "x$lb_cv_compile_xattr_handler_flags" = xyes)
 AM_CONDITIONAL(SELINUX, test "$SELINUX" = "-lselinux")
 AM_CONDITIONAL(GETSEPOL, test x$enable_getsepol = xyes)
+AM_CONDITIONAL(LLCRYPT, test x$enable_llcrypt = xyes)
 ]) # LC_CONDITIONALS
 
 #
index 18d5241..45222b0 100644 (file)
@@ -138,6 +138,10 @@ struct lustre_sb_info {
        struct list_head          lsi_lwp_list;
        unsigned long             lsi_lwp_started:1,
                                  lsi_server_started:1;
+#ifdef CONFIG_LL_ENCRYPTION
+       const struct llcrypt_operations *lsi_cop;
+       struct key               *lsi_master_keys; /* master crypto keys used */
+#endif
 };
 
 #define LSI_UMOUNT_FAILOVER              0x00200000
index de23440..d9122c5 100644 (file)
@@ -49,6 +49,7 @@
 #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;
@@ -625,6 +626,7 @@ static int lustre_free_lsi(struct super_block *sb)
        /* someone didn't call server_put_mount. */
        LASSERT(atomic_read(&lsi->lsi_mounts) == 0);
 
+       llcrypt_sb_free(sb);
        if (lsi->lsi_lmd != NULL) {
                if (lsi->lsi_lmd->lmd_dev != NULL)
                        OBD_FREE(lsi->lsi_lmd->lmd_dev,