1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
|
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_APPLE_MACH_PORT_RENDEZVOUS_H_
#define BASE_APPLE_MACH_PORT_RENDEZVOUS_H_
#include <dispatch/dispatch.h>
#include <mach/mach.h>
#include <stdint.h>
#include <sys/types.h>
#include <map>
#include <memory>
#include <vector>
#include "base/apple/dispatch_source.h"
#include "base/apple/scoped_mach_port.h"
#include "base/base_export.h"
#include "base/containers/buffer_iterator.h"
#include "base/feature_list.h"
#include "base/synchronization/lock.h"
#include "base/thread_annotations.h"
#include "build/ios_buildflags.h"
namespace base {
// Mach Port Rendezvous is a technique to exchange Mach port rights across
// child process creation. macOS does not provide a way to inherit Mach port
// rights, unlike what is possible with file descriptors. Port rendezvous
// enables a parent process to register Mach port rights for a nascent child,
// which the child can then retrieve using Mach IPC by looking up the endpoint
// in launchd's bootstrap namespace.
//
// The same mechanism is used on iOS but the Mach IPC endpoint is not found
// via launchd's bootstrap namespace but via an initial XPC connection.
//
// When launching a child process, the parent process' rendezvous server lets
// calling code register a collection of ports for the new child. In order to
// acquire the ports, a child looks up the rendezvous server in the bootstrap
// namespace, and it sends an IPC message to the server, the reply to which
// contains the registered ports.
//
// Port rendezvous is only permitted between a parent and its direct child
// process descendants.
// A MachRendezvousPort contains a single Mach port to pass to the child
// process. The associated disposition controls how the reference count will
// be manipulated.
class BASE_EXPORT MachRendezvousPort {
public:
MachRendezvousPort() = default;
// Creates a rendezvous port that allows specifying the specific disposition.
MachRendezvousPort(mach_port_t name, mach_msg_type_name_t disposition);
// Creates a rendezvous port for MACH_MSG_TYPE_MOVE_SEND.
explicit MachRendezvousPort(apple::ScopedMachSendRight send_right);
// Creates a rendezvous port for MACH_MSG_TYPE_MOVE_RECEIVE.
explicit MachRendezvousPort(apple::ScopedMachReceiveRight receive_right);
// Note that the destructor does not call Destroy() explicitly.
// To avoid leaking ports, either use dispositions that create rights during
// transit (MAKE or COPY), or use base::LaunchProcess, which will destroy
// rights on failure.
~MachRendezvousPort();
// Destroys the Mach port right type conveyed |disposition| named by |name|.
void Destroy();
mach_port_t name() const { return name_; }
mach_msg_type_name_t disposition() const { return disposition_; }
private:
mach_port_t name_ = MACH_PORT_NULL;
mach_msg_type_name_t disposition_ = 0;
// Copy and assign allowed.
};
// The collection of ports to pass to a child process. There are no restrictions
// regarding the keys of the map. Clients are responsible for avoiding
// collisions with other clients.
using MachPortsForRendezvous = std::map<uint32_t, MachRendezvousPort>;
// Base class that runs a Mach message server, listening to requests on
// mach server port.
class BASE_EXPORT MachPortRendezvousServerBase {
protected:
MachPortRendezvousServerBase();
virtual ~MachPortRendezvousServerBase();
// The Mach receive right for the server. A send right to this is port is
// registered in the bootstrap server.
apple::ScopedMachReceiveRight server_port_;
// Mach message dispatch source for |server_port_|.
std::unique_ptr<apple::DispatchSource> dispatch_source_;
// Ask for the associated ports associated with `audit_token`.
// Return `std::nullopt` if the client is not authorized to
// retrieve ports.
virtual std::optional<MachPortsForRendezvous> PortsForClient(
audit_token_t audit_token) = 0;
// Return whether `msg_id` should be accepted along with the known
// message IDs. Platform-specific subclasses may return additional data
// based on the `msg_id` within `AdditionalDataForReply`.
virtual bool IsValidAdditionalMessageId(mach_msg_id_t msg_id) const = 0;
// Return additional data to be attached to a reply for `request`.
virtual std::vector<uint8_t> AdditionalDataForReply(
mach_msg_id_t request) const = 0;
// The server-side Mach message handler. Called by |dispatch_source_| when a
// message is received.
void HandleRequest();
// Returns a buffer containing a well-formed Mach message, destined for
// `reply_port` containing descriptors for the specified `ports` and
// `additional_data`.
std::unique_ptr<uint8_t[]> CreateReplyMessage(
mach_port_t reply_port,
const MachPortsForRendezvous& ports,
std::vector<uint8_t> additional_data);
};
// Client class for accessing the memory object exposed by the
// MachPortRendezvousServer.
class BASE_EXPORT MachPortRendezvousClient {
public:
MachPortRendezvousClient(const MachPortRendezvousClient&) = delete;
MachPortRendezvousClient& operator=(const MachPortRendezvousClient&) = delete;
// Connects to the MachPortRendezvousServer and requests any registered Mach
// ports. This only performs the rendezvous once. Subsequent calls to this
// method return the same instance. If the rendezvous fails, which can happen
// if the server is not available or if the server fails the code signature
// validation and requirement check, this returns null. Acquiring zero ports
// from the exchange is not considered a failure.
static MachPortRendezvousClient* GetInstance();
// Returns the Mach send right that was registered with |key|. If no such
// right exists, or it was already taken, returns an invalid right. Safe to
// call from any thread. DCHECKs if the right referenced by |key| is not a
// send or send-once right.
apple::ScopedMachSendRight TakeSendRight(
MachPortsForRendezvous::key_type key);
// Returns the Mach receive right that was registered with |key|. If no such
// right exists, or it was already taken, returns an invalid right. Safe to
// call from any thread. DCHECKs if the right referenced by |key| is not a
// receive right.
apple::ScopedMachReceiveRight TakeReceiveRight(
MachPortsForRendezvous::key_type key);
// Returns the number of ports in the client. After PerformRendezvous(), this
// reflects the number of ports acquired. But as rights are taken, this
// only reflects the number of remaining rights.
size_t GetPortCount();
protected:
MachPortRendezvousClient();
virtual ~MachPortRendezvousClient();
// Perform platform-specific validation on a received message and the peer
// that sent it.
virtual bool ValidateMessage(mach_msg_base_t* message,
BufferIterator<uint8_t> body) = 0;
// Sends the actual IPC message to |server_port| and parses the reply.
bool SendRequest(apple::ScopedMachSendRight server_port,
mach_msg_id_t request_id,
size_t additional_response_data_size = 0)
EXCLUSIVE_LOCKS_REQUIRED(lock_);
// Returns a MachRendezvousPort for a given key and removes it from the
// |ports_| map. If an entry does not exist for that key, then a
// MachRendezvousPort with MACH_PORT_NULL is returned.
MachRendezvousPort PortForKey(MachPortsForRendezvous::key_type key);
Lock lock_;
// The collection of ports that was acquired.
MachPortsForRendezvous ports_ GUARDED_BY(lock_);
};
namespace internal {
// This limit is arbitrary and can be safely increased in the future.
inline constexpr size_t kMaximumRendezvousPorts = 6;
enum MachRendezvousMsgId : mach_msg_id_t {
kMachRendezvousMsgIdRequest = 'mrzv',
kMachRendezvousMsgIdResponse = 'MRZV',
#if BUILDFLAG(IS_MAC)
// When MachPortRendezvousClientMac has a `ProcessRequirement` that requests
// dynamic-only validation, it will request that the server provide a copy of
// its Info.plist data in the rendezvous response. Dynamic-only validation
// validates the running process without enforcing that it matches its on-disk
// representation. This is necessary when validating applications such as
// Chrome that may be updated on disk while the application is running.
//
// The Info.plist data ends up passed to `SecCodeCopyGuestWithAttributes`,
// where it is validated against the hash stored within the code signature
// before using it to evaluate any requirements involving Info.plist data.
kMachRendezvousMsgIdRequestWithInfoPlistData = 'mrzV',
#endif // BUILDFLAG(IS_MAC)
};
} // namespace internal
} // namespace base
#endif // BASE_APPLE_MACH_PORT_RENDEZVOUS_H_
|