Home | History | Annotate | Download | only in services
      1 // Copyright 2015 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "sandbox/linux/services/namespace_sandbox.h"
      6 
      7 #include <signal.h>
      8 #include <sys/types.h>
      9 #include <sys/wait.h>
     10 #include <unistd.h>
     11 
     12 #include <string>
     13 #include <utility>
     14 
     15 #include "base/command_line.h"
     16 #include "base/files/file_enumerator.h"
     17 #include "base/files/file_path.h"
     18 #include "base/logging.h"
     19 #include "base/memory/scoped_ptr.h"
     20 #include "base/process/launch.h"
     21 #include "base/process/process.h"
     22 #include "base/test/multiprocess_test.h"
     23 #include "sandbox/linux/services/credentials.h"
     24 #include "sandbox/linux/services/namespace_utils.h"
     25 #include "sandbox/linux/services/proc_util.h"
     26 #include "sandbox/linux/tests/unit_tests.h"
     27 #include "testing/gtest/include/gtest/gtest.h"
     28 #include "testing/multiprocess_func_list.h"
     29 
     30 namespace sandbox {
     31 
     32 namespace {
     33 
     34 bool RootDirectoryIsEmpty() {
     35   base::FilePath root("/");
     36   int file_type =
     37       base::FileEnumerator::DIRECTORIES | base::FileEnumerator::FILES;
     38   base::FileEnumerator enumerator_before(root, false, file_type);
     39   return enumerator_before.Next().empty();
     40 }
     41 
     42 class NamespaceSandboxTest : public base::MultiProcessTest {
     43  public:
     44   void TestProc(const std::string& procname) {
     45     TestProcWithOptions(procname, NamespaceSandbox::Options());
     46   }
     47 
     48   void TestProcWithOptions(
     49       const std::string& procname,
     50       const NamespaceSandbox::Options& ns_sandbox_options) {
     51     if (!Credentials::CanCreateProcessInNewUserNS()) {
     52       return;
     53     }
     54 
     55     base::FileHandleMappingVector fds_to_remap = {
     56         std::make_pair(STDOUT_FILENO, STDOUT_FILENO),
     57         std::make_pair(STDERR_FILENO, STDERR_FILENO),
     58     };
     59     base::LaunchOptions launch_options;
     60     launch_options.fds_to_remap = &fds_to_remap;
     61 
     62     base::Process process = NamespaceSandbox::LaunchProcessWithOptions(
     63         MakeCmdLine(procname), launch_options, ns_sandbox_options);
     64     ASSERT_TRUE(process.IsValid());
     65 
     66     const int kDummyExitCode = 42;
     67     int exit_code = kDummyExitCode;
     68     EXPECT_TRUE(process.WaitForExit(&exit_code));
     69     EXPECT_EQ(0, exit_code);
     70   }
     71 };
     72 
     73 MULTIPROCESS_TEST_MAIN(SimpleChildProcess) {
     74   const bool in_user_ns = NamespaceSandbox::InNewUserNamespace();
     75   const bool in_pid_ns = NamespaceSandbox::InNewPidNamespace();
     76   const bool in_net_ns = NamespaceSandbox::InNewNetNamespace();
     77   CHECK(in_user_ns);
     78   CHECK_EQ(in_pid_ns,
     79            NamespaceUtils::KernelSupportsUnprivilegedNamespace(CLONE_NEWPID));
     80   CHECK_EQ(in_net_ns,
     81            NamespaceUtils::KernelSupportsUnprivilegedNamespace(CLONE_NEWNET));
     82   if (in_pid_ns) {
     83     CHECK_EQ(1, getpid());
     84   }
     85   return 0;
     86 }
     87 
     88 TEST_F(NamespaceSandboxTest, BasicUsage) {
     89   TestProc("SimpleChildProcess");
     90 }
     91 
     92 MULTIPROCESS_TEST_MAIN(PidNsOnlyChildProcess) {
     93   const bool in_user_ns = NamespaceSandbox::InNewUserNamespace();
     94   const bool in_pid_ns = NamespaceSandbox::InNewPidNamespace();
     95   const bool in_net_ns = NamespaceSandbox::InNewNetNamespace();
     96   CHECK(in_user_ns);
     97   CHECK_EQ(in_pid_ns,
     98            NamespaceUtils::KernelSupportsUnprivilegedNamespace(CLONE_NEWPID));
     99   CHECK(!in_net_ns);
    100   if (in_pid_ns) {
    101     CHECK_EQ(1, getpid());
    102   }
    103   return 0;
    104 }
    105 
    106 
    107 TEST_F(NamespaceSandboxTest, BasicUsageWithOptions) {
    108   NamespaceSandbox::Options options;
    109   options.ns_types = CLONE_NEWUSER | CLONE_NEWPID;
    110   TestProcWithOptions("PidNsOnlyChildProcess", options);
    111 }
    112 
    113 MULTIPROCESS_TEST_MAIN(ChrootMe) {
    114   CHECK(!RootDirectoryIsEmpty());
    115   CHECK(sandbox::Credentials::MoveToNewUserNS());
    116   CHECK(sandbox::Credentials::DropFileSystemAccess(ProcUtil::OpenProc().get()));
    117   CHECK(RootDirectoryIsEmpty());
    118   return 0;
    119 }
    120 
    121 // Temporarily disabled on ASAN due to crbug.com/451603.
    122 TEST_F(NamespaceSandboxTest, DISABLE_ON_ASAN(ChrootAndDropCapabilities)) {
    123   TestProc("ChrootMe");
    124 }
    125 
    126 MULTIPROCESS_TEST_MAIN(NestedNamespaceSandbox) {
    127   base::FileHandleMappingVector fds_to_remap = {
    128       std::make_pair(STDOUT_FILENO, STDOUT_FILENO),
    129       std::make_pair(STDERR_FILENO, STDERR_FILENO),
    130   };
    131   base::LaunchOptions launch_options;
    132   launch_options.fds_to_remap = &fds_to_remap;
    133   base::Process process = NamespaceSandbox::LaunchProcess(
    134       base::CommandLine(base::FilePath("/bin/true")), launch_options);
    135   CHECK(process.IsValid());
    136 
    137   const int kDummyExitCode = 42;
    138   int exit_code = kDummyExitCode;
    139   CHECK(process.WaitForExit(&exit_code));
    140   CHECK_EQ(0, exit_code);
    141   return 0;
    142 }
    143 
    144 TEST_F(NamespaceSandboxTest, NestedNamespaceSandbox) {
    145   TestProc("NestedNamespaceSandbox");
    146 }
    147 
    148 const int kNormalExitCode = 0;
    149 
    150 // Ensure that CHECK(false) is distinguishable from _exit(kNormalExitCode).
    151 // Allowing noise since CHECK(false) will write a stack trace to stderr.
    152 SANDBOX_TEST_ALLOW_NOISE(ForkInNewPidNamespace, CheckDoesNotReturnZero) {
    153   if (!Credentials::CanCreateProcessInNewUserNS()) {
    154     return;
    155   }
    156 
    157   CHECK(sandbox::Credentials::MoveToNewUserNS());
    158   const pid_t pid = NamespaceSandbox::ForkInNewPidNamespace(
    159       /*drop_capabilities_in_child=*/true);
    160   CHECK_GE(pid, 0);
    161 
    162   if (pid == 0) {
    163     CHECK(false);
    164     _exit(kNormalExitCode);
    165   }
    166 
    167   int status;
    168   PCHECK(waitpid(pid, &status, 0) == pid);
    169   if (WIFEXITED(status)) {
    170     CHECK_NE(kNormalExitCode, WEXITSTATUS(status));
    171   }
    172 }
    173 
    174 SANDBOX_TEST(ForkInNewPidNamespace, BasicUsage) {
    175   if (!Credentials::CanCreateProcessInNewUserNS()) {
    176     return;
    177   }
    178 
    179   CHECK(sandbox::Credentials::MoveToNewUserNS());
    180   const pid_t pid = NamespaceSandbox::ForkInNewPidNamespace(
    181       /*drop_capabilities_in_child=*/true);
    182   CHECK_GE(pid, 0);
    183 
    184   if (pid == 0) {
    185     CHECK_EQ(1, getpid());
    186     CHECK(!Credentials::HasAnyCapability());
    187     _exit(kNormalExitCode);
    188   }
    189 
    190   int status;
    191   PCHECK(waitpid(pid, &status, 0) == pid);
    192   CHECK(WIFEXITED(status));
    193   CHECK_EQ(kNormalExitCode, WEXITSTATUS(status));
    194 }
    195 
    196 SANDBOX_TEST(ForkInNewPidNamespace, ExitWithSignal) {
    197   if (!Credentials::CanCreateProcessInNewUserNS()) {
    198     return;
    199   }
    200 
    201   CHECK(sandbox::Credentials::MoveToNewUserNS());
    202   const pid_t pid = NamespaceSandbox::ForkInNewPidNamespace(
    203       /*drop_capabilities_in_child=*/true);
    204   CHECK_GE(pid, 0);
    205 
    206   if (pid == 0) {
    207     CHECK_EQ(1, getpid());
    208     CHECK(!Credentials::HasAnyCapability());
    209     CHECK(NamespaceSandbox::InstallTerminationSignalHandler(
    210         SIGTERM, NamespaceSandbox::SignalExitCode(SIGTERM)));
    211     while (true) {
    212       raise(SIGTERM);
    213     }
    214   }
    215 
    216   int status;
    217   PCHECK(waitpid(pid, &status, 0) == pid);
    218   CHECK(WIFEXITED(status));
    219   CHECK_EQ(NamespaceSandbox::SignalExitCode(SIGTERM), WEXITSTATUS(status));
    220 }
    221 
    222 volatile sig_atomic_t signal_handler_called;
    223 void ExitSuccessfully(int sig) {
    224   signal_handler_called = 1;
    225 }
    226 
    227 SANDBOX_TEST(InstallTerminationSignalHandler, DoesNotOverrideExistingHandlers) {
    228   struct sigaction action = {};
    229   action.sa_handler = &ExitSuccessfully;
    230   PCHECK(sigaction(SIGUSR1, &action, nullptr) == 0);
    231 
    232   NamespaceSandbox::InstallDefaultTerminationSignalHandlers();
    233   CHECK(!NamespaceSandbox::InstallTerminationSignalHandler(
    234             SIGUSR1, NamespaceSandbox::SignalExitCode(SIGUSR1)));
    235 
    236   raise(SIGUSR1);
    237   CHECK_EQ(1, signal_handler_called);
    238 }
    239 
    240 }  // namespace
    241 
    242 }  // namespace sandbox
    243