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