summaryrefslogtreecommitdiffstats
path: root/chromium/sandbox/mac/launchd_interception_server.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/sandbox/mac/launchd_interception_server.cc')
-rw-r--r--chromium/sandbox/mac/launchd_interception_server.cc153
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