]> git.saurik.com Git - apple/libc.git/blame - libdarwin/variant.c
Libc-1439.100.3.tar.gz
[apple/libc.git] / libdarwin / variant.c
CommitLineData
b061a43b 1/*
70ad1dc8 2 * Copyright (c) 2016 Apple Inc. All rights reserved.
b061a43b
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
a9aaacca 24#include <errno.h>
b061a43b
A
25#include <stdlib.h>
26#include <unistd.h>
27#include <sys/stat.h>
28#include <sys/sysctl.h>
29#include <sys/types.h>
30#include <dispatch/dispatch.h>
31#include <xpc/xpc.h>
32#include <xpc/private.h>
33#include <System/sys/csr.h>
34#include <System/machine/cpu_capabilities.h>
35
36#include <os/assumes.h>
507116e3 37#include <os/bsd.h>
6dccf0e0 38#include <os/stdlib.h>
b061a43b 39#include <os/variant_private.h>
a9aaacca 40#include <os/boot_mode_private.h>
b061a43b 41
6dccf0e0
A
42/*
43 * Lists all properties overridden by an empty file
44 */
45#define ALL_OVERRIDES_STR "content,diagnostics,ui,security"
46
b061a43b 47enum variant_property {
b061a43b
A
48 VP_CONTENT,
49 VP_DIAGNOSTICS,
50 VP_UI,
51 VP_SECURITY,
52 VP_MAX
53};
54
55enum check_status {
56 S_UNKNOWN = 0,
57 S_NO = 2,
58 S_YES = 3
59};
60
507116e3
A
61typedef struct {
62 const char *variant;
63 bool (*function)(const char*);
64} variant_check_mapping;
65
b061a43b
A
66static bool
67status2bool(enum check_status status) {
68 switch (status) {
69 case S_NO:
70 return false;
71 case S_YES:
72 return true;
507116e3 73 case S_UNKNOWN:
b061a43b
A
74 default:
75 os_crash("os_variant had unexpected status");
76 }
77}
78
79#define VAR_FILE_LEGACY "/var/db/disableAppleInternal"
80
81#if TARGET_OS_OSX
82#define VAR_FILE_OVERRIDE "/var/db/os_variant_override"
83#else
84#define VAR_FILE_OVERRIDE "/usr/share/misc/os_variant_override"
85#endif
86
87#if !TARGET_OS_SIMULATOR
88#define INTERNAL_CONTENT_PATH "/System/Library/CoreServices/AppleInternalVariant.plist"
89#else
90#define INTERNAL_CONTENT_PATH "/AppleInternal"
91#endif
92
93#define SYSTEM_VERSION_PLIST_PATH "/System/Library/CoreServices/SystemVersion.plist"
94#define SYSTEM_VERSION_PLIST_KEY "ReleaseType"
95
96#if TARGET_OS_IPHONE
97#define INTERNAL_SETTINGS_PATH "/AppleInternal/Library/PreferenceBundles/Internal Settings.bundle"
98#else
99#define INTERNAL_DIAGS_PROFILE_PATH "/var/db/ConfigurationProfiles/Settings/com.apple.InternalDiagnostics.plist"
6dccf0e0 100#define FACTORY_CONTENT_PATH "/System/Library/CoreServices/AppleFactoryVariant.plist"
507116e3 101#define BASE_SYSTEM_CONTENT_PATH "/System/Library/BaseSystem"
a9aaacca 102#define DARWINOS_CONTENT_PATH "/System/Library/CoreServices/DarwinVariant.plist"
b061a43b
A
103#endif
104
a9aaacca
A
105static void _check_all_statuses(void);
106
b061a43b
A
107#if !TARGET_OS_SIMULATOR
108#define CACHE_SYSCTL_NAME "kern.osvariant_status"
109
110static void _restore_cached_check_status(uint64_t status);
111static uint64_t _get_cached_check_status(void);
112
113static char * _read_file(const char *path, size_t *size_out)
114{
115 char *buf = NULL;
116
117 int fd = open(path, O_RDONLY);
118 if (fd == -1) return NULL;
119
120 struct stat sb;
121 int rc = fstat(fd, &sb);
122 if (rc != 0 || sb.st_size == 0) {
123 goto error;
124 }
125
126 size_t size_limit = (size_out && *size_out != 0) ? *size_out : 1024;
127 size_t size = (size_t)sb.st_size;
128 if (size_out) *size_out = (size_t)sb.st_size;
129 if (size > size_limit) {
130 goto error;
131 }
132
133 buf = malloc(size + 1);
134 if (!buf) {
135 goto error;
136 }
137
138 ssize_t bytes_read = read(fd, buf, size);
139 buf[size] = '\0';
140
141
142 if (bytes_read == (ssize_t)size) {
143 close(fd);
144 return buf;
145 }
146
147error:
148 close(fd);
149 free(buf);
150 return NULL;
151}
152
153static xpc_object_t read_plist(const char *path)
154{
155 size_t size = 16 * 1024;
156 uint8_t *buf = (uint8_t*)_read_file(path, &size);
157 if (!buf) return NULL;
158
159 xpc_object_t plist = xpc_create_from_plist(buf, size);
160 if (plist && xpc_get_type(plist) != XPC_TYPE_DICTIONARY) {
161 xpc_release(plist);
162 plist = NULL;
163 }
164
165 free(buf);
166
167 return plist;
168}
169#endif
170
171#if !TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
172static enum check_status internal_content = S_UNKNOWN;
173#endif
174#if !TARGET_OS_SIMULATOR
175static enum check_status can_has_debugger = S_UNKNOWN;
a9aaacca 176static enum check_status has_full_logging = S_UNKNOWN;
b061a43b
A
177#if TARGET_OS_IPHONE
178static enum check_status internal_release_type = S_UNKNOWN;
6dccf0e0 179static enum check_status factory_release_type = S_UNKNOWN;
507116e3
A
180static enum check_status darwin_release_type = S_UNKNOWN;
181static enum check_status recovery_release_type = S_UNKNOWN;
182static enum check_status development_kernel = S_UNKNOWN;
b061a43b
A
183#else // TARGET_OS_IPHONE
184static enum check_status internal_diags_profile = S_UNKNOWN;
6dccf0e0 185static enum check_status factory_content = S_UNKNOWN;
507116e3 186static enum check_status base_system_content = S_UNKNOWN;
a9aaacca 187static enum check_status darwinos_content = S_UNKNOWN;
b061a43b
A
188#endif // TARGET_OS_IPHONE
189#endif // !TARGET_OS_SIMULATOR
507116e3 190static enum check_status is_ephemeral = S_UNKNOWN;
b061a43b
A
191
192static bool disabled_status[VP_MAX] = {};
193
194static void _parse_disabled_status(char *test_string)
195{
196#if TARGET_OS_SIMULATOR
197#pragma unused(test_string)
198#else // TARGET_OS_SIMULATOR
199 char *override_str = NULL;
200
201 bzero(disabled_status, sizeof(disabled_status));
202
203 if (test_string != NULL) {
204 /* used for unit tests */
6dccf0e0 205 override_str = os_strdup(test_string);
b061a43b
A
206 } else {
207 if (access(VAR_FILE_LEGACY, F_OK) == 0) {
6dccf0e0 208 override_str = os_strdup(ALL_OVERRIDES_STR);
b061a43b
A
209 } else if (access(VAR_FILE_OVERRIDE, F_OK) != 0) {
210 return;
211 }
212
213 override_str = _read_file(VAR_FILE_OVERRIDE, NULL);
214 }
215
6dccf0e0
A
216 if (override_str == NULL) {
217 override_str = os_strdup(ALL_OVERRIDES_STR);
218 }
b061a43b
A
219
220 char *token, *string = override_str;
221 while ((token = strsep(&string, ",\n")) != NULL) {
222 if (strcmp(token, "content") == 0) {
223 disabled_status[VP_CONTENT] = true;
224 } else if (strcmp(token, "diagnostics") == 0) {
225 disabled_status[VP_DIAGNOSTICS] = true;
226 } else if (strcmp(token, "ui") == 0) {
227 disabled_status[VP_UI] = true;
228 } else if (strcmp(token, "security") == 0) {
229 disabled_status[VP_SECURITY] = true;
230 }
231 }
232
233 free(override_str);
234 return;
b061a43b
A
235#endif //!TARGET_OS_SIMULATOR
236}
237
238#if !TARGET_OS_SIMULATOR
239static bool _load_cached_status(void)
240{
241 uint64_t status = 0;
242 size_t status_size = sizeof(status);
243 int ret = sysctlbyname(CACHE_SYSCTL_NAME, &status, &status_size, NULL, 0);
244 if (ret != 0) {
245 return false;
246 }
247
248 if (status) {
249 _restore_cached_check_status(status);
250 return true;
251 }
252
b061a43b
A
253 return false;
254}
255#endif
256
a9aaacca 257static void _initialize_status(void)
b061a43b 258{
a9aaacca
A
259 static dispatch_once_t once;
260 dispatch_once(&once, ^{
261#if !TARGET_OS_SIMULATOR && !defined(VARIANT_SKIP_EXPORTED)
262 if (_load_cached_status() && !_os_xbs_chrooted) {
263 return;
264 }
b061a43b 265#endif
a9aaacca
A
266 _check_all_statuses();
267 });
b061a43b
A
268}
269
270static bool _check_disabled(enum variant_property variant_property)
271{
a9aaacca 272 _initialize_status();
b061a43b
A
273
274 return disabled_status[variant_property];
275}
276
277#if !TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
a9aaacca 278static void _check_internal_content_impl(void)
b061a43b 279{
a9aaacca
A
280 if (_os_xbs_chrooted && internal_content != S_UNKNOWN) {
281 return;
282 } else {
283 os_assert(internal_content == S_UNKNOWN);
284 }
285
b061a43b 286#if !TARGET_OS_SIMULATOR
a9aaacca 287 const char * path = INTERNAL_CONTENT_PATH;
b061a43b 288#else
a9aaacca
A
289 char *simulator_root = getenv("IPHONE_SIMULATOR_ROOT");
290 char *to_free = NULL, *path = NULL;
291 if (simulator_root) {
292 asprintf(&path, "%s/%s", simulator_root, INTERNAL_CONTENT_PATH);
293 if (path == NULL) {
294 internal_content = S_NO;
295 return;
b061a43b 296 }
a9aaacca
A
297 to_free = path;
298 }
b061a43b 299#endif
a9aaacca 300 internal_content = (access(path, F_OK) == 0) ? S_YES : S_NO;
b061a43b 301#if TARGET_OS_SIMULATOR
a9aaacca 302 free(to_free);
b061a43b 303#endif
a9aaacca
A
304}
305
306static bool _check_internal_content(void)
307{
308 _initialize_status();
b061a43b
A
309 return status2bool(internal_content);
310}
311#endif // !TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
312
6dccf0e0 313#if TARGET_OS_OSX
a9aaacca 314static void _check_factory_content_impl(void)
6dccf0e0 315{
a9aaacca
A
316 if (_os_xbs_chrooted && factory_content != S_UNKNOWN) {
317 return;
318 } else {
319 os_assert(factory_content == S_UNKNOWN);
6dccf0e0 320 }
a9aaacca
A
321
322 const char * path = FACTORY_CONTENT_PATH;
323 factory_content = (access(path, F_OK) == 0) ? S_YES : S_NO;
324}
325
326static bool _check_factory_content(void)
327{
328 _initialize_status();
329
6dccf0e0
A
330 return status2bool(factory_content);
331}
332#endif // TARGET_OS_OSX
333
b061a43b
A
334#if TARGET_OS_IPHONE
335
6dccf0e0
A
336#if !TARGET_OS_SIMULATOR
337static bool _parse_system_version_plist(void)
338{
339 xpc_object_t system_version_plist = read_plist(SYSTEM_VERSION_PLIST_PATH);
340 if (!system_version_plist) {
341 return false;
342 }
343
344 const char *release_type =
345 xpc_dictionary_get_string(system_version_plist,
346 SYSTEM_VERSION_PLIST_KEY);
347
348 if (release_type == NULL) {
349 /*
350 * Confusingly, customer images are just completely missing this key.
351 */
352 internal_release_type = S_NO;
353 factory_release_type = S_NO;
507116e3
A
354 darwin_release_type = S_NO;
355 recovery_release_type = S_NO;
6dccf0e0 356 } else if (strcmp(release_type, "NonUI") == 0) {
6dccf0e0 357 factory_release_type = S_YES;
507116e3
A
358 internal_release_type = S_YES;
359 darwin_release_type = S_NO;
360 recovery_release_type = S_NO;
6dccf0e0 361 } else {
6dccf0e0 362 factory_release_type = S_NO;
507116e3
A
363 internal_release_type = (strstr(release_type, "Internal") != NULL) ? S_YES : S_NO;
364 darwin_release_type = (strstr(release_type, "Darwin") != NULL) ? S_YES : S_NO;
365 recovery_release_type = (strstr(release_type, "Recovery") != NULL) ? S_YES : S_NO;
6dccf0e0
A
366 }
367
368 xpc_release(system_version_plist);
369
370 return true;
371}
a9aaacca
A
372
373static void _check_system_version_plist_statuses_impl(void)
374{
375 os_assert(internal_release_type == S_UNKNOWN);
376 os_assert(factory_release_type == S_UNKNOWN);
377 os_assert(darwin_release_type == S_UNKNOWN);
378 os_assert(recovery_release_type == S_UNKNOWN);
379
380 if (!_parse_system_version_plist()) {
381 internal_release_type = (access(INTERNAL_SETTINGS_PATH, F_OK) == 0) ? S_YES : S_NO;
382 factory_release_type = S_NO;
383 darwin_release_type = S_NO;
384 recovery_release_type = S_NO;
385 }
386}
6dccf0e0
A
387#endif //!TARGET_OS_SIMULATOR
388
b061a43b
A
389static bool _check_internal_release_type(void)
390{
391#if TARGET_OS_SIMULATOR
392 return _check_internal_content();
393#else // TARGET_OS_SIMULATOR
a9aaacca 394 _initialize_status();
b061a43b
A
395
396 return status2bool(internal_release_type);
397#endif // TARGET_OS_SIMULATOR
398}
399
6dccf0e0
A
400static bool _check_factory_release_type(void)
401{
402#if TARGET_OS_SIMULATOR
403 return false;
404#else // TARGET_OS_SIMULATOR
a9aaacca 405 _initialize_status();
6dccf0e0
A
406
407 return status2bool(factory_release_type);
408#endif // TARGET_OS_SIMULATOR
409}
410
507116e3
A
411static bool _check_darwin_release_type(void)
412{
413#if TARGET_OS_SIMULATOR
414 return false;
415#else // TARGET_OS_SIMULATOR
a9aaacca 416 _initialize_status();
507116e3
A
417
418 return status2bool(darwin_release_type);
419#endif // TARGET_OS_SIMULATOR
420}
421
422static bool _check_recovery_release_type(void)
423{
424#if TARGET_OS_SIMULATOR
425 return false;
426#else // TARGET_OS_SIMULATOR
a9aaacca 427 _initialize_status();
507116e3
A
428
429 return status2bool(recovery_release_type);
430#endif // TARGET_OS_SIMULATOR
431}
432
433#else // TARGET_OS_IPHONE
b061a43b 434
a9aaacca 435static void _check_internal_diags_profile_impl(void)
70ad1dc8 436{
a9aaacca
A
437 if (_os_xbs_chrooted && internal_diags_profile != S_UNKNOWN) {
438 return;
439 } else {
440 os_assert(internal_diags_profile == S_UNKNOWN);
b061a43b
A
441 }
442
a9aaacca
A
443 xpc_object_t profile_settings = read_plist(INTERNAL_DIAGS_PROFILE_PATH);
444 if (profile_settings) {
445 internal_diags_profile = xpc_dictionary_get_bool(profile_settings, "AppleInternal") ? S_YES : S_NO;
446 xpc_release(profile_settings);
447 } else {
448 internal_diags_profile = S_NO;
449 }
450}
451
452static bool _check_internal_diags_profile(void)
453{
454 _initialize_status();
455
b061a43b
A
456 return status2bool(internal_diags_profile);
457}
458
a9aaacca 459static void _check_base_system_content_impl(void)
507116e3 460{
a9aaacca
A
461 if (_os_xbs_chrooted && base_system_content != S_UNKNOWN) {
462 return;
463 } else {
464 os_assert(base_system_content == S_UNKNOWN);
507116e3 465 }
a9aaacca
A
466
467 const char * path = BASE_SYSTEM_CONTENT_PATH;
468 base_system_content = (access(path, F_OK) == 0) ? S_YES : S_NO;
469}
470
471static bool _check_base_system_content(void)
472{
473 _initialize_status();
474
507116e3
A
475 return status2bool(base_system_content);
476}
477
a9aaacca
A
478static void _check_darwinos_content_impl(void)
479{
480 if (_os_xbs_chrooted && darwinos_content != S_UNKNOWN) {
481 return;
482 } else {
483 os_assert(darwinos_content == S_UNKNOWN);
484 }
485
486 const char * path = DARWINOS_CONTENT_PATH;
487 darwinos_content = (access(path, F_OK) == 0) ? S_YES : S_NO;
488}
489
490static bool _check_darwinos_content(void)
491{
492 _initialize_status();
493
494 return status2bool(darwinos_content);
495}
496
b061a43b
A
497#endif
498
507116e3 499#if !TARGET_OS_SIMULATOR
a9aaacca 500static void _check_can_has_debugger_impl(void)
b061a43b 501{
a9aaacca
A
502 if (_os_xbs_chrooted && can_has_debugger != S_UNKNOWN) {
503 return;
504 } else {
505 os_assert(can_has_debugger == S_UNKNOWN);
506 }
507
b061a43b 508#if TARGET_OS_IPHONE
a9aaacca 509 can_has_debugger = *((uint32_t *)_COMM_PAGE_DEV_FIRM) ? S_YES : S_NO;
b061a43b 510#else
a9aaacca
A
511 /*
512 * The comm page bit does exist on macOS, but also requires kernel
513 * debugging in the CSR configuration. We don't need to be that strict
514 * here.
515 */
516 can_has_debugger = (csr_check(CSR_ALLOW_APPLE_INTERNAL) == 0) ? S_YES : S_NO;
b061a43b 517#endif
a9aaacca
A
518}
519
520static bool _check_can_has_debugger(void)
521{
522 _initialize_status();
523
b061a43b 524 return status2bool(can_has_debugger);
b061a43b 525}
507116e3
A
526#endif // !TARGET_OS_SIMULATOR
527
528#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
a9aaacca 529static void _check_development_kernel_impl(void)
507116e3 530{
a9aaacca
A
531 os_assert(development_kernel == S_UNKNOWN);
532 /*
533 * Whitelist values from SUPPORTED_KERNEL_CONFIGS.
534 */
535 char *osbuildconfig = NULL;
536 size_t osbuildconfig_sz = 0;
537 errno_t err = sysctlbyname_get_data_np("kern.osbuildconfig", (void **)&osbuildconfig, &osbuildconfig_sz);
538 if (err == 0) {
539 if (strcmp(osbuildconfig, "development") == 0 ||
540 strcmp(osbuildconfig, "debug") == 0 ||
541 strcmp(osbuildconfig, "profile") == 0 ||
542 strcmp(osbuildconfig, "kasan") == 0) {
543 development_kernel = S_YES;
507116e3 544 }
a9aaacca
A
545 }
546 free(osbuildconfig);
507116e3 547
a9aaacca
A
548 if (development_kernel == S_UNKNOWN) {
549 development_kernel = S_NO;
507116e3 550 }
a9aaacca
A
551}
552
553static bool _check_development_kernel(void)
554{
555 _initialize_status();
556
507116e3
A
557 return status2bool(development_kernel);
558}
559#endif // TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
b061a43b 560
a9aaacca
A
561static void _check_uses_ephemeral_storage_impl(void)
562{
563 if (_os_xbs_chrooted && is_ephemeral != S_UNKNOWN) {
564 return;
565 } else {
566 os_assert(is_ephemeral == S_UNKNOWN);
567 }
568
569 uint32_t buffer = 0;
570 size_t buffer_size = sizeof(buffer);
571
572 sysctlbyname("hw.ephemeral_storage", (void *)&buffer, &buffer_size, NULL, 0);
573
574 is_ephemeral = (buffer != 0) ? S_YES : S_NO;
575}
576
577static bool _check_uses_ephemeral_storage(void)
578{
579 _initialize_status();
580
581 return status2bool(is_ephemeral);
582}
583
584#if !TARGET_OS_SIMULATOR
585// internal upcall into libtrace
586extern bool
587_os_trace_basesystem_storage_available(void);
588
589static void
590_init_has_full_logging(void)
591{
592#if TARGET_OS_OSX
593 if (_check_base_system_content() &&
594 !_os_trace_basesystem_storage_available()) {
595 has_full_logging = S_NO;
596 return;
597 }
598#endif
599
600 has_full_logging = S_YES;
601}
602
603static bool _check_has_full_logging(void)
604{
605 _initialize_status();
606
607 return status2bool(has_full_logging);
608}
609#endif // !TARGET_OS_SIMULATOR
610
611static void _check_all_statuses(void)
612{
613#if !TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
614 _check_internal_content_impl();
615#endif
616
617 _check_uses_ephemeral_storage_impl();
618
619#if !TARGET_OS_SIMULATOR
620 _check_can_has_debugger_impl();
621
622#if TARGET_OS_IPHONE
623 _check_system_version_plist_statuses_impl();
624 _check_development_kernel_impl();
625#else
626 _check_internal_diags_profile_impl();
627 _check_factory_content_impl();
628 _check_base_system_content_impl();
629 _check_darwinos_content_impl();
630#endif
631
632#endif // !TARGET_OS_SIMULUATOR
633
634 _parse_disabled_status(NULL);
635}
636
637static bool
638os_variant_has_full_logging(const char * __unused subsystem)
639{
640#if TARGET_OS_SIMULATOR
641 return true;
642#else
643 return _check_has_full_logging();
644#endif
645}
646
647static const variant_check_mapping _variant_map[] = {
648 {.variant = "AllowsInternalSecurityPolicies", .function = os_variant_allows_internal_security_policies},
649 {.variant = "HasFactoryContent", .function = os_variant_has_factory_content},
650 {.variant = "HasFullLogging", .function = os_variant_has_full_logging},
651 {.variant = "HasInternalContent", .function = os_variant_has_internal_content},
652 {.variant = "HasInternalDiagnostics", .function = os_variant_has_internal_diagnostics},
653 {.variant = "HasInternalUI", .function = os_variant_has_internal_ui},
654#if TARGET_OS_OSX
655 {.variant = "IsBaseSystem", .function = os_variant_is_basesystem},
656#endif
657 {.variant = "IsDarwinOS", .function = os_variant_is_darwinos},
658 {.variant = "IsRecovery", .function = os_variant_is_recovery},
659 {.variant = "UsesEphemeralStorage", .function = os_variant_uses_ephemeral_storage},
660 {.variant = NULL, .function = NULL}
661};
662
b061a43b
A
663// For unit tests
664#ifndef VARIANT_SKIP_EXPORTED
665
666bool
667os_variant_has_internal_content(const char * __unused subsystem)
668{
669 if (_check_disabled(VP_CONTENT)) {
670 return false;
671 }
672
673#if TARGET_OS_IPHONE
674 return _check_internal_release_type();
675#else
676 return _check_internal_content();
677#endif
678}
679
680
681bool
682os_variant_has_internal_diagnostics(const char * __unused subsystem)
683{
684 if (_check_disabled(VP_DIAGNOSTICS)) {
685 return false;
686 }
687
688#if TARGET_OS_IPHONE
689 return _check_internal_release_type();
690#else
691 return _check_internal_content() || _check_internal_diags_profile();
692#endif
693}
694
695bool
696os_variant_has_internal_ui(const char * __unused subsystem)
697{
698 if (_check_disabled(VP_UI)) {
699 return false;
700 }
701
702#if TARGET_OS_IPHONE
703 return _check_internal_release_type();
704#else
705 return _check_internal_content();
706#endif
707}
708
709bool
710os_variant_allows_internal_security_policies(const char * __unused subsystem)
711{
712 if (_check_disabled(VP_SECURITY)) {
713 return false;
714 }
715
507116e3
A
716#if TARGET_OS_SIMULATOR
717 return _check_internal_content();
718#elif TARGET_OS_IPHONE
719 return _check_can_has_debugger() || _check_development_kernel();
720#else
b061a43b 721 return _check_can_has_debugger();
507116e3 722#endif
b061a43b
A
723}
724
6dccf0e0
A
725bool
726os_variant_has_factory_content(const char * __unused subsystem)
727{
728#if TARGET_OS_IPHONE
729 return _check_factory_release_type();
730#else
731 return _check_factory_content();
732#endif
733}
734
507116e3
A
735bool
736os_variant_is_darwinos(const char * __unused subsystem)
737{
507116e3
A
738#if TARGET_OS_IPHONE
739 return _check_darwin_release_type();
740#else
a9aaacca 741 return _check_darwinos_content();
507116e3
A
742#endif
743}
744
745bool
746os_variant_is_recovery(const char * __unused subsystem)
747{
748#if TARGET_OS_IPHONE
749 return _check_recovery_release_type();
750#else
751 return _check_base_system_content();
752#endif
753}
754
a9aaacca 755#if TARGET_OS_OSX
507116e3 756bool
a9aaacca 757os_variant_is_basesystem(const char * __unused subsystem)
507116e3 758{
a9aaacca
A
759 return _check_base_system_content();
760}
761#endif
507116e3 762
a9aaacca
A
763bool
764os_variant_uses_ephemeral_storage(const char * __unused subsystem)
765{
766 return _check_uses_ephemeral_storage();
507116e3
A
767}
768
769bool
a9aaacca
A
770os_variant_check(const char *subsystem, const char *variant)
771{
772 variant_check_mapping *current = (variant_check_mapping *)_variant_map;
507116e3
A
773
774 while (current->variant) {
775 if (0 == strncasecmp(current->variant, variant, strlen(current->variant))) {
a9aaacca 776 return current->function(subsystem);
507116e3
A
777 }
778 current ++;
779 }
780
781 return false;
782}
783
a9aaacca
A
784char *
785os_variant_copy_description(const char *subsystem)
786{
787 variant_check_mapping *current = (variant_check_mapping *)_variant_map;
788
789 char *desc = NULL;
790 size_t desc_size = 0;
791 FILE *outstream = open_memstream(&desc, &desc_size);
792 if (!outstream) {
793 return NULL;
794 }
795
796 int error = 0;
797 bool needs_space = false;
798 while (current->variant) {
799 if (current->function(subsystem)) {
800 if (needs_space) {
801 int written = fputc(' ', outstream);
802 if (written == EOF) {
803 error = errno;
804 goto error_out;
805 }
806 }
807 int written = fputs(current->variant, outstream);
808 if (written == EOF) {
809 error = errno;
810 goto error_out;
811 }
812 needs_space = true;
813 }
814 current++;
815 }
816
817 int closed = fclose(outstream);
818 if (closed == EOF) {
819 error = errno;
820 goto close_error_out;
821 }
822 return desc;
823
824error_out:
825 (void)fclose(outstream);
826close_error_out:
827 free(desc);
828 errno = error;
829 return NULL;
830}
831
832#if TARGET_OS_OSX
833
834// XXX As an implementation detail, os_boot_mode is piggy-backing on
835// os_variant's infrastructure. This is not necessarily its long-term home,
836// particularly after rdar://59966472
837
838static enum boot_mode {
839 BOOTMODE_UNKNOWN = 0,
840 BOOTMODE_NONE,
841 BOOTMODE_FVUNLOCK,
842 BOOTMODE_KCGEN,
843 BOOTMODE_DIAGNOSTICS,
844 BOOTMODE_MIGRATION,
845} os_boot_mode;
846
847static void
848_os_boot_mode_launchd_init(const char *boot_mode)
849{
850 if (boot_mode == NULL) {
851 os_boot_mode = BOOTMODE_NONE;
852 } else if (strcmp(boot_mode, OS_BOOT_MODE_FVUNLOCK) == 0) {
853 os_boot_mode = BOOTMODE_FVUNLOCK;
854 } else if (strcmp(boot_mode, OS_BOOT_MODE_KCGEN) == 0) {
855 os_boot_mode = BOOTMODE_KCGEN;
856 } else if (strcmp(boot_mode, OS_BOOT_MODE_DIAGNOSTICS) == 0) {
857 os_boot_mode = BOOTMODE_DIAGNOSTICS;
858 } else if (strcmp(boot_mode, OS_BOOT_MODE_MIGRATION) == 0) {
859 os_boot_mode = BOOTMODE_MIGRATION;
860 }
861}
862
863bool
864os_boot_mode_query(const char **boot_mode_out)
865{
866 _initialize_status();
867
868 switch (os_boot_mode) {
869 case BOOTMODE_NONE:
870 *boot_mode_out = NULL;
871 return true;
872 case BOOTMODE_FVUNLOCK:
873 *boot_mode_out = OS_BOOT_MODE_FVUNLOCK;
874 return true;
875 case BOOTMODE_KCGEN:
876 *boot_mode_out = OS_BOOT_MODE_KCGEN;
877 return true;
878 case BOOTMODE_DIAGNOSTICS:
879 *boot_mode_out = OS_BOOT_MODE_DIAGNOSTICS;
880 return true;
881 case BOOTMODE_MIGRATION:
882 *boot_mode_out = OS_BOOT_MODE_MIGRATION;
883 return true;
884 default:
885 return false;
886 }
887}
888
889#endif // TARGET_OS_OSX
890
891void
892os_variant_init_4launchd(const char *boot_mode)
893{
894#if TARGET_OS_SIMULATOR
895 os_crash("simulator launchd does not initialize os_variant");
896#else
897 os_assert(getpid() == 1);
898
899 _init_has_full_logging();
900
901#if TARGET_OS_OSX
902 _os_boot_mode_launchd_init(boot_mode);
903#endif
904
905 // re-initialize disabled status even if we've already initialized
906 // previously, as it's possible we may have initialized before the override
907 // file was available to read
908 _parse_disabled_status(NULL);
909
910 uint64_t status = _get_cached_check_status();
911 size_t status_size = sizeof(status);
912 // TODO: assert that this succeeds
913 sysctlbyname(CACHE_SYSCTL_NAME, NULL, 0, &status, status_size);
914#endif
915}
916
b061a43b
A
917#endif // VARIANT_SKIP_EXPORTED
918
a9aaacca
A
919/*
920 * Bit allocation in kern.osvariant_status (all ranges inclusive):
921 * - [0-27] are 2-bit check_status values
922 * - [28-31] are 0xF
923 * - [32-32+VP_MAX-1] encode variant_property booleans
924 * - [48-51] encode the boot mode, if known
925 * - [60-62] are 0x7
926 */
b061a43b
A
927#define STATUS_INITIAL_BITS 0x70000000F0000000ULL
928#define STATUS_BIT_WIDTH 2
929#define STATUS_SET 0x2
930#define STATUS_MASK 0x3
931
932enum status_flags_positions {
933 SFP_INTERNAL_CONTENT = 0,
934 SFP_CAN_HAS_DEBUGGER = 1,
935 SFP_INTERNAL_RELEASE_TYPE = 2,
6dccf0e0
A
936 SFP_INTERNAL_DIAGS_PROFILE = 3,
937 SFP_FACTORY_CONTENT = 4,
938 SFP_FACTORY_RELEASE_TYPE = 5,
507116e3
A
939 SFP_DARWINOS_RELEASE_TYPE = 6,
940 SFP_EPHEMERAL_VOLUME = 7,
941 SFP_RECOVERY_RELEASE_TYPE = 8,
942 SFP_BASE_SYSTEM_CONTENT = 9,
943 SFP_DEVELOPMENT_KERNEL = 10,
a9aaacca
A
944 SFP_DARWINOS_CONTENT = 11,
945 SFP_FULL_LOGGING = 12,
b061a43b
A
946};
947
a9aaacca
A
948#define STATUS_BOOT_MODE_SHIFT 48
949#define STATUS_BOOT_MODE_MASK 0x000F000000000000ULL
950
b061a43b
A
951#if !TARGET_OS_SIMULATOR
952static uint64_t _get_cached_check_status(void)
953{
a9aaacca
A
954 _initialize_status();
955
b061a43b
A
956 uint64_t res = STATUS_INITIAL_BITS;
957
958#if !TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
a9aaacca
A
959 os_assert(internal_content != S_UNKNOWN);
960 res |= internal_content << SFP_INTERNAL_CONTENT * STATUS_BIT_WIDTH;
b061a43b
A
961#endif
962
a9aaacca
A
963 os_assert(can_has_debugger != S_UNKNOWN);
964 res |= can_has_debugger << SFP_CAN_HAS_DEBUGGER * STATUS_BIT_WIDTH;
b061a43b 965
a9aaacca
A
966 os_assert(is_ephemeral != S_UNKNOWN);
967 res |= is_ephemeral << SFP_EPHEMERAL_VOLUME * STATUS_BIT_WIDTH;
968
969#ifdef VARIANT_SKIP_EXPORTED
970 // has_full_logging can't be computed outside launchd, so in the tests/etc.
971 // cheat and use the value reported by libdarwin rather than re-computing
972 has_full_logging = os_variant_check("com.apple.Libc.tests", "HasFullLogging") ?
973 S_YES : S_NO;
974#else
975 os_assert(has_full_logging != S_UNKNOWN);
976#endif
977 res |= has_full_logging << SFP_FULL_LOGGING * STATUS_BIT_WIDTH;
507116e3 978
b061a43b 979#if TARGET_OS_IPHONE
a9aaacca
A
980 os_assert(internal_release_type != S_UNKNOWN);
981 res |= internal_release_type << SFP_INTERNAL_RELEASE_TYPE * STATUS_BIT_WIDTH;
6dccf0e0 982
a9aaacca
A
983 os_assert(factory_release_type != S_UNKNOWN);
984 res |= factory_release_type << SFP_FACTORY_RELEASE_TYPE * STATUS_BIT_WIDTH;
507116e3 985
a9aaacca
A
986 os_assert(darwin_release_type != S_UNKNOWN);
987 res |= darwin_release_type << SFP_DARWINOS_RELEASE_TYPE * STATUS_BIT_WIDTH;
507116e3 988
a9aaacca
A
989 os_assert(recovery_release_type != S_UNKNOWN);
990 res |= recovery_release_type << SFP_RECOVERY_RELEASE_TYPE * STATUS_BIT_WIDTH;
507116e3 991
a9aaacca
A
992 os_assert(development_kernel != S_UNKNOWN);
993 res |= development_kernel << SFP_DEVELOPMENT_KERNEL * STATUS_BIT_WIDTH;
b061a43b 994#else
a9aaacca
A
995 os_assert(internal_diags_profile != S_UNKNOWN);
996 res |= internal_diags_profile << SFP_INTERNAL_DIAGS_PROFILE * STATUS_BIT_WIDTH;
997
998 os_assert(factory_content != S_UNKNOWN);
999 res |= factory_content << SFP_FACTORY_CONTENT * STATUS_BIT_WIDTH;
6dccf0e0 1000
a9aaacca
A
1001 os_assert(base_system_content != S_UNKNOWN);
1002 res |= base_system_content << SFP_BASE_SYSTEM_CONTENT * STATUS_BIT_WIDTH;
507116e3 1003
a9aaacca
A
1004 os_assert(darwinos_content != S_UNKNOWN);
1005 res |= darwinos_content << SFP_DARWINOS_CONTENT * STATUS_BIT_WIDTH;
b061a43b
A
1006#endif
1007
b061a43b
A
1008 for (int i = 0; i < VP_MAX; i++) {
1009 if (disabled_status[i]) {
1010 res |= 0x1ULL << (i + 32);
1011 }
1012 }
1013
a9aaacca
A
1014#if !defined(VARIANT_SKIP_EXPORTED) && TARGET_OS_OSX
1015 res |= ((uint64_t)os_boot_mode) << STATUS_BOOT_MODE_SHIFT;
1016#endif // TARGET_OS_OSX
1017
b061a43b
A
1018 return res;
1019}
1020
1021static void _restore_cached_check_status(uint64_t status)
1022{
1023#if !TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
1024 if ((status >> (SFP_INTERNAL_CONTENT * STATUS_BIT_WIDTH)) & STATUS_SET)
1025 internal_content = (status >> (SFP_INTERNAL_CONTENT * STATUS_BIT_WIDTH)) & STATUS_MASK;
1026#endif
6dccf0e0 1027
b061a43b
A
1028 if ((status >> (SFP_CAN_HAS_DEBUGGER * STATUS_BIT_WIDTH)) & STATUS_SET)
1029 can_has_debugger = (status >> (SFP_CAN_HAS_DEBUGGER * STATUS_BIT_WIDTH)) & STATUS_MASK;
6dccf0e0 1030
507116e3
A
1031 if ((status >> (SFP_EPHEMERAL_VOLUME * STATUS_BIT_WIDTH)) & STATUS_SET)
1032 is_ephemeral = (status >> (SFP_EPHEMERAL_VOLUME * STATUS_BIT_WIDTH)) & STATUS_MASK;
1033
a9aaacca
A
1034 if ((status >> (SFP_FULL_LOGGING * STATUS_BIT_WIDTH)) & STATUS_SET)
1035 has_full_logging = (status >> (SFP_FULL_LOGGING * STATUS_BIT_WIDTH)) & STATUS_MASK;
1036
b061a43b
A
1037#if TARGET_OS_IPHONE
1038 if ((status >> (SFP_INTERNAL_RELEASE_TYPE * STATUS_BIT_WIDTH)) & STATUS_SET)
1039 internal_release_type = (status >> (SFP_INTERNAL_RELEASE_TYPE * STATUS_BIT_WIDTH)) & STATUS_MASK;
6dccf0e0
A
1040
1041 if ((status >> (SFP_FACTORY_RELEASE_TYPE * STATUS_BIT_WIDTH)) & STATUS_SET)
1042 factory_release_type = (status >> (SFP_FACTORY_RELEASE_TYPE * STATUS_BIT_WIDTH)) & STATUS_MASK;
507116e3
A
1043
1044 if ((status >> (SFP_DARWINOS_RELEASE_TYPE * STATUS_BIT_WIDTH)) & STATUS_SET)
1045 darwin_release_type = (status >> (SFP_DARWINOS_RELEASE_TYPE * STATUS_BIT_WIDTH)) & STATUS_MASK;
1046
1047 if ((status >> (SFP_RECOVERY_RELEASE_TYPE * STATUS_BIT_WIDTH)) & STATUS_SET)
1048 recovery_release_type = (status >> (SFP_RECOVERY_RELEASE_TYPE * STATUS_BIT_WIDTH)) & STATUS_MASK;
1049
1050 if ((status >> (SFP_DEVELOPMENT_KERNEL * STATUS_BIT_WIDTH)) & STATUS_SET)
1051 development_kernel = (status >> (SFP_DEVELOPMENT_KERNEL * STATUS_BIT_WIDTH)) & STATUS_MASK;
b061a43b
A
1052#else
1053 if ((status >> (SFP_INTERNAL_DIAGS_PROFILE * STATUS_BIT_WIDTH)) & STATUS_SET)
1054 internal_diags_profile = (status >> (SFP_INTERNAL_DIAGS_PROFILE * STATUS_BIT_WIDTH)) & STATUS_MASK;
6dccf0e0
A
1055
1056 if ((status >> (SFP_FACTORY_CONTENT * STATUS_BIT_WIDTH)) & STATUS_SET)
1057 factory_content = (status >> (SFP_FACTORY_CONTENT * STATUS_BIT_WIDTH)) & STATUS_MASK;
507116e3
A
1058
1059 if ((status >> (SFP_BASE_SYSTEM_CONTENT * STATUS_BIT_WIDTH)) & STATUS_SET)
1060 base_system_content = (status >> (SFP_BASE_SYSTEM_CONTENT * STATUS_BIT_WIDTH)) & STATUS_MASK;
a9aaacca
A
1061
1062 if ((status >> (SFP_DARWINOS_CONTENT * STATUS_BIT_WIDTH)) & STATUS_SET)
1063 darwinos_content = (status >> (SFP_DARWINOS_CONTENT * STATUS_BIT_WIDTH)) & STATUS_MASK;
b061a43b
A
1064#endif
1065
1066 for (int i = 0; i < VP_MAX; i++) {
1067 disabled_status[i] = (status >> (32 + i)) & 0x1;
1068 }
a9aaacca
A
1069
1070#if !defined(VARIANT_SKIP_EXPORTED) && TARGET_OS_OSX
1071 os_boot_mode = (enum boot_mode)((status & STATUS_BOOT_MODE_MASK) >> STATUS_BOOT_MODE_SHIFT);
1072#endif // TARGET_OS_OSX
b061a43b
A
1073}
1074#endif // !TARGET_OS_SIMULATOR