Index: contrib/capsicum-test/capsicum-test-main.cc =================================================================== --- contrib/capsicum-test/capsicum-test-main.cc +++ contrib/capsicum-test/capsicum-test-main.cc @@ -1,13 +1,17 @@ #include -#ifdef __linux__ +#if defined(__FreeBSD__) +#include +#elif defined(__linux__) #include #include #endif #include #include +#include #include #include #include +#include #include #include "gtest/gtest.h" #include "capsicum-test.h" @@ -19,6 +23,7 @@ public: SetupEnvironment() : teardown_tmpdir_(false) {} void SetUp() override { + CheckCapsicumSupport(); if (tmpdir.empty()) { std::cerr << "Generating temporary directory root: "; CreateTemporaryRoot(); @@ -27,6 +32,40 @@ } std::cerr << tmpdir << std::endl; } + void CheckCapsicumSupport() { + // For versions of googletest that lack GTEST_SKIP. +#ifndef GTEST_SKIP +#define GTEST_SKIP GTEST_FAIL +#define GTEST_SKIP_defined +#endif + +#ifdef __FreeBSD__ + size_t trap_enotcap_enabled_len; + int rc; + bool trap_enotcap_enabled; + + trap_enotcap_enabled_len = sizeof(trap_enotcap_enabled); + + if (feature_present("security_capabilities") == 0) { + GTEST_SKIP() << "Skipping tests because capsicum support is not " + << "enabled in the kernel."; + } + const char *oid = "kern.trap_enotcap"; + rc = sysctlbyname(oid, &trap_enotcap_enabled, &trap_enotcap_enabled_len, + nullptr, 0); + if (rc != 0) { + GTEST_FAIL() << "sysctlbyname failed: " << strerror(errno); + } + if (trap_enotcap_enabled) { + GTEST_SKIP() << "Sysctl " << oid << " enabled. " + << "Skipping tests to avoid non-determinism with results"; + } +#endif /* FreeBSD */ + +#ifdef GTEST_SKIP_defined +#undef GTEST_SKIP +#endif + } void CreateTemporaryRoot() { char *tmpdir_name = tempnam(nullptr, "cptst"); @@ -47,7 +86,27 @@ bool teardown_tmpdir_; }; +std::string capsicum_test_bindir; + int main(int argc, char* argv[]) { + // Set up the test program path, so capsicum-test can find programs, like + // mini-me* when executed from an absolute path. + { + char *new_path, *old_path, *program_name; + + program_name = strdup(argv[0]); + assert(program_name); + capsicum_test_bindir = std::string(dirname(program_name)); + free(program_name); + + old_path = getenv("PATH"); + assert(old_path); + + assert(asprintf(&new_path, "%s:%s", capsicum_test_bindir.c_str(), + old_path) > 0); + assert(setenv("PATH", new_path, 1) == 0); + } + ::testing::InitGoogleTest(&argc, argv); for (int ii = 1; ii < argc; ii++) { if (strcmp(argv[ii], "-v") == 0) { Index: contrib/capsicum-test/capsicum.h =================================================================== --- contrib/capsicum-test/capsicum.h +++ contrib/capsicum-test/capsicum.h @@ -167,4 +167,9 @@ #endif /* new/old style rights manipulation */ +#ifdef __cplusplus +#include +extern std::string capsicum_test_bindir; +#endif + #endif /*__CAPSICUM_H__*/ Index: contrib/capsicum-test/fexecve.cc =================================================================== --- contrib/capsicum-test/fexecve.cc +++ contrib/capsicum-test/fexecve.cc @@ -1,12 +1,12 @@ -#include -#include #include #include #include +#include #include -#include #include #include +#include +#include #include @@ -14,41 +14,76 @@ #include "capsicum.h" #include "capsicum-test.h" -// We need a program to exec(), but for fexecve() to work in capability -// mode that program needs to be statically linked (otherwise ld.so will -// attempt to traverse the filesystem to load (e.g.) /lib/libc.so and -// fail). -#define EXEC_PROG "./mini-me" -#define EXEC_PROG_NOEXEC EXEC_PROG ".noexec" -#define EXEC_PROG_SETUID EXEC_PROG ".setuid" - // Arguments to use in execve() calls. -static char* argv_pass[] = {(char*)EXEC_PROG, (char*)"--pass", NULL}; -static char* argv_fail[] = {(char*)EXEC_PROG, (char*)"--fail", NULL}; -static char* argv_checkroot[] = {(char*)EXEC_PROG, (char*)"--checkroot", NULL}; static char* null_envp[] = {NULL}; class Execve : public ::testing::Test { public: - Execve() : exec_fd_(open(EXEC_PROG, O_RDONLY)) { + Execve() : exec_fd_(-1) { + // We need a program to exec(), but for fexecve() to work in capability + // mode that program needs to be statically linked (otherwise ld.so will + // attempt to traverse the filesystem to load (e.g.) /lib/libc.so and + // fail). + exec_prog_ = capsicum_test_bindir + "/mini-me"; + exec_prog_noexec_ = capsicum_test_bindir + "/mini-me.noexec"; + exec_prog_setuid_ = capsicum_test_bindir + "/mini-me.setuid"; + + exec_fd_ = open(exec_prog_.c_str(), O_RDONLY); if (exec_fd_ < 0) { - fprintf(stderr, "Error! Failed to open %s\n", EXEC_PROG); + fprintf(stderr, "Error! Failed to open %s\n", exec_prog_.c_str()); + } + argv_checkroot_[0] = (char*)exec_prog_.c_str(); + argv_fail_[0] = (char*)exec_prog_.c_str(); + argv_pass_[0] = (char*)exec_prog_.c_str(); + } + ~Execve() { + if (exec_fd_ >= 0) { + close(exec_fd_); + exec_fd_ = -1; } } - ~Execve() { if (exec_fd_ >= 0) close(exec_fd_); } protected: + char* argv_checkroot_[3] = {nullptr, (char*)"--checkroot", nullptr}; + char* argv_fail_[3] = {nullptr, (char*)"--fail", nullptr}; + char* argv_pass_[3] = {nullptr, (char*)"--pass", nullptr}; + std::string exec_prog_, exec_prog_noexec_, exec_prog_setuid_; int exec_fd_; }; +class Fexecve : public Execve { + public: + Fexecve() : Execve() {} +}; + +class FexecveWithScript : public Fexecve { + public: + FexecveWithScript() : + Fexecve(), temp_script_filename_(TmpFile("cap_sh_script")) {} + + void SetUp() override { + // First, build an executable shell script + int fd = open(temp_script_filename_, O_RDWR|O_CREAT, 0755); + EXPECT_OK(fd); + const char* contents = "#!/bin/sh\nexit 99\n"; + EXPECT_OK(write(fd, contents, strlen(contents))); + close(fd); + } + void TearDown() override { + (void)::unlink(temp_script_filename_); + } + + const char *temp_script_filename_; +}; + FORK_TEST_F(Execve, BasicFexecve) { - EXPECT_OK(fexecve_(exec_fd_, argv_pass, null_envp)); + EXPECT_OK(fexecve_(exec_fd_, argv_pass_, null_envp)); // Should not reach here, exec() takes over. EXPECT_TRUE(!"fexecve() should never return"); } FORK_TEST_F(Execve, InCapMode) { EXPECT_OK(cap_enter()); - EXPECT_OK(fexecve_(exec_fd_, argv_pass, null_envp)); + EXPECT_OK(fexecve_(exec_fd_, argv_pass_, null_envp)); // Should not reach here, exec() takes over. EXPECT_TRUE(!"fexecve() should never return"); } @@ -60,7 +95,7 @@ cap_rights_t rights; cap_rights_init(&rights, 0); EXPECT_OK(cap_rights_limit(cap_fd, &rights)); - EXPECT_EQ(-1, fexecve_(cap_fd, argv_fail, null_envp)); + EXPECT_EQ(-1, fexecve_(cap_fd, argv_fail_, null_envp)); EXPECT_EQ(ENOTCAPABLE, errno); } @@ -73,59 +108,54 @@ // rights -- just CAP_FEXECVE|CAP_READ or CAP_FEXECVE would be preferable. cap_rights_init(&rights, CAP_FEXECVE, CAP_LOOKUP, CAP_READ); EXPECT_OK(cap_rights_limit(cap_fd, &rights)); - EXPECT_OK(fexecve_(cap_fd, argv_pass, null_envp)); + EXPECT_OK(fexecve_(cap_fd, argv_pass_, null_envp)); // Should not reach here, exec() takes over. EXPECT_TRUE(!"fexecve() should have succeeded"); } -FORK_TEST(Fexecve, ExecutePermissionCheck) { - int fd = open(EXEC_PROG_NOEXEC, O_RDONLY); +FORK_TEST_F(Fexecve, ExecutePermissionCheck) { + int fd = open(exec_prog_noexec_.c_str(), O_RDONLY); EXPECT_OK(fd); if (fd >= 0) { struct stat data; EXPECT_OK(fstat(fd, &data)); EXPECT_EQ((mode_t)0, data.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)); - EXPECT_EQ(-1, fexecve_(fd, argv_fail, null_envp)); + EXPECT_EQ(-1, fexecve_(fd, argv_fail_, null_envp)); EXPECT_EQ(EACCES, errno); close(fd); } } -FORK_TEST(Fexecve, SetuidIgnored) { +FORK_TEST_F(Fexecve, SetuidIgnored) { if (geteuid() == 0) { TEST_SKIPPED("requires non-root"); return; } - int fd = open(EXEC_PROG_SETUID, O_RDONLY); + int fd = open(exec_prog_setuid_.c_str(), O_RDONLY); EXPECT_OK(fd); EXPECT_OK(cap_enter()); if (fd >= 0) { struct stat data; EXPECT_OK(fstat(fd, &data)); EXPECT_EQ((mode_t)S_ISUID, data.st_mode & S_ISUID); - EXPECT_OK(fexecve_(fd, argv_checkroot, null_envp)); + EXPECT_OK(fexecve_(fd, argv_checkroot_, null_envp)); // Should not reach here, exec() takes over. EXPECT_TRUE(!"fexecve() should have succeeded"); close(fd); } } -FORK_TEST(Fexecve, ExecveFailure) { +FORK_TEST_F(Fexecve, ExecveFailure) { EXPECT_OK(cap_enter()); - EXPECT_EQ(-1, execve(argv_fail[0], argv_fail, null_envp)); + EXPECT_EQ(-1, execve(argv_fail_[0], argv_fail_, null_envp)); EXPECT_EQ(ECAPMODE, errno); } -FORK_TEST_ON(Fexecve, CapModeScriptFail, TmpFile("cap_sh_script")) { - // First, build an executable shell script - int fd = open(TmpFile("cap_sh_script"), O_RDWR|O_CREAT, 0755); - EXPECT_OK(fd); - const char* contents = "#!/bin/sh\nexit 99\n"; - EXPECT_OK(write(fd, contents, strlen(contents))); - close(fd); +FORK_TEST_F(FexecveWithScript, CapModeScriptFail) { + int fd; // Open the script file, with CAP_FEXECVE rights. - fd = open(TmpFile("cap_sh_script"), O_RDONLY); + fd = open(temp_script_filename_, O_RDONLY); cap_rights_t rights; cap_rights_init(&rights, CAP_FEXECVE, CAP_READ, CAP_SEEK); EXPECT_OK(cap_rights_limit(fd, &rights)); @@ -133,12 +163,12 @@ EXPECT_OK(cap_enter()); // Enter capability mode // Attempt fexecve; should fail, because "/bin/sh" is inaccessible. - EXPECT_EQ(-1, fexecve_(fd, argv_pass, null_envp)); + EXPECT_EQ(-1, fexecve_(fd, argv_pass_, null_envp)); } #ifdef HAVE_EXECVEAT TEST(Execveat, NoUpwardTraversal) { - char *abspath = realpath(EXEC_PROG, NULL); + char *abspath = realpath(exec_prog_, NULL); char cwd[1024]; getcwd(cwd, sizeof(cwd)); @@ -148,9 +178,9 @@ EXPECT_OK(cap_enter()); // Enter capability mode. // Can't execveat() an absolute path, even relative to a dfd. EXPECT_SYSCALL_FAIL(ECAPMODE, - execveat(AT_FDCWD, abspath, argv_pass, null_envp, 0)); + execveat(AT_FDCWD, abspath, argv_pass_, null_envp, 0)); EXPECT_SYSCALL_FAIL(E_NO_TRAVERSE_CAPABILITY, - execveat(dfd, abspath, argv_pass, null_envp, 0)); + execveat(dfd, abspath, argv_pass_, null_envp, 0)); // Can't execveat() a relative path ("..//./"). char *p = cwd + strlen(cwd); @@ -158,9 +188,9 @@ char buffer[1024] = "../"; strcat(buffer, ++p); strcat(buffer, "/"); - strcat(buffer, EXEC_PROG); + strcat(buffer, exec_prog_); EXPECT_SYSCALL_FAIL(E_NO_TRAVERSE_CAPABILITY, - execveat(dfd, buffer, argv_pass, null_envp, 0)); + execveat(dfd, buffer, argv_pass_, null_envp, 0)); exit(HasFailure() ? 99 : 123); } int status; Index: contrib/capsicum-test/openat.cc =================================================================== --- contrib/capsicum-test/openat.cc +++ contrib/capsicum-test/openat.cc @@ -148,7 +148,7 @@ } #define TOPDIR "cap_topdir" -#define SUBDIR_ABS TOPDIR "/subdir" +#define SUBDIR TOPDIR "/subdir" class OpenatTest : public ::testing::Test { public: // Build a collection of files, subdirs and symlinks: @@ -156,20 +156,20 @@ // /topfile // /subdir/ // /subdir/bottomfile - // /symlink.samedir -> topfile - // /dsymlink.samedir -> ./ - // /symlink.down -> subdir/bottomfile - // /dsymlink.down -> subdir/ - // /symlink.absolute_in -> /tmp/cap_topdir/topfile - // /dsymlink.absolute_in -> /tmp/cap_topdir/ - // /symlink.absolute_out -> /etc/passwd - // /dsymlink.absolute_out -> /etc/ - // /symlink.relative_in -> ../../tmp/cap_topdir/topfile - // /dsymlink.relative_in -> ../../tmp/cap_topdir/ - // /symlink.relative_out -> ../../etc/passwd - // /dsymlink.relative_out -> ../../etc/ - // /subdir/symlink.up -> ../topfile - // /subdir/dsymlink.up -> ../ + // /symlink.samedir -> topfile + // /dsymlink.samedir -> ./ + // /symlink.down -> subdir/bottomfile + // /dsymlink.down -> subdir/ + // /symlink.absolute_out -> /etc/passwd + // /dsymlink.absolute_out -> /etc/ + // /symlink.relative_in -> ../../tmp/cap_topdir/topfile + // /dsymlink.relative_in -> ../../tmp/cap_topdir/ + // /symlink.relative_out -> ../../etc/passwd + // /dsymlink.relative_out -> ../../etc/ + // /subdir/dsymlink.absolute_in -> /tmp/cap_topdir/ + // /subdir/dsymlink.up -> ../ + // /subdir/symlink.absolute_in -> /tmp/cap_topdir/topfile + // /subdir/symlink.up -> ../topfile // (In practice, this is a little more complicated because tmpdir might // not be "/tmp".) OpenatTest() { @@ -179,7 +179,7 @@ if (rc < 0) { EXPECT_EQ(EEXIST, errno); } - rc = mkdir(TmpFile(SUBDIR_ABS), 0755); + rc = mkdir(TmpFile(SUBDIR), 0755); EXPECT_OK(rc); if (rc < 0) { EXPECT_EQ(EEXIST, errno); @@ -197,34 +197,34 @@ // Create normal files in each. CreateFile(TmpFile(TOPDIR "/topfile"), "Top-level file"); - CreateFile(TmpFile(SUBDIR_ABS "/bottomfile"), "File in subdirectory"); + CreateFile(TmpFile(SUBDIR "/bottomfile"), "File in subdirectory"); // Create various symlinks to files. EXPECT_OK(symlink("topfile", TmpFile(TOPDIR "/symlink.samedir"))); EXPECT_OK(symlink("subdir/bottomfile", TmpFile(TOPDIR "/symlink.down"))); - EXPECT_OK(symlink(TmpFile(TOPDIR "/topfile"), TmpFile(TOPDIR "/symlink.absolute_in"))); + EXPECT_OK(symlink(TmpFile(TOPDIR "/topfile"), TmpFile(SUBDIR "/symlink.absolute_in"))); EXPECT_OK(symlink("/etc/passwd", TmpFile(TOPDIR "/symlink.absolute_out"))); std::string dots2top = dots2root + TmpFile(TOPDIR "/topfile"); EXPECT_OK(symlink(dots2top.c_str(), TmpFile(TOPDIR "/symlink.relative_in"))); std::string dots2passwd = dots2root + "/etc/passwd"; EXPECT_OK(symlink(dots2passwd.c_str(), TmpFile(TOPDIR "/symlink.relative_out"))); - EXPECT_OK(symlink("../topfile", TmpFile(SUBDIR_ABS "/symlink.up"))); + EXPECT_OK(symlink("../topfile", TmpFile(SUBDIR "/symlink.up"))); // Create various symlinks to directories. EXPECT_OK(symlink("./", TmpFile(TOPDIR "/dsymlink.samedir"))); EXPECT_OK(symlink("subdir/", TmpFile(TOPDIR "/dsymlink.down"))); - EXPECT_OK(symlink(TmpFile(TOPDIR "/"), TmpFile(TOPDIR "/dsymlink.absolute_in"))); + EXPECT_OK(symlink(TmpFile(TOPDIR "/"), TmpFile(SUBDIR "/dsymlink.absolute_in"))); EXPECT_OK(symlink("/etc/", TmpFile(TOPDIR "/dsymlink.absolute_out"))); std::string dots2cwd = dots2root + tmpdir + "/"; EXPECT_OK(symlink(dots2cwd.c_str(), TmpFile(TOPDIR "/dsymlink.relative_in"))); std::string dots2etc = dots2root + "/etc/"; EXPECT_OK(symlink(dots2etc.c_str(), TmpFile(TOPDIR "/dsymlink.relative_out"))); - EXPECT_OK(symlink("../", TmpFile(SUBDIR_ABS "/dsymlink.up"))); + EXPECT_OK(symlink("../", TmpFile(SUBDIR "/dsymlink.up"))); // Open directory FDs for those directories and for cwd. dir_fd_ = open(TmpFile(TOPDIR), O_RDONLY); EXPECT_OK(dir_fd_); - sub_fd_ = open(TmpFile(SUBDIR_ABS), O_RDONLY); + sub_fd_ = open(TmpFile(SUBDIR), O_RDONLY); EXPECT_OK(sub_fd_); cwd_ = openat(AT_FDCWD, ".", O_RDONLY); EXPECT_OK(cwd_); @@ -236,23 +236,23 @@ close(cwd_); close(sub_fd_); close(dir_fd_); - unlink(TmpFile(SUBDIR_ABS "/symlink.up")); - unlink(TmpFile(TOPDIR "/symlink.absolute_in")); + unlink(TmpFile(SUBDIR "/symlink.up")); + unlink(TmpFile(SUBDIR "/symlink.absolute_in")); unlink(TmpFile(TOPDIR "/symlink.absolute_out")); unlink(TmpFile(TOPDIR "/symlink.relative_in")); unlink(TmpFile(TOPDIR "/symlink.relative_out")); unlink(TmpFile(TOPDIR "/symlink.down")); unlink(TmpFile(TOPDIR "/symlink.samedir")); - unlink(TmpFile(SUBDIR_ABS "/dsymlink.up")); - unlink(TmpFile(TOPDIR "/dsymlink.absolute_in")); + unlink(TmpFile(SUBDIR "/dsymlink.up")); + unlink(TmpFile(SUBDIR "/dsymlink.absolute_in")); unlink(TmpFile(TOPDIR "/dsymlink.absolute_out")); unlink(TmpFile(TOPDIR "/dsymlink.relative_in")); unlink(TmpFile(TOPDIR "/dsymlink.relative_out")); unlink(TmpFile(TOPDIR "/dsymlink.down")); unlink(TmpFile(TOPDIR "/dsymlink.samedir")); - unlink(TmpFile(SUBDIR_ABS "/bottomfile")); + unlink(TmpFile(SUBDIR "/bottomfile")); unlink(TmpFile(TOPDIR "/topfile")); - rmdir(TmpFile(SUBDIR_ABS)); + rmdir(TmpFile(SUBDIR)); rmdir(TmpFile(TOPDIR)); } @@ -281,18 +281,18 @@ // Should only be able to open symlinks that stay within the directory. EXPECT_OPEN_OK(openat(dir_fd_, "symlink.samedir", O_RDONLY|oflag)); EXPECT_OPEN_OK(openat(dir_fd_, "symlink.down", O_RDONLY|oflag)); - EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "symlink.absolute_in", O_RDONLY|oflag); EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "symlink.absolute_out", O_RDONLY|oflag); EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "symlink.relative_in", O_RDONLY|oflag); EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "symlink.relative_out", O_RDONLY|oflag); + EXPECT_OPENAT_FAIL_TRAVERSAL(sub_fd_, "symlink.absolute_in", O_RDONLY|oflag); EXPECT_OPENAT_FAIL_TRAVERSAL(sub_fd_, "symlink.up", O_RDONLY|oflag); EXPECT_OPEN_OK(openat(dir_fd_, "dsymlink.samedir/topfile", O_RDONLY|oflag)); EXPECT_OPEN_OK(openat(dir_fd_, "dsymlink.down/bottomfile", O_RDONLY|oflag)); - EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "dsymlink.absolute_in/topfile", O_RDONLY|oflag); EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "dsymlink.absolute_out/passwd", O_RDONLY|oflag); EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "dsymlink.relative_in/topfile", O_RDONLY|oflag); EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "dsymlink.relative_out/passwd", O_RDONLY|oflag); + EXPECT_OPENAT_FAIL_TRAVERSAL(sub_fd_, "dsymlink.absolute_in/topfile", O_RDONLY|oflag); EXPECT_OPENAT_FAIL_TRAVERSAL(sub_fd_, "dsymlink.up/topfile", O_RDONLY|oflag); // Although recall that O_NOFOLLOW prevents symlink following in final component. @@ -310,10 +310,10 @@ // Any kind of symlink can be opened relative to an ordinary directory FD. EXPECT_OPEN_OK(openat(dir_fd_, "symlink.samedir", O_RDONLY)); EXPECT_OPEN_OK(openat(dir_fd_, "symlink.down", O_RDONLY)); - EXPECT_OPEN_OK(openat(dir_fd_, "symlink.absolute_in", O_RDONLY)); EXPECT_OPEN_OK(openat(dir_fd_, "symlink.absolute_out", O_RDONLY)); EXPECT_OPEN_OK(openat(dir_fd_, "symlink.relative_in", O_RDONLY)); EXPECT_OPEN_OK(openat(dir_fd_, "symlink.relative_out", O_RDONLY)); + EXPECT_OPEN_OK(openat(sub_fd_, "symlink.absolute_in", O_RDONLY)); EXPECT_OPEN_OK(openat(sub_fd_, "symlink.up", O_RDONLY)); // Now make both DFDs into Capsicum capabilities. Index: tests/sys/capsicum/Makefile =================================================================== --- tests/sys/capsicum/Makefile +++ tests/sys/capsicum/Makefile @@ -1,5 +1,7 @@ # $FreeBSD$ +.include + TESTSDIR= ${TESTSBASE}/sys/capsicum ATF_TESTS_C+= bindat_connectat @@ -7,6 +9,49 @@ CFLAGS+= -I${SRCTOP}/tests +.if ${MK_GOOGLETEST} != no + +.PATH: ${SRCTOP}/contrib/capsicum-test + +GTESTS+= capsicum-test + +SRCS.capsicum-test+= \ + capsicum-test-main.cc \ + capsicum-test.cc \ + capability-fd.cc \ + fexecve.cc \ + procdesc.cc \ + capmode.cc \ + fcntl.cc \ + ioctl.cc \ + openat.cc \ + sysctl.cc \ + select.cc \ + mqueue.cc \ + socket.cc \ + sctp.cc \ + capability-fd-pair.cc \ + overhead.cc \ + rename.cc + +LIBADD.capsicum-test+= gtest pthread +TEST_METADATA.capsicum-test= required_user="unprivileged" + +.for p in mini-me mini-me.noexec mini-me.setuid +PROGS+= $p +NO_SHARED.$p= +SRCS.$p= mini-me.c +.endfor + +BINDIR= ${TESTSDIR} + +BINMODE.mini-me.noexec= ${NOBINMODE} +BINMODE.mini-me.setuid= 4555 + +WARNS.capsicum-test= 3 + +.endif + WARNS?= 6 .include