From bfca8338e5f2ae1b7c16cc1d0c2376523d68685e Mon Sep 17 00:00:00 2001 From: Sebastien Buisson Date: Fri, 31 Jul 2015 16:02:06 +0200 Subject: [PATCH] LU-5560 tests: add sanity-selinux.sh New test sanity-selinux.sh aims at exercing SELinux support on the client side, as implemented according to LU-5560. This patch also adds new fail_locs in CLIO. Test-Parameters: trivial testlist=sanity-selinux Signed-off-by: Sebastien Buisson Signed-off-by: Sebastien Buisson Change-Id: I222a82329142705345837e1235dd01e1d85e7a28 Signed-off-by: Saurabh Tandan Reviewed-on: http://review.whamcloud.com/15818 Tested-by: Jenkins Tested-by: Maloo Reviewed-by: Andrew Perepechko Reviewed-by: Li Xi Reviewed-by: Oleg Drokin --- lustre/include/obd_support.h | 4 + lustre/llite/dir.c | 2 + lustre/llite/namei.c | 12 +- lustre/tests/Makefile.am | 2 +- lustre/tests/sanity-selinux.sh | 405 +++++++++++++++++++++++++++++++++++++++++ lustre/tests/test-framework.sh | 14 +- 6 files changed, 430 insertions(+), 9 deletions(-) create mode 100644 lustre/tests/sanity-selinux.sh diff --git a/lustre/include/obd_support.h b/lustre/include/obd_support.h index 3016487..70d15ce 100644 --- a/lustre/include/obd_support.h +++ b/lustre/include/obd_support.h @@ -520,6 +520,10 @@ extern char obd_jobid_var[]; #define OBD_FAIL_LLITE_LOST_LAYOUT 0x1407 #define OBD_FAIL_LLITE_NO_CHECK_DEAD 0x1408 #define OBD_FAIL_GETATTR_DELAY 0x1409 +#define OBD_FAIL_LLITE_CREATE_FILE_PAUSE 0x1409 +#define OBD_FAIL_LLITE_NEWNODE_PAUSE 0x140a +#define OBD_FAIL_LLITE_SETDIRSTRIPE_PAUSE 0x140b + #define OBD_FAIL_FID_INDIR 0x1501 #define OBD_FAIL_FID_INLMA 0x1502 diff --git a/lustre/llite/dir.c b/lustre/llite/dir.c index a02ead0..3536107 100644 --- a/lustre/llite/dir.c +++ b/lustre/llite/dir.c @@ -494,6 +494,8 @@ static int ll_dir_setdirstripe(struct inode *parent, struct lmv_user_md *lump, if (err) GOTO(err_exit, err); + CFS_FAIL_TIMEOUT(OBD_FAIL_LLITE_SETDIRSTRIPE_PAUSE, cfs_fail_val); + err = ll_prep_inode(&inode, request, parent->i_sb, NULL); if (err) GOTO(err_exit, err); diff --git a/lustre/llite/namei.c b/lustre/llite/namei.c index 3e6c706..1c17b50 100644 --- a/lustre/llite/namei.c +++ b/lustre/llite/namei.c @@ -716,6 +716,8 @@ static int ll_atomic_open(struct inode *dir, struct dentry *dentry, else if (de != NULL) dentry = de; + CFS_FAIL_TIMEOUT(OBD_FAIL_LLITE_CREATE_FILE_PAUSE, cfs_fail_val); + if (!rc) { if (it_disposition(it, DISP_OPEN_CREATE)) { /* Dentry instantiated in ll_create_it. */ @@ -1005,7 +1007,9 @@ again: goto again; } - ll_update_times(request, dir); + ll_update_times(request, dir); + + CFS_FAIL_TIMEOUT(OBD_FAIL_LLITE_NEWNODE_PAUSE, cfs_fail_val); err = ll_prep_inode(&inode, request, dchild->d_sb, NULL); if (err) @@ -1072,6 +1076,8 @@ static int ll_create_nd(struct inode *dir, struct dentry *dentry, { int rc; + CFS_FAIL_TIMEOUT(OBD_FAIL_LLITE_CREATE_FILE_PAUSE, cfs_fail_val); + CDEBUG(D_VFSTRACE, "VFS Op:name=%.*s, dir="DFID"(%p), " "flags=%u, excl=%d\n", dentry->d_name.len, dentry->d_name.name, PFID(ll_inode2fid(dir)), @@ -1092,7 +1098,9 @@ static int ll_create_nd(struct inode *dir, struct dentry *dentry, { struct ll_dentry_data *lld = ll_d2d(dentry); struct lookup_intent *it = NULL; - int rc; + int rc; + + CFS_FAIL_TIMEOUT(OBD_FAIL_LLITE_CREATE_FILE_PAUSE, cfs_fail_val); if (lld != NULL) it = lld->lld_it; diff --git a/lustre/tests/Makefile.am b/lustre/tests/Makefile.am index 31419a09..469c14a 100644 --- a/lustre/tests/Makefile.am +++ b/lustre/tests/Makefile.am @@ -23,7 +23,7 @@ noinst_SCRIPTS += mdsrate-create-large.sh mdsrate-lookup-1dir.sh noinst_SCRIPTS += mdsrate-lookup-10dirs.sh sanity-benchmark.sh noinst_SCRIPTS += mdsrate-stat-small.sh mdsrate-stat-large.sh noinst_SCRIPTS += lockorder.sh socketclient socketserver runmultiop_bg_pause -noinst_SCRIPTS += sanity-sec.sh sanity-gss.sh +noinst_SCRIPTS += sanity-sec.sh sanity-gss.sh sanity-selinux.sh noinst_SCRIPTS += sanity-krb5.sh krb5_login.sh setup_kerberos.sh noinst_SCRIPTS += recovery-mds-scale.sh run_dd.sh run_tar.sh run_iozone.sh noinst_SCRIPTS += run_dbench.sh run_IOR.sh recovery-double-scale.sh diff --git a/lustre/tests/sanity-selinux.sh b/lustre/tests/sanity-selinux.sh new file mode 100644 index 0000000..d86c11c --- /dev/null +++ b/lustre/tests/sanity-selinux.sh @@ -0,0 +1,405 @@ +#!/bin/bash +# +# Run select tests by setting ONLY, or as arguments to the script. +# Skip specific tests by setting EXCEPT. +# +# e.g. ONLY="22 23" or ONLY="`seq 32 39`" or EXCEPT="31" +set -e + +ONLY=${ONLY:-"$*"} +# bug number for skipped test: +ALWAYS_EXCEPT=${ALWAYS_EXCEPT:-"$SANITY_SELINUX_EXCEPT"} +# UPDATE THE COMMENT ABOVE WITH BUG NUMBERS WHEN CHANGING ALWAYS_EXCEPT! + +SRCDIR=$(dirname $0) +SAVE_PWD=$PWD + +LUSTRE=${LUSTRE:-$(dirname $0)/..} +. $LUSTRE/tests/test-framework.sh +init_test_env $@ +. ${CONFIG:=$LUSTRE/tests/cfg/$NAME.sh} +init_logging + +require_dsh_mds || exit 0 + +[ "$SLOW" = "no" ] && EXCEPT_SLOW="xxx" + +# $RUNAS_ID may get set incorrectly somewhere else +[ $UID -eq 0 -a $RUNAS_ID -eq 0 ] && + error "RUNAS_ID set to 0, but UID is also 0!" + +# +# global variables of this sanity +# + +check_selinux() { + echo -n "Checking SELinux environment... " + local selinux_status=$(getenforce) + if [ "$selinux_status" != "Enforcing" ]; then + skip "SELinux is currently in $selinux_status mode," \ + "but it must be enforced to run sanity-selinux" && exit 0 + fi + local selinux_policy=$(sestatus | + awk -F':' '$1 == "Loaded policy name" {print $2}' | + xargs) + if [ -z "$selinux_policy" ]; then + selinux_policy=$(sestatus | + awk -F':' '$1 == "Policy from config file" + {print $2}' | + xargs) + fi + [ "$selinux_policy" == "targeted" ] || + error "Accepting only targeted policy" + echo "$selinux_status, $selinux_policy" +} + +check_selinux + +# we want double mount +MOUNT_2=${MOUNT_2:-"yes"} +check_and_setup_lustre + +rm -rf $DIR/[df][0-9]* + +check_runas_id $RUNAS_ID $RUNAS_ID $RUNAS + +build_test_filter + +umask 077 + +check_selinux_xattr() { + local mds=$1 + local mds_path=$2 + local mds_dev=$(facet_device $mds) + local mntpt="/tmp/mdt_" + local opts + + do_facet $mds mkdir -p $mntpt || error "mkdir $mntpt failed" + mount_fstype $mds $mntpt || error "mount $mds failed" + + local xattrval=$(do_facet $mds getfattr -n security.selinux \ + ${mntpt}/ROOT/$mds_path | + awk -F"=" '$1=="security.selinux" {print $2}') + + unmount_fstype $mds $mntpt || error "umount $mds failed" + do_facet $mds rmdir $mntpt || error "rmdir $mntpt failed" + + echo $xattrval +} + + +test_1() { + local devname=$(mdsdevname 1) + local filename=${DIR}/${tdir}/df1 + local mds_path=${filename#$MOUNT} + + mds_path=${mds_path#/} + + $LFS setdirstripe -i0 -c1 ${DIR}/$tdir || error "create dir $tdir failed" + touch $filename || error "cannot touch $filename" + + local xattrval=$(check_selinux_xattr "mds1" $mds_path) + + [ -n "$xattrval" -a "$xattrval" != '""' ] || + error "security.selinux xattr is not set" +} +run_test 1 "create file and check security.selinux xattr is set on MDT" + +test_2a() { + local devname=$(mdsdevname 1) + local dirname=${DIR}/${tdir}/dir2a + local mds_path=${dirname#$MOUNT} + + mds_path=${mds_path#/} + + $LFS setdirstripe -i0 -c1 ${DIR}/$tdir || error "create dir failed" + mkdir $dirname || error "cannot mkdir $dirname" + + local xattrval=$(check_selinux_xattr "mds1" $mds_path) + + [ -n "$xattrval" -a "$xattrval" != '""' ] || + error "security.selinux xattr is not set" +} +run_test 2a "create dir (mkdir) and check security.selinux xattr is set on MDT" + +test_2b() { + local devname=$(mdsdevname 1) + local dirname1=${DIR}/$tdir/dir2b1 + local dirname2=${DIR}/$tdir/dir2b2 + local mds_path=${dirname1#$MOUNT} + + mds_path=${mds_path#/} + + $LFS setdirstripe -i0 -c1 ${DIR}/$tdir || error "create dir failed" + $LFS mkdir -c0 $dirname1 || error "cannot 'lfs mkdir' $dirname1" + + local xattrval=$(check_selinux_xattr "mds1" $mds_path) + + mds_path=${dirname2#$MOUNT} + mds_path=${mds_path#/} + + [ -n "$xattrval" -a "$xattrval" != '""' ] || + error "security.selinux xattr is not set" + + $LFS setdirstripe -i0 $dirname2 || + error "cannot 'lfs setdirstripe' $dirname2" + + xattrval=$(check_selinux_xattr "mds1" $mds_path) + + [ -n "$xattrval" -a "$xattrval" != '""' ] || + error "security.selinux xattr is not set" +} +run_test 2b "create dir with lfs and check security.selinux xattr is set on MDT" + +test_3() { + local filename=$DIR/df3 + + # get current mapping of runasid, and save it + local uname=$(getent passwd $RUNAS_ID | cut -d: -f1) + local sename=$(semanage login -l | + awk -v uname=$uname '$1==uname {print $2}') + local serange=$(semanage login -l | + awk -v uname=$uname '$1==uname {print $3}') + + # change mapping of runasid to unconfined_u + semanage login -a -s unconfined_u $uname || + error "unable to map $uname to unconfined_u" + + # "access" Lustre + echo "${uname} mapped as unconfined_u: touch $filename" + $PDSH ${uname}@localhost "touch $filename" || + error "can't touch $filename" + echo "${uname} mapped as unconfined_u: rm -f $filename" + $PDSH ${uname}@localhost "rm -f $filename" || + error "can't remove $filename" + + # restore original mapping of runasid + if [ -n "$sename" ]; then + if [ -n "$serange" ]; then + semanage login -a -s $sename -r $serange $uname || + error "unable to restore mapping for $uname" + else + semanage login -a -s $sename $uname || + error "unable to restore mapping for $uname" + fi + else + semanage login -d $uname + fi + + return 0 +} +run_test 3 "access with unconfined user" + +test_4() { + local filename=$DIR/df4 + + # get current mapping of runasid, and save it + local uname=$(getent passwd $RUNAS_ID | cut -d: -f1) + local sename=$(semanage login -l | + awk -v uname=$uname '$1==uname {print $2}') + local serange=$(semanage login -l | + awk -v uname=$uname '$1==uname {print $3}') + + # change mapping of runasid to guest_u + semanage login -a -s guest_u $uname || + error "unable to map $uname to guest_u" + + # "access" Lustre + echo "${uname} mapped as guest_u: touch $filename" + $PDSH ${uname}@localhost "touch $filename" && + error "touch $filename should have failed" + + # change mapping of runasid to user_u + semanage login -a -s user_u $uname || + error "unable to map $uname to user_u" + + # "access" Lustre + echo "${uname} mapped as user_u: touch $filename" + $PDSH ${uname}@localhost "touch $filename" || + error "can't touch $filename" + echo "${uname} mapped as user_u: rm -f $filename" + $PDSH ${uname}@localhost "rm -f $filename" || + error "can't remove $filename" + + # restore original mapping of runasid + if [ -n "$sename" ]; then + if [ -n "$serange" ]; then + semanage login -a -s $sename -r $serange $uname || + error "unable to restore mapping for $uname" + else + semanage login -a -s $sename $uname || + error "unable to restore mapping for $uname" + fi + else + semanage login -d $uname + fi + + return 0 +} +run_test 4 "access with specific SELinux user" + +test_5() { + local filename=$DIR/df5 + local newsecctx="nfs_t" + + # create file + touch $filename || error "cannot touch $filename" + + # change sec context + chcon -t $newsecctx $filename + ls -lZ $filename + + # purge client's cache + sync ; echo 3 > /proc/sys/vm/drop_caches + + # get sec context + ls -lZ $filename + local secctxseen=$(ls -lZ $filename | awk '{print $4}' | cut -d: -f3) + + [ "$newsecctx" == "$secctxseen" ] || + error "sec context seen from 1st mount point is not correct" + + return 0 +} +run_test 5 "security context retrieval from MDT xattr" + +test_10() { + local filename1=$DIR/df10 + local filename2=$DIR2/df10 + local newsecctx="nfs_t" + + # create file from 1st mount point + touch $filename1 || error "cannot touch $filename1" + ls -lZ $filename1 + + # change sec context from 2nd mount point + chcon -t $newsecctx $filename2 + ls -lZ $filename2 + + # get sec context from 1st mount point + ls -lZ $filename1 + local secctxseen=$(ls -lZ $filename1 | awk '{print $4}' | cut -d: -f3) + + [ "$newsecctx" == "$secctxseen" ] || + error "sec context seen from 1st mount point is not correct" + + return 0 +} +run_test 10 "[consistency] concurrent security context change" + +test_20a() { + local uname=$(getent passwd $RUNAS_ID | cut -d: -f1) + local filename1=$DIR/df20a + local filename2=$DIR2/df20a + local req_delay=20 + + # sleep some time in ll_create_nd() + #define OBD_FAIL_LLITE_CREATE_FILE_PAUSE 0x1409 + do_facet client "$LCTL set_param fail_val=$req_delay fail_loc=0x1409" + + # create file on first mount point + $PDSH ${uname}@localhost "touch $filename1" & + local touchpid=$! + sleep 5 + + if [[ -z "$(ps h -o comm -p $touchpid)" ]]; then + error "touch failed to sleep, pid=$touchpid" + fi + + # get sec info on second mount point + if [ -e "$filename2" ]; then + secinfo2=$(ls -lZ $filename2 | awk '{print $4}') + fi + + # get sec info on first mount point + wait $touchpid + secinfo1=$(ls -lZ $filename1 | awk '{print $4}') + + # compare sec contexts + [ -z "$secinfo2" -o "$secinfo1" == "$secinfo2" ] || + error "sec context seen from 2nd mount point is not correct" + + return 0 +} +run_test 20a "[atomicity] concurrent access from another client (file)" + +test_20b() { + local uname=$(getent passwd $RUNAS_ID | cut -d: -f1) + local dirname1=$DIR/dd20b + local dirname2=$DIR2/dd20b + local req_delay=20 + + # sleep some time in ll_create_nd() + #define OBD_FAIL_LLITE_NEWNODE_PAUSE 0x140a + do_facet client "$LCTL set_param fail_val=$req_delay fail_loc=0x140a" + + # create file on first mount point + $PDSH ${uname}@localhost "mkdir $dirname1" & + local mkdirpid=$! + sleep 5 + + if [[ -z "$(ps h -o comm -p $mkdirpid)" ]]; then + error "mkdir failed to sleep, pid=$mkdirpid" + fi + + # get sec info on second mount point + if [ -e "$dirname2" ]; then + secinfo2=$(ls -ldZ $dirname2 | awk '{print $4}') + else + secinfo2="" + fi + + # get sec info on first mount point + wait $mkdirpid + secinfo1=$(ls -ldZ $dirname1 | awk '{print $4}') + + # compare sec contexts + [ -z "$secinfo2" -o "$secinfo1" == "$secinfo2" ] || + error "sec context seen from 2nd mount point is not correct" + + return 0 +} +run_test 20b "[atomicity] concurrent access from another client (dir)" + +test_20c() { + local dirname1=$DIR/dd20c + local dirname2=$DIR2/dd20c + local req_delay=20 + + # sleep some time in ll_create_nd() + #define OBD_FAIL_LLITE_SETDIRSTRIPE_PAUSE 0x140b + do_facet client "$LCTL set_param fail_val=$req_delay fail_loc=0x140b" + + # create file on first mount point + lfs mkdir -c0 $dirname1 & + local mkdirpid=$! + sleep 5 + + if [[ -z "$(ps h -o comm -p $mkdirpid)" ]]; then + error "lfs mkdir failed to sleep, pid=$mkdirpid" + fi + + # get sec info on second mount point + if [ -e "$dirname2" ]; then + secinfo2=$(ls -ldZ $dirname2 | awk '{print $4}') + else + secinfo2="" + fi + + # get sec info on first mount point + wait $mkdirpid + secinfo1=$(ls -ldZ $dirname1 | awk '{print $4}') + + # compare sec contexts + [ -z "$secinfo2" -o "$secinfo1" == "$secinfo2" ] || + error "sec context seen from 2nd mount point is not correct" + + return 0 +} +run_test 20c "[atomicity] concurrent access from another client (dir via lfs)" + + +complete $SECONDS +check_and_cleanup_lustre +exit_status + diff --git a/lustre/tests/test-framework.sh b/lustre/tests/test-framework.sh index 4e82e08..8e4ff45 100755 --- a/lustre/tests/test-framework.sh +++ b/lustre/tests/test-framework.sh @@ -3247,7 +3247,7 @@ facet_mntpt () { mount_ldiskfs() { local facet=$1 local dev=$(facet_device $facet) - local mnt=$(facet_mntpt $facet) + local mnt=${2:-$(facet_mntpt $facet)} local opts if ! do_facet $facet test -b $dev; then @@ -3259,7 +3259,7 @@ mount_ldiskfs() { unmount_ldiskfs() { local facet=$1 local dev=$(facet_device $facet) - local mnt=$(facet_mntpt $facet) + local mnt=${2:-$(facet_mntpt $facet)} do_facet $facet $UMOUNT $mnt } @@ -3271,7 +3271,7 @@ var_name() { mount_zfs() { local facet=$1 local ds=$(facet_device $facet) - local mnt=$(facet_mntpt $facet) + local mnt=${2:-$(facet_mntpt $facet)} local canmnt local mntpt @@ -3294,7 +3294,7 @@ mount_zfs() { unmount_zfs() { local facet=$1 local ds=$(facet_device $facet) - local mnt=$(facet_mntpt $facet) + local mnt=${2:-$(facet_mntpt $facet)} local var_mntpt=mz_$(var_name ${facet}_$ds)_mountpoint local var_canmnt=mz_$(var_name ${facet}_$ds)_canmount local mntpt=${!var_mntpt} @@ -3310,16 +3310,18 @@ unmount_zfs() { mount_fstype() { local facet=$1 + local mnt=$2 local fstype=$(facet_fstype $facet) - mount_$fstype $facet + mount_$fstype $facet $mnt } unmount_fstype() { local facet=$1 + local mnt=$2 local fstype=$(facet_fstype $facet) - unmount_$fstype $facet + unmount_$fstype $facet $mnt } ######## -- 1.8.3.1