• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
MachMessageServer(MessageDemuxer * demuxer,mach_port_t server_receive_right,mach_msg_size_t buffer_size)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 
~MachMessageServer()32 MachMessageServer::~MachMessageServer() {
33   if (server_source_)
34     dispatch_release(server_source_);
35   if (server_queue_)
36     dispatch_release(server_queue_);
37 }
38 
Initialize()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 
GetMessageSenderPID(mach_msg_header_t * request)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 
SendReply(mach_msg_header_t * reply)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 
ForwardMessage(mach_msg_header_t * request,mach_port_t destination)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 
RejectMessage(mach_msg_header_t * reply,int error_code)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 
ReceiveMessage()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