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.
5 #include "sandbox/mac/mach_message_server.h"
7 #include <bsm/libbsm.h>
8 #include <servers/bootstrap.h>
12 #include "base/logging.h"
13 #include "base/mac/mach_logging.h"
14 #include "base/strings/stringprintf.h"
18 MachMessageServer::MachMessageServer(
19 MessageDemuxer
* demuxer
,
20 mach_port_t server_receive_right
,
21 mach_msg_size_t buffer_size
)
23 server_port_(server_receive_right
),
26 source_canceled_(dispatch_semaphore_create(0)),
28 mach_vm_round_page(buffer_size
+ sizeof(mach_msg_audit_trailer_t
))),
29 did_forward_message_(false) {
33 MachMessageServer::~MachMessageServer() {
35 dispatch_source_cancel(server_source_
);
36 dispatch_release(server_source_
);
38 dispatch_semaphore_wait(source_canceled_
, DISPATCH_TIME_FOREVER
);
39 dispatch_release(source_canceled_
);
42 dispatch_release(server_queue_
);
45 bool MachMessageServer::Initialize() {
46 mach_port_t task
= mach_task_self();
49 // Allocate a port for use as a new server port if one was not passed to the
51 if (!server_port_
.is_valid()) {
53 if ((kr
= mach_port_allocate(task
, MACH_PORT_RIGHT_RECEIVE
, &port
)) !=
55 MACH_LOG(ERROR
, kr
) << "Failed to allocate new server port.";
58 server_port_
.reset(port
);
61 // Allocate the message request and reply buffers.
62 const int kMachMsgMemoryFlags
= VM_MAKE_TAG(VM_MEMORY_MACH_MSG
) |
64 vm_address_t buffer
= 0;
66 kr
= vm_allocate(task
, &buffer
, buffer_size_
, kMachMsgMemoryFlags
);
67 if (kr
!= KERN_SUCCESS
) {
68 MACH_LOG(ERROR
, kr
) << "Failed to allocate request buffer.";
71 request_buffer_
.reset(buffer
, buffer_size_
);
73 kr
= vm_allocate(task
, &buffer
, buffer_size_
, kMachMsgMemoryFlags
);
74 if (kr
!= KERN_SUCCESS
) {
75 MACH_LOG(ERROR
, kr
) << "Failed to allocate reply buffer.";
78 reply_buffer_
.reset(buffer
, buffer_size_
);
80 // Set up the dispatch queue to service the bootstrap port.
81 // TODO(rsesek): Specify DISPATCH_QUEUE_SERIAL, in the 10.7 SDK. NULL means
82 // the same thing but is not symbolically clear.
83 std::string label
= base::StringPrintf(
84 "org.chromium.sandbox.MachMessageServer.%p", demuxer_
);
85 server_queue_
= dispatch_queue_create(label
.c_str(), NULL
);
86 server_source_
= dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV
,
87 server_port_
.get(), 0, server_queue_
);
88 dispatch_source_set_event_handler(server_source_
, ^{ ReceiveMessage(); });
89 dispatch_source_set_cancel_handler(server_source_
, ^{
90 dispatch_semaphore_signal(source_canceled_
);
92 dispatch_resume(server_source_
);
97 pid_t
MachMessageServer::GetMessageSenderPID(IPCMessage request
) {
98 // Get the PID of the task that sent this request. This requires getting at
99 // the trailer of the message, from the header.
100 mach_msg_audit_trailer_t
* trailer
=
101 reinterpret_cast<mach_msg_audit_trailer_t
*>(
102 reinterpret_cast<vm_address_t
>(request
.mach
) +
103 round_msg(request
.mach
->msgh_size
));
104 // TODO(rsesek): In the 10.7 SDK, there's audit_token_to_pid().
106 audit_token_to_au32(trailer
->msgh_audit
,
107 NULL
, NULL
, NULL
, NULL
, NULL
, &sender_pid
, NULL
, NULL
);
111 bool MachMessageServer::SendReply(IPCMessage reply
) {
112 kern_return_t kr
= mach_msg(reply
.mach
, MACH_SEND_MSG
,
113 reply
.mach
->msgh_size
, 0, MACH_PORT_NULL
, MACH_MSG_TIMEOUT_NONE
,
115 MACH_LOG_IF(ERROR
, kr
!= KERN_SUCCESS
, kr
)
116 << "Unable to send intercepted reply message.";
117 return kr
== KERN_SUCCESS
;
120 void MachMessageServer::ForwardMessage(IPCMessage message
,
121 mach_port_t destination
) {
122 mach_msg_header_t
* request
= message
.mach
;
123 request
->msgh_local_port
= request
->msgh_remote_port
;
124 request
->msgh_remote_port
= destination
;
125 // Preserve the msgh_bits that do not deal with the local and remote ports.
126 request
->msgh_bits
= (request
->msgh_bits
& ~MACH_MSGH_BITS_PORTS_MASK
) |
127 MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND
, MACH_MSG_TYPE_MOVE_SEND_ONCE
);
128 kern_return_t kr
= mach_msg_send(request
);
129 if (kr
== KERN_SUCCESS
) {
130 did_forward_message_
= true;
132 MACH_LOG(ERROR
, kr
) << "Unable to forward message to the real launchd.";
136 void MachMessageServer::RejectMessage(IPCMessage reply
, int error_code
) {
137 mig_reply_error_t
* error_reply
=
138 reinterpret_cast<mig_reply_error_t
*>(reply
.mach
);
139 error_reply
->Head
.msgh_size
= sizeof(mig_reply_error_t
);
140 error_reply
->Head
.msgh_bits
=
141 MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MOVE_SEND_ONCE
);
142 error_reply
->NDR
= NDR_record
;
143 error_reply
->RetCode
= error_code
;
147 mach_port_t
MachMessageServer::GetServerPort() const {
148 return server_port_
.get();
151 void MachMessageServer::ReceiveMessage() {
152 const mach_msg_options_t kRcvOptions
= MACH_RCV_MSG
|
153 MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0
) |
154 MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT
);
156 mach_msg_header_t
* request
=
157 reinterpret_cast<mach_msg_header_t
*>(request_buffer_
.address());
158 mach_msg_header_t
* reply
=
159 reinterpret_cast<mach_msg_header_t
*>(reply_buffer_
.address());
161 // Zero out the buffers from handling any previous message.
162 bzero(request
, buffer_size_
);
163 bzero(reply
, buffer_size_
);
164 did_forward_message_
= false;
166 // A Mach message server-once. The system library to run a message server
167 // cannot be used here, because some requests are conditionally forwarded
168 // to another server.
169 kern_return_t kr
= mach_msg(request
, kRcvOptions
, 0, buffer_size_
,
170 server_port_
.get(), MACH_MSG_TIMEOUT_NONE
, MACH_PORT_NULL
);
171 if (kr
!= KERN_SUCCESS
) {
172 MACH_LOG(ERROR
, kr
) << "Unable to receive message.";
176 // Set up a reply message in case it will be used.
177 reply
->msgh_bits
= MACH_MSGH_BITS_REMOTE(reply
->msgh_bits
);
178 // Since mach_msg will automatically swap the request and reply ports,
180 reply
->msgh_remote_port
= request
->msgh_remote_port
;
181 reply
->msgh_local_port
= MACH_PORT_NULL
;
182 // MIG servers simply add 100 to the request ID to generate the reply ID.
183 reply
->msgh_id
= request
->msgh_id
+ 100;
185 // Process the message.
186 IPCMessage request_message
= { request
};
187 IPCMessage reply_message
= { reply
};
188 demuxer_
->DemuxMessage(request_message
, reply_message
);
190 // Free any descriptors in the message body. If the message was forwarded,
191 // any descriptors would have been moved out of the process on send. If the
192 // forwarded message was sent from the process hosting this sandbox server,
193 // destroying the message could also destroy rights held outside the scope of
194 // this message server.
195 if (!did_forward_message_
) {
196 mach_msg_destroy(request
);
197 mach_msg_destroy(reply
);
201 } // namespace sandbox