• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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