Home | History | Annotate | Download | only in mac
      1 // Copyright 2014 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/mac/bootstrap_sandbox.h"
      6 
      7 #include <servers/bootstrap.h>
      8 #include <unistd.h>
      9 
     10 #include "base/logging.h"
     11 #include "base/mac/foundation_util.h"
     12 #include "base/mac/mach_logging.h"
     13 #include "base/strings/stringprintf.h"
     14 #include "sandbox/mac/launchd_interception_server.h"
     15 
     16 namespace sandbox {
     17 
     18 const int kNotAPolicy = -1;
     19 
     20 // static
     21 scoped_ptr<BootstrapSandbox> BootstrapSandbox::Create() {
     22   scoped_ptr<BootstrapSandbox> null;  // Used for early returns.
     23   scoped_ptr<BootstrapSandbox> sandbox(new BootstrapSandbox());
     24   sandbox->server_.reset(new LaunchdInterceptionServer(sandbox.get()));
     25 
     26   // Check in with launchd to get the receive right for the server that is
     27   // published in the bootstrap namespace.
     28   mach_port_t port = MACH_PORT_NULL;
     29   kern_return_t kr = bootstrap_check_in(bootstrap_port,
     30       sandbox->server_bootstrap_name().c_str(), &port);
     31   if (kr != KERN_SUCCESS) {
     32     BOOTSTRAP_LOG(ERROR, kr)
     33         << "Failed to bootstrap_check_in the sandbox server.";
     34     return null.Pass();
     35   }
     36   base::mac::ScopedMachReceiveRight scoped_port(port);
     37 
     38   // Start the sandbox server.
     39   if (sandbox->server_->Initialize(scoped_port.get()))
     40     ignore_result(scoped_port.release());  // Transferred to server_.
     41   else
     42     return null.Pass();
     43 
     44   return sandbox.Pass();
     45 }
     46 
     47 BootstrapSandbox::~BootstrapSandbox() {
     48 }
     49 
     50 void BootstrapSandbox::RegisterSandboxPolicy(
     51     int sandbox_policy_id,
     52     const BootstrapSandboxPolicy& policy) {
     53   CHECK(IsPolicyValid(policy));
     54   CHECK_GT(sandbox_policy_id, kNotAPolicy);
     55   base::AutoLock lock(lock_);
     56   DCHECK(policies_.find(sandbox_policy_id) == policies_.end());
     57   policies_.insert(std::make_pair(sandbox_policy_id, policy));
     58 }
     59 
     60 void BootstrapSandbox::PrepareToForkWithPolicy(int sandbox_policy_id) {
     61   base::AutoLock lock(lock_);
     62 
     63   // Verify that this is a real policy.
     64   CHECK(policies_.find(sandbox_policy_id) != policies_.end());
     65   CHECK_EQ(kNotAPolicy, effective_policy_id_)
     66       << "Cannot nest calls to PrepareToForkWithPolicy()";
     67 
     68   // Store the policy for the process we're about to create.
     69   effective_policy_id_ = sandbox_policy_id;
     70 }
     71 
     72 // TODO(rsesek): The |lock_| needs to be taken twice because
     73 // base::LaunchProcess handles both fork+exec, and holding the lock for the
     74 // duration would block servicing of other bootstrap messages. If a better
     75 // LaunchProcess existed (do arbitrary work without layering violations), this
     76 // could be avoided.
     77 
     78 void BootstrapSandbox::FinishedFork(base::ProcessHandle handle) {
     79   base::AutoLock lock(lock_);
     80 
     81   CHECK_NE(kNotAPolicy, effective_policy_id_)
     82       << "Must PrepareToForkWithPolicy() before FinishedFork()";
     83 
     84   // Apply the policy to the new process.
     85   if (handle != base::kNullProcessHandle) {
     86     const auto& existing_process = sandboxed_processes_.find(handle);
     87     CHECK(existing_process == sandboxed_processes_.end());
     88     sandboxed_processes_.insert(std::make_pair(handle, effective_policy_id_));
     89     VLOG(3) << "Bootstrap sandbox enforced for pid " << handle;
     90   }
     91 
     92   effective_policy_id_ = kNotAPolicy;
     93 }
     94 
     95 void BootstrapSandbox::ChildDied(base::ProcessHandle handle) {
     96   base::AutoLock lock(lock_);
     97   const auto& it = sandboxed_processes_.find(handle);
     98   if (it != sandboxed_processes_.end())
     99     sandboxed_processes_.erase(it);
    100 }
    101 
    102 const BootstrapSandboxPolicy* BootstrapSandbox::PolicyForProcess(
    103     pid_t pid) const {
    104   base::AutoLock lock(lock_);
    105   const auto& process = sandboxed_processes_.find(pid);
    106 
    107   // The new child could send bootstrap requests before the parent calls
    108   // FinishedFork().
    109   int policy_id = effective_policy_id_;
    110   if (process != sandboxed_processes_.end()) {
    111     policy_id = process->second;
    112   }
    113 
    114   if (policy_id == kNotAPolicy)
    115     return NULL;
    116 
    117   return &policies_.find(policy_id)->second;
    118 }
    119 
    120 BootstrapSandbox::BootstrapSandbox()
    121     : server_bootstrap_name_(
    122           base::StringPrintf("%s.sandbox.%d", base::mac::BaseBundleID(),
    123               getpid())),
    124       real_bootstrap_port_(MACH_PORT_NULL),
    125       effective_policy_id_(kNotAPolicy) {
    126   mach_port_t port = MACH_PORT_NULL;
    127   kern_return_t kr = task_get_special_port(
    128       mach_task_self(), TASK_BOOTSTRAP_PORT, &port);
    129   MACH_CHECK(kr == KERN_SUCCESS, kr);
    130   real_bootstrap_port_.reset(port);
    131 }
    132 
    133 }  // namespace sandbox
    134