blob: c3d6eaac57928ff5c53ea44cde2a139b5e08c444 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "sandbox/mac/launchd_interception_server.h"
#include <servers/bootstrap.h>
#include "base/logging.h"
#include "base/mac/mach_logging.h"
#include "sandbox/mac/bootstrap_sandbox.h"
namespace sandbox {
// The buffer size for all launchd messages. This comes from
// sizeof(union __RequestUnion__vproc_mig_job_subsystem) in launchd, and it
// is larger than the __ReplyUnion.
const mach_msg_size_t kBufferSize = 2096;
const BootstrapSandbox* sandbox)
: sandbox_(sandbox),
compat_shim_(GetLaunchdCompatibilityShim()) {
LaunchdInterceptionServer::~LaunchdInterceptionServer() {
bool LaunchdInterceptionServer::Initialize(mach_port_t server_receive_right) {
mach_port_t task = mach_task_self();
kern_return_t kr;
// Allocate the dummy sandbox port.
mach_port_t port;
if ((kr = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &port)) !=
MACH_LOG(ERROR, kr) << "Failed to allocate dummy sandbox port.";
return false;
if ((kr = mach_port_insert_right(task, sandbox_port_, sandbox_port_,
MACH_LOG(ERROR, kr) << "Failed to allocate dummy sandbox port send right.";
return false;
new MachMessageServer(this, server_receive_right, kBufferSize));
return message_server_->Initialize();
void LaunchdInterceptionServer::DemuxMessage(mach_msg_header_t* request,
mach_msg_header_t* reply) {
VLOG(3) << "Incoming message #" << request->msgh_id;
pid_t sender_pid = message_server_->GetMessageSenderPID(request);
const BootstrapSandboxPolicy* policy =
if (policy == NULL) {
// No sandbox policy is in place for the sender of this message, which
// means it came from the unknown. Reject it.
VLOG(3) << "Message from unknown pid " << sender_pid << " rejected.";
message_server_->RejectMessage(request, MIG_REMOTE_ERROR);
if (request->msgh_id == compat_shim_.msg_id_look_up2) {
// Filter messages sent via bootstrap_look_up to enforce the sandbox policy
// over the bootstrap namespace.
HandleLookUp(request, reply, policy);
} else if (request->msgh_id == compat_shim_.msg_id_swap_integer) {
// Ensure that any vproc_swap_integer requests are safe.
HandleSwapInteger(request, reply);
} else {
// All other messages are not permitted.
VLOG(1) << "Rejecting unhandled message #" << request->msgh_id;
message_server_->RejectMessage(reply, MIG_REMOTE_ERROR);
void LaunchdInterceptionServer::HandleLookUp(
mach_msg_header_t* request,
mach_msg_header_t* reply,
const BootstrapSandboxPolicy* policy) {
const std::string request_service_name(
VLOG(2) << "Incoming look_up2 request for " << request_service_name;
// Find the Rule for this service. If a named rule is not found, use the
// default specified by the policy.
const BootstrapSandboxPolicy::NamedRules::const_iterator it =
Rule rule(policy->default_rule);
if (it != policy->rules.end())
rule = it->second;
if (rule.result == POLICY_ALLOW) {
// This service is explicitly allowed, so this message will not be
// intercepted by the sandbox.
VLOG(1) << "Permitting and forwarding look_up2: " << request_service_name;
} else if (rule.result == POLICY_DENY_ERROR) {
// The child is not permitted to look up this service. Send a MIG error
// reply to the client. Returning a NULL or unserviced port for a look up
// can cause clients to crash or hang.
VLOG(1) << "Denying look_up2 with MIG error: " << request_service_name;
message_server_->RejectMessage(reply, BOOTSTRAP_UNKNOWN_SERVICE);
} else if (rule.result == POLICY_DENY_DUMMY_PORT ||
rule.result == POLICY_SUBSTITUTE_PORT) {
// The policy result is to deny access to the real service port, replying
// with a sandboxed port in its stead. Use either the dummy sandbox_port_
// or the one specified in the policy.
VLOG(1) << "Intercepting look_up2 with a sandboxed service port: "
<< request_service_name;
mach_port_t result_port;
if (rule.result == POLICY_DENY_DUMMY_PORT)
result_port = sandbox_port_.get();
result_port = rule.substitute_port;
compat_shim_.look_up2_fill_reply(reply, result_port);
// If the message was sent successfully, clear the result_port out of the
// message so that it is not destroyed at the end of ReceiveMessage. The
// above-inserted right has been moved out of the process, and destroying
// the message will unref yet another right.
if (message_server_->SendReply(reply))
compat_shim_.look_up2_fill_reply(reply, MACH_PORT_NULL);
} else {
void LaunchdInterceptionServer::HandleSwapInteger(mach_msg_header_t* request,
mach_msg_header_t* reply) {
// Only allow getting information out of launchd. Do not allow setting
// values. Two commonly observed values that are retrieved are
if (compat_shim_.swap_integer_is_get_only(request)) {
VLOG(2) << "Forwarding vproc swap_integer message.";
} else {
VLOG(2) << "Rejecting non-read-only swap_integer message.";
message_server_->RejectMessage(reply, BOOTSTRAP_NOT_PRIVILEGED);
void LaunchdInterceptionServer::ForwardMessage(mach_msg_header_t* request) {
message_server_->ForwardMessage(request, sandbox_->real_bootstrap_port());
} // namespace sandbox