1 #include <sys/types.h> 2 #include <sys/wait.h> 3 #include <sys/stat.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, SetuidIgnored) { 130 if (geteuid() == 0) { 131 TEST_SKIPPED("requires non-root"); 132 return; 133 } 134 int fd = open(exec_prog_setuid_.c_str(), O_RDONLY); 135 EXPECT_OK(fd); 136 EXPECT_OK(cap_enter()); 137 if (fd >= 0) { 138 struct stat data; 139 EXPECT_OK(fstat(fd, &data)); 140 EXPECT_EQ((mode_t)S_ISUID, data.st_mode & S_ISUID); 141 EXPECT_OK(fexecve_(fd, argv_checkroot_, null_envp)); 142 // Should not reach here, exec() takes over. 143 EXPECT_TRUE(!"fexecve() should have succeeded"); 144 close(fd); 145 } 146 } 147 148 FORK_TEST_F(Fexecve, ExecveFailure) { 149 EXPECT_OK(cap_enter()); 150 EXPECT_EQ(-1, execve(argv_fail_[0], argv_fail_, null_envp)); 151 EXPECT_EQ(ECAPMODE, errno); 152 } 153 154 FORK_TEST_F(FexecveWithScript, CapModeScriptFail) { 155 int fd; 156 157 // Open the script file, with CAP_FEXECVE rights. 158 fd = open(temp_script_filename_, O_RDONLY); 159 cap_rights_t rights; 160 cap_rights_init(&rights, CAP_FEXECVE, CAP_READ, CAP_SEEK); 161 EXPECT_OK(cap_rights_limit(fd, &rights)); 162 163 EXPECT_OK(cap_enter()); // Enter capability mode 164 165 // Attempt fexecve; should fail, because "/bin/sh" is inaccessible. 166 EXPECT_EQ(-1, fexecve_(fd, argv_pass_, null_envp)); 167 } 168 169 #ifdef HAVE_EXECVEAT 170 class Execveat : public Execve { 171 public: 172 Execveat() : Execve() {} 173 }; 174 175 TEST_F(Execveat, NoUpwardTraversal) { 176 char *abspath = realpath(exec_prog_, NULL); 177 char cwd[1024]; 178 getcwd(cwd, sizeof(cwd)); 179 180 int dfd = open(".", O_DIRECTORY|O_RDONLY); 181 pid_t child = fork(); 182 if (child == 0) { 183 EXPECT_OK(cap_enter()); // Enter capability mode. 184 // Can't execveat() an absolute path, even relative to a dfd. 185 EXPECT_SYSCALL_FAIL(ECAPMODE, 186 execveat(AT_FDCWD, abspath, argv_pass_, null_envp, 0)); 187 EXPECT_SYSCALL_FAIL(E_NO_TRAVERSE_CAPABILITY, 188 execveat(dfd, abspath, argv_pass_, null_envp, 0)); 189 190 // Can't execveat() a relative path ("../<dir>/./<exe>"). 191 char *p = cwd + strlen(cwd); 192 while (*p != '/') p--; 193 char buffer[1024] = "../"; 194 strcat(buffer, ++p); 195 strcat(buffer, "/"); 196 strcat(buffer, exec_prog_); 197 EXPECT_SYSCALL_FAIL(E_NO_TRAVERSE_CAPABILITY, 198 execveat(dfd, buffer, argv_pass_, null_envp, 0)); 199 exit(HasFailure() ? 99 : 123); 200 } 201 int status; 202 EXPECT_EQ(child, waitpid(child, &status, 0)); 203 EXPECT_TRUE(WIFEXITED(status)) << "0x" << std::hex << status; 204 EXPECT_EQ(123, WEXITSTATUS(status)); 205 free(abspath); 206 close(dfd); 207 } 208 #endif 209