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