#include <sys/systm.h>
#include <sys/types.h>
+#if CONFIG_CSR_FROM_DT
+
+/*
+ * New style CSR for non-x86 platforms, using Per-OS Security Policy
+ * (POSP)
+ */
+
+#include <libkern/section_keywords.h>
+#include <pexpert/device_tree.h>
+
+#if defined(KERNEL_INTEGRITY_KTRR) || defined(KERNEL_INTEGRITY_CTRR)
+#include <arm64/amcc_rorgn.h>
+#endif
+
+static SECURITY_READ_ONLY_LATE(csr_config_t) csr_config = 0;
+
+// WARNING: Used extremely early during boot. See csr_bootstrap().
+static bool
+_csr_get_dt_bool(DTEntry *entry, char const *name, bool *out)
+{
+ const uint32_t *value;
+ unsigned int size;
+
+ if (SecureDTGetProperty(*entry, name, (const void**)&value, &size) != kSuccess) {
+ return false;
+ }
+
+ if (size != sizeof(uint32_t)) {
+ panic("unexpected size %xu for bool property '%s'", size, name);
+ }
+
+ *out = (bool)*value;
+ return true;
+}
+
+// WARNING: Used extremely early during boot. See csr_bootstrap().
+static bool
+_csr_get_dt_uint64(DTEntry *entry, char const *name, uint64_t *out)
+{
+ const uint64_t *value;
+ unsigned int size;
+
+ if (SecureDTGetProperty(*entry, name, (const void**)&value, &size) != kSuccess) {
+ return false;
+ }
+
+ if (size != sizeof(uint64_t)) {
+ panic("unexpected size %xu for uint64 property '%s'", size, name);
+ }
+
+ *out = *value;
+ return true;
+}
+
+// WARNING: Used extremely early during boot. See csr_bootstrap().
+static bool
+_csr_dt_string_is_equal(DTEntry *entry, const char *name, const char *str)
+{
+ const void *value;
+ unsigned size;
+ size_t str_size;
+
+ str_size = strlen(str) + 1;
+ return entry != NULL &&
+ SecureDTGetProperty(*entry, name, &value, &size) == kSuccess &&
+ value != NULL &&
+ size == str_size &&
+ strncmp(str, value, str_size) == 0;
+}
+
+static bool
+_csr_is_recovery_environment(void)
+{
+ DTEntry chosen;
+
+ return SecureDTLookupEntry(0, "/chosen", &chosen) == kSuccess &&
+ _csr_dt_string_is_equal(&chosen, "osenvironment", "recoveryos");
+}
+
+static bool
+_csr_is_iuou_or_iuos_device(void)
+{
+ DTEntry entry;
+ bool bool_value;
+
+ return (SecureDTLookupEntry(0, "/chosen", &entry) == kSuccess &&
+ (_csr_get_dt_bool(&entry, "internal-use-only-unit", &bool_value) && bool_value)) ||
+ (SecureDTLookupEntry(0, "/chosen/manifest-properties", &entry) == kSuccess &&
+ (_csr_get_dt_bool(&entry, "iuos", &bool_value) && bool_value));
+}
+
+static bool
+_csr_should_allow_device_configuration(void)
+{
+ /*
+ * Allow CSR_ALLOW_DEVICE_CONFIGURATION if the device is running in a
+ * restore environment, or if the "csr-allow-device-configuration"
+ * property is set in the device tree.
+ */
+ DTEntry chosen;
+ bool bool_value;
+
+ return _csr_is_recovery_environment() || (
+ SecureDTLookupEntry(0, "/chosen", &chosen) == kSuccess &&
+ _csr_get_dt_bool(&chosen, "csr-allow-device-configuration", &bool_value) && bool_value);
+}
+
+/*
+ * Initialize CSR from the Device Tree.
+ *
+ * WARNING: csr_bootstrap() is called extremely early in the kernel
+ * startup process in kernel_startup_bootstrap(), which happens
+ * before even the vm or pmap layer are initialized.
+ *
+ * It is marked as STARTUP_RANK_FIRST so that it is called before panic_init(),
+ * which runs during STARTUP_RANK_MIDDLE. This is necessary because panic_init()
+ * calls csr_check() to determine whether the device is configured to allow
+ * kernel debugging.
+ *
+ * Only do things here that don't require any dynamic memory (other
+ * than the stack). Parsing boot-args, walking the device tree and
+ * setting global variables is fine, most other things are not. Defer
+ * those other things with global variables, if necessary.
+ *
+ */
+__startup_func
+static void
+csr_bootstrap(void)
+{
+ DTEntry entry;
+ uint64_t uint64_value;
+ bool config_active = false;
+ bool bool_value;
+
+ csr_config = 0; // start out fully restrictive
+
+ if (SecureDTLookupEntry(0, "/chosen/asmb", &entry) == kSuccess &&
+ _csr_get_dt_uint64(&entry, "lp-sip0", &uint64_value)) {
+ csr_config = (uint32_t)uint64_value; // Currently only 32 bits used.
+ config_active = true;
+ }
+
+ /*
+ * If the device is an Internal Use Only Unit (IUOU) or if it is running a
+ * build that is signed with the Internal Use Only Software (IUOS) tag, then
+ * allow the preservation of the CSR_ALLOW_APPLE_INTERNAL bit. Otherwise,
+ * forcefully remove the bit on boot.
+ */
+ if (!_csr_is_iuou_or_iuos_device()) {
+ csr_config &= ~CSR_ALLOW_APPLE_INTERNAL;
+ } else if (!config_active) {
+ // If there is no custom configuration present, infer the AppleInternal
+ // bit on IUOU or IUOS devices.
+ csr_config |= CSR_ALLOW_APPLE_INTERNAL;
+ }
+
+ if (_csr_should_allow_device_configuration()) {
+ csr_config |= CSR_ALLOW_DEVICE_CONFIGURATION;
+ }
+
+ // The CSR_ALLOW_UNAUTHENTICATED_ROOT flag must be synthesized from sip1
+ // in the local boot policy.
+ if (_csr_get_dt_bool(&entry, "lp-sip1", &bool_value) && bool_value) {
+ csr_config |= CSR_ALLOW_UNAUTHENTICATED_ROOT;
+ } else {
+ csr_config &= ~CSR_ALLOW_UNAUTHENTICATED_ROOT;
+ }
+
+#if defined(KERNEL_INTEGRITY_KTRR) || defined(KERNEL_INTEGRITY_CTRR)
+ // Check whether we have to disable CTRR.
+ // lp-sip2 in the local boot policy is the bit driving this,
+ // which csrutil also sets implicitly when e.g. requesting kernel debugging.
+ csr_unsafe_kernel_text = _csr_get_dt_bool(&entry, "lp-sip2", &bool_value) && bool_value;
+#endif
+}
+STARTUP(TUNABLES, STARTUP_RANK_FIRST, csr_bootstrap);
+
+int
+csr_get_active_config(csr_config_t * config)
+{
+ *config = (csr_config & CSR_VALID_FLAGS);
+
+ return 0;
+}
+
+int
+csr_check(csr_config_t mask)
+{
+ csr_config_t config;
+ int ret = csr_get_active_config(&config);
+
+ if (ret != 0) {
+ return ret;
+ }
+
+ // CSR_ALLOW_KERNEL_DEBUGGER needs to be allowed when SIP is disabled
+ // to allow 3rd-party developers to debug their kexts. Use
+ // CSR_ALLOW_UNTRUSTED_KEXTS as a proxy for "SIP is disabled" on the
+ // grounds that you can do the same damage with a kernel debugger as
+ // you can with an untrusted kext.
+ if ((config & (CSR_ALLOW_UNTRUSTED_KEXTS | CSR_ALLOW_APPLE_INTERNAL)) != 0) {
+ config |= CSR_ALLOW_KERNEL_DEBUGGER;
+ }
+
+ return ((config & mask) == mask) ? 0 : EPERM;
+}
+
+#else
+
+/*
+ * Old style CSR for x86 platforms, using NVRAM values
+ */
+
+#include <libkern/section_keywords.h>
+
/* enable enforcement by default */
-static int csr_allow_all = 0;
+static SECURITY_READ_ONLY_LATE(int) csr_allow_all = 0;
-void
-csr_init(void)
+/*
+ * Initialize csr_allow_all from device boot state.
+ *
+ * Needs to be run before panic_init() since panic_init()
+ * calls into csr_check() and runs during STARTUP_RANK_MIDDLE.
+ */
+__startup_func
+static void
+csr_bootstrap(void)
{
boot_args *args = (boot_args *)PE_state.bootArgs;
if (args->flags & kBootArgsFlagCSRBoot) {
csr_allow_all = 1;
}
}
+STARTUP(TUNABLES, STARTUP_RANK_FIRST, csr_bootstrap);
+
int
csr_get_active_config(csr_config_t *config)
csr_check(csr_config_t mask)
{
boot_args *args = (boot_args *)PE_state.bootArgs;
- if ((mask & CSR_ALLOW_DEVICE_CONFIGURATION) && !(args->flags & kBootArgsFlagCSRConfigMode))
- return EPERM;
-
- if (csr_allow_all) {
- return 0;
+ if (mask & CSR_ALLOW_DEVICE_CONFIGURATION) {
+ return (args->flags & kBootArgsFlagCSRConfigMode) ? 0 : EPERM;
}
csr_config_t config;
- int error = csr_get_active_config(&config);
- if (error) {
- return error;
+ int ret = csr_get_active_config(&config);
+ if (ret) {
+ return ret;
}
- if (mask == 0) {
- /* pass 0 to check if Rootless enforcement is active */
- return -1;
+ // CSR_ALLOW_KERNEL_DEBUGGER needs to be allowed when SIP is disabled
+ // to allow 3rd-party developers to debug their kexts. Use
+ // CSR_ALLOW_UNTRUSTED_KEXTS as a proxy for "SIP is disabled" on the
+ // grounds that you can do the same damage with a kernel debugger as
+ // you can with an untrusted kext.
+ if ((config & (CSR_ALLOW_UNTRUSTED_KEXTS | CSR_ALLOW_APPLE_INTERNAL)) != 0) {
+ config |= CSR_ALLOW_KERNEL_DEBUGGER;
}
- error = (config & mask) ? 0 : EPERM;
- return error;
+ ret = ((config & mask) == mask) ? 0 : EPERM;
+ if (ret == EPERM) {
+ // Override the return value if booted from the BaseSystem and the mask does not contain any flag that should always be enforced.
+ if (csr_allow_all && (mask & CSR_ALWAYS_ENFORCED_FLAGS) == 0) {
+ ret = 0;
+ }
+ }
+
+ return ret;
}
+#endif /* CONFIG_CSR_FROM_DT */
+
/*
* Syscall stubs
*/
csr_config_t mask = 0;
int error = 0;
- if (args->useraddr == 0 || args->usersize != sizeof(mask))
+ if (args->useraddr == 0 || args->usersize != sizeof(mask)) {
return EINVAL;
+ }
error = copyin(args->useraddr, &mask, sizeof(mask));
- if (error)
+ if (error) {
return error;
+ }
return csr_check(mask);
}
csr_config_t config = 0;
int error = 0;
- if (args->useraddr == 0 || args->usersize != sizeof(config))
+ if (args->useraddr == 0 || args->usersize != sizeof(config)) {
return EINVAL;
+ }
error = csr_get_active_config(&config);
- if (error)
+ if (error) {
return error;
+ }
return copyout(&config, args->useraddr, sizeof(config));
}
csrctl(__unused proc_t p, struct csrctl_args *args, __unused int32_t *retval)
{
switch (args->op) {
- case CSR_SYSCALL_CHECK:
- return syscall_csr_check(args);
- case CSR_SYSCALL_GET_ACTIVE_CONFIG:
- return syscall_csr_get_active_config(args);
- default:
- return ENOSYS;
+ case CSR_SYSCALL_CHECK:
+ return syscall_csr_check(args);
+ case CSR_SYSCALL_GET_ACTIVE_CONFIG:
+ return syscall_csr_get_active_config(args);
+ default:
+ return ENOSYS;
}
}