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