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