Restore simple fling status bookkeeping
[chromium-blink-merge.git] / sandbox / mac / mach_message_server.cc
blob5a733577f310c4ba9f8edbce7a1761d2ce572107
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>
10 #include <string>
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"
17 namespace sandbox {
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_);
31 MachMessageServer::~MachMessageServer() {
34 bool MachMessageServer::Initialize() {
35 mach_port_t task = mach_task_self();
36 kern_return_t kr;
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;
47 server_port_.reset(port);
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;
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;
60 request_buffer_.reset(buffer, buffer_size_);
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;
67 reply_buffer_.reset(buffer, buffer_size_);
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();
76 return true;
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;
93 IPCMessage MachMessageServer::CreateReply(IPCMessage request_message) {
94 mach_msg_header_t* request = request_message.mach;
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_);
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;
109 return reply_message;
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;
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.";
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);
149 mach_port_t MachMessageServer::GetServerPort() const {
150 return server_port_.get();
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);
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());
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;
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;
178 // Process the message.
179 IPCMessage request_message = { request };
180 demuxer_->DemuxMessage(request_message);
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);
193 } // namespace sandbox