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