OLD | NEW |
| (Empty) |
1 // Copyright 2015 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 "ipc/attachment_broker_privileged_mac.h" | |
6 | |
7 #include <stdint.h> | |
8 | |
9 #include <tuple> | |
10 | |
11 #include "base/mac/mach_port_util.h" | |
12 #include "base/mac/scoped_mach_port.h" | |
13 #include "base/memory/shared_memory.h" | |
14 #include "base/process/port_provider_mac.h" | |
15 #include "base/process/process.h" | |
16 #include "base/synchronization/lock.h" | |
17 #include "ipc/attachment_broker_messages.h" | |
18 #include "ipc/brokerable_attachment.h" | |
19 #include "ipc/ipc_channel.h" | |
20 #include "ipc/mach_port_attachment_mac.h" | |
21 | |
22 namespace IPC { | |
23 | |
24 AttachmentBrokerPrivilegedMac::AttachmentBrokerPrivilegedMac( | |
25 base::PortProvider* port_provider) | |
26 : port_provider_(port_provider) { | |
27 port_provider_->AddObserver(this); | |
28 } | |
29 | |
30 AttachmentBrokerPrivilegedMac::~AttachmentBrokerPrivilegedMac() { | |
31 port_provider_->RemoveObserver(this); | |
32 { | |
33 base::AutoLock l(precursors_lock_); | |
34 for (auto it : precursors_) | |
35 delete it.second; | |
36 } | |
37 { | |
38 base::AutoLock l(extractors_lock_); | |
39 for (auto it : extractors_) | |
40 delete it.second; | |
41 } | |
42 } | |
43 | |
44 bool AttachmentBrokerPrivilegedMac::SendAttachmentToProcess( | |
45 const scoped_refptr<IPC::BrokerableAttachment>& attachment, | |
46 base::ProcessId destination_process) { | |
47 switch (attachment->GetBrokerableType()) { | |
48 case BrokerableAttachment::MACH_PORT: { | |
49 internal::MachPortAttachmentMac* mach_port_attachment = | |
50 static_cast<internal::MachPortAttachmentMac*>(attachment.get()); | |
51 MachPortWireFormat wire_format = | |
52 mach_port_attachment->GetWireFormat(destination_process); | |
53 AddPrecursor(wire_format.destination_process, | |
54 base::mac::ScopedMachSendRight(wire_format.mach_port), | |
55 wire_format.attachment_id); | |
56 mach_port_attachment->reset_mach_port_ownership(); | |
57 SendPrecursorsForProcess(wire_format.destination_process, true); | |
58 return true; | |
59 } | |
60 default: | |
61 NOTREACHED(); | |
62 return false; | |
63 } | |
64 return false; | |
65 } | |
66 | |
67 void AttachmentBrokerPrivilegedMac::DeregisterCommunicationChannel( | |
68 Endpoint* endpoint) { | |
69 AttachmentBrokerPrivileged::DeregisterCommunicationChannel(endpoint); | |
70 | |
71 if (!endpoint) | |
72 return; | |
73 | |
74 base::ProcessId pid = endpoint->GetPeerPID(); | |
75 if (pid == base::kNullProcessId) | |
76 return; | |
77 | |
78 { | |
79 base::AutoLock l(precursors_lock_); | |
80 auto it = precursors_.find(pid); | |
81 if (it != precursors_.end()) { | |
82 delete it->second; | |
83 precursors_.erase(pid); | |
84 } | |
85 } | |
86 | |
87 { | |
88 base::AutoLock l(extractors_lock_); | |
89 auto it = extractors_.find(pid); | |
90 if (it != extractors_.end()) { | |
91 delete it->second; | |
92 extractors_.erase(pid); | |
93 } | |
94 } | |
95 } | |
96 | |
97 void AttachmentBrokerPrivilegedMac::ReceivedPeerPid(base::ProcessId peer_pid) { | |
98 ProcessExtractorsForProcess(peer_pid, false); | |
99 SendPrecursorsForProcess(peer_pid, false); | |
100 } | |
101 | |
102 bool AttachmentBrokerPrivilegedMac::OnMessageReceived(const Message& msg) { | |
103 bool handled = true; | |
104 switch (msg.type()) { | |
105 IPC_MESSAGE_HANDLER_GENERIC(AttachmentBrokerMsg_DuplicateMachPort, | |
106 OnDuplicateMachPort(msg)) | |
107 IPC_MESSAGE_UNHANDLED(handled = false) | |
108 } | |
109 return handled; | |
110 } | |
111 | |
112 void AttachmentBrokerPrivilegedMac::OnReceivedTaskPort( | |
113 base::ProcessHandle process) { | |
114 SendPrecursorsForProcess(process, true); | |
115 } | |
116 | |
117 AttachmentBrokerPrivilegedMac::AttachmentPrecursor::AttachmentPrecursor( | |
118 const base::ProcessId& pid, | |
119 base::mac::ScopedMachSendRight port, | |
120 const BrokerableAttachment::AttachmentId& id) | |
121 : pid_(pid), port_(port.release()), id_(id) {} | |
122 | |
123 AttachmentBrokerPrivilegedMac::AttachmentPrecursor::~AttachmentPrecursor() {} | |
124 | |
125 base::mac::ScopedMachSendRight | |
126 AttachmentBrokerPrivilegedMac::AttachmentPrecursor::TakePort() { | |
127 return base::mac::ScopedMachSendRight(port_.release()); | |
128 } | |
129 | |
130 AttachmentBrokerPrivilegedMac::AttachmentExtractor::AttachmentExtractor( | |
131 const base::ProcessId& source_pid, | |
132 const base::ProcessId& dest_pid, | |
133 mach_port_name_t port, | |
134 const BrokerableAttachment::AttachmentId& id) | |
135 : source_pid_(source_pid), | |
136 dest_pid_(dest_pid), | |
137 port_to_extract_(port), | |
138 id_(id) {} | |
139 | |
140 AttachmentBrokerPrivilegedMac::AttachmentExtractor::~AttachmentExtractor() {} | |
141 | |
142 void AttachmentBrokerPrivilegedMac::OnDuplicateMachPort( | |
143 const IPC::Message& message) { | |
144 DCHECK_NE(0, message.get_sender_pid()); | |
145 AttachmentBrokerMsg_DuplicateMachPort::Param param; | |
146 if (!AttachmentBrokerMsg_DuplicateMachPort::Read(&message, ¶m)) { | |
147 LogError(ERROR_PARSE_DUPLICATE_MACH_PORT_MESSAGE); | |
148 return; | |
149 } | |
150 IPC::internal::MachPortAttachmentMac::WireFormat wire_format = | |
151 std::get<0>(param); | |
152 | |
153 if (wire_format.destination_process == base::kNullProcessId) { | |
154 LogError(NO_DESTINATION); | |
155 return; | |
156 } | |
157 | |
158 AddExtractor(message.get_sender_pid(), wire_format.destination_process, | |
159 wire_format.mach_port, wire_format.attachment_id); | |
160 ProcessExtractorsForProcess(message.get_sender_pid(), true); | |
161 } | |
162 | |
163 void AttachmentBrokerPrivilegedMac::RoutePrecursorToSelf( | |
164 AttachmentPrecursor* precursor) { | |
165 DCHECK_EQ(base::Process::Current().Pid(), precursor->pid()); | |
166 | |
167 // Intentionally leak the port, since the attachment takes ownership. | |
168 internal::MachPortAttachmentMac::WireFormat wire_format( | |
169 precursor->TakePort().release(), precursor->pid(), precursor->id()); | |
170 scoped_refptr<BrokerableAttachment> attachment( | |
171 new internal::MachPortAttachmentMac(wire_format)); | |
172 HandleReceivedAttachment(attachment); | |
173 } | |
174 | |
175 bool AttachmentBrokerPrivilegedMac::RouteWireFormatToAnother( | |
176 const MachPortWireFormat& wire_format) { | |
177 DCHECK_NE(wire_format.destination_process, base::Process::Current().Pid()); | |
178 | |
179 // Another process is the destination. | |
180 base::ProcessId dest = wire_format.destination_process; | |
181 base::AutoLock auto_lock(*get_lock()); | |
182 AttachmentBrokerPrivileged::EndpointRunnerPair pair = | |
183 GetSenderWithProcessId(dest); | |
184 if (!pair.first) { | |
185 // The extractor was successfully processed, which implies that the | |
186 // communication channel was established. This implies that the | |
187 // communication was taken down in the interim. | |
188 LOG(ERROR) << "Failed to deliver brokerable attachment to process with id: " | |
189 << dest; | |
190 LogError(DESTINATION_NOT_FOUND); | |
191 return false; | |
192 } | |
193 | |
194 LogError(DESTINATION_FOUND); | |
195 SendMessageToEndpoint( | |
196 pair, new AttachmentBrokerMsg_MachPortHasBeenDuplicated(wire_format)); | |
197 return true; | |
198 } | |
199 | |
200 base::mac::ScopedMachSendRight AttachmentBrokerPrivilegedMac::ExtractNamedRight( | |
201 mach_port_t task_port, | |
202 mach_port_name_t named_right) { | |
203 if (named_right == MACH_PORT_NULL) | |
204 return base::mac::ScopedMachSendRight(MACH_PORT_NULL); | |
205 | |
206 mach_port_t extracted_right = MACH_PORT_NULL; | |
207 mach_msg_type_name_t extracted_right_type; | |
208 kern_return_t kr = | |
209 mach_port_extract_right(task_port, named_right, MACH_MSG_TYPE_MOVE_SEND, | |
210 &extracted_right, &extracted_right_type); | |
211 if (kr != KERN_SUCCESS) { | |
212 LogError(ERROR_EXTRACT_SOURCE_RIGHT); | |
213 return base::mac::ScopedMachSendRight(MACH_PORT_NULL); | |
214 } | |
215 | |
216 DCHECK_EQ(static_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_PORT_SEND), | |
217 extracted_right_type); | |
218 | |
219 return base::mac::ScopedMachSendRight(extracted_right); | |
220 } | |
221 | |
222 AttachmentBrokerPrivilegedMac::MachPortWireFormat | |
223 AttachmentBrokerPrivilegedMac::CopyWireFormat( | |
224 const MachPortWireFormat& wire_format, | |
225 uint32_t mach_port) { | |
226 return MachPortWireFormat(mach_port, wire_format.destination_process, | |
227 wire_format.attachment_id); | |
228 } | |
229 | |
230 void AttachmentBrokerPrivilegedMac::SendPrecursorsForProcess( | |
231 base::ProcessId pid, | |
232 bool store_on_failure) { | |
233 base::AutoLock l(precursors_lock_); | |
234 auto it = precursors_.find(pid); | |
235 if (it == precursors_.end()) | |
236 return; | |
237 | |
238 // Whether this process is the destination process. | |
239 bool to_self = pid == base::GetCurrentProcId(); | |
240 | |
241 if (!to_self) { | |
242 base::AutoLock auto_lock(*get_lock()); | |
243 AttachmentBrokerPrivileged::EndpointRunnerPair pair = | |
244 GetSenderWithProcessId(pid); | |
245 if (!pair.first) { | |
246 // Try again later. | |
247 if (store_on_failure) | |
248 return; | |
249 | |
250 // If there is no sender, then permanently fail. | |
251 LogError(DESTINATION_NOT_FOUND); | |
252 delete it->second; | |
253 precursors_.erase(it); | |
254 return; | |
255 } | |
256 } | |
257 | |
258 mach_port_t task_port = port_provider_->TaskForPid(pid); | |
259 | |
260 // It's possible that the destination process has not yet provided the | |
261 // privileged process with its task port. | |
262 if (!to_self && task_port == MACH_PORT_NULL) | |
263 return; | |
264 | |
265 while (!it->second->empty()) { | |
266 auto precursor_it = it->second->begin(); | |
267 if (to_self) { | |
268 RoutePrecursorToSelf(*precursor_it); | |
269 } else { | |
270 if (!SendPrecursor(*precursor_it, task_port)) | |
271 break; | |
272 } | |
273 it->second->erase(precursor_it); | |
274 } | |
275 | |
276 delete it->second; | |
277 precursors_.erase(it); | |
278 } | |
279 | |
280 bool AttachmentBrokerPrivilegedMac::SendPrecursor( | |
281 AttachmentPrecursor* precursor, | |
282 mach_port_t task_port) { | |
283 DCHECK(task_port); | |
284 internal::MachPortAttachmentMac::WireFormat wire_format( | |
285 MACH_PORT_NULL, precursor->pid(), precursor->id()); | |
286 base::mac::ScopedMachSendRight port_to_insert = precursor->TakePort(); | |
287 mach_port_name_t intermediate_port = MACH_PORT_NULL; | |
288 if (port_to_insert.get() != MACH_PORT_NULL) { | |
289 base::MachCreateError error_code; | |
290 intermediate_port = base::CreateIntermediateMachPort( | |
291 task_port, base::mac::ScopedMachSendRight(port_to_insert.release()), | |
292 &error_code); | |
293 if (intermediate_port == MACH_PORT_NULL) { | |
294 UMAError uma_error; | |
295 switch (error_code) { | |
296 case base::MachCreateError::ERROR_MAKE_RECEIVE_PORT: | |
297 uma_error = ERROR_MAKE_RECEIVE_PORT; | |
298 break; | |
299 case base::MachCreateError::ERROR_SET_ATTRIBUTES: | |
300 uma_error = ERROR_SET_ATTRIBUTES; | |
301 break; | |
302 case base::MachCreateError::ERROR_EXTRACT_DEST_RIGHT: | |
303 uma_error = ERROR_EXTRACT_DEST_RIGHT; | |
304 break; | |
305 case base::MachCreateError::ERROR_SEND_MACH_PORT: | |
306 uma_error = ERROR_SEND_MACH_PORT; | |
307 break; | |
308 } | |
309 LogError(uma_error); | |
310 } | |
311 } | |
312 return RouteWireFormatToAnother( | |
313 CopyWireFormat(wire_format, intermediate_port)); | |
314 } | |
315 | |
316 void AttachmentBrokerPrivilegedMac::AddPrecursor( | |
317 base::ProcessId pid, | |
318 base::mac::ScopedMachSendRight port, | |
319 const BrokerableAttachment::AttachmentId& id) { | |
320 base::AutoLock l(precursors_lock_); | |
321 auto it = precursors_.find(pid); | |
322 if (it == precursors_.end()) | |
323 precursors_[pid] = new ScopedVector<AttachmentPrecursor>; | |
324 | |
325 precursors_[pid]->push_back(new AttachmentPrecursor( | |
326 pid, base::mac::ScopedMachSendRight(port.release()), id)); | |
327 } | |
328 | |
329 void AttachmentBrokerPrivilegedMac::ProcessExtractorsForProcess( | |
330 base::ProcessId pid, | |
331 bool store_on_failure) { | |
332 base::AutoLock l(extractors_lock_); | |
333 auto it = extractors_.find(pid); | |
334 if (it == extractors_.end()) | |
335 return; | |
336 | |
337 { | |
338 base::AutoLock auto_lock(*get_lock()); | |
339 AttachmentBrokerPrivileged::EndpointRunnerPair pair = | |
340 GetSenderWithProcessId(pid); | |
341 if (!pair.first) { | |
342 // If there is no sender, then the communication channel with the source | |
343 // process has not yet been established. Try again later. | |
344 if (store_on_failure) | |
345 return; | |
346 | |
347 // There is no sender. Permanently fail. | |
348 LogError(ERROR_SOURCE_NOT_FOUND); | |
349 delete it->second; | |
350 extractors_.erase(it); | |
351 return; | |
352 } | |
353 } | |
354 | |
355 mach_port_t task_port = port_provider_->TaskForPid(pid); | |
356 | |
357 // It's possible that the source process has not yet provided the privileged | |
358 // process with its task port. | |
359 if (task_port == MACH_PORT_NULL) | |
360 return; | |
361 | |
362 while (!it->second->empty()) { | |
363 auto extractor_it = it->second->begin(); | |
364 ProcessExtractor(*extractor_it, task_port); | |
365 it->second->erase(extractor_it); | |
366 } | |
367 | |
368 delete it->second; | |
369 extractors_.erase(it); | |
370 } | |
371 | |
372 void AttachmentBrokerPrivilegedMac::ProcessExtractor( | |
373 AttachmentExtractor* extractor, | |
374 mach_port_t task_port) { | |
375 DCHECK(task_port); | |
376 base::mac::ScopedMachSendRight send_right = | |
377 ExtractNamedRight(task_port, extractor->port()); | |
378 AddPrecursor(extractor->dest_pid(), | |
379 base::mac::ScopedMachSendRight(send_right.release()), | |
380 extractor->id()); | |
381 SendPrecursorsForProcess(extractor->dest_pid(), true); | |
382 } | |
383 | |
384 void AttachmentBrokerPrivilegedMac::AddExtractor( | |
385 base::ProcessId source_pid, | |
386 base::ProcessId dest_pid, | |
387 mach_port_name_t port, | |
388 const BrokerableAttachment::AttachmentId& id) { | |
389 base::AutoLock l(extractors_lock_); | |
390 DCHECK_NE(base::GetCurrentProcId(), source_pid); | |
391 | |
392 auto it = extractors_.find(source_pid); | |
393 if (it == extractors_.end()) | |
394 extractors_[source_pid] = new ScopedVector<AttachmentExtractor>; | |
395 | |
396 extractors_[source_pid]->push_back( | |
397 new AttachmentExtractor(source_pid, dest_pid, port, id)); | |
398 } | |
399 | |
400 } // namespace IPC | |
OLD | NEW |