Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(195)

Side by Side Diff: sandbox/mac/launchd_interception_server.cc

Issue 264923003: Initial implementation of the Mac Bootstrap Sandbox. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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/launchd_interception_server.h"
6
7 #include <bsm/libbsm.h>
8 #include <servers/bootstrap.h>
9
10 #include "base/logging.h"
11 #include "sandbox/mac/bootstrap_sandbox.h"
12
13 namespace sandbox {
14
15 LaunchdInterceptionServer::LaunchdInterceptionServer(
16 const BootstrapSandbox* sandbox)
17 : sandbox_(sandbox),
18 server_port_(MACH_PORT_NULL),
19 sandbox_port_(MACH_PORT_NULL),
20 compat_shim_(GetLaunchdCompatibilityShim()) {
21 }
22
23 LaunchdInterceptionServer::~LaunchdInterceptionServer() {
24 dispatch_release(server_source_);
25 dispatch_release(server_queue_);
26 }
27
28 bool LaunchdInterceptionServer::Initialize() {
29 mach_port_t task = mach_task_self();
30
31 mach_port_t port;
32 if (mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &port) !=
33 KERN_SUCCESS) {
34 return false;
35 }
36 if (mach_port_insert_right(task, port, port, MACH_MSG_TYPE_MAKE_SEND) !=
37 KERN_SUCCESS) {
38 return false;
39 }
40 server_port_.reset(port);
41
42 server_queue_ = dispatch_queue_create(
43 "chromium.sandbox.LaunchdInterceptionServer", NULL);
Mark Mentovai 2014/05/06 20:51:50 org.chromium.blah?
Mark Mentovai 2014/05/06 20:51:50 Hopefully nothing on this queue will result in a c
Robert Sesek 2014/05/08 20:58:12 Oops lost org. somehow.
Robert Sesek 2014/05/08 20:58:12 It shouldn't…
44 server_source_ = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV,
45 server_port_.get(), 0, server_queue_);
46 dispatch_source_set_event_handler(server_source_, ^{ ReceiveMessage(); });
47 dispatch_resume(server_source_);
48
49 if (mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &port) !=
Mark Mentovai 2014/05/06 20:51:50 Can we get away with not even having a receive rig
Robert Sesek 2014/05/08 20:58:12 Nope, can't do that. xnu-2422.1.72/osfmk/ipc/mach_
50 KERN_SUCCESS) {
51 return false;
52 }
53 sandbox_port_.reset(port);
54
55 return true;
56 }
57
58 void LaunchdInterceptionServer::ReceiveMessage() {
59 // The buffer size comes from
60 // sizeof(union __RequestUnion__vproc_mig_job_subsystem) in launchd, and it
61 // is larger than the __ReplyUnion.
62 const mach_msg_size_t kBufferSize = 2096;
Mark Mentovai 2014/05/06 20:51:50 Does this include space for the trailer?
Robert Sesek 2014/05/08 20:58:12 Done.
63
64 const mach_msg_options_t kRcvOptions = MACH_RCV_MSG |
65 MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) |
66 MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT);
67
68 mach_port_t task = mach_task_self();
69 kern_return_t kr;
Mark Mentovai 2014/05/06 20:51:50 Don’t declare this ’til you use it.
Robert Sesek 2014/05/08 20:58:12 Done.
70
71 // A Mach message server-once. The system library to run a message server
72 // cannot be used here, because some requests are conditionally forwarded
73 // to another server.
74 mach_msg_header_t *request, *reply;
Mark Mentovai 2014/05/06 20:51:50 Don’t declare *reply ’til you use it. (And Chromiu
Robert Sesek 2014/05/08 20:58:12 Done.
75
76 kr = vm_allocate(task, reinterpret_cast<vm_address_t*>(&request), kBufferSize,
Mark Mentovai 2014/05/06 20:51:50 If you’re vm_allocating, you might as well round k
Robert Sesek 2014/05/08 20:58:12 Done.
77 VM_MAKE_TAG(VM_MEMORY_MACH_MSG)|TRUE);
Mark Mentovai 2014/05/06 20:51:50 Spaces around |. Same on line 85.
Robert Sesek 2014/05/08 20:58:12 Done.
78 if (kr != KERN_SUCCESS) {
79 LOG(ERROR) << "Failed to allocate request buffer. Error #" << kr << ": "
80 << mach_error_string(kr);
81 return;
82 }
83
84 kr = vm_allocate(task, reinterpret_cast<vm_address_t*>(&reply), kBufferSize,
85 VM_MAKE_TAG(VM_MEMORY_MACH_MSG)|TRUE);
86 if (kr != KERN_SUCCESS) {
87 LOG(ERROR) << "Failed to allocate reply buffer. Error #" << kr << ": "
Mark Mentovai 2014/05/06 20:51:50 Leaks the request buffer.
Robert Sesek 2014/05/08 20:58:12 Done.
88 << mach_error_string(kr);
89 return;
90 }
91
92 kr = mach_msg(request, kRcvOptions, 0, kBufferSize, server_port_.get(),
93 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
94 if (kr != KERN_SUCCESS) {
95 LOG(ERROR) << "Unable to receive message. Error #" << kr << ": "
Mark Mentovai 2014/05/06 20:51:50 Leaks the request and reply buffers. Do you want b
Robert Sesek 2014/05/08 20:58:12 Please. Want to upstream it?
96 << mach_error_string(kr);
97 return;
98 }
99
100 // Set up a reply message in case it will be used.
101 reply->msgh_bits = MACH_MSGH_BITS_REMOTE(reply->msgh_bits);
102 // Since mach_msg will automatically swap the request and reply ports,
103 // undo that.
104 reply->msgh_remote_port = request->msgh_remote_port;
105 reply->msgh_local_port = MACH_PORT_NULL;
106 // MIG servers simply add 100 to the request ID to generate the reply ID.
107 reply->msgh_id = request->msgh_id + 100;
108
109 // Process the message.
110 DemuxMessage(request, reply);
111
112 // Free any descriptors in the message body.
113 mach_msg_destroy(request);
114 mach_msg_destroy(reply);
115
116 // Checking return codes here is not useful, so don't.
117 vm_deallocate(task, reinterpret_cast<vm_address_t>(request), kBufferSize);
Mark Mentovai 2014/05/06 20:51:50 The nice thing about writing your own server loop
Robert Sesek 2014/05/08 20:58:12 I moved the buffers to be member variables, since
118 vm_deallocate(task, reinterpret_cast<vm_address_t>(reply), kBufferSize);
119 }
120
121 void LaunchdInterceptionServer::DemuxMessage(mach_msg_header_t* request,
122 mach_msg_header_t* reply) {
123 VLOG(3) << "Incoming message #" << request->msgh_id;
124
125 // Get the PID of the task that sent this request. This requires getting at
126 // the trailer of the message, from the header.
127 mach_msg_audit_trailer_t* trailer =
128 reinterpret_cast<mach_msg_audit_trailer_t*>(
129 reinterpret_cast<vm_address_t>(request) +
130 round_msg(request->msgh_size));
131 // TODO(rsesek): In the 10.7 SDK, there's audit_token_to_pid().
132 pid_t sender_pid;
133 audit_token_to_au32(trailer->msgh_audit,
134 NULL, NULL, NULL, NULL, NULL, &sender_pid, NULL, NULL);
135
136 if (sandbox_->PolicyForProcess(sender_pid) == NULL) {
137 // No sandbox policy is in place for the sender of this message, which
138 // means it is from the sandbox host proceess or an unsandboxed child.
Mark Mentovai 2014/05/06 20:51:50 Do we have unsandboxed children? Is it fair to req
Robert Sesek 2014/05/08 20:58:12 We do have unsandboxed children. Besides certain p
139 VLOG(3) << "Message from pid " << sender_pid << " forwarded to launchd";
140 ForwardMessage(request, reply);
141 return;
142 }
143
144 if (request->msgh_id == compat_shim_.msg_id_look_up2) {
145 // Filter messages sent via bootstrap_look_up to enforce the sandbox policy
146 // over the bootstrap namespace.
147 HandleLookUp(request, reply, sender_pid);
148 } else if (request->msgh_id == compat_shim_.msg_id_swap_integer) {
149 // The vproc system in launchd is needed to fully check-in processes with
150 // launchd. These messages are generally safe and merely permit swapping
151 // small bits of data across processes.
152 VLOG(2) << "Forwarding vproc swap message #" << request->msgh_id;
Mark Mentovai 2014/05/06 20:51:50 I’m still afraid of these. If they’re required for
Robert Sesek 2014/05/08 20:58:12 I'm pretty confident these are safe. Because launc
153 ForwardMessage(request, reply);
154 } else {
155 // All other messages are not permitted.
156 VLOG(1) << "Rejecting unhandled message #" << request->msgh_id;
157 RejectMessage(request, reply, MIG_REMOTE_ERROR);
158 }
159 }
160
161 void LaunchdInterceptionServer::HandleLookUp(mach_msg_header_t* request,
162 mach_msg_header_t* reply,
163 pid_t sender_pid) {
164 const std::string request_service_name(
165 compat_shim_.look_up2_get_request_name(request));
Mark Mentovai 2014/05/06 20:51:50 Can we make this interface use StringBuffer or som
Robert Sesek 2014/05/08 20:58:12 I'm assuming you meant StringPiece. But no because
166
167 VLOG(2) << "Incoming look_up2 request for " << request_service_name;
168
169 // Find the Rule for this service. If one is not found, use
170 // a safe default, POLICY_DENY_ERROR.
171 const BootstrapSandboxPolicy* policy = sandbox_->PolicyForProcess(sender_pid);
172 const BootstrapSandboxPolicy::const_iterator it =
173 policy->find(request_service_name);
174 Rule rule(POLICY_DENY_ERROR);
175 if (it != policy->end())
176 rule = it->second;
177
178 if (rule.result == POLICY_ALLOW) {
179 // This service is explicitly allowed, so this message will not be
180 // intercepted by the sandbox.
181 VLOG(1) << "Permitting and forwarding look_up2: " << request_service_name;
182 ForwardMessage(request, reply);
183 } else if (rule.result == POLICY_DENY_ERROR) {
184 // The child is not permitted to look up this service. Send a MIG error
185 // reply to the client. Returning a NULL or unserviced port for a look up
186 // can cause clients to crash or hang.
187 VLOG(1) << "Denying look_up2 with MIG error: " << request_service_name;
188 RejectMessage(request, reply, BOOTSTRAP_UNKNOWN_SERVICE);
189 } else if (rule.result == POLICY_DENY_DUMMY_PORT ||
190 rule.result == POLICY_SUBSTITUE_PORT) {
191 // The policy result is to deny access to the real service port, replying
192 // with a sandboxed port in its stead. Use either the dummy sandbox_port_
193 // or the one specified in the policy.
194 VLOG(1) << "Intercepting look_up2 with a sandboxed service port: "
195 << request_service_name;
196
197 mach_port_t result_port;
198 if (rule.result == POLICY_DENY_DUMMY_PORT)
199 result_port = sandbox_port_.get();
200 else
201 result_port = rule.substitute_port;
202
203 // Grant an additional send right on the result_port so that it can be
204 // sent to the sandboxed child process.
205 kern_return_t kr = mach_port_insert_right(mach_task_self(),
206 result_port, result_port, MACH_MSG_TYPE_MAKE_SEND);
207 if (kr != KERN_SUCCESS) {
208 LOG(ERROR) << "Unable to insert right on result_port. Error #" << kr
209 << ": " << mach_error_string(kr);
210 }
211
212 compat_shim_.look_up2_fill_reply(reply, result_port);
213 SendReply(reply);
214 } else {
215 NOTREACHED();
Mark Mentovai 2014/05/06 20:51:50 Send a reject message or the child will hang forev
Robert Sesek 2014/05/08 20:58:12 I think NOTREACHED() means NOTREACHED().
216 }
217 }
218
219 void LaunchdInterceptionServer::SendReply(mach_msg_header_t* reply) {
220 kern_return_t kr = mach_msg(reply, MACH_SEND_MSG, reply->msgh_size, 0,
221 MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
222 if (kr != KERN_SUCCESS) {
223 LOG(ERROR) << "Unable to send intercepted reply message. Error #"
224 << kr << ": " << mach_error_string(kr);
Mark Mentovai 2014/05/06 20:51:50 All of these…I should really check in my mach_logg
Robert Sesek 2014/05/08 20:58:12 Yes, you should :). Would review for free.
225 }
226 }
227
228 void LaunchdInterceptionServer::ForwardMessage(mach_msg_header_t* request,
229 mach_msg_header_t* reply) {
230 request->msgh_local_port = request->msgh_remote_port;
231 request->msgh_remote_port = sandbox_->real_bootstrap_port();
232 // Preserve the msgh_bits that do not deal with the local and remote ports.
233 request->msgh_bits = (request->msgh_bits & ~MACH_MSGH_BITS_PORTS_MASK) |
234 MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MOVE_SEND_ONCE);
235 kern_return_t kr = mach_msg_send(request);
236 if (kr != KERN_SUCCESS) {
237 LOG(ERROR) << "Unable to forward message to the real launchd. Error #"
238 << kr << ": " << mach_error_string(kr);
239 }
240 }
241
242 void LaunchdInterceptionServer::RejectMessage(mach_msg_header_t* request,
243 mach_msg_header_t* reply,
244 int error_code) {
245 mig_reply_error_t* error_reply = reinterpret_cast<mig_reply_error_t*>(reply);
246 error_reply->Head.msgh_size = sizeof(mig_reply_error_t);
247 error_reply->Head.msgh_bits =
248 MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MOVE_SEND_ONCE);
249 error_reply->NDR = NDR_record;
250 error_reply->RetCode = error_code;
251 SendReply(&error_reply->Head);
252 }
253
254 } // namespace sandbox
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698