2 * Copyright (c) 2019-2020 Apple Inc. All rights reserved.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * https://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #import "mDNSEmbeddedAPI.h"
18 #import "mdns_trust.h"
19 #import "mdns_trust_checks.h"
20 #import "mdns_helpers.h"
22 #if TARGET_OS_IOS && (TARGET_OS_EMBEDDED || TARGET_OS_SIMULATOR || !TARGET_OS_IOSMAC)
23 #define USE_IOS_LIBS 1
25 #define USE_IOS_LIBS 0
29 #import <CoreServices/LSBundleProxy.h>
30 #import <CoreServices/LSDiskUsagePriv.h>
31 #import <CoreServices/LSApplicationRecordPriv.h>
33 #import <CoreServices/CoreServicesPriv.h>
34 #import <Security/CodeSigning.h>
37 #import <bsm/libbsm.h>
38 #import <CoreAnalytics/CoreAnalytics.h>
39 #import <CoreUtils/DebugServices.h>
40 #import <NetworkExtension/NetworkExtensionPrivate.h>
41 #import <nw/path_evaluation.h>
42 #import <os/feature_private.h>
43 #import <xpc/private.h>
46 #define BROWSE_ALL_SERVICES_PRIVATE_ENTITLEMENT "com.apple.developer.networking.multicast"
47 #define ON_DEMAND_ENTITLEMENT "com.apple.developer.on-demand-install-capable"
48 #define TRUST_BYPASS_GAME_CENTER_SERVICE "_gamecenter._tcp"
50 #define kLocalDomain ((const domainname *) "\x5" "local" )
51 #define kReverseIPv6Domain ((const domainname *) "\x3" "ip6" "\x4" "arpa")
52 #define kReverseIPv4Domain ((const domainname *) "\x7" "in-addr" "\x4" "arpa")
54 MDNS_LOG_CATEGORY_DEFINE(trust, "trust");
56 //======================================================================================================================
57 // MARK: - mdns_trust_check struct
59 typedef struct mdns_trust_check_s {
60 trust_request_t request;
61 const char * query_name;
62 const char * service_name;
63 audit_token_t * audit_token;
64 trust_policy_state_t policy_state;
65 uint16_t request_type;
66 mdns_trust_flags_t flags;
67 bool entitlement_allowed;
69 } *mdns_trust_check_t;
71 //======================================================================================================================
72 // MARK: - Private helpers
75 _mdns_trust_post_analytic(const mdns_trust_check_t me, const LSBundleProxy * bundle_proxy,
76 const NSArray * _Nullable services)
78 AnalyticsSendEvent(@"com.apple.network.localnetwork.check",
79 @{@"bundleID" : bundle_proxy.bundleIdentifier,
80 @"entitlement" : (me->flags & mdns_trust_flags_entitlement) ? @YES : @NO,
81 @"allowed" : (me->entitlement_allowed) ? @YES : @NO,
82 @"services" : services ?: @[],
83 @"service" : [NSString stringWithUTF8String:me->service_name]});
87 _mdns_trust_checks_is_same_domain_name(const char *domain1, const char *domain2)
90 bool good = (MakeDomainNameFromDNSNameString(&d1, domain1) != NULL);
91 require_quiet(good, exit);
93 good = (MakeDomainNameFromDNSNameString(&d2, domain2) != NULL);
94 require_quiet(good, exit);
96 good = (SameDomainName(&d1, &d2) != mDNSfalse);
102 static LSBundleProxy*
103 _mdns_trust_checks_bundle_proxy_for_app(const audit_token_t *audit_token)
106 LSBundleProxy *bundle_proxy = nil;
108 if (__builtin_available(macOS 10.15, ios 13.0, watchos 6.0, tvos 13.0, *)) {
109 bundle_proxy = [LSBundleProxy bundleProxyWithAuditToken:*audit_token error:&error];
116 _mdns_trust_checks_is_local_address(const char * _Nonnull address)
118 nw_endpoint_t endpoint = nw_endpoint_create_host(address, "0");
119 nw_path_evaluator_t evaluator = nw_path_create_evaluator_for_endpoint(endpoint, nil);
120 nw_path_t path = nw_path_evaluator_copy_path(evaluator);
121 bool local_network = nw_path_is_direct(path);
122 return (local_network != false);
126 _mdns_trust_checks_reverse_ipv6_to_ipv6_string(const domainname *name, char * _Nonnull addr_buffer,
127 socklen_t addr_buffer_len)
134 // If the name is of the form "x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.ip6.arpa.", where
135 // each x is a hex digit, then the sequence of 32 hex digit labels represents the nibbles of an IPv6 address in
136 // reverse order. See <https://tools.ietf.org/html/rfc3596#section-2.5>.
139 for (i = 0; i < 32; i++)
141 unsigned int c, nibble;
142 const int j = 15 - (i / 2);
143 require_quiet(*ptr++ == 1, exit); // If this label's length is not 1, then fail.
144 c = *ptr++; // Get label byte.
145 if ( (c >= '0') && (c <= '9')) {
146 nibble = c - '0'; // If it's a hex digit, get its numeric value.
147 } else if ((c >= 'a') && (c <= 'f')) {
148 nibble = (c - 'a') + 10;
149 } else if ((c >= 'A') && (c <= 'F')) {
150 nibble = (c - 'A') + 10;
155 ipv6[j] = (uint8_t)nibble;
157 ipv6[j] |= (uint8_t)(nibble << 4);
161 // The rest of the name needs to be "ip6.arpa.". If it isn't, fail.
162 require_quiet(SameDomainName((const domainname *)ptr, kReverseIPv6Domain), exit);
163 (void)inet_ntop(AF_INET6, &ipv6, addr_buffer, addr_buffer_len);
170 _mdns_trust_checks_reverse_ipv4_to_ipv4_string(const domainname *name, char * _Nonnull addr_buffer,
171 socklen_t addr_buffer_len)
178 // If the name is of the form "x.x.x.x.in-addr.arpa.", where each x is a uint8
183 int c, v, label_length;
186 for (int i = 1; i <= 4; i++)
191 label_length = *ptr++;
192 require_quiet((label_length >= 1) && (label_length <= 3), exit);
193 for (int j = 0; j < label_length; j++) {
194 c = *ptr++; // Get label byte.
195 require_quiet((c >= '0') && (c <= '9'), exit);
196 v = (*dst * 10) + (c - '0');
197 require_quiet(v <= 255, exit);
201 require_quiet(segments <= 4, exit);
205 require_quiet(segments == i, exit);
207 require_quiet(segments == 4, exit);
208 ipv4_addr = *(uint32_t*)&ipv4; // Already in network byte order
210 // The rest of the name needs to be "ip-addr.arpa.". If it isn't, fail.
211 require_quiet(SameDomainName((const domainname *)ptr, kReverseIPv4Domain), exit);
212 (void)inet_ntop(AF_INET, &ipv4_addr, addr_buffer, addr_buffer_len);
219 _mdns_trust_checks_should_check_query_type(const char *qname, uint16_t qtype)
222 bool good = (MakeDomainNameFromDNSNameString(&domain, qname) != NULL);
223 require_quiet(good, exit);
225 bool qtype_valid = (qtype == kDNSType_PTR || qtype == kDNSQType_ANY);
226 const domainname *d = &domain;
228 const domainname *d1, *d2; // Top-level domain, second-level domain
232 d = (const domainname*)(d->c + 1 + d->c[0]);
235 if (d1 && SameDomainName(d1, kLocalDomain)) {
237 } else if (qtype_valid && IsLocalDomain(&domain)) {
239 } else if (d2 && qtype_valid && SameDomainName(d2, kReverseIPv4Domain)) {
240 char str_buffer[INET_ADDRSTRLEN];
241 if (_mdns_trust_checks_reverse_ipv4_to_ipv4_string(&domain, str_buffer, sizeof(str_buffer))) {
242 if (_mdns_trust_checks_is_local_address(str_buffer)) {
246 } else if (d2 && qtype_valid && SameDomainName(d2, kReverseIPv6Domain)) {
247 char str_buffer[INET6_ADDRSTRLEN];
248 if (_mdns_trust_checks_reverse_ipv6_to_ipv6_string(&domain, str_buffer, sizeof(str_buffer))) {
249 if (_mdns_trust_checks_is_local_address(str_buffer)) {
258 //======================================================================================================================
259 // MARK: - mdns_trust_checks System checks
264 _mdns_trust_checks_system_trusted_app(const LSBundleProxy *bundle_proxy)
266 NSString * bundleid = bundle_proxy.bundleIdentifier;
267 return ([bundleid hasPrefix: @"com.apple."] != NO);
270 #else // !USE_IOS_LIBS
273 _mdns_trust_checks_codesigned_by_apple(const LSBundleProxy *bundle_proxy)
276 SecRequirementRef requirement = NULL;
277 SecStaticCodeRef code = NULL;
279 err = SecRequirementCreateWithString(CFSTR("anchor apple"), kSecCSDefaultFlags, &requirement);
280 require_noerr_quiet(err, exit);
282 err = SecStaticCodeCreateWithPath((__bridge CFURLRef)bundle_proxy.bundleURL, kSecCSDefaultFlags, &code);
283 require_noerr_quiet(err, exit);
285 err = SecStaticCodeCheckValidity(code, kSecCSDefaultFlags, requirement);
292 CFRelease(requirement);
295 return (err == noErr);
298 #endif // USE_IOS_LIBS
301 _mdns_trust_checks_app_is_apple_internal(const LSBundleProxy *bundle_proxy)
304 NSString * bundleType = bundle_proxy.bundleType;
305 if ([bundleType isEqualToString:LSInternalApplicationType] ||
306 [bundleType isEqualToString:LSSystemApplicationType]) {
308 } else if ([bundleType isEqualToString:LSPlugInKitType] &&
309 _mdns_trust_checks_system_trusted_app(bundle_proxy)) {
314 #else // !USE_IOS_LIBS
315 return _mdns_trust_checks_codesigned_by_apple(bundle_proxy);
316 #endif // USE_IOS_LIBS
320 _mdns_trust_checks_app_sdk_is_minimum_version(const LSBundleProxy *bundle_proxy)
323 #define MIN_SDK_VERSION "10.16"
324 #elif TARGET_OS_WATCH
325 #define MIN_SDK_VERSION "7.0"
327 #define MIN_SDK_VERSION "14.0"
329 NSString * min_vers = [NSString stringWithUTF8String:MIN_SDK_VERSION];
330 NSComparisonResult compare_result = [[bundle_proxy sdkVersion] compare:min_vers options:NSNumericSearch];
331 bool result = ((compare_result == NSOrderedSame) || (compare_result == NSOrderedDescending));
333 return (result != false);
336 //======================================================================================================================
337 // MARK: - mdns_trust_checks Policy checks
339 static trust_policy_state_t
340 _mdns_trust_checks_app_is_local_network_allowed(const LSBundleProxy *bundle_proxy)
342 bool denyMulticast = false;
343 bool userPrompted = false;
344 if ([NEPathController class]) {
345 NSArray<NEPathRule *> *aggregateRules = [NEPathController copyAggregatePathRules];
346 for (NEPathRule *pathRule in aggregateRules) {
347 if ([pathRule.matchSigningIdentifier isEqualToString:bundle_proxy.bundleIdentifier]) {
348 #ifdef NE_PATH_RULE_SUPPORTS_DENY_MULTICAST
349 denyMulticast = (pathRule.denyMulticast != NO);
350 #endif // NE_PATH_RULE_SUPPORTS_DENY_MULTICAST
351 #ifdef NE_PATH_RULE_SUPPORTS_MULTICAST_PREFERENCE_SET
352 userPrompted = (pathRule.multicastPreferenceSet != NO);
355 #endif // NE_PATH_RULE_SUPPORTS_MULTICAST_PREFERENCE_SET
361 trust_policy_state_t state = denyMulticast ?
362 (userPrompted ? trust_policy_state_denied : trust_policy_state_pending) :
363 trust_policy_state_granted;
368 _mdns_trust_checks_policy_check(const mdns_trust_check_t me, const LSBundleProxy *bundle_proxy)
370 me->policy_state = _mdns_trust_checks_app_is_local_network_allowed(bundle_proxy);
371 if (me->policy_state != trust_policy_state_granted) {
372 os_log_info(_mdns_trust_log(), "Local network access to %{public}s(%{private}s) policy \'%{public}s\' for (%{public}@).",
373 _mdns_trust_checks_request_to_string(me->request), me->service_name ?: me->query_name,
374 _mdns_trust_checks_policy_to_string(me->policy_state), bundle_proxy.localizedShortName);
378 //======================================================================================================================
379 // MARK: - mdns_trust_checks Entitlement checks
382 _mdns_trust_checks_check_on_demand_entitlement(const mdns_trust_check_t me, const LSBundleProxy *bundle_proxy)
384 // The presense of this entitlement disallows these functions
385 xpc_object_t entitlement = xpc_copy_entitlement_for_token(ON_DEMAND_ENTITLEMENT, me->audit_token);
387 os_log_info(_mdns_trust_log(), "Entitlement \'%{public}s\' disallows request for (%{public}@)", ON_DEMAND_ENTITLEMENT, bundle_proxy.localizedShortName);
395 _mdns_trust_checks_check_browse_all_entitlement(const mdns_trust_check_t me)
397 xpc_object_t entitlement = xpc_copy_entitlement_for_token(BROWSE_ALL_SERVICES_PRIVATE_ENTITLEMENT, me->audit_token);
399 bool allowed = false;
400 if (xpc_get_type(entitlement) == XPC_TYPE_BOOL) {
401 allowed = (xpc_bool_get_value(entitlement) != false);
410 _mdns_trust_checks_app_info_has_bonjour_service(const LSBundleProxy *bundle_proxy, const NSArray *services, const char * const service)
412 for (NSString * next in services) {
413 if (_mdns_trust_checks_is_same_domain_name(service, next.UTF8String)) {
418 if (_mdns_trust_checks_is_same_domain_name(service, TRUST_BYPASS_GAME_CENTER_SERVICE)) {
422 os_log_error(_mdns_trust_log(), "App Info.plist(NSBonjourServices) does not allow \'%{public}s\' for (%{public}@)", service, bundle_proxy.localizedShortName);
427 _mdns_trust_checks_entitlement_check(const mdns_trust_check_t me, const LSBundleProxy *bundle_proxy)
429 me->entitlement_allowed = _mdns_trust_checks_check_on_demand_entitlement(me, bundle_proxy);
430 require_quiet(me->entitlement_allowed, exit);
432 if (os_feature_enabled(mDNSResponder, bonjour_privacy) &&
433 _mdns_trust_checks_app_sdk_is_minimum_version(bundle_proxy)) {
434 require_quiet(me->service_name, exit); // No service_name is allowed to skip entitlement checks
435 NSArray *services = nil;
436 me->entitlement_allowed = _mdns_trust_checks_check_browse_all_entitlement(me);
437 if (me->entitlement_allowed) {
438 me->flags |= mdns_trust_flags_entitlement;
440 // Only check if previous is false
441 services = [bundle_proxy objectForInfoDictionaryKey:@"NSBonjourServices" ofClass:[NSArray class]];
442 me->entitlement_allowed = _mdns_trust_checks_app_info_has_bonjour_service(bundle_proxy, services, me->service_name);
444 _mdns_trust_post_analytic(me, bundle_proxy, services);
451 //======================================================================================================================
452 // MARK: - mdns_trust_checks Internal checks
455 _mdns_trust_checks_should_check_trust(const mdns_trust_check_t me)
457 bool should_check = true;
459 if (me->request == trust_request_query) {
460 should_check = (me->force_multicast || _mdns_trust_checks_should_check_query_type(me->query_name, me->request_type));
461 } else if (me->request == trust_request_reg_service) {
462 should_check = (_mdns_trust_checks_is_same_domain_name(me->service_name, TRUST_BYPASS_GAME_CENTER_SERVICE) == false);
468 static mdns_trust_status_t
469 _mdns_trust_checks_get_status(const mdns_trust_check_t me)
471 mdns_trust_status_t status;
473 if (me->entitlement_allowed) {
474 if (me->policy_state == trust_policy_state_granted) {
475 status = mdns_trust_status_granted;
476 } else if (me->policy_state == trust_policy_state_pending) {
477 status = mdns_trust_status_pending;
479 status = mdns_trust_status_denied;
482 status = mdns_trust_status_no_entitlement;
488 _mdns_trust_checks_perform_all_trust_checks(const mdns_trust_check_t me)
490 LSBundleProxy *bundle_proxy = _mdns_trust_checks_bundle_proxy_for_app(me->audit_token);
491 require_quiet(bundle_proxy, exit);
493 bool check_more = (_mdns_trust_checks_app_is_apple_internal(bundle_proxy) == false);
494 require_quiet(check_more, exit); // Internal always allowed
496 _mdns_trust_checks_entitlement_check(me, bundle_proxy);
497 check_more = (me->entitlement_allowed != false);
498 require_quiet(check_more, exit); // Continue if allowed by entitlement
500 _mdns_trust_checks_policy_check(me, bundle_proxy); // Can interact with user so check policy last
506 //======================================================================================================================
507 // MARK: - mdns_trust_checks Public functions
509 static _Atomic bool g_is_initialized = false;
512 mdns_trust_checks_init(void)
514 static dispatch_once_t s_once;
515 dispatch_once(&s_once, ^{
516 os_log_info(_mdns_trust_log(), "Initializing Launch Services -- PENDING");
517 dispatch_queue_t queue = dispatch_queue_create("com.apple.dnssd.trust.init", DISPATCH_QUEUE_SERIAL_WITH_AUTORELEASE_POOL);
518 dispatch_async(queue, ^{
519 os_log_info(_mdns_trust_log(), "Initializing Launch Services -- START");
520 if (__builtin_available(macOS 10.16, ios 14.0, watchos 7.0, tvos 14.0, *)) {
521 // Issue a query to LaunchServices to ensure we only proceed after their database is built during migration
522 (void)[[LSApplicationRecord alloc] initWithBundleIdentifier:@"com.apple.dummy.nonexistent" allowPlaceholder:NO error:nil];
524 atomic_store(&g_is_initialized, true);
525 os_log_info(_mdns_trust_log(), "Initializing Launch Services -- COMPLETE");
531 mdns_trust_checks_local_network_access_policy_update(audit_token_t *audit_token, dispatch_queue_t queue,
532 const char * _Nullable query, mdns_trust_flags_t flags, _mdns_trust_checks_update_handler_t handler)
534 #ifndef NE_HAS_SHOW_LOCAL_NETWORK_ALERT_FOR_APP_EXTENDED
539 LSBundleProxy *bundle_proxy = _mdns_trust_checks_bundle_proxy_for_app(audit_token);
540 __block trust_policy_state_t state = (bundle_proxy != nil) ? _mdns_trust_checks_app_is_local_network_allowed(bundle_proxy) : trust_policy_state_granted;
541 #ifdef NE_HAS_SHOW_LOCAL_NETWORK_ALERT_FOR_APP
542 if ([NEConfigurationManager class] && state == trust_policy_state_pending) {
543 os_log_info(_mdns_trust_log(), "Local network alert for (%{public}@) query(%{public}s).", bundle_proxy.localizedShortName, query ?: "local");
544 NEConfigurationManager *sharedManager = [NEConfigurationManager sharedManagerForAllUsers];
545 [sharedManager showLocalNetworkAlertForApp:bundle_proxy.bundleIdentifier
546 withCompletionQueue:queue
547 #ifdef NE_HAS_SHOW_LOCAL_NETWORK_ALERT_FOR_APP_EXTENDED
548 query:query ? [NSString stringWithUTF8String:query] : @"local"
549 hasEntitlement:(flags & mdns_trust_flags_entitlement) ? YES : NO
551 handler:^(BOOL allowed) {
552 state = allowed ? trust_policy_state_granted : trust_policy_state_denied;
553 os_log_info(_mdns_trust_log(), "Local network alert policy status \'%{public}s\' for (%{public}@).", _mdns_trust_checks_policy_to_string(state), bundle_proxy.localizedShortName);
557 #endif // HAVE_SHOW_LOCAL_NETWORK_ALERT_FOR_APP
559 if (bundle_proxy == nil) {
560 os_log_info(_mdns_trust_log(), "No bundle found for local network access policy update for PID(%d).", audit_token_to_pid(*audit_token));
562 dispatch_async(queue, ^{
570 mdns_trust_checks_check(audit_token_t *audit_token, trust_request_t request, const char * _Nullable query_name,
571 const char * _Nullable service_name, uint16_t qtype, bool force_multicast, mdns_trust_flags_t * _Nullable flags)
573 struct mdns_trust_check_s ref;
574 ref.audit_token = audit_token;
575 ref.request = request;
576 ref.query_name = query_name;
577 ref.service_name = service_name;
578 ref.request_type = qtype;
579 ref.flags = mdns_trust_flags_none;
580 ref.entitlement_allowed = true; // Default allow
581 ref.force_multicast = force_multicast;
582 ref.policy_state = trust_policy_state_granted; // Default granted
584 require_quiet(atomic_load(&g_is_initialized), exit);
587 bool check_more = (_mdns_trust_checks_should_check_trust(&ref) != false);
588 require_quiet(check_more, exit);
590 _mdns_trust_checks_perform_all_trust_checks(&ref);
597 return _mdns_trust_checks_get_status(&ref);