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 <sched.h> 8#include <signal.h> 9#include <stddef.h> 10#include <stdlib.h> 11#include <sys/types.h> 12#include <unistd.h> 13 14#include <string> 15#include <utility> 16#include <vector> 17 18#include "base/command_line.h" 19#include "base/environment.h" 20#include "base/files/scoped_file.h" 21#include "base/logging.h" 22#include "base/macros.h" 23#include "base/posix/eintr_wrapper.h" 24#include "base/process/launch.h" 25#include "base/process/process.h" 26#include "sandbox/linux/services/credentials.h" 27#include "sandbox/linux/services/namespace_utils.h" 28#include "sandbox/linux/services/syscall_wrappers.h" 29#include "sandbox/linux/system_headers/linux_signal.h" 30 31namespace sandbox { 32 33namespace { 34 35const char kSandboxUSERNSEnvironmentVarName[] = "SBX_USER_NS"; 36const char kSandboxPIDNSEnvironmentVarName[] = "SBX_PID_NS"; 37const char kSandboxNETNSEnvironmentVarName[] = "SBX_NET_NS"; 38 39#if !defined(OS_NACL_NONSFI) 40class WriteUidGidMapDelegate : public base::LaunchOptions::PreExecDelegate { 41 public: 42 WriteUidGidMapDelegate() 43 : uid_(getuid()), 44 gid_(getgid()), 45 supports_deny_setgroups_( 46 NamespaceUtils::KernelSupportsDenySetgroups()) {} 47 48 ~WriteUidGidMapDelegate() override {} 49 50 void RunAsyncSafe() override { 51 if (supports_deny_setgroups_) { 52 RAW_CHECK(NamespaceUtils::DenySetgroups()); 53 } 54 RAW_CHECK(NamespaceUtils::WriteToIdMapFile("/proc/self/uid_map", uid_)); 55 RAW_CHECK(NamespaceUtils::WriteToIdMapFile("/proc/self/gid_map", gid_)); 56 } 57 58 private: 59 const uid_t uid_; 60 const gid_t gid_; 61 const bool supports_deny_setgroups_; 62 DISALLOW_COPY_AND_ASSIGN(WriteUidGidMapDelegate); 63}; 64 65void SetEnvironForNamespaceType(base::EnvironmentMap* environ, 66 base::NativeEnvironmentString env_var, 67 bool value) { 68 // An empty string causes the env var to be unset in the child process. 69 (*environ)[env_var] = value ? "1" : ""; 70} 71#endif // !defined(OS_NACL_NONSFI) 72 73// Linux supports up to 64 signals. This should be updated if that ever changes. 74int g_signal_exit_codes[64]; 75 76void TerminationSignalHandler(int sig) { 77 // Return a special exit code so that the process is detected as terminated by 78 // a signal. 79 const size_t sig_idx = static_cast<size_t>(sig); 80 if (sig_idx < arraysize(g_signal_exit_codes)) { 81 _exit(g_signal_exit_codes[sig_idx]); 82 } 83 84 _exit(NamespaceSandbox::SignalExitCode(sig)); 85} 86 87} // namespace 88 89#if !defined(OS_NACL_NONSFI) 90NamespaceSandbox::Options::Options() 91 : ns_types(CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWNET), 92 fail_on_unsupported_ns_type(false) {} 93 94NamespaceSandbox::Options::~Options() {} 95 96// static 97base::Process NamespaceSandbox::LaunchProcess( 98 const base::CommandLine& cmdline, 99 const base::LaunchOptions& launch_options) { 100 return LaunchProcessWithOptions(cmdline.argv(), launch_options, Options()); 101} 102 103// static 104base::Process NamespaceSandbox::LaunchProcess( 105 const std::vector<std::string>& argv, 106 const base::LaunchOptions& launch_options) { 107 return LaunchProcessWithOptions(argv, launch_options, Options()); 108} 109 110// static 111base::Process NamespaceSandbox::LaunchProcessWithOptions( 112 const base::CommandLine& cmdline, 113 const base::LaunchOptions& launch_options, 114 const Options& ns_sandbox_options) { 115 return LaunchProcessWithOptions(cmdline.argv(), launch_options, 116 ns_sandbox_options); 117} 118 119// static 120base::Process NamespaceSandbox::LaunchProcessWithOptions( 121 const std::vector<std::string>& argv, 122 const base::LaunchOptions& launch_options, 123 const Options& ns_sandbox_options) { 124 // These fields may not be set by the caller. 125 CHECK(launch_options.pre_exec_delegate == nullptr); 126 CHECK_EQ(0, launch_options.clone_flags); 127 128 int clone_flags = 0; 129 const int kSupportedTypes[] = {CLONE_NEWUSER, CLONE_NEWPID, CLONE_NEWNET}; 130 for (const int ns_type : kSupportedTypes) { 131 if ((ns_type & ns_sandbox_options.ns_types) == 0) { 132 continue; 133 } 134 135 if (NamespaceUtils::KernelSupportsUnprivilegedNamespace(ns_type)) { 136 clone_flags |= ns_type; 137 } else if (ns_sandbox_options.fail_on_unsupported_ns_type) { 138 return base::Process(); 139 } 140 } 141 CHECK(clone_flags & CLONE_NEWUSER); 142 143 WriteUidGidMapDelegate write_uid_gid_map_delegate; 144 145 base::LaunchOptions launch_options_copy = launch_options; 146 launch_options_copy.pre_exec_delegate = &write_uid_gid_map_delegate; 147 launch_options_copy.clone_flags = clone_flags; 148 149 const std::pair<int, const char*> clone_flag_environ[] = { 150 std::make_pair(CLONE_NEWUSER, kSandboxUSERNSEnvironmentVarName), 151 std::make_pair(CLONE_NEWPID, kSandboxPIDNSEnvironmentVarName), 152 std::make_pair(CLONE_NEWNET, kSandboxNETNSEnvironmentVarName), 153 }; 154 155 base::EnvironmentMap* environ = &launch_options_copy.environ; 156 for (const auto& entry : clone_flag_environ) { 157 const int flag = entry.first; 158 const char* environ_name = entry.second; 159 SetEnvironForNamespaceType(environ, environ_name, clone_flags & flag); 160 } 161 162 return base::LaunchProcess(argv, launch_options_copy); 163} 164#endif // !defined(OS_NACL_NONSFI) 165 166// static 167pid_t NamespaceSandbox::ForkInNewPidNamespace(bool drop_capabilities_in_child) { 168 const pid_t pid = 169 base::ForkWithFlags(CLONE_NEWPID | LINUX_SIGCHLD, nullptr, nullptr); 170 if (pid < 0) { 171 return pid; 172 } 173 174 if (pid == 0) { 175 DCHECK_EQ(1, getpid()); 176 if (drop_capabilities_in_child) { 177 // Since we just forked, we are single-threaded, so this should be safe. 178 CHECK(Credentials::DropAllCapabilitiesOnCurrentThread()); 179 } 180 return 0; 181 } 182 183 return pid; 184} 185 186// static 187void NamespaceSandbox::InstallDefaultTerminationSignalHandlers() { 188 static const int kDefaultTermSignals[] = { 189 LINUX_SIGHUP, LINUX_SIGINT, LINUX_SIGABRT, LINUX_SIGQUIT, 190 LINUX_SIGPIPE, LINUX_SIGTERM, LINUX_SIGUSR1, LINUX_SIGUSR2, 191 }; 192 193 for (const int sig : kDefaultTermSignals) { 194 InstallTerminationSignalHandler(sig, SignalExitCode(sig)); 195 } 196} 197 198// static 199bool NamespaceSandbox::InstallTerminationSignalHandler( 200 int sig, 201 int exit_code) { 202 struct sigaction old_action; 203 PCHECK(sys_sigaction(sig, nullptr, &old_action) == 0); 204 205#if !defined(OS_NACL_NONSFI) 206 if (old_action.sa_flags & SA_SIGINFO && 207 old_action.sa_sigaction != nullptr) { 208 return false; 209 } 210#endif 211 212 if (old_action.sa_handler != LINUX_SIG_DFL) { 213 return false; 214 } 215 216 const size_t sig_idx = static_cast<size_t>(sig); 217 CHECK_LT(sig_idx, arraysize(g_signal_exit_codes)); 218 219 DCHECK_GE(exit_code, 0); 220 DCHECK_LT(exit_code, 256); 221 222 g_signal_exit_codes[sig_idx] = exit_code; 223 224 struct sigaction action = {}; 225 action.sa_handler = &TerminationSignalHandler; 226 PCHECK(sys_sigaction(sig, &action, nullptr) == 0); 227 return true; 228} 229 230// static 231bool NamespaceSandbox::InNewUserNamespace() { 232 return getenv(kSandboxUSERNSEnvironmentVarName) != nullptr; 233} 234 235// static 236bool NamespaceSandbox::InNewPidNamespace() { 237 return getenv(kSandboxPIDNSEnvironmentVarName) != nullptr; 238} 239 240// static 241bool NamespaceSandbox::InNewNetNamespace() { 242 return getenv(kSandboxNETNSEnvironmentVarName) != nullptr; 243} 244 245} // namespace sandbox 246