2 * Copyright (c) 2016 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
27 #include <sys/sysctl.h>
28 #include <sys/types.h>
29 #include <dispatch/dispatch.h>
31 #include <xpc/private.h>
32 #include <System/sys/csr.h>
33 #include <System/machine/cpu_capabilities.h>
35 #include <os/assumes.h>
36 #include <os/variant_private.h>
38 enum variant_property
{
54 status2bool(enum check_status status
) {
61 os_crash("os_variant had unexpected status");
65 #define VAR_FILE_LEGACY "/var/db/disableAppleInternal"
68 #define VAR_FILE_OVERRIDE "/var/db/os_variant_override"
70 #define VAR_FILE_OVERRIDE "/usr/share/misc/os_variant_override"
73 #if !TARGET_OS_SIMULATOR
74 #define INTERNAL_CONTENT_PATH "/System/Library/CoreServices/AppleInternalVariant.plist"
76 #define INTERNAL_CONTENT_PATH "/AppleInternal"
79 #define SYSTEM_VERSION_PLIST_PATH "/System/Library/CoreServices/SystemVersion.plist"
80 #define SYSTEM_VERSION_PLIST_KEY "ReleaseType"
83 #define INTERNAL_SETTINGS_PATH "/AppleInternal/Library/PreferenceBundles/Internal Settings.bundle"
85 #define INTERNAL_DIAGS_PROFILE_PATH "/var/db/ConfigurationProfiles/Settings/com.apple.InternalDiagnostics.plist"
88 #if !TARGET_OS_SIMULATOR
89 #define CACHE_SYSCTL_NAME "kern.osvariant_status"
91 static void _restore_cached_check_status(uint64_t status
);
92 static uint64_t _get_cached_check_status(void);
94 static char * _read_file(const char *path
, size_t *size_out
)
98 int fd
= open(path
, O_RDONLY
);
99 if (fd
== -1) return NULL
;
102 int rc
= fstat(fd
, &sb
);
103 if (rc
!= 0 || sb
.st_size
== 0) {
107 size_t size_limit
= (size_out
&& *size_out
!= 0) ? *size_out
: 1024;
108 size_t size
= (size_t)sb
.st_size
;
109 if (size_out
) *size_out
= (size_t)sb
.st_size
;
110 if (size
> size_limit
) {
114 buf
= malloc(size
+ 1);
119 ssize_t bytes_read
= read(fd
, buf
, size
);
123 if (bytes_read
== (ssize_t
)size
) {
134 static xpc_object_t
read_plist(const char *path
)
136 size_t size
= 16 * 1024;
137 uint8_t *buf
= (uint8_t*)_read_file(path
, &size
);
138 if (!buf
) return NULL
;
140 xpc_object_t plist
= xpc_create_from_plist(buf
, size
);
141 if (plist
&& xpc_get_type(plist
) != XPC_TYPE_DICTIONARY
) {
152 #if !TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
153 static enum check_status internal_content
= S_UNKNOWN
;
155 #if !TARGET_OS_SIMULATOR
156 static enum check_status can_has_debugger
= S_UNKNOWN
;
158 static enum check_status internal_release_type
= S_UNKNOWN
;
159 #else // TARGET_OS_IPHONE
160 static enum check_status internal_diags_profile
= S_UNKNOWN
;
161 #endif // TARGET_OS_IPHONE
162 #endif // !TARGET_OS_SIMULATOR
164 static bool disabled_status
[VP_MAX
] = {};
166 static void _parse_disabled_status(char *test_string
)
168 #if TARGET_OS_SIMULATOR
169 #pragma unused(test_string)
170 #else // TARGET_OS_SIMULATOR
171 char *override_str
= NULL
;
173 bzero(disabled_status
, sizeof(disabled_status
));
175 if (test_string
!= NULL
) {
176 /* used for unit tests */
177 override_str
= strdup(test_string
);
179 if (access(VAR_FILE_LEGACY
, F_OK
) == 0) {
181 } else if (access(VAR_FILE_OVERRIDE
, F_OK
) != 0) {
185 override_str
= _read_file(VAR_FILE_OVERRIDE
, NULL
);
188 if (override_str
== NULL
) goto disable_all
;
190 char *token
, *string
= override_str
;
191 while ((token
= strsep(&string
, ",\n")) != NULL
) {
192 if (strcmp(token
, "content") == 0) {
193 disabled_status
[VP_CONTENT
] = true;
194 } else if (strcmp(token
, "diagnostics") == 0) {
195 disabled_status
[VP_DIAGNOSTICS
] = true;
196 } else if (strcmp(token
, "ui") == 0) {
197 disabled_status
[VP_UI
] = true;
198 } else if (strcmp(token
, "security") == 0) {
199 disabled_status
[VP_SECURITY
] = true;
207 for (int i
= 0; i
< VP_MAX
; i
++) {
208 disabled_status
[i
] = true;
210 #endif //!TARGET_OS_SIMULATOR
213 #if !TARGET_OS_SIMULATOR
214 static bool _load_cached_status(void)
217 size_t status_size
= sizeof(status
);
218 int ret
= sysctlbyname(CACHE_SYSCTL_NAME
, &status
, &status_size
, NULL
, 0);
224 _restore_cached_check_status(status
);
228 if (status
== 0 && getpid() == 1) {
230 * Looks like we are in launchd; try to set the status.
232 * We don't actually care if this works because we'll have warmed our state.
234 status
= _get_cached_check_status();
235 sysctlbyname(CACHE_SYSCTL_NAME
, NULL
, 0, &status
, status_size
);
243 static void _initialize_status(void * __unused ctx
)
245 #if !TARGET_OS_SIMULATOR
246 if (!_load_cached_status()) {
247 _parse_disabled_status(NULL
);
250 _parse_disabled_status(NULL
);
254 static bool _check_disabled(enum variant_property variant_property
)
256 static dispatch_once_t disabled_status_pred
;
257 dispatch_once_f(&disabled_status_pred
, NULL
, _initialize_status
);
259 return disabled_status
[variant_property
];
262 #if !TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
263 static bool _check_internal_content(void)
265 if (internal_content
== S_UNKNOWN
) {
266 #if !TARGET_OS_SIMULATOR
267 const char * path
= INTERNAL_CONTENT_PATH
;
269 char *simulator_root
= getenv("IPHONE_SIMULATOR_ROOT");
270 char *to_free
= NULL
, *path
= NULL
;
271 if (simulator_root
) {
272 asprintf(&path
, "%s/%s", simulator_root
, INTERNAL_CONTENT_PATH
);
279 internal_content
= (access(path
, F_OK
) == 0) ? S_YES
: S_NO
;
280 #if TARGET_OS_SIMULATOR
284 return status2bool(internal_content
);
286 #endif // !TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
291 * This set of criteria was taken from copyInternalBuild in MobileGestalt.c
293 static bool _check_internal_release_type(void)
295 #if TARGET_OS_SIMULATOR
296 return _check_internal_content();
297 #else // TARGET_OS_SIMULATOR
298 if (internal_release_type
== S_UNKNOWN
) {
299 xpc_object_t system_version_plist
= read_plist(SYSTEM_VERSION_PLIST_PATH
);
300 if (system_version_plist
) {
301 const char *release_type
=
302 xpc_dictionary_get_string(system_version_plist
,
303 SYSTEM_VERSION_PLIST_KEY
);
305 if (release_type
== NULL
) {
307 * Confusingly, customer images are just completely missing this key.
309 internal_release_type
= S_NO
;
310 } else if (strcmp(release_type
, "Internal") == 0 ||
311 strcmp(release_type
, "Lite Internal") == 0 ||
312 strcmp(release_type
, "NonUI") == 0) {
313 internal_release_type
= S_YES
;
315 internal_release_type
= S_NO
;
318 xpc_release(system_version_plist
);
320 internal_release_type
= (access(INTERNAL_SETTINGS_PATH
, F_OK
) == 0) ? S_YES
: S_NO
;
324 return status2bool(internal_release_type
);
325 #endif // TARGET_OS_SIMULATOR
330 static bool _check_internal_diags_profile() {
331 static enum check_status internal_diags_profile
= S_UNKNOWN
;
333 if (internal_diags_profile
== S_UNKNOWN
) {
334 xpc_object_t profile_settings
= read_plist(INTERNAL_DIAGS_PROFILE_PATH
);
335 if (profile_settings
) {
336 internal_diags_profile
= xpc_dictionary_get_bool(profile_settings
, "AppleInternal") ? S_YES
: S_NO
;
337 xpc_release(profile_settings
);
339 internal_diags_profile
= S_NO
;
343 return status2bool(internal_diags_profile
);
348 static bool _check_can_has_debugger(void)
350 #if TARGET_OS_SIMULATOR
351 return _check_internal_content();
353 if (can_has_debugger
== S_UNKNOWN
) {
355 can_has_debugger
= *((uint32_t *)_COMM_PAGE_DEV_FIRM
) ? S_YES
: S_NO
;
358 * The comm page bit does exist on macOS, but also requires kernel
359 * debugging in the CSR configuration. We don't need to be that strict
362 can_has_debugger
= (csr_check(CSR_ALLOW_APPLE_INTERNAL
) == 0) ? S_YES
: S_NO
;
365 return status2bool(can_has_debugger
);
366 #endif // TARGET_OS_SIMULATOR
370 #ifndef VARIANT_SKIP_EXPORTED
373 os_variant_has_internal_content(const char * __unused subsystem
)
375 if (_check_disabled(VP_CONTENT
)) {
380 return _check_internal_release_type();
382 return _check_internal_content();
388 os_variant_has_internal_diagnostics(const char * __unused subsystem
)
390 if (_check_disabled(VP_DIAGNOSTICS
)) {
395 return _check_internal_release_type();
397 return _check_internal_content() || _check_internal_diags_profile();
402 os_variant_has_internal_ui(const char * __unused subsystem
)
404 if (_check_disabled(VP_UI
)) {
409 return _check_internal_release_type();
411 return _check_internal_content();
416 os_variant_allows_internal_security_policies(const char * __unused subsystem
)
418 if (_check_disabled(VP_SECURITY
)) {
422 return _check_can_has_debugger();
425 #endif // VARIANT_SKIP_EXPORTED
427 #define STATUS_INITIAL_BITS 0x70000000F0000000ULL
428 #define STATUS_BIT_WIDTH 2
429 #define STATUS_SET 0x2
430 #define STATUS_MASK 0x3
432 enum status_flags_positions
{
433 SFP_INTERNAL_CONTENT
= 0,
434 SFP_CAN_HAS_DEBUGGER
= 1,
435 SFP_INTERNAL_RELEASE_TYPE
= 2,
436 SFP_INTERNAL_DIAGS_PROFILE
= 3
439 #if !TARGET_OS_SIMULATOR
440 static uint64_t _get_cached_check_status(void)
442 uint64_t res
= STATUS_INITIAL_BITS
;
444 #if !TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
445 _check_internal_content();
446 if (internal_content
!= S_UNKNOWN
)
447 res
|= internal_content
<< SFP_INTERNAL_CONTENT
* STATUS_BIT_WIDTH
;
450 _check_can_has_debugger();
451 if (can_has_debugger
!= S_UNKNOWN
)
452 res
|= can_has_debugger
<< SFP_CAN_HAS_DEBUGGER
* STATUS_BIT_WIDTH
;
455 _check_internal_release_type();
456 if (internal_release_type
!= S_UNKNOWN
)
457 res
|= internal_release_type
<< SFP_INTERNAL_RELEASE_TYPE
* STATUS_BIT_WIDTH
;
459 _check_internal_diags_profile();
460 if (internal_diags_profile
!= S_UNKNOWN
)
461 res
|= internal_diags_profile
<< SFP_INTERNAL_DIAGS_PROFILE
* STATUS_BIT_WIDTH
;
464 _parse_disabled_status(NULL
);
465 for (int i
= 0; i
< VP_MAX
; i
++) {
466 if (disabled_status
[i
]) {
467 res
|= 0x1ULL
<< (i
+ 32);
474 static void _restore_cached_check_status(uint64_t status
)
476 #if !TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
477 if ((status
>> (SFP_INTERNAL_CONTENT
* STATUS_BIT_WIDTH
)) & STATUS_SET
)
478 internal_content
= (status
>> (SFP_INTERNAL_CONTENT
* STATUS_BIT_WIDTH
)) & STATUS_MASK
;
480 if ((status
>> (SFP_CAN_HAS_DEBUGGER
* STATUS_BIT_WIDTH
)) & STATUS_SET
)
481 can_has_debugger
= (status
>> (SFP_CAN_HAS_DEBUGGER
* STATUS_BIT_WIDTH
)) & STATUS_MASK
;
483 if ((status
>> (SFP_INTERNAL_RELEASE_TYPE
* STATUS_BIT_WIDTH
)) & STATUS_SET
)
484 internal_release_type
= (status
>> (SFP_INTERNAL_RELEASE_TYPE
* STATUS_BIT_WIDTH
)) & STATUS_MASK
;
486 if ((status
>> (SFP_INTERNAL_DIAGS_PROFILE
* STATUS_BIT_WIDTH
)) & STATUS_SET
)
487 internal_diags_profile
= (status
>> (SFP_INTERNAL_DIAGS_PROFILE
* STATUS_BIT_WIDTH
)) & STATUS_MASK
;
490 for (int i
= 0; i
< VP_MAX
; i
++) {
491 disabled_status
[i
] = (status
>> (32 + i
)) & 0x1;
494 #endif // !TARGET_OS_SIMULATOR