From f1e322a33f58d96c5d6eb12e5eb2768517d3f5f8 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. Lustre-change: https://review.whamcloud.com/46167 Lustre-commit: c12378fba7f0aa2f25de2b295ee110e2932f2a39 Signed-off-by: Sebastien Buisson Change-Id: I2a0ee360f724da1df49b2be0df986d52e06f45fd Reviewed-by: Patrick Farrell Reviewed-by: Andreas Dilger Reviewed-on: https://review.whamcloud.com/46888 Tested-by: jenkins Tested-by: Maloo --- lustre/include/uapi/linux/lustre/lustre_user.h | 1 + lustre/llite/crypto.c | 10 +++- lustre/llite/llite_lib.c | 12 ++++- lustre/llite/namei.c | 10 ++++ lustre/tests/sanity-sec.sh | 72 +++++++++++++++++++++++++- 5 files changed, 101 insertions(+), 4 deletions(-) diff --git a/lustre/include/uapi/linux/lustre/lustre_user.h b/lustre/include/uapi/linux/lustre/lustre_user.h index 1f1e4f2..7d3c150 100644 --- a/lustre/include/uapi/linux/lustre/lustre_user.h +++ b/lustre/include/uapi/linux/lustre/lustre_user.h @@ -2202,6 +2202,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 b0f8e74..1fa0831 100644 --- a/lustre/llite/crypto.c +++ b/lustre/llite/crypto.c @@ -234,8 +234,14 @@ 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))) { + ((dir == dir->i_sb->s_root->d_inode && + 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 c525ab1..37654a0 100644 --- a/lustre/llite/llite_lib.c +++ b/lustre/llite/llite_lib.c @@ -3156,7 +3156,17 @@ 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 && + i1 == i1->i_sb->s_root->d_inode && + !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 ec66bd7..428bee2 100644 --- a/lustre/llite/namei.c +++ b/lustre/llite/namei.c @@ -2132,6 +2132,16 @@ 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(tgt == tgt->i_sb->s_root->d_inode && + !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 10b0c93..e9c1d9a 100755 --- a/lustre/tests/sanity-sec.sh +++ b/lustre/tests/sanity-sec.sh @@ -4216,6 +4216,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" @@ -4291,7 +4293,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