]> git.saurik.com Git - apple/libc.git/blame - libdarwin/variant.c
Libc-1272.250.1.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
24#include <stdlib.h>
25#include <unistd.h>
26#include <sys/stat.h>
27#include <sys/sysctl.h>
28#include <sys/types.h>
29#include <dispatch/dispatch.h>
30#include <xpc/xpc.h>
31#include <xpc/private.h>
32#include <System/sys/csr.h>
33#include <System/machine/cpu_capabilities.h>
34
35#include <os/assumes.h>
6dccf0e0 36#include <os/stdlib.h>
b061a43b
A
37#include <os/variant_private.h>
38
6dccf0e0
A
39/*
40 * Lists all properties overridden by an empty file
41 */
42#define ALL_OVERRIDES_STR "content,diagnostics,ui,security"
43
b061a43b 44enum variant_property {
b061a43b
A
45 VP_CONTENT,
46 VP_DIAGNOSTICS,
47 VP_UI,
48 VP_SECURITY,
49 VP_MAX
50};
51
52enum check_status {
53 S_UNKNOWN = 0,
54 S_NO = 2,
55 S_YES = 3
56};
57
58static bool
59status2bool(enum check_status status) {
60 switch (status) {
61 case S_NO:
62 return false;
63 case S_YES:
64 return true;
65 default:
66 os_crash("os_variant had unexpected status");
67 }
68}
69
70#define VAR_FILE_LEGACY "/var/db/disableAppleInternal"
71
72#if TARGET_OS_OSX
73#define VAR_FILE_OVERRIDE "/var/db/os_variant_override"
74#else
75#define VAR_FILE_OVERRIDE "/usr/share/misc/os_variant_override"
76#endif
77
78#if !TARGET_OS_SIMULATOR
79#define INTERNAL_CONTENT_PATH "/System/Library/CoreServices/AppleInternalVariant.plist"
80#else
81#define INTERNAL_CONTENT_PATH "/AppleInternal"
82#endif
83
84#define SYSTEM_VERSION_PLIST_PATH "/System/Library/CoreServices/SystemVersion.plist"
85#define SYSTEM_VERSION_PLIST_KEY "ReleaseType"
86
87#if TARGET_OS_IPHONE
88#define INTERNAL_SETTINGS_PATH "/AppleInternal/Library/PreferenceBundles/Internal Settings.bundle"
89#else
90#define INTERNAL_DIAGS_PROFILE_PATH "/var/db/ConfigurationProfiles/Settings/com.apple.InternalDiagnostics.plist"
6dccf0e0 91#define FACTORY_CONTENT_PATH "/System/Library/CoreServices/AppleFactoryVariant.plist"
b061a43b
A
92#endif
93
94#if !TARGET_OS_SIMULATOR
95#define CACHE_SYSCTL_NAME "kern.osvariant_status"
96
97static void _restore_cached_check_status(uint64_t status);
98static uint64_t _get_cached_check_status(void);
99
100static char * _read_file(const char *path, size_t *size_out)
101{
102 char *buf = NULL;
103
104 int fd = open(path, O_RDONLY);
105 if (fd == -1) return NULL;
106
107 struct stat sb;
108 int rc = fstat(fd, &sb);
109 if (rc != 0 || sb.st_size == 0) {
110 goto error;
111 }
112
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) {
117 goto error;
118 }
119
120 buf = malloc(size + 1);
121 if (!buf) {
122 goto error;
123 }
124
125 ssize_t bytes_read = read(fd, buf, size);
126 buf[size] = '\0';
127
128
129 if (bytes_read == (ssize_t)size) {
130 close(fd);
131 return buf;
132 }
133
134error:
135 close(fd);
136 free(buf);
137 return NULL;
138}
139
140static xpc_object_t read_plist(const char *path)
141{
142 size_t size = 16 * 1024;
143 uint8_t *buf = (uint8_t*)_read_file(path, &size);
144 if (!buf) return NULL;
145
146 xpc_object_t plist = xpc_create_from_plist(buf, size);
147 if (plist && xpc_get_type(plist) != XPC_TYPE_DICTIONARY) {
148 xpc_release(plist);
149 plist = NULL;
150 }
151
152 free(buf);
153
154 return plist;
155}
156#endif
157
158#if !TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
159static enum check_status internal_content = S_UNKNOWN;
160#endif
161#if !TARGET_OS_SIMULATOR
162static enum check_status can_has_debugger = S_UNKNOWN;
163#if TARGET_OS_IPHONE
164static enum check_status internal_release_type = S_UNKNOWN;
6dccf0e0 165static enum check_status factory_release_type = S_UNKNOWN;
b061a43b
A
166#else // TARGET_OS_IPHONE
167static enum check_status internal_diags_profile = S_UNKNOWN;
6dccf0e0 168static enum check_status factory_content = S_UNKNOWN;
b061a43b
A
169#endif // TARGET_OS_IPHONE
170#endif // !TARGET_OS_SIMULATOR
171
172static bool disabled_status[VP_MAX] = {};
173
174static void _parse_disabled_status(char *test_string)
175{
176#if TARGET_OS_SIMULATOR
177#pragma unused(test_string)
178#else // TARGET_OS_SIMULATOR
179 char *override_str = NULL;
180
181 bzero(disabled_status, sizeof(disabled_status));
182
183 if (test_string != NULL) {
184 /* used for unit tests */
6dccf0e0 185 override_str = os_strdup(test_string);
b061a43b
A
186 } else {
187 if (access(VAR_FILE_LEGACY, F_OK) == 0) {
6dccf0e0 188 override_str = os_strdup(ALL_OVERRIDES_STR);
b061a43b
A
189 } else if (access(VAR_FILE_OVERRIDE, F_OK) != 0) {
190 return;
191 }
192
193 override_str = _read_file(VAR_FILE_OVERRIDE, NULL);
194 }
195
6dccf0e0
A
196 if (override_str == NULL) {
197 override_str = os_strdup(ALL_OVERRIDES_STR);
198 }
b061a43b
A
199
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;
210 }
211 }
212
213 free(override_str);
214 return;
b061a43b
A
215#endif //!TARGET_OS_SIMULATOR
216}
217
218#if !TARGET_OS_SIMULATOR
219static bool _load_cached_status(void)
220{
221 uint64_t status = 0;
222 size_t status_size = sizeof(status);
223 int ret = sysctlbyname(CACHE_SYSCTL_NAME, &status, &status_size, NULL, 0);
224 if (ret != 0) {
225 return false;
226 }
227
228 if (status) {
229 _restore_cached_check_status(status);
230 return true;
231 }
232
233 if (status == 0 && getpid() == 1) {
234 /*
235 * Looks like we are in launchd; try to set the status.
236 *
237 * We don't actually care if this works because we'll have warmed our state.
238 */
239 status = _get_cached_check_status();
240 sysctlbyname(CACHE_SYSCTL_NAME, NULL, 0, &status, status_size);
241 return true;
242 }
243
244 return false;
245}
246#endif
247
248static void _initialize_status(void * __unused ctx)
249{
250#if !TARGET_OS_SIMULATOR
251 if (!_load_cached_status()) {
252 _parse_disabled_status(NULL);
253 }
254#else
255 _parse_disabled_status(NULL);
256#endif
257}
258
259static bool _check_disabled(enum variant_property variant_property)
260{
261 static dispatch_once_t disabled_status_pred;
262 dispatch_once_f(&disabled_status_pred, NULL, _initialize_status);
263
264 return disabled_status[variant_property];
265}
266
267#if !TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
268static bool _check_internal_content(void)
269{
270 if (internal_content == S_UNKNOWN) {
271#if !TARGET_OS_SIMULATOR
272 const char * path = INTERNAL_CONTENT_PATH;
273#else
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);
278 if (path == NULL) {
279 return false;
280 }
281 to_free = path;
282 }
283#endif
284 internal_content = (access(path, F_OK) == 0) ? S_YES : S_NO;
285#if TARGET_OS_SIMULATOR
286 free(to_free);
287#endif
288 }
289 return status2bool(internal_content);
290}
291#endif // !TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
292
6dccf0e0
A
293#if TARGET_OS_OSX
294static bool _check_factory_content(void)
295{
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;
299 }
300 return status2bool(factory_content);
301}
302#endif // TARGET_OS_OSX
303
b061a43b
A
304#if TARGET_OS_IPHONE
305
6dccf0e0
A
306#if !TARGET_OS_SIMULATOR
307static bool _parse_system_version_plist(void)
308{
309 xpc_object_t system_version_plist = read_plist(SYSTEM_VERSION_PLIST_PATH);
310 if (!system_version_plist) {
311 return false;
312 }
313
314 const char *release_type =
315 xpc_dictionary_get_string(system_version_plist,
316 SYSTEM_VERSION_PLIST_KEY);
317
318 if (release_type == NULL) {
319 /*
320 * Confusingly, customer images are just completely missing this key.
321 */
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;
331 } else {
332 internal_release_type = S_NO;
333 factory_release_type = S_NO;
334 }
335
336 xpc_release(system_version_plist);
337
338 return true;
339}
340#endif //!TARGET_OS_SIMULATOR
341
b061a43b
A
342/*
343 * This set of criteria was taken from copyInternalBuild in MobileGestalt.c
344 */
345static bool _check_internal_release_type(void)
346{
347#if TARGET_OS_SIMULATOR
348 return _check_internal_content();
349#else // TARGET_OS_SIMULATOR
350 if (internal_release_type == S_UNKNOWN) {
6dccf0e0 351 if (!_parse_system_version_plist()) {
b061a43b
A
352 internal_release_type = (access(INTERNAL_SETTINGS_PATH, F_OK) == 0) ? S_YES : S_NO;
353 }
354 }
355
356 return status2bool(internal_release_type);
357#endif // TARGET_OS_SIMULATOR
358}
359
6dccf0e0
A
360static bool _check_factory_release_type(void)
361{
362#if TARGET_OS_SIMULATOR
363 return false;
364#else // TARGET_OS_SIMULATOR
365 if (factory_release_type == S_UNKNOWN) {
366 if (!_parse_system_version_plist()) {
367 factory_release_type = S_NO;
368 }
369 }
370
371 return status2bool(factory_release_type);
372#endif // TARGET_OS_SIMULATOR
373}
374
b061a43b
A
375#else
376
70ad1dc8
A
377static bool _check_internal_diags_profile(void)
378{
b061a43b
A
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);
384 } else {
385 internal_diags_profile = S_NO;
386 }
387 }
388
389 return status2bool(internal_diags_profile);
390}
391
392#endif
393
394static bool _check_can_has_debugger(void)
395{
396#if TARGET_OS_SIMULATOR
397 return _check_internal_content();
398#else
399 if (can_has_debugger == S_UNKNOWN) {
400#if TARGET_OS_IPHONE
401 can_has_debugger = *((uint32_t *)_COMM_PAGE_DEV_FIRM) ? S_YES : S_NO;
402#else
403 /*
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
406 * here.
407 */
408 can_has_debugger = (csr_check(CSR_ALLOW_APPLE_INTERNAL) == 0) ? S_YES : S_NO;
409#endif
410 }
411 return status2bool(can_has_debugger);
412#endif // TARGET_OS_SIMULATOR
413}
414
415// For unit tests
416#ifndef VARIANT_SKIP_EXPORTED
417
418bool
419os_variant_has_internal_content(const char * __unused subsystem)
420{
421 if (_check_disabled(VP_CONTENT)) {
422 return false;
423 }
424
425#if TARGET_OS_IPHONE
426 return _check_internal_release_type();
427#else
428 return _check_internal_content();
429#endif
430}
431
432
433bool
434os_variant_has_internal_diagnostics(const char * __unused subsystem)
435{
436 if (_check_disabled(VP_DIAGNOSTICS)) {
437 return false;
438 }
439
440#if TARGET_OS_IPHONE
441 return _check_internal_release_type();
442#else
443 return _check_internal_content() || _check_internal_diags_profile();
444#endif
445}
446
447bool
448os_variant_has_internal_ui(const char * __unused subsystem)
449{
450 if (_check_disabled(VP_UI)) {
451 return false;
452 }
453
454#if TARGET_OS_IPHONE
455 return _check_internal_release_type();
456#else
457 return _check_internal_content();
458#endif
459}
460
461bool
462os_variant_allows_internal_security_policies(const char * __unused subsystem)
463{
464 if (_check_disabled(VP_SECURITY)) {
465 return false;
466 }
467
468 return _check_can_has_debugger();
469}
470
6dccf0e0
A
471bool
472os_variant_has_factory_content(const char * __unused subsystem)
473{
474#if TARGET_OS_IPHONE
475 return _check_factory_release_type();
476#else
477 return _check_factory_content();
478#endif
479}
480
b061a43b
A
481#endif // VARIANT_SKIP_EXPORTED
482
483#define STATUS_INITIAL_BITS 0x70000000F0000000ULL
484#define STATUS_BIT_WIDTH 2
485#define STATUS_SET 0x2
486#define STATUS_MASK 0x3
487
488enum status_flags_positions {
489 SFP_INTERNAL_CONTENT = 0,
490 SFP_CAN_HAS_DEBUGGER = 1,
491 SFP_INTERNAL_RELEASE_TYPE = 2,
6dccf0e0
A
492 SFP_INTERNAL_DIAGS_PROFILE = 3,
493 SFP_FACTORY_CONTENT = 4,
494 SFP_FACTORY_RELEASE_TYPE = 5,
b061a43b
A
495};
496
497#if !TARGET_OS_SIMULATOR
498static uint64_t _get_cached_check_status(void)
499{
500 uint64_t res = STATUS_INITIAL_BITS;
501
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;
506#endif
507
508 _check_can_has_debugger();
509 if (can_has_debugger != S_UNKNOWN)
510 res |= can_has_debugger << SFP_CAN_HAS_DEBUGGER * STATUS_BIT_WIDTH;
511
512#if TARGET_OS_IPHONE
513 _check_internal_release_type();
514 if (internal_release_type != S_UNKNOWN)
515 res |= internal_release_type << SFP_INTERNAL_RELEASE_TYPE * STATUS_BIT_WIDTH;
6dccf0e0
A
516
517 _check_factory_release_type();
518 if (factory_release_type != S_UNKNOWN)
519 res |= factory_release_type << SFP_FACTORY_RELEASE_TYPE * STATUS_BIT_WIDTH;
b061a43b
A
520#else
521 _check_internal_diags_profile();
522 if (internal_diags_profile != S_UNKNOWN)
523 res |= internal_diags_profile << SFP_INTERNAL_DIAGS_PROFILE * STATUS_BIT_WIDTH;
6dccf0e0
A
524
525 _check_factory_content();
526 if (factory_content != S_UNKNOWN)
527 res |= factory_content << SFP_FACTORY_CONTENT * STATUS_BIT_WIDTH;
b061a43b
A
528#endif
529
530 _parse_disabled_status(NULL);
531 for (int i = 0; i < VP_MAX; i++) {
532 if (disabled_status[i]) {
533 res |= 0x1ULL << (i + 32);
534 }
535 }
536
537 return res;
538}
539
540static void _restore_cached_check_status(uint64_t status)
541{
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;
545#endif
6dccf0e0 546
b061a43b
A
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;
6dccf0e0 549
b061a43b
A
550#if TARGET_OS_IPHONE
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;
6dccf0e0
A
553
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;
b061a43b
A
556#else
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;
6dccf0e0
A
559
560 if ((status >> (SFP_FACTORY_CONTENT * STATUS_BIT_WIDTH)) & STATUS_SET)
561 factory_content = (status >> (SFP_FACTORY_CONTENT * STATUS_BIT_WIDTH)) & STATUS_MASK;
b061a43b
A
562#endif
563
564 for (int i = 0; i < VP_MAX; i++) {
565 disabled_status[i] = (status >> (32 + i)) & 0x1;
566 }
567}
568#endif // !TARGET_OS_SIMULATOR