Home | History | Annotate | Download | only in services
      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 
     31 namespace sandbox {
     32 
     33 namespace {
     34 
     35 const char kSandboxUSERNSEnvironmentVarName[] = "SBX_USER_NS";
     36 const char kSandboxPIDNSEnvironmentVarName[] = "SBX_PID_NS";
     37 const char kSandboxNETNSEnvironmentVarName[] = "SBX_NET_NS";
     38 
     39 #if !defined(OS_NACL_NONSFI)
     40 class 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 
     65 void 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.
     74 int g_signal_exit_codes[64];
     75 
     76 void 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)
     90 NamespaceSandbox::Options::Options()
     91     : ns_types(CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWNET),
     92       fail_on_unsupported_ns_type(false) {}
     93 
     94 NamespaceSandbox::Options::~Options() {}
     95 
     96 // static
     97 base::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
    104 base::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
    111 base::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
    120 base::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
    167 pid_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
    187 void 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
    199 bool 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
    231 bool NamespaceSandbox::InNewUserNamespace() {
    232   return getenv(kSandboxUSERNSEnvironmentVarName) != nullptr;
    233 }
    234 
    235 // static
    236 bool NamespaceSandbox::InNewPidNamespace() {
    237   return getenv(kSandboxPIDNSEnvironmentVarName) != nullptr;
    238 }
    239 
    240 // static
    241 bool NamespaceSandbox::InNewNetNamespace() {
    242   return getenv(kSandboxNETNSEnvironmentVarName) != nullptr;
    243 }
    244 
    245 }  // namespace sandbox
    246