Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / sandbox / mac / bootstrap_sandbox.cc
blobe8997f9ef68f774a5a826312add6fee87f47c71a
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.
5 #include "sandbox/mac/bootstrap_sandbox.h"
7 #include <servers/bootstrap.h>
8 #include <unistd.h>
10 #include "base/logging.h"
11 #include "base/mac/foundation_util.h"
12 #include "base/mac/mach_logging.h"
13 #include "base/rand_util.h"
14 #include "base/strings/stringprintf.h"
15 #include "sandbox/mac/launchd_interception_server.h"
16 #include "sandbox/mac/pre_exec_delegate.h"
18 namespace sandbox {
20 namespace {
22 struct SandboxCheckInRequest {
23 mach_msg_header_t header;
24 uint64_t token;
27 struct SandboxCheckInReply {
28 mach_msg_header_t header;
29 mach_msg_body_t body;
30 mach_msg_port_descriptor_t bootstrap_port;
33 class ScopedCallMachMsgDestroy {
34 public:
35 explicit ScopedCallMachMsgDestroy(mach_msg_header_t* message)
36 : message_(message) {}
38 ~ScopedCallMachMsgDestroy() {
39 if (message_)
40 mach_msg_destroy(message_);
43 void Disarm() {
44 message_ = nullptr;
47 private:
48 mach_msg_header_t* message_;
50 DISALLOW_COPY_AND_ASSIGN(ScopedCallMachMsgDestroy);
53 } // namespace
55 // static
56 scoped_ptr<BootstrapSandbox> BootstrapSandbox::Create() {
57 scoped_ptr<BootstrapSandbox> null; // Used for early returns.
58 scoped_ptr<BootstrapSandbox> sandbox(new BootstrapSandbox());
59 sandbox->launchd_server_.reset(new LaunchdInterceptionServer(sandbox.get()));
61 // Check in with launchd to get the receive right for the server that is
62 // published in the bootstrap namespace.
63 mach_port_t port = MACH_PORT_NULL;
64 kern_return_t kr = bootstrap_check_in(bootstrap_port,
65 sandbox->server_bootstrap_name().c_str(), &port);
66 if (kr != KERN_SUCCESS) {
67 BOOTSTRAP_LOG(ERROR, kr)
68 << "Failed to bootstrap_check_in the sandbox server.";
69 return null.Pass();
71 sandbox->check_in_port_.reset(port);
73 BootstrapSandbox* __block sandbox_ptr = sandbox.get();
74 sandbox->check_in_server_.reset(new base::DispatchSourceMach(
75 "org.chromium.sandbox.BootstrapClientManager",
76 sandbox->check_in_port_.get(),
77 ^{ sandbox_ptr->HandleChildCheckIn(); }));
78 sandbox->check_in_server_->Resume();
80 // Start the sandbox server.
81 if (!sandbox->launchd_server_->Initialize(MACH_PORT_NULL))
82 return null.Pass();
84 return sandbox.Pass();
87 // Warning: This function must be safe to call in
88 // PreExecDelegate::RunAsyncSafe().
89 // static
90 bool BootstrapSandbox::ClientCheckIn(mach_port_t sandbox_server_port,
91 uint64_t sandbox_token,
92 mach_port_t* new_bootstrap_port) {
93 // Create a reply port for the check in message.
94 mach_port_t reply_port;
95 kern_return_t kr = mach_port_allocate(mach_task_self(),
96 MACH_PORT_RIGHT_RECEIVE,
97 &reply_port);
98 if (kr != KERN_SUCCESS) {
99 RAW_LOG(ERROR, "ClientCheckIn: mach_port_allocate failed");
100 return false;
102 base::mac::ScopedMachReceiveRight scoped_reply_port(reply_port);
104 // Check in with the sandbox server, presenting the |sandbox_token| in
105 // exchange for a new task bootstrap port.
106 union {
107 SandboxCheckInRequest request;
108 struct {
109 SandboxCheckInReply reply;
110 mach_msg_trailer_t trailer;
112 } msg = {};
113 msg.request.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,
114 MACH_MSG_TYPE_MAKE_SEND_ONCE);
115 msg.request.header.msgh_remote_port = sandbox_server_port;
116 msg.request.header.msgh_local_port = reply_port;
117 msg.request.header.msgh_size = sizeof(msg);
118 msg.request.token = sandbox_token;
120 kr = mach_msg(&msg.request.header, MACH_SEND_MSG | MACH_RCV_MSG,
121 sizeof(msg.request), sizeof(msg), reply_port,
122 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
123 if (kr == KERN_SUCCESS) {
124 *new_bootstrap_port = msg.reply.bootstrap_port.name;
125 return true;
126 } else {
127 RAW_LOG(ERROR, "ClientCheckIn: mach_msg failed");
128 return false;
132 BootstrapSandbox::~BootstrapSandbox() {
135 void BootstrapSandbox::RegisterSandboxPolicy(
136 int sandbox_policy_id,
137 const BootstrapSandboxPolicy& policy) {
138 CHECK(IsPolicyValid(policy));
139 base::AutoLock lock(lock_);
140 DCHECK(policies_.find(sandbox_policy_id) == policies_.end());
141 policies_.insert(std::make_pair(sandbox_policy_id, policy));
144 scoped_ptr<PreExecDelegate> BootstrapSandbox::NewClient(int sandbox_policy_id) {
145 base::AutoLock lock(lock_);
147 DCHECK(policies_.find(sandbox_policy_id) != policies_.end());
149 uint64_t token;
150 while (true) {
151 token = base::RandUint64();
152 if (awaiting_processes_.find(token) == awaiting_processes_.end())
153 break;
156 awaiting_processes_[token] = sandbox_policy_id;
157 return make_scoped_ptr(new PreExecDelegate(server_bootstrap_name_, token));
160 void BootstrapSandbox::RevokeToken(uint64_t token) {
161 base::AutoLock lock(lock_);
162 const auto& it = awaiting_processes_.find(token);
163 if (it != awaiting_processes_.end())
164 awaiting_processes_.erase(it);
167 void BootstrapSandbox::InvalidateClient(base::ProcessHandle handle) {
168 base::AutoLock lock(lock_);
169 const auto& it = sandboxed_processes_.find(handle);
170 if (it != sandboxed_processes_.end())
171 sandboxed_processes_.erase(it);
174 const BootstrapSandboxPolicy* BootstrapSandbox::PolicyForProcess(
175 pid_t pid) const {
176 base::AutoLock lock(lock_);
177 const auto& process = sandboxed_processes_.find(pid);
178 if (process != sandboxed_processes_.end()) {
179 return &policies_.find(process->second)->second;
182 return nullptr;
185 BootstrapSandbox::BootstrapSandbox()
186 : server_bootstrap_name_(
187 base::StringPrintf("%s.sandbox.%d", base::mac::BaseBundleID(),
188 getpid())),
189 real_bootstrap_port_(MACH_PORT_NULL) {
190 mach_port_t port = MACH_PORT_NULL;
191 kern_return_t kr = task_get_special_port(
192 mach_task_self(), TASK_BOOTSTRAP_PORT, &port);
193 MACH_CHECK(kr == KERN_SUCCESS, kr);
194 real_bootstrap_port_.reset(port);
197 void BootstrapSandbox::HandleChildCheckIn() {
198 struct {
199 SandboxCheckInRequest request;
200 mach_msg_audit_trailer_t trailer;
201 } msg = {};
202 msg.request.header.msgh_local_port = check_in_port_.get();
203 msg.request.header.msgh_size = sizeof(msg.request);
204 const mach_msg_option_t kOptions = MACH_RCV_MSG |
205 MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) |
206 MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT);
207 kern_return_t kr = mach_msg(&msg.request.header, kOptions, 0,
208 sizeof(msg), check_in_port_.get(),
209 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
210 if (kr != KERN_SUCCESS) {
211 MACH_LOG(ERROR, kr) << "HandleChildCheckIn mach_msg MACH_RCV_MSG";
212 return;
215 // Call mach_msg_destroy to clean up the reply send-once right.
216 ScopedCallMachMsgDestroy message_destroyer(&msg.request.header);
218 pid_t client_pid;
219 audit_token_to_au32(msg.trailer.msgh_audit, nullptr, nullptr, nullptr,
220 nullptr, nullptr, &client_pid, nullptr, nullptr);
223 base::AutoLock lock(lock_);
225 auto awaiting_it = awaiting_processes_.find(msg.request.token);
226 if (awaiting_it == awaiting_processes_.end()) {
227 LOG(ERROR) << "Received sandbox check-in message from unknown client.";
228 return;
231 CHECK(sandboxed_processes_.find(client_pid) == sandboxed_processes_.end());
232 sandboxed_processes_[client_pid] = awaiting_it->second;
233 awaiting_processes_.erase(awaiting_it);
236 SandboxCheckInReply reply = {};
237 reply.header.msgh_bits = MACH_MSGH_BITS_REMOTE(msg.request.header.msgh_bits) |
238 MACH_MSGH_BITS_COMPLEX;
239 reply.header.msgh_remote_port = msg.request.header.msgh_remote_port;
240 reply.header.msgh_size = sizeof(reply);
241 reply.body.msgh_descriptor_count = 1;
242 reply.bootstrap_port.name = launchd_server_->server_port();
243 reply.bootstrap_port.disposition = MACH_MSG_TYPE_MAKE_SEND;
244 reply.bootstrap_port.type = MACH_MSG_PORT_DESCRIPTOR;
246 kr = mach_msg(&reply.header, MACH_SEND_MSG | MACH_SEND_TIMEOUT,
247 sizeof(reply), 0, MACH_PORT_NULL, 100 /*ms*/, MACH_PORT_NULL);
248 if (kr == KERN_SUCCESS) {
249 message_destroyer.Disarm(); // The send-once was consumed at mach_msg().
250 } else {
252 base::AutoLock lock(lock_);
253 sandboxed_processes_.erase(client_pid);
255 MACH_LOG(ERROR, kr) << "HandleChildCheckIn mach_msg MACH_SEND_MSG";
259 } // namespace sandbox