diff options
Diffstat (limited to 'chromium/sandbox/mac/mach_message_server.cc')
-rw-r--r-- | chromium/sandbox/mac/mach_message_server.cc | 184 |
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 |