2 * Copyright (c) 2016 Apple 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/stdlib.h>
37 #include <os/variant_private.h>
40 * Lists all properties overridden by an empty file
42 #define ALL_OVERRIDES_STR "content,diagnostics,ui,security"
44 enum variant_property
{
59 status2bool(enum check_status status
) {
66 os_crash("os_variant had unexpected status");
70 #define VAR_FILE_LEGACY "/var/db/disableAppleInternal"
73 #define VAR_FILE_OVERRIDE "/var/db/os_variant_override"
75 #define VAR_FILE_OVERRIDE "/usr/share/misc/os_variant_override"
78 #if !TARGET_OS_SIMULATOR
79 #define INTERNAL_CONTENT_PATH "/System/Library/CoreServices/AppleInternalVariant.plist"
81 #define INTERNAL_CONTENT_PATH "/AppleInternal"
84 #define SYSTEM_VERSION_PLIST_PATH "/System/Library/CoreServices/SystemVersion.plist"
85 #define SYSTEM_VERSION_PLIST_KEY "ReleaseType"
88 #define INTERNAL_SETTINGS_PATH "/AppleInternal/Library/PreferenceBundles/Internal Settings.bundle"
90 #define INTERNAL_DIAGS_PROFILE_PATH "/var/db/ConfigurationProfiles/Settings/com.apple.InternalDiagnostics.plist"
91 #define FACTORY_CONTENT_PATH "/System/Library/CoreServices/AppleFactoryVariant.plist"
94 #if !TARGET_OS_SIMULATOR
95 #define CACHE_SYSCTL_NAME "kern.osvariant_status"
97 static void _restore_cached_check_status(uint64_t status
);
98 static uint64_t _get_cached_check_status(void);
100 static char * _read_file(const char *path
, size_t *size_out
)
104 int fd
= open(path
, O_RDONLY
);
105 if (fd
== -1) return NULL
;
108 int rc
= fstat(fd
, &sb
);
109 if (rc
!= 0 || sb
.st_size
== 0) {
113 size_t size_limit
= (size_out
&& *size_out
!= 0) ? *size_out
: 1024;
114 size_t size
= (size_t)sb
.st_size
;
115 if (size_out
) *size_out
= (size_t)sb
.st_size
;
116 if (size
> size_limit
) {
120 buf
= malloc(size
+ 1);
125 ssize_t bytes_read
= read(fd
, buf
, size
);
129 if (bytes_read
== (ssize_t
)size
) {
140 static xpc_object_t
read_plist(const char *path
)
142 size_t size
= 16 * 1024;
143 uint8_t *buf
= (uint8_t*)_read_file(path
, &size
);
144 if (!buf
) return NULL
;
146 xpc_object_t plist
= xpc_create_from_plist(buf
, size
);
147 if (plist
&& xpc_get_type(plist
) != XPC_TYPE_DICTIONARY
) {
158 #if !TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
159 static enum check_status internal_content
= S_UNKNOWN
;
161 #if !TARGET_OS_SIMULATOR
162 static enum check_status can_has_debugger
= S_UNKNOWN
;
164 static enum check_status internal_release_type
= S_UNKNOWN
;
165 static enum check_status factory_release_type
= S_UNKNOWN
;
166 #else // TARGET_OS_IPHONE
167 static enum check_status internal_diags_profile
= S_UNKNOWN
;
168 static enum check_status factory_content
= S_UNKNOWN
;
169 #endif // TARGET_OS_IPHONE
170 #endif // !TARGET_OS_SIMULATOR
172 static bool disabled_status
[VP_MAX
] = {};
174 static void _parse_disabled_status(char *test_string
)
176 #if TARGET_OS_SIMULATOR
177 #pragma unused(test_string)
178 #else // TARGET_OS_SIMULATOR
179 char *override_str
= NULL
;
181 bzero(disabled_status
, sizeof(disabled_status
));
183 if (test_string
!= NULL
) {
184 /* used for unit tests */
185 override_str
= os_strdup(test_string
);
187 if (access(VAR_FILE_LEGACY
, F_OK
) == 0) {
188 override_str
= os_strdup(ALL_OVERRIDES_STR
);
189 } else if (access(VAR_FILE_OVERRIDE
, F_OK
) != 0) {
193 override_str
= _read_file(VAR_FILE_OVERRIDE
, NULL
);
196 if (override_str
== NULL
) {
197 override_str
= os_strdup(ALL_OVERRIDES_STR
);
200 char *token
, *string
= override_str
;
201 while ((token
= strsep(&string
, ",\n")) != NULL
) {
202 if (strcmp(token
, "content") == 0) {
203 disabled_status
[VP_CONTENT
] = true;
204 } else if (strcmp(token
, "diagnostics") == 0) {
205 disabled_status
[VP_DIAGNOSTICS
] = true;
206 } else if (strcmp(token
, "ui") == 0) {
207 disabled_status
[VP_UI
] = true;
208 } else if (strcmp(token
, "security") == 0) {
209 disabled_status
[VP_SECURITY
] = true;
215 #endif //!TARGET_OS_SIMULATOR
218 #if !TARGET_OS_SIMULATOR
219 static bool _load_cached_status(void)
222 size_t status_size
= sizeof(status
);
223 int ret
= sysctlbyname(CACHE_SYSCTL_NAME
, &status
, &status_size
, NULL
, 0);
229 _restore_cached_check_status(status
);
233 if (status
== 0 && getpid() == 1) {
235 * Looks like we are in launchd; try to set the status.
237 * We don't actually care if this works because we'll have warmed our state.
239 status
= _get_cached_check_status();
240 sysctlbyname(CACHE_SYSCTL_NAME
, NULL
, 0, &status
, status_size
);
248 static void _initialize_status(void * __unused ctx
)
250 #if !TARGET_OS_SIMULATOR
251 if (!_load_cached_status()) {
252 _parse_disabled_status(NULL
);
255 _parse_disabled_status(NULL
);
259 static bool _check_disabled(enum variant_property variant_property
)
261 static dispatch_once_t disabled_status_pred
;
262 dispatch_once_f(&disabled_status_pred
, NULL
, _initialize_status
);
264 return disabled_status
[variant_property
];
267 #if !TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
268 static bool _check_internal_content(void)
270 if (internal_content
== S_UNKNOWN
) {
271 #if !TARGET_OS_SIMULATOR
272 const char * path
= INTERNAL_CONTENT_PATH
;
274 char *simulator_root
= getenv("IPHONE_SIMULATOR_ROOT");
275 char *to_free
= NULL
, *path
= NULL
;
276 if (simulator_root
) {
277 asprintf(&path
, "%s/%s", simulator_root
, INTERNAL_CONTENT_PATH
);
284 internal_content
= (access(path
, F_OK
) == 0) ? S_YES
: S_NO
;
285 #if TARGET_OS_SIMULATOR
289 return status2bool(internal_content
);
291 #endif // !TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
294 static bool _check_factory_content(void)
296 if (factory_content
== S_UNKNOWN
) {
297 const char * path
= FACTORY_CONTENT_PATH
;
298 factory_content
= (access(path
, F_OK
) == 0) ? S_YES
: S_NO
;
300 return status2bool(factory_content
);
302 #endif // TARGET_OS_OSX
306 #if !TARGET_OS_SIMULATOR
307 static bool _parse_system_version_plist(void)
309 xpc_object_t system_version_plist
= read_plist(SYSTEM_VERSION_PLIST_PATH
);
310 if (!system_version_plist
) {
314 const char *release_type
=
315 xpc_dictionary_get_string(system_version_plist
,
316 SYSTEM_VERSION_PLIST_KEY
);
318 if (release_type
== NULL
) {
320 * Confusingly, customer images are just completely missing this key.
322 internal_release_type
= S_NO
;
323 factory_release_type
= S_NO
;
324 } else if (strcmp(release_type
, "Internal") == 0 ||
325 strcmp(release_type
, "Lite Internal") == 0) {
326 internal_release_type
= S_YES
;
327 factory_release_type
= S_NO
;
328 } else if (strcmp(release_type
, "NonUI") == 0) {
329 internal_release_type
= S_YES
;
330 factory_release_type
= S_YES
;
332 internal_release_type
= S_NO
;
333 factory_release_type
= S_NO
;
336 xpc_release(system_version_plist
);
340 #endif //!TARGET_OS_SIMULATOR
343 * This set of criteria was taken from copyInternalBuild in MobileGestalt.c
345 static bool _check_internal_release_type(void)
347 #if TARGET_OS_SIMULATOR
348 return _check_internal_content();
349 #else // TARGET_OS_SIMULATOR
350 if (internal_release_type
== S_UNKNOWN
) {
351 if (!_parse_system_version_plist()) {
352 internal_release_type
= (access(INTERNAL_SETTINGS_PATH
, F_OK
) == 0) ? S_YES
: S_NO
;
356 return status2bool(internal_release_type
);
357 #endif // TARGET_OS_SIMULATOR
360 static bool _check_factory_release_type(void)
362 #if TARGET_OS_SIMULATOR
364 #else // TARGET_OS_SIMULATOR
365 if (factory_release_type
== S_UNKNOWN
) {
366 if (!_parse_system_version_plist()) {
367 factory_release_type
= S_NO
;
371 return status2bool(factory_release_type
);
372 #endif // TARGET_OS_SIMULATOR
377 static bool _check_internal_diags_profile(void)
379 if (internal_diags_profile
== S_UNKNOWN
) {
380 xpc_object_t profile_settings
= read_plist(INTERNAL_DIAGS_PROFILE_PATH
);
381 if (profile_settings
) {
382 internal_diags_profile
= xpc_dictionary_get_bool(profile_settings
, "AppleInternal") ? S_YES
: S_NO
;
383 xpc_release(profile_settings
);
385 internal_diags_profile
= S_NO
;
389 return status2bool(internal_diags_profile
);
394 static bool _check_can_has_debugger(void)
396 #if TARGET_OS_SIMULATOR
397 return _check_internal_content();
399 if (can_has_debugger
== S_UNKNOWN
) {
401 can_has_debugger
= *((uint32_t *)_COMM_PAGE_DEV_FIRM
) ? S_YES
: S_NO
;
404 * The comm page bit does exist on macOS, but also requires kernel
405 * debugging in the CSR configuration. We don't need to be that strict
408 can_has_debugger
= (csr_check(CSR_ALLOW_APPLE_INTERNAL
) == 0) ? S_YES
: S_NO
;
411 return status2bool(can_has_debugger
);
412 #endif // TARGET_OS_SIMULATOR
416 #ifndef VARIANT_SKIP_EXPORTED
419 os_variant_has_internal_content(const char * __unused subsystem
)
421 if (_check_disabled(VP_CONTENT
)) {
426 return _check_internal_release_type();
428 return _check_internal_content();
434 os_variant_has_internal_diagnostics(const char * __unused subsystem
)
436 if (_check_disabled(VP_DIAGNOSTICS
)) {
441 return _check_internal_release_type();
443 return _check_internal_content() || _check_internal_diags_profile();
448 os_variant_has_internal_ui(const char * __unused subsystem
)
450 if (_check_disabled(VP_UI
)) {
455 return _check_internal_release_type();
457 return _check_internal_content();
462 os_variant_allows_internal_security_policies(const char * __unused subsystem
)
464 if (_check_disabled(VP_SECURITY
)) {
468 return _check_can_has_debugger();
472 os_variant_has_factory_content(const char * __unused subsystem
)
475 return _check_factory_release_type();
477 return _check_factory_content();
481 #endif // VARIANT_SKIP_EXPORTED
483 #define STATUS_INITIAL_BITS 0x70000000F0000000ULL
484 #define STATUS_BIT_WIDTH 2
485 #define STATUS_SET 0x2
486 #define STATUS_MASK 0x3
488 enum status_flags_positions
{
489 SFP_INTERNAL_CONTENT
= 0,
490 SFP_CAN_HAS_DEBUGGER
= 1,
491 SFP_INTERNAL_RELEASE_TYPE
= 2,
492 SFP_INTERNAL_DIAGS_PROFILE
= 3,
493 SFP_FACTORY_CONTENT
= 4,
494 SFP_FACTORY_RELEASE_TYPE
= 5,
497 #if !TARGET_OS_SIMULATOR
498 static uint64_t _get_cached_check_status(void)
500 uint64_t res
= STATUS_INITIAL_BITS
;
502 #if !TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
503 _check_internal_content();
504 if (internal_content
!= S_UNKNOWN
)
505 res
|= internal_content
<< SFP_INTERNAL_CONTENT
* STATUS_BIT_WIDTH
;
508 _check_can_has_debugger();
509 if (can_has_debugger
!= S_UNKNOWN
)
510 res
|= can_has_debugger
<< SFP_CAN_HAS_DEBUGGER
* STATUS_BIT_WIDTH
;
513 _check_internal_release_type();
514 if (internal_release_type
!= S_UNKNOWN
)
515 res
|= internal_release_type
<< SFP_INTERNAL_RELEASE_TYPE
* STATUS_BIT_WIDTH
;
517 _check_factory_release_type();
518 if (factory_release_type
!= S_UNKNOWN
)
519 res
|= factory_release_type
<< SFP_FACTORY_RELEASE_TYPE
* STATUS_BIT_WIDTH
;
521 _check_internal_diags_profile();
522 if (internal_diags_profile
!= S_UNKNOWN
)
523 res
|= internal_diags_profile
<< SFP_INTERNAL_DIAGS_PROFILE
* STATUS_BIT_WIDTH
;
525 _check_factory_content();
526 if (factory_content
!= S_UNKNOWN
)
527 res
|= factory_content
<< SFP_FACTORY_CONTENT
* STATUS_BIT_WIDTH
;
530 _parse_disabled_status(NULL
);
531 for (int i
= 0; i
< VP_MAX
; i
++) {
532 if (disabled_status
[i
]) {
533 res
|= 0x1ULL
<< (i
+ 32);
540 static void _restore_cached_check_status(uint64_t status
)
542 #if !TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
543 if ((status
>> (SFP_INTERNAL_CONTENT
* STATUS_BIT_WIDTH
)) & STATUS_SET
)
544 internal_content
= (status
>> (SFP_INTERNAL_CONTENT
* STATUS_BIT_WIDTH
)) & STATUS_MASK
;
547 if ((status
>> (SFP_CAN_HAS_DEBUGGER
* STATUS_BIT_WIDTH
)) & STATUS_SET
)
548 can_has_debugger
= (status
>> (SFP_CAN_HAS_DEBUGGER
* STATUS_BIT_WIDTH
)) & STATUS_MASK
;
551 if ((status
>> (SFP_INTERNAL_RELEASE_TYPE
* STATUS_BIT_WIDTH
)) & STATUS_SET
)
552 internal_release_type
= (status
>> (SFP_INTERNAL_RELEASE_TYPE
* STATUS_BIT_WIDTH
)) & STATUS_MASK
;
554 if ((status
>> (SFP_FACTORY_RELEASE_TYPE
* STATUS_BIT_WIDTH
)) & STATUS_SET
)
555 factory_release_type
= (status
>> (SFP_FACTORY_RELEASE_TYPE
* STATUS_BIT_WIDTH
)) & STATUS_MASK
;
557 if ((status
>> (SFP_INTERNAL_DIAGS_PROFILE
* STATUS_BIT_WIDTH
)) & STATUS_SET
)
558 internal_diags_profile
= (status
>> (SFP_INTERNAL_DIAGS_PROFILE
* STATUS_BIT_WIDTH
)) & STATUS_MASK
;
560 if ((status
>> (SFP_FACTORY_CONTENT
* STATUS_BIT_WIDTH
)) & STATUS_SET
)
561 factory_content
= (status
>> (SFP_FACTORY_CONTENT
* STATUS_BIT_WIDTH
)) & STATUS_MASK
;
564 for (int i
= 0; i
< VP_MAX
; i
++) {
565 disabled_status
[i
] = (status
>> (32 + i
)) & 0x1;
568 #endif // !TARGET_OS_SIMULATOR