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 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/341324165): Fix and remove.
7 #pragma allow_unsafe_buffers
8 #endif
9
10 #include "base/apple/mach_port_rendezvous.h"
11
12 #include <mach/mig.h>
13 #include <unistd.h>
14
15 #include <utility>
16
17 #include "base/apple/foundation_util.h"
18 #include "base/apple/mach_logging.h"
19 #include "base/bits.h"
20 #include "base/containers/buffer_iterator.h"
21 #include "base/feature_list.h"
22 #include "base/logging.h"
23 #include "base/mac/scoped_mach_msg_destroy.h"
24 #include "base/no_destructor.h"
25 #include "base/notreached.h"
26 #include "base/numerics/byte_conversions.h"
27 #include "base/strings/stringprintf.h"
28 #include "base/types/cxx23_to_underlying.h"
29
30 #if BUILDFLAG(IS_IOS)
31 #include "base/ios/sim_header_shims.h"
32 #endif
33
34 #if BUILDFLAG(IS_MAC)
35 #include <bsm/libbsm.h>
36 #include <servers/bootstrap.h>
37
38 #include "base/environment.h"
39 #include "base/mac/info_plist_data.h"
40 #include "base/strings/string_number_conversions.h"
41 #endif
42
43 namespace base {
44
45 #if BUILDFLAG(IS_MAC)
46 // Whether any peer process requirements should be validated.
47 BASE_FEATURE(kMachPortRendezvousValidatePeerRequirements,
48 "MachPortRendezvousValidatePeerRequirements",
49 base::FEATURE_DISABLED_BY_DEFAULT);
50
51 // Whether a failure to validate a peer process against a requirement
52 // should result in aborting the rendezvous.
53 BASE_FEATURE(kMachPortRendezvousEnforcePeerRequirements,
54 "MachPortRendezvousEnforcePeerRequirements",
55 base::FEATURE_DISABLED_BY_DEFAULT);
56 #endif
57
58 namespace {
59
60 #if BUILDFLAG(IS_IOS)
61 static MachPortRendezvousClientIOS* g_client = nullptr;
62 #endif
63
64 #if BUILDFLAG(IS_MAC)
65 // The name to use in the bootstrap server, formatted with the BaseBundleID and
66 // PID of the server.
67 constexpr char kBootstrapNameFormat[] = "%s.MachPortRendezvousServer.%d";
68
69 // This can be safely increased if Info.plist grows in the future.
70 constexpr size_t kMaxInfoPlistDataSize = 18 * 1024;
71
72 #endif
73
74 // This limit is arbitrary and can be safely increased in the future.
75 constexpr size_t kMaximumRendezvousPorts = 5;
76
77 enum MachRendezvousMsgId : mach_msg_id_t {
78 kMachRendezvousMsgIdRequest = 'mrzv',
79 kMachRendezvousMsgIdResponse = 'MRZV',
80
81 #if BUILDFLAG(IS_MAC)
82 // When MachPortRendezvousClientMac has a `ProcessRequirement` that requests
83 // dynamic-only validation, it will request that the server provide a copy of
84 // its Info.plist data in the rendezvous response. Dynamic-only validation
85 // validates the running process without enforcing that it matches its on-disk
86 // representation. This is necessary when validating applications such as
87 // Chrome that may be updated on disk while the application is running.
88 //
89 // The Info.plist data ends up passed to `SecCodeCopyGuestWithAttributes`,
90 // where it is validated against the hash stored within the code signature
91 // before using it to evaluate any requirements involving Info.plist data.
92 kMachRendezvousMsgIdRequestWithInfoPlistData = 'mrzV',
93 #endif
94 };
95
CalculateResponseSize(size_t num_ports,size_t additional_data_length)96 size_t CalculateResponseSize(size_t num_ports, size_t additional_data_length) {
97 return bits::AlignUp(
98 sizeof(mach_msg_base_t) +
99 (num_ports * sizeof(mach_msg_port_descriptor_t)) +
100 (num_ports * sizeof(MachPortsForRendezvous::key_type)) +
101 sizeof(uint64_t) + additional_data_length,
102 sizeof(uint32_t));
103 }
104
105 #if BUILDFLAG(IS_MAC)
106
107 // The state of the peer validation policy features is passed to child processes
108 // via this environment variable as Mach port rendezvous is performed before the
109 // feature list is initialized.
110 // TODO(crbug.com/362302761): Remove once enforcement is enabled by default.
111 constexpr char kPeerValidationPolicyEnvironmentVariable[] =
112 "MACH_PORT_RENDEZVOUS_PEER_VALDATION";
113
114 MachPortRendezvousPeerValidationPolicy GetPeerValidationPolicy();
115
ShouldValidateProcessRequirements()116 bool ShouldValidateProcessRequirements() {
117 return GetPeerValidationPolicy() !=
118 MachPortRendezvousPeerValidationPolicy::kNoValidation;
119 }
120
ShouldEnforceProcessRequirements()121 bool ShouldEnforceProcessRequirements() {
122 return GetPeerValidationPolicy() ==
123 MachPortRendezvousPeerValidationPolicy::kEnforce;
124 }
125
126 #endif
127
128 } // namespace
129
MachRendezvousPort(mach_port_t name,mach_msg_type_name_t disposition)130 MachRendezvousPort::MachRendezvousPort(mach_port_t name,
131 mach_msg_type_name_t disposition)
132 : name_(name), disposition_(disposition) {
133 DCHECK(disposition == MACH_MSG_TYPE_MOVE_RECEIVE ||
134 disposition == MACH_MSG_TYPE_MOVE_SEND ||
135 disposition == MACH_MSG_TYPE_MOVE_SEND_ONCE ||
136 disposition == MACH_MSG_TYPE_COPY_SEND ||
137 disposition == MACH_MSG_TYPE_MAKE_SEND ||
138 disposition == MACH_MSG_TYPE_MAKE_SEND_ONCE);
139 }
140
MachRendezvousPort(apple::ScopedMachSendRight send_right)141 MachRendezvousPort::MachRendezvousPort(apple::ScopedMachSendRight send_right)
142 : name_(send_right.release()), disposition_(MACH_MSG_TYPE_MOVE_SEND) {}
143
MachRendezvousPort(apple::ScopedMachReceiveRight receive_right)144 MachRendezvousPort::MachRendezvousPort(
145 apple::ScopedMachReceiveRight receive_right)
146 : name_(receive_right.release()),
147 disposition_(MACH_MSG_TYPE_MOVE_RECEIVE) {}
148
149 MachRendezvousPort::~MachRendezvousPort() = default;
150
Destroy()151 void MachRendezvousPort::Destroy() {
152 // Map the disposition to the type of right to deallocate.
153 mach_port_right_t right = 0;
154 switch (disposition_) {
155 case 0:
156 DCHECK(name_ == MACH_PORT_NULL);
157 return;
158 case MACH_MSG_TYPE_COPY_SEND:
159 case MACH_MSG_TYPE_MAKE_SEND:
160 case MACH_MSG_TYPE_MAKE_SEND_ONCE:
161 // Right is not owned, would be created by transit.
162 return;
163 case MACH_MSG_TYPE_MOVE_RECEIVE:
164 right = MACH_PORT_RIGHT_RECEIVE;
165 break;
166 case MACH_MSG_TYPE_MOVE_SEND:
167 right = MACH_PORT_RIGHT_SEND;
168 break;
169 case MACH_MSG_TYPE_MOVE_SEND_ONCE:
170 right = MACH_PORT_RIGHT_SEND_ONCE;
171 break;
172 default:
173 NOTREACHED() << "Leaking port name " << name_ << " with disposition "
174 << disposition_;
175 }
176 kern_return_t kr = mach_port_mod_refs(mach_task_self(), name_, right, -1);
177 MACH_DCHECK(kr == KERN_SUCCESS, kr)
178 << "Failed to drop ref on port name " << name_;
179
180 name_ = MACH_PORT_NULL;
181 disposition_ = 0;
182 }
183
184 MachPortRendezvousServerBase::MachPortRendezvousServerBase() = default;
185 MachPortRendezvousServerBase::~MachPortRendezvousServerBase() = default;
186
HandleRequest()187 void MachPortRendezvousServerBase::HandleRequest() {
188 // Receive the request message, using the kernel audit token to ascertain the
189 // PID of the sender.
190 struct : mach_msg_header_t {
191 mach_msg_audit_trailer_t trailer;
192 } request{};
193 request.msgh_size = sizeof(request);
194 request.msgh_local_port = server_port_.get();
195
196 const mach_msg_option_t options =
197 MACH_RCV_MSG | MACH_RCV_TIMEOUT |
198 MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) |
199 MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT);
200
201 mach_msg_return_t mr = mach_msg(&request, options, 0, sizeof(request),
202 server_port_.get(), 0, MACH_PORT_NULL);
203 if (mr != KERN_SUCCESS) {
204 MACH_LOG(ERROR, mr) << "mach_msg receive";
205 return;
206 }
207
208 // Destroy the message in case of an early return, which will release
209 // any rights from a bad message. In the case of a disallowed sender,
210 // the destruction of the reply port will break them out of a mach_msg.
211 ScopedMachMsgDestroy scoped_message(&request);
212
213 if ((request.msgh_id != kMachRendezvousMsgIdRequest &&
214 !IsValidAdditionalMessageId(request.msgh_id)) ||
215 request.msgh_size != sizeof(mach_msg_header_t)) {
216 // Do not reply to messages that are unexpected.
217 return;
218 }
219
220 std::optional<MachPortsForRendezvous> ports_to_send =
221 PortsForClient(request.trailer.msgh_audit);
222 if (!ports_to_send) {
223 return;
224 }
225
226 std::vector<uint8_t> additional_data =
227 AdditionalDataForReply(request.msgh_id);
228
229 std::unique_ptr<uint8_t[]> response = CreateReplyMessage(
230 request.msgh_remote_port, *ports_to_send, std::move(additional_data));
231 auto* header = reinterpret_cast<mach_msg_header_t*>(response.get());
232
233 mr = mach_msg(header, MACH_SEND_MSG, header->msgh_size, 0, MACH_PORT_NULL,
234 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
235
236 if (mr == KERN_SUCCESS) {
237 scoped_message.Disarm();
238 } else {
239 MACH_LOG(ERROR, mr) << "mach_msg send";
240 }
241 }
242
CreateReplyMessage(mach_port_t reply_port,const MachPortsForRendezvous & ports,std::vector<uint8_t> additional_data)243 std::unique_ptr<uint8_t[]> MachPortRendezvousServerBase::CreateReplyMessage(
244 mach_port_t reply_port,
245 const MachPortsForRendezvous& ports,
246 std::vector<uint8_t> additional_data) {
247 const size_t port_count = ports.size();
248 const size_t buffer_size =
249 CalculateResponseSize(port_count, additional_data.size());
250 auto buffer = std::make_unique<uint8_t[]>(buffer_size);
251 BufferIterator<uint8_t> iterator(buffer.get(), buffer_size);
252
253 auto* message = iterator.MutableObject<mach_msg_base_t>();
254 message->header.msgh_bits =
255 MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MOVE_SEND_ONCE) |
256 MACH_MSGH_BITS_COMPLEX;
257 message->header.msgh_size = checked_cast<mach_msg_size_t>(buffer_size);
258 message->header.msgh_remote_port = reply_port;
259 message->header.msgh_id = kMachRendezvousMsgIdResponse;
260 message->body.msgh_descriptor_count =
261 checked_cast<mach_msg_size_t>(port_count);
262
263 auto descriptors =
264 iterator.MutableSpan<mach_msg_port_descriptor_t>(port_count);
265 auto port_identifiers =
266 iterator.MutableSpan<MachPortsForRendezvous::key_type>(port_count);
267
268 auto port_it = ports.begin();
269 for (size_t i = 0; i < port_count; ++i, ++port_it) {
270 const MachRendezvousPort& port_for_rendezvous = port_it->second;
271 mach_msg_port_descriptor_t* descriptor = &descriptors[i];
272 descriptor->name = port_for_rendezvous.name();
273 descriptor->disposition = port_for_rendezvous.disposition();
274 descriptor->type = MACH_MSG_PORT_DESCRIPTOR;
275
276 port_identifiers[i] = port_it->first;
277 }
278
279 // The current iterator location may not have appropriate alignment to
280 // directly store a uint64_t. Write the size as bytes instead.
281 iterator.MutableSpan<uint8_t, 8>()->copy_from(
282 base::U64ToNativeEndian(additional_data.size()));
283 iterator.MutableSpan<uint8_t>(additional_data.size())
284 .copy_from(additional_data);
285
286 return buffer;
287 }
288
289 #if BUILDFLAG(IS_IOS)
GetMachSendRight()290 apple::ScopedMachSendRight MachPortRendezvousServerIOS::GetMachSendRight() {
291 return apple::RetainMachSendRight(send_right_.get());
292 }
293
MachPortRendezvousServerIOS(const MachPortsForRendezvous & ports)294 MachPortRendezvousServerIOS::MachPortRendezvousServerIOS(
295 const MachPortsForRendezvous& ports)
296 : ports_(ports) {
297 DCHECK_LT(ports_.size(), kMaximumRendezvousPorts);
298 bool res = apple::CreateMachPort(&server_port_, &send_right_);
299 CHECK(res) << "Failed to create mach server port";
300 dispatch_source_ = std::make_unique<apple::DispatchSourceMach>(
301 "MachPortRendezvousServer", server_port_.get(), ^{
302 HandleRequest();
303 });
304 dispatch_source_->Resume();
305 }
306
307 std::optional<MachPortsForRendezvous>
PortsForClient(audit_token_t audit_token)308 MachPortRendezvousServerIOS::PortsForClient(audit_token_t audit_token) {
309 // `audit_token` is ignored as a server handles a single client on iOS.
310 return ports_;
311 }
312
IsValidAdditionalMessageId(mach_msg_id_t) const313 bool MachPortRendezvousServerIOS::IsValidAdditionalMessageId(
314 mach_msg_id_t) const {
315 return false;
316 }
AdditionalDataForReply(mach_msg_id_t) const317 std::vector<uint8_t> MachPortRendezvousServerIOS::AdditionalDataForReply(
318 mach_msg_id_t) const {
319 return {};
320 }
321
322 MachPortRendezvousServerIOS::~MachPortRendezvousServerIOS() = default;
323
324 #endif
325
326 #if BUILDFLAG(IS_MAC)
327
328 // static
GetInstance()329 MachPortRendezvousServerMac* MachPortRendezvousServerMac::GetInstance() {
330 static auto* instance = new MachPortRendezvousServerMac();
331 return instance;
332 }
333
334 // static
AddFeatureStateToEnvironment(EnvironmentMap & environment)335 void MachPortRendezvousServerMac::AddFeatureStateToEnvironment(
336 EnvironmentMap& environment) {
337 environment.insert(
338 {kPeerValidationPolicyEnvironmentVariable,
339 NumberToString(static_cast<int>(GetPeerValidationPolicy()))});
340 }
341
342 MachPortRendezvousServerMac::ClientData&
ClientDataForPid(pid_t pid)343 MachPortRendezvousServerMac::ClientDataForPid(pid_t pid) {
344 lock_.AssertAcquired();
345
346 auto [it, inserted] = client_data_.emplace(pid, ClientData{});
347 if (inserted) {
348 apple::ScopedDispatchObject<dispatch_source_t> exit_watcher(
349 dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC,
350 static_cast<uintptr_t>(pid), DISPATCH_PROC_EXIT,
351 dispatch_source_->Queue()));
352 dispatch_source_set_event_handler(exit_watcher.get(), ^{
353 OnClientExited(pid);
354 });
355 dispatch_resume(exit_watcher.get());
356 it->second.exit_watcher = std::move(exit_watcher);
357 }
358
359 return it->second;
360 }
361
RegisterPortsForPid(pid_t pid,const MachPortsForRendezvous & ports)362 void MachPortRendezvousServerMac::RegisterPortsForPid(
363 pid_t pid,
364 const MachPortsForRendezvous& ports) {
365 lock_.AssertAcquired();
366 DCHECK_LT(ports.size(), kMaximumRendezvousPorts);
367 DCHECK(!ports.empty());
368
369 ClientData& client = ClientDataForPid(pid);
370 CHECK(client.ports.empty());
371 client.ports = ports;
372 }
373
SetProcessRequirementForPid(pid_t pid,mac::ProcessRequirement requirement)374 void MachPortRendezvousServerMac::SetProcessRequirementForPid(
375 pid_t pid,
376 mac::ProcessRequirement requirement) {
377 lock_.AssertAcquired();
378
379 ClientData& client = ClientDataForPid(pid);
380 CHECK(!client.requirement.has_value());
381 client.requirement = std::move(requirement);
382 }
383
384 MachPortRendezvousServerMac::ClientData::ClientData() = default;
385 MachPortRendezvousServerMac::ClientData::ClientData(ClientData&&) = default;
386
~ClientData()387 MachPortRendezvousServerMac::ClientData::~ClientData() {
388 for (auto& pair : ports) {
389 pair.second.Destroy();
390 }
391 }
392
MachPortRendezvousServerMac()393 MachPortRendezvousServerMac::MachPortRendezvousServerMac() {
394 std::string bootstrap_name =
395 StringPrintf(kBootstrapNameFormat, apple::BaseBundleID(), getpid());
396 kern_return_t kr = bootstrap_check_in(
397 bootstrap_port, bootstrap_name.c_str(),
398 apple::ScopedMachReceiveRight::Receiver(server_port_).get());
399 BOOTSTRAP_CHECK(kr == KERN_SUCCESS, kr)
400 << "bootstrap_check_in " << bootstrap_name;
401 dispatch_source_ = std::make_unique<apple::DispatchSourceMach>(
402 bootstrap_name.c_str(), server_port_.get(), ^{
403 HandleRequest();
404 });
405 dispatch_source_->Resume();
406 }
407
408 MachPortRendezvousServerMac::~MachPortRendezvousServerMac() = default;
409
410 std::optional<MachPortsForRendezvous>
PortsForClient(audit_token_t audit_token)411 MachPortRendezvousServerMac::PortsForClient(audit_token_t audit_token) {
412 pid_t pid = audit_token_to_pid(audit_token);
413 std::optional<mac::ProcessRequirement> requirement;
414 MachPortsForRendezvous ports_to_send;
415
416 {
417 AutoLock lock(lock_);
418 auto it = client_data_.find(pid);
419 if (it != client_data_.end()) {
420 ports_to_send = std::move(it->second.ports);
421 requirement = std::move(it->second.requirement);
422 client_data_.erase(it);
423 }
424 }
425
426 if (requirement.has_value() && ShouldValidateProcessRequirements()) {
427 bool client_is_valid = requirement->ValidateProcess(audit_token);
428 if (!client_is_valid && ShouldEnforceProcessRequirements()) {
429 return std::nullopt;
430 }
431 }
432
433 return ports_to_send;
434 }
435
OnClientExited(pid_t pid)436 void MachPortRendezvousServerMac::OnClientExited(pid_t pid) {
437 AutoLock lock(lock_);
438 client_data_.erase(pid);
439 }
440
IsValidAdditionalMessageId(mach_msg_id_t request) const441 bool MachPortRendezvousServerMac::IsValidAdditionalMessageId(
442 mach_msg_id_t request) const {
443 return request == kMachRendezvousMsgIdRequestWithInfoPlistData;
444 }
445
AdditionalDataForReply(mach_msg_id_t request) const446 std::vector<uint8_t> MachPortRendezvousServerMac::AdditionalDataForReply(
447 mach_msg_id_t request) const {
448 if (request == kMachRendezvousMsgIdRequestWithInfoPlistData) {
449 std::vector<uint8_t> info_plist_data =
450 mac::OuterBundleCachedInfoPlistData();
451 if (info_plist_data.size() > kMaxInfoPlistDataSize) {
452 LOG(WARNING) << "Info.plist data too large to send to client.";
453 return {};
454 }
455 return info_plist_data;
456 }
457 return {};
458 }
459
460 #endif // BUILDFLAG(IS_MAC)
461
462 MachPortRendezvousClient::MachPortRendezvousClient() = default;
463
464 MachPortRendezvousClient::~MachPortRendezvousClient() = default;
465
TakeSendRight(MachPortsForRendezvous::key_type key)466 apple::ScopedMachSendRight MachPortRendezvousClient::TakeSendRight(
467 MachPortsForRendezvous::key_type key) {
468 MachRendezvousPort port = PortForKey(key);
469 DCHECK(port.disposition() == 0 ||
470 port.disposition() == MACH_MSG_TYPE_PORT_SEND ||
471 port.disposition() == MACH_MSG_TYPE_PORT_SEND_ONCE);
472 return apple::ScopedMachSendRight(port.name());
473 }
474
TakeReceiveRight(MachPortsForRendezvous::key_type key)475 apple::ScopedMachReceiveRight MachPortRendezvousClient::TakeReceiveRight(
476 MachPortsForRendezvous::key_type key) {
477 MachRendezvousPort port = PortForKey(key);
478 DCHECK(port.disposition() == 0 ||
479 port.disposition() == MACH_MSG_TYPE_PORT_RECEIVE);
480 return apple::ScopedMachReceiveRight(port.name());
481 }
482
GetPortCount()483 size_t MachPortRendezvousClient::GetPortCount() {
484 AutoLock lock(lock_);
485 return ports_.size();
486 }
487
SendRequest(apple::ScopedMachSendRight server_port,mach_msg_id_t request_msg_id,size_t additional_response_data_size)488 bool MachPortRendezvousClient::SendRequest(
489 apple::ScopedMachSendRight server_port,
490 mach_msg_id_t request_msg_id,
491 size_t additional_response_data_size) {
492 const size_t buffer_size =
493 CalculateResponseSize(kMaximumRendezvousPorts,
494 additional_response_data_size) +
495 sizeof(mach_msg_audit_trailer_t);
496 auto buffer = std::make_unique<uint8_t[]>(buffer_size);
497 BufferIterator<uint8_t> iterator(buffer.get(), buffer_size);
498
499 // Perform a send and receive mach_msg.
500 auto* message = iterator.MutableObject<mach_msg_base_t>();
501 message->header.msgh_bits =
502 MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE);
503 // The |buffer_size| is used for receiving, since it includes space for the
504 // the entire reply and receiving trailer. But for the request being sent,
505 // the size is just an empty message.
506 message->header.msgh_size = sizeof(mach_msg_header_t);
507 message->header.msgh_remote_port = server_port.release();
508 message->header.msgh_local_port = mig_get_reply_port();
509 message->header.msgh_id = request_msg_id;
510
511 const mach_msg_option_t options =
512 MACH_SEND_MSG | MACH_RCV_MSG |
513 MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) |
514 MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT);
515 kern_return_t mr = mach_msg(
516 &message->header, options, message->header.msgh_size,
517 checked_cast<mach_msg_size_t>(buffer_size),
518 message->header.msgh_local_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
519 if (mr != KERN_SUCCESS) {
520 MACH_LOG(ERROR, mr) << "mach_msg";
521 return false;
522 }
523
524 if (message->header.msgh_id != kMachRendezvousMsgIdResponse) {
525 // Check if the response contains a rendezvous reply. If there were no
526 // ports for this client, then the send right would have been destroyed.
527 if (message->header.msgh_id == MACH_NOTIFY_SEND_ONCE) {
528 return true;
529 }
530 return false;
531 }
532
533 if (!ValidateMessage(message, iterator)) {
534 return false;
535 }
536
537 const size_t port_count = message->body.msgh_descriptor_count;
538
539 auto descriptors = iterator.Span<mach_msg_port_descriptor_t>(port_count);
540 auto port_identifiers =
541 iterator.Span<MachPortsForRendezvous::key_type>(port_count);
542
543 if (descriptors.size() != port_identifiers.size()) {
544 // Ensure that the descriptors and keys are of the same size.
545 return false;
546 }
547
548 for (size_t i = 0; i < port_count; ++i) {
549 MachRendezvousPort rendezvous_port(descriptors[i].name,
550 descriptors[i].disposition);
551 ports_.emplace(port_identifiers[i], rendezvous_port);
552 }
553
554 return true;
555 }
556
PortForKey(MachPortsForRendezvous::key_type key)557 MachRendezvousPort MachPortRendezvousClient::PortForKey(
558 MachPortsForRendezvous::key_type key) {
559 AutoLock lock(lock_);
560 auto it = ports_.find(key);
561 MachRendezvousPort port;
562 if (it != ports_.end()) {
563 port = it->second;
564 ports_.erase(it);
565 }
566 return port;
567 }
568
569 #if BUILDFLAG(IS_IOS)
570 // static
GetInstance()571 MachPortRendezvousClient* MachPortRendezvousClient::GetInstance() {
572 CHECK(g_client);
573 return g_client;
574 }
575
576 MachPortRendezvousClientIOS::MachPortRendezvousClientIOS() = default;
577 MachPortRendezvousClientIOS::~MachPortRendezvousClientIOS() = default;
578
Initialize(apple::ScopedMachSendRight server_port)579 bool MachPortRendezvousClientIOS::Initialize(
580 apple::ScopedMachSendRight server_port) {
581 CHECK(!g_client);
582 g_client = new MachPortRendezvousClientIOS();
583 if (!g_client->AcquirePorts(std::move(server_port))) {
584 delete g_client;
585 g_client = nullptr;
586 }
587 return true;
588 }
589
AcquirePorts(apple::ScopedMachSendRight server_port)590 bool MachPortRendezvousClientIOS::AcquirePorts(
591 apple::ScopedMachSendRight server_port) {
592 AutoLock lock(lock_);
593 return SendRequest(std::move(server_port), kMachRendezvousMsgIdRequest);
594 }
595
ValidateMessage(mach_msg_base_t *,BufferIterator<uint8_t>)596 bool MachPortRendezvousClientIOS::ValidateMessage(mach_msg_base_t*,
597 BufferIterator<uint8_t>) {
598 return true;
599 }
600
601 #endif // BUILDFLAG(IS_IOS)
602
603 #if BUILDFLAG(IS_MAC)
604
605 // static
GetInstance()606 MachPortRendezvousClient* MachPortRendezvousClient::GetInstance() {
607 static MachPortRendezvousClientMac* client = []() -> auto* {
608 auto* client = new MachPortRendezvousClientMac();
609 if (!client->AcquirePorts()) {
610 delete client;
611 client = nullptr;
612 }
613 return client;
614 }();
615 return client;
616 }
617
618 // static
GetBootstrapName()619 std::string MachPortRendezvousClientMac::GetBootstrapName() {
620 return StringPrintf(kBootstrapNameFormat, apple::BaseBundleID(), getppid());
621 }
622
MachPortRendezvousClientMac()623 MachPortRendezvousClientMac::MachPortRendezvousClientMac()
624 : server_requirement_(TakeServerCodeSigningRequirement()) {}
625
626 MachPortRendezvousClientMac::~MachPortRendezvousClientMac() = default;
627
AcquirePorts()628 bool MachPortRendezvousClientMac::AcquirePorts() {
629 AutoLock lock(lock_);
630
631 apple::ScopedMachSendRight server_port;
632 std::string bootstrap_name = GetBootstrapName();
633 kern_return_t kr = bootstrap_look_up(
634 bootstrap_port, const_cast<char*>(bootstrap_name.c_str()),
635 apple::ScopedMachSendRight::Receiver(server_port).get());
636 if (kr != KERN_SUCCESS) {
637 BOOTSTRAP_LOG(ERROR, kr) << "bootstrap_look_up " << bootstrap_name;
638 return false;
639 }
640
641 mach_msg_id_t message_id = kMachRendezvousMsgIdRequest;
642 size_t additional_data_size = 0;
643 if (NeedsInfoPlistData()) {
644 message_id = kMachRendezvousMsgIdRequestWithInfoPlistData;
645 additional_data_size = kMaxInfoPlistDataSize;
646 }
647 return SendRequest(std::move(server_port), message_id, additional_data_size);
648 }
649
ValidateMessage(mach_msg_base_t * message,BufferIterator<uint8_t> iterator)650 bool MachPortRendezvousClientMac::ValidateMessage(
651 mach_msg_base_t* message,
652 BufferIterator<uint8_t> iterator) {
653 if (!server_requirement_.has_value() ||
654 !ShouldValidateProcessRequirements()) {
655 return true;
656 }
657
658 span<const uint8_t> info_plist_data;
659 if (NeedsInfoPlistData()) {
660 // Skip over the Mach ports to find the Info.plist data to use for
661 // validation.
662 iterator.Seek(iterator.position() +
663 message->body.msgh_descriptor_count *
664 (sizeof(mach_msg_port_descriptor_t) +
665 sizeof(MachPortsForRendezvous::key_type)));
666 auto info_plist_length = iterator.CopyObject<uint64_t>();
667 CHECK(info_plist_length.has_value());
668 CHECK(*info_plist_length <= kMaxInfoPlistDataSize);
669 info_plist_data = iterator.Span<uint8_t>(info_plist_length.value());
670 }
671
672 iterator.Seek(message->header.msgh_size);
673 auto* trailer = iterator.Object<mach_msg_audit_trailer_t>();
674 bool valid = server_requirement_->ValidateProcess(trailer->msgh_audit,
675 info_plist_data);
676 if (ShouldEnforceProcessRequirements()) {
677 return valid;
678 }
679 return true;
680 }
681
NeedsInfoPlistData() const682 bool MachPortRendezvousClientMac::NeedsInfoPlistData() const {
683 return ShouldValidateProcessRequirements() &&
684 server_requirement_.has_value() &&
685 server_requirement_->ShouldCheckDynamicValidityOnly();
686 }
687
688 namespace {
689
690 struct RequirementWithLock {
691 Lock lock;
692 std::optional<mac::ProcessRequirement> requirement;
693 };
694
ServerCodeSigningRequirementWithLock()695 RequirementWithLock& ServerCodeSigningRequirementWithLock() {
696 static NoDestructor<RequirementWithLock> requirement_with_lock;
697 return *requirement_with_lock;
698 }
699
700 } // namespace
701
702 // static
SetServerProcessRequirement(mac::ProcessRequirement requirement)703 void MachPortRendezvousClientMac::SetServerProcessRequirement(
704 mac::ProcessRequirement requirement) {
705 AutoLock lock(ServerCodeSigningRequirementWithLock().lock);
706 ServerCodeSigningRequirementWithLock().requirement = requirement;
707 }
708
709 // static
710 std::optional<mac::ProcessRequirement>
TakeServerCodeSigningRequirement()711 MachPortRendezvousClientMac::TakeServerCodeSigningRequirement() {
712 AutoLock lock(ServerCodeSigningRequirementWithLock().lock);
713 return std::move(ServerCodeSigningRequirementWithLock().requirement);
714 }
715
716 // static
717 MachPortRendezvousPeerValidationPolicy
PeerValidationPolicyForTesting()718 MachPortRendezvousClientMac::PeerValidationPolicyForTesting() {
719 return GetPeerValidationPolicy();
720 }
721
722 namespace {
723
724 // Helper function to avoid the compiler detecting that comparisons involving
725 // `default_state` are compile-time constants and declaring code as unreachable.
IsEnabledByDefault(const Feature & feature)726 bool IsEnabledByDefault(const Feature& feature) {
727 return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
728 }
729
GetDefaultPeerValidationPolicy()730 MachPortRendezvousPeerValidationPolicy GetDefaultPeerValidationPolicy() {
731 CHECK(!FeatureList::GetInstance());
732 if (IsEnabledByDefault(kMachPortRendezvousEnforcePeerRequirements)) {
733 return MachPortRendezvousPeerValidationPolicy::kEnforce;
734 }
735 if (IsEnabledByDefault(kMachPortRendezvousValidatePeerRequirements)) {
736 return MachPortRendezvousPeerValidationPolicy::kValidateOnly;
737 }
738 return MachPortRendezvousPeerValidationPolicy::kNoValidation;
739 }
740
741 MachPortRendezvousPeerValidationPolicy
GetPeerValidationPolicyFromFeatureList()742 GetPeerValidationPolicyFromFeatureList() {
743 if (base::FeatureList::IsEnabled(
744 kMachPortRendezvousEnforcePeerRequirements)) {
745 return MachPortRendezvousPeerValidationPolicy::kEnforce;
746 }
747 if (base::FeatureList::IsEnabled(
748 kMachPortRendezvousValidatePeerRequirements)) {
749 return MachPortRendezvousPeerValidationPolicy::kValidateOnly;
750 }
751 return MachPortRendezvousPeerValidationPolicy::kNoValidation;
752 }
753
754 MachPortRendezvousPeerValidationPolicy
GetPeerValidationPolicyFromEnvironment()755 GetPeerValidationPolicyFromEnvironment() {
756 // The environment variable is set at launch and does not change. Compute the
757 // policy once and cache it.
758 static MachPortRendezvousPeerValidationPolicy policy = [] {
759 std::unique_ptr<Environment> environment = Environment::Create();
760 int policy_int = INT_MAX;
761 std::string policy_str;
762 if (environment->GetVar(kPeerValidationPolicyEnvironmentVariable,
763 &policy_str)) {
764 if (!StringToInt(policy_str, &policy_int)) {
765 // StringToInt modifies the output value even on failure.
766 policy_int = INT_MAX;
767 }
768 }
769
770 switch (policy_int) {
771 case to_underlying(MachPortRendezvousPeerValidationPolicy::kNoValidation):
772 return MachPortRendezvousPeerValidationPolicy::kNoValidation;
773 case to_underlying(MachPortRendezvousPeerValidationPolicy::kValidateOnly):
774 return MachPortRendezvousPeerValidationPolicy::kValidateOnly;
775 case to_underlying(MachPortRendezvousPeerValidationPolicy::kEnforce):
776 return MachPortRendezvousPeerValidationPolicy::kEnforce;
777 default:
778 // An invalid policy or no policy was passed via the environment. Fall
779 // back to the default values of the feature flags.
780 return GetDefaultPeerValidationPolicy();
781 }
782 }();
783 return policy;
784 }
785
GetPeerValidationPolicy()786 MachPortRendezvousPeerValidationPolicy GetPeerValidationPolicy() {
787 if (base::FeatureList::GetInstance()) {
788 return GetPeerValidationPolicyFromFeatureList();
789 }
790
791 // In child processes, MachPortRendezvousClient is used during feature list
792 // initialization so the validation policy is passed via an environment
793 // variable.
794 return GetPeerValidationPolicyFromEnvironment();
795 }
796
797 } // namespace
798
799 #endif
800
801 } // namespace base
802