summaryrefslogtreecommitdiffstats
path: root/chromium/sandbox/mac/mach_message_server.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/sandbox/mac/mach_message_server.cc')
-rw-r--r--chromium/sandbox/mac/mach_message_server.cc184
1 files changed, 184 insertions, 0 deletions
diff --git a/chromium/sandbox/mac/mach_message_server.cc b/chromium/sandbox/mac/mach_message_server.cc
new file mode 100644
index 00000000000..555ee0f494e
--- /dev/null
+++ b/chromium/sandbox/mac/mach_message_server.cc
@@ -0,0 +1,184 @@
+// 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/mach_message_server.h"
+
+#include <bsm/libbsm.h>
+#include <servers/bootstrap.h>
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/mac/mach_logging.h"
+#include "base/strings/stringprintf.h"
+
+namespace sandbox {
+
+MachMessageServer::MachMessageServer(
+ MessageDemuxer* demuxer,
+ mach_port_t server_receive_right,
+ mach_msg_size_t buffer_size)
+ : demuxer_(demuxer),
+ server_port_(server_receive_right),
+ server_queue_(NULL),
+ server_source_(NULL),
+ buffer_size_(
+ mach_vm_round_page(buffer_size + sizeof(mach_msg_audit_trailer_t))),
+ did_forward_message_(false) {
+ DCHECK(demuxer_);
+}
+
+MachMessageServer::~MachMessageServer() {
+ if (server_source_)
+ dispatch_release(server_source_);
+ if (server_queue_)
+ dispatch_release(server_queue_);
+}
+
+bool MachMessageServer::Initialize() {
+ mach_port_t task = mach_task_self();
+ kern_return_t kr;
+
+ // Allocate a port for use as a new server port if one was not passed to the
+ // constructor.
+ if (!server_port_.is_valid()) {
+ mach_port_t port;
+ if ((kr = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &port)) !=
+ KERN_SUCCESS) {
+ MACH_LOG(ERROR, kr) << "Failed to allocate new server port.";
+ return false;
+ }
+ server_port_.reset(port);
+ }
+
+ // Allocate the message request and reply buffers.
+ const int kMachMsgMemoryFlags = VM_MAKE_TAG(VM_MEMORY_MACH_MSG) |
+ VM_FLAGS_ANYWHERE;
+ vm_address_t buffer = 0;
+
+ kr = vm_allocate(task, &buffer, buffer_size_, kMachMsgMemoryFlags);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(ERROR, kr) << "Failed to allocate request buffer.";
+ return false;
+ }
+ request_buffer_.reset(buffer, buffer_size_);
+
+ kr = vm_allocate(task, &buffer, buffer_size_, kMachMsgMemoryFlags);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(ERROR, kr) << "Failed to allocate reply buffer.";
+ return false;
+ }
+ reply_buffer_.reset(buffer, buffer_size_);
+
+ // Set up the dispatch queue to service the bootstrap port.
+ // TODO(rsesek): Specify DISPATCH_QUEUE_SERIAL, in the 10.7 SDK. NULL means
+ // the same thing but is not symbolically clear.
+ std::string label = base::StringPrintf(
+ "org.chromium.sandbox.MachMessageServer.%p", demuxer_);
+ server_queue_ = dispatch_queue_create(label.c_str(), NULL);
+ server_source_ = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV,
+ server_port_.get(), 0, server_queue_);
+ dispatch_source_set_event_handler(server_source_, ^{ ReceiveMessage(); });
+ dispatch_resume(server_source_);
+
+ return true;
+}
+
+pid_t MachMessageServer::GetMessageSenderPID(mach_msg_header_t* request) {
+ // Get the PID of the task that sent this request. This requires getting at
+ // the trailer of the message, from the header.
+ mach_msg_audit_trailer_t* trailer =
+ reinterpret_cast<mach_msg_audit_trailer_t*>(
+ reinterpret_cast<vm_address_t>(request) +
+ round_msg(request->msgh_size));
+ // TODO(rsesek): In the 10.7 SDK, there's audit_token_to_pid().
+ pid_t sender_pid;
+ audit_token_to_au32(trailer->msgh_audit,
+ NULL, NULL, NULL, NULL, NULL, &sender_pid, NULL, NULL);
+ return sender_pid;
+}
+
+bool MachMessageServer::SendReply(mach_msg_header_t* reply) {
+ kern_return_t kr = mach_msg(reply, MACH_SEND_MSG, reply->msgh_size, 0,
+ MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+ MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr)
+ << "Unable to send intercepted reply message.";
+ return kr == KERN_SUCCESS;
+}
+
+void MachMessageServer::ForwardMessage(mach_msg_header_t* request,
+ mach_port_t destination) {
+ request->msgh_local_port = request->msgh_remote_port;
+ request->msgh_remote_port = destination;
+ // Preserve the msgh_bits that do not deal with the local and remote ports.
+ request->msgh_bits = (request->msgh_bits & ~MACH_MSGH_BITS_PORTS_MASK) |
+ MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MOVE_SEND_ONCE);
+ kern_return_t kr = mach_msg_send(request);
+ if (kr == KERN_SUCCESS) {
+ did_forward_message_ = true;
+ } else {
+ MACH_LOG(ERROR, kr) << "Unable to forward message to the real launchd.";
+ }
+}
+
+void MachMessageServer::RejectMessage(mach_msg_header_t* reply,
+ int error_code) {
+ mig_reply_error_t* error_reply = reinterpret_cast<mig_reply_error_t*>(reply);
+ error_reply->Head.msgh_size = sizeof(mig_reply_error_t);
+ error_reply->Head.msgh_bits =
+ MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MOVE_SEND_ONCE);
+ error_reply->NDR = NDR_record;
+ error_reply->RetCode = error_code;
+ SendReply(&error_reply->Head);
+}
+
+void MachMessageServer::ReceiveMessage() {
+ const mach_msg_options_t kRcvOptions = MACH_RCV_MSG |
+ MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) |
+ MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT);
+
+ mach_msg_header_t* request =
+ reinterpret_cast<mach_msg_header_t*>(request_buffer_.address());
+ mach_msg_header_t* reply =
+ reinterpret_cast<mach_msg_header_t*>(reply_buffer_.address());
+
+ // Zero out the buffers from handling any previous message.
+ bzero(request, buffer_size_);
+ bzero(reply, buffer_size_);
+ did_forward_message_ = false;
+
+ // A Mach message server-once. The system library to run a message server
+ // cannot be used here, because some requests are conditionally forwarded
+ // to another server.
+ kern_return_t kr = mach_msg(request, kRcvOptions, 0, buffer_size_,
+ server_port_.get(), MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(ERROR, kr) << "Unable to receive message.";
+ return;
+ }
+
+ // Set up a reply message in case it will be used.
+ reply->msgh_bits = MACH_MSGH_BITS_REMOTE(reply->msgh_bits);
+ // Since mach_msg will automatically swap the request and reply ports,
+ // undo that.
+ reply->msgh_remote_port = request->msgh_remote_port;
+ reply->msgh_local_port = MACH_PORT_NULL;
+ // MIG servers simply add 100 to the request ID to generate the reply ID.
+ reply->msgh_id = request->msgh_id + 100;
+
+ // Process the message.
+ demuxer_->DemuxMessage(request, reply);
+
+ // Free any descriptors in the message body. If the message was forwarded,
+ // any descriptors would have been moved out of the process on send. If the
+ // forwarded message was sent from the process hosting this sandbox server,
+ // destroying the message could also destroy rights held outside the scope of
+ // this message server.
+ if (!did_forward_message_) {
+ mach_msg_destroy(request);
+ mach_msg_destroy(reply);
+ }
+}
+
+} // namespace sandbox