Index: stable/12/tests/sys/fs/fusefs/utils.cc =================================================================== --- stable/12/tests/sys/fs/fusefs/utils.cc (revision 361125) +++ stable/12/tests/sys/fs/fusefs/utils.cc (revision 361126) @@ -1,623 +1,637 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2019 The FreeBSD Foundation * * This software was developed by BFF Storage Systems, LLC under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ extern "C" { #include #include #include #include #include #include #include #include #include #include #include } #include #include "mockfs.hh" #include "utils.hh" using namespace testing; /* * The default max_write is set to this formula in libfuse, though * individual filesystems can lower it. The "- 4096" was added in * commit 154ffe2, with the commit message "fix". */ const uint32_t libfuse_max_write = 32 * getpagesize() + 0x1000 - 4096; /* * Set the default max_write to a distinct value from MAXPHYS to catch bugs * that confuse the two. */ const uint32_t default_max_write = MIN(libfuse_max_write, MAXPHYS / 2); /* Check that fusefs(4) is accessible and the current user can mount(2) */ void check_environment() { const char *devnode = "/dev/fuse"; + const char *bsdextended_node = "security.mac.bsdextended.enabled"; + int bsdextended_val = 0; + size_t bsdextended_size = sizeof(bsdextended_val); + int bsdextended_found; const char *usermount_node = "vfs.usermount"; int usermount_val = 0; size_t usermount_size = sizeof(usermount_val); if (eaccess(devnode, R_OK | W_OK)) { if (errno == ENOENT) { GTEST_SKIP() << devnode << " does not exist"; } else if (errno == EACCES) { GTEST_SKIP() << devnode << " is not accessible by the current user"; } else { GTEST_SKIP() << strerror(errno); } } + // mac_bsdextended(4), when enabled, generates many more GETATTR + // operations. The fusefs tests' expectations don't account for those, + // and adding extra code to handle them obfuscates the real purpose of + // the tests. Better just to skip the fusefs tests if mac_bsdextended + // is enabled. + bsdextended_found = sysctlbyname(bsdextended_node, &bsdextended_val, + &bsdextended_size, NULL, 0); + if (bsdextended_found == 0 && bsdextended_val != 0) + GTEST_SKIP() << + "The fusefs tests are incompatible with mac_bsdextended."; ASSERT_EQ(sysctlbyname(usermount_node, &usermount_val, &usermount_size, NULL, 0), - 0);; + 0); if (geteuid() != 0 && !usermount_val) GTEST_SKIP() << "current user is not allowed to mount"; } const char *cache_mode_to_s(enum cache_mode cm) { switch (cm) { case Uncached: return "Uncached"; case Writethrough: return "Writethrough"; case Writeback: return "Writeback"; case WritebackAsync: return "WritebackAsync"; default: return "Unknown"; } } bool is_unsafe_aio_enabled(void) { const char *node = "vfs.aio.enable_unsafe"; int val = 0; size_t size = sizeof(val); if (sysctlbyname(node, &val, &size, NULL, 0)) { perror("sysctlbyname"); return (false); } return (val != 0); } class FuseEnv: public Environment { virtual void SetUp() { } }; void FuseTest::SetUp() { const char *maxbcachebuf_node = "vfs.maxbcachebuf"; const char *maxphys_node = "kern.maxphys"; int val = 0; size_t size = sizeof(val); /* * XXX check_environment should be called from FuseEnv::SetUp, but * can't due to https://github.com/google/googletest/issues/2189 */ check_environment(); if (IsSkipped()) return; ASSERT_EQ(0, sysctlbyname(maxbcachebuf_node, &val, &size, NULL, 0)) << strerror(errno); m_maxbcachebuf = val; ASSERT_EQ(0, sysctlbyname(maxphys_node, &val, &size, NULL, 0)) << strerror(errno); m_maxphys = val; try { m_mock = new MockFS(m_maxreadahead, m_allow_other, m_default_permissions, m_push_symlinks_in, m_ro, m_pm, m_init_flags, m_kernel_minor_version, m_maxwrite, m_async, m_noclusterr, m_time_gran, m_nointr); /* * FUSE_ACCESS is called almost universally. Expecting it in * each test case would be super-annoying. Instead, set a * default expectation for FUSE_ACCESS and return ENOSYS. * * Individual test cases can override this expectation since * googlemock evaluates expectations in LIFO order. */ EXPECT_CALL(*m_mock, process( ResultOf([=](auto in) { return (in.header.opcode == FUSE_ACCESS); }, Eq(true)), _) ).Times(AnyNumber()) .WillRepeatedly(Invoke(ReturnErrno(ENOSYS))); /* * FUSE_BMAP is called for most test cases that read data. Set * a default expectation and return ENOSYS. * * Individual test cases can override this expectation since * googlemock evaluates expectations in LIFO order. */ EXPECT_CALL(*m_mock, process( ResultOf([=](auto in) { return (in.header.opcode == FUSE_BMAP); }, Eq(true)), _) ).Times(AnyNumber()) .WillRepeatedly(Invoke(ReturnErrno(ENOSYS))); } catch (std::system_error err) { FAIL() << err.what(); } } void FuseTest::expect_access(uint64_t ino, mode_t access_mode, int error) { EXPECT_CALL(*m_mock, process( ResultOf([=](auto in) { return (in.header.opcode == FUSE_ACCESS && in.header.nodeid == ino && in.body.access.mask == access_mode); }, Eq(true)), _) ).WillOnce(Invoke(ReturnErrno(error))); } void FuseTest::expect_destroy(int error) { EXPECT_CALL(*m_mock, process( ResultOf([=](auto in) { return (in.header.opcode == FUSE_DESTROY); }, Eq(true)), _) ).WillOnce(Invoke( ReturnImmediate([&](auto in, auto& out) { m_mock->m_quit = true; out.header.len = sizeof(out.header); out.header.unique = in.header.unique; out.header.error = -error; }))); } void FuseTest::expect_flush(uint64_t ino, int times, ProcessMockerT r) { EXPECT_CALL(*m_mock, process( ResultOf([=](auto in) { return (in.header.opcode == FUSE_FLUSH && in.header.nodeid == ino); }, Eq(true)), _) ).Times(times) .WillRepeatedly(Invoke(r)); } void FuseTest::expect_forget(uint64_t ino, uint64_t nlookup, sem_t *sem) { EXPECT_CALL(*m_mock, process( ResultOf([=](auto in) { return (in.header.opcode == FUSE_FORGET && in.header.nodeid == ino && in.body.forget.nlookup == nlookup); }, Eq(true)), _) ).WillOnce(Invoke([=](auto in __unused, auto &out __unused) { if (sem != NULL) sem_post(sem); /* FUSE_FORGET has no response! */ })); } void FuseTest::expect_getattr(uint64_t ino, uint64_t size) { EXPECT_CALL(*m_mock, process( ResultOf([=](auto in) { return (in.header.opcode == FUSE_GETATTR && in.header.nodeid == ino); }, Eq(true)), _) ).WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto& out) { SET_OUT_HEADER_LEN(out, attr); out.body.attr.attr.ino = ino; // Must match nodeid out.body.attr.attr.mode = S_IFREG | 0644; out.body.attr.attr.size = size; out.body.attr.attr_valid = UINT64_MAX; }))); } void FuseTest::expect_lookup(const char *relpath, uint64_t ino, mode_t mode, uint64_t size, int times, uint64_t attr_valid, uid_t uid, gid_t gid) { EXPECT_LOOKUP(FUSE_ROOT_ID, relpath) .Times(times) .WillRepeatedly(Invoke( ReturnImmediate([=](auto in __unused, auto& out) { SET_OUT_HEADER_LEN(out, entry); out.body.entry.attr.mode = mode; out.body.entry.nodeid = ino; out.body.entry.attr.nlink = 1; out.body.entry.attr_valid = attr_valid; out.body.entry.attr.size = size; out.body.entry.attr.uid = uid; out.body.entry.attr.gid = gid; }))); } void FuseTest::expect_lookup_7_8(const char *relpath, uint64_t ino, mode_t mode, uint64_t size, int times, uint64_t attr_valid, uid_t uid, gid_t gid) { EXPECT_LOOKUP(FUSE_ROOT_ID, relpath) .Times(times) .WillRepeatedly(Invoke( ReturnImmediate([=](auto in __unused, auto& out) { SET_OUT_HEADER_LEN(out, entry_7_8); out.body.entry.attr.mode = mode; out.body.entry.nodeid = ino; out.body.entry.attr.nlink = 1; out.body.entry.attr_valid = attr_valid; out.body.entry.attr.size = size; out.body.entry.attr.uid = uid; out.body.entry.attr.gid = gid; }))); } void FuseTest::expect_open(uint64_t ino, uint32_t flags, int times) { EXPECT_CALL(*m_mock, process( ResultOf([=](auto in) { return (in.header.opcode == FUSE_OPEN && in.header.nodeid == ino); }, Eq(true)), _) ).Times(times) .WillRepeatedly(Invoke( ReturnImmediate([=](auto in __unused, auto& out) { out.header.len = sizeof(out.header); SET_OUT_HEADER_LEN(out, open); out.body.open.fh = FH; out.body.open.open_flags = flags; }))); } void FuseTest::expect_opendir(uint64_t ino) { /* opendir(3) calls fstatfs */ EXPECT_CALL(*m_mock, process( ResultOf([](auto in) { return (in.header.opcode == FUSE_STATFS); }, Eq(true)), _) ).WillRepeatedly(Invoke( ReturnImmediate([=](auto i __unused, auto& out) { SET_OUT_HEADER_LEN(out, statfs); }))); EXPECT_CALL(*m_mock, process( ResultOf([=](auto in) { return (in.header.opcode == FUSE_OPENDIR && in.header.nodeid == ino); }, Eq(true)), _) ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { out.header.len = sizeof(out.header); SET_OUT_HEADER_LEN(out, open); out.body.open.fh = FH; }))); } void FuseTest::expect_read(uint64_t ino, uint64_t offset, uint64_t isize, uint64_t osize, const void *contents, int flags) { EXPECT_CALL(*m_mock, process( ResultOf([=](auto in) { return (in.header.opcode == FUSE_READ && in.header.nodeid == ino && in.body.read.fh == FH && in.body.read.offset == offset && in.body.read.size == isize && flags == -1 ? (in.body.read.flags == O_RDONLY || in.body.read.flags == O_RDWR) : in.body.read.flags == (uint32_t)flags); }, Eq(true)), _) ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { out.header.len = sizeof(struct fuse_out_header) + osize; memmove(out.body.bytes, contents, osize); }))).RetiresOnSaturation(); } void FuseTest::expect_readdir(uint64_t ino, uint64_t off, std::vector &ents) { EXPECT_CALL(*m_mock, process( ResultOf([=](auto in) { return (in.header.opcode == FUSE_READDIR && in.header.nodeid == ino && in.body.readdir.fh == FH && in.body.readdir.offset == off); }, Eq(true)), _) ).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto& out) { struct fuse_dirent *fde = (struct fuse_dirent*)&(out.body); int i = 0; out.header.error = 0; out.header.len = 0; for (const auto& it: ents) { size_t entlen, entsize; fde->ino = it.d_fileno; fde->off = it.d_off; fde->type = it.d_type; fde->namelen = it.d_namlen; strncpy(fde->name, it.d_name, it.d_namlen); entlen = FUSE_NAME_OFFSET + fde->namelen; entsize = FUSE_DIRENT_SIZE(fde); /* * The FUSE protocol does not require zeroing out the * unused portion of the name. But it's a good * practice to prevent information disclosure to the * FUSE client, even though the client is usually the * kernel */ memset(fde->name + fde->namelen, 0, entsize - entlen); if (out.header.len + entsize > in.body.read.size) { printf("Overflow in readdir expectation: i=%d\n" , i); break; } out.header.len += entsize; fde = (struct fuse_dirent*) ((intmax_t*)fde + entsize / sizeof(intmax_t)); i++; } out.header.len += sizeof(out.header); }))); } void FuseTest::expect_release(uint64_t ino, uint64_t fh) { EXPECT_CALL(*m_mock, process( ResultOf([=](auto in) { return (in.header.opcode == FUSE_RELEASE && in.header.nodeid == ino && in.body.release.fh == fh); }, Eq(true)), _) ).WillOnce(Invoke(ReturnErrno(0))); } void FuseTest::expect_releasedir(uint64_t ino, ProcessMockerT r) { EXPECT_CALL(*m_mock, process( ResultOf([=](auto in) { return (in.header.opcode == FUSE_RELEASEDIR && in.header.nodeid == ino && in.body.release.fh == FH); }, Eq(true)), _) ).WillOnce(Invoke(r)); } void FuseTest::expect_unlink(uint64_t parent, const char *path, int error) { EXPECT_CALL(*m_mock, process( ResultOf([=](auto in) { return (in.header.opcode == FUSE_UNLINK && 0 == strcmp(path, in.body.unlink) && in.header.nodeid == parent); }, Eq(true)), _) ).WillOnce(Invoke(ReturnErrno(error))); } void FuseTest::expect_write(uint64_t ino, uint64_t offset, uint64_t isize, uint64_t osize, uint32_t flags_set, uint32_t flags_unset, const void *contents) { EXPECT_CALL(*m_mock, process( ResultOf([=](auto in) { const char *buf = (const char*)in.body.bytes + sizeof(struct fuse_write_in); bool pid_ok; uint32_t wf = in.body.write.write_flags; if (wf & FUSE_WRITE_CACHE) pid_ok = true; else pid_ok = (pid_t)in.header.pid == getpid(); return (in.header.opcode == FUSE_WRITE && in.header.nodeid == ino && in.body.write.fh == FH && in.body.write.offset == offset && in.body.write.size == isize && pid_ok && (wf & flags_set) == flags_set && (wf & flags_unset) == 0 && (in.body.write.flags == O_WRONLY || in.body.write.flags == O_RDWR) && 0 == bcmp(buf, contents, isize)); }, Eq(true)), _) ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { SET_OUT_HEADER_LEN(out, write); out.body.write.size = osize; }))); } void FuseTest::expect_write_7_8(uint64_t ino, uint64_t offset, uint64_t isize, uint64_t osize, const void *contents) { EXPECT_CALL(*m_mock, process( ResultOf([=](auto in) { const char *buf = (const char*)in.body.bytes + FUSE_COMPAT_WRITE_IN_SIZE; bool pid_ok = (pid_t)in.header.pid == getpid(); return (in.header.opcode == FUSE_WRITE && in.header.nodeid == ino && in.body.write.fh == FH && in.body.write.offset == offset && in.body.write.size == isize && pid_ok && 0 == bcmp(buf, contents, isize)); }, Eq(true)), _) ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { SET_OUT_HEADER_LEN(out, write); out.body.write.size = osize; }))); } void get_unprivileged_id(uid_t *uid, gid_t *gid) { struct passwd *pw; struct group *gr; /* * First try "tests", Kyua's default unprivileged user. XXX after * GoogleTest gains a proper Kyua wrapper, get this with the Kyua API */ pw = getpwnam("tests"); if (pw == NULL) { /* Fall back to "nobody" */ pw = getpwnam("nobody"); } if (pw == NULL) GTEST_SKIP() << "Test requires an unprivileged user"; /* Use group "nobody", which is Kyua's default unprivileged group */ gr = getgrnam("nobody"); if (gr == NULL) GTEST_SKIP() << "Test requires an unprivileged group"; *uid = pw->pw_uid; *gid = gr->gr_gid; } void FuseTest::fork(bool drop_privs, int *child_status, std::function parent_func, std::function child_func) { sem_t *sem; int mprot = PROT_READ | PROT_WRITE; int mflags = MAP_ANON | MAP_SHARED; pid_t child; uid_t uid; gid_t gid; if (drop_privs) { get_unprivileged_id(&uid, &gid); if (IsSkipped()) return; } sem = (sem_t*)mmap(NULL, sizeof(*sem), mprot, mflags, -1, 0); ASSERT_NE(MAP_FAILED, sem) << strerror(errno); ASSERT_EQ(0, sem_init(sem, 1, 0)) << strerror(errno); if ((child = ::fork()) == 0) { /* In child */ int err = 0; if (sem_wait(sem)) { perror("sem_wait"); err = 1; goto out; } if (drop_privs && 0 != setegid(gid)) { perror("setegid"); err = 1; goto out; } if (drop_privs && 0 != setreuid(-1, uid)) { perror("setreuid"); err = 1; goto out; } err = child_func(); out: sem_destroy(sem); _exit(err); } else if (child > 0) { /* * In parent. Cleanup must happen here, because it's still * privileged. */ m_mock->m_child_pid = child; ASSERT_NO_FATAL_FAILURE(parent_func()); /* Signal the child process to go */ ASSERT_EQ(0, sem_post(sem)) << strerror(errno); ASSERT_LE(0, wait(child_status)) << strerror(errno); } else { FAIL() << strerror(errno); } munmap(sem, sizeof(*sem)); return; } static void usage(char* progname) { fprintf(stderr, "Usage: %s [-v]\n\t-v increase verbosity\n", progname); exit(2); } int main(int argc, char **argv) { int ch; FuseEnv *fuse_env = new FuseEnv; InitGoogleTest(&argc, argv); AddGlobalTestEnvironment(fuse_env); while ((ch = getopt(argc, argv, "v")) != -1) { switch (ch) { case 'v': verbosity++; break; default: usage(argv[0]); break; } } return (RUN_ALL_TESTS()); } Index: stable/12/tests/sys/mac/bsdextended/Makefile =================================================================== --- stable/12/tests/sys/mac/bsdextended/Makefile (revision 361125) +++ stable/12/tests/sys/mac/bsdextended/Makefile (revision 361126) @@ -1,13 +1,15 @@ # $FreeBSD$ TESTSDIR= ${TESTSBASE}/sys/mac/bsdextended +ATF_TESTS_SH+= matches_test TAP_TESTS_C+= ugidfw_test -TAP_TESTS_SH+= matches_test LIBADD.ugidfw_test+= ugidfw -TEST_METADATA.matches_test+= required_user="root" TEST_METADATA.ugidfw_test+= required_user="root" +# Each test case of matches_test reuses the same ruleset number, so they cannot +# be run simultaneously +TEST_METADATA.matches_test+= is_exclusive=true .include Index: stable/12/tests/sys/mac/bsdextended/matches_test.sh =================================================================== --- stable/12/tests/sys/mac/bsdextended/matches_test.sh (revision 361125) +++ stable/12/tests/sys/mac/bsdextended/matches_test.sh (revision 361126) @@ -1,365 +1,399 @@ #!/bin/sh # # $FreeBSD$ # uidrange="60000:100000" gidrange="60000:100000" uidinrange="nobody" uidoutrange="daemon" gidinrange="nobody" # We expect $uidinrange in this group gidoutrange="daemon" # We expect $uidinrange in this group -test_num=1 -pass() -{ - echo "ok $test_num # $@" - : $(( test_num += 1 )) -} -fail() +check_ko() { - echo "not ok $test_num # $@" - : $(( test_num += 1 )) + if ! sysctl -N security.mac.bsdextended >/dev/null 2>&1; then + atf_skip "mac_bsdextended(4) support isn't available" + fi + if [ $(sysctl -n security.mac.bsdextended.enabled) = "0" ]; then + # The kernel module is loaded but disabled. Enable it for the + # duration of the test. + touch enabled_bsdextended + sysctl security.mac.bsdextended.enabled=1 + fi } -# -# Setup -# - -: ${TMPDIR=/tmp} -if [ $(id -u) -ne 0 ]; then - echo "1..0 # SKIP test must be run as root" - exit 0 -fi -if ! sysctl -N security.mac.bsdextended >/dev/null 2>&1; then - echo "1..0 # SKIP mac_bsdextended(4) support isn't available" - exit 0 -fi -if [ "$TMPDIR" != "/tmp" ]; then - if ! chmod -Rf 0755 $TMPDIR; then - echo "1..0 # SKIP failed to chmod $TMPDIR" - exit 0 +setup() +{ + check_ko + mkdir mnt + mdmfs -s 25m md mnt \ + || atf_fail "failed to mount md device" + chmod a+rwx mnt + md_device=$(mount -p | grep "$PWD/mnt" | awk '{ gsub(/^\/dev\//, "", $1); print $1 }') + if [ -z "$md_device" ]; then + atf_fail "md device not properly attached to the system" fi -fi -if ! playground=$(mktemp -d $TMPDIR/tmp.XXXXXXX); then - echo "1..0 # SKIP failed to create temporary directory" - exit 0 -fi -trap "rmdir $playground" EXIT INT TERM -if ! mdmfs -s 25m md $playground; then - echo "1..0 # SKIP failed to mount md device" - exit 0 -fi -chmod a+rwx $playground -md_device=$(mount -p | grep "$playground" | awk '{ gsub(/^\/dev\//, "", $1); print $1 }') -trap "umount -f $playground; mdconfig -d -u $md_device; rmdir $playground" EXIT INT TERM -if [ -z "$md_device" ]; then - mount -p | grep $playground - echo "1..0 # SKIP md device not properly attached to the system" -fi + echo $md_device > md_device -ugidfw remove 1 + ugidfw remove 1 -file1=$playground/test-$uidinrange -file2=$playground/test-$uidoutrange -cat > $playground/test-script.sh <<'EOF' + cat > mnt/test-script.sh <<'EOF' #!/bin/sh : > $1 EOF -if [ $? -ne 0 ]; then - echo "1..0 # SKIP failed to create test script" - exit 0 -fi -echo "1..30" + if [ $? -ne 0 ]; then + atf_fail "failed to create test script" + fi -command1="sh $playground/test-script.sh $file1" -command2="sh $playground/test-script.sh $file2" + file1=mnt/test-$uidinrange + file2=mnt/test-$uidoutrange + command1="sh mnt/test-script.sh $file1" + command2="sh mnt/test-script.sh $file2" -desc="$uidinrange file" -if su -m $uidinrange -c "$command1"; then - pass $desc -else - fail $desc -fi + # $uidinrange file + atf_check -s exit:0 su -m $uidinrange -c "$command1" -chown "$uidinrange":"$gidinrange" $file1 -chmod a+w $file1 + chown "$uidinrange":"$gidinrange" $file1 + chmod a+w $file1 -desc="$uidoutrange file" -if $command2; then - pass $desc -else - fail $desc -fi + # $uidoutrange file + if ! $command2; then + atf_fail $desc + fi -chown "$uidoutrange":"$gidoutrange" $file2 -chmod a+w $file2 + chown "$uidoutrange":"$gidoutrange" $file2 + chmod a+w $file2 +} -# -# No rules -# -desc="no rules $uidinrange" -if su -fm $uidinrange -c "$command1"; then - pass $desc -else - fail $desc -fi +cleanup() +{ + ugidfw remove 1 -desc="no rules $uidoutrange" -if su -fm $uidoutrange -c "$command1"; then - pass $desc -else - fail $desc -fi + umount -f mnt + if [ -f md_device ]; then + mdconfig -d -u $( cat md_device ) + fi + if [ -f enabled_bsdextended ]; then + sysctl security.mac.bsdextended.enabled=0 + fi +} -# -# Subject Match on uid -# -ugidfw set 1 subject uid $uidrange object mode rasx -desc="subject uid in range" -if su -fm $uidinrange -c "$command1"; then - fail $desc -else - pass $desc -fi +atf_test_case no_rules cleanup +no_rules_head() +{ + atf_set "require.user" "root" +} +no_rules_body() +{ + setup -desc="subject uid out range" -if su -fm $uidoutrange -c "$command1"; then - pass $desc -else - fail $desc -fi + # no rules $uidinrange + atf_check -s exit:0 su -fm $uidinrange -c "$command1" -# -# Subject Match on gid -# -ugidfw set 1 subject gid $gidrange object mode rasx + # no rules $uidoutrange + atf_check -s exit:0 su -fm $uidoutrange -c "$command1" +} +no_rules_cleanup() +{ + cleanup +} -desc="subject gid in range" -if su -fm $uidinrange -c "$command1"; then - fail $desc -else - pass $desc -fi +atf_test_case subject_match_on_uid cleanup +subject_match_on_uid_head() +{ + atf_set "require.user" "root" +} +subject_match_on_uid_body() +{ + setup -desc="subject gid out range" -if su -fm $uidoutrange -c "$command1"; then - pass $desc -else - fail $desc -fi + atf_check -s exit:0 ugidfw set 1 subject uid $uidrange object mode rasx + # subject uid in range + atf_check -s not-exit:0 -e match:"Permission denied" \ + su -fm $uidinrange -c "$command1" -if which jail >/dev/null; then - # - # Subject Match on jail - # - rm -f $playground/test-jail + # subject uid out range + atf_check -s exit:0 su -fm $uidoutrange -c "$command1" - desc="subject matching jailid" - jailid=`jail -i / localhost 127.0.0.1 /usr/sbin/daemon -f /bin/sh -c "(sleep 5; touch $playground/test-jail) &"` - ugidfw set 1 subject jailid $jailid object mode rasx +} +subject_match_on_uid_cleanup() +{ + cleanup +} + +atf_test_case subject_match_on_gid cleanup +subject_match_on_gid_head() +{ + atf_set "require.user" "root" +} +subject_match_on_gid_body() +{ + setup + + atf_check -s exit:0 ugidfw set 1 subject gid $gidrange object mode rasx + + # subject gid in range + atf_check -s not-exit:0 -e match:"Permission denied" \ + su -fm $uidinrange -c "$command1" + + # subject gid out range + atf_check -s exit:0 su -fm $uidoutrange -c "$command1" +} +subject_match_on_gid_cleanup() +{ + cleanup +} + +atf_test_case subject_match_on_jail cleanup +subject_match_on_jail_head() +{ + atf_set "require.progs" "jail" + atf_set "require.user" "root" +} +subject_match_on_jail_body() +{ + setup + + atf_expect_fail "this testcase fails (see bug # 205481)" + # subject matching jailid + jailid=`jail -i / localhost 127.0.0.1 /usr/sbin/daemon -f /bin/sh -c "(sleep 5; touch mnt/test-jail) &"` + atf_check -s exit:0 ugidfw set 1 subject jailid $jailid object mode rasx sleep 10 - if [ -f $playground/test-jail ]; then - fail "TODO $desc: this testcase fails (see bug # 205481)" - else - pass $desc + if [ -f mnt/test-jail ]; then + atf_fail "$desc" fi - rm -f $playground/test-jail - desc="subject nonmatching jailid" - jailid=`jail -i / localhost 127.0.0.1 /usr/sbin/daemon -f /bin/sh -c "(sleep 5; touch $playground/test-jail) &"` + rm -f mnt/test-jail + # subject nonmatching jailid + jailid=`jail -i / localhost 127.0.0.1 /usr/sbin/daemon -f /bin/sh -c "(sleep 5; touch mnt/test-jail) &"` sleep 10 - if [ -f $playground/test-jail ]; then - pass $desc - else - fail $desc + if ! [ -f mnt/test-jail ]; then + atf_fail $desc fi -else - # XXX: kyua is too dumb to parse skip ranges, still.. - pass "skip jail(8) not installed" - pass "skip jail(8) not installed" -fi +} +subject_match_on_jail_cleanup() +{ + cleanup +} -# -# Object uid -# -ugidfw set 1 subject object uid $uidrange mode rasx +atf_test_case object_uid cleanup +object_uid_head() +{ + atf_set "require.user" "root" +} +object_uid_body() +{ + setup -desc="object uid in range" -if su -fm $uidinrange -c "$command1"; then - fail $desc -else - pass $desc -fi + atf_check -s exit:0 ugidfw set 1 subject object uid $uidrange mode rasx -desc="object uid out range" -if su -fm $uidinrange -c "$command2"; then - pass $desc -else - fail $desc -fi -ugidfw set 1 subject object uid $uidrange mode rasx + # object uid in range + atf_check -s not-exit:0 -e match:"Permission denied" \ + su -fm $uidinrange -c "$command1" -desc="object uid in range (different subject)" -if su -fm $uidoutrange -c "$command1"; then - fail $desc -else - pass $desc -fi + # object uid out range + atf_check -s exit:0 su -fm $uidinrange -c "$command2" + atf_check -s exit:0 ugidfw set 1 subject object uid $uidrange mode rasx -desc="object uid out range (different subject)" -if su -fm $uidoutrange -c "$command2"; then - pass $desc -else - fail $desc -fi + # object uid in range (different subject) + atf_check -s not-exit:0 -e match:"Permission denied" \ + su -fm $uidoutrange -c "$command1" -# -# Object gid -# -ugidfw set 1 subject object gid $uidrange mode rasx + # object uid out range (different subject) + atf_check -s exit:0 su -fm $uidoutrange -c "$command2" -desc="object gid in range" -if su -fm $uidinrange -c "$command1"; then - fail $desc -else - pass $desc -fi +} +object_uid_cleanup() +{ + cleanup +} -desc="object gid out range" -if su -fm $uidinrange -c "$command2"; then - pass $desc -else - fail $desc -fi -desc="object gid in range (different subject)" -if su -fm $uidoutrange -c "$command1"; then - fail $desc -else - pass $desc -fi +atf_test_case object_gid cleanup +object_gid_head() +{ + atf_set "require.user" "root" +} +object_gid_body() +{ + setup -desc="object gid out range (different subject)" -if su -fm $uidoutrange -c "$command2"; then - pass $desc -else - fail $desc -fi + atf_check -s exit:0 ugidfw set 1 subject object gid $uidrange mode rasx -# -# Object filesys -# -ugidfw set 1 subject uid $uidrange object filesys / mode rasx -desc="object out of filesys" -if su -fm $uidinrange -c "$command1"; then - pass $desc -else - fail $desc -fi + # object gid in range + atf_check -s not-exit:0 -e match:"Permission denied" \ + su -fm $uidinrange -c "$command1" -ugidfw set 1 subject uid $uidrange object filesys $playground mode rasx -desc="object in filesys" -if su -fm $uidinrange -c "$command1"; then - fail $desc -else - pass $desc -fi + # object gid out range + atf_check -s exit:0 su -fm $uidinrange -c "$command2" + # object gid in range (different subject) + atf_check -s not-exit:0 -e match:"Permission denied" \ + su -fm $uidoutrange -c "$command1" -# -# Object suid -# -ugidfw set 1 subject uid $uidrange object suid mode rasx -desc="object notsuid" -if su -fm $uidinrange -c "$command1"; then - pass $desc -else - fail $desc -fi + # object gid out range (different subject) + atf_check -s exit:0 su -fm $uidoutrange -c "$command2" +} +object_gid_cleanup() +{ + cleanup +} -chmod u+s $file1 -desc="object suid" -if su -fm $uidinrange -c "$command1"; then - fail $desc -else - pass $desc -fi -chmod u-s $file1 +atf_test_case object_filesys cleanup +object_filesys_head() +{ + atf_set "require.user" "root" +} +object_filesys_body() +{ + setup -# -# Object sgid -# -ugidfw set 1 subject uid $uidrange object sgid mode rasx -desc="object notsgid" -if su -fm $uidinrange -c "$command1"; then - pass $desc -else - fail $desc -fi + atf_check -s exit:0 ugidfw set 1 subject uid $uidrange object filesys / mode rasx + # object out of filesys + atf_check -s exit:0 su -fm $uidinrange -c "$command1" -chmod g+s $file1 -desc="object sgid" -if su -fm $uidinrange -c "$command1"; then - fail $desc -else - pass $desc -fi -chmod g-s $file1 + atf_check -s exit:0 ugidfw set 1 subject uid $uidrange object filesys mnt mode rasx + # object in filesys + atf_check -s not-exit:0 -e match:"Permission denied" \ + su -fm $uidinrange -c "$command1" +} +object_filesys_cleanup() +{ + cleanup +} -# -# Object uid matches subject -# -ugidfw set 1 subject uid $uidrange object uid_of_subject mode rasx +atf_test_case object_suid cleanup +object_suid_head() +{ + atf_set "require.user" "root" +} +object_suid_body() +{ + setup -desc="object uid notmatches subject" -if su -fm $uidinrange -c "$command2"; then - pass $desc -else - fail $desc -fi + atf_check -s exit:0 ugidfw set 1 subject uid $uidrange object suid mode rasx + # object notsuid + atf_check -s exit:0 su -fm $uidinrange -c "$command1" -desc="object uid matches subject" -if su -fm $uidinrange -c "$command1"; then - fail $desc -else - pass $desc -fi + chmod u+s $file1 + # object suid + atf_check -s not-exit:0 -e match:"Permission denied" \ + su -fm $uidinrange -c "$command1" + chmod u-s $file1 -# -# Object gid matches subject -# -ugidfw set 1 subject uid $uidrange object gid_of_subject mode rasx +} +object_suid_cleanup() +{ + cleanup +} -desc="object gid notmatches subject" -if su -fm $uidinrange -c "$command2"; then - pass $desc -else - fail $desc -fi +atf_test_case object_sgid cleanup +object_sgid_head() +{ + atf_set "require.user" "root" +} +object_sgid_body() +{ + setup -desc="object gid matches subject" -if su -fm $uidinrange -c "$command1"; then - fail $desc -else - pass $desc -fi + atf_check -s exit:0 ugidfw set 1 subject uid $uidrange object sgid mode rasx + # object notsgid + atf_check -s exit:0 su -fm $uidinrange -c "$command1" -# -# Object type -# -desc="object not type" -ugidfw set 1 subject uid $uidrange object type dbclsp mode rasx -if su -fm $uidinrange -c "$command1"; then - pass $desc -else - fail $desc -fi + chmod g+s $file1 + # object sgid + atf_check -s not-exit:0 -e match:"Permission denied" \ + su -fm $uidinrange -c "$command1" + chmod g-s $file1 +} +object_sgid_cleanup() +{ + cleanup +} -desc="object type" -ugidfw set 1 subject uid $uidrange object type r mode rasx -if su -fm $uidinrange -c "$command1"; then - fail $desc -else - pass $desc -fi +atf_test_case object_uid_matches_subject cleanup +object_uid_matches_subject_head() +{ + atf_set "require.user" "root" +} +object_uid_matches_subject_body() +{ + setup + + atf_check -s exit:0 ugidfw set 1 subject uid $uidrange object uid_of_subject mode rasx + + # object uid notmatches subject + atf_check -s exit:0 su -fm $uidinrange -c "$command2" + + # object uid matches subject + atf_check -s not-exit:0 -e match:"Permission denied" \ + su -fm $uidinrange -c "$command1" +} +object_uid_matches_subject_cleanup() +{ + cleanup +} + +atf_test_case object_gid_matches_subject cleanup +object_gid_matches_subject_head() +{ + atf_set "require.user" "root" +} +object_gid_matches_subject_body() +{ + setup + + atf_check -s exit:0 ugidfw set 1 subject uid $uidrange object gid_of_subject mode rasx + + # object gid notmatches subject + atf_check -s exit:0 su -fm $uidinrange -c "$command2" + + # object gid matches subject + atf_check -s not-exit:0 -e match:"Permission denied" \ + su -fm $uidinrange -c "$command1" + +} +object_gid_matches_subject_cleanup() +{ + cleanup +} + +atf_test_case object_type cleanup +object_type_head() +{ + atf_set "require.user" "root" +} +object_type_body() +{ + setup + + # object not type + atf_check -s exit:0 ugidfw set 1 subject uid $uidrange object type dbclsp mode rasx + atf_check -s exit:0 su -fm $uidinrange -c "$command1" + + # object type + atf_check -s exit:0 ugidfw set 1 subject uid $uidrange object type r mode rasx + atf_check -s not-exit:0 -e match:"Permission denied" \ + su -fm $uidinrange -c "$command1" +} +object_type_cleanup() +{ + cleanup +} + +atf_init_test_cases() +{ + atf_add_test_case no_rules + atf_add_test_case subject_match_on_uid + atf_add_test_case subject_match_on_gid + atf_add_test_case subject_match_on_jail + atf_add_test_case object_uid + atf_add_test_case object_gid + atf_add_test_case object_filesys + atf_add_test_case object_suid + atf_add_test_case object_sgid + atf_add_test_case object_uid_matches_subject + atf_add_test_case object_gid_matches_subject + atf_add_test_case object_type +} Index: stable/12 =================================================================== --- stable/12 (revision 361125) +++ stable/12 (revision 361126) Property changes on: stable/12 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r360339,360567