xref: /freebsd/tests/sys/capsicum/fexecve.cc (revision 670b568ec1c36464c6d55e400382c290b0391ccf)
1*670b568eSEd Maste #include <sys/types.h>
2*670b568eSEd Maste #include <sys/stat.h>
3*670b568eSEd Maste #include <sys/wait.h>
4*670b568eSEd Maste #include <errno.h>
5*670b568eSEd Maste #include <fcntl.h>
6*670b568eSEd Maste #include <limits.h>
7*670b568eSEd Maste #include <stdlib.h>
8*670b568eSEd Maste #include <string.h>
9*670b568eSEd Maste #include <unistd.h>
10*670b568eSEd Maste 
11*670b568eSEd Maste #include <sstream>
12*670b568eSEd Maste 
13*670b568eSEd Maste #include "syscalls.h"
14*670b568eSEd Maste #include "capsicum.h"
15*670b568eSEd Maste #include "capsicum-test.h"
16*670b568eSEd Maste 
17*670b568eSEd Maste // Arguments to use in execve() calls.
18*670b568eSEd Maste static char* null_envp[] = {NULL};
19*670b568eSEd Maste 
20*670b568eSEd Maste class Execve : public ::testing::Test {
21*670b568eSEd Maste  public:
Execve()22*670b568eSEd Maste   Execve() : exec_fd_(-1) {
23*670b568eSEd Maste     // We need a program to exec(), but for fexecve() to work in capability
24*670b568eSEd Maste     // mode that program needs to be statically linked (otherwise ld.so will
25*670b568eSEd Maste     // attempt to traverse the filesystem to load (e.g.) /lib/libc.so and
26*670b568eSEd Maste     // fail).
27*670b568eSEd Maste     exec_prog_ = capsicum_test_bindir + "/mini-me";
28*670b568eSEd Maste     exec_prog_noexec_ = capsicum_test_bindir + "/mini-me.noexec";
29*670b568eSEd Maste     exec_prog_setuid_ = capsicum_test_bindir + "/mini-me.setuid";
30*670b568eSEd Maste 
31*670b568eSEd Maste     exec_fd_ = open(exec_prog_.c_str(), O_RDONLY);
32*670b568eSEd Maste     if (exec_fd_ < 0) {
33*670b568eSEd Maste       fprintf(stderr, "Error! Failed to open %s\n", exec_prog_.c_str());
34*670b568eSEd Maste     }
35*670b568eSEd Maste     argv_checkroot_[0] = (char*)exec_prog_.c_str();
36*670b568eSEd Maste     argv_fail_[0] = (char*)exec_prog_.c_str();
37*670b568eSEd Maste     argv_pass_[0] = (char*)exec_prog_.c_str();
38*670b568eSEd Maste   }
~Execve()39*670b568eSEd Maste   ~Execve() {
40*670b568eSEd Maste     if (exec_fd_ >= 0) {
41*670b568eSEd Maste       close(exec_fd_);
42*670b568eSEd Maste       exec_fd_ = -1;
43*670b568eSEd Maste     }
44*670b568eSEd Maste   }
45*670b568eSEd Maste protected:
46*670b568eSEd Maste   char* argv_checkroot_[3] = {nullptr, (char*)"--checkroot", nullptr};
47*670b568eSEd Maste   char* argv_fail_[3] = {nullptr, (char*)"--fail", nullptr};
48*670b568eSEd Maste   char* argv_pass_[3] = {nullptr, (char*)"--pass", nullptr};
49*670b568eSEd Maste   std::string exec_prog_, exec_prog_noexec_, exec_prog_setuid_;
50*670b568eSEd Maste   int exec_fd_;
51*670b568eSEd Maste };
52*670b568eSEd Maste 
53*670b568eSEd Maste class Fexecve : public Execve {
54*670b568eSEd Maste  public:
Fexecve()55*670b568eSEd Maste   Fexecve() : Execve() {}
56*670b568eSEd Maste };
57*670b568eSEd Maste 
58*670b568eSEd Maste class FexecveWithScript : public Fexecve {
59*670b568eSEd Maste  public:
FexecveWithScript()60*670b568eSEd Maste   FexecveWithScript() :
61*670b568eSEd Maste     Fexecve(), temp_script_filename_(TmpFile("cap_sh_script")) {}
62*670b568eSEd Maste 
SetUp()63*670b568eSEd Maste   void SetUp() override {
64*670b568eSEd Maste     // First, build an executable shell script
65*670b568eSEd Maste     int fd = open(temp_script_filename_, O_RDWR|O_CREAT, 0755);
66*670b568eSEd Maste     EXPECT_OK(fd);
67*670b568eSEd Maste     const char* contents = "#!/bin/sh\nexit 99\n";
68*670b568eSEd Maste     EXPECT_OK(write(fd, contents, strlen(contents)));
69*670b568eSEd Maste     close(fd);
70*670b568eSEd Maste   }
TearDown()71*670b568eSEd Maste   void TearDown() override {
72*670b568eSEd Maste     (void)::unlink(temp_script_filename_);
73*670b568eSEd Maste   }
74*670b568eSEd Maste 
75*670b568eSEd Maste   const char *temp_script_filename_;
76*670b568eSEd Maste };
77*670b568eSEd Maste 
FORK_TEST_F(Execve,BasicFexecve)78*670b568eSEd Maste FORK_TEST_F(Execve, BasicFexecve) {
79*670b568eSEd Maste   EXPECT_OK(fexecve_(exec_fd_, argv_pass_, null_envp));
80*670b568eSEd Maste   // Should not reach here, exec() takes over.
81*670b568eSEd Maste   EXPECT_TRUE(!"fexecve() should never return");
82*670b568eSEd Maste }
83*670b568eSEd Maste 
FORK_TEST_F(Execve,InCapMode)84*670b568eSEd Maste FORK_TEST_F(Execve, InCapMode) {
85*670b568eSEd Maste   EXPECT_OK(cap_enter());
86*670b568eSEd Maste   EXPECT_OK(fexecve_(exec_fd_, argv_pass_, null_envp));
87*670b568eSEd Maste   // Should not reach here, exec() takes over.
88*670b568eSEd Maste   EXPECT_TRUE(!"fexecve() should never return");
89*670b568eSEd Maste }
90*670b568eSEd Maste 
FORK_TEST_F(Execve,FailWithoutCap)91*670b568eSEd Maste FORK_TEST_F(Execve, FailWithoutCap) {
92*670b568eSEd Maste   EXPECT_OK(cap_enter());
93*670b568eSEd Maste   int cap_fd = dup(exec_fd_);
94*670b568eSEd Maste   EXPECT_OK(cap_fd);
95*670b568eSEd Maste   cap_rights_t rights;
96*670b568eSEd Maste   cap_rights_init(&rights, 0);
97*670b568eSEd Maste   EXPECT_OK(cap_rights_limit(cap_fd, &rights));
98*670b568eSEd Maste   EXPECT_EQ(-1, fexecve_(cap_fd, argv_fail_, null_envp));
99*670b568eSEd Maste   EXPECT_EQ(ENOTCAPABLE, errno);
100*670b568eSEd Maste }
101*670b568eSEd Maste 
FORK_TEST_F(Execve,SucceedWithCap)102*670b568eSEd Maste FORK_TEST_F(Execve, SucceedWithCap) {
103*670b568eSEd Maste   EXPECT_OK(cap_enter());
104*670b568eSEd Maste   int cap_fd = dup(exec_fd_);
105*670b568eSEd Maste   EXPECT_OK(cap_fd);
106*670b568eSEd Maste   cap_rights_t rights;
107*670b568eSEd Maste   // TODO(drysdale): would prefer that Linux Capsicum not need all of these
108*670b568eSEd Maste   // rights -- just CAP_FEXECVE|CAP_READ or CAP_FEXECVE would be preferable.
109*670b568eSEd Maste   cap_rights_init(&rights, CAP_FEXECVE, CAP_LOOKUP, CAP_READ);
110*670b568eSEd Maste   EXPECT_OK(cap_rights_limit(cap_fd, &rights));
111*670b568eSEd Maste   EXPECT_OK(fexecve_(cap_fd, argv_pass_, null_envp));
112*670b568eSEd Maste   // Should not reach here, exec() takes over.
113*670b568eSEd Maste   EXPECT_TRUE(!"fexecve() should have succeeded");
114*670b568eSEd Maste }
115*670b568eSEd Maste 
FORK_TEST_F(Fexecve,ExecutePermissionCheck)116*670b568eSEd Maste FORK_TEST_F(Fexecve, ExecutePermissionCheck) {
117*670b568eSEd Maste   int fd = open(exec_prog_noexec_.c_str(), O_RDONLY);
118*670b568eSEd Maste   EXPECT_OK(fd);
119*670b568eSEd Maste   if (fd >= 0) {
120*670b568eSEd Maste     struct stat data;
121*670b568eSEd Maste     EXPECT_OK(fstat(fd, &data));
122*670b568eSEd Maste     EXPECT_EQ((mode_t)0, data.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH));
123*670b568eSEd Maste     EXPECT_EQ(-1, fexecve_(fd, argv_fail_, null_envp));
124*670b568eSEd Maste     EXPECT_EQ(EACCES, errno);
125*670b568eSEd Maste     close(fd);
126*670b568eSEd Maste   }
127*670b568eSEd Maste }
128*670b568eSEd Maste 
FORK_TEST_F(Fexecve,SetuidIgnoredIfNonRoot)129*670b568eSEd Maste FORK_TEST_F(Fexecve, SetuidIgnoredIfNonRoot) {
130*670b568eSEd Maste   if (geteuid() == 0) {
131*670b568eSEd Maste     GTEST_SKIP() << "requires non-root";
132*670b568eSEd Maste   }
133*670b568eSEd Maste   int fd = open(exec_prog_setuid_.c_str(), O_RDONLY);
134*670b568eSEd Maste   EXPECT_OK(fd);
135*670b568eSEd Maste   EXPECT_OK(cap_enter());
136*670b568eSEd Maste   if (fd >= 0) {
137*670b568eSEd Maste     struct stat data;
138*670b568eSEd Maste     EXPECT_OK(fstat(fd, &data));
139*670b568eSEd Maste     EXPECT_EQ((mode_t)S_ISUID, data.st_mode & S_ISUID);
140*670b568eSEd Maste     EXPECT_OK(fexecve_(fd, argv_checkroot_, null_envp));
141*670b568eSEd Maste     // Should not reach here, exec() takes over.
142*670b568eSEd Maste     EXPECT_TRUE(!"fexecve() should have succeeded");
143*670b568eSEd Maste     close(fd);
144*670b568eSEd Maste   }
145*670b568eSEd Maste }
146*670b568eSEd Maste 
FORK_TEST_F(Fexecve,ExecveFailure)147*670b568eSEd Maste FORK_TEST_F(Fexecve, ExecveFailure) {
148*670b568eSEd Maste   EXPECT_OK(cap_enter());
149*670b568eSEd Maste   EXPECT_EQ(-1, execve(argv_fail_[0], argv_fail_, null_envp));
150*670b568eSEd Maste   EXPECT_EQ(ECAPMODE, errno);
151*670b568eSEd Maste }
152*670b568eSEd Maste 
FORK_TEST_F(FexecveWithScript,CapModeScriptFail)153*670b568eSEd Maste FORK_TEST_F(FexecveWithScript, CapModeScriptFail) {
154*670b568eSEd Maste   int fd;
155*670b568eSEd Maste 
156*670b568eSEd Maste   // Open the script file, with CAP_FEXECVE rights.
157*670b568eSEd Maste   fd = open(temp_script_filename_, O_RDONLY);
158*670b568eSEd Maste   cap_rights_t rights;
159*670b568eSEd Maste   cap_rights_init(&rights, CAP_FEXECVE, CAP_READ, CAP_SEEK);
160*670b568eSEd Maste   EXPECT_OK(cap_rights_limit(fd, &rights));
161*670b568eSEd Maste 
162*670b568eSEd Maste   EXPECT_OK(cap_enter());  // Enter capability mode
163*670b568eSEd Maste 
164*670b568eSEd Maste   // Attempt fexecve; should fail, because "/bin/sh" is inaccessible.
165*670b568eSEd Maste   EXPECT_EQ(-1, fexecve_(fd, argv_pass_, null_envp));
166*670b568eSEd Maste }
167*670b568eSEd Maste 
168*670b568eSEd Maste #ifdef HAVE_EXECVEAT
169*670b568eSEd Maste class Execveat : public Execve {
170*670b568eSEd Maste  public:
Execveat()171*670b568eSEd Maste   Execveat() : Execve() {}
172*670b568eSEd Maste };
173*670b568eSEd Maste 
TEST_F(Execveat,NoUpwardTraversal)174*670b568eSEd Maste TEST_F(Execveat, NoUpwardTraversal) {
175*670b568eSEd Maste   char *abspath = realpath(exec_prog_.c_str(), NULL);
176*670b568eSEd Maste   char cwd[1024];
177*670b568eSEd Maste   getcwd(cwd, sizeof(cwd));
178*670b568eSEd Maste 
179*670b568eSEd Maste   int dfd = open(".", O_DIRECTORY|O_RDONLY);
180*670b568eSEd Maste   pid_t child = fork();
181*670b568eSEd Maste   if (child == 0) {
182*670b568eSEd Maste     EXPECT_OK(cap_enter());  // Enter capability mode.
183*670b568eSEd Maste     // Can't execveat() an absolute path, even relative to a dfd.
184*670b568eSEd Maste     EXPECT_SYSCALL_FAIL(ECAPMODE,
185*670b568eSEd Maste                         execveat(AT_FDCWD, abspath, argv_pass_, null_envp, 0));
186*670b568eSEd Maste     EXPECT_SYSCALL_FAIL(E_NO_TRAVERSE_CAPABILITY,
187*670b568eSEd Maste                         execveat(dfd, abspath, argv_pass_, null_envp, 0));
188*670b568eSEd Maste 
189*670b568eSEd Maste     // Can't execveat() a relative path ("../<dir>/./<exe>").
190*670b568eSEd Maste     char *p = cwd + strlen(cwd);
191*670b568eSEd Maste     while (*p != '/') p--;
192*670b568eSEd Maste     char buffer[1024] = "../";
193*670b568eSEd Maste     strcat(buffer, ++p);
194*670b568eSEd Maste     strcat(buffer, "/");
195*670b568eSEd Maste     strcat(buffer, exec_prog_.c_str());
196*670b568eSEd Maste     EXPECT_SYSCALL_FAIL(E_NO_TRAVERSE_CAPABILITY,
197*670b568eSEd Maste                         execveat(dfd, buffer, argv_pass_, null_envp, 0));
198*670b568eSEd Maste     exit(HasFailure() ? 99 : 123);
199*670b568eSEd Maste   }
200*670b568eSEd Maste   int status;
201*670b568eSEd Maste   EXPECT_EQ(child, waitpid(child, &status, 0));
202*670b568eSEd Maste   EXPECT_TRUE(WIFEXITED(status)) << "0x" << std::hex << status;
203*670b568eSEd Maste   EXPECT_EQ(123, WEXITSTATUS(status));
204*670b568eSEd Maste   free(abspath);
205*670b568eSEd Maste   close(dfd);
206*670b568eSEd Maste }
207*670b568eSEd Maste #endif
208