// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ipc/attachment_broker_privileged_mac.h" #include #include #include "base/mac/mach_port_util.h" #include "base/mac/scoped_mach_port.h" #include "base/memory/shared_memory.h" #include "base/process/port_provider_mac.h" #include "base/process/process.h" #include "base/synchronization/lock.h" #include "ipc/attachment_broker_messages.h" #include "ipc/brokerable_attachment.h" #include "ipc/ipc_channel.h" #include "ipc/mach_port_attachment_mac.h" namespace IPC { AttachmentBrokerPrivilegedMac::AttachmentBrokerPrivilegedMac( base::PortProvider* port_provider) : port_provider_(port_provider) { port_provider_->AddObserver(this); } AttachmentBrokerPrivilegedMac::~AttachmentBrokerPrivilegedMac() { port_provider_->RemoveObserver(this); { base::AutoLock l(precursors_lock_); for (auto it : precursors_) delete it.second; } { base::AutoLock l(extractors_lock_); for (auto it : extractors_) delete it.second; } } bool AttachmentBrokerPrivilegedMac::SendAttachmentToProcess( const scoped_refptr& attachment, base::ProcessId destination_process) { switch (attachment->GetBrokerableType()) { case BrokerableAttachment::MACH_PORT: { internal::MachPortAttachmentMac* mach_port_attachment = static_cast(attachment.get()); MachPortWireFormat wire_format = mach_port_attachment->GetWireFormat(destination_process); AddPrecursor(wire_format.destination_process, base::mac::ScopedMachSendRight(wire_format.mach_port), wire_format.attachment_id); mach_port_attachment->reset_mach_port_ownership(); SendPrecursorsForProcess(wire_format.destination_process); return true; } default: NOTREACHED(); return false; } return false; } void AttachmentBrokerPrivilegedMac::DeregisterCommunicationChannel( Endpoint* endpoint) { AttachmentBrokerPrivileged::DeregisterCommunicationChannel(endpoint); if (!endpoint) return; base::ProcessId pid = endpoint->GetPeerPID(); if (pid == base::kNullProcessId) return; { base::AutoLock l(precursors_lock_); auto it = precursors_.find(pid); if (it != precursors_.end()) { delete it->second; precursors_.erase(pid); } } { base::AutoLock l(extractors_lock_); auto it = extractors_.find(pid); if (it != extractors_.end()) { delete it->second; extractors_.erase(pid); } } } bool AttachmentBrokerPrivilegedMac::OnMessageReceived(const Message& msg) { bool handled = true; switch (msg.type()) { IPC_MESSAGE_HANDLER_GENERIC(AttachmentBrokerMsg_DuplicateMachPort, OnDuplicateMachPort(msg)) IPC_MESSAGE_UNHANDLED(handled = false) } return handled; } void AttachmentBrokerPrivilegedMac::OnReceivedTaskPort( base::ProcessHandle process) { SendPrecursorsForProcess(process); } AttachmentBrokerPrivilegedMac::AttachmentPrecursor::AttachmentPrecursor( const base::ProcessId& pid, base::mac::ScopedMachSendRight port, const BrokerableAttachment::AttachmentId& id) : pid_(pid), port_(port.release()), id_(id) {} AttachmentBrokerPrivilegedMac::AttachmentPrecursor::~AttachmentPrecursor() {} base::mac::ScopedMachSendRight AttachmentBrokerPrivilegedMac::AttachmentPrecursor::TakePort() { return base::mac::ScopedMachSendRight(port_.release()); } AttachmentBrokerPrivilegedMac::AttachmentExtractor::AttachmentExtractor( const base::ProcessId& source_pid, const base::ProcessId& dest_pid, mach_port_name_t port, const BrokerableAttachment::AttachmentId& id) : source_pid_(source_pid), dest_pid_(dest_pid), port_to_extract_(port), id_(id) {} AttachmentBrokerPrivilegedMac::AttachmentExtractor::~AttachmentExtractor() {} void AttachmentBrokerPrivilegedMac::OnDuplicateMachPort( const IPC::Message& message) { DCHECK_NE(0, message.get_sender_pid()); AttachmentBrokerMsg_DuplicateMachPort::Param param; if (!AttachmentBrokerMsg_DuplicateMachPort::Read(&message, ¶m)) { LogError(ERROR_PARSE_DUPLICATE_MACH_PORT_MESSAGE); return; } IPC::internal::MachPortAttachmentMac::WireFormat wire_format = std::get<0>(param); if (wire_format.destination_process == base::kNullProcessId) { LogError(NO_DESTINATION); return; } AddExtractor(message.get_sender_pid(), wire_format.destination_process, wire_format.mach_port, wire_format.attachment_id); ProcessExtractorsForProcess(message.get_sender_pid()); } void AttachmentBrokerPrivilegedMac::RoutePrecursorToSelf( AttachmentPrecursor* precursor) { DCHECK_EQ(base::Process::Current().Pid(), precursor->pid()); // Intentionally leak the port, since the attachment takes ownership. internal::MachPortAttachmentMac::WireFormat wire_format( precursor->TakePort().release(), precursor->pid(), precursor->id()); scoped_refptr attachment( new internal::MachPortAttachmentMac(wire_format)); HandleReceivedAttachment(attachment); } bool AttachmentBrokerPrivilegedMac::RouteWireFormatToAnother( const MachPortWireFormat& wire_format) { DCHECK_NE(wire_format.destination_process, base::Process::Current().Pid()); // Another process is the destination. base::ProcessId dest = wire_format.destination_process; base::AutoLock auto_lock(*get_lock()); AttachmentBrokerPrivileged::EndpointRunnerPair pair = GetSenderWithProcessId(dest); if (!pair.first) { // Assuming that this message was not sent from a malicious process, the // channel endpoint that would have received this message will block // forever. LOG(ERROR) << "Failed to deliver brokerable attachment to process with id: " << dest; LogError(DESTINATION_NOT_FOUND); return false; } LogError(DESTINATION_FOUND); SendMessageToEndpoint( pair, new AttachmentBrokerMsg_MachPortHasBeenDuplicated(wire_format)); return true; } base::mac::ScopedMachSendRight AttachmentBrokerPrivilegedMac::ExtractNamedRight( mach_port_t task_port, mach_port_name_t named_right) { mach_port_t extracted_right = MACH_PORT_NULL; mach_msg_type_name_t extracted_right_type; kern_return_t kr = mach_port_extract_right(task_port, named_right, MACH_MSG_TYPE_MOVE_SEND, &extracted_right, &extracted_right_type); if (kr != KERN_SUCCESS) { LogError(ERROR_EXTRACT_SOURCE_RIGHT); return base::mac::ScopedMachSendRight(MACH_PORT_NULL); } DCHECK_EQ(static_cast(MACH_MSG_TYPE_PORT_SEND), extracted_right_type); return base::mac::ScopedMachSendRight(extracted_right); } AttachmentBrokerPrivilegedMac::MachPortWireFormat AttachmentBrokerPrivilegedMac::CopyWireFormat( const MachPortWireFormat& wire_format, uint32_t mach_port) { return MachPortWireFormat(mach_port, wire_format.destination_process, wire_format.attachment_id); } void AttachmentBrokerPrivilegedMac::SendPrecursorsForProcess( base::ProcessId pid) { base::AutoLock l(precursors_lock_); auto it = precursors_.find(pid); if (it == precursors_.end()) return; // Whether this process is the destination process. bool to_self = pid == base::GetCurrentProcId(); if (!to_self) { base::AutoLock auto_lock(*get_lock()); AttachmentBrokerPrivileged::EndpointRunnerPair pair = GetSenderWithProcessId(pid); if (!pair.first) { // If there is no sender, then the destination process is no longer // running, or never existed to begin with. LogError(DESTINATION_NOT_FOUND); delete it->second; precursors_.erase(it); return; } } mach_port_t task_port = port_provider_->TaskForPid(pid); // It's possible that the destination process has not yet provided the // privileged process with its task port. if (!to_self && task_port == MACH_PORT_NULL) return; while (!it->second->empty()) { auto precursor_it = it->second->begin(); if (to_self) { RoutePrecursorToSelf(*precursor_it); } else { if (!SendPrecursor(*precursor_it, task_port)) break; } it->second->erase(precursor_it); } delete it->second; precursors_.erase(it); } bool AttachmentBrokerPrivilegedMac::SendPrecursor( AttachmentPrecursor* precursor, mach_port_t task_port) { DCHECK(task_port); internal::MachPortAttachmentMac::WireFormat wire_format( MACH_PORT_NULL, precursor->pid(), precursor->id()); base::mac::ScopedMachSendRight port_to_insert = precursor->TakePort(); mach_port_name_t intermediate_port = MACH_PORT_NULL; if (port_to_insert.get() != MACH_PORT_NULL) { base::MachCreateError error_code; intermediate_port = base::CreateIntermediateMachPort( task_port, base::mac::ScopedMachSendRight(port_to_insert.release()), &error_code); if (intermediate_port == MACH_PORT_NULL) { UMAError uma_error; switch (error_code) { case base::MachCreateError::ERROR_MAKE_RECEIVE_PORT: uma_error = ERROR_MAKE_RECEIVE_PORT; break; case base::MachCreateError::ERROR_SET_ATTRIBUTES: uma_error = ERROR_SET_ATTRIBUTES; break; case base::MachCreateError::ERROR_EXTRACT_DEST_RIGHT: uma_error = ERROR_EXTRACT_DEST_RIGHT; break; case base::MachCreateError::ERROR_SEND_MACH_PORT: uma_error = ERROR_SEND_MACH_PORT; break; } LogError(uma_error); } } return RouteWireFormatToAnother( CopyWireFormat(wire_format, intermediate_port)); } void AttachmentBrokerPrivilegedMac::AddPrecursor( base::ProcessId pid, base::mac::ScopedMachSendRight port, const BrokerableAttachment::AttachmentId& id) { base::AutoLock l(precursors_lock_); auto it = precursors_.find(pid); if (it == precursors_.end()) precursors_[pid] = new ScopedVector; precursors_[pid]->push_back(new AttachmentPrecursor( pid, base::mac::ScopedMachSendRight(port.release()), id)); } void AttachmentBrokerPrivilegedMac::ProcessExtractorsForProcess( base::ProcessId pid) { base::AutoLock l(extractors_lock_); auto it = extractors_.find(pid); if (it == extractors_.end()) return; { base::AutoLock auto_lock(*get_lock()); AttachmentBrokerPrivileged::EndpointRunnerPair pair = GetSenderWithProcessId(pid); if (!pair.first) { // If there is no sender, then the source process is no longer running. LogError(ERROR_SOURCE_NOT_FOUND); delete it->second; extractors_.erase(it); return; } } mach_port_t task_port = port_provider_->TaskForPid(pid); // It's possible that the source process has not yet provided the privileged // process with its task port. if (task_port == MACH_PORT_NULL) return; while (!it->second->empty()) { auto extractor_it = it->second->begin(); ProcessExtractor(*extractor_it, task_port); it->second->erase(extractor_it); } delete it->second; extractors_.erase(it); } void AttachmentBrokerPrivilegedMac::ProcessExtractor( AttachmentExtractor* extractor, mach_port_t task_port) { DCHECK(task_port); base::mac::ScopedMachSendRight send_right = ExtractNamedRight(task_port, extractor->port()); AddPrecursor(extractor->dest_pid(), base::mac::ScopedMachSendRight(send_right.release()), extractor->id()); SendPrecursorsForProcess(extractor->dest_pid()); } void AttachmentBrokerPrivilegedMac::AddExtractor( base::ProcessId source_pid, base::ProcessId dest_pid, mach_port_name_t port, const BrokerableAttachment::AttachmentId& id) { base::AutoLock l(extractors_lock_); DCHECK_NE(base::GetCurrentProcId(), source_pid); auto it = extractors_.find(source_pid); if (it == extractors_.end()) extractors_[source_pid] = new ScopedVector; extractors_[source_pid]->push_back( new AttachmentExtractor(source_pid, dest_pid, port, id)); } } // namespace IPC