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