xref: /freebsd/tests/sys/capsicum/capsicum-test-main.cc (revision 670b568ec1c36464c6d55e400382c290b0391ccf)
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