• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "sandbox/mac/mach_message_server.h"
13 
14 namespace sandbox {
15 
16 // The buffer size for all launchd messages. This comes from
17 // sizeof(union __RequestUnion__vproc_mig_job_subsystem) in launchd, and it
18 // is larger than the __ReplyUnion.
19 const mach_msg_size_t kBufferSize = 2096;
20 
LaunchdInterceptionServer(const BootstrapSandbox * sandbox)21 LaunchdInterceptionServer::LaunchdInterceptionServer(
22     const BootstrapSandbox* sandbox)
23     : sandbox_(sandbox),
24       sandbox_port_(MACH_PORT_NULL),
25       compat_shim_(GetLaunchdCompatibilityShim()) {
26 }
27 
~LaunchdInterceptionServer()28 LaunchdInterceptionServer::~LaunchdInterceptionServer() {
29 }
30 
Initialize(mach_port_t server_receive_right)31 bool LaunchdInterceptionServer::Initialize(mach_port_t server_receive_right) {
32   mach_port_t task = mach_task_self();
33   kern_return_t kr;
34 
35   // Allocate the dummy sandbox port.
36   mach_port_t port;
37   if ((kr = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &port)) !=
38           KERN_SUCCESS) {
39     MACH_LOG(ERROR, kr) << "Failed to allocate dummy sandbox port.";
40     return false;
41   }
42   sandbox_port_.reset(port);
43   if ((kr = mach_port_insert_right(task, sandbox_port_, sandbox_port_,
44           MACH_MSG_TYPE_MAKE_SEND) != KERN_SUCCESS)) {
45     MACH_LOG(ERROR, kr) << "Failed to allocate dummy sandbox port send right.";
46     return false;
47   }
48   sandbox_send_port_.reset(sandbox_port_);
49 
50   message_server_.reset(
51       new MachMessageServer(this, server_receive_right, kBufferSize));
52   return message_server_->Initialize();
53 }
54 
DemuxMessage(IPCMessage request)55 void LaunchdInterceptionServer::DemuxMessage(IPCMessage request) {
56   const uint64_t message_id = compat_shim_.ipc_message_get_id(request);
57   VLOG(3) << "Incoming message #" << message_id;
58 
59   pid_t sender_pid = message_server_->GetMessageSenderPID(request);
60   const BootstrapSandboxPolicy* policy =
61       sandbox_->PolicyForProcess(sender_pid);
62   if (policy == NULL) {
63     // No sandbox policy is in place for the sender of this message, which
64     // means it came from the unknown. Reject it.
65     VLOG(3) << "Message from unknown pid " << sender_pid << " rejected.";
66     message_server_->RejectMessage(request, MIG_REMOTE_ERROR);
67     return;
68   }
69 
70   if (message_id == compat_shim_.msg_id_look_up2) {
71     // Filter messages sent via bootstrap_look_up to enforce the sandbox policy
72     // over the bootstrap namespace.
73     HandleLookUp(request, policy);
74   } else if (message_id == compat_shim_.msg_id_swap_integer) {
75     // Ensure that any vproc_swap_integer requests are safe.
76     HandleSwapInteger(request);
77   } else {
78     // All other messages are not permitted.
79     VLOG(1) << "Rejecting unhandled message #" << message_id;
80     message_server_->RejectMessage(request, MIG_REMOTE_ERROR);
81   }
82 }
83 
HandleLookUp(IPCMessage request,const BootstrapSandboxPolicy * policy)84 void LaunchdInterceptionServer::HandleLookUp(
85     IPCMessage request,
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(request, 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     IPCMessage reply = message_server_->CreateReply(request);
125     compat_shim_.look_up2_fill_reply(reply, result_port);
126     // If the message was sent successfully, clear the result_port out of the
127     // message so that it is not destroyed at the end of ReceiveMessage. The
128     // above-inserted right has been moved out of the process, and destroying
129     // the message will unref yet another right.
130     if (message_server_->SendReply(reply))
131       compat_shim_.look_up2_fill_reply(reply, MACH_PORT_NULL);
132   } else {
133     NOTREACHED();
134   }
135 }
136 
HandleSwapInteger(IPCMessage request)137 void LaunchdInterceptionServer::HandleSwapInteger(IPCMessage request) {
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(request, BOOTSTRAP_NOT_PRIVILEGED);
147   }
148 }
ForwardMessage(IPCMessage request)149 void LaunchdInterceptionServer::ForwardMessage(IPCMessage request) {
150   message_server_->ForwardMessage(request, sandbox_->real_bootstrap_port());
151 }
152 
153 }  // namespace sandbox
154