// 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 #include #include #include "base/logging.h" #include "base/mac/mach_logging.h" #include "base/strings/stringprintf.h" #include "sandbox/mac/dispatch_source_mach.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), buffer_size_( mach_vm_round_page(buffer_size + sizeof(mach_msg_audit_trailer_t))), did_forward_message_(false) { DCHECK(demuxer_); } MachMessageServer::~MachMessageServer() { } 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. std::string label = base::StringPrintf( "org.chromium.sandbox.MachMessageServer.%p", demuxer_); dispatch_source_.reset(new DispatchSourceMach( label.c_str(), server_port_.get(), ^{ ReceiveMessage(); })); dispatch_source_->Resume(); return true; } pid_t MachMessageServer::GetMessageSenderPID(IPCMessage 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( reinterpret_cast(request.mach) + round_msg(request.mach->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; } IPCMessage MachMessageServer::CreateReply(IPCMessage request_message) { mach_msg_header_t* request = request_message.mach; IPCMessage reply_message; mach_msg_header_t* reply = reply_message.mach = reinterpret_cast(reply_buffer_.address()); bzero(reply, buffer_size_); 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; return reply_message; } bool MachMessageServer::SendReply(IPCMessage reply) { kern_return_t kr = mach_msg(reply.mach, MACH_SEND_MSG, reply.mach->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(IPCMessage message, mach_port_t destination) { mach_msg_header_t* request = message.mach; 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(IPCMessage request, int error_code) { IPCMessage reply = CreateReply(request); mig_reply_error_t* error_reply = reinterpret_cast(reply.mach); 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(reply); } mach_port_t MachMessageServer::GetServerPort() const { return server_port_.get(); } 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(request_buffer_.address()); mach_msg_header_t* reply = reinterpret_cast(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; } // Process the message. IPCMessage request_message = { request }; demuxer_->DemuxMessage(request_message); // 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