Whamcloud - gitweb
LU-15787 sec: block enc unaware clients on enc files 56/47156/5
authorSebastien Buisson <sbuisson@ddn.com>
Wed, 27 Apr 2022 15:33:57 +0000 (17:33 +0200)
committerOleg Drokin <green@whamcloud.com>
Thu, 5 May 2022 18:47:27 +0000 (18:47 +0000)
Prevent encryption unaware clients from manipulating encrypted files
and directories. Those can be old clients, or clients built without
encryption support (intentionally or because they run on an old
kernel).
In the mdt layer, check that clients have the OBD_CONNECT2_ENCRYPT
connection flag, and if not, block access if they try to manipulate
a file or directory that has the LUSTRE_ENCRYPT_FL flag.
The forbidden operations from encryption unaware clients are:
- open
- create
- link
- rename
- migrate
Improve sanity-sec test_54 to test this use case.

Test-Parameters: testlist=sanity-sec mdscount=2 mdtcount=4 osscount=1 ostcount=8 clientcount=2 serverdistro=el7.9
Signed-off-by: Sebastien Buisson <sbuisson@ddn.com>
Change-Id: Ief0639e49c0a8e1a1a0cb19cb13c006edfdff6c4
Reviewed-on: https://review.whamcloud.com/47156
Tested-by: jenkins <devops@whamcloud.com>
Reviewed-by: John L. Hammond <jhammond@whamcloud.com>
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
lustre/mdt/mdt_internal.h
lustre/mdt/mdt_open.c
lustre/mdt/mdt_reint.c
lustre/tests/sanity-sec.sh

index 0dbde34..59d9257 100644 (file)
@@ -1428,6 +1428,42 @@ static inline bool mdt_is_rootadmin(struct mdt_thread_info *info)
        return is_admin;
 }
 
+/* We forbid operations from encryption-unaware clients if they try to
+ * manipulate encrypted files/directories.
+ */
+static inline int mdt_check_enc(struct mdt_thread_info *info,
+                               struct mdt_object *obj)
+{
+       struct obd_export *exp;
+       struct md_attr ma = { 0 };
+       int rc = 0;
+
+       if (!mdt_info_req(info))
+               /* no req, so cannot tell about client connection flags:
+                * allow access
+                */
+               return 0;
+       exp = mdt_info_req(info)->rq_export;
+       if (exp_connect_encrypt(exp))
+               /* client is encryption aware: fine */
+               return 0;
+
+       ma.ma_need = MA_INODE;
+       mdt_attr_get_complex(info, obj, &ma);
+       if (ma.ma_attr.la_valid & LA_FLAGS &&
+           ma.ma_attr.la_flags & LUSTRE_ENCRYPT_FL)
+               rc = -ENOKEY;
+
+       if (rc)
+               CDEBUG(D_SEC, "%s: operation on encrypted "DFID
+                      " from encryption-unaware client %s: %d\n",
+                      mdt_obd_name(info->mti_mdt),
+                      PFID(lu_object_fid(&obj->mot_obj)),
+                      libcfs_nidstr(&exp->exp_connection->c_peer.nid), rc);
+
+       return rc;
+}
+
 int mdt_reint_migrate(struct mdt_thread_info *info,
                      struct mdt_lock_handle *unused);
 int mdt_dir_layout_update(struct mdt_thread_info *info);
index 2d72e8e..165caaf 100644 (file)
@@ -718,6 +718,10 @@ static int mdt_open_by_fid(struct mdt_thread_info *info, struct ldlm_reply *rep,
        if (IS_ERR(o))
                RETURN(rc = PTR_ERR(o));
 
+       rc = mdt_check_enc(info, o);
+       if (rc)
+               GOTO(out, rc);
+
        if (unlikely(mdt_object_remote(o))) {
                /* the child object was created on remote server */
                struct mdt_body *repbody;
@@ -1078,6 +1082,13 @@ static int mdt_open_by_fid_lock(struct mdt_thread_info *info,
                GOTO(out, rc = -ENOENT);
        }
 
+       /* do not check enc for directory: always allow open */
+       if (!S_ISDIR(lu_object_attr(&o->mot_obj))) {
+               rc = mdt_check_enc(info, o);
+               if (rc)
+                       GOTO(out, rc);
+       }
+
        mdt_set_disposition(info, rep, (DISP_IT_EXECD | DISP_LOOKUP_EXECD));
 
        mdt_prep_ma_buf_from_rep(info, o, ma);
@@ -1157,6 +1168,10 @@ static int mdt_cross_open(struct mdt_thread_info *info,
        if (IS_ERR(o))
                RETURN(rc = PTR_ERR(o));
 
+       rc = mdt_check_enc(info, o);
+       if (rc)
+               GOTO(out, rc);
+
        if (mdt_object_remote(o)) {
                /* Something is wrong here, the object is on another MDS! */
                CERROR("%s: "DFID" isn't on this server!: rc = %d\n",
@@ -1391,6 +1406,10 @@ int mdt_reint_open(struct mdt_thread_info *info, struct mdt_lock_handle *lhc)
                GOTO(out, result);
        }
 
+       result = mdt_check_enc(info, parent);
+       if (result)
+               GOTO(out_parent, result);
+
        fid_zero(child_fid);
        result = -ENOENT;
        lock_mode = mdt_open_lock_mode(info, parent, &rr->rr_name, open_flags);
index 3d40151..ab3cdd4 100644 (file)
@@ -566,6 +566,10 @@ static int mdt_create(struct mdt_thread_info *info)
        if (!mdt_object_exists(parent))
                GOTO(put_parent, rc = -ENOENT);
 
+       rc = mdt_check_enc(info, parent);
+       if (rc)
+               GOTO(put_parent, rc);
+
        /*
         * LU-10235: check if name exists locklessly first to avoid massive
         * lock recalls on existing directories.
@@ -1395,6 +1399,10 @@ static int mdt_reint_link(struct mdt_thread_info *info,
        if (rc)
                GOTO(put_parent, rc);
 
+       rc = mdt_check_enc(info, mp);
+       if (rc)
+               GOTO(put_parent, rc);
+
        /* step 2: find source */
        ms = mdt_object_find(info->mti_env, info->mti_mdt, rr->rr_fid1);
        if (IS_ERR(ms))
@@ -2294,6 +2302,10 @@ int mdt_reint_migrate(struct mdt_thread_info *info,
        if (!S_ISDIR(lu_object_attr(&pobj->mot_obj)))
                GOTO(put_parent, rc = -ENOTDIR);
 
+       rc = mdt_check_enc(info, pobj);
+       if (rc)
+               GOTO(put_parent, rc);
+
        rc = mdt_stripe_get(info, pobj, ma, XATTR_NAME_LMV);
        if (rc)
                GOTO(put_parent, rc);
@@ -2699,6 +2711,10 @@ static int mdt_reint_rename(struct mdt_thread_info *info,
        if (IS_ERR(msrcdir))
                RETURN(PTR_ERR(msrcdir));
 
+       rc = mdt_check_enc(info, msrcdir);
+       if (rc)
+               GOTO(out_put_srcdir, rc);
+
        OBD_FAIL_TIMEOUT(OBD_FAIL_MDS_RENAME3, 5);
 
        if (lu_fid_eq(rr->rr_fid1, rr->rr_fid2)) {
@@ -2710,6 +2726,10 @@ static int mdt_reint_rename(struct mdt_thread_info *info,
                        GOTO(out_put_srcdir, rc = PTR_ERR(mtgtdir));
        }
 
+       rc = mdt_check_enc(info, mtgtdir);
+       if (rc)
+               GOTO(out_put_tgtdir, rc);
+
        /*
         * Note: do not enqueue rename lock for replay request, because
         * if other MDT holds rename lock, but being blocked to wait for
index be5142c..a0c608d 100755 (executable)
@@ -4261,6 +4261,7 @@ run_test 53 "Mixed PAGE_SIZE clients"
 
 test_54() {
        local testdir=$DIR/$tdir/$ID0
+       local testdir2=$DIR2/$tdir/$ID0
        local testfile=$testdir/$tfile
        local testfile2=$testdir/${tfile}withveryverylongnametoexercisecode
        local tmpfile=$TMP/${tfile}.tmp
@@ -4335,6 +4336,33 @@ test_54() {
        $RUNAS hexdump -C ${scrambledfiles[1]} &&
                error "reading ${scrambledfiles[1]} should fail without key"
 
+       # server local client incompatible with SSK keys installed
+       if [ "$SHARED_KEY" != true ]; then
+               mount_mds_client
+               do_facet $SINGLEMDS touch $DIR2/$tdir/newfile
+               mdsscrambledfile=$(do_facet $SINGLEMDS find $testdir2/ \
+                                       -maxdepth 1 -type f | head -n1)
+               [ -n "$mdsscrambledfile" ] || error "could not find file"
+               do_facet $SINGLEMDS cat "$mdsscrambledfile" &&
+                       error "reading $mdsscrambledfile should fail on MDS"
+               do_facet $SINGLEMDS "echo aaa >> \"$mdsscrambledfile\"" &&
+                       error "writing $mdsscrambledfile should fail on MDS"
+               do_facet $SINGLEMDS $MULTIOP $testdir2/fileA m &&
+                       error "creating $testdir2/fileA should fail on MDS"
+               do_facet $SINGLEMDS mkdir $testdir2/dirA &&
+                       error "mkdir $testdir2/dirA should fail on MDS"
+               do_facet $SINGLEMDS ln -s $DIR2/$tdir/newfile $testdir2/sl1 &&
+                       error "ln -s $testdir2/sl1 should fail on MDS"
+               do_facet $SINGLEMDS ln $DIR2/$tdir/newfile $testdir2/hl1 &&
+                       error "ln $testdir2/hl1 should fail on MDS"
+               do_facet $SINGLEMDS mv "$mdsscrambledfile" $testdir2/fB &&
+                       error "mv $mdsscrambledfile should fail on MDS"
+               do_facet $SINGLEMDS mrename "$mdsscrambledfile" $testdir2/fB &&
+                       error "mrename $mdsscrambledfile should fail on MDS"
+               do_facet $SINGLEMDS rm -f $DIR2/$tdir/newfile
+               umount_mds_client
+       fi
+
        echo mypass | $RUNAS fscrypt unlock --verbose $testdir ||
                error "fscrypt unlock $testdir failed (2)"