| // Copyright 2016 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 "base/mac/mach_port_broker.h" | 
 |  | 
 | #include <bsm/libbsm.h> | 
 | #include <servers/bootstrap.h> | 
 |  | 
 | #include "base/logging.h" | 
 | #include "base/mac/foundation_util.h" | 
 | #include "base/mac/mach_logging.h" | 
 | #include "base/strings/string_util.h" | 
 | #include "base/strings/stringprintf.h" | 
 |  | 
 | namespace base { | 
 |  | 
 | namespace { | 
 |  | 
 | // Mach message structure used in the child as a sending message. | 
 | struct MachPortBroker_ChildSendMsg { | 
 |   mach_msg_header_t header; | 
 |   mach_msg_body_t body; | 
 |   mach_msg_port_descriptor_t child_task_port; | 
 | }; | 
 |  | 
 | // Complement to the ChildSendMsg, this is used in the parent for receiving | 
 | // a message. Contains a message trailer with audit information. | 
 | struct MachPortBroker_ParentRecvMsg : public MachPortBroker_ChildSendMsg { | 
 |   mach_msg_audit_trailer_t trailer; | 
 | }; | 
 |  | 
 | }  // namespace | 
 |  | 
 | // static | 
 | bool MachPortBroker::ChildSendTaskPortToParent(const std::string& name) { | 
 |   // Look up the named MachPortBroker port that's been registered with the | 
 |   // bootstrap server. | 
 |   mach_port_t parent_port; | 
 |   kern_return_t kr = bootstrap_look_up(bootstrap_port, | 
 |       const_cast<char*>(GetMachPortName(name, true).c_str()), &parent_port); | 
 |   if (kr != KERN_SUCCESS) { | 
 |     BOOTSTRAP_LOG(ERROR, kr) << "bootstrap_look_up"; | 
 |     return false; | 
 |   } | 
 |   base::mac::ScopedMachSendRight scoped_right(parent_port); | 
 |  | 
 |   // Create the check in message. This will copy a send right on this process' | 
 |   // (the child's) task port and send it to the parent. | 
 |   MachPortBroker_ChildSendMsg msg; | 
 |   bzero(&msg, sizeof(msg)); | 
 |   msg.header.msgh_bits = MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_COPY_SEND) | | 
 |                          MACH_MSGH_BITS_COMPLEX; | 
 |   msg.header.msgh_remote_port = parent_port; | 
 |   msg.header.msgh_size = sizeof(msg); | 
 |   msg.body.msgh_descriptor_count = 1; | 
 |   msg.child_task_port.name = mach_task_self(); | 
 |   msg.child_task_port.disposition = MACH_MSG_TYPE_PORT_SEND; | 
 |   msg.child_task_port.type = MACH_MSG_PORT_DESCRIPTOR; | 
 |  | 
 |   kr = mach_msg(&msg.header, MACH_SEND_MSG | MACH_SEND_TIMEOUT, sizeof(msg), | 
 |       0, MACH_PORT_NULL, 100 /*milliseconds*/, MACH_PORT_NULL); | 
 |   if (kr != KERN_SUCCESS) { | 
 |     MACH_LOG(ERROR, kr) << "mach_msg"; | 
 |     return false; | 
 |   } | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | // static | 
 | std::string MachPortBroker::GetMachPortName(const std::string& name, | 
 |                                             bool is_child) { | 
 |   // In child processes, use the parent's pid. | 
 |   const pid_t pid = is_child ? getppid() : getpid(); | 
 |   return base::StringPrintf( | 
 |       "%s.%s.%d", base::mac::BaseBundleID(), name.c_str(), pid); | 
 | } | 
 |  | 
 | mach_port_t MachPortBroker::TaskForPid(base::ProcessHandle pid) const { | 
 |   base::AutoLock lock(lock_); | 
 |   MachPortBroker::MachMap::const_iterator it = mach_map_.find(pid); | 
 |   if (it == mach_map_.end()) | 
 |     return MACH_PORT_NULL; | 
 |   return it->second; | 
 | } | 
 |  | 
 | MachPortBroker::MachPortBroker(const std::string& name) : name_(name) {} | 
 |  | 
 | MachPortBroker::~MachPortBroker() {} | 
 |  | 
 | bool MachPortBroker::Init() { | 
 |   DCHECK(server_port_.get() == MACH_PORT_NULL); | 
 |  | 
 |   // Check in with launchd and publish the service name. | 
 |   mach_port_t port; | 
 |   kern_return_t kr = bootstrap_check_in( | 
 |       bootstrap_port, GetMachPortName(name_, false).c_str(), &port); | 
 |   if (kr != KERN_SUCCESS) { | 
 |     BOOTSTRAP_LOG(ERROR, kr) << "bootstrap_check_in"; | 
 |     return false; | 
 |   } | 
 |   server_port_.reset(port); | 
 |  | 
 |   // Start the dispatch source. | 
 |   std::string queue_name = | 
 |       base::StringPrintf("%s.MachPortBroker", base::mac::BaseBundleID()); | 
 |   dispatch_source_.reset(new base::DispatchSourceMach( | 
 |       queue_name.c_str(), server_port_.get(), ^{ HandleRequest(); })); | 
 |   dispatch_source_->Resume(); | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | void MachPortBroker::AddPlaceholderForPid(base::ProcessHandle pid) { | 
 |   lock_.AssertAcquired(); | 
 |   DCHECK_EQ(0u, mach_map_.count(pid)); | 
 |   mach_map_[pid] = MACH_PORT_NULL; | 
 | } | 
 |  | 
 | void MachPortBroker::InvalidatePid(base::ProcessHandle pid) { | 
 |   lock_.AssertAcquired(); | 
 |  | 
 |   MachMap::iterator mach_it = mach_map_.find(pid); | 
 |   if (mach_it != mach_map_.end()) { | 
 |     kern_return_t kr = mach_port_deallocate(mach_task_self(), mach_it->second); | 
 |     MACH_LOG_IF(WARNING, kr != KERN_SUCCESS, kr) << "mach_port_deallocate"; | 
 |     mach_map_.erase(mach_it); | 
 |   } | 
 | } | 
 |  | 
 | void MachPortBroker::HandleRequest() { | 
 |   MachPortBroker_ParentRecvMsg msg; | 
 |   bzero(&msg, sizeof(msg)); | 
 |   msg.header.msgh_size = sizeof(msg); | 
 |   msg.header.msgh_local_port = server_port_.get(); | 
 |  | 
 |   const mach_msg_option_t options = MACH_RCV_MSG | | 
 |       MACH_RCV_TRAILER_TYPE(MACH_RCV_TRAILER_AUDIT) | | 
 |       MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT); | 
 |  | 
 |   kern_return_t kr = mach_msg(&msg.header, | 
 |                               options, | 
 |                               0, | 
 |                               sizeof(msg), | 
 |                               server_port_.get(), | 
 |                               MACH_MSG_TIMEOUT_NONE, | 
 |                               MACH_PORT_NULL); | 
 |   if (kr != KERN_SUCCESS) { | 
 |     MACH_LOG(ERROR, kr) << "mach_msg"; | 
 |     return; | 
 |   } | 
 |  | 
 |   // Use the kernel audit information to make sure this message is from | 
 |   // a task that this process spawned. The kernel audit token contains the | 
 |   // unspoofable pid of the task that sent the message. | 
 |   pid_t child_pid = audit_token_to_pid(msg.trailer.msgh_audit); | 
 |   mach_port_t child_task_port = msg.child_task_port.name; | 
 |  | 
 |   // Take the lock and update the broker information. | 
 |   { | 
 |     base::AutoLock lock(lock_); | 
 |     FinalizePid(child_pid, child_task_port); | 
 |   } | 
 |   NotifyObservers(child_pid); | 
 | } | 
 |  | 
 | void MachPortBroker::FinalizePid(base::ProcessHandle pid, | 
 |                                  mach_port_t task_port) { | 
 |   lock_.AssertAcquired(); | 
 |  | 
 |   MachMap::iterator it = mach_map_.find(pid); | 
 |   if (it == mach_map_.end()) { | 
 |     // Do nothing for unknown pids. | 
 |     LOG(ERROR) << "Unknown process " << pid << " is sending Mach IPC messages!"; | 
 |     return; | 
 |   } | 
 |  | 
 |   DCHECK(it->second == MACH_PORT_NULL); | 
 |   if (it->second == MACH_PORT_NULL) | 
 |     it->second = task_port; | 
 | } | 
 |  | 
 | }  // namespace base |