Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 03 Sep 2019 14:05:50 -0000
From:      Enji Cooper <ngie@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-projects@freebsd.org
Subject:   svn commit: r345649 - projects/capsicum-test/contrib/capsicum-test
Message-ID:  <201903281956.x2SJuZwS059085@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
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;





Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201903281956.x2SJuZwS059085>