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