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>
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"
22 struct SandboxCheckInRequest
{
23 mach_msg_header_t header
;
27 struct SandboxCheckInReply
{
28 mach_msg_header_t header
;
30 mach_msg_port_descriptor_t bootstrap_port
;
33 class ScopedCallMachMsgDestroy
{
35 explicit ScopedCallMachMsgDestroy(mach_msg_header_t
* message
)
36 : message_(message
) {}
38 ~ScopedCallMachMsgDestroy() {
40 mach_msg_destroy(message_
);
48 mach_msg_header_t
* message_
;
50 DISALLOW_COPY_AND_ASSIGN(ScopedCallMachMsgDestroy
);
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.";
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
))
84 return sandbox
.Pass();
87 // Warning: This function must be safe to call in
88 // PreExecDelegate::RunAsyncSafe().
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
,
98 if (kr
!= KERN_SUCCESS
) {
99 RAW_LOG(ERROR
, "ClientCheckIn: mach_port_allocate failed");
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.
107 SandboxCheckInRequest request
;
109 SandboxCheckInReply reply
;
110 mach_msg_trailer_t trailer
;
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
;
127 RAW_LOG(ERROR
, "ClientCheckIn: mach_msg failed");
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());
151 token
= base::RandUint64();
152 if (awaiting_processes_
.find(token
) == awaiting_processes_
.end())
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(
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
;
185 BootstrapSandbox::BootstrapSandbox()
186 : server_bootstrap_name_(
187 base::StringPrintf("%s.sandbox.%d", base::mac::BaseBundleID(),
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() {
199 SandboxCheckInRequest request
;
200 mach_msg_audit_trailer_t trailer
;
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";
215 // Call mach_msg_destroy to clean up the reply send-once right.
216 ScopedCallMachMsgDestroy
message_destroyer(&msg
.request
.header
);
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.";
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().
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