Home | History | Annotate | Download | only in mac
      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 
     20 LaunchdInterceptionServer::LaunchdInterceptionServer(
     21     const BootstrapSandbox* sandbox)
     22     : sandbox_(sandbox),
     23       sandbox_port_(MACH_PORT_NULL),
     24       compat_shim_(GetLaunchdCompatibilityShim()) {
     25 }
     26 
     27 LaunchdInterceptionServer::~LaunchdInterceptionServer() {
     28 }
     29 
     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 
     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 
     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 
    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 }
    149 void LaunchdInterceptionServer::ForwardMessage(mach_msg_header_t* request) {
    150   message_server_->ForwardMessage(request, sandbox_->real_bootstrap_port());
    151 }
    152 
    153 }  // namespace sandbox
    154