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
16namespace sandbox {
17
18const int kNotAPolicy = -1;
19
20// static
21scoped_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
47BootstrapSandbox::~BootstrapSandbox() {
48}
49
50void 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
60void 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
78void 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
95void 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
102const 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
120BootstrapSandbox::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