/*
* Copyright (c) 2022 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* The contents of this file constitute Original Code as defined in and
* are subject to the Apple Public Source License Version 1.1 (the
* "License"). You may not use this file except in compliance with the
* License. Please obtain a copy of the License at
* http://www.apple.com/publicsource and read it before using this file.
*
* This Original Code and all software distributed under the License are
* distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
* License for the specific language governing rights and limitations
* under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
#include <os/overflow.h>
#include <machine/atomic.h>
#include <mach/vm_param.h>
#include <vm/vm_kern.h>
#include <kern/zalloc.h>
#include <kern/kalloc.h>
#include <kern/assert.h>
#include <kern/locks.h>
#include <kern/lock_rw.h>
#include <libkern/libkern.h>
#include <libkern/section_keywords.h>
#include <libkern/coretrust/coretrust.h>
#include <pexpert/pexpert.h>
#include <sys/vm.h>
#include <sys/proc.h>
#include <sys/codesign.h>
#include <sys/code_signing.h>
#include <uuid/uuid.h>
#include <IOKit/IOBSD.h>
#if !CODE_SIGNING_MONITOR
/*
* We don't have a monitor environment available. This means someone with a kernel
* memory exploit will be able to corrupt code signing state. There is not much we
* can do here, since this is older HW.
*/
LCK_GRP_DECLARE(xnu_codesigning_lck_grp, "xnu_codesigning_lck_grp");
#pragma mark Initialization
static decl_lck_mtx_data(, compilation_service_lock);
void
code_signing_init()
{
/* Initialize compilation service lock */
lck_mtx_init(&compilation_service_lock, &xnu_codesigning_lck_grp, 0);
}
kern_return_t
xnu_secure_channel_shared_page(
__unused uint64_t *secure_channel_phys,
__unused size_t *secure_channel_size)
{
return KERN_NOT_SUPPORTED;
}
#pragma mark Developer Mode
static bool developer_mode_storage = true;
SECURITY_READ_ONLY_LATE(bool*) developer_mode_enabled = &developer_mode_storage;
void
xnu_toggle_developer_mode(
bool state)
{
/* No extra validation needed within XNU */
os_atomic_store(developer_mode_enabled, state, relaxed);
}
#pragma mark Restricted Execution Mode
kern_return_t
xnu_rem_enable(void)
{
return KERN_NOT_SUPPORTED;
}
kern_return_t
xnu_rem_state(void)
{
return KERN_NOT_SUPPORTED;
}
#pragma mark Device State
void
xnu_update_device_state(void)
{
/* Does nothing */
}
void
xnu_complete_security_boot_mode(
__unused uint32_t security_boot_mode)
{
/* Does nothing */
}
#pragma mark Code Signing
static uint8_t compilation_service_cdhash[CS_CDHASH_LEN] = {0};
void
xnu_set_compilation_service_cdhash(
const uint8_t cdhash[CS_CDHASH_LEN])
{
lck_mtx_lock(&compilation_service_lock);
memcpy(compilation_service_cdhash, cdhash, CS_CDHASH_LEN);
lck_mtx_unlock(&compilation_service_lock);
}
bool
xnu_match_compilation_service_cdhash(
const uint8_t cdhash[CS_CDHASH_LEN])
{
bool match = false;
lck_mtx_lock(&compilation_service_lock);
if (bcmp(compilation_service_cdhash, cdhash, CS_CDHASH_LEN) == 0) {
match = true;
}
lck_mtx_unlock(&compilation_service_lock);
return match;
}
static bool local_signing_key_set = false;
static uint8_t local_signing_public_key[XNU_LOCAL_SIGNING_KEY_SIZE] = {0};
void
xnu_set_local_signing_public_key(
const uint8_t public_key[XNU_LOCAL_SIGNING_KEY_SIZE])
{
bool key_set = false;
/*
* os_atomic_cmpxchg returns true in case the exchange was successful. For us,
* a successful exchange means that the local signing public key has _not_ been
* set. In case the key has been set, we panic as we would never expect the
* kernel to attempt to set the key more than once.
*/
key_set = !os_atomic_cmpxchg(&local_signing_key_set, false, true, relaxed);
if (key_set) {
panic("attempted to set the local signing public key multiple times");
}
memcpy(local_signing_public_key, public_key, sizeof(local_signing_public_key));
}
uint8_t*
xnu_get_local_signing_public_key(void)
{
bool key_set = os_atomic_load(&local_signing_key_set, relaxed);
if (key_set) {
return local_signing_public_key;
}
return NULL;
}
#pragma mark Image4
static uint8_t __attribute__((aligned(8)))
_xnu_image4_storage[IMG4_PMAP_DATA_SIZE_RECOMMENDED] = {0};
void*
xnu_image4_storage_data(
size_t *allocated_size)
{
if (allocated_size) {
*allocated_size = sizeof(_xnu_image4_storage);
}
return _xnu_image4_storage;
}
void
xnu_image4_set_nonce(
const img4_nonce_domain_index_t ndi,
const img4_nonce_t *nonce)
{
/*
* As a hold over from legacy code, AppleImage4 only ever manages nonces
* from the kernel interface through the PMAP_CS runtime. So even though
* we don't have a PMAP_CS monitor, we still pass in the PMAP_CS runtime.
*/
IMG4_RUNTIME_PMAP_CS->i4rt_set_nonce(
IMG4_RUNTIME_PMAP_CS,
ndi,
nonce);
}
void
xnu_image4_roll_nonce(
const img4_nonce_domain_index_t ndi)
{
/*
* As a hold over from legacy code, AppleImage4 only ever manages nonces
* from the kernel interface through the PMAP_CS runtime. So even though
* we don't have a PMAP_CS monitor, we still pass in the PMAP_CS runtime.
*/
IMG4_RUNTIME_PMAP_CS->i4rt_roll_nonce(
IMG4_RUNTIME_PMAP_CS,
ndi);
}
errno_t
xnu_image4_copy_nonce(
const img4_nonce_domain_index_t ndi,
img4_nonce_t *nonce_out)
{
errno_t ret = EPERM;
/*
* As a hold over from legacy code, AppleImage4 only ever manages nonces
* from the kernel interface through the PMAP_CS runtime. So even though
* we don't have a PMAP_CS monitor, we still pass in the PMAP_CS runtime.
*/
ret = IMG4_RUNTIME_PMAP_CS->i4rt_copy_nonce(
IMG4_RUNTIME_PMAP_CS,
ndi,
nonce_out);
if (ret != 0) {
printf("unable to copy image4 nonce: %llu | %d\n", ndi, ret);
}
return ret;
}
errno_t
xnu_image4_execute_object(
img4_runtime_object_spec_index_t obj_spec_index,
const img4_buff_t *payload,
const img4_buff_t *manifest)
{
errno_t ret = EPERM;
const img4_runtime_object_spec_t *obj_spec = NULL;
obj_spec = image4_get_object_spec_from_index(obj_spec_index);
if (obj_spec == NULL) {
return ENOENT;
}
/*
* As a hold over from legacy code, AppleImage4 only ever executes objects
* through the kernel interface through the PMAP_CS runtime. So even though
* we don't have a PMAP_CS monitor, we still pass in the PMAP_CS runtime.
*/
ret = img4_runtime_execute_object(
IMG4_RUNTIME_PMAP_CS,
obj_spec,
payload,
manifest);
if (ret != 0) {
printf("unable to execute image4 object: %d\n", ret);
}
return ret;
}
errno_t
xnu_image4_copy_object(
img4_runtime_object_spec_index_t obj_spec_index,
vm_address_t object_out,
size_t *object_length)
{
errno_t ret = EPERM;
img4_buff_t object_payload = IMG4_BUFF_INIT;
size_t object_payload_length = 0;
const img4_runtime_object_spec_t *obj_spec = NULL;
obj_spec = image4_get_object_spec_from_index(obj_spec_index);
if (obj_spec == NULL) {
return ENOENT;
}
/*
* The object length is used as an in/out parameter, so we require that this parameter
* is used to specify the length of the buffer.
*/
object_payload_length = *object_length;
object_payload.i4b_bytes = (void*)object_out;
object_payload.i4b_len = object_payload_length;
/*
* As a hold over from legacy code, AppleImage4 only ever copies objects
* through the kernel interface through the PMAP_CS runtime. So even though
* we don't have a PMAP_CS monitor, we still pass in the PMAP_CS runtime.
*/
ret = img4_runtime_copy_object(
IMG4_RUNTIME_PMAP_CS,
obj_spec,
&object_payload,
&object_payload_length);
if (ret != 0) {
printf("unable to copy image4 object: %d\n", ret);
}
/* Update the length with what we received from the image4 runtime */
*object_length = object_payload_length;
return ret;
}
const void*
xnu_image4_get_monitor_exports(void)
{
printf("monitor exports not supported without a monitor\n");
return NULL;
}
errno_t
xnu_image4_set_release_type(
__unused const char *release_type)
{
/*
* We don't need to inform the monitor about the release type when there
* is no monitor environment available.
*/
printf("explicit release-type-set not supported without a monitor\n");
return ENOTSUP;
}
errno_t
xnu_image4_set_bnch_shadow(
__unused const img4_nonce_domain_index_t ndi)
{
/*
* We don't need to inform the monitor about the BNCH shadow when there
* is no monitor environment available.
*/
printf("explicit BNCH-shadow-set not supported without a monitor\n");
return ENOTSUP;
}
#pragma mark Image4 - New
kern_return_t
xnu_image4_transfer_region(
image4_cs_trap_t selector,
__unused vm_address_t region_addr,
__unused vm_size_t region_size)
{
panic("image4 dispatch: transfer without code signing monitor: %llu", selector);
}
kern_return_t
xnu_image4_reclaim_region(
image4_cs_trap_t selector,
__unused vm_address_t region_addr,
__unused vm_size_t region_size)
{
panic("image4 dispatch: reclaim without code signing monitor: %llu", selector);
}
errno_t
xnu_image4_monitor_trap(
image4_cs_trap_t selector,
__unused const void *input_data,
__unused size_t input_size)
{
panic("image4 dispatch: trap without code signing monitor: %llu", selector);
}
#endif /* !CODE_SIGNING_MONITOR */