···11+/*
22+ * Copyright (c) 2016 Apple Computer, Inc. All rights reserved.
33+ *
44+ * @APPLE_LICENSE_HEADER_START@
55+ *
66+ * This file contains Original Code and/or Modifications of Original Code
77+ * as defined in and that are subject to the Apple Public Source License
88+ * Version 2.0 (the 'License'). You may not use this file except in
99+ * compliance with the License. Please obtain a copy of the License at
1010+ * http://www.opensource.apple.com/apsl/ and read it before using this
1111+ * file.
1212+ *
1313+ * The Original Code and all software distributed under the License are
1414+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1515+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
1616+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
1717+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
1818+ * Please see the License for the specific language governing rights and
1919+ * limitations under the License.
2020+ *
2121+ * @APPLE_LICENSE_HEADER_END@
2222+ */
2323+2424+#include <stdlib.h>
2525+#include <unistd.h>
2626+#include <sys/stat.h>
2727+#include <sys/sysctl.h>
2828+#include <sys/types.h>
2929+#include <dispatch/dispatch.h>
3030+#include <xpc/xpc.h>
3131+#include <xpc/private.h>
3232+#include <System/sys/csr.h>
3333+#include <System/machine/cpu_capabilities.h>
3434+3535+#include <os/assumes.h>
3636+#include <os/variant_private.h>
3737+3838+enum variant_property {
3939+ VP_ALL = 0,
4040+ VP_CONTENT,
4141+ VP_DIAGNOSTICS,
4242+ VP_UI,
4343+ VP_SECURITY,
4444+ VP_MAX
4545+};
4646+4747+enum check_status {
4848+ S_UNKNOWN = 0,
4949+ S_NO = 2,
5050+ S_YES = 3
5151+};
5252+5353+static bool
5454+status2bool(enum check_status status) {
5555+ switch (status) {
5656+ case S_NO:
5757+ return false;
5858+ case S_YES:
5959+ return true;
6060+ default:
6161+ os_crash("os_variant had unexpected status");
6262+ }
6363+}
6464+6565+#define VAR_FILE_LEGACY "/var/db/disableAppleInternal"
6666+6767+#if TARGET_OS_OSX
6868+#define VAR_FILE_OVERRIDE "/var/db/os_variant_override"
6969+#else
7070+#define VAR_FILE_OVERRIDE "/usr/share/misc/os_variant_override"
7171+#endif
7272+7373+#if !TARGET_OS_SIMULATOR
7474+#define INTERNAL_CONTENT_PATH "/System/Library/CoreServices/AppleInternalVariant.plist"
7575+#else
7676+#define INTERNAL_CONTENT_PATH "/AppleInternal"
7777+#endif
7878+7979+#define SYSTEM_VERSION_PLIST_PATH "/System/Library/CoreServices/SystemVersion.plist"
8080+#define SYSTEM_VERSION_PLIST_KEY "ReleaseType"
8181+8282+#if TARGET_OS_IPHONE
8383+#define INTERNAL_SETTINGS_PATH "/AppleInternal/Library/PreferenceBundles/Internal Settings.bundle"
8484+#else
8585+#define INTERNAL_DIAGS_PROFILE_PATH "/var/db/ConfigurationProfiles/Settings/com.apple.InternalDiagnostics.plist"
8686+#endif
8787+8888+#if !TARGET_OS_SIMULATOR
8989+#define CACHE_SYSCTL_NAME "kern.osvariant_status"
9090+9191+static void _restore_cached_check_status(uint64_t status);
9292+static uint64_t _get_cached_check_status(void);
9393+9494+static char * _read_file(const char *path, size_t *size_out)
9595+{
9696+ char *buf = NULL;
9797+9898+ int fd = open(path, O_RDONLY);
9999+ if (fd == -1) return NULL;
100100+101101+ struct stat sb;
102102+ int rc = fstat(fd, &sb);
103103+ if (rc != 0 || sb.st_size == 0) {
104104+ goto error;
105105+ }
106106+107107+ size_t size_limit = (size_out && *size_out != 0) ? *size_out : 1024;
108108+ size_t size = (size_t)sb.st_size;
109109+ if (size_out) *size_out = (size_t)sb.st_size;
110110+ if (size > size_limit) {
111111+ goto error;
112112+ }
113113+114114+ buf = malloc(size + 1);
115115+ if (!buf) {
116116+ goto error;
117117+ }
118118+119119+ ssize_t bytes_read = read(fd, buf, size);
120120+ buf[size] = '\0';
121121+122122+123123+ if (bytes_read == (ssize_t)size) {
124124+ close(fd);
125125+ return buf;
126126+ }
127127+128128+error:
129129+ close(fd);
130130+ free(buf);
131131+ return NULL;
132132+}
133133+134134+static xpc_object_t read_plist(const char *path)
135135+{
136136+ size_t size = 16 * 1024;
137137+ uint8_t *buf = (uint8_t*)_read_file(path, &size);
138138+ if (!buf) return NULL;
139139+140140+ xpc_object_t plist = xpc_create_from_plist(buf, size);
141141+ if (plist && xpc_get_type(plist) != XPC_TYPE_DICTIONARY) {
142142+ xpc_release(plist);
143143+ plist = NULL;
144144+ }
145145+146146+ free(buf);
147147+148148+ return plist;
149149+}
150150+#endif
151151+152152+#if !TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
153153+static enum check_status internal_content = S_UNKNOWN;
154154+#endif
155155+#if !TARGET_OS_SIMULATOR
156156+static enum check_status can_has_debugger = S_UNKNOWN;
157157+#if TARGET_OS_IPHONE
158158+static enum check_status internal_release_type = S_UNKNOWN;
159159+#else // TARGET_OS_IPHONE
160160+static enum check_status internal_diags_profile = S_UNKNOWN;
161161+#endif // TARGET_OS_IPHONE
162162+#endif // !TARGET_OS_SIMULATOR
163163+164164+static bool disabled_status[VP_MAX] = {};
165165+166166+static void _parse_disabled_status(char *test_string)
167167+{
168168+#if TARGET_OS_SIMULATOR
169169+#pragma unused(test_string)
170170+#else // TARGET_OS_SIMULATOR
171171+ char *override_str = NULL;
172172+173173+ bzero(disabled_status, sizeof(disabled_status));
174174+175175+ if (test_string != NULL) {
176176+ /* used for unit tests */
177177+ override_str = strdup(test_string);
178178+ } else {
179179+ if (access(VAR_FILE_LEGACY, F_OK) == 0) {
180180+ goto disable_all;
181181+ } else if (access(VAR_FILE_OVERRIDE, F_OK) != 0) {
182182+ return;
183183+ }
184184+185185+ override_str = _read_file(VAR_FILE_OVERRIDE, NULL);
186186+ }
187187+188188+ if (override_str == NULL) goto disable_all;
189189+190190+ char *token, *string = override_str;
191191+ while ((token = strsep(&string, ",\n")) != NULL) {
192192+ if (strcmp(token, "content") == 0) {
193193+ disabled_status[VP_CONTENT] = true;
194194+ } else if (strcmp(token, "diagnostics") == 0) {
195195+ disabled_status[VP_DIAGNOSTICS] = true;
196196+ } else if (strcmp(token, "ui") == 0) {
197197+ disabled_status[VP_UI] = true;
198198+ } else if (strcmp(token, "security") == 0) {
199199+ disabled_status[VP_SECURITY] = true;
200200+ }
201201+ }
202202+203203+ free(override_str);
204204+ return;
205205+206206+disable_all:
207207+ for (int i = 0; i < VP_MAX; i++) {
208208+ disabled_status[i] = true;
209209+ }
210210+#endif //!TARGET_OS_SIMULATOR
211211+}
212212+213213+#if !TARGET_OS_SIMULATOR
214214+static bool _load_cached_status(void)
215215+{
216216+ uint64_t status = 0;
217217+ size_t status_size = sizeof(status);
218218+ int ret = sysctlbyname(CACHE_SYSCTL_NAME, &status, &status_size, NULL, 0);
219219+ if (ret != 0) {
220220+ return false;
221221+ }
222222+223223+ if (status) {
224224+ _restore_cached_check_status(status);
225225+ return true;
226226+ }
227227+228228+ if (status == 0 && getpid() == 1) {
229229+ /*
230230+ * Looks like we are in launchd; try to set the status.
231231+ *
232232+ * We don't actually care if this works because we'll have warmed our state.
233233+ */
234234+ status = _get_cached_check_status();
235235+ sysctlbyname(CACHE_SYSCTL_NAME, NULL, 0, &status, status_size);
236236+ return true;
237237+ }
238238+239239+ return false;
240240+}
241241+#endif
242242+243243+static void _initialize_status(void * __unused ctx)
244244+{
245245+#if !TARGET_OS_SIMULATOR
246246+ if (!_load_cached_status()) {
247247+ _parse_disabled_status(NULL);
248248+ }
249249+#else
250250+ _parse_disabled_status(NULL);
251251+#endif
252252+}
253253+254254+static bool _check_disabled(enum variant_property variant_property)
255255+{
256256+ static dispatch_once_t disabled_status_pred;
257257+ dispatch_once_f(&disabled_status_pred, NULL, _initialize_status);
258258+259259+ return disabled_status[variant_property];
260260+}
261261+262262+#if !TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
263263+static bool _check_internal_content(void)
264264+{
265265+ if (internal_content == S_UNKNOWN) {
266266+#if !TARGET_OS_SIMULATOR
267267+ const char * path = INTERNAL_CONTENT_PATH;
268268+#else
269269+ char *simulator_root = getenv("IPHONE_SIMULATOR_ROOT");
270270+ char *to_free = NULL, *path = NULL;
271271+ if (simulator_root) {
272272+ asprintf(&path, "%s/%s", simulator_root, INTERNAL_CONTENT_PATH);
273273+ if (path == NULL) {
274274+ return false;
275275+ }
276276+ to_free = path;
277277+ }
278278+#endif
279279+ internal_content = (access(path, F_OK) == 0) ? S_YES : S_NO;
280280+#if TARGET_OS_SIMULATOR
281281+ free(to_free);
282282+#endif
283283+ }
284284+ return status2bool(internal_content);
285285+}
286286+#endif // !TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
287287+288288+#if TARGET_OS_IPHONE
289289+290290+/*
291291+ * This set of criteria was taken from copyInternalBuild in MobileGestalt.c
292292+ */
293293+static bool _check_internal_release_type(void)
294294+{
295295+#if TARGET_OS_SIMULATOR
296296+ return _check_internal_content();
297297+#else // TARGET_OS_SIMULATOR
298298+ if (internal_release_type == S_UNKNOWN) {
299299+ xpc_object_t system_version_plist = read_plist(SYSTEM_VERSION_PLIST_PATH);
300300+ if (system_version_plist) {
301301+ const char *release_type =
302302+ xpc_dictionary_get_string(system_version_plist,
303303+ SYSTEM_VERSION_PLIST_KEY);
304304+305305+ if (release_type == NULL) {
306306+ /*
307307+ * Confusingly, customer images are just completely missing this key.
308308+ */
309309+ internal_release_type = S_NO;
310310+ } else if (strcmp(release_type, "Internal") == 0 ||
311311+ strcmp(release_type, "Lite Internal") == 0 ||
312312+ strcmp(release_type, "NonUI") == 0) {
313313+ internal_release_type = S_YES;
314314+ } else {
315315+ internal_release_type = S_NO;
316316+ }
317317+318318+ xpc_release(system_version_plist);
319319+ } else {
320320+ internal_release_type = (access(INTERNAL_SETTINGS_PATH, F_OK) == 0) ? S_YES : S_NO;
321321+ }
322322+ }
323323+324324+ return status2bool(internal_release_type);
325325+#endif // TARGET_OS_SIMULATOR
326326+}
327327+328328+#else
329329+330330+static bool _check_internal_diags_profile() {
331331+ static enum check_status internal_diags_profile = S_UNKNOWN;
332332+333333+ if (internal_diags_profile == S_UNKNOWN) {
334334+ xpc_object_t profile_settings = read_plist(INTERNAL_DIAGS_PROFILE_PATH);
335335+ if (profile_settings) {
336336+ internal_diags_profile = xpc_dictionary_get_bool(profile_settings, "AppleInternal") ? S_YES : S_NO;
337337+ xpc_release(profile_settings);
338338+ } else {
339339+ internal_diags_profile = S_NO;
340340+ }
341341+ }
342342+343343+ return status2bool(internal_diags_profile);
344344+}
345345+346346+#endif
347347+348348+static bool _check_can_has_debugger(void)
349349+{
350350+#if TARGET_OS_SIMULATOR
351351+ return _check_internal_content();
352352+#else
353353+ if (can_has_debugger == S_UNKNOWN) {
354354+#if TARGET_OS_IPHONE
355355+ can_has_debugger = *((uint32_t *)_COMM_PAGE_DEV_FIRM) ? S_YES : S_NO;
356356+#else
357357+ /*
358358+ * The comm page bit does exist on macOS, but also requires kernel
359359+ * debugging in the CSR configuration. We don't need to be that strict
360360+ * here.
361361+ */
362362+ can_has_debugger = (csr_check(CSR_ALLOW_APPLE_INTERNAL) == 0) ? S_YES : S_NO;
363363+#endif
364364+ }
365365+ return status2bool(can_has_debugger);
366366+#endif // TARGET_OS_SIMULATOR
367367+}
368368+369369+// For unit tests
370370+#ifndef VARIANT_SKIP_EXPORTED
371371+372372+bool
373373+os_variant_has_internal_content(const char * __unused subsystem)
374374+{
375375+ if (_check_disabled(VP_CONTENT)) {
376376+ return false;
377377+ }
378378+379379+#if TARGET_OS_IPHONE
380380+ return _check_internal_release_type();
381381+#else
382382+ return _check_internal_content();
383383+#endif
384384+}
385385+386386+387387+bool
388388+os_variant_has_internal_diagnostics(const char * __unused subsystem)
389389+{
390390+ if (_check_disabled(VP_DIAGNOSTICS)) {
391391+ return false;
392392+ }
393393+394394+#if TARGET_OS_IPHONE
395395+ return _check_internal_release_type();
396396+#else
397397+ return _check_internal_content() || _check_internal_diags_profile();
398398+#endif
399399+}
400400+401401+bool
402402+os_variant_has_internal_ui(const char * __unused subsystem)
403403+{
404404+ if (_check_disabled(VP_UI)) {
405405+ return false;
406406+ }
407407+408408+#if TARGET_OS_IPHONE
409409+ return _check_internal_release_type();
410410+#else
411411+ return _check_internal_content();
412412+#endif
413413+}
414414+415415+bool
416416+os_variant_allows_internal_security_policies(const char * __unused subsystem)
417417+{
418418+ if (_check_disabled(VP_SECURITY)) {
419419+ return false;
420420+ }
421421+422422+ return _check_can_has_debugger();
423423+}
424424+425425+#endif // VARIANT_SKIP_EXPORTED
426426+427427+#define STATUS_INITIAL_BITS 0x70000000F0000000ULL
428428+#define STATUS_BIT_WIDTH 2
429429+#define STATUS_SET 0x2
430430+#define STATUS_MASK 0x3
431431+432432+enum status_flags_positions {
433433+ SFP_INTERNAL_CONTENT = 0,
434434+ SFP_CAN_HAS_DEBUGGER = 1,
435435+ SFP_INTERNAL_RELEASE_TYPE = 2,
436436+ SFP_INTERNAL_DIAGS_PROFILE = 3
437437+};
438438+439439+#if !TARGET_OS_SIMULATOR
440440+static uint64_t _get_cached_check_status(void)
441441+{
442442+ uint64_t res = STATUS_INITIAL_BITS;
443443+444444+#if !TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
445445+ _check_internal_content();
446446+ if (internal_content != S_UNKNOWN)
447447+ res |= internal_content << SFP_INTERNAL_CONTENT * STATUS_BIT_WIDTH;
448448+#endif
449449+450450+ _check_can_has_debugger();
451451+ if (can_has_debugger != S_UNKNOWN)
452452+ res |= can_has_debugger << SFP_CAN_HAS_DEBUGGER * STATUS_BIT_WIDTH;
453453+454454+#if TARGET_OS_IPHONE
455455+ _check_internal_release_type();
456456+ if (internal_release_type != S_UNKNOWN)
457457+ res |= internal_release_type << SFP_INTERNAL_RELEASE_TYPE * STATUS_BIT_WIDTH;
458458+#else
459459+ _check_internal_diags_profile();
460460+ if (internal_diags_profile != S_UNKNOWN)
461461+ res |= internal_diags_profile << SFP_INTERNAL_DIAGS_PROFILE * STATUS_BIT_WIDTH;
462462+#endif
463463+464464+ _parse_disabled_status(NULL);
465465+ for (int i = 0; i < VP_MAX; i++) {
466466+ if (disabled_status[i]) {
467467+ res |= 0x1ULL << (i + 32);
468468+ }
469469+ }
470470+471471+ return res;
472472+}
473473+474474+static void _restore_cached_check_status(uint64_t status)
475475+{
476476+#if !TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
477477+ if ((status >> (SFP_INTERNAL_CONTENT * STATUS_BIT_WIDTH)) & STATUS_SET)
478478+ internal_content = (status >> (SFP_INTERNAL_CONTENT * STATUS_BIT_WIDTH)) & STATUS_MASK;
479479+#endif
480480+ if ((status >> (SFP_CAN_HAS_DEBUGGER * STATUS_BIT_WIDTH)) & STATUS_SET)
481481+ can_has_debugger = (status >> (SFP_CAN_HAS_DEBUGGER * STATUS_BIT_WIDTH)) & STATUS_MASK;
482482+#if TARGET_OS_IPHONE
483483+ if ((status >> (SFP_INTERNAL_RELEASE_TYPE * STATUS_BIT_WIDTH)) & STATUS_SET)
484484+ internal_release_type = (status >> (SFP_INTERNAL_RELEASE_TYPE * STATUS_BIT_WIDTH)) & STATUS_MASK;
485485+#else
486486+ if ((status >> (SFP_INTERNAL_DIAGS_PROFILE * STATUS_BIT_WIDTH)) & STATUS_SET)
487487+ internal_diags_profile = (status >> (SFP_INTERNAL_DIAGS_PROFILE * STATUS_BIT_WIDTH)) & STATUS_MASK;
488488+#endif
489489+490490+ for (int i = 0; i < VP_MAX; i++) {
491491+ disabled_status[i] = (status >> (32 + i)) & 0x1;
492492+ }
493493+}
494494+#endif // !TARGET_OS_SIMULATOR
+140
src/libc/include/os/variant_private.h
···11+/*
22+ * Copyright (c) 2017 Apple Inc. All rights reserved.
33+ *
44+ * @APPLE_APACHE_LICENSE_HEADER_START@
55+ *
66+ * Licensed under the Apache License, Version 2.0 (the "License");
77+ * you may not use this file except in compliance with the License.
88+ * You may obtain a copy of the License at
99+ *
1010+ * http://www.apache.org/licenses/LICENSE-2.0
1111+ *
1212+ * Unless required by applicable law or agreed to in writing, software
1313+ * distributed under the License is distributed on an "AS IS" BASIS,
1414+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1515+ * See the License for the specific language governing permissions and
1616+ * limitations under the License.
1717+ *
1818+ * @APPLE_APACHE_LICENSE_HEADER_END@
1919+ */
2020+2121+#ifndef __OS_VARIANT_H__
2222+#define __OS_VARIANT_H__
2323+2424+#include <stdbool.h>
2525+2626+#include <os/base.h>
2727+2828+/*! @header
2929+ * OS Variant SPI
3030+ *
3131+ * Provides a mechanism to determine the currently running OS variant.
3232+ *
3333+ * Any of these APIs may be overridden to its non-internal behavior on a
3434+ * device by creating on override file. On macOS, this file is placed
3535+ * at:
3636+ * /var/db/os_variant_override
3737+ * On embedded platforms, this file is placed at:
3838+ * /usr/share/misc/os_variant_override
3939+ *
4040+ * Individual internal behaviors can be selectively disabled (ie.
4141+ * individual os_variant_has_internal_*() predicates can be overriden to
4242+ * false) by writing the file with a comma- or newline-delimited list of
4343+ * names to disable. To disable all internal behaviors, empty the file.
4444+ *
4545+ * Each of these functions takes a constant string argument for the requesting
4646+ * subsystem. This should be a reverse-DNS string describing the subsystem
4747+ * performing the check. This may be used in the future for auditing and
4848+ * selective overriding of checks.
4949+ *
5050+ */
5151+5252+__BEGIN_DECLS
5353+5454+/*!
5555+ * @function os_variant_has_internal_content
5656+ *
5757+ * @abstract returns whether this system variant has internal content installed
5858+ * ("content")
5959+ *
6060+ * @result
6161+ * Returns true if this build has this property. False otherwise or upon error.
6262+ */
6363+OS_EXPORT OS_WARN_RESULT
6464+bool
6565+os_variant_has_internal_content(const char *subsystem);
6666+6767+/*!
6868+ * @function os_variant_has_internal_diagnostics
6969+ *
7070+ * @abstract returns whether this system variant has internal diagnostics
7171+ * enabled ("diagnostics")
7272+ *
7373+ * @description
7474+ *
7575+ * Internal diagnostics include behaviors that emit extra diagnostic or
7676+ * debugging information when an error occurs.
7777+ *
7878+ * On macOS, this check will look for presence of AppleInternal content or the
7979+ * AppleInternalDiagnostics profile to be installed.
8080+ *
8181+ * On embedded platforms, this check will look for an internal install variant
8282+ * in a manner similar to the MobileGestalt check for InternalBuild.
8383+ *
8484+ * @result
8585+ * Returns true if this build has this property. False otherwise or upon error.
8686+ */
8787+OS_EXPORT OS_WARN_RESULT
8888+bool
8989+os_variant_has_internal_diagnostics(const char *subsystem);
9090+9191+/*!
9292+ * @function os_variant_has_internal_ui
9393+ *
9494+ * @abstract returns whether this system variant has internal UI visible ("ui")
9595+ *
9696+ * @description
9797+ *
9898+ * Internal UI includes debug menus and internal settings.
9999+ *
100100+ * On macOS, this will check for the presence of AppleInternal content. On
101101+ * embedded platforms, this check will look for an internal install variant in
102102+ * a manor similar to the MobileGestalt check for InternalBuild.
103103+ *
104104+ * @result
105105+ * Returns true if this build has this property. False otherwise or upon error.
106106+ */
107107+OS_EXPORT OS_WARN_RESULT
108108+bool
109109+os_variant_has_internal_ui(const char *subsystem);
110110+111111+/*!
112112+ * @function os_variant_allows_internal_security_policies
113113+ *
114114+ * @abstract returns whether this system variant allows internal security policies
115115+ * ("security")
116116+ *
117117+ * @description
118118+ *
119119+ * On macOS, this will check the CSR status for whether AppleInternal policies
120120+ * are enabled.
121121+ *
122122+ * On embedded platforms, this will check for a build/device combination that
123123+ * allows for removal of codesigning and debugging restrictions. This usually
124124+ * returns whether the hardware is development fused and may return true on
125125+ * such hardware even if a customer build is installed.
126126+ *
127127+ * n.b. The result of this API should /not/ be used to automatically enable
128128+ * relaxed security policies, only to signal that other mechanisms to enable
129129+ * them are allowed, e.g. a "defaults write".
130130+ *
131131+ * @result
132132+ * Returns true if this build has this property. False otherwise or upon error.
133133+ */
134134+OS_EXPORT OS_WARN_RESULT
135135+bool
136136+os_variant_allows_internal_security_policies(const char *subsystem);
137137+138138+__END_DECLS
139139+140140+#endif // __os_variant_H__