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