Whamcloud - gitweb
LU-15787 sec: block enc unaware clients on enc files
authorSebastien Buisson <sbuisson@ddn.com>
Wed, 27 Apr 2022 15:33:57 +0000 (17:33 +0200)
committerAndreas Dilger <adilger@whamcloud.com>
Tue, 10 May 2022 06:27:08 +0000 (06:27 +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.

Lustre-change: https://review.whamcloud.com/47156
Lustre-commit: a31db2ec062ccc995527d37f0330edbca9d486a9

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-by: John L. Hammond <jhammond@whamcloud.com>
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
Reviewed-on: https://review.whamcloud.com/47227
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
lustre/mdt/mdt_internal.h
lustre/mdt/mdt_open.c
lustre/mdt/mdt_reint.c
lustre/tests/sanity-sec.sh

index a87649f..01bfc4c 100644 (file)
@@ -1433,6 +1433,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: rc = %d\n",
+                      mdt_obd_name(info->mti_mdt),
+                      PFID(lu_object_fid(&obj->mot_obj)),
+                      obd_export_nid2str(exp), 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 ce3bd66..a97b0c6 100644 (file)
@@ -765,6 +765,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;
@@ -790,7 +794,7 @@ static int mdt_open_by_fid(struct mdt_thread_info *info, struct ldlm_reply *rep)
                        rc = -ENOENT;
                }
        }
-
+out:
        mdt_object_put(info->mti_env, o);
        RETURN(rc);
 }
@@ -1115,6 +1119,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);
@@ -1193,6 +1204,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",
@@ -1400,10 +1415,12 @@ int mdt_reint_open(struct mdt_thread_info *info, struct mdt_lock_handle *lhc)
 
        /* get and check version of parent */
        result = mdt_version_get_check(info, parent, 0);
-       if (result) {
-               mdt_object_put(info->mti_env, parent);
-               GOTO(out, result);
-       }
+       if (result)
+               GOTO(out_parent, result);
+
+       result = mdt_check_enc(info, parent);
+       if (result)
+               GOTO(out_parent, result);
 
        OBD_RACE(OBD_FAIL_MDS_REINT_OPEN);
 again_pw:
@@ -1417,9 +1434,9 @@ again_pw:
                mdt_lock_pdo_init(lh, lock_mode, &rr->rr_name);
                result = mdt_object_lock(info, parent, lh, MDS_INODELOCK_UPDATE);
                if (result != 0) {
-                       mdt_object_put(info->mti_env, parent);
-                       GOTO(out, result);
-               }
+                       lh = NULL;
+                       GOTO(out_parent, result);
+                }
 
                result = mdo_lookup(info->mti_env, mdt_object_child(parent),
                                    &rr->rr_name, child_fid, &info->mti_spec);
index ac9c73f..a3a0f2f 100644 (file)
@@ -567,6 +567,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.
@@ -1398,6 +1402,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);
@@ -2662,6 +2674,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)) {
@@ -2673,6 +2689,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 fb8f090..f894dfb 100755 (executable)
@@ -4263,6 +4263,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
@@ -4337,6 +4338,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)"