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"
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
)
27 sandbox_port_(MACH_PORT_NULL
),
28 compat_shim_(OSCompatibility::CreateForPlatform()) {
31 LaunchdInterceptionServer::~LaunchdInterceptionServer() {
34 bool LaunchdInterceptionServer::Initialize(mach_port_t server_receive_right
) {
35 mach_port_t task
= mach_task_self();
38 // Allocate the dummy sandbox port.
40 if ((kr
= mach_port_allocate(task
, MACH_PORT_RIGHT_RECEIVE
, &port
)) !=
42 MACH_LOG(ERROR
, kr
) << "Failed to allocate dummy sandbox port.";
45 sandbox_port_
.reset(port
);
46 if ((kr
= mach_port_insert_right(task
, sandbox_port_
, sandbox_port_
,
47 MACH_MSG_TYPE_MAKE_SEND
) != KERN_SUCCESS
)) {
48 MACH_LOG(ERROR
, kr
) << "Failed to allocate dummy sandbox port send right.";
51 sandbox_send_port_
.reset(sandbox_port_
);
53 if (base::mac::IsOSYosemiteOrLater()) {
54 message_server_
.reset(new XPCMessageServer(this, server_receive_right
));
56 message_server_
.reset(
57 new MachMessageServer(this, server_receive_right
, kBufferSize
));
59 return message_server_
->Initialize();
62 void LaunchdInterceptionServer::DemuxMessage(IPCMessage request
) {
63 const uint64_t message_subsystem
=
64 compat_shim_
->GetMessageSubsystem(request
);
65 const uint64_t message_id
= compat_shim_
->GetMessageID(request
);
66 VLOG(3) << "Incoming message #" << message_subsystem
<< "," << message_id
;
68 pid_t sender_pid
= message_server_
->GetMessageSenderPID(request
);
69 const BootstrapSandboxPolicy
* policy
=
70 sandbox_
->PolicyForProcess(sender_pid
);
72 // No sandbox policy is in place for the sender of this message, which
73 // means it came from the unknown. Reject it.
74 VLOG(3) << "Message from unknown pid " << sender_pid
<< " rejected.";
75 message_server_
->RejectMessage(request
, MIG_REMOTE_ERROR
);
79 if (compat_shim_
->IsServiceLookUpRequest(request
)) {
80 // Filter messages sent via bootstrap_look_up to enforce the sandbox policy
81 // over the bootstrap namespace.
82 HandleLookUp(request
, policy
);
83 } else if (compat_shim_
->IsVprocSwapInteger(request
)) {
84 // Ensure that any vproc_swap_integer requests are safe.
85 HandleSwapInteger(request
);
86 } else if (compat_shim_
->IsXPCDomainManagement(request
)) {
87 // XPC domain management requests just require an ACK.
88 message_server_
->SendReply(message_server_
->CreateReply(request
));
90 // All other messages are not permitted.
91 VLOG(1) << "Rejecting unhandled message #" << message_id
;
92 message_server_
->RejectMessage(request
, MIG_REMOTE_ERROR
);
96 void LaunchdInterceptionServer::HandleLookUp(
98 const BootstrapSandboxPolicy
* policy
) {
99 const std::string
request_service_name(
100 compat_shim_
->GetServiceLookupName(request
));
101 VLOG(2) << "Incoming look_up2 request for " << request_service_name
;
103 // Find the Rule for this service. If a named rule is not found, use the
104 // default specified by the policy.
105 const BootstrapSandboxPolicy::NamedRules::const_iterator it
=
106 policy
->rules
.find(request_service_name
);
107 Rule
rule(policy
->default_rule
);
108 if (it
!= policy
->rules
.end())
111 if (rule
.result
== POLICY_ALLOW
) {
112 // This service is explicitly allowed, so this message will not be
113 // intercepted by the sandbox.
114 VLOG(1) << "Permitting and forwarding look_up2: " << request_service_name
;
115 ForwardMessage(request
);
116 } else if (rule
.result
== POLICY_DENY_ERROR
) {
117 // The child is not permitted to look up this service. Send a MIG error
118 // reply to the client. Returning a NULL or unserviced port for a look up
119 // can cause clients to crash or hang.
120 VLOG(1) << "Denying look_up2 with MIG error: " << request_service_name
;
121 message_server_
->RejectMessage(request
, BOOTSTRAP_UNKNOWN_SERVICE
);
122 } else if (rule
.result
== POLICY_DENY_DUMMY_PORT
||
123 rule
.result
== POLICY_SUBSTITUTE_PORT
) {
124 // The policy result is to deny access to the real service port, replying
125 // with a sandboxed port in its stead. Use either the dummy sandbox_port_
126 // or the one specified in the policy.
127 VLOG(1) << "Intercepting look_up2 with a sandboxed service port: "
128 << request_service_name
;
130 mach_port_t result_port
;
131 if (rule
.result
== POLICY_DENY_DUMMY_PORT
)
132 result_port
= sandbox_port_
.get();
134 result_port
= rule
.substitute_port
;
136 IPCMessage reply
= message_server_
->CreateReply(request
);
137 compat_shim_
->WriteServiceLookUpReply(reply
, result_port
);
138 // If the message was sent successfully, clear the result_port out of the
139 // message so that it is not destroyed at the end of ReceiveMessage. The
140 // above-inserted right has been moved out of the process, and destroying
141 // the message will unref yet another right.
142 if (message_server_
->SendReply(reply
))
143 compat_shim_
->WriteServiceLookUpReply(reply
, MACH_PORT_NULL
);
149 void LaunchdInterceptionServer::HandleSwapInteger(IPCMessage request
) {
150 // Only allow getting information out of launchd. Do not allow setting
151 // values. Two commonly observed values that are retrieved are
152 // VPROC_GSK_MGR_PID and VPROC_GSK_TRANSACTIONS_ENABLED.
153 if (compat_shim_
->IsSwapIntegerReadOnly(request
)) {
154 VLOG(2) << "Forwarding vproc swap_integer message.";
155 ForwardMessage(request
);
157 VLOG(2) << "Rejecting non-read-only swap_integer message.";
158 message_server_
->RejectMessage(request
, BOOTSTRAP_NOT_PRIVILEGED
);
161 void LaunchdInterceptionServer::ForwardMessage(IPCMessage request
) {
162 message_server_
->ForwardMessage(request
, sandbox_
->real_bootstrap_port());
165 } // namespace sandbox