From 75414af6bf310244d38284958ecf037d61936726 Mon Sep 17 00:00:00 2001 From: Sebastien Buisson Date: Tue, 5 Oct 2021 16:51:52 +0200 Subject: [PATCH] LU-13717 sec: fix handling of encrypted file with long name The ciphertext representation of the name of an encrypted file or directory can be up to 256 bytes of binary data, if the cleartext name is up to NAME_MAX. But then this ciphertext is encoded via critical_encode() before being sent to servers. Once encoded, the length can exceed NAME_MAX because of the escaped critical characters. So make sure ll_prep_md_op_data() accepts those too long encoded names if it is called for lookup or create of an encrypted file or directory. In the other cases, the 'name' taken as input is the plain text version, so it must conform to the NAME_MAX limit. When carrying out operations on an encrypted file with long name, we manipulate a digested form whose hash needs to be matched against the content of the LinkEA. The name found in the LinkEA is not NUL terminated, so this aspect must be taken care of. Fixes: 4d38566a00 ("LU-13717 sec: filename encryption") Fixes: ed4a625d88 ("LU-13717 sec: filename encryption - digest support") Signed-off-by: Sebastien Buisson Change-Id: I4b0e51eee5e549ab56292fe0fec3c1be1b487fc7 Reviewed-on: https://review.whamcloud.com/45163 Reviewed-by: Andreas Dilger Tested-by: jenkins Tested-by: Maloo Reviewed-by: Patrick Farrell Reviewed-by: Oleg Drokin --- lustre/llite/llite_lib.c | 4 +++- lustre/mdd/mdd_dir.c | 15 ++++++++++++++- lustre/tests/sanity-sec.sh | 17 +++++++++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/lustre/llite/llite_lib.c b/lustre/llite/llite_lib.c index d5c3c94..e818e1e 100644 --- a/lustre/llite/llite_lib.c +++ b/lustre/llite/llite_lib.c @@ -3221,7 +3221,9 @@ struct md_op_data *ll_prep_md_op_data(struct md_op_data *op_data, if (namelen != 0) return ERR_PTR(-EINVAL); } else { - if (namelen > ll_i2sbi(i1)->ll_namelen) + if ((!IS_ENCRYPTED(i1) || + (opc != LUSTRE_OPC_LOOKUP && opc != LUSTRE_OPC_CREATE)) && + namelen > ll_i2sbi(i1)->ll_namelen) return ERR_PTR(-ENAMETOOLONG); /* "/" is not valid name, but it's allowed */ diff --git a/lustre/mdd/mdd_dir.c b/lustre/mdd/mdd_dir.c index fcad947..03b308d 100644 --- a/lustre/mdd/mdd_dir.c +++ b/lustre/mdd/mdd_dir.c @@ -1717,7 +1717,7 @@ static int mdd_unlink(const struct lu_env *env, struct md_object *pobj, struct md_object *cobj, const struct lu_name *lname, struct md_attr *ma, int no_name) { - const char *name = lname->ln_name; + char *name = (char *)lname->ln_name; struct lu_attr *pattr = MDD_ENV_VAR(env, pattr); struct lu_attr *cattr = MDD_ENV_VAR(env, cattr); struct lu_attr *la = &mdd_env_info(env)->mdi_la_for_fix; @@ -1776,6 +1776,16 @@ static int mdd_unlink(const struct lu_env *env, struct md_object *pobj, if (likely(mdd_cobj != NULL)) mdd_write_lock(env, mdd_cobj, DT_TGT_CHILD); + if (lname->ln_name[lname->ln_namelen] != '\0') { + /* lname->ln_name is not necessarily NUL terminated */ + name = kmalloc(lname->ln_namelen + 1, GFP_NOFS); + if (!name) + GOTO(cleanup, rc = -ENOMEM); + + memcpy(name, lname->ln_name, lname->ln_namelen); + name[lname->ln_namelen] = '\0'; + } + if (likely(no_name == 0) && !OBD_FAIL_CHECK(OBD_FAIL_LFSCK_DANGLING2)) { rc = __mdd_index_delete(env, mdd_pobj, name, is_dir, handle); if (rc) @@ -1850,6 +1860,9 @@ static int mdd_unlink(const struct lu_env *env, struct md_object *pobj, EXIT; cleanup: + if (name != lname->ln_name) + kfree(name); + if (likely(mdd_cobj != NULL)) mdd_write_unlock(env, mdd_cobj); diff --git a/lustre/tests/sanity-sec.sh b/lustre/tests/sanity-sec.sh index 0f38097..579a30b 100755 --- a/lustre/tests/sanity-sec.sh +++ b/lustre/tests/sanity-sec.sh @@ -3374,6 +3374,11 @@ test_46() { local testfile=$testdir/myfile local testdir2=$DIR/$tdir/mydirwithaveryverylongnametotestcodebehaviour0 local testfile2=$testdir/myfilewithaveryverylongnametotestcodebehaviour0 + # testdir3, testfile3, testhl3 and testsl3 names are 255 bytes long + local testdir3=$testdir2/dir_abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz012345678 + local testfile3=$testdir2/file_abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz01234567 + local testhl3=$testdir2/hl_abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789 + local testsl3=$testdir2/sl_abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789 local lsfile=$TMP/lsfile local scrambleddir local scrambledfile @@ -3396,6 +3401,14 @@ test_46() { else mkdir $testdir2 fi + if [ "$mds1_FSTYPE" = ldiskfs ]; then + # For now, restrict this part of the test to ldiskfs backend, + # as osd-zfs does not support 255 byte-long encrypted names. + mkdir $testdir3 || error "cannot mkdir $testdir3" + touch $testfile3 || error "cannot touch $testfile3" + ln $testfile3 $testhl3 || error "cannot ln $testhl3" + ln -s $testfile3 $testsl3 || error "cannot ln $testsl3" + fi sync ; echo 3 > /proc/sys/vm/drop_caches # remove fscrypt key from keyring @@ -3406,6 +3419,10 @@ test_46() { # this is $testdir2 scrambleddir=$(find $DIR/$tdir/ -maxdepth 1 -mindepth 1 -type d| grep _) stat $scrambleddir || error "stat $scrambleddir failed" + if [ "$mds1_FSTYPE" = ldiskfs ]; then + stat $scrambleddir/* || error "cannot stat in $scrambleddir" + rm -rf $scrambleddir/* || error "cannot clean in $scrambleddir" + fi rmdir $scrambleddir || error "rmdir $scrambleddir failed" scrambleddir=$(find $DIR/$tdir/ -maxdepth 1 -mindepth 1 -type d) -- 1.8.3.1