18ac5aef8SEnji Cooper #include <sys/types.h>
28ac5aef8SEnji Cooper #ifdef __linux__
38ac5aef8SEnji Cooper #include <sys/vfs.h>
48ac5aef8SEnji Cooper #include <linux/magic.h>
58ac5aef8SEnji Cooper #elif defined(__FreeBSD__)
68ac5aef8SEnji Cooper #include <sys/sysctl.h>
78ac5aef8SEnji Cooper #endif
88ac5aef8SEnji Cooper #include <ctype.h>
98ac5aef8SEnji Cooper #include <errno.h>
108ac5aef8SEnji Cooper #include <libgen.h>
118ac5aef8SEnji Cooper #include <pwd.h>
128ac5aef8SEnji Cooper #include <stdio.h>
138ac5aef8SEnji Cooper #include <stdlib.h>
148ac5aef8SEnji Cooper #include <unistd.h>
158ac5aef8SEnji Cooper #include <iostream>
168ac5aef8SEnji Cooper #include "gtest/gtest.h"
178ac5aef8SEnji Cooper #include "capsicum-test.h"
188ac5aef8SEnji Cooper
198ac5aef8SEnji Cooper // For versions of googletest that lack GTEST_SKIP.
208ac5aef8SEnji Cooper #ifndef GTEST_SKIP
218ac5aef8SEnji Cooper #define GTEST_SKIP GTEST_FAIL
228ac5aef8SEnji Cooper #endif
238ac5aef8SEnji Cooper
248ac5aef8SEnji Cooper std::string tmpdir;
258ac5aef8SEnji Cooper
268ac5aef8SEnji Cooper class SetupEnvironment : public ::testing::Environment
278ac5aef8SEnji Cooper {
288ac5aef8SEnji Cooper public:
SetupEnvironment()298ac5aef8SEnji Cooper SetupEnvironment() : teardown_tmpdir_(false) {}
SetUp()308ac5aef8SEnji Cooper void SetUp() override {
318ac5aef8SEnji Cooper CheckCapsicumSupport();
328ac5aef8SEnji Cooper if (tmpdir.empty()) {
338ac5aef8SEnji Cooper std::cerr << "Generating temporary directory root: ";
348ac5aef8SEnji Cooper CreateTemporaryRoot();
358ac5aef8SEnji Cooper } else {
368ac5aef8SEnji Cooper std::cerr << "User provided temporary directory root: ";
378ac5aef8SEnji Cooper }
388ac5aef8SEnji Cooper std::cerr << tmpdir << std::endl;
398ac5aef8SEnji Cooper }
CheckCapsicumSupport()408ac5aef8SEnji Cooper void CheckCapsicumSupport() {
418ac5aef8SEnji Cooper #ifdef __FreeBSD__
428ac5aef8SEnji Cooper int rc;
438ac5aef8SEnji Cooper bool trap_enotcap_enabled;
448ac5aef8SEnji Cooper size_t trap_enotcap_enabled_len = sizeof(trap_enotcap_enabled);
458ac5aef8SEnji Cooper
468ac5aef8SEnji Cooper if (feature_present("security_capabilities") == 0) {
478ac5aef8SEnji Cooper GTEST_SKIP() << "Skipping tests because capsicum support is not "
488ac5aef8SEnji Cooper << "enabled in the kernel.";
498ac5aef8SEnji Cooper }
508ac5aef8SEnji Cooper // If this OID is enabled, it will send SIGTRAP to the process when
518ac5aef8SEnji Cooper // `ENOTCAPABLE` is returned.
528ac5aef8SEnji Cooper const char *oid = "kern.trap_enotcap";
538ac5aef8SEnji Cooper rc = sysctlbyname(oid, &trap_enotcap_enabled, &trap_enotcap_enabled_len,
548ac5aef8SEnji Cooper nullptr, 0);
558ac5aef8SEnji Cooper if (rc != 0) {
568ac5aef8SEnji Cooper GTEST_FAIL() << "sysctlbyname failed: " << strerror(errno);
578ac5aef8SEnji Cooper }
588ac5aef8SEnji Cooper if (trap_enotcap_enabled) {
598ac5aef8SEnji Cooper GTEST_SKIP() << "Debug sysctl, " << oid << ", enabled. "
608ac5aef8SEnji Cooper << "Skipping tests because its enablement invalidates the "
618ac5aef8SEnji Cooper << "test results.";
628ac5aef8SEnji Cooper }
638ac5aef8SEnji Cooper #endif /* FreeBSD */
648ac5aef8SEnji Cooper }
CreateTemporaryRoot()658ac5aef8SEnji Cooper void CreateTemporaryRoot() {
668ac5aef8SEnji Cooper char *tmpdir_name = tempnam(nullptr, "cptst");
678ac5aef8SEnji Cooper
688ac5aef8SEnji Cooper ASSERT_NE(tmpdir_name, nullptr);
698ac5aef8SEnji Cooper ASSERT_EQ(mkdir(tmpdir_name, 0700), 0) <<
708ac5aef8SEnji Cooper "Could not create temp directory, " << tmpdir_name << ": " <<
718ac5aef8SEnji Cooper strerror(errno);
728ac5aef8SEnji Cooper tmpdir = std::string(tmpdir_name);
738ac5aef8SEnji Cooper free(tmpdir_name);
748ac5aef8SEnji Cooper teardown_tmpdir_ = true;
758ac5aef8SEnji Cooper }
TearDown()768ac5aef8SEnji Cooper void TearDown() override {
778ac5aef8SEnji Cooper if (teardown_tmpdir_) {
788ac5aef8SEnji Cooper rmdir(tmpdir.c_str());
798ac5aef8SEnji Cooper }
808ac5aef8SEnji Cooper }
818ac5aef8SEnji Cooper private:
828ac5aef8SEnji Cooper bool teardown_tmpdir_;
838ac5aef8SEnji Cooper };
848ac5aef8SEnji Cooper
858ac5aef8SEnji Cooper std::string capsicum_test_bindir;
868ac5aef8SEnji Cooper
87*2d936e6cSAlex Richardson // Adds a directory to $PATH.
AddDirectoryToPath(const char * dir)88*2d936e6cSAlex Richardson static void AddDirectoryToPath(const char *dir) {
89*2d936e6cSAlex Richardson char *new_path, *old_path;
90*2d936e6cSAlex Richardson
91*2d936e6cSAlex Richardson old_path = getenv("PATH");
92*2d936e6cSAlex Richardson assert(old_path);
93*2d936e6cSAlex Richardson
94*2d936e6cSAlex Richardson assert(asprintf(&new_path, "%s:%s", dir, old_path) > 0);
95*2d936e6cSAlex Richardson assert(setenv("PATH", new_path, 1) == 0);
96*2d936e6cSAlex Richardson }
97*2d936e6cSAlex Richardson
main(int argc,char * argv[])988ac5aef8SEnji Cooper int main(int argc, char* argv[]) {
998ac5aef8SEnji Cooper // Set up the test program path, so capsicum-test can find programs, like
1008ac5aef8SEnji Cooper // mini-me* when executed from an absolute path.
101*2d936e6cSAlex Richardson char *program_name;
1028ac5aef8SEnji Cooper
103*2d936e6cSAlex Richardson // Copy argv[0], so dirname can do an in-place manipulation of the buffer's
104*2d936e6cSAlex Richardson // contents.
1058ac5aef8SEnji Cooper program_name = strdup(argv[0]);
1068ac5aef8SEnji Cooper assert(program_name);
1078ac5aef8SEnji Cooper capsicum_test_bindir = std::string(dirname(program_name));
1088ac5aef8SEnji Cooper free(program_name);
1098ac5aef8SEnji Cooper
110*2d936e6cSAlex Richardson AddDirectoryToPath(capsicum_test_bindir.c_str());
1118ac5aef8SEnji Cooper
1128ac5aef8SEnji Cooper ::testing::InitGoogleTest(&argc, argv);
1138ac5aef8SEnji Cooper for (int ii = 1; ii < argc; ii++) {
1148ac5aef8SEnji Cooper if (strcmp(argv[ii], "-v") == 0) {
1158ac5aef8SEnji Cooper verbose = true;
1168ac5aef8SEnji Cooper } else if (strcmp(argv[ii], "-T") == 0) {
1178ac5aef8SEnji Cooper ii++;
1188ac5aef8SEnji Cooper assert(ii < argc);
1198ac5aef8SEnji Cooper tmpdir = argv[ii];
1208ac5aef8SEnji Cooper struct stat info;
1218ac5aef8SEnji Cooper stat(tmpdir.c_str(), &info);
1228ac5aef8SEnji Cooper assert(S_ISDIR(info.st_mode));
1238ac5aef8SEnji Cooper } else if (strcmp(argv[ii], "-t") == 0) {
1248ac5aef8SEnji Cooper force_mt = true;
1258ac5aef8SEnji Cooper } else if (strcmp(argv[ii], "-F") == 0) {
1268ac5aef8SEnji Cooper force_nofork = true;
1278ac5aef8SEnji Cooper } else if (strcmp(argv[ii], "-u") == 0) {
1288ac5aef8SEnji Cooper if (++ii >= argc) {
1298ac5aef8SEnji Cooper std::cerr << "-u needs argument" << std::endl;
1308ac5aef8SEnji Cooper exit(1);
1318ac5aef8SEnji Cooper }
1328ac5aef8SEnji Cooper if (isdigit(argv[ii][0])) {
1338ac5aef8SEnji Cooper other_uid = atoi(argv[ii]);
1348ac5aef8SEnji Cooper } else {
1358ac5aef8SEnji Cooper struct passwd *p = getpwnam(argv[ii]);
1368ac5aef8SEnji Cooper if (!p) {
1378ac5aef8SEnji Cooper std::cerr << "Failed to get entry for " << argv[ii] << ", errno=" << errno << std::endl;
1388ac5aef8SEnji Cooper exit(1);
1398ac5aef8SEnji Cooper }
1408ac5aef8SEnji Cooper other_uid = p->pw_uid;
1418ac5aef8SEnji Cooper }
1428ac5aef8SEnji Cooper }
1438ac5aef8SEnji Cooper }
1448ac5aef8SEnji Cooper if (other_uid == 0) {
1458ac5aef8SEnji Cooper struct stat info;
1468ac5aef8SEnji Cooper if (stat(argv[0], &info) == 0) {
1478ac5aef8SEnji Cooper other_uid = info.st_uid;
1488ac5aef8SEnji Cooper }
1498ac5aef8SEnji Cooper }
1508ac5aef8SEnji Cooper
1518ac5aef8SEnji Cooper #ifdef __linux__
1528ac5aef8SEnji Cooper // Check whether our temporary directory is on a tmpfs volume.
1538ac5aef8SEnji Cooper struct statfs fsinfo;
1548ac5aef8SEnji Cooper statfs(tmpdir.c_str(), &fsinfo);
1558ac5aef8SEnji Cooper tmpdir_on_tmpfs = (fsinfo.f_type == TMPFS_MAGIC);
1568ac5aef8SEnji Cooper #endif
1578ac5aef8SEnji Cooper
1588ac5aef8SEnji Cooper testing::AddGlobalTestEnvironment(new SetupEnvironment());
159*2d936e6cSAlex Richardson return RUN_ALL_TESTS();
1608ac5aef8SEnji Cooper }
161