Update capsicum-test to 7707222b46abe52d18fd4fbb76115ffdb3e6f74b
Using the following steps: ``` git worktree add ../freebsd-vendor-capsicum-test freebsd/vendor/google/capsicum-test cd ../freebsd-vendor-capsicum-test git remote add upstream-capsicum-test https://github.com/google/capsicum-test git fetch upstream-capsicum-test git checkout -f upstream-capsicum-test/dev -- git rm -rf autoconf/ casper/ gtest-1.10.0/ libcaprights/ git commit ```
This commit is contained in:
parent
33d7e3ee78
commit
b6973c8f4a
@ -1085,8 +1085,6 @@ TEST(Capability, SyscallAt) {
|
|||||||
cap_rights_init(&r_no_mkdir, CAP_READ, CAP_LOOKUP, CAP_UNLINKAT, CAP_MKFIFOAT);
|
cap_rights_init(&r_no_mkdir, CAP_READ, CAP_LOOKUP, CAP_UNLINKAT, CAP_MKFIFOAT);
|
||||||
cap_rights_t r_no_mkfifo;
|
cap_rights_t r_no_mkfifo;
|
||||||
cap_rights_init(&r_no_mkfifo, CAP_READ, CAP_LOOKUP, CAP_UNLINKAT, CAP_MKDIRAT);
|
cap_rights_init(&r_no_mkfifo, CAP_READ, CAP_LOOKUP, CAP_UNLINKAT, CAP_MKDIRAT);
|
||||||
cap_rights_t r_no_mknod;
|
|
||||||
cap_rights_init(&r_no_mknod, CAP_READ, CAP_LOOKUP, CAP_UNLINKAT, CAP_MKDIRAT);
|
|
||||||
cap_rights_t r_create;
|
cap_rights_t r_create;
|
||||||
cap_rights_init(&r_create, CAP_READ, CAP_LOOKUP, CAP_CREATE);
|
cap_rights_init(&r_create, CAP_READ, CAP_LOOKUP, CAP_CREATE);
|
||||||
cap_rights_t r_bind;
|
cap_rights_t r_bind;
|
||||||
@ -1106,9 +1104,6 @@ TEST(Capability, SyscallAt) {
|
|||||||
int cap_dfd_no_mkfifo = dup(dfd);
|
int cap_dfd_no_mkfifo = dup(dfd);
|
||||||
EXPECT_OK(cap_dfd_no_mkfifo);
|
EXPECT_OK(cap_dfd_no_mkfifo);
|
||||||
EXPECT_OK(cap_rights_limit(cap_dfd_no_mkfifo, &r_no_mkfifo));
|
EXPECT_OK(cap_rights_limit(cap_dfd_no_mkfifo, &r_no_mkfifo));
|
||||||
int cap_dfd_no_mknod = dup(dfd);
|
|
||||||
EXPECT_OK(cap_dfd_no_mknod);
|
|
||||||
EXPECT_OK(cap_rights_limit(cap_dfd_no_mknod, &r_no_mknod));
|
|
||||||
int cap_dfd_create = dup(dfd);
|
int cap_dfd_create = dup(dfd);
|
||||||
EXPECT_OK(cap_dfd_create);
|
EXPECT_OK(cap_dfd_create);
|
||||||
EXPECT_OK(cap_rights_limit(cap_dfd_create, &r_create));
|
EXPECT_OK(cap_rights_limit(cap_dfd_create, &r_create));
|
||||||
@ -1148,24 +1143,7 @@ TEST(Capability, SyscallAt) {
|
|||||||
unlink(TmpFile("cap_at_topdir/cap_socket"));
|
unlink(TmpFile("cap_at_topdir/cap_socket"));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (getuid() == 0) {
|
|
||||||
// Need CAP_MKNODAT to mknodat(2) a device
|
|
||||||
EXPECT_NOTCAPABLE(mknodat(cap_dfd_no_mknod, "cap_device", S_IFCHR|0755, makedev(99, 123)));
|
|
||||||
unlink(TmpFile("cap_at_topdir/cap_device"));
|
|
||||||
EXPECT_OK(mknodat(cap_dfd_all, "cap_device", S_IFCHR|0755, makedev(99, 123)));
|
|
||||||
unlink(TmpFile("cap_at_topdir/cap_device"));
|
|
||||||
|
|
||||||
// Need CAP_MKFIFOAT to mknodat(2) for a FIFO.
|
|
||||||
EXPECT_NOTCAPABLE(mknodat(cap_dfd_no_mkfifo, "cap_fifo", S_IFIFO|0755, 0));
|
|
||||||
unlink(TmpFile("cap_at_topdir/cap_fifo"));
|
|
||||||
EXPECT_OK(mknodat(cap_dfd_all, "cap_fifo", S_IFIFO|0755, 0));
|
|
||||||
unlink(TmpFile("cap_at_topdir/cap_fifo"));
|
|
||||||
} else {
|
|
||||||
TEST_SKIPPED("requires root (partial)");
|
|
||||||
}
|
|
||||||
|
|
||||||
close(cap_dfd_all);
|
close(cap_dfd_all);
|
||||||
close(cap_dfd_no_mknod);
|
|
||||||
close(cap_dfd_no_mkfifo);
|
close(cap_dfd_no_mkfifo);
|
||||||
close(cap_dfd_no_mkdir);
|
close(cap_dfd_no_mkdir);
|
||||||
close(cap_dfd_no_unlink);
|
close(cap_dfd_no_unlink);
|
||||||
@ -1177,7 +1155,53 @@ TEST(Capability, SyscallAt) {
|
|||||||
rmdir(TmpFile("cap_at_topdir"));
|
rmdir(TmpFile("cap_at_topdir"));
|
||||||
}
|
}
|
||||||
|
|
||||||
FORK_TEST_ON(Capability, ExtendedAttributes, TmpFile("cap_extattr")) {
|
TEST(Capability, SyscallAtIfRoot) {
|
||||||
|
GTEST_SKIP_IF_NOT_ROOT();
|
||||||
|
int rc = mkdir(TmpFile("cap_at_topdir"), 0755);
|
||||||
|
EXPECT_OK(rc);
|
||||||
|
if (rc < 0 && errno != EEXIST) return;
|
||||||
|
|
||||||
|
cap_rights_t r_all;
|
||||||
|
cap_rights_init(&r_all, CAP_READ, CAP_LOOKUP, CAP_MKNODAT, CAP_UNLINKAT, CAP_MKDIRAT, CAP_MKFIFOAT);
|
||||||
|
cap_rights_t r_no_mkfifo;
|
||||||
|
cap_rights_init(&r_no_mkfifo, CAP_READ, CAP_LOOKUP, CAP_UNLINKAT, CAP_MKDIRAT);
|
||||||
|
cap_rights_t r_no_mknod;
|
||||||
|
cap_rights_init(&r_no_mknod, CAP_READ, CAP_LOOKUP, CAP_UNLINKAT, CAP_MKDIRAT);
|
||||||
|
|
||||||
|
int dfd = open(TmpFile("cap_at_topdir"), O_RDONLY);
|
||||||
|
EXPECT_OK(dfd);
|
||||||
|
int cap_dfd_all = dup(dfd);
|
||||||
|
EXPECT_OK(cap_dfd_all);
|
||||||
|
EXPECT_OK(cap_rights_limit(cap_dfd_all, &r_all));
|
||||||
|
int cap_dfd_no_mkfifo = dup(dfd);
|
||||||
|
EXPECT_OK(cap_dfd_no_mkfifo);
|
||||||
|
EXPECT_OK(cap_rights_limit(cap_dfd_no_mkfifo, &r_no_mkfifo));
|
||||||
|
int cap_dfd_no_mknod = dup(dfd);
|
||||||
|
EXPECT_OK(cap_dfd_no_mknod);
|
||||||
|
EXPECT_OK(cap_rights_limit(cap_dfd_no_mknod, &r_no_mknod));
|
||||||
|
|
||||||
|
// Need CAP_MKNODAT to mknodat(2) a device
|
||||||
|
EXPECT_NOTCAPABLE(mknodat(cap_dfd_no_mknod, "cap_device", S_IFCHR|0755, makedev(99, 123)));
|
||||||
|
unlink(TmpFile("cap_at_topdir/cap_device"));
|
||||||
|
EXPECT_OK(mknodat(cap_dfd_all, "cap_device", S_IFCHR|0755, makedev(99, 123)));
|
||||||
|
unlink(TmpFile("cap_at_topdir/cap_device"));
|
||||||
|
|
||||||
|
// Need CAP_MKFIFOAT to mknodat(2) for a FIFO.
|
||||||
|
EXPECT_NOTCAPABLE(mknodat(cap_dfd_no_mkfifo, "cap_fifo", S_IFIFO|0755, 0));
|
||||||
|
unlink(TmpFile("cap_at_topdir/cap_fifo"));
|
||||||
|
EXPECT_OK(mknodat(cap_dfd_all, "cap_fifo", S_IFIFO|0755, 0));
|
||||||
|
unlink(TmpFile("cap_at_topdir/cap_fifo"));
|
||||||
|
|
||||||
|
close(cap_dfd_all);
|
||||||
|
close(cap_dfd_no_mknod);
|
||||||
|
close(cap_dfd_no_mkfifo);
|
||||||
|
close(dfd);
|
||||||
|
|
||||||
|
// Tidy up.
|
||||||
|
rmdir(TmpFile("cap_at_topdir"));
|
||||||
|
}
|
||||||
|
|
||||||
|
FORK_TEST_ON(Capability, ExtendedAttributesIfAvailable, TmpFile("cap_extattr")) {
|
||||||
int fd = open(TmpFile("cap_extattr"), O_RDONLY|O_CREAT, 0644);
|
int fd = open(TmpFile("cap_extattr"), O_RDONLY|O_CREAT, 0644);
|
||||||
EXPECT_OK(fd);
|
EXPECT_OK(fd);
|
||||||
|
|
||||||
@ -1185,9 +1209,8 @@ FORK_TEST_ON(Capability, ExtendedAttributes, TmpFile("cap_extattr")) {
|
|||||||
int rc = fgetxattr_(fd, "user.capsicumtest", buffer, sizeof(buffer));
|
int rc = fgetxattr_(fd, "user.capsicumtest", buffer, sizeof(buffer));
|
||||||
if (rc < 0 && errno == ENOTSUP) {
|
if (rc < 0 && errno == ENOTSUP) {
|
||||||
// Need user_xattr mount option for non-root users on Linux
|
// Need user_xattr mount option for non-root users on Linux
|
||||||
TEST_SKIPPED("/tmp doesn't support extended attributes");
|
|
||||||
close(fd);
|
close(fd);
|
||||||
return;
|
GTEST_SKIP() << "/tmp doesn't support extended attributes";
|
||||||
}
|
}
|
||||||
|
|
||||||
cap_rights_t r_rws;
|
cap_rights_t r_rws;
|
||||||
@ -1278,8 +1301,8 @@ TEST(Capability, PipeUnseekable) {
|
|||||||
close(fds[1]);
|
close(fds[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Capability, NoBypassDAC) {
|
TEST(Capability, NoBypassDACIfRoot) {
|
||||||
REQUIRE_ROOT();
|
GTEST_SKIP_IF_NOT_ROOT();
|
||||||
int fd = open(TmpFile("cap_root_owned"), O_RDONLY|O_CREAT, 0644);
|
int fd = open(TmpFile("cap_root_owned"), O_RDONLY|O_CREAT, 0644);
|
||||||
EXPECT_OK(fd);
|
EXPECT_OK(fd);
|
||||||
cap_rights_t rights;
|
cap_rights_t rights;
|
||||||
@ -1289,7 +1312,10 @@ TEST(Capability, NoBypassDAC) {
|
|||||||
pid_t child = fork();
|
pid_t child = fork();
|
||||||
if (child == 0) {
|
if (child == 0) {
|
||||||
// Child: change uid to a lesser being
|
// Child: change uid to a lesser being
|
||||||
setuid(other_uid);
|
ASSERT_NE(0u, other_uid) << "other_uid not initialized correctly, "
|
||||||
|
"please pass the -u <uid> flag.";
|
||||||
|
EXPECT_EQ(0, setuid(other_uid));
|
||||||
|
EXPECT_EQ(other_uid, getuid());
|
||||||
// Attempt to fchmod the file, and fail.
|
// Attempt to fchmod the file, and fail.
|
||||||
// Having CAP_FCHMOD doesn't bypass the need to comply with DAC policy.
|
// Having CAP_FCHMOD doesn't bypass the need to comply with DAC policy.
|
||||||
int rc = fchmod(fd, 0666);
|
int rc = fchmod(fd, 0666);
|
||||||
|
@ -2,16 +2,25 @@
|
|||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
#include <sys/vfs.h>
|
#include <sys/vfs.h>
|
||||||
#include <linux/magic.h>
|
#include <linux/magic.h>
|
||||||
|
#elif defined(__FreeBSD__)
|
||||||
|
#include <sys/sysctl.h>
|
||||||
#endif
|
#endif
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <libgen.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
#include "capsicum-test.h"
|
#include "capsicum-test.h"
|
||||||
|
|
||||||
|
// For versions of googletest that lack GTEST_SKIP.
|
||||||
|
#ifndef GTEST_SKIP
|
||||||
|
#define GTEST_SKIP GTEST_FAIL
|
||||||
|
#endif
|
||||||
|
|
||||||
std::string tmpdir;
|
std::string tmpdir;
|
||||||
|
|
||||||
class SetupEnvironment : public ::testing::Environment
|
class SetupEnvironment : public ::testing::Environment
|
||||||
@ -19,6 +28,7 @@ class SetupEnvironment : public ::testing::Environment
|
|||||||
public:
|
public:
|
||||||
SetupEnvironment() : teardown_tmpdir_(false) {}
|
SetupEnvironment() : teardown_tmpdir_(false) {}
|
||||||
void SetUp() override {
|
void SetUp() override {
|
||||||
|
CheckCapsicumSupport();
|
||||||
if (tmpdir.empty()) {
|
if (tmpdir.empty()) {
|
||||||
std::cerr << "Generating temporary directory root: ";
|
std::cerr << "Generating temporary directory root: ";
|
||||||
CreateTemporaryRoot();
|
CreateTemporaryRoot();
|
||||||
@ -27,6 +37,31 @@ public:
|
|||||||
}
|
}
|
||||||
std::cerr << tmpdir << std::endl;
|
std::cerr << tmpdir << std::endl;
|
||||||
}
|
}
|
||||||
|
void CheckCapsicumSupport() {
|
||||||
|
#ifdef __FreeBSD__
|
||||||
|
int rc;
|
||||||
|
bool trap_enotcap_enabled;
|
||||||
|
size_t 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.";
|
||||||
|
}
|
||||||
|
// If this OID is enabled, it will send SIGTRAP to the process when
|
||||||
|
// `ENOTCAPABLE` is returned.
|
||||||
|
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() << "Debug sysctl, " << oid << ", enabled. "
|
||||||
|
<< "Skipping tests because its enablement invalidates the "
|
||||||
|
<< "test results.";
|
||||||
|
}
|
||||||
|
#endif /* FreeBSD */
|
||||||
|
}
|
||||||
void CreateTemporaryRoot() {
|
void CreateTemporaryRoot() {
|
||||||
char *tmpdir_name = tempnam(nullptr, "cptst");
|
char *tmpdir_name = tempnam(nullptr, "cptst");
|
||||||
|
|
||||||
@ -47,7 +82,33 @@ private:
|
|||||||
bool teardown_tmpdir_;
|
bool teardown_tmpdir_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::string capsicum_test_bindir;
|
||||||
|
|
||||||
|
// Adds a directory to $PATH.
|
||||||
|
static void AddDirectoryToPath(const char *dir) {
|
||||||
|
char *new_path, *old_path;
|
||||||
|
|
||||||
|
old_path = getenv("PATH");
|
||||||
|
assert(old_path);
|
||||||
|
|
||||||
|
assert(asprintf(&new_path, "%s:%s", dir, old_path) > 0);
|
||||||
|
assert(setenv("PATH", new_path, 1) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
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 *program_name;
|
||||||
|
|
||||||
|
// Copy argv[0], so dirname can do an in-place manipulation of the buffer's
|
||||||
|
// contents.
|
||||||
|
program_name = strdup(argv[0]);
|
||||||
|
assert(program_name);
|
||||||
|
capsicum_test_bindir = std::string(dirname(program_name));
|
||||||
|
free(program_name);
|
||||||
|
|
||||||
|
AddDirectoryToPath(capsicum_test_bindir.c_str());
|
||||||
|
|
||||||
::testing::InitGoogleTest(&argc, argv);
|
::testing::InitGoogleTest(&argc, argv);
|
||||||
for (int ii = 1; ii < argc; ii++) {
|
for (int ii = 1; ii < argc; ii++) {
|
||||||
if (strcmp(argv[ii], "-v") == 0) {
|
if (strcmp(argv[ii], "-v") == 0) {
|
||||||
@ -95,7 +156,5 @@ int main(int argc, char* argv[]) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
testing::AddGlobalTestEnvironment(new SetupEnvironment());
|
testing::AddGlobalTestEnvironment(new SetupEnvironment());
|
||||||
int rc = RUN_ALL_TESTS();
|
return RUN_ALL_TESTS();
|
||||||
ShowSkippedTests(std::cerr);
|
|
||||||
return rc;
|
|
||||||
}
|
}
|
||||||
|
@ -76,27 +76,3 @@ char ProcessState(int pid) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef std::vector<std::string> TestList;
|
|
||||||
typedef std::map<std::string, TestList*> SkippedTestMap;
|
|
||||||
static SkippedTestMap skipped_tests;
|
|
||||||
void TestSkipped(const char *testcase, const char *test, const std::string& reason) {
|
|
||||||
if (skipped_tests.find(reason) == skipped_tests.end()) {
|
|
||||||
skipped_tests[reason] = new TestList;
|
|
||||||
}
|
|
||||||
std::string testname(testcase);
|
|
||||||
testname += ".";
|
|
||||||
testname += test;
|
|
||||||
skipped_tests[reason]->push_back(testname);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShowSkippedTests(std::ostream& os) {
|
|
||||||
for (SkippedTestMap::iterator skiplist = skipped_tests.begin();
|
|
||||||
skiplist != skipped_tests.end(); ++skiplist) {
|
|
||||||
os << "Following tests were skipped because: " << skiplist->first << std::endl;
|
|
||||||
for (size_t ii = 0; ii < skiplist->second->size(); ++ii) {
|
|
||||||
const std::string& testname((*skiplist->second)[ii]);
|
|
||||||
os << " " << testname << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include <ios>
|
#include <ios>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
@ -75,7 +76,7 @@ const char *TmpFile(const char *pathname);
|
|||||||
} \
|
} \
|
||||||
} else if (pid > 0) { \
|
} else if (pid > 0) { \
|
||||||
int rc, status; \
|
int rc, status; \
|
||||||
int remaining_us = 10000000; \
|
int remaining_us = 30000000; \
|
||||||
while (remaining_us > 0) { \
|
while (remaining_us > 0) { \
|
||||||
status = 0; \
|
status = 0; \
|
||||||
rc = waitpid(pid, &status, WNOHANG); \
|
rc = waitpid(pid, &status, WNOHANG); \
|
||||||
@ -169,12 +170,14 @@ const char *TmpFile(const char *pathname);
|
|||||||
} else { \
|
} else { \
|
||||||
EXPECT_SYSCALL_FAIL(E_NO_TRAVERSE_CAPABILITY, result); \
|
EXPECT_SYSCALL_FAIL(E_NO_TRAVERSE_CAPABILITY, result); \
|
||||||
} \
|
} \
|
||||||
|
if (result >= 0) { close(result); } \
|
||||||
} while (0)
|
} while (0)
|
||||||
#else
|
#else
|
||||||
#define EXPECT_OPENAT_FAIL_TRAVERSAL(fd, path, flags) \
|
#define EXPECT_OPENAT_FAIL_TRAVERSAL(fd, path, flags) \
|
||||||
do { \
|
do { \
|
||||||
const int result = openat((fd), (path), (flags)); \
|
const int result = openat((fd), (path), (flags)); \
|
||||||
EXPECT_SYSCALL_FAIL(E_NO_TRAVERSE_CAPABILITY, result); \
|
EXPECT_SYSCALL_FAIL(E_NO_TRAVERSE_CAPABILITY, result); \
|
||||||
|
if (result >= 0) { close(result); } \
|
||||||
} while (0)
|
} while (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -241,20 +244,10 @@ char ProcessState(int pid);
|
|||||||
#define EXPECT_PID_ZOMBIE(pid) EXPECT_PID_REACHES_STATES(pid, 'Z', 'Z');
|
#define EXPECT_PID_ZOMBIE(pid) EXPECT_PID_REACHES_STATES(pid, 'Z', 'Z');
|
||||||
#define EXPECT_PID_GONE(pid) EXPECT_PID_REACHES_STATES(pid, '\0', '\0');
|
#define EXPECT_PID_GONE(pid) EXPECT_PID_REACHES_STATES(pid, '\0', '\0');
|
||||||
|
|
||||||
void ShowSkippedTests(std::ostream& os);
|
|
||||||
void TestSkipped(const char *testcase, const char *test, const std::string& reason);
|
|
||||||
#define TEST_SKIPPED(reason) \
|
|
||||||
do { \
|
|
||||||
const ::testing::TestInfo* const info = ::testing::UnitTest::GetInstance()->current_test_info(); \
|
|
||||||
std::cerr << "Skipping " << info->test_case_name() << "::" << info->name() << " because: " << reason << std::endl; \
|
|
||||||
TestSkipped(info->test_case_name(), info->name(), reason); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
// Mark a test that can only be run as root.
|
// Mark a test that can only be run as root.
|
||||||
#define REQUIRE_ROOT() \
|
#define GTEST_SKIP_IF_NOT_ROOT() \
|
||||||
if (getuid() != 0) { \
|
if (getuid() != 0) { GTEST_SKIP() << "requires root"; }
|
||||||
TEST_SKIPPED("requires root"); \
|
|
||||||
return; \
|
extern std::string capsicum_test_bindir;
|
||||||
}
|
|
||||||
|
|
||||||
#endif // CAPSICUM_TEST_H
|
#endif // CAPSICUM_TEST_H
|
||||||
|
128
fexecve.cc
128
fexecve.cc
@ -1,12 +1,12 @@
|
|||||||
#include <errno.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <unistd.h>
|
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
@ -14,41 +14,76 @@
|
|||||||
#include "capsicum.h"
|
#include "capsicum.h"
|
||||||
#include "capsicum-test.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.
|
// 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};
|
static char* null_envp[] = {NULL};
|
||||||
|
|
||||||
class Execve : public ::testing::Test {
|
class Execve : public ::testing::Test {
|
||||||
public:
|
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) {
|
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:
|
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_;
|
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) {
|
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.
|
// Should not reach here, exec() takes over.
|
||||||
EXPECT_TRUE(!"fexecve() should never return");
|
EXPECT_TRUE(!"fexecve() should never return");
|
||||||
}
|
}
|
||||||
|
|
||||||
FORK_TEST_F(Execve, InCapMode) {
|
FORK_TEST_F(Execve, InCapMode) {
|
||||||
EXPECT_OK(cap_enter());
|
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.
|
// Should not reach here, exec() takes over.
|
||||||
EXPECT_TRUE(!"fexecve() should never return");
|
EXPECT_TRUE(!"fexecve() should never return");
|
||||||
}
|
}
|
||||||
@ -60,7 +95,7 @@ FORK_TEST_F(Execve, FailWithoutCap) {
|
|||||||
cap_rights_t rights;
|
cap_rights_t rights;
|
||||||
cap_rights_init(&rights, 0);
|
cap_rights_init(&rights, 0);
|
||||||
EXPECT_OK(cap_rights_limit(cap_fd, &rights));
|
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);
|
EXPECT_EQ(ENOTCAPABLE, errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,59 +108,53 @@ FORK_TEST_F(Execve, SucceedWithCap) {
|
|||||||
// rights -- just CAP_FEXECVE|CAP_READ or CAP_FEXECVE would be preferable.
|
// rights -- just CAP_FEXECVE|CAP_READ or CAP_FEXECVE would be preferable.
|
||||||
cap_rights_init(&rights, CAP_FEXECVE, CAP_LOOKUP, CAP_READ);
|
cap_rights_init(&rights, CAP_FEXECVE, CAP_LOOKUP, CAP_READ);
|
||||||
EXPECT_OK(cap_rights_limit(cap_fd, &rights));
|
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.
|
// Should not reach here, exec() takes over.
|
||||||
EXPECT_TRUE(!"fexecve() should have succeeded");
|
EXPECT_TRUE(!"fexecve() should have succeeded");
|
||||||
}
|
}
|
||||||
|
|
||||||
FORK_TEST(Fexecve, ExecutePermissionCheck) {
|
FORK_TEST_F(Fexecve, ExecutePermissionCheck) {
|
||||||
int fd = open(EXEC_PROG_NOEXEC, O_RDONLY);
|
int fd = open(exec_prog_noexec_.c_str(), O_RDONLY);
|
||||||
EXPECT_OK(fd);
|
EXPECT_OK(fd);
|
||||||
if (fd >= 0) {
|
if (fd >= 0) {
|
||||||
struct stat data;
|
struct stat data;
|
||||||
EXPECT_OK(fstat(fd, &data));
|
EXPECT_OK(fstat(fd, &data));
|
||||||
EXPECT_EQ((mode_t)0, data.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH));
|
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);
|
EXPECT_EQ(EACCES, errno);
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FORK_TEST(Fexecve, SetuidIgnored) {
|
FORK_TEST_F(Fexecve, SetuidIgnoredIfNonRoot) {
|
||||||
if (geteuid() == 0) {
|
if (geteuid() == 0) {
|
||||||
TEST_SKIPPED("requires non-root");
|
GTEST_SKIP() << "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(fd);
|
||||||
EXPECT_OK(cap_enter());
|
EXPECT_OK(cap_enter());
|
||||||
if (fd >= 0) {
|
if (fd >= 0) {
|
||||||
struct stat data;
|
struct stat data;
|
||||||
EXPECT_OK(fstat(fd, &data));
|
EXPECT_OK(fstat(fd, &data));
|
||||||
EXPECT_EQ((mode_t)S_ISUID, data.st_mode & S_ISUID);
|
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.
|
// Should not reach here, exec() takes over.
|
||||||
EXPECT_TRUE(!"fexecve() should have succeeded");
|
EXPECT_TRUE(!"fexecve() should have succeeded");
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FORK_TEST(Fexecve, ExecveFailure) {
|
FORK_TEST_F(Fexecve, ExecveFailure) {
|
||||||
EXPECT_OK(cap_enter());
|
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);
|
EXPECT_EQ(ECAPMODE, errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
FORK_TEST_ON(Fexecve, CapModeScriptFail, TmpFile("cap_sh_script")) {
|
FORK_TEST_F(FexecveWithScript, CapModeScriptFail) {
|
||||||
// First, build an executable shell script
|
int fd;
|
||||||
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);
|
|
||||||
|
|
||||||
// Open the script file, with CAP_FEXECVE rights.
|
// 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_t rights;
|
||||||
cap_rights_init(&rights, CAP_FEXECVE, CAP_READ, CAP_SEEK);
|
cap_rights_init(&rights, CAP_FEXECVE, CAP_READ, CAP_SEEK);
|
||||||
EXPECT_OK(cap_rights_limit(fd, &rights));
|
EXPECT_OK(cap_rights_limit(fd, &rights));
|
||||||
@ -133,12 +162,17 @@ FORK_TEST_ON(Fexecve, CapModeScriptFail, TmpFile("cap_sh_script")) {
|
|||||||
EXPECT_OK(cap_enter()); // Enter capability mode
|
EXPECT_OK(cap_enter()); // Enter capability mode
|
||||||
|
|
||||||
// Attempt fexecve; should fail, because "/bin/sh" is inaccessible.
|
// 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
|
#ifdef HAVE_EXECVEAT
|
||||||
TEST(Execveat, NoUpwardTraversal) {
|
class Execveat : public Execve {
|
||||||
char *abspath = realpath(EXEC_PROG, NULL);
|
public:
|
||||||
|
Execveat() : Execve() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(Execveat, NoUpwardTraversal) {
|
||||||
|
char *abspath = realpath(exec_prog_.c_str(), NULL);
|
||||||
char cwd[1024];
|
char cwd[1024];
|
||||||
getcwd(cwd, sizeof(cwd));
|
getcwd(cwd, sizeof(cwd));
|
||||||
|
|
||||||
@ -148,9 +182,9 @@ TEST(Execveat, NoUpwardTraversal) {
|
|||||||
EXPECT_OK(cap_enter()); // Enter capability mode.
|
EXPECT_OK(cap_enter()); // Enter capability mode.
|
||||||
// Can't execveat() an absolute path, even relative to a dfd.
|
// Can't execveat() an absolute path, even relative to a dfd.
|
||||||
EXPECT_SYSCALL_FAIL(ECAPMODE,
|
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,
|
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 ("../<dir>/./<exe>").
|
// Can't execveat() a relative path ("../<dir>/./<exe>").
|
||||||
char *p = cwd + strlen(cwd);
|
char *p = cwd + strlen(cwd);
|
||||||
@ -158,9 +192,9 @@ TEST(Execveat, NoUpwardTraversal) {
|
|||||||
char buffer[1024] = "../";
|
char buffer[1024] = "../";
|
||||||
strcat(buffer, ++p);
|
strcat(buffer, ++p);
|
||||||
strcat(buffer, "/");
|
strcat(buffer, "/");
|
||||||
strcat(buffer, EXEC_PROG);
|
strcat(buffer, exec_prog_.c_str());
|
||||||
EXPECT_SYSCALL_FAIL(E_NO_TRAVERSE_CAPABILITY,
|
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);
|
exit(HasFailure() ? 99 : 123);
|
||||||
}
|
}
|
||||||
int status;
|
int status;
|
||||||
|
51
linux.cc
51
linux.cc
@ -104,10 +104,9 @@ TEST(Linux, TimerFD) {
|
|||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
FORK_TEST(Linux, SignalFD) {
|
FORK_TEST(Linux, SignalFDIfSingleThreaded) {
|
||||||
if (force_mt) {
|
if (force_mt) {
|
||||||
TEST_SKIPPED("multi-threaded run clashes with signals");
|
GTEST_SKIP() << "multi-threaded run clashes with signals";
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
pid_t me = getpid();
|
pid_t me = getpid();
|
||||||
sigset_t mask;
|
sigset_t mask;
|
||||||
@ -372,8 +371,8 @@ TEST(Linux, fstatat) {
|
|||||||
|
|
||||||
// fanotify support may not be available at compile-time
|
// fanotify support may not be available at compile-time
|
||||||
#ifdef __NR_fanotify_init
|
#ifdef __NR_fanotify_init
|
||||||
TEST(Linux, fanotify) {
|
TEST(Linux, FanotifyIfRoot) {
|
||||||
REQUIRE_ROOT();
|
GTEST_SKIP_IF_NOT_ROOT();
|
||||||
int fa_fd = fanotify_init(FAN_CLASS_NOTIF, O_RDWR);
|
int fa_fd = fanotify_init(FAN_CLASS_NOTIF, O_RDWR);
|
||||||
EXPECT_OK(fa_fd);
|
EXPECT_OK(fa_fd);
|
||||||
if (fa_fd < 0) return; // May not be enabled
|
if (fa_fd < 0) return; // May not be enabled
|
||||||
@ -577,7 +576,7 @@ TEST(Linux, inotify) {
|
|||||||
unlink(TmpFile("cap_inotify"));
|
unlink(TmpFile("cap_inotify"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Linux, ArchChange) {
|
TEST(Linux, ArchChangeIfAvailable) {
|
||||||
const char* prog_candidates[] = {"./mini-me.32", "./mini-me.x32", "./mini-me.64"};
|
const char* prog_candidates[] = {"./mini-me.32", "./mini-me.x32", "./mini-me.64"};
|
||||||
const char* progs[] = {NULL, NULL, NULL};
|
const char* progs[] = {NULL, NULL, NULL};
|
||||||
char* argv_pass[] = {(char*)"to-come", (char*)"--capmode", NULL};
|
char* argv_pass[] = {(char*)"to-come", (char*)"--capmode", NULL};
|
||||||
@ -593,8 +592,7 @@ TEST(Linux, ArchChange) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (count == 0) {
|
if (count == 0) {
|
||||||
TEST_SKIPPED("no different-architecture programs available");
|
GTEST_SKIP() << "no different-architecture programs available";
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int ii = 0; ii < count; ii++) {
|
for (int ii = 0; ii < count; ii++) {
|
||||||
@ -617,8 +615,8 @@ TEST(Linux, ArchChange) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FORK_TEST(Linux, Namespace) {
|
FORK_TEST(Linux, NamespaceIfRoot) {
|
||||||
REQUIRE_ROOT();
|
GTEST_SKIP_IF_NOT_ROOT();
|
||||||
pid_t me = getpid_();
|
pid_t me = getpid_();
|
||||||
|
|
||||||
// Create a new UTS namespace.
|
// Create a new UTS namespace.
|
||||||
@ -758,9 +756,9 @@ static int ChildFunc(void *arg) {
|
|||||||
#define STACK_SIZE (1024 * 1024)
|
#define STACK_SIZE (1024 * 1024)
|
||||||
static char child_stack[STACK_SIZE];
|
static char child_stack[STACK_SIZE];
|
||||||
|
|
||||||
// TODO(drysdale): fork into a user namespace first so REQUIRE_ROOT can be removed.
|
// TODO(drysdale): fork into a user namespace first so GTEST_SKIP_IF_NOT_ROOT can be removed.
|
||||||
TEST(Linux, PidNamespacePdFork) {
|
TEST(Linux, PidNamespacePdForkIfRoot) {
|
||||||
REQUIRE_ROOT();
|
GTEST_SKIP_IF_NOT_ROOT();
|
||||||
// Pass process descriptors in both directions across a PID namespace boundary.
|
// Pass process descriptors in both directions across a PID namespace boundary.
|
||||||
// pdfork() off a child before we start, holding its process descriptor in a global
|
// pdfork() off a child before we start, holding its process descriptor in a global
|
||||||
// variable that's accessible to children.
|
// variable that's accessible to children.
|
||||||
@ -871,8 +869,8 @@ int NSInit(void *data) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Linux, DeadNSInit) {
|
TEST(Linux, DeadNSInitIfRoot) {
|
||||||
REQUIRE_ROOT();
|
GTEST_SKIP_IF_NOT_ROOT();
|
||||||
|
|
||||||
// Prepare sockets to communicate with child process.
|
// Prepare sockets to communicate with child process.
|
||||||
EXPECT_OK(socketpair(AF_UNIX, SOCK_STREAM, 0, shared_sock_fds));
|
EXPECT_OK(socketpair(AF_UNIX, SOCK_STREAM, 0, shared_sock_fds));
|
||||||
@ -916,8 +914,8 @@ TEST(Linux, DeadNSInit) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Linux, DeadNSInit2) {
|
TEST(Linux, DeadNSInit2IfRoot) {
|
||||||
REQUIRE_ROOT();
|
GTEST_SKIP_IF_NOT_ROOT();
|
||||||
|
|
||||||
// Prepare sockets to communicate with child process.
|
// Prepare sockets to communicate with child process.
|
||||||
EXPECT_OK(socketpair(AF_UNIX, SOCK_STREAM, 0, shared_sock_fds));
|
EXPECT_OK(socketpair(AF_UNIX, SOCK_STREAM, 0, shared_sock_fds));
|
||||||
@ -1188,7 +1186,7 @@ TEST(Linux, AIO) {
|
|||||||
#ifndef KCMP_FILE
|
#ifndef KCMP_FILE
|
||||||
#define KCMP_FILE 0
|
#define KCMP_FILE 0
|
||||||
#endif
|
#endif
|
||||||
TEST(Linux, Kcmp) {
|
TEST(Linux, KcmpIfAvailable) {
|
||||||
// This requires CONFIG_CHECKPOINT_RESTORE in kernel config.
|
// This requires CONFIG_CHECKPOINT_RESTORE in kernel config.
|
||||||
int fd = open("/etc/passwd", O_RDONLY);
|
int fd = open("/etc/passwd", O_RDONLY);
|
||||||
EXPECT_OK(fd);
|
EXPECT_OK(fd);
|
||||||
@ -1197,8 +1195,7 @@ TEST(Linux, Kcmp) {
|
|||||||
errno = 0;
|
errno = 0;
|
||||||
int rc = syscall(__NR_kcmp, parent, parent, KCMP_FILE, fd, fd);
|
int rc = syscall(__NR_kcmp, parent, parent, KCMP_FILE, fd, fd);
|
||||||
if (rc == -1 && errno == ENOSYS) {
|
if (rc == -1 && errno == ENOSYS) {
|
||||||
TEST_SKIPPED("kcmp(2) gives -ENOSYS");
|
GTEST_SKIP() << "kcmp(2) gives -ENOSYS";
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pid_t child = fork();
|
pid_t child = fork();
|
||||||
@ -1362,8 +1359,8 @@ TEST(Linux, InvalidRightsSyscall) {
|
|||||||
unlink(TmpFile("cap_invalid_rights"));
|
unlink(TmpFile("cap_invalid_rights"));
|
||||||
}
|
}
|
||||||
|
|
||||||
FORK_TEST_ON(Linux, OpenByHandleAt, TmpFile("cap_openbyhandle_testfile")) {
|
FORK_TEST_ON(Linux, OpenByHandleAtIfRoot, TmpFile("cap_openbyhandle_testfile")) {
|
||||||
REQUIRE_ROOT();
|
GTEST_SKIP_IF_NOT_ROOT();
|
||||||
int dir = open(tmpdir.c_str(), O_RDONLY);
|
int dir = open(tmpdir.c_str(), O_RDONLY);
|
||||||
EXPECT_OK(dir);
|
EXPECT_OK(dir);
|
||||||
int fd = openat(dir, "cap_openbyhandle_testfile", O_RDWR|O_CREAT, 0644);
|
int fd = openat(dir, "cap_openbyhandle_testfile", O_RDWR|O_CREAT, 0644);
|
||||||
@ -1380,8 +1377,9 @@ FORK_TEST_ON(Linux, OpenByHandleAt, TmpFile("cap_openbyhandle_testfile")) {
|
|||||||
fd = open_by_handle_at(dir, fhandle, O_RDONLY);
|
fd = open_by_handle_at(dir, fhandle, O_RDONLY);
|
||||||
EXPECT_OK(fd);
|
EXPECT_OK(fd);
|
||||||
char buffer[200];
|
char buffer[200];
|
||||||
EXPECT_OK(read(fd, buffer, 199));
|
ssize_t len = read(fd, buffer, 199);
|
||||||
EXPECT_EQ(std::string(message), std::string(buffer));
|
EXPECT_OK(len);
|
||||||
|
EXPECT_EQ(std::string(message), std::string(buffer, len));
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
||||||
// Cannot issue open_by_handle_at after entering capability mode.
|
// Cannot issue open_by_handle_at after entering capability mode.
|
||||||
@ -1423,11 +1421,10 @@ int memfd_create_(const char *name, unsigned int flags) {
|
|||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)
|
||||||
#include <linux/memfd.h> // Requires 3.17 kernel
|
#include <linux/memfd.h> // Requires 3.17 kernel
|
||||||
TEST(Linux, MemFDDeathTest) {
|
TEST(Linux, MemFDDeathTestIfAvailable) {
|
||||||
int memfd = memfd_create_("capsicum-test", MFD_ALLOW_SEALING);
|
int memfd = memfd_create_("capsicum-test", MFD_ALLOW_SEALING);
|
||||||
if (memfd == -1 && errno == ENOSYS) {
|
if (memfd == -1 && errno == ENOSYS) {
|
||||||
TEST_SKIPPED("memfd_create(2) gives -ENOSYS");
|
GTEST_SKIP() << "memfd_create(2) gives -ENOSYS";
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
const int LEN = 16;
|
const int LEN = 16;
|
||||||
EXPECT_OK(ftruncate(memfd, LEN));
|
EXPECT_OK(ftruncate(memfd, LEN));
|
||||||
|
4
makefile
4
makefile
@ -1,7 +1,7 @@
|
|||||||
all: capsicum-test smoketest mini-me mini-me.noexec mini-me.setuid $(EXTRA_PROGS)
|
all: capsicum-test smoketest mini-me mini-me.noexec mini-me.setuid $(EXTRA_PROGS)
|
||||||
OBJECTS=capsicum-test-main.o capsicum-test.o capability-fd.o fexecve.o procdesc.o capmode.o fcntl.o ioctl.o openat.o sysctl.o select.o mqueue.o socket.o sctp.o capability-fd-pair.o linux.o overhead.o rename.o
|
OBJECTS=capsicum-test-main.o capsicum-test.o capability-fd.o fexecve.o procdesc.o capmode.o fcntl.o ioctl.o openat.o sysctl.o select.o mqueue.o socket.o sctp.o capability-fd-pair.o linux.o overhead.o rename.o
|
||||||
|
|
||||||
GTEST_DIR=gtest-1.8.1
|
GTEST_DIR=gtest-1.10.0
|
||||||
GTEST_INCS=-I$(GTEST_DIR)/include -I$(GTEST_DIR)
|
GTEST_INCS=-I$(GTEST_DIR)/include -I$(GTEST_DIR)
|
||||||
GTEST_FLAGS=-DGTEST_USE_OWN_TR1_TUPLE=1 -DGTEST_HAS_TR1_TUPLE=1
|
GTEST_FLAGS=-DGTEST_USE_OWN_TR1_TUPLE=1 -DGTEST_HAS_TR1_TUPLE=1
|
||||||
CXXFLAGS+=$(ARCHFLAG) -Wall -g $(GTEST_INCS) $(GTEST_FLAGS) --std=c++11
|
CXXFLAGS+=$(ARCHFLAG) -Wall -g $(GTEST_INCS) $(GTEST_FLAGS) --std=c++11
|
||||||
@ -28,7 +28,7 @@ smoketest: $(SMOKETEST_OBJECTS) $(LOCAL_LIBS)
|
|||||||
test: capsicum-test mini-me mini-me.noexec mini-me.setuid $(EXTRA_PROGS)
|
test: capsicum-test mini-me mini-me.noexec mini-me.setuid $(EXTRA_PROGS)
|
||||||
./capsicum-test
|
./capsicum-test
|
||||||
gtest-all.o:
|
gtest-all.o:
|
||||||
$(CXX) $(ARCHFLAG) -I$(GTEST_DIR)/include -I$(GTEST_DIR) $(GTEST_FLAGS) -c ${GTEST_DIR}/src/gtest-all.cc
|
$(CXX) $(CXXFLAGS) $(ARCHFLAG) -I$(GTEST_DIR)/include -I$(GTEST_DIR) $(GTEST_FLAGS) -c ${GTEST_DIR}/src/gtest-all.cc
|
||||||
libgtest.a: gtest-all.o
|
libgtest.a: gtest-all.o
|
||||||
$(AR) -rv libgtest.a gtest-all.o
|
$(AR) -rv libgtest.a gtest-all.o
|
||||||
|
|
||||||
|
@ -28,14 +28,13 @@ void seen_it_done_it(int) {
|
|||||||
invoked = true;
|
invoked = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
FORK_TEST_ON_MQ(PosixMqueue, CapMode, "/cap_mq") {
|
FORK_TEST_ON_MQ(PosixMqueue, CapModeIfMqOpenAvailable, "/cap_mq") {
|
||||||
int mq = mq_open_("/cap_mq", O_RDWR|O_CREAT, 0644, NULL);
|
int mq = mq_open_("/cap_mq", O_RDWR|O_CREAT, 0644, NULL);
|
||||||
// On FreeBSD, turn on message queue support with:
|
// On FreeBSD, turn on message queue support with:
|
||||||
// - 'kldload mqueuefs'
|
// - 'kldload mqueuefs'
|
||||||
// - 'options P1003_1B_MQUEUE' in kernel build config.
|
// - 'options P1003_1B_MQUEUE' in kernel build config.
|
||||||
if (mq < 0 && errno == ENOSYS) {
|
if (mq < 0 && errno == ENOSYS) {
|
||||||
TEST_SKIPPED("mq_open -> -ENOSYS");
|
GTEST_SKIP() << "mq_open -> -ENOSYS";
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
EXPECT_OK(mq);
|
EXPECT_OK(mq);
|
||||||
cap_rights_t r_read;
|
cap_rights_t r_read;
|
||||||
|
@ -519,8 +519,8 @@ TEST_F(PipePdfork, CloseLast) {
|
|||||||
signal(SIGCHLD, original);
|
signal(SIGCHLD, original);
|
||||||
}
|
}
|
||||||
|
|
||||||
FORK_TEST(Pdfork, OtherUser) {
|
FORK_TEST(Pdfork, OtherUserIfRoot) {
|
||||||
REQUIRE_ROOT();
|
GTEST_SKIP_IF_NOT_ROOT();
|
||||||
int pd;
|
int pd;
|
||||||
pid_t pid = pdfork(&pd, 0);
|
pid_t pid = pdfork(&pd, 0);
|
||||||
EXPECT_OK(pid);
|
EXPECT_OK(pid);
|
||||||
@ -531,7 +531,10 @@ FORK_TEST(Pdfork, OtherUser) {
|
|||||||
usleep(100);
|
usleep(100);
|
||||||
|
|
||||||
// Now that the second process has been pdfork()ed, change euid.
|
// Now that the second process has been pdfork()ed, change euid.
|
||||||
setuid(other_uid);
|
ASSERT_NE(0u, other_uid) << "other_uid not initialized correctly, "
|
||||||
|
"please pass the -u <uid> flag.";
|
||||||
|
EXPECT_EQ(0, setuid(other_uid));
|
||||||
|
EXPECT_EQ(other_uid, getuid());
|
||||||
if (verbose) fprintf(stderr, "uid=%d euid=%d\n", getuid(), geteuid());
|
if (verbose) fprintf(stderr, "uid=%d euid=%d\n", getuid(), geteuid());
|
||||||
|
|
||||||
// Fail to kill child with normal PID operation.
|
// Fail to kill child with normal PID operation.
|
||||||
|
Loading…
Reference in New Issue
Block a user