• 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/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 
RootDirectoryIsEmpty()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:
TestProc(const std::string & procname)44   void TestProc(const std::string& procname) {
45     TestProcWithOptions(procname, NamespaceSandbox::Options());
46   }
47 
TestProcWithOptions(const std::string & procname,const NamespaceSandbox::Options & ns_sandbox_options)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 
MULTIPROCESS_TEST_MAIN(SimpleChildProcess)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 
TEST_F(NamespaceSandboxTest,BasicUsage)88 TEST_F(NamespaceSandboxTest, BasicUsage) {
89   TestProc("SimpleChildProcess");
90 }
91 
MULTIPROCESS_TEST_MAIN(PidNsOnlyChildProcess)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 
TEST_F(NamespaceSandboxTest,BasicUsageWithOptions)107 TEST_F(NamespaceSandboxTest, BasicUsageWithOptions) {
108   NamespaceSandbox::Options options;
109   options.ns_types = CLONE_NEWUSER | CLONE_NEWPID;
110   TestProcWithOptions("PidNsOnlyChildProcess", options);
111 }
112 
MULTIPROCESS_TEST_MAIN(ChrootMe)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.
TEST_F(NamespaceSandboxTest,DISABLE_ON_ASAN (ChrootAndDropCapabilities))122 TEST_F(NamespaceSandboxTest, DISABLE_ON_ASAN(ChrootAndDropCapabilities)) {
123   TestProc("ChrootMe");
124 }
125 
MULTIPROCESS_TEST_MAIN(NestedNamespaceSandbox)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 
TEST_F(NamespaceSandboxTest,NestedNamespaceSandbox)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.
SANDBOX_TEST_ALLOW_NOISE(ForkInNewPidNamespace,CheckDoesNotReturnZero)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 
SANDBOX_TEST(ForkInNewPidNamespace,BasicUsage)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 
SANDBOX_TEST(ForkInNewPidNamespace,ExitWithSignal)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;
ExitSuccessfully(int sig)223 void ExitSuccessfully(int sig) {
224   signal_handler_called = 1;
225 }
226 
SANDBOX_TEST(InstallTerminationSignalHandler,DoesNotOverrideExistingHandlers)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