1 // Copyright 2019 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef BASE_APPLE_MACH_PORT_RENDEZVOUS_H_ 6 #define BASE_APPLE_MACH_PORT_RENDEZVOUS_H_ 7 8 #include <dispatch/dispatch.h> 9 #include <mach/mach.h> 10 #include <stdint.h> 11 #include <sys/types.h> 12 13 #include <map> 14 #include <memory> 15 #include <string> 16 #include <vector> 17 18 #include "base/apple/dispatch_source_mach.h" 19 #include "base/apple/scoped_mach_port.h" 20 #include "base/base_export.h" 21 #include "base/containers/buffer_iterator.h" 22 #include "base/feature_list.h" 23 #include "base/synchronization/lock.h" 24 #include "base/thread_annotations.h" 25 #include "build/ios_buildflags.h" 26 27 #if BUILDFLAG(IS_MAC) 28 #include "base/apple/scoped_dispatch_object.h" 29 #include "base/environment.h" 30 #include "base/mac/process_requirement.h" 31 #endif 32 33 namespace base { 34 35 // Mach Port Rendezvous is a technique to exchange Mach port rights across 36 // child process creation. macOS does not provide a way to inherit Mach port 37 // rights, unlike what is possible with file descriptors. Port rendezvous 38 // enables a parent process to register Mach port rights for a nascent child, 39 // which the child can then retrieve using Mach IPC by looking up the endpoint 40 // in launchd's bootstrap namespace. 41 // 42 // The same mechanism is used on iOS but the Mach IPC endpoint is not found 43 // via launchd's bootstrap namespace but via an initial XPC connection. 44 // 45 // When launching a child process, the parent process' rendezvous server lets 46 // calling code register a collection of ports for the new child. In order to 47 // acquire the ports, a child looks up the rendezvous server in the bootstrap 48 // namespace, and it sends an IPC message to the server, the reply to which 49 // contains the registered ports. 50 // 51 // Port rendezvous is only permitted between a parent and its direct child 52 // process descendants. 53 54 // A MachRendezvousPort contains a single Mach port to pass to the child 55 // process. The associated disposition controls how the reference count will 56 // be manipulated. 57 class BASE_EXPORT MachRendezvousPort { 58 public: 59 MachRendezvousPort() = default; 60 // Creates a rendezvous port that allows specifying the specific disposition. 61 MachRendezvousPort(mach_port_t name, mach_msg_type_name_t disposition); 62 // Creates a rendezvous port for MACH_MSG_TYPE_MOVE_SEND. 63 explicit MachRendezvousPort(apple::ScopedMachSendRight send_right); 64 // Creates a rendezvous port for MACH_MSG_TYPE_MOVE_RECEIVE. 65 explicit MachRendezvousPort(apple::ScopedMachReceiveRight receive_right); 66 67 // Note that the destructor does not call Destroy() explicitly. 68 // To avoid leaking ports, either use dispositions that create rights during 69 // transit (MAKE or COPY), or use base::LaunchProcess, which will destroy 70 // rights on failure. 71 ~MachRendezvousPort(); 72 73 // Destroys the Mach port right type conveyed |disposition| named by |name|. 74 void Destroy(); 75 name()76 mach_port_t name() const { return name_; } 77 disposition()78 mach_msg_type_name_t disposition() const { return disposition_; } 79 80 private: 81 mach_port_t name_ = MACH_PORT_NULL; 82 mach_msg_type_name_t disposition_ = 0; 83 // Copy and assign allowed. 84 }; 85 86 // The collection of ports to pass to a child process. There are no restrictions 87 // regarding the keys of the map. Clients are responsible for avoiding 88 // collisions with other clients. 89 using MachPortsForRendezvous = std::map<uint32_t, MachRendezvousPort>; 90 91 // Base class that runs a Mach message server, listening to requests on 92 // mach server port. 93 class BASE_EXPORT MachPortRendezvousServerBase { 94 protected: 95 MachPortRendezvousServerBase(); 96 virtual ~MachPortRendezvousServerBase(); 97 98 // The Mach receive right for the server. A send right to this is port is 99 // registered in the bootstrap server. 100 apple::ScopedMachReceiveRight server_port_; 101 102 // Mach message dispatch source for |server_port_|. 103 std::unique_ptr<apple::DispatchSourceMach> dispatch_source_; 104 105 // Ask for the associated ports associated with `audit_token`. 106 // Return `std::nullopt` if the client is not authorized to 107 // retrieve ports. 108 virtual std::optional<MachPortsForRendezvous> PortsForClient( 109 audit_token_t audit_token) = 0; 110 111 // Return whether `msg_id` should be accepted along with the known 112 // message IDs. Platform-specific subclasses may return additional data 113 // based on the `msg_id` within `AdditionalDataForReply`. 114 virtual bool IsValidAdditionalMessageId(mach_msg_id_t msg_id) const = 0; 115 116 // Return additional data to be attached to a reply for `request`. 117 virtual std::vector<uint8_t> AdditionalDataForReply( 118 mach_msg_id_t request) const = 0; 119 120 // The server-side Mach message handler. Called by |dispatch_source_| when a 121 // message is received. 122 void HandleRequest(); 123 124 // Returns a buffer containing a well-formed Mach message, destined for 125 // `reply_port` containing descriptors for the specified `ports` and 126 // `additional_data`. 127 std::unique_ptr<uint8_t[]> CreateReplyMessage( 128 mach_port_t reply_port, 129 const MachPortsForRendezvous& ports, 130 std::vector<uint8_t> additional_data); 131 }; 132 133 #if BUILDFLAG(IS_IOS) 134 // An implementation class that works for a single process. It is intended 135 // that each process spawned will create a corresponding instance and the 136 // mach send right of this server will be sent using XPC to the process. 137 class BASE_EXPORT MachPortRendezvousServerIOS final 138 : public MachPortRendezvousServerBase { 139 public: 140 MachPortRendezvousServerIOS(const MachPortsForRendezvous& ports); 141 ~MachPortRendezvousServerIOS() override; 142 MachPortRendezvousServerIOS(const MachPortRendezvousServerIOS&) = delete; 143 MachPortRendezvousServerIOS& operator=(const MachPortRendezvousServerIOS&) = 144 delete; 145 146 // Retrieve the send right to be sent to the process. 147 apple::ScopedMachSendRight GetMachSendRight(); 148 149 protected: 150 std::optional<MachPortsForRendezvous> PortsForClient(audit_token_t) override; 151 bool IsValidAdditionalMessageId(mach_msg_id_t) const override; 152 std::vector<uint8_t> AdditionalDataForReply(mach_msg_id_t) const override; 153 154 private: 155 apple::ScopedMachSendRight send_right_; 156 MachPortsForRendezvous ports_; 157 }; 158 159 #endif // BUILDFLAG(IS_IOS) 160 161 #if BUILDFLAG(IS_MAC) 162 163 // An implementation class that uses bootstrap to register ports to many 164 // processes. 165 class BASE_EXPORT MachPortRendezvousServerMac final 166 : public MachPortRendezvousServerBase { 167 public: 168 // Returns the instance of the server. Upon the first call to this method, 169 // the server is created, which registers an endpoint in the Mach bootstrap 170 // namespace. 171 static MachPortRendezvousServerMac* GetInstance(); 172 173 // Add feature state to an environment variable that will be used when 174 // launching a child process. MachPortRendezvousClient is used during 175 // feature list initialization so any state it uses must be passed 176 // via a side channel. 177 // TODO(crbug.com/362302761): Remove once enforcement is enabled by default. 178 static void AddFeatureStateToEnvironment(EnvironmentMap& environment); 179 180 MachPortRendezvousServerMac(const MachPortRendezvousServerMac&) = delete; 181 MachPortRendezvousServerMac& operator=(const MachPortRendezvousServerMac&) = 182 delete; 183 184 // Registers a collection of Mach ports |ports| to be acquirable by the 185 // process known by |pid|. This cannot be called again for the same |pid| 186 // until the process known by |pid| has either acquired the ports or died. 187 // 188 // This must be called with the lock from GetLock() held. 189 void RegisterPortsForPid(pid_t pid, const MachPortsForRendezvous& ports) 190 EXCLUSIVE_LOCKS_REQUIRED(GetLock()); 191 192 // Sets the process requirement that `pid` must match before it 193 // can acquire any Mach ports. This cannot be called again for the same `pid` 194 // until the process known by `pid` has acquired the ports or died. 195 // 196 // This must be called with the lock from GetLock() held. 197 void SetProcessRequirementForPid(pid_t pid, 198 mac::ProcessRequirement requirement) 199 EXCLUSIVE_LOCKS_REQUIRED(GetLock()); 200 201 // Returns a lock on the internal port registration map. The parent process 202 // should hold this lock for the duration of launching a process, including 203 // after calling RegisterPortsForPid(). This ensures that a child process 204 // cannot race acquiring ports before they are registered. The lock should 205 // be released after the child process is launched and the ports are 206 // registered. GetLock()207 Lock& GetLock() LOCK_RETURNED(lock_) { return lock_; } 208 209 protected: 210 // Returns the registered collection of ports for the specified `audit_token`. 211 // `std::nullopt` indicates that the client is not authorized to retrieve the 212 // ports. This claims the collection of ports and removes the entry from 213 // `client_data_`. 214 std::optional<MachPortsForRendezvous> PortsForClient( 215 audit_token_t audit_token) override; 216 217 bool IsValidAdditionalMessageId(mach_msg_id_t) const override; 218 std::vector<uint8_t> AdditionalDataForReply( 219 mach_msg_id_t request) const override; 220 221 private: 222 friend class MachPortRendezvousServerTest; 223 friend struct MachPortRendezvousFuzzer; 224 225 MachPortRendezvousServerMac(); 226 ~MachPortRendezvousServerMac() override; 227 228 struct ClientData { 229 ClientData(); 230 ClientData(ClientData&&); 231 ~ClientData(); 232 233 // A DISPATCH_SOURCE_TYPE_PROC / DISPATCH_PROC_EXIT dispatch source. When 234 // the source is triggered, it calls OnClientExited(). 235 apple::ScopedDispatchObject<dispatch_source_t> exit_watcher; 236 237 MachPortsForRendezvous ports; 238 std::optional<mac::ProcessRequirement> requirement; 239 }; 240 241 // Returns the `ClientData` for `pid`, creating it if necessary. 242 // It will be cleaned up automatically when `pid` exits. 243 ClientData& ClientDataForPid(int pid) EXCLUSIVE_LOCKS_REQUIRED(GetLock()); 244 245 // Called by the ClientData::exit_watcher dispatch sources when a process 246 // for which ports have been registered exits. This releases port rights 247 // that are strongly owned, in the event that the child has not claimed them. 248 void OnClientExited(pid_t pid); 249 250 Lock lock_; 251 // Association of pid-to-ports. 252 std::map<pid_t, ClientData> client_data_ GUARDED_BY(lock_); 253 }; 254 255 #endif 256 257 // Client class for accessing the memory object exposed by the 258 // MachPortRendezvousServer. 259 class BASE_EXPORT MachPortRendezvousClient { 260 public: 261 MachPortRendezvousClient(const MachPortRendezvousClient&) = delete; 262 MachPortRendezvousClient& operator=(const MachPortRendezvousClient&) = delete; 263 264 // Connects to the MachPortRendezvousServer and requests any registered Mach 265 // ports. This only performs the rendezvous once. Subsequent calls to this 266 // method return the same instance. If the rendezvous fails, which can happen 267 // if the server is not available or if the server fails the code signature 268 // validation and requirement check, this returns null. Acquiring zero ports 269 // from the exchange is not considered a failure. 270 static MachPortRendezvousClient* GetInstance(); 271 272 // Returns the Mach send right that was registered with |key|. If no such 273 // right exists, or it was already taken, returns an invalid right. Safe to 274 // call from any thread. DCHECKs if the right referenced by |key| is not a 275 // send or send-once right. 276 apple::ScopedMachSendRight TakeSendRight( 277 MachPortsForRendezvous::key_type key); 278 279 // Returns the Mach receive right that was registered with |key|. If no such 280 // right exists, or it was already taken, returns an invalid right. Safe to 281 // call from any thread. DCHECKs if the right referenced by |key| is not a 282 // receive right. 283 apple::ScopedMachReceiveRight TakeReceiveRight( 284 MachPortsForRendezvous::key_type key); 285 286 // Returns the number of ports in the client. After PerformRendezvous(), this 287 // reflects the number of ports acquired. But as rights are taken, this 288 // only reflects the number of remaining rights. 289 size_t GetPortCount(); 290 291 protected: 292 MachPortRendezvousClient(); 293 virtual ~MachPortRendezvousClient(); 294 295 // Perform platform-specific validation on a received message and the peer 296 // that sent it. 297 virtual bool ValidateMessage(mach_msg_base_t* message, 298 BufferIterator<uint8_t> body) = 0; 299 300 // Sends the actual IPC message to |server_port| and parses the reply. 301 bool SendRequest(apple::ScopedMachSendRight server_port, 302 mach_msg_id_t request_id, 303 size_t additional_response_data_size = 0) 304 EXCLUSIVE_LOCKS_REQUIRED(lock_); 305 306 // Returns a MachRendezvousPort for a given key and removes it from the 307 // |ports_| map. If an entry does not exist for that key, then a 308 // MachRendezvousPort with MACH_PORT_NULL is returned. 309 MachRendezvousPort PortForKey(MachPortsForRendezvous::key_type key); 310 311 Lock lock_; 312 // The collection of ports that was acquired. 313 MachPortsForRendezvous ports_ GUARDED_BY(lock_); 314 }; 315 316 #if BUILDFLAG(IS_IOS) 317 BASE_EXPORT 318 class BASE_EXPORT MachPortRendezvousClientIOS final 319 : public MachPortRendezvousClient { 320 public: 321 // Initialize the MacPortRendezvousClient using `server_port`. 322 static bool Initialize(apple::ScopedMachSendRight server_port); 323 324 protected: 325 bool ValidateMessage(mach_msg_base_t* message, 326 BufferIterator<uint8_t> body) override; 327 328 private: 329 MachPortRendezvousClientIOS(); 330 ~MachPortRendezvousClientIOS() override; 331 332 // Helper method to look up the server in the bootstrap namespace and send 333 // the acquisition request message. 334 bool AcquirePorts(apple::ScopedMachSendRight server_port); 335 }; 336 #endif 337 338 #if BUILDFLAG(IS_MAC) 339 340 // Describes how the `ProcessRequirement` should be used during Mach port 341 // rendezvous. The active policy is derived from the feature flags in the 342 // browser process and is passed via an environment variable to child processes. 343 // TODO(crbug.com/362302761): Remove this policy once enforcement is enabled by 344 // default. 345 enum class MachPortRendezvousPeerValidationPolicy { 346 // Do not validate the peer against a process requirement. 347 kNoValidation, 348 349 // Validate the peer against a process requirement, if specified, but do not 350 // abort rendezvous if validation fails. Used to gather success metrics during 351 // experiment rollout. 352 kValidateOnly, 353 354 // Validate the peer against a process requirement, if specified, and abort 355 // rendezvous if the validation fails. 356 kEnforce, 357 }; 358 359 class BASE_EXPORT MachPortRendezvousClientMac final 360 : public MachPortRendezvousClient { 361 public: 362 // Set a ProcessRequirement that the server should be validated 363 // against before accepting any Mach ports from it. 364 // 365 // Must be called before `GetInstance` or this will have no effect. 366 static void SetServerProcessRequirement(mac::ProcessRequirement requirement); 367 368 // Get the peer validation policy that was derived from feature flags. 369 static MachPortRendezvousPeerValidationPolicy 370 PeerValidationPolicyForTesting(); 371 372 protected: 373 // Validate the server against a process requirement if one was set via 374 // `SetServerProcessRequirement`. 375 bool ValidateMessage(mach_msg_base_t* message, 376 BufferIterator<uint8_t> body) override; 377 378 private: 379 friend class MachPortRendezvousClient; 380 381 MachPortRendezvousClientMac(); 382 ~MachPortRendezvousClientMac() override; 383 384 // Returns the name of the server to look up in the bootstrap namespace. 385 static std::string GetBootstrapName(); 386 387 // Helper method to look up the server in the bootstrap namespace and send 388 // the acquisition request message. 389 bool AcquirePorts(); 390 391 // Take ownership of the server process requirement, if any. 392 static std::optional<mac::ProcessRequirement> 393 TakeServerCodeSigningRequirement(); 394 395 // Whether Info.plist data is needed from the server in order 396 // to validate `server_requirement_`. 397 bool NeedsInfoPlistData() const; 398 399 std::optional<mac::ProcessRequirement> server_requirement_; 400 }; 401 402 // Whether any peer process requirements should be validated. 403 BASE_EXPORT BASE_DECLARE_FEATURE(kMachPortRendezvousValidatePeerRequirements); 404 405 // Whether a failure to validate a peer process against a requirement 406 // should result in aborting the rendezvous. 407 BASE_EXPORT BASE_DECLARE_FEATURE(kMachPortRendezvousEnforcePeerRequirements); 408 409 #endif 410 411 } // namespace base 412 413 #endif // BASE_APPLE_MACH_PORT_RENDEZVOUS_H_ 414