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>
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);
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;
rc = -ENOENT;
}
}
-
+out:
mdt_object_put(info->mti_env, o);
RETURN(rc);
}
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);
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",
/* 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:
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);
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.
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))
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);
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)) {
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
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
$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)"