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 <servers/bootstrap.h>
8
9 #include "base/logging.h"
10 #include "base/mac/mach_logging.h"
11 #include "sandbox/mac/bootstrap_sandbox.h"
12
13 namespace sandbox {
14
15 // The buffer size for all launchd messages. This comes from
16 // sizeof(union __RequestUnion__vproc_mig_job_subsystem) in launchd, and it
17 // is larger than the __ReplyUnion.
18 const mach_msg_size_t kBufferSize = 2096;
19
LaunchdInterceptionServer(const BootstrapSandbox * sandbox)20 LaunchdInterceptionServer::LaunchdInterceptionServer(
21 const BootstrapSandbox* sandbox)
22 : sandbox_(sandbox),
23 sandbox_port_(MACH_PORT_NULL),
24 compat_shim_(GetLaunchdCompatibilityShim()) {
25 }
26
~LaunchdInterceptionServer()27 LaunchdInterceptionServer::~LaunchdInterceptionServer() {
28 }
29
Initialize(mach_port_t server_receive_right)30 bool LaunchdInterceptionServer::Initialize(mach_port_t server_receive_right) {
31 mach_port_t task = mach_task_self();
32 kern_return_t kr;
33
34 // Allocate the dummy sandbox port.
35 mach_port_t port;
36 if ((kr = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &port)) !=
37 KERN_SUCCESS) {
38 MACH_LOG(ERROR, kr) << "Failed to allocate dummy sandbox port.";
39 return false;
40 }
41 sandbox_port_.reset(port);
42 if ((kr = mach_port_insert_right(task, sandbox_port_, sandbox_port_,
43 MACH_MSG_TYPE_MAKE_SEND) != KERN_SUCCESS)) {
44 MACH_LOG(ERROR, kr) << "Failed to allocate dummy sandbox port send right.";
45 return false;
46 }
47 sandbox_send_port_.reset(sandbox_port_);
48
49 message_server_.reset(
50 new MachMessageServer(this, server_receive_right, kBufferSize));
51 return message_server_->Initialize();
52 }
53
DemuxMessage(mach_msg_header_t * request,mach_msg_header_t * reply)54 void LaunchdInterceptionServer::DemuxMessage(mach_msg_header_t* request,
55 mach_msg_header_t* reply) {
56 VLOG(3) << "Incoming message #" << request->msgh_id;
57
58 pid_t sender_pid = message_server_->GetMessageSenderPID(request);
59 const BootstrapSandboxPolicy* policy =
60 sandbox_->PolicyForProcess(sender_pid);
61 if (policy == NULL) {
62 // No sandbox policy is in place for the sender of this message, which
63 // means it came from the unknown. Reject it.
64 VLOG(3) << "Message from unknown pid " << sender_pid << " rejected.";
65 message_server_->RejectMessage(request, MIG_REMOTE_ERROR);
66 return;
67 }
68
69 if (request->msgh_id == compat_shim_.msg_id_look_up2) {
70 // Filter messages sent via bootstrap_look_up to enforce the sandbox policy
71 // over the bootstrap namespace.
72 HandleLookUp(request, reply, policy);
73 } else if (request->msgh_id == compat_shim_.msg_id_swap_integer) {
74 // Ensure that any vproc_swap_integer requests are safe.
75 HandleSwapInteger(request, reply);
76 } else {
77 // All other messages are not permitted.
78 VLOG(1) << "Rejecting unhandled message #" << request->msgh_id;
79 message_server_->RejectMessage(reply, MIG_REMOTE_ERROR);
80 }
81 }
82
HandleLookUp(mach_msg_header_t * request,mach_msg_header_t * reply,const BootstrapSandboxPolicy * policy)83 void LaunchdInterceptionServer::HandleLookUp(
84 mach_msg_header_t* request,
85 mach_msg_header_t* reply,
86 const BootstrapSandboxPolicy* policy) {
87 const std::string request_service_name(
88 compat_shim_.look_up2_get_request_name(request));
89 VLOG(2) << "Incoming look_up2 request for " << request_service_name;
90
91 // Find the Rule for this service. If a named rule is not found, use the
92 // default specified by the policy.
93 const BootstrapSandboxPolicy::NamedRules::const_iterator it =
94 policy->rules.find(request_service_name);
95 Rule rule(policy->default_rule);
96 if (it != policy->rules.end())
97 rule = it->second;
98
99 if (rule.result == POLICY_ALLOW) {
100 // This service is explicitly allowed, so this message will not be
101 // intercepted by the sandbox.
102 VLOG(1) << "Permitting and forwarding look_up2: " << request_service_name;
103 ForwardMessage(request);
104 } else if (rule.result == POLICY_DENY_ERROR) {
105 // The child is not permitted to look up this service. Send a MIG error
106 // reply to the client. Returning a NULL or unserviced port for a look up
107 // can cause clients to crash or hang.
108 VLOG(1) << "Denying look_up2 with MIG error: " << request_service_name;
109 message_server_->RejectMessage(reply, BOOTSTRAP_UNKNOWN_SERVICE);
110 } else if (rule.result == POLICY_DENY_DUMMY_PORT ||
111 rule.result == POLICY_SUBSTITUTE_PORT) {
112 // The policy result is to deny access to the real service port, replying
113 // with a sandboxed port in its stead. Use either the dummy sandbox_port_
114 // or the one specified in the policy.
115 VLOG(1) << "Intercepting look_up2 with a sandboxed service port: "
116 << request_service_name;
117
118 mach_port_t result_port;
119 if (rule.result == POLICY_DENY_DUMMY_PORT)
120 result_port = sandbox_port_.get();
121 else
122 result_port = rule.substitute_port;
123
124 compat_shim_.look_up2_fill_reply(reply, result_port);
125 // If the message was sent successfully, clear the result_port out of the
126 // message so that it is not destroyed at the end of ReceiveMessage. The
127 // above-inserted right has been moved out of the process, and destroying
128 // the message will unref yet another right.
129 if (message_server_->SendReply(reply))
130 compat_shim_.look_up2_fill_reply(reply, MACH_PORT_NULL);
131 } else {
132 NOTREACHED();
133 }
134 }
135
HandleSwapInteger(mach_msg_header_t * request,mach_msg_header_t * reply)136 void LaunchdInterceptionServer::HandleSwapInteger(mach_msg_header_t* request,
137 mach_msg_header_t* reply) {
138 // Only allow getting information out of launchd. Do not allow setting
139 // values. Two commonly observed values that are retrieved are
140 // VPROC_GSK_MGR_PID and VPROC_GSK_TRANSACTIONS_ENABLED.
141 if (compat_shim_.swap_integer_is_get_only(request)) {
142 VLOG(2) << "Forwarding vproc swap_integer message.";
143 ForwardMessage(request);
144 } else {
145 VLOG(2) << "Rejecting non-read-only swap_integer message.";
146 message_server_->RejectMessage(reply, BOOTSTRAP_NOT_PRIVILEGED);
147 }
148 }
ForwardMessage(mach_msg_header_t * request)149 void LaunchdInterceptionServer::ForwardMessage(mach_msg_header_t* request) {
150 message_server_->ForwardMessage(request, sandbox_->real_bootstrap_port());
151 }
152
153 } // namespace sandbox
154