From c12378fba7f0aa2f25de2b295ee110e2932f2a39 Mon Sep 17 00:00:00 2001 From: Sebastien Buisson Date: Wed, 12 Jan 2022 11:13:44 +0100 Subject: [PATCH] LU-15176 sec: present .fscrypt in subdir mount fscrypt userspace tool works with a .fscrypt directory at the root of the file system. In case of subdirectory mount, we virtually present this .fscrypt directory at the root of the mount point so that fscrypt can be used. This makes it possible to even do a subdirectory mount of an encrypted directory, making clients access encrypted content only. Internally, the .fscrypt directory is always stored at the root of Lustre. Signed-off-by: Sebastien Buisson Change-Id: I2a0ee360f724da1df49b2be0df986d52e06f45fd Reviewed-on: https://review.whamcloud.com/46167 Reviewed-by: Patrick Farrell Tested-by: jenkins Reviewed-by: James Simmons Reviewed-by: Andreas Dilger Tested-by: Maloo Reviewed-by: Oleg Drokin --- lustre/include/uapi/linux/lustre/lustre_user.h | 1 + lustre/llite/crypto.c | 9 +++- lustre/llite/llite_lib.c | 11 +++- lustre/llite/namei.c | 9 ++++ lustre/tests/sanity-sec.sh | 72 +++++++++++++++++++++++++- 5 files changed, 98 insertions(+), 4 deletions(-) diff --git a/lustre/include/uapi/linux/lustre/lustre_user.h b/lustre/include/uapi/linux/lustre/lustre_user.h index e9be1a6..4e3b720 100644 --- a/lustre/include/uapi/linux/lustre/lustre_user.h +++ b/lustre/include/uapi/linux/lustre/lustre_user.h @@ -2172,6 +2172,7 @@ enum ioc_data_version_flags { #endif #define dot_lustre_name ".lustre" +#define dot_fscrypt_name ".fscrypt" /********* HSM **********/ diff --git a/lustre/llite/crypto.c b/lustre/llite/crypto.c index 3d7b63e..797fa3d 100644 --- a/lustre/llite/crypto.c +++ b/lustre/llite/crypto.c @@ -235,8 +235,13 @@ int ll_setup_filename(struct inode *dir, const struct qstr *iname, } rc = llcrypt_setup_filename(dir, &dname, lookup, fname); if (rc == -ENOENT && lookup && - !llcrypt_has_encryption_key(dir) && - unlikely(filename_is_volatile(iname->name, iname->len, NULL))) { + ((is_root_inode(dir) && iname->len == strlen(dot_fscrypt_name) && + strncmp(iname->name, dot_fscrypt_name, iname->len) == 0) || + (!llcrypt_has_encryption_key(dir) && + unlikely(filename_is_volatile(iname->name, iname->len, NULL))))) { + /* In case of subdir mount of an encrypted directory, we allow + * lookup of /.fscrypt directory. + */ /* For purpose of migration or mirroring without enc key, we * allow lookup of volatile file without enc context. */ diff --git a/lustre/llite/llite_lib.c b/lustre/llite/llite_lib.c index 88d193d..77aa3a7 100644 --- a/lustre/llite/llite_lib.c +++ b/lustre/llite/llite_lib.c @@ -3268,7 +3268,16 @@ struct md_op_data *ll_prep_md_op_data(struct md_op_data *op_data, return ERR_PTR(-ENOMEM); ll_i2gids(op_data->op_suppgids, i1, i2); - op_data->op_fid1 = *ll_inode2fid(i1); + /* If the client is using a subdir mount and looks at what it sees as + * /.fscrypt, interpret it as the .fscrypt dir at the root of the fs. + */ + if (unlikely(i1->i_sb && i1->i_sb->s_root && is_root_inode(i1) && + !fid_is_root(ll_inode2fid(i1)) && + name && namelen == strlen(dot_fscrypt_name) && + strncmp(name, dot_fscrypt_name, namelen) == 0)) + lu_root_fid(&op_data->op_fid1); + else + op_data->op_fid1 = *ll_inode2fid(i1); if (S_ISDIR(i1->i_mode)) { down_read_non_owner(&ll_i2info(i1)->lli_lsm_sem); diff --git a/lustre/llite/namei.c b/lustre/llite/namei.c index 7ca34c1..6681993 100644 --- a/lustre/llite/namei.c +++ b/lustre/llite/namei.c @@ -2147,6 +2147,15 @@ static int ll_rename(struct user_namespace *mnt_userns, if (IS_ERR(op_data)) RETURN(PTR_ERR(op_data)); + /* If the client is using a subdir mount and does a rename to what it + * sees as /.fscrypt, interpret it as the .fscrypt dir at fs root. + */ + if (unlikely(is_root_inode(tgt) && !fid_is_root(ll_inode2fid(tgt)) && + tgt_dchild->d_name.len == strlen(dot_fscrypt_name) && + strncmp(tgt_dchild->d_name.name, dot_fscrypt_name, + tgt_dchild->d_name.len) == 0)) + lu_root_fid(&op_data->op_fid2); + if (src_dchild->d_inode) op_data->op_fid3 = *ll_inode2fid(src_dchild->d_inode); diff --git a/lustre/tests/sanity-sec.sh b/lustre/tests/sanity-sec.sh index ee4fd1a..2e4c5b1 100755 --- a/lustre/tests/sanity-sec.sh +++ b/lustre/tests/sanity-sec.sh @@ -4234,6 +4234,8 @@ test_54() { local testfile2=$testdir/${tfile}withveryverylongnametoexercisecode local tmpfile=$TMP/${tfile}.tmp local resfile=$TMP/${tfile}.res + local fid1 + local fid2 $LCTL get_param mdc.*.import | grep -q client_encryption || skip "client encryption not supported" @@ -4309,7 +4311,75 @@ test_54() { $RUNAS fscrypt lock --verbose $testdir || error "fscrypt lock $testdir failed (3)" - rm -f $tmpfile $resfile + rm -rf $tmpfile $resfile $testdir ${testdir}2 $MOUNT/.fscrypt + + # remount client with subdirectory mount + umount_client $MOUNT || error "umount $MOUNT failed (1)" + export FILESET=/$tdir + mount_client $MOUNT ${MOUNT_OPTS} || error "remount failed (1)" + export FILESET="" + + # setup encryption from inside this subdir mount + # the .fscrypt directory is going to be created at the real fs root + fscrypt setup --verbose $MOUNT || + error "fscrypt setup $MOUNT failed (2)" + testdir=$MOUNT/vault + mkdir $testdir + chown -R $ID0:$ID0 $testdir + fid1=$(path2fid $MOUNT/.fscrypt) + echo "With FILESET $tdir, .fscrypt FID is $fid1" + + # encrypt 'vault' dir inside the subdir mount + echo -e 'mypass\nmypass' | su - $USER0 -c "fscrypt encrypt --verbose \ + --source=custom_passphrase --name=protector $testdir" || + error "fscrypt encrypt failed" + + echo abc > $tmpfile + chmod 666 $tmpfile + $RUNAS cp $tmpfile $testdir/encfile + + $RUNAS fscrypt lock --verbose $testdir || + error "fscrypt lock $testdir failed (4)" + + # remount client with encrypted dir as subdirectory mount + umount_client $MOUNT || error "umount $MOUNT failed (2)" + export FILESET=/$tdir/vault + mount_client $MOUNT ${MOUNT_OPTS} || error "remount failed (2)" + export FILESET="" + ls -laR $MOUNT + fid2=$(path2fid $MOUNT/.fscrypt) + echo "With FILESET $tdir/vault, .fscrypt FID is $fid2" + [ "$fid1" == "$fid2" ] || error "fid1 $fid1 != fid2 $fid2 (1)" + + # all content seen by this mount is encrypted, but .fscrypt is virtually + # presented, letting us call fscrypt lock/unlock + echo mypass | $RUNAS fscrypt unlock --verbose $MOUNT || + error "fscrypt unlock $MOUNT failed (3)" + + ls -laR $MOUNT + [ $(cat $MOUNT/encfile) == "abc" ] || error "cat encfile failed" + + # remount client without subdir mount + umount_client $MOUNT || error "umount $MOUNT failed (3)" + mount_client $MOUNT ${MOUNT_OPTS} || error "remount failed (3)" + ls -laR $MOUNT + fid2=$(path2fid $MOUNT/.fscrypt) + echo "Without FILESET, .fscrypt FID is $fid2" + [ "$fid1" == "$fid2" ] || error "fid1 $fid1 != fid2 $fid2 (2)" + + # because .fscrypt was actually created at the real root of the fs, + # we can call fscrypt lock/unlock on the encrypted dir + echo mypass | $RUNAS fscrypt unlock --verbose $DIR/$tdir/vault || + error "fscrypt unlock $$DIR/$tdir/vault failed (4)" + + ls -laR $MOUNT + echo c >> $DIR/$tdir/vault/encfile || error "write to encfile failed" + + rm -rf $DIR/$tdir/vault/* + $RUNAS fscrypt lock --verbose $DIR/$tdir/vault || + error "fscrypt lock $DIR/$tdir/vault failed (5)" + + rm -rf $tmpfile $MOUNT/.fscrypt } run_test 54 "Encryption policies with fscrypt" -- 1.8.3.1