diff options
Diffstat (limited to 'chromium/sandbox/mac/launchd_interception_server.cc')
-rw-r--r-- | chromium/sandbox/mac/launchd_interception_server.cc | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/chromium/sandbox/mac/launchd_interception_server.cc b/chromium/sandbox/mac/launchd_interception_server.cc new file mode 100644 index 00000000000..c3d6eaac579 --- /dev/null +++ b/chromium/sandbox/mac/launchd_interception_server.cc @@ -0,0 +1,153 @@ +// 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; + +LaunchdInterceptionServer::LaunchdInterceptionServer( + const BootstrapSandbox* sandbox) + : sandbox_(sandbox), + sandbox_port_(MACH_PORT_NULL), + 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)) != + KERN_SUCCESS) { + MACH_LOG(ERROR, kr) << "Failed to allocate dummy sandbox port."; + return false; + } + sandbox_port_.reset(port); + if ((kr = mach_port_insert_right(task, sandbox_port_, sandbox_port_, + MACH_MSG_TYPE_MAKE_SEND) != KERN_SUCCESS)) { + MACH_LOG(ERROR, kr) << "Failed to allocate dummy sandbox port send right."; + return false; + } + sandbox_send_port_.reset(sandbox_port_); + + message_server_.reset( + 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 = + sandbox_->PolicyForProcess(sender_pid); + 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); + return; + } + + 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( + compat_shim_.look_up2_get_request_name(request)); + 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 = + policy->rules.find(request_service_name); + 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; + ForwardMessage(request); + } 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(); + else + 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 { + NOTREACHED(); + } +} + +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 + // VPROC_GSK_MGR_PID and VPROC_GSK_TRANSACTIONS_ENABLED. + if (compat_shim_.swap_integer_is_get_only(request)) { + VLOG(2) << "Forwarding vproc swap_integer message."; + ForwardMessage(request); + } 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 |