svn commit: r345649 - projects/capsicum-test/contrib/capsicum-test
Enji Cooper
ngie at FreeBSD.org
Tue Sep 3 14:05:50 UTC 2019
Author: ngie
Date: Thu Mar 28 19:56:35 2019
New Revision: 345649
URL: https://svnweb.freebsd.org/changeset/base/345649
Log:
Allow `capsicum-test` to be executed from an absolute path by stashing the execution directory
This change stashes the executing directory for `capsicum-test` then uses it to
determine where `mini-me*` lives, allowing the script to be executed from an
absolute path.
This change required refactoring how the fexecve tests were executed, as the
path for `mini-me*` could no longer be determined at compile-time, but needs
to be determined at runtime.
As such, switch from FORK_TEST to FORK_TEST_F (with an appropriate class) and
FORK_TEST_ON to FORK_F with proper setup/teardown fixtures for cleaning up the
temporary script.
Modified:
projects/capsicum-test/contrib/capsicum-test/capsicum-test-main.cc
projects/capsicum-test/contrib/capsicum-test/capsicum.h
projects/capsicum-test/contrib/capsicum-test/fexecve.cc
Modified: projects/capsicum-test/contrib/capsicum-test/capsicum-test-main.cc
==============================================================================
--- projects/capsicum-test/contrib/capsicum-test/capsicum-test-main.cc Thu Mar 28 19:46:59 2019 (r345648)
+++ projects/capsicum-test/contrib/capsicum-test/capsicum-test-main.cc Thu Mar 28 19:56:35 2019 (r345649)
@@ -5,9 +5,11 @@
#endif
#include <ctype.h>
#include <errno.h>
+#include <libgen.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
+#include <unistd.h>
#include <iostream>
#include "gtest/gtest.h"
#include "capsicum-test.h"
@@ -47,7 +49,27 @@ class SetupEnvironment : public ::testing::Environment
bool teardown_tmpdir_;
};
+std::string capsicum_test_bindir;
+
int main(int argc, char* argv[]) {
+ // Set up the test program path, so capsicum-test can find programs, like
+ // mini-me* when executed from an absolute path.
+ {
+ char *new_path, *old_path, *program_name;
+
+ program_name = strdup(argv[0]);
+ assert(program_name);
+ capsicum_test_bindir = std::string(dirname(program_name));
+ free(program_name);
+
+ old_path = getenv("PATH");
+ assert(old_path);
+
+ assert(asprintf(&new_path, "%s:%s", capsicum_test_bindir.c_str(),
+ old_path) > 0);
+ assert(setenv("PATH", new_path, 1) == 0);
+ }
+
::testing::InitGoogleTest(&argc, argv);
for (int ii = 1; ii < argc; ii++) {
if (strcmp(argv[ii], "-v") == 0) {
Modified: projects/capsicum-test/contrib/capsicum-test/capsicum.h
==============================================================================
--- projects/capsicum-test/contrib/capsicum-test/capsicum.h Thu Mar 28 19:46:59 2019 (r345648)
+++ projects/capsicum-test/contrib/capsicum-test/capsicum.h Thu Mar 28 19:56:35 2019 (r345649)
@@ -167,4 +167,9 @@ static inline void cap_rights_describe(const cap_right
#endif /* new/old style rights manipulation */
+#ifdef __cplusplus
+#include <string>
+extern std::string capsicum_test_bindir;
+#endif
+
#endif /*__CAPSICUM_H__*/
Modified: projects/capsicum-test/contrib/capsicum-test/fexecve.cc
==============================================================================
--- projects/capsicum-test/contrib/capsicum-test/fexecve.cc Thu Mar 28 19:46:59 2019 (r345648)
+++ projects/capsicum-test/contrib/capsicum-test/fexecve.cc Thu Mar 28 19:56:35 2019 (r345649)
@@ -1,12 +1,12 @@
-#include <errno.h>
-#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
+#include <errno.h>
#include <fcntl.h>
-#include <unistd.h>
#include <limits.h>
#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
#include <sstream>
@@ -14,41 +14,76 @@
#include "capsicum.h"
#include "capsicum-test.h"
-// We need a program to exec(), but for fexecve() to work in capability
-// mode that program needs to be statically linked (otherwise ld.so will
-// attempt to traverse the filesystem to load (e.g.) /lib/libc.so and
-// fail).
-#define EXEC_PROG "./mini-me"
-#define EXEC_PROG_NOEXEC EXEC_PROG ".noexec"
-#define EXEC_PROG_SETUID EXEC_PROG ".setuid"
-
// Arguments to use in execve() calls.
-static char* argv_pass[] = {(char*)EXEC_PROG, (char*)"--pass", NULL};
-static char* argv_fail[] = {(char*)EXEC_PROG, (char*)"--fail", NULL};
-static char* argv_checkroot[] = {(char*)EXEC_PROG, (char*)"--checkroot", NULL};
static char* null_envp[] = {NULL};
class Execve : public ::testing::Test {
public:
- Execve() : exec_fd_(open(EXEC_PROG, O_RDONLY)) {
+ Execve() : exec_fd_(-1) {
+ // We need a program to exec(), but for fexecve() to work in capability
+ // mode that program needs to be statically linked (otherwise ld.so will
+ // attempt to traverse the filesystem to load (e.g.) /lib/libc.so and
+ // fail).
+ exec_prog_ = capsicum_test_bindir + "/mini-me";
+ exec_prog_noexec_ = capsicum_test_bindir + "/mini-me.noexec";
+ exec_prog_setuid_ = capsicum_test_bindir + "/mini-me.setuid";
+
+ exec_fd_ = open(exec_prog_.c_str(), O_RDONLY);
if (exec_fd_ < 0) {
- fprintf(stderr, "Error! Failed to open %s\n", EXEC_PROG);
+ fprintf(stderr, "Error! Failed to open %s\n", exec_prog_.c_str());
}
+ argv_checkroot_[0] = (char*)exec_prog_.c_str();
+ argv_fail_[0] = (char*)exec_prog_.c_str();
+ argv_pass_[0] = (char*)exec_prog_.c_str();
}
- ~Execve() { if (exec_fd_ >= 0) close(exec_fd_); }
+ ~Execve() {
+ if (exec_fd_ >= 0) {
+ close(exec_fd_);
+ exec_fd_ = -1;
+ }
+ }
protected:
+ char* argv_checkroot_[3] = {nullptr, (char*)"--checkroot", nullptr};
+ char* argv_fail_[3] = {nullptr, (char*)"--fail", nullptr};
+ char* argv_pass_[3] = {nullptr, (char*)"--pass", nullptr};
+ std::string exec_prog_, exec_prog_noexec_, exec_prog_setuid_;
int exec_fd_;
};
+class Fexecve : public Execve {
+ public:
+ Fexecve() : Execve() {}
+};
+
+class FexecveWithScript : public Fexecve {
+ public:
+ FexecveWithScript() :
+ Fexecve(), temp_script_filename_(TmpFile("cap_sh_script")) {}
+
+ void SetUp() override {
+ // First, build an executable shell script
+ int fd = open(temp_script_filename_, O_RDWR|O_CREAT, 0755);
+ EXPECT_OK(fd);
+ const char* contents = "#!/bin/sh\nexit 99\n";
+ EXPECT_OK(write(fd, contents, strlen(contents)));
+ close(fd);
+ }
+ void TearDown() override {
+ (void)::unlink(temp_script_filename_);
+ }
+
+ const char *temp_script_filename_;
+};
+
FORK_TEST_F(Execve, BasicFexecve) {
- EXPECT_OK(fexecve_(exec_fd_, argv_pass, null_envp));
+ EXPECT_OK(fexecve_(exec_fd_, argv_pass_, null_envp));
// Should not reach here, exec() takes over.
EXPECT_TRUE(!"fexecve() should never return");
}
FORK_TEST_F(Execve, InCapMode) {
EXPECT_OK(cap_enter());
- EXPECT_OK(fexecve_(exec_fd_, argv_pass, null_envp));
+ EXPECT_OK(fexecve_(exec_fd_, argv_pass_, null_envp));
// Should not reach here, exec() takes over.
EXPECT_TRUE(!"fexecve() should never return");
}
@@ -60,7 +95,7 @@ FORK_TEST_F(Execve, FailWithoutCap) {
cap_rights_t rights;
cap_rights_init(&rights, 0);
EXPECT_OK(cap_rights_limit(cap_fd, &rights));
- EXPECT_EQ(-1, fexecve_(cap_fd, argv_fail, null_envp));
+ EXPECT_EQ(-1, fexecve_(cap_fd, argv_fail_, null_envp));
EXPECT_EQ(ENOTCAPABLE, errno);
}
@@ -73,59 +108,54 @@ FORK_TEST_F(Execve, SucceedWithCap) {
// rights -- just CAP_FEXECVE|CAP_READ or CAP_FEXECVE would be preferable.
cap_rights_init(&rights, CAP_FEXECVE, CAP_LOOKUP, CAP_READ);
EXPECT_OK(cap_rights_limit(cap_fd, &rights));
- EXPECT_OK(fexecve_(cap_fd, argv_pass, null_envp));
+ EXPECT_OK(fexecve_(cap_fd, argv_pass_, null_envp));
// Should not reach here, exec() takes over.
EXPECT_TRUE(!"fexecve() should have succeeded");
}
-FORK_TEST(Fexecve, ExecutePermissionCheck) {
- int fd = open(EXEC_PROG_NOEXEC, O_RDONLY);
+FORK_TEST_F(Fexecve, ExecutePermissionCheck) {
+ int fd = open(exec_prog_noexec_.c_str(), O_RDONLY);
EXPECT_OK(fd);
if (fd >= 0) {
struct stat data;
EXPECT_OK(fstat(fd, &data));
EXPECT_EQ((mode_t)0, data.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH));
- EXPECT_EQ(-1, fexecve_(fd, argv_fail, null_envp));
+ EXPECT_EQ(-1, fexecve_(fd, argv_fail_, null_envp));
EXPECT_EQ(EACCES, errno);
close(fd);
}
}
-FORK_TEST(Fexecve, SetuidIgnored) {
+FORK_TEST_F(Fexecve, SetuidIgnored) {
if (geteuid() == 0) {
TEST_SKIPPED("requires non-root");
return;
}
- int fd = open(EXEC_PROG_SETUID, O_RDONLY);
+ int fd = open(exec_prog_setuid_.c_str(), O_RDONLY);
EXPECT_OK(fd);
EXPECT_OK(cap_enter());
if (fd >= 0) {
struct stat data;
EXPECT_OK(fstat(fd, &data));
EXPECT_EQ((mode_t)S_ISUID, data.st_mode & S_ISUID);
- EXPECT_OK(fexecve_(fd, argv_checkroot, null_envp));
+ EXPECT_OK(fexecve_(fd, argv_checkroot_, null_envp));
// Should not reach here, exec() takes over.
EXPECT_TRUE(!"fexecve() should have succeeded");
close(fd);
}
}
-FORK_TEST(Fexecve, ExecveFailure) {
+FORK_TEST_F(Fexecve, ExecveFailure) {
EXPECT_OK(cap_enter());
- EXPECT_EQ(-1, execve(argv_fail[0], argv_fail, null_envp));
+ EXPECT_EQ(-1, execve(argv_fail_[0], argv_fail_, null_envp));
EXPECT_EQ(ECAPMODE, errno);
}
-FORK_TEST_ON(Fexecve, CapModeScriptFail, TmpFile("cap_sh_script")) {
- // First, build an executable shell script
- int fd = open(TmpFile("cap_sh_script"), O_RDWR|O_CREAT, 0755);
- EXPECT_OK(fd);
- const char* contents = "#!/bin/sh\nexit 99\n";
- EXPECT_OK(write(fd, contents, strlen(contents)));
- close(fd);
+FORK_TEST_F(FexecveWithScript, CapModeScriptFail) {
+ int fd;
// Open the script file, with CAP_FEXECVE rights.
- fd = open(TmpFile("cap_sh_script"), O_RDONLY);
+ fd = open(temp_script_filename_, O_RDONLY);
cap_rights_t rights;
cap_rights_init(&rights, CAP_FEXECVE, CAP_READ, CAP_SEEK);
EXPECT_OK(cap_rights_limit(fd, &rights));
@@ -133,12 +163,12 @@ FORK_TEST_ON(Fexecve, CapModeScriptFail, TmpFile("cap_
EXPECT_OK(cap_enter()); // Enter capability mode
// Attempt fexecve; should fail, because "/bin/sh" is inaccessible.
- EXPECT_EQ(-1, fexecve_(fd, argv_pass, null_envp));
+ EXPECT_EQ(-1, fexecve_(fd, argv_pass_, null_envp));
}
#ifdef HAVE_EXECVEAT
TEST(Execveat, NoUpwardTraversal) {
- char *abspath = realpath(EXEC_PROG, NULL);
+ char *abspath = realpath(exec_prog_, NULL);
char cwd[1024];
getcwd(cwd, sizeof(cwd));
@@ -148,9 +178,9 @@ TEST(Execveat, NoUpwardTraversal) {
EXPECT_OK(cap_enter()); // Enter capability mode.
// Can't execveat() an absolute path, even relative to a dfd.
EXPECT_SYSCALL_FAIL(ECAPMODE,
- execveat(AT_FDCWD, abspath, argv_pass, null_envp, 0));
+ execveat(AT_FDCWD, abspath, argv_pass_, null_envp, 0));
EXPECT_SYSCALL_FAIL(E_NO_TRAVERSE_CAPABILITY,
- execveat(dfd, abspath, argv_pass, null_envp, 0));
+ execveat(dfd, abspath, argv_pass_, null_envp, 0));
// Can't execveat() a relative path ("../<dir>/./<exe>").
char *p = cwd + strlen(cwd);
@@ -158,9 +188,9 @@ TEST(Execveat, NoUpwardTraversal) {
char buffer[1024] = "../";
strcat(buffer, ++p);
strcat(buffer, "/");
- strcat(buffer, EXEC_PROG);
+ strcat(buffer, exec_prog_);
EXPECT_SYSCALL_FAIL(E_NO_TRAVERSE_CAPABILITY,
- execveat(dfd, buffer, argv_pass, null_envp, 0));
+ execveat(dfd, buffer, argv_pass_, null_envp, 0));
exit(HasFailure() ? 99 : 123);
}
int status;
More information about the svn-src-projects
mailing list