Home | History | Annotate | Download | only in mac
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "sandbox/mac/mach_message_server.h"
      6 
      7 #include <bsm/libbsm.h>
      8 #include <servers/bootstrap.h>
      9 
     10 #include <string>
     11 
     12 #include "base/logging.h"
     13 #include "base/mac/mach_logging.h"
     14 #include "base/strings/stringprintf.h"
     15 
     16 namespace sandbox {
     17 
     18 MachMessageServer::MachMessageServer(
     19     MessageDemuxer* demuxer,
     20     mach_port_t server_receive_right,
     21     mach_msg_size_t buffer_size)
     22     : demuxer_(demuxer),
     23       server_port_(server_receive_right),
     24       server_queue_(NULL),
     25       server_source_(NULL),
     26       buffer_size_(
     27           mach_vm_round_page(buffer_size + sizeof(mach_msg_audit_trailer_t))),
     28       did_forward_message_(false) {
     29   DCHECK(demuxer_);
     30 }
     31 
     32 MachMessageServer::~MachMessageServer() {
     33   if (server_source_)
     34     dispatch_release(server_source_);
     35   if (server_queue_)
     36     dispatch_release(server_queue_);
     37 }
     38 
     39 bool MachMessageServer::Initialize() {
     40   mach_port_t task = mach_task_self();
     41   kern_return_t kr;
     42 
     43   // Allocate a port for use as a new server port if one was not passed to the
     44   // constructor.
     45   if (!server_port_.is_valid()) {
     46     mach_port_t port;
     47     if ((kr = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &port)) !=
     48             KERN_SUCCESS) {
     49       MACH_LOG(ERROR, kr) << "Failed to allocate new server port.";
     50       return false;
     51     }
     52     server_port_.reset(port);
     53   }
     54 
     55   // Allocate the message request and reply buffers.
     56   const int kMachMsgMemoryFlags = VM_MAKE_TAG(VM_MEMORY_MACH_MSG) |
     57                                   VM_FLAGS_ANYWHERE;
     58   vm_address_t buffer = 0;
     59 
     60   kr = vm_allocate(task, &buffer, buffer_size_, kMachMsgMemoryFlags);
     61   if (kr != KERN_SUCCESS) {
     62     MACH_LOG(ERROR, kr) << "Failed to allocate request buffer.";
     63     return false;
     64   }
     65   request_buffer_.reset(buffer, buffer_size_);
     66 
     67   kr = vm_allocate(task, &buffer, buffer_size_, kMachMsgMemoryFlags);
     68   if (kr != KERN_SUCCESS) {
     69     MACH_LOG(ERROR, kr) << "Failed to allocate reply buffer.";
     70     return false;
     71   }
     72   reply_buffer_.reset(buffer, buffer_size_);
     73 
     74   // Set up the dispatch queue to service the bootstrap port.
     75   // TODO(rsesek): Specify DISPATCH_QUEUE_SERIAL, in the 10.7 SDK. NULL means
     76   // the same thing but is not symbolically clear.
     77   std::string label = base::StringPrintf(
     78       "org.chromium.sandbox.MachMessageServer.%p", demuxer_);
     79   server_queue_ = dispatch_queue_create(label.c_str(), NULL);
     80   server_source_ = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV,
     81       server_port_.get(), 0, server_queue_);
     82   dispatch_source_set_event_handler(server_source_, ^{ ReceiveMessage(); });
     83   dispatch_resume(server_source_);
     84 
     85   return true;
     86 }
     87 
     88 pid_t MachMessageServer::GetMessageSenderPID(mach_msg_header_t* request) {
     89   // Get the PID of the task that sent this request. This requires getting at
     90   // the trailer of the message, from the header.
     91   mach_msg_audit_trailer_t* trailer =
     92       reinterpret_cast<mach_msg_audit_trailer_t*>(
     93           reinterpret_cast<vm_address_t>(request) +
     94               round_msg(request->msgh_size));
     95   // TODO(rsesek): In the 10.7 SDK, there's audit_token_to_pid().
     96   pid_t sender_pid;
     97   audit_token_to_au32(trailer->msgh_audit,
     98       NULL, NULL, NULL, NULL, NULL, &sender_pid, NULL, NULL);
     99   return sender_pid;
    100 }
    101 
    102 bool MachMessageServer::SendReply(mach_msg_header_t* reply) {
    103   kern_return_t kr = mach_msg(reply, MACH_SEND_MSG, reply->msgh_size, 0,
    104       MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
    105   MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr)
    106       << "Unable to send intercepted reply message.";
    107   return kr == KERN_SUCCESS;
    108 }
    109 
    110 void MachMessageServer::ForwardMessage(mach_msg_header_t* request,
    111                                        mach_port_t destination) {
    112   request->msgh_local_port = request->msgh_remote_port;
    113   request->msgh_remote_port = destination;
    114   // Preserve the msgh_bits that do not deal with the local and remote ports.
    115   request->msgh_bits = (request->msgh_bits & ~MACH_MSGH_BITS_PORTS_MASK) |
    116       MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MOVE_SEND_ONCE);
    117   kern_return_t kr = mach_msg_send(request);
    118   if (kr == KERN_SUCCESS) {
    119     did_forward_message_ = true;
    120   } else {
    121     MACH_LOG(ERROR, kr) << "Unable to forward message to the real launchd.";
    122   }
    123 }
    124 
    125 void MachMessageServer::RejectMessage(mach_msg_header_t* reply,
    126                                       int error_code) {
    127   mig_reply_error_t* error_reply = reinterpret_cast<mig_reply_error_t*>(reply);
    128   error_reply->Head.msgh_size = sizeof(mig_reply_error_t);
    129   error_reply->Head.msgh_bits =
    130       MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MOVE_SEND_ONCE);
    131   error_reply->NDR = NDR_record;
    132   error_reply->RetCode = error_code;
    133   SendReply(&error_reply->Head);
    134 }
    135 
    136 void MachMessageServer::ReceiveMessage() {
    137   const mach_msg_options_t kRcvOptions = MACH_RCV_MSG |
    138       MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) |
    139       MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT);
    140 
    141   mach_msg_header_t* request =
    142       reinterpret_cast<mach_msg_header_t*>(request_buffer_.address());
    143   mach_msg_header_t* reply =
    144       reinterpret_cast<mach_msg_header_t*>(reply_buffer_.address());
    145 
    146   // Zero out the buffers from handling any previous message.
    147   bzero(request, buffer_size_);
    148   bzero(reply, buffer_size_);
    149   did_forward_message_ = false;
    150 
    151   // A Mach message server-once. The system library to run a message server
    152   // cannot be used here, because some requests are conditionally forwarded
    153   // to another server.
    154   kern_return_t kr = mach_msg(request, kRcvOptions, 0, buffer_size_,
    155       server_port_.get(), MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
    156   if (kr != KERN_SUCCESS) {
    157     MACH_LOG(ERROR, kr) << "Unable to receive message.";
    158     return;
    159   }
    160 
    161   // Set up a reply message in case it will be used.
    162   reply->msgh_bits = MACH_MSGH_BITS_REMOTE(reply->msgh_bits);
    163   // Since mach_msg will automatically swap the request and reply ports,
    164   // undo that.
    165   reply->msgh_remote_port = request->msgh_remote_port;
    166   reply->msgh_local_port = MACH_PORT_NULL;
    167   // MIG servers simply add 100 to the request ID to generate the reply ID.
    168   reply->msgh_id = request->msgh_id + 100;
    169 
    170   // Process the message.
    171   demuxer_->DemuxMessage(request, reply);
    172 
    173   // Free any descriptors in the message body. If the message was forwarded,
    174   // any descriptors would have been moved out of the process on send. If the
    175   // forwarded message was sent from the process hosting this sandbox server,
    176   // destroying the message could also destroy rights held outside the scope of
    177   // this message server.
    178   if (!did_forward_message_) {
    179     mach_msg_destroy(request);
    180     mach_msg_destroy(reply);
    181   }
    182 }
    183 
    184 }  // namespace sandbox
    185