1 #include <sys/types.h> 2 #ifdef __linux__ 3 #include <sys/vfs.h> 4 #include <linux/magic.h> 5 #elif defined(__FreeBSD__) 6 #include <sys/sysctl.h> 7 #endif 8 #include <ctype.h> 9 #include <errno.h> 10 #include <libgen.h> 11 #include <pwd.h> 12 #include <stdio.h> 13 #include <stdlib.h> 14 #include <unistd.h> 15 #include <iostream> 16 #include "gtest/gtest.h" 17 #include "capsicum-test.h" 18 19 // For versions of googletest that lack GTEST_SKIP. 20 #ifndef GTEST_SKIP 21 #define GTEST_SKIP GTEST_FAIL 22 #endif 23 24 std::string tmpdir; 25 26 class SetupEnvironment : public ::testing::Environment 27 { 28 public: 29 SetupEnvironment() : teardown_tmpdir_(false) {} 30 void SetUp() override { 31 CheckCapsicumSupport(); 32 if (tmpdir.empty()) { 33 std::cerr << "Generating temporary directory root: "; 34 CreateTemporaryRoot(); 35 } else { 36 std::cerr << "User provided temporary directory root: "; 37 } 38 std::cerr << tmpdir << std::endl; 39 } 40 void CheckCapsicumSupport() { 41 #ifdef __FreeBSD__ 42 int rc; 43 bool trap_enotcap_enabled; 44 size_t trap_enotcap_enabled_len = sizeof(trap_enotcap_enabled); 45 46 if (feature_present("security_capabilities") == 0) { 47 GTEST_SKIP() << "Skipping tests because capsicum support is not " 48 << "enabled in the kernel."; 49 } 50 // If this OID is enabled, it will send SIGTRAP to the process when 51 // `ENOTCAPABLE` is returned. 52 const char *oid = "kern.trap_enotcap"; 53 rc = sysctlbyname(oid, &trap_enotcap_enabled, &trap_enotcap_enabled_len, 54 nullptr, 0); 55 if (rc != 0) { 56 GTEST_FAIL() << "sysctlbyname failed: " << strerror(errno); 57 } 58 if (trap_enotcap_enabled) { 59 GTEST_SKIP() << "Debug sysctl, " << oid << ", enabled. " 60 << "Skipping tests because its enablement invalidates the " 61 << "test results."; 62 } 63 #endif /* FreeBSD */ 64 } 65 void CreateTemporaryRoot() { 66 char *tmpdir_name = tempnam(nullptr, "cptst"); 67 68 ASSERT_NE(tmpdir_name, nullptr); 69 ASSERT_EQ(mkdir(tmpdir_name, 0700), 0) << 70 "Could not create temp directory, " << tmpdir_name << ": " << 71 strerror(errno); 72 tmpdir = std::string(tmpdir_name); 73 free(tmpdir_name); 74 teardown_tmpdir_ = true; 75 } 76 void TearDown() override { 77 if (teardown_tmpdir_) { 78 rmdir(tmpdir.c_str()); 79 } 80 } 81 private: 82 bool teardown_tmpdir_; 83 }; 84 85 std::string capsicum_test_bindir; 86 87 // Adds a directory to $PATH. 88 static void AddDirectoryToPath(const char *dir) { 89 char *new_path, *old_path; 90 91 old_path = getenv("PATH"); 92 assert(old_path); 93 94 assert(asprintf(&new_path, "%s:%s", dir, old_path) > 0); 95 assert(setenv("PATH", new_path, 1) == 0); 96 } 97 98 int main(int argc, char* argv[]) { 99 // Set up the test program path, so capsicum-test can find programs, like 100 // mini-me* when executed from an absolute path. 101 char *program_name; 102 103 // Copy argv[0], so dirname can do an in-place manipulation of the buffer's 104 // contents. 105 program_name = strdup(argv[0]); 106 assert(program_name); 107 capsicum_test_bindir = std::string(dirname(program_name)); 108 free(program_name); 109 110 AddDirectoryToPath(capsicum_test_bindir.c_str()); 111 112 ::testing::InitGoogleTest(&argc, argv); 113 for (int ii = 1; ii < argc; ii++) { 114 if (strcmp(argv[ii], "-v") == 0) { 115 verbose = true; 116 } else if (strcmp(argv[ii], "-T") == 0) { 117 ii++; 118 assert(ii < argc); 119 tmpdir = argv[ii]; 120 struct stat info; 121 stat(tmpdir.c_str(), &info); 122 assert(S_ISDIR(info.st_mode)); 123 } else if (strcmp(argv[ii], "-t") == 0) { 124 force_mt = true; 125 } else if (strcmp(argv[ii], "-F") == 0) { 126 force_nofork = true; 127 } else if (strcmp(argv[ii], "-u") == 0) { 128 if (++ii >= argc) { 129 std::cerr << "-u needs argument" << std::endl; 130 exit(1); 131 } 132 if (isdigit(argv[ii][0])) { 133 other_uid = atoi(argv[ii]); 134 } else { 135 struct passwd *p = getpwnam(argv[ii]); 136 if (!p) { 137 std::cerr << "Failed to get entry for " << argv[ii] << ", errno=" << errno << std::endl; 138 exit(1); 139 } 140 other_uid = p->pw_uid; 141 } 142 } 143 } 144 if (other_uid == 0) { 145 struct stat info; 146 if (stat(argv[0], &info) == 0) { 147 other_uid = info.st_uid; 148 } 149 } 150 151 #ifdef __linux__ 152 // Check whether our temporary directory is on a tmpfs volume. 153 struct statfs fsinfo; 154 statfs(tmpdir.c_str(), &fsinfo); 155 tmpdir_on_tmpfs = (fsinfo.f_type == TMPFS_MAGIC); 156 #endif 157 158 testing::AddGlobalTestEnvironment(new SetupEnvironment()); 159 return RUN_ALL_TESTS(); 160 } 161