[MD settings] moving attached() code
[chromium-blink-merge.git] / sandbox / mac / launchd_interception_server.cc
blobf466e77d4bc18d608d5bf7227d2fda104a650fcc
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/launchd_interception_server.h"
7 #include <servers/bootstrap.h>
9 #include "base/logging.h"
10 #include "base/mac/mac_util.h"
11 #include "base/mac/mach_logging.h"
12 #include "sandbox/mac/bootstrap_sandbox.h"
13 #include "sandbox/mac/mach_message_server.h"
14 #include "sandbox/mac/os_compatibility.h"
15 #include "sandbox/mac/xpc_message_server.h"
17 namespace sandbox {
19 // The buffer size for all launchd messages. This comes from
20 // sizeof(union __RequestUnion__vproc_mig_job_subsystem) in launchd, and it
21 // is larger than the __ReplyUnion.
22 const mach_msg_size_t kBufferSize = 2096;
24 LaunchdInterceptionServer::LaunchdInterceptionServer(
25 const BootstrapSandbox* sandbox)
26 : sandbox_(sandbox),
27 xpc_launchd_(false),
28 sandbox_port_(MACH_PORT_NULL),
29 compat_shim_(OSCompatibility::CreateForPlatform()) {
32 LaunchdInterceptionServer::~LaunchdInterceptionServer() {
35 bool LaunchdInterceptionServer::Initialize(mach_port_t server_receive_right) {
36 mach_port_t task = mach_task_self();
37 kern_return_t kr;
39 // Allocate the dummy sandbox port.
40 mach_port_t port;
41 if ((kr = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &port)) !=
42 KERN_SUCCESS) {
43 MACH_LOG(ERROR, kr) << "Failed to allocate dummy sandbox port.";
44 return false;
46 sandbox_port_.reset(port);
47 if ((kr = mach_port_insert_right(task, sandbox_port_, sandbox_port_,
48 MACH_MSG_TYPE_MAKE_SEND) != KERN_SUCCESS)) {
49 MACH_LOG(ERROR, kr) << "Failed to allocate dummy sandbox port send right.";
50 return false;
52 sandbox_send_port_.reset(sandbox_port_);
54 if (base::mac::IsOSYosemiteOrLater()) {
55 message_server_.reset(new XPCMessageServer(this, server_receive_right));
56 xpc_launchd_ = true;
57 } else {
58 message_server_.reset(
59 new MachMessageServer(this, server_receive_right, kBufferSize));
61 return message_server_->Initialize();
64 void LaunchdInterceptionServer::DemuxMessage(IPCMessage request) {
65 const uint64_t message_subsystem =
66 compat_shim_->GetMessageSubsystem(request);
67 const uint64_t message_id = compat_shim_->GetMessageID(request);
68 VLOG(3) << "Incoming message #" << message_subsystem << "," << message_id;
70 pid_t sender_pid = message_server_->GetMessageSenderPID(request);
71 const BootstrapSandboxPolicy* policy =
72 sandbox_->PolicyForProcess(sender_pid);
73 if (policy == NULL) {
74 // No sandbox policy is in place for the sender of this message, which
75 // means it came from the unknown. Reject it.
76 VLOG(3) << "Message from unknown pid " << sender_pid << " rejected.";
77 message_server_->RejectMessage(request, MIG_REMOTE_ERROR);
78 return;
81 if (compat_shim_->IsServiceLookUpRequest(request)) {
82 // Filter messages sent via bootstrap_look_up to enforce the sandbox policy
83 // over the bootstrap namespace.
84 HandleLookUp(request, policy);
85 } else if (compat_shim_->IsVprocSwapInteger(request)) {
86 // Ensure that any vproc_swap_integer requests are safe.
87 HandleSwapInteger(request);
88 } else if (compat_shim_->IsXPCDomainManagement(request)) {
89 // XPC domain management requests just require an ACK.
90 message_server_->SendReply(message_server_->CreateReply(request));
91 } else {
92 // All other messages are not permitted.
93 VLOG(1) << "Rejecting unhandled message #" << message_id;
94 message_server_->RejectMessage(request, MIG_REMOTE_ERROR);
98 void LaunchdInterceptionServer::HandleLookUp(
99 IPCMessage request,
100 const BootstrapSandboxPolicy* policy) {
101 const std::string request_service_name(
102 compat_shim_->GetServiceLookupName(request));
103 VLOG(2) << "Incoming look_up2 request for " << request_service_name;
105 // Find the Rule for this service. If a named rule is not found, use the
106 // default specified by the policy.
107 const BootstrapSandboxPolicy::NamedRules::const_iterator it =
108 policy->rules.find(request_service_name);
109 Rule rule(policy->default_rule);
110 if (it != policy->rules.end())
111 rule = it->second;
113 if (rule.result == POLICY_ALLOW) {
114 // This service is explicitly allowed, so this message will not be
115 // intercepted by the sandbox.
116 VLOG(1) << "Permitting and forwarding look_up2: " << request_service_name;
117 ForwardMessage(request);
118 } else if (rule.result == POLICY_DENY_ERROR) {
119 // The child is not permitted to look up this service. Send a MIG error
120 // reply to the client. Returning a NULL or unserviced port for a look up
121 // can cause clients to crash or hang.
122 VLOG(1) << "Denying look_up2 with MIG error: " << request_service_name;
123 message_server_->RejectMessage(request, BOOTSTRAP_UNKNOWN_SERVICE);
124 } else if (rule.result == POLICY_DENY_DUMMY_PORT ||
125 rule.result == POLICY_SUBSTITUTE_PORT) {
126 // The policy result is to deny access to the real service port, replying
127 // with a sandboxed port in its stead. Use either the dummy sandbox_port_
128 // or the one specified in the policy.
129 VLOG(1) << "Intercepting look_up2 with a sandboxed service port: "
130 << request_service_name;
132 mach_port_t result_port;
133 if (rule.result == POLICY_DENY_DUMMY_PORT)
134 result_port = sandbox_port_.get();
135 else
136 result_port = rule.substitute_port;
138 IPCMessage reply = message_server_->CreateReply(request);
139 compat_shim_->WriteServiceLookUpReply(reply, result_port);
140 // If the message was sent successfully, clear the result_port out of the
141 // message so that it is not destroyed at the end of ReceiveMessage. The
142 // above-inserted right has been moved out of the process, and destroying
143 // the message will unref yet another right.
144 if (message_server_->SendReply(reply))
145 compat_shim_->WriteServiceLookUpReply(reply, MACH_PORT_NULL);
146 } else {
147 NOTREACHED();
151 void LaunchdInterceptionServer::HandleSwapInteger(IPCMessage request) {
152 // Only allow getting information out of launchd. Do not allow setting
153 // values. Two commonly observed values that are retrieved are
154 // VPROC_GSK_MGR_PID and VPROC_GSK_TRANSACTIONS_ENABLED.
155 if (compat_shim_->IsSwapIntegerReadOnly(request)) {
156 VLOG(2) << "Forwarding vproc swap_integer message.";
157 ForwardMessage(request);
158 } else {
159 VLOG(2) << "Rejecting non-read-only swap_integer message.";
160 message_server_->RejectMessage(request, BOOTSTRAP_NOT_PRIVILEGED);
163 void LaunchdInterceptionServer::ForwardMessage(IPCMessage request) {
164 // If launchd is using XPC, then when the request is forwarded, it must
165 // contain a valid domain port. Because the client processes are sandboxed,
166 // they have not had their launchd domains uncorked (and launchd will
167 // reject the message as being from an invalid client). Instead, provide the
168 // original bootstrap as the domain port, so launchd services the request
169 // as if it were coming from the sandbox host process (this).
170 if (xpc_launchd_) {
171 // xpc_dictionary_set_mach_send increments the send right count.
172 xpc_dictionary_set_mach_send(request.xpc, "domain-port",
173 sandbox_->real_bootstrap_port());
176 message_server_->ForwardMessage(request, sandbox_->real_bootstrap_port());
179 } // namespace sandbox