blob: 1aac68be87ed8761bbaf19b8f69509bd1d73b1e3 [file] [log] [blame]
// Copyright 2015 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/pre_exec_delegate.h"
#include <mach/mach.h>
#include <servers/bootstrap.h>
#include <stdint.h>
#include "base/logging.h"
#include "base/mac/mac_util.h"
#include "sandbox/mac/bootstrap_sandbox.h"
#include "sandbox/mac/xpc.h"
namespace sandbox {
PreExecDelegate::PreExecDelegate(
const std::string& sandbox_server_bootstrap_name,
uint64_t sandbox_token)
: sandbox_server_bootstrap_name_(sandbox_server_bootstrap_name),
sandbox_server_bootstrap_name_ptr_(
sandbox_server_bootstrap_name_.c_str()),
sandbox_token_(sandbox_token),
is_yosemite_or_later_(base::mac::IsAtLeastOS10_10()),
look_up_message_(CreateBootstrapLookUpMessage()) {}
PreExecDelegate::~PreExecDelegate() {}
void PreExecDelegate::RunAsyncSafe() {
mach_port_t sandbox_server_port = MACH_PORT_NULL;
kern_return_t kr = DoBootstrapLookUp(&sandbox_server_port);
if (kr != KERN_SUCCESS)
RAW_LOG(FATAL, "Failed to look up bootstrap sandbox server port.");
mach_port_t new_bootstrap_port = MACH_PORT_NULL;
if (!BootstrapSandbox::ClientCheckIn(sandbox_server_port,
sandbox_token_,
&new_bootstrap_port)) {
RAW_LOG(FATAL, "Failed to check in with sandbox server.");
}
kr = task_set_bootstrap_port(mach_task_self(), new_bootstrap_port);
if (kr != KERN_SUCCESS)
RAW_LOG(FATAL, "Failed to replace bootstrap port.");
// On OS X 10.10 and higher, libxpc uses the port stash to transfer the
// XPC root port. This is effectively the same connection as the Mach
// bootstrap port, but not transferred using the task special port.
// Therefore, stash the replacement bootstrap port, so that on 10.10 it
// will be retrieved by the XPC code and used as a replacement for the
// XPC root port as well.
if (is_yosemite_or_later_) {
kr = mach_ports_register(mach_task_self(), &new_bootstrap_port, 1);
if (kr != KERN_SUCCESS)
RAW_LOG(ERROR, "Failed to register replacement bootstrap port.");
}
}
xpc_object_t PreExecDelegate::CreateBootstrapLookUpMessage() {
if (is_yosemite_or_later_) {
xpc_object_t dictionary = xpc_dictionary_create(nullptr, nullptr, 0);
xpc_dictionary_set_uint64(dictionary, "type", 7);
xpc_dictionary_set_uint64(dictionary, "handle", 0);
xpc_dictionary_set_string(dictionary, "name",
sandbox_server_bootstrap_name_ptr_);
xpc_dictionary_set_int64(dictionary, "targetpid", 0);
xpc_dictionary_set_uint64(dictionary, "flags", 0);
xpc_dictionary_set_uint64(dictionary, "subsystem", 5);
xpc_dictionary_set_uint64(dictionary, "routine", 207);
// Add a NULL port so that the slot in the dictionary is already
// allocated.
xpc_dictionary_set_mach_send(dictionary, "domain-port", MACH_PORT_NULL);
return dictionary;
}
return nullptr;
}
kern_return_t PreExecDelegate::DoBootstrapLookUp(mach_port_t* out_port) {
if (is_yosemite_or_later_) {
xpc_dictionary_set_mach_send(look_up_message_, "domain-port",
bootstrap_port);
// |pipe| cannot be created pre-fork() since the |bootstrap_port| will
// be invalidated. Deliberately leak |pipe| as well.
xpc_pipe_t pipe = xpc_pipe_create_from_port(bootstrap_port, 0);
xpc_object_t reply;
int rv = xpc_pipe_routine(pipe, look_up_message_, &reply);
if (rv != 0) {
return xpc_dictionary_get_int64(reply, "error");
} else {
xpc_object_t port_value = xpc_dictionary_get_value(reply, "port");
*out_port = xpc_mach_send_get_right(port_value);
return *out_port != MACH_PORT_NULL ? KERN_SUCCESS : KERN_INVALID_RIGHT;
}
} else {
// On non-XPC launchd systems, bootstrap_look_up() is MIG-based and
// generally safe.
return bootstrap_look_up(bootstrap_port,
sandbox_server_bootstrap_name_ptr_, out_port);
}
}
} // namespace sandbox