1923 lines
40 KiB
C
1923 lines
40 KiB
C
/*
|
|
* Copyright (c) 2000-2012 Apple Inc. All rights reserved.
|
|
*
|
|
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
|
|
*
|
|
* This file contains Original Code and/or Modifications of Original Code
|
|
* as defined in and that are subject to the Apple Public Source License
|
|
* Version 2.0 (the 'License'). You may not use this file except in
|
|
* compliance with the License. The rights granted to you under the License
|
|
* may not be used to create, or enable the creation or redistribution of,
|
|
* unlawful or unlicensed copies of an Apple operating system, or to
|
|
* circumvent, violate, or enable the circumvention or violation of, any
|
|
* terms of an Apple operating system software license agreement.
|
|
*
|
|
* Please obtain a copy of the License at
|
|
* http://www.opensource.apple.com/apsl/ and read it before using this file.
|
|
*
|
|
* The 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, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
|
* Please see the License for the specific language governing rights and
|
|
* limitations under the License.
|
|
*
|
|
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/proc_internal.h>
|
|
#include <sys/sysctl.h>
|
|
#include <sys/signal.h>
|
|
#include <sys/signalvar.h>
|
|
#include <sys/codesign.h>
|
|
#include <sys/code_signing.h>
|
|
|
|
#include <sys/fcntl.h>
|
|
#include <sys/file.h>
|
|
#include <sys/file_internal.h>
|
|
#include <sys/kauth.h>
|
|
#include <sys/mount.h>
|
|
#include <sys/msg.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/socketvar.h>
|
|
#include <sys/vnode.h>
|
|
#include <sys/vnode_internal.h>
|
|
|
|
#include <sys/ubc.h>
|
|
#include <sys/ubc_internal.h>
|
|
|
|
#include <security/mac.h>
|
|
#include <security/mac_policy.h>
|
|
#include <security/mac_framework.h>
|
|
|
|
#include <mach/mach_types.h>
|
|
#include <mach/vm_map.h>
|
|
#include <mach/mach_vm.h>
|
|
|
|
#include <kern/kern_types.h>
|
|
#include <kern/startup.h>
|
|
#include <kern/task.h>
|
|
|
|
#include <vm/vm_map.h>
|
|
#include <vm/pmap.h>
|
|
#include <vm/vm_kern.h>
|
|
|
|
|
|
#include <kern/assert.h>
|
|
#include <kern/cs_blobs.h>
|
|
|
|
#include <pexpert/pexpert.h>
|
|
|
|
#include <mach/shared_region.h>
|
|
|
|
#include <libkern/section_keywords.h>
|
|
#include <libkern/amfi/amfi.h>
|
|
|
|
|
|
unsigned long cs_procs_killed = 0;
|
|
unsigned long cs_procs_invalidated = 0;
|
|
|
|
int cs_force_kill = 0;
|
|
int cs_force_hard = 0;
|
|
int cs_debug = 0;
|
|
// If set, AMFI will error out early on unsigned code, before evaluation the normal policy.
|
|
int cs_debug_fail_on_unsigned_code = 0;
|
|
// If the previous mode is enabled, we count the resulting failures here.
|
|
unsigned int cs_debug_unsigned_exec_failures = 0;
|
|
unsigned int cs_debug_unsigned_mmap_failures = 0;
|
|
|
|
#if CONFIG_ENFORCE_SIGNED_CODE
|
|
#define DEFAULT_CS_SYSTEM_ENFORCEMENT_ENABLE 1
|
|
#define DEFAULT_CS_PROCESS_ENFORCEMENT_ENABLE 1
|
|
#else
|
|
#define DEFAULT_CS_SYSTEM_ENFORCEMENT_ENABLE 1
|
|
#define DEFAULT_CS_PROCESS_ENFORCEMENT_ENABLE 0
|
|
#endif
|
|
|
|
#if CONFIG_ENFORCE_LIBRARY_VALIDATION
|
|
#define DEFAULT_CS_LIBRARY_VA_ENABLE 1
|
|
#else
|
|
#define DEFAULT_CS_LIBRARY_VA_ENABLE 0
|
|
#endif
|
|
|
|
#if SECURE_KERNEL
|
|
|
|
/*
|
|
* Here we split cs_enforcement_enable into cs_system_enforcement_enable and cs_process_enforcement_enable
|
|
*
|
|
* cs_system_enforcement_enable governs whether or not system level code signing enforcement mechanisms
|
|
* are applied on the system. Today, the only such mechanism is code signing enforcement of the dyld shared
|
|
* cache.
|
|
*
|
|
* cs_process_enforcement_enable governs whether code signing enforcement mechanisms are applied to all
|
|
* processes or only those that opt into such enforcement.
|
|
*
|
|
* (On iOS and related, both of these are set by default. On macOS, only cs_system_enforcement_enable
|
|
* is set by default. Processes can then be opted into code signing enforcement on a case by case basis.)
|
|
*/
|
|
SECURITY_READ_ONLY_EARLY(int) cs_system_enforcement_enable = DEFAULT_CS_SYSTEM_ENFORCEMENT_ENABLE;
|
|
SECURITY_READ_ONLY_EARLY(int) cs_process_enforcement_enable = DEFAULT_CS_PROCESS_ENFORCEMENT_ENABLE;
|
|
SECURITY_READ_ONLY_EARLY(int) cs_library_val_enable = DEFAULT_CS_LIBRARY_VA_ENABLE;
|
|
|
|
#else /* !SECURE_KERNEL */
|
|
int cs_enforcement_panic = 0;
|
|
int cs_relax_platform_task_ports = 0;
|
|
|
|
SECURITY_READ_ONLY_LATE(int) cs_system_enforcement_enable = DEFAULT_CS_SYSTEM_ENFORCEMENT_ENABLE;
|
|
SECURITY_READ_ONLY_LATE(int) cs_process_enforcement_enable = DEFAULT_CS_PROCESS_ENFORCEMENT_ENABLE;
|
|
|
|
SECURITY_READ_ONLY_LATE(int) cs_library_val_enable = DEFAULT_CS_LIBRARY_VA_ENABLE;
|
|
|
|
#endif /* !SECURE_KERNEL */
|
|
int cs_all_vnodes = 0;
|
|
|
|
SYSCTL_INT(_vm, OID_AUTO, cs_force_kill, CTLFLAG_RW | CTLFLAG_LOCKED, &cs_force_kill, 0, "");
|
|
SYSCTL_INT(_vm, OID_AUTO, cs_force_hard, CTLFLAG_RW | CTLFLAG_LOCKED, &cs_force_hard, 0, "");
|
|
SYSCTL_INT(_vm, OID_AUTO, cs_debug, CTLFLAG_RW | CTLFLAG_LOCKED, &cs_debug, 0, "");
|
|
SYSCTL_INT(_vm, OID_AUTO, cs_debug_fail_on_unsigned_code, CTLFLAG_RW | CTLFLAG_LOCKED,
|
|
&cs_debug_fail_on_unsigned_code, 0, "");
|
|
SYSCTL_UINT(_vm, OID_AUTO, cs_debug_unsigned_exec_failures, CTLFLAG_RD | CTLFLAG_LOCKED,
|
|
&cs_debug_unsigned_exec_failures, 0, "");
|
|
SYSCTL_UINT(_vm, OID_AUTO, cs_debug_unsigned_mmap_failures, CTLFLAG_RD | CTLFLAG_LOCKED,
|
|
&cs_debug_unsigned_mmap_failures, 0, "");
|
|
|
|
SYSCTL_INT(_vm, OID_AUTO, cs_all_vnodes, CTLFLAG_RW | CTLFLAG_LOCKED, &cs_all_vnodes, 0, "");
|
|
|
|
#if !SECURE_KERNEL
|
|
SYSCTL_INT(_vm, OID_AUTO, cs_system_enforcement, CTLFLAG_RD | CTLFLAG_LOCKED, &cs_system_enforcement_enable, 0, "");
|
|
SYSCTL_INT(_vm, OID_AUTO, cs_process_enforcement, CTLFLAG_RD | CTLFLAG_LOCKED, &cs_process_enforcement_enable, 0, "");
|
|
SYSCTL_INT(_vm, OID_AUTO, cs_enforcement_panic, CTLFLAG_RW | CTLFLAG_LOCKED, &cs_enforcement_panic, 0, "");
|
|
|
|
#if !CONFIG_ENFORCE_LIBRARY_VALIDATION
|
|
SYSCTL_INT(_vm, OID_AUTO, cs_library_validation, CTLFLAG_RD | CTLFLAG_LOCKED, &cs_library_val_enable, 0, "");
|
|
#endif
|
|
#endif /* !SECURE_KERNEL */
|
|
|
|
__startup_func
|
|
static void
|
|
cs_init(void)
|
|
{
|
|
#if !SECURE_KERNEL
|
|
int disable_cs_enforcement = 0;
|
|
PE_parse_boot_argn("cs_enforcement_disable", &disable_cs_enforcement,
|
|
sizeof(disable_cs_enforcement));
|
|
if (disable_cs_enforcement && PE_i_can_has_debugger(NULL) != 0) {
|
|
cs_system_enforcement_enable = 0;
|
|
cs_process_enforcement_enable = 0;
|
|
} else {
|
|
int panic = 0;
|
|
PE_parse_boot_argn("cs_enforcement_panic", &panic, sizeof(panic));
|
|
cs_enforcement_panic = (panic != 0);
|
|
}
|
|
|
|
PE_parse_boot_argn("cs_relax_platform_task_ports",
|
|
&cs_relax_platform_task_ports,
|
|
sizeof(cs_relax_platform_task_ports));
|
|
|
|
PE_parse_boot_argn("cs_debug", &cs_debug, sizeof(cs_debug));
|
|
|
|
#if !CONFIG_ENFORCE_LIBRARY_VALIDATION
|
|
PE_parse_boot_argn("cs_library_val_enable", &cs_library_val_enable,
|
|
sizeof(cs_library_val_enable));
|
|
#endif
|
|
#endif /* !SECURE_KERNEL */
|
|
}
|
|
STARTUP(CODESIGNING, STARTUP_RANK_FIRST, cs_init);
|
|
|
|
int
|
|
cs_allow_invalid(struct proc *p)
|
|
{
|
|
uint64_t flags;
|
|
|
|
#if MACH_ASSERT
|
|
lck_mtx_assert(&p->p_mlock, LCK_MTX_ASSERT_NOTOWNED);
|
|
#endif
|
|
#if CONFIG_MACF
|
|
/* There needs to be a MAC policy to implement this hook, or else the
|
|
* kill bits will be cleared here every time. If we have
|
|
* CONFIG_ENFORCE_SIGNED_CODE, we can assume there is a policy
|
|
* implementing the hook.
|
|
*/
|
|
if (0 != mac_proc_check_run_cs_invalid(p)) {
|
|
if (cs_debug) {
|
|
printf("CODE SIGNING: cs_allow_invalid() "
|
|
"not allowed: pid %d\n",
|
|
proc_getpid(p));
|
|
}
|
|
return 0;
|
|
}
|
|
if (cs_debug) {
|
|
printf("CODE SIGNING: cs_allow_invalid() "
|
|
"allowed: pid %d\n",
|
|
proc_getpid(p));
|
|
}
|
|
proc_lock(p);
|
|
flags = proc_getcsflags(p) & ~(CS_KILL | CS_HARD);
|
|
if (flags & CS_VALID) {
|
|
flags |= CS_DEBUGGED;
|
|
}
|
|
proc_csflags_update(p, flags);
|
|
|
|
task_t procTask = proc_task(p);
|
|
if (procTask) {
|
|
vm_map_t proc_map = get_task_map_reference(procTask);
|
|
if (proc_map) {
|
|
if (vm_map_cs_wx_enable(proc_map) != KERN_SUCCESS) {
|
|
printf("CODE SIGNING: cs_allow_invalid() not allowed by pmap: pid %d\n", proc_getpid(p));
|
|
}
|
|
vm_map_deallocate(proc_map);
|
|
}
|
|
}
|
|
|
|
proc_unlock(p);
|
|
|
|
/* allow a debugged process to hide some (debug-only!) memory */
|
|
task_set_memory_ownership_transfer(proc_task(p), TRUE);
|
|
|
|
vm_map_switch_protect(get_task_map(proc_task(p)), FALSE);
|
|
vm_map_cs_debugged_set(get_task_map(proc_task(p)), TRUE);
|
|
#endif
|
|
return (proc_getcsflags(p) & (CS_KILL | CS_HARD)) == 0;
|
|
}
|
|
|
|
int
|
|
cs_invalid_page(addr64_t vaddr, boolean_t *cs_killed)
|
|
{
|
|
struct proc *p;
|
|
int send_kill = 0, retval = 0, verbose = cs_debug;
|
|
uint64_t flags;
|
|
|
|
p = current_proc();
|
|
|
|
if (verbose) {
|
|
printf("CODE SIGNING: cs_invalid_page(0x%llx): p=%d[%s]\n",
|
|
vaddr, proc_getpid(p), p->p_comm);
|
|
}
|
|
|
|
proc_lock(p);
|
|
|
|
flags = proc_getcsflags(p);
|
|
|
|
/* XXX for testing */
|
|
if (cs_force_kill) {
|
|
flags |= CS_KILL;
|
|
}
|
|
if (cs_force_hard) {
|
|
flags |= CS_HARD;
|
|
}
|
|
|
|
/* CS_KILL triggers a kill signal, and no you can't have the page. Nothing else. */
|
|
if (flags & CS_KILL) {
|
|
flags |= CS_KILLED;
|
|
cs_procs_killed++;
|
|
send_kill = 1;
|
|
retval = 1;
|
|
}
|
|
|
|
/* CS_HARD means fail the mapping operation so the process stays valid. */
|
|
if (flags & CS_HARD) {
|
|
retval = 1;
|
|
proc_csflags_update(p, flags);
|
|
} else {
|
|
if (flags & CS_VALID) {
|
|
flags &= ~CS_VALID;
|
|
cs_procs_invalidated++;
|
|
verbose = 1;
|
|
proc_csflags_update(p, flags);
|
|
cs_process_invalidated(NULL);
|
|
} else {
|
|
proc_csflags_update(p, flags);
|
|
}
|
|
}
|
|
proc_unlock(p);
|
|
|
|
if (verbose) {
|
|
printf("CODE SIGNING: cs_invalid_page(0x%llx): "
|
|
"p=%d[%s] final status 0x%x, %s page%s\n",
|
|
vaddr, proc_getpid(p), p->p_comm, (unsigned int)proc_getcsflags(p),
|
|
retval ? "denying" : "allowing (remove VALID)",
|
|
send_kill ? " sending SIGKILL" : "");
|
|
}
|
|
|
|
if (send_kill) {
|
|
/* We will set the exit reason for the thread later */
|
|
threadsignal(current_thread(), SIGKILL, EXC_BAD_ACCESS, FALSE);
|
|
if (cs_killed) {
|
|
*cs_killed = TRUE;
|
|
}
|
|
} else if (cs_killed) {
|
|
*cs_killed = FALSE;
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
/*
|
|
* Called after a process got its CS_VALID bit removed, either by
|
|
* a previous call to cs_invalid_page, or through other means.
|
|
* Called from fault handler with vm object lock held.
|
|
* Called with proc lock held for current_proc or, if passed in, p,
|
|
* to ensure MACF hook can suspend the task before other threads
|
|
* can access the memory that is paged in after cs_invalid_page
|
|
* returns 0 due to missing CS_HARD|CS_KILL.
|
|
*/
|
|
void
|
|
cs_process_invalidated(struct proc * __unused p)
|
|
{
|
|
#if CONFIG_MACF
|
|
if (p == NULL) {
|
|
p = current_proc();
|
|
}
|
|
mac_proc_notify_cs_invalidated(p);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Assumes p (if passed in) is locked with proc_lock().
|
|
*/
|
|
|
|
int
|
|
cs_process_enforcement(struct proc *p)
|
|
{
|
|
if (cs_process_enforcement_enable) {
|
|
return 1;
|
|
}
|
|
|
|
if (p == NULL) {
|
|
p = current_proc();
|
|
}
|
|
|
|
if (p != NULL && (proc_getcsflags(p) & CS_ENFORCEMENT)) {
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
cs_process_global_enforcement(void)
|
|
{
|
|
return cs_process_enforcement_enable ? 1 : 0;
|
|
}
|
|
|
|
int
|
|
cs_system_enforcement(void)
|
|
{
|
|
return cs_system_enforcement_enable ? 1 : 0;
|
|
}
|
|
|
|
int
|
|
cs_vm_supports_4k_translations(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Returns whether a given process is still valid.
|
|
*/
|
|
int
|
|
cs_valid(struct proc *p)
|
|
{
|
|
if (p == NULL) {
|
|
p = current_proc();
|
|
}
|
|
|
|
if (p != NULL && (proc_getcsflags(p) & CS_VALID)) {
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Library validation functions
|
|
*/
|
|
int
|
|
cs_require_lv(struct proc *p)
|
|
{
|
|
if (cs_library_val_enable) {
|
|
return 1;
|
|
}
|
|
|
|
if (p == NULL) {
|
|
p = current_proc();
|
|
}
|
|
|
|
if (p != NULL && (proc_getcsflags(p) & CS_REQUIRE_LV)) {
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
csproc_forced_lv(struct proc* p)
|
|
{
|
|
if (p == NULL) {
|
|
p = current_proc();
|
|
}
|
|
if (p != NULL && (proc_getcsflags(p) & CS_FORCED_LV)) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* <rdar://problem/24634089> added to allow system level library
|
|
* validation check at mac_cred_label_update_execve time
|
|
*/
|
|
int
|
|
cs_system_require_lv(void)
|
|
{
|
|
return cs_library_val_enable ? 1 : 0;
|
|
}
|
|
|
|
/*
|
|
* Function: csblob_get_base_offset
|
|
*
|
|
* Description: This function returns the base offset into the (possibly universal) binary
|
|
* for a given blob.
|
|
*/
|
|
|
|
off_t
|
|
csblob_get_base_offset(struct cs_blob *blob)
|
|
{
|
|
return blob->csb_base_offset;
|
|
}
|
|
|
|
/*
|
|
* Function: csblob_get_size
|
|
*
|
|
* Description: This function returns the size of a given blob.
|
|
*/
|
|
|
|
vm_size_t
|
|
csblob_get_size(struct cs_blob *blob)
|
|
{
|
|
return blob->csb_mem_size;
|
|
}
|
|
|
|
/*
|
|
* Function: csblob_get_addr
|
|
*
|
|
* Description: This function returns the address of a given blob.
|
|
*/
|
|
|
|
vm_address_t
|
|
csblob_get_addr(struct cs_blob *blob)
|
|
{
|
|
return (vm_address_t)blob->csb_mem_kaddr;
|
|
}
|
|
|
|
/*
|
|
* Function: csblob_get_platform_binary
|
|
*
|
|
* Description: This function returns true if the binary is
|
|
* in the trust cache.
|
|
*/
|
|
|
|
int
|
|
csblob_get_platform_binary(struct cs_blob *blob)
|
|
{
|
|
if (blob && blob->csb_platform_binary) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Function: csblob_invalidate_flags
|
|
*
|
|
* Description: This function is used to clear the CS_VALID bit on a blob
|
|
* when a vnode may have been modified.
|
|
*
|
|
*/
|
|
void
|
|
csblob_invalidate_flags(struct cs_blob *csblob)
|
|
{
|
|
bool ro_blob = csblob == csblob->csb_ro_addr;
|
|
unsigned int current_flags = csblob->csb_flags;
|
|
unsigned int updated_flags = current_flags & (~CS_VALID);
|
|
if (ro_blob == true) {
|
|
zalloc_ro_update_field(ZONE_ID_CS_BLOB, csblob, csb_flags, &updated_flags);
|
|
} else {
|
|
csblob->csb_flags = updated_flags;
|
|
}
|
|
|
|
if (csblob->csb_entitlements != NULL) {
|
|
amfi->OSEntitlements_invalidate(csblob->csb_entitlements);
|
|
}
|
|
|
|
printf("Invalidated flags, old %x new %x\n", current_flags, csblob->csb_flags);
|
|
}
|
|
|
|
/*
|
|
* Function: csvnode_invalidate_flags
|
|
*
|
|
* Description: This function is used to clear the CS_VALID bit on all blobs
|
|
* attached to a vnode.
|
|
*
|
|
*/
|
|
void
|
|
csvnode_invalidate_flags(struct vnode *vp)
|
|
{
|
|
struct cs_blob* oblob;
|
|
bool mark_ubcinfo = false;
|
|
|
|
for (oblob = ubc_get_cs_blobs(vp);
|
|
oblob != NULL;
|
|
oblob = oblob->csb_next) {
|
|
if (!mark_ubcinfo) {
|
|
mark_ubcinfo = true;
|
|
vnode_lock(vp);
|
|
if (vp->v_ubcinfo) {
|
|
vp->v_ubcinfo->ui_flags |= UI_CSBLOBINVALID;
|
|
}
|
|
vnode_unlock(vp);
|
|
}
|
|
csblob_invalidate_flags(oblob);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function: csblob_get_flags
|
|
*
|
|
* Description: This function returns the flags for a given blob
|
|
*/
|
|
|
|
unsigned int
|
|
csblob_get_flags(struct cs_blob *blob)
|
|
{
|
|
return blob->csb_flags;
|
|
}
|
|
|
|
/*
|
|
* Function: csblob_get_hashtype
|
|
*
|
|
* Description: This function returns the hash type for a given blob
|
|
*/
|
|
|
|
uint8_t
|
|
csblob_get_hashtype(struct cs_blob const * const blob)
|
|
{
|
|
return blob->csb_hashtype != NULL ? cs_hash_type(blob->csb_hashtype) : 0;
|
|
}
|
|
|
|
/*
|
|
* Function: csproc_get_blob
|
|
*
|
|
* Description: This function returns the cs_blob
|
|
* for the process p
|
|
*/
|
|
struct cs_blob *
|
|
csproc_get_blob(struct proc *p)
|
|
{
|
|
if (NULL == p) {
|
|
return NULL;
|
|
}
|
|
|
|
if (NULL == p->p_textvp) {
|
|
return NULL;
|
|
}
|
|
|
|
if ((proc_getcsflags(p) & CS_SIGNED) == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
return ubc_cs_blob_get(p->p_textvp, -1, -1, p->p_textoff);
|
|
}
|
|
|
|
/*
|
|
* Function: csvnode_get_blob
|
|
*
|
|
* Description: This function returns the cs_blob
|
|
* for the vnode vp
|
|
*/
|
|
struct cs_blob *
|
|
csvnode_get_blob(struct vnode *vp, off_t offset)
|
|
{
|
|
return ubc_cs_blob_get(vp, -1, -1, offset);
|
|
}
|
|
|
|
/*
|
|
* Function: csblob_get_teamid
|
|
*
|
|
* Description: This function returns a pointer to the
|
|
* team id of csblob
|
|
*/
|
|
const char *
|
|
csblob_get_teamid(struct cs_blob *csblob)
|
|
{
|
|
return csblob->csb_teamid;
|
|
}
|
|
|
|
/*
|
|
* Function: csblob_get_identity
|
|
*
|
|
* Description: This function returns a pointer to the
|
|
* identity string
|
|
*/
|
|
const char *
|
|
csblob_get_identity(struct cs_blob *csblob)
|
|
{
|
|
const CS_CodeDirectory *cd;
|
|
|
|
cd = (const CS_CodeDirectory *)csblob_find_blob(csblob, CSSLOT_CODEDIRECTORY, CSMAGIC_CODEDIRECTORY);
|
|
if (cd == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (cd->identOffset == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
return ((const char *)cd) + ntohl(cd->identOffset);
|
|
}
|
|
|
|
/*
|
|
* Function: csblob_get_cdhash
|
|
*
|
|
* Description: This function returns a pointer to the
|
|
* cdhash of csblob (20 byte array)
|
|
*/
|
|
const uint8_t *
|
|
csblob_get_cdhash(struct cs_blob *csblob)
|
|
{
|
|
return csblob->csb_cdhash;
|
|
}
|
|
|
|
/*
|
|
* Function: csblob_get_signer_type
|
|
*
|
|
* Description: This function returns the signer type
|
|
* as an integer
|
|
*/
|
|
unsigned int
|
|
csblob_get_signer_type(struct cs_blob *csblob)
|
|
{
|
|
return csblob->csb_signer_type;
|
|
}
|
|
|
|
/*
|
|
* Function: csblob_set_validation_category
|
|
*
|
|
* Description: This function is used to set the validation
|
|
* category on a cs_blob. Can only be set once,
|
|
* except when set as none.
|
|
*
|
|
* Return: 0 on success, otherwise -1.
|
|
*/
|
|
int
|
|
csblob_set_validation_category(struct cs_blob *csblob, unsigned int category)
|
|
{
|
|
bool ro_blob = csblob == csblob->csb_ro_addr;
|
|
|
|
if ((csblob->csb_validation_category == CS_VALIDATION_CATEGORY_INVALID) ||
|
|
(csblob->csb_validation_category == CS_VALIDATION_CATEGORY_NONE)) {
|
|
if (ro_blob == true) {
|
|
zalloc_ro_update_field(ZONE_ID_CS_BLOB, csblob, csb_validation_category, &category);
|
|
} else {
|
|
csblob->csb_validation_category = category;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Always allow when setting to none */
|
|
if (category == CS_VALIDATION_CATEGORY_NONE) {
|
|
if (ro_blob == true) {
|
|
zalloc_ro_update_field(ZONE_ID_CS_BLOB, csblob, csb_validation_category, &category);
|
|
} else {
|
|
csblob->csb_validation_category = category;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Allow setting to the same category */
|
|
if (category == csblob->csb_validation_category) {
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Function: csblob_get_validation_category
|
|
*
|
|
* Description: This function is used to get the validation
|
|
* category on a cs_blob.
|
|
*/
|
|
unsigned int
|
|
csblob_get_validation_category(struct cs_blob *csblob)
|
|
{
|
|
return csblob->csb_validation_category;
|
|
}
|
|
|
|
/*
|
|
* Function: csblob_get_code_directory
|
|
*
|
|
* Description: This function returns the best code directory
|
|
* as chosen by the system
|
|
*/
|
|
const CS_CodeDirectory*
|
|
csblob_get_code_directory(struct cs_blob *csblob)
|
|
{
|
|
return csblob->csb_cd;
|
|
}
|
|
|
|
void *
|
|
csblob_entitlements_dictionary_copy(struct cs_blob *csblob)
|
|
{
|
|
if (!csblob->csb_entitlements) {
|
|
return NULL;
|
|
}
|
|
if (!amfi) {
|
|
panic("CoreEntitlements: missing AMFI bridge\n");
|
|
}
|
|
return amfi->OSEntitlements_asdict(csblob->csb_entitlements);
|
|
}
|
|
|
|
OS_NORETURN
|
|
void
|
|
csblob_entitlements_dictionary_set(struct cs_blob __unused *csblob, void __unused *entitlements)
|
|
{
|
|
panic("CoreEntitlements: This API is no longer supported\n");
|
|
}
|
|
|
|
void
|
|
csblob_os_entitlements_set(struct cs_blob *csblob, void * entitlements)
|
|
{
|
|
assert(csblob->csb_entitlements == NULL);
|
|
if (entitlements) {
|
|
osobject_retain(entitlements);
|
|
}
|
|
csblob->csb_entitlements = entitlements;
|
|
}
|
|
|
|
void *
|
|
csblob_os_entitlements_copy(struct cs_blob *csblob)
|
|
{
|
|
if (!csblob->csb_entitlements) {
|
|
return NULL;
|
|
}
|
|
osobject_retain(csblob->csb_entitlements);
|
|
return csblob->csb_entitlements;
|
|
}
|
|
|
|
void *
|
|
csblob_os_entitlements_get(struct cs_blob *csblob)
|
|
{
|
|
if (!csblob->csb_entitlements) {
|
|
return NULL;
|
|
}
|
|
return csblob->csb_entitlements;
|
|
}
|
|
|
|
void *
|
|
csblob_get_storage_addr(struct cs_blob *csblob)
|
|
{
|
|
void *addr = csblob->csb_ro_addr;
|
|
cs_blob_require((struct cs_blob *)addr, NULL);
|
|
return addr;
|
|
}
|
|
|
|
/*
|
|
* Function: csproc_get_teamid
|
|
*
|
|
* Description: This function returns a pointer to the
|
|
* team id of the process p
|
|
*/
|
|
const char *
|
|
csproc_get_teamid(struct proc *p)
|
|
{
|
|
struct cs_blob *csblob;
|
|
|
|
csblob = csproc_get_blob(p);
|
|
if (csblob == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return csblob_get_teamid(csblob);
|
|
}
|
|
|
|
const char *
|
|
csproc_get_identity(struct proc *p)
|
|
{
|
|
struct cs_blob *csblob = NULL;
|
|
|
|
csblob = csproc_get_blob(p);
|
|
if (csblob == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return csblob_get_identity(csblob);
|
|
}
|
|
|
|
/*
|
|
* Function: csproc_get_signer_type
|
|
*
|
|
* Description: This function returns the signer type
|
|
* of the process p
|
|
*/
|
|
unsigned int
|
|
csproc_get_signer_type(struct proc *p)
|
|
{
|
|
struct cs_blob *csblob;
|
|
|
|
csblob = csproc_get_blob(p);
|
|
if (csblob == NULL) {
|
|
return CS_SIGNER_TYPE_UNKNOWN;
|
|
}
|
|
|
|
return csblob_get_signer_type(csblob);
|
|
}
|
|
|
|
/*
|
|
* Function: csvnode_get_teamid
|
|
*
|
|
* Description: This function returns a pointer to the
|
|
* team id of the binary at the given offset in vnode vp
|
|
*/
|
|
const char *
|
|
csvnode_get_teamid(struct vnode *vp, off_t offset)
|
|
{
|
|
struct cs_blob *csblob;
|
|
|
|
if (vp == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
csblob = ubc_cs_blob_get(vp, -1, -1, offset);
|
|
if (csblob == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return csblob_get_teamid(csblob);
|
|
}
|
|
|
|
/*
|
|
* Function: csproc_get_platform_binary
|
|
*
|
|
* Description: This function returns the value
|
|
* of the platform_binary field for proc p
|
|
*/
|
|
int
|
|
csproc_get_platform_binary(struct proc *p)
|
|
{
|
|
struct cs_blob *csblob;
|
|
|
|
csblob = csproc_get_blob(p);
|
|
|
|
/* If there is no csblob this returns 0 because
|
|
* it is true that it is not a platform binary */
|
|
return (csblob == NULL) ? 0 : csblob->csb_platform_binary;
|
|
}
|
|
|
|
int
|
|
csproc_get_platform_path(struct proc *p)
|
|
{
|
|
struct cs_blob *csblob;
|
|
|
|
csblob = csproc_get_blob(p);
|
|
|
|
return (csblob == NULL) ? 0 : csblob->csb_platform_path;
|
|
}
|
|
|
|
#if DEVELOPMENT || DEBUG
|
|
void
|
|
csproc_clear_platform_binary(struct proc *p)
|
|
{
|
|
struct cs_blob *csblob = csproc_get_blob(p);
|
|
struct cs_blob_platform_flags platform_flags;
|
|
|
|
if (csblob == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (cs_debug) {
|
|
printf("clearing platform binary on proc/task: pid = %d\n", proc_getpid(p));
|
|
}
|
|
|
|
platform_flags = csblob->csb_platform_flags;
|
|
platform_flags.csb_platform_binary = 0;
|
|
platform_flags.csb_platform_path = 0;
|
|
|
|
zalloc_ro_update_field(ZONE_ID_CS_BLOB, csblob, csb_platform_flags,
|
|
&platform_flags);
|
|
|
|
task_set_platform_binary(proc_task(p), FALSE);
|
|
}
|
|
#endif
|
|
|
|
void
|
|
csproc_disable_enforcement(struct proc* __unused p)
|
|
{
|
|
#if !CONFIG_ENFORCE_SIGNED_CODE
|
|
if (p != NULL) {
|
|
proc_lock(p);
|
|
proc_csflags_clear(p, CS_ENFORCEMENT);
|
|
vm_map_cs_enforcement_set(get_task_map(proc_task(p)), FALSE);
|
|
proc_unlock(p);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Function: csproc_mark_invalid_allowed
|
|
*
|
|
* Description: Mark the process as being allowed to go invalid. Called as part of
|
|
* task_for_pid and ptrace policy. Note CS_INVALID_ALLOWED only matters for
|
|
* processes that have been opted into CS_ENFORCEMENT.
|
|
*/
|
|
void
|
|
csproc_mark_invalid_allowed(struct proc* __unused p)
|
|
{
|
|
#if !CONFIG_ENFORCE_SIGNED_CODE
|
|
if (p != NULL) {
|
|
proc_lock(p);
|
|
proc_csflags_set(p, CS_INVALID_ALLOWED);
|
|
proc_unlock(p);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Function: csproc_check_invalid_allowed
|
|
*
|
|
* Description: Returns 1 if the process has been marked as allowed to go invalid
|
|
* because it gave its task port to an allowed process.
|
|
*/
|
|
int
|
|
csproc_check_invalid_allowed(struct proc* __unused p)
|
|
{
|
|
#if !CONFIG_ENFORCE_SIGNED_CODE
|
|
if (p == NULL) {
|
|
p = current_proc();
|
|
}
|
|
|
|
if (p != NULL && (proc_getcsflags(p) & CS_INVALID_ALLOWED)) {
|
|
return 1;
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Function: csproc_get_prod_signed
|
|
*
|
|
* Description: Returns 1 if process is not signed with a developer identity.
|
|
* Note the inverted meaning from the cs_flag to make the error case safer.
|
|
* Will go away with rdar://problem/28322552.
|
|
*/
|
|
int
|
|
csproc_get_prod_signed(struct proc *p)
|
|
{
|
|
return (proc_getcsflags(p) & CS_DEV_CODE) == 0;
|
|
}
|
|
|
|
int
|
|
csproc_get_validation_category(struct proc *pt, unsigned int *out_validation_category)
|
|
{
|
|
struct cs_blob* blob = NULL;
|
|
unsigned int validation_category = CS_VALIDATION_CATEGORY_INVALID;
|
|
int error;
|
|
|
|
proc_lock(pt);
|
|
if ((proc_getcsflags(pt) & (CS_VALID | CS_DEBUGGED)) == 0) {
|
|
proc_unlock(pt);
|
|
error = EINVAL;
|
|
goto out;
|
|
}
|
|
blob = csproc_get_blob(pt);
|
|
proc_unlock(pt);
|
|
|
|
if (!blob) {
|
|
error = EBADEXEC;
|
|
goto out;
|
|
}
|
|
|
|
validation_category = csblob_get_validation_category(blob);
|
|
if (out_validation_category) {
|
|
*out_validation_category = validation_category;
|
|
}
|
|
error = KERN_SUCCESS;
|
|
out:
|
|
return error;
|
|
}
|
|
|
|
|
|
/*
|
|
* Function: csfg_get_platform_binary
|
|
*
|
|
* Description: This function returns the
|
|
* platform binary field for the
|
|
* fileglob fg
|
|
*/
|
|
int
|
|
csfg_get_platform_binary(struct fileglob *fg)
|
|
{
|
|
int platform_binary = 0;
|
|
struct ubc_info *uip;
|
|
vnode_t vp;
|
|
|
|
if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE) {
|
|
return 0;
|
|
}
|
|
|
|
vp = (struct vnode *)fg_get_data(fg);
|
|
if (vp == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
vnode_lock(vp);
|
|
if (!UBCINFOEXISTS(vp)) {
|
|
goto out;
|
|
}
|
|
|
|
uip = vp->v_ubcinfo;
|
|
if (uip == NULL) {
|
|
goto out;
|
|
}
|
|
|
|
if (uip->cs_blobs == NULL) {
|
|
goto out;
|
|
}
|
|
|
|
/* It is OK to extract the teamid from the first blob
|
|
* because all blobs of a vnode must have the same teamid */
|
|
platform_binary = uip->cs_blobs->csb_platform_binary;
|
|
out:
|
|
vnode_unlock(vp);
|
|
|
|
return platform_binary;
|
|
}
|
|
|
|
int
|
|
csfg_get_supplement_platform_binary(struct fileglob *fg __unused)
|
|
{
|
|
#if CONFIG_SUPPLEMENTAL_SIGNATURES
|
|
int platform_binary = 0;
|
|
struct ubc_info *uip;
|
|
vnode_t vp;
|
|
|
|
if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE) {
|
|
return 0;
|
|
}
|
|
|
|
vp = (struct vnode *)fg_get_data(fg);
|
|
if (vp == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
vnode_lock(vp);
|
|
if (!UBCINFOEXISTS(vp)) {
|
|
goto out;
|
|
}
|
|
|
|
uip = vp->v_ubcinfo;
|
|
if (uip == NULL) {
|
|
goto out;
|
|
}
|
|
|
|
if (uip->cs_blob_supplement == NULL) {
|
|
goto out;
|
|
}
|
|
|
|
platform_binary = uip->cs_blob_supplement->csb_platform_binary;
|
|
out:
|
|
vnode_unlock(vp);
|
|
|
|
return platform_binary;
|
|
#else
|
|
// Supplemental signatures are only allowed in CONFIG_SUPPLEMENTAL_SIGNATURES
|
|
// Return false if anyone asks about them
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
uint8_t *
|
|
csfg_get_cdhash(struct fileglob *fg, uint64_t offset, size_t *cdhash_size)
|
|
{
|
|
vnode_t vp;
|
|
|
|
if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE) {
|
|
return NULL;
|
|
}
|
|
|
|
vp = (struct vnode *)fg_get_data(fg);
|
|
if (vp == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
struct cs_blob *csblob = NULL;
|
|
if ((csblob = ubc_cs_blob_get(vp, -1, -1, offset)) == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (cdhash_size) {
|
|
*cdhash_size = CS_CDHASH_LEN;
|
|
}
|
|
return csblob->csb_cdhash;
|
|
}
|
|
|
|
uint8_t *
|
|
csfg_get_supplement_cdhash(struct fileglob *fg __unused, uint64_t offset __unused, size_t *cdhash_size __unused)
|
|
{
|
|
#if CONFIG_SUPPLEMENTAL_SIGNATURES
|
|
vnode_t vp;
|
|
|
|
if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE) {
|
|
return NULL;
|
|
}
|
|
|
|
vp = (struct vnode *)fg_get_data(fg);
|
|
if (vp == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
struct cs_blob *csblob = NULL;
|
|
if ((csblob = ubc_cs_blob_get_supplement(vp, offset)) == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (cdhash_size) {
|
|
*cdhash_size = CS_CDHASH_LEN;
|
|
}
|
|
return csblob->csb_cdhash;
|
|
#else
|
|
// Supplemental signatures are only available in CONFIG_SUPPLEMENTAL_SIGNATURES
|
|
// return NULL if anyone asks about them
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
const uint8_t *
|
|
csfg_get_supplement_linkage_cdhash(struct fileglob *fg __unused, uint64_t offset __unused, size_t *cdhash_size __unused)
|
|
{
|
|
#if CONFIG_SUPPLEMENTAL_SIGNATURES
|
|
vnode_t vp;
|
|
|
|
if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE) {
|
|
return NULL;
|
|
}
|
|
|
|
vp = (struct vnode *)fg_get_data(fg);
|
|
if (vp == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
struct cs_blob *csblob = NULL;
|
|
if ((csblob = ubc_cs_blob_get_supplement(vp, offset)) == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (cdhash_size) {
|
|
*cdhash_size = CS_CDHASH_LEN;
|
|
}
|
|
|
|
return csblob->csb_linkage;
|
|
#else
|
|
// Supplemental signatures are only available in CONFIG_SUPPLEMENTAL_SIGNATURES
|
|
// return NULL if anyone asks about them
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Function: csfg_get_signer_type
|
|
*
|
|
* Description: This returns the signer type
|
|
* for the fileglob fg
|
|
*/
|
|
unsigned int
|
|
csfg_get_signer_type(struct fileglob *fg)
|
|
{
|
|
struct ubc_info *uip;
|
|
unsigned int signer_type = CS_SIGNER_TYPE_UNKNOWN;
|
|
vnode_t vp;
|
|
|
|
if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE) {
|
|
return CS_SIGNER_TYPE_UNKNOWN;
|
|
}
|
|
|
|
vp = (struct vnode *)fg_get_data(fg);
|
|
if (vp == NULL) {
|
|
return CS_SIGNER_TYPE_UNKNOWN;
|
|
}
|
|
|
|
vnode_lock(vp);
|
|
if (!UBCINFOEXISTS(vp)) {
|
|
goto out;
|
|
}
|
|
|
|
uip = vp->v_ubcinfo;
|
|
if (uip == NULL) {
|
|
goto out;
|
|
}
|
|
|
|
if (uip->cs_blobs == NULL) {
|
|
goto out;
|
|
}
|
|
|
|
/* It is OK to extract the signer type from the first blob,
|
|
* because all blobs of a vnode must have the same signer type. */
|
|
signer_type = uip->cs_blobs->csb_signer_type;
|
|
out:
|
|
vnode_unlock(vp);
|
|
|
|
return signer_type;
|
|
}
|
|
|
|
unsigned int
|
|
csfg_get_supplement_signer_type(struct fileglob *fg __unused)
|
|
{
|
|
#if CONFIG_SUPPLEMENTAL_SIGNATURES
|
|
struct ubc_info *uip;
|
|
unsigned int signer_type = CS_SIGNER_TYPE_UNKNOWN;
|
|
vnode_t vp;
|
|
|
|
if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE) {
|
|
return CS_SIGNER_TYPE_UNKNOWN;
|
|
}
|
|
|
|
vp = (struct vnode *)fg_get_data(fg);
|
|
if (vp == NULL) {
|
|
return CS_SIGNER_TYPE_UNKNOWN;
|
|
}
|
|
|
|
vnode_lock(vp);
|
|
if (!UBCINFOEXISTS(vp)) {
|
|
goto out;
|
|
}
|
|
|
|
uip = vp->v_ubcinfo;
|
|
if (uip == NULL) {
|
|
goto out;
|
|
}
|
|
|
|
if (uip->cs_blob_supplement == NULL) {
|
|
goto out;
|
|
}
|
|
|
|
signer_type = uip->cs_blob_supplement->csb_signer_type;
|
|
out:
|
|
vnode_unlock(vp);
|
|
|
|
return signer_type;
|
|
#else
|
|
// Supplemental signatures are only available in CONFIG_SUPPLEMENTAL_SIGNATURES
|
|
// Return unknown if anyone asks
|
|
return CS_SIGNER_TYPE_UNKNOWN;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Function: csfg_get_validation_category
|
|
*
|
|
* Description: This returns the validation category
|
|
* for the fileglob fg
|
|
*/
|
|
unsigned int
|
|
csfg_get_validation_category(struct fileglob *fg, uint64_t offset)
|
|
{
|
|
unsigned int validation_category = CS_VALIDATION_CATEGORY_INVALID;
|
|
vnode_t vp;
|
|
|
|
if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE) {
|
|
return CS_VALIDATION_CATEGORY_INVALID;
|
|
}
|
|
|
|
vp = (struct vnode *)fg_get_data(fg);
|
|
if (vp == NULL) {
|
|
return CS_VALIDATION_CATEGORY_INVALID;
|
|
}
|
|
|
|
vnode_lock(vp);
|
|
|
|
struct cs_blob *csblob = NULL;
|
|
if ((csblob = ubc_cs_blob_get(vp, -1, -1, offset)) == NULL) {
|
|
goto out;
|
|
}
|
|
|
|
validation_category = csblob->csb_validation_category;
|
|
out:
|
|
vnode_unlock(vp);
|
|
|
|
return validation_category;
|
|
}
|
|
|
|
unsigned int
|
|
csfg_get_supplement_validation_category(struct fileglob *fg __unused, uint64_t offset __unused)
|
|
{
|
|
#if CONFIG_SUPPLEMENTAL_SIGNATURES
|
|
unsigned int validation_category = CS_VALIDATION_CATEGORY_INVALID;
|
|
vnode_t vp;
|
|
|
|
if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE) {
|
|
return CS_SIGNER_TYPE_UNKNOWN;
|
|
}
|
|
|
|
vp = (struct vnode *)fg_get_data(fg);
|
|
if (vp == NULL) {
|
|
return CS_SIGNER_TYPE_UNKNOWN;
|
|
}
|
|
|
|
vnode_lock(vp);
|
|
|
|
struct cs_blob *csblob = NULL;
|
|
if ((csblob = ubc_cs_blob_get_supplement(vp, offset)) == NULL) {
|
|
goto out;
|
|
}
|
|
|
|
validation_category = csblob->csb_validation_category;
|
|
out:
|
|
vnode_unlock(vp);
|
|
|
|
return validation_category;
|
|
#else
|
|
// Supplemental signatures are only available in CONFIG_SUPPLEMENTAL_SIGNATURES
|
|
// Return invalid if anyone asks
|
|
return CS_VALIDATION_CATEGORY_INVALID;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Function: csfg_get_teamid
|
|
*
|
|
* Description: This returns a pointer to
|
|
* the teamid for the fileglob fg
|
|
*/
|
|
const char *
|
|
csfg_get_teamid(struct fileglob *fg)
|
|
{
|
|
struct ubc_info *uip;
|
|
const char *str = NULL;
|
|
vnode_t vp;
|
|
|
|
if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE) {
|
|
return NULL;
|
|
}
|
|
|
|
vp = (struct vnode *)fg_get_data(fg);
|
|
if (vp == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
vnode_lock(vp);
|
|
if (!UBCINFOEXISTS(vp)) {
|
|
goto out;
|
|
}
|
|
|
|
uip = vp->v_ubcinfo;
|
|
if (uip == NULL) {
|
|
goto out;
|
|
}
|
|
|
|
if (uip->cs_blobs == NULL) {
|
|
goto out;
|
|
}
|
|
|
|
/* It is OK to extract the teamid from the first blob
|
|
* because all blobs of a vnode must have the same teamid */
|
|
str = uip->cs_blobs->csb_teamid;
|
|
out:
|
|
vnode_unlock(vp);
|
|
|
|
return str;
|
|
}
|
|
|
|
const char *
|
|
csfg_get_supplement_teamid(struct fileglob *fg __unused)
|
|
{
|
|
#if CONFIG_SUPPLEMENTAL_SIGNATURES
|
|
struct ubc_info *uip;
|
|
const char *str = NULL;
|
|
vnode_t vp;
|
|
|
|
if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE) {
|
|
return NULL;
|
|
}
|
|
|
|
vp = (struct vnode *)fg_get_data(fg);
|
|
if (vp == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
vnode_lock(vp);
|
|
if (!UBCINFOEXISTS(vp)) {
|
|
goto out;
|
|
}
|
|
|
|
uip = vp->v_ubcinfo;
|
|
if (uip == NULL) {
|
|
goto out;
|
|
}
|
|
|
|
if (uip->cs_blob_supplement == NULL) {
|
|
goto out;
|
|
}
|
|
|
|
str = uip->cs_blob_supplement->csb_supplement_teamid;
|
|
out:
|
|
vnode_unlock(vp);
|
|
|
|
return str;
|
|
#else
|
|
// Supplemental Signatures are only available in CONFIG_SUPPLEMENTAL_SIGNATURES
|
|
// Return NULL if anyone asks
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Function: csfg_get_csblob
|
|
*
|
|
* Description: This returns a pointer to
|
|
* the csblob for the fileglob fg
|
|
*/
|
|
struct cs_blob*
|
|
csfg_get_csblob(struct fileglob *fg, uint64_t offset)
|
|
{
|
|
vnode_t vp;
|
|
|
|
if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE) {
|
|
return NULL;
|
|
}
|
|
|
|
vp = (struct vnode *)fg_get_data(fg);
|
|
if (vp == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
struct cs_blob *csblob = NULL;
|
|
if ((csblob = ubc_cs_blob_get(vp, -1, -1, offset)) == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return csblob;
|
|
}
|
|
|
|
struct cs_blob*
|
|
csfg_get_supplement_csblob(__unused struct fileglob *fg, __unused uint64_t offset)
|
|
{
|
|
#if CONFIG_SUPPLEMENTAL_SIGNATURES
|
|
vnode_t vp;
|
|
|
|
if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE) {
|
|
return NULL;
|
|
}
|
|
|
|
vp = (struct vnode *)fg_get_data(fg);
|
|
if (vp == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
struct cs_blob *csblob = NULL;
|
|
if ((csblob = ubc_cs_blob_get_supplement(vp, offset)) == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return csblob;
|
|
#else
|
|
// Supplemental signatures are only available in CONFIG_SUPPLEMENTAL_SIGNATURES
|
|
// return NULL if anyone asks about them
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Function: csfg_get_prod_signed
|
|
*
|
|
* Description: Returns 1 if code is not signed with a developer identity.
|
|
* Note the inverted meaning from the cs_flag to make the error case safer.
|
|
* Will go away with rdar://problem/28322552.
|
|
*/
|
|
int
|
|
csfg_get_prod_signed(struct fileglob *fg)
|
|
{
|
|
struct ubc_info *uip;
|
|
vnode_t vp;
|
|
int prod_signed = 0;
|
|
|
|
if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE) {
|
|
return 0;
|
|
}
|
|
|
|
vp = (struct vnode *)fg_get_data(fg);
|
|
if (vp == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
vnode_lock(vp);
|
|
if (!UBCINFOEXISTS(vp)) {
|
|
goto out;
|
|
}
|
|
|
|
uip = vp->v_ubcinfo;
|
|
if (uip == NULL) {
|
|
goto out;
|
|
}
|
|
|
|
if (uip->cs_blobs == NULL) {
|
|
goto out;
|
|
}
|
|
|
|
/* It is OK to extract the flag from the first blob
|
|
* because all blobs of a vnode must have the same cs_flags */
|
|
prod_signed = (uip->cs_blobs->csb_flags & CS_DEV_CODE) == 0;
|
|
out:
|
|
vnode_unlock(vp);
|
|
|
|
return prod_signed;
|
|
}
|
|
|
|
int
|
|
csfg_get_supplement_prod_signed(struct fileglob *fg __unused)
|
|
{
|
|
#if CONFIG_SUPPLEMENTAL_SIGNATURES
|
|
struct ubc_info *uip;
|
|
vnode_t vp;
|
|
int prod_signed = 0;
|
|
|
|
if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE) {
|
|
return 0;
|
|
}
|
|
|
|
vp = (struct vnode *)fg_get_data(fg);
|
|
if (vp == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
vnode_lock(vp);
|
|
if (!UBCINFOEXISTS(vp)) {
|
|
goto out;
|
|
}
|
|
|
|
uip = vp->v_ubcinfo;
|
|
if (uip == NULL) {
|
|
goto out;
|
|
}
|
|
|
|
if (uip->cs_blob_supplement == NULL) {
|
|
goto out;
|
|
}
|
|
|
|
/* It is OK to extract the flag from the first blob
|
|
* because all blobs of a vnode must have the same cs_flags */
|
|
prod_signed = (uip->cs_blob_supplement->csb_flags & CS_DEV_CODE) == 0;
|
|
out:
|
|
vnode_unlock(vp);
|
|
|
|
return prod_signed;
|
|
#else
|
|
// Supplemental signatures are only available in CONFIG_SUPPLEMENTAL_SIGNATURES
|
|
// Indicate development signed if anyone tries to ask about one.
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Function: csfg_get_identity
|
|
*
|
|
* Description: This function returns the codesign identity
|
|
* for the fileglob
|
|
*/
|
|
const char *
|
|
csfg_get_identity(struct fileglob *fg, off_t offset)
|
|
{
|
|
vnode_t vp;
|
|
struct cs_blob *csblob = NULL;
|
|
|
|
if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE) {
|
|
return NULL;
|
|
}
|
|
|
|
vp = (struct vnode *)fg_get_data(fg);
|
|
if (vp == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
csblob = ubc_cs_blob_get(vp, -1, -1, offset);
|
|
if (csblob == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return csblob_get_identity(csblob);
|
|
}
|
|
|
|
/*
|
|
* Function: csfg_get_platform_identifier
|
|
*
|
|
* Description: This function returns the codesign platform
|
|
* identifier for the fileglob. Assumes the fileproc
|
|
* is being held busy to keep the fileglob consistent.
|
|
*/
|
|
uint8_t
|
|
csfg_get_platform_identifier(struct fileglob *fg, off_t offset)
|
|
{
|
|
vnode_t vp;
|
|
|
|
if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE) {
|
|
return 0;
|
|
}
|
|
|
|
vp = (struct vnode *)fg_get_data(fg);
|
|
if (vp == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return csvnode_get_platform_identifier(vp, offset);
|
|
}
|
|
|
|
/*
|
|
* Function: csvnode_get_platform_identifier
|
|
*
|
|
* Description: This function returns the codesign platform
|
|
* identifier for the vnode. Assumes a vnode reference
|
|
* is held.
|
|
*/
|
|
uint8_t
|
|
csvnode_get_platform_identifier(struct vnode *vp, off_t offset)
|
|
{
|
|
struct cs_blob *csblob;
|
|
const CS_CodeDirectory *code_dir;
|
|
|
|
csblob = ubc_cs_blob_get(vp, -1, -1, offset);
|
|
if (csblob == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
code_dir = csblob->csb_cd;
|
|
if (code_dir == NULL || ntohl(code_dir->length) < 8) {
|
|
return 0;
|
|
}
|
|
|
|
return code_dir->platform;
|
|
}
|
|
|
|
/*
|
|
* Function: csproc_get_platform_identifier
|
|
*
|
|
* Description: This function returns the codesign platform
|
|
* identifier for the proc. Assumes proc will remain
|
|
* valid through call.
|
|
*/
|
|
uint8_t
|
|
csproc_get_platform_identifier(struct proc *p)
|
|
{
|
|
if (NULL == p->p_textvp) {
|
|
return 0;
|
|
}
|
|
|
|
return csvnode_get_platform_identifier(p->p_textvp, p->p_textoff);
|
|
}
|
|
|
|
uint32_t
|
|
cs_entitlement_flags(struct proc *p)
|
|
{
|
|
return proc_getcsflags(p) & CS_ENTITLEMENT_FLAGS;
|
|
}
|
|
|
|
int
|
|
cs_restricted(struct proc *p)
|
|
{
|
|
return (proc_getcsflags(p) & CS_RESTRICT) ? 1 : 0;
|
|
}
|
|
|
|
int
|
|
csproc_hardened_runtime(struct proc* p)
|
|
{
|
|
return (proc_getcsflags(p) & CS_RUNTIME) ? 1 : 0;
|
|
}
|
|
|
|
/*
|
|
* Function: csfg_get_path
|
|
*
|
|
* Description: This populates the buffer passed in
|
|
* with the path of the vnode
|
|
* When calling this, the fileglob
|
|
* cannot go away. The caller must have a
|
|
* a reference on the fileglob or fileproc
|
|
*/
|
|
int
|
|
csfg_get_path(struct fileglob *fg, char *path, int *len)
|
|
{
|
|
vnode_t vp = NULL;
|
|
|
|
if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE) {
|
|
return -1;
|
|
}
|
|
|
|
vp = (struct vnode *)fg_get_data(fg);
|
|
|
|
/* vn_getpath returns 0 for success,
|
|
* or an error code */
|
|
return vn_getpath(vp, path, len);
|
|
}
|
|
|
|
/*
|
|
* Retrieve the entitlements blob for a vnode
|
|
* Returns:
|
|
* EINVAL no text vnode associated with the process
|
|
* EBADEXEC invalid code signing data
|
|
* 0 no error occurred
|
|
*
|
|
* On success, out_start and out_length will point to the
|
|
* entitlements blob if found; or will be set to NULL/zero
|
|
* if there were no entitlements.
|
|
*/
|
|
int
|
|
cs_entitlements_blob_get_vnode(vnode_t vnode, off_t offset, void **out_start, size_t *out_length)
|
|
{
|
|
struct cs_blob *csblob;
|
|
|
|
*out_start = NULL;
|
|
*out_length = 0;
|
|
|
|
if (vnode == NULL) {
|
|
return EINVAL;
|
|
}
|
|
|
|
if ((csblob = ubc_cs_blob_get(vnode, -1, -1, offset)) == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return csblob_get_entitlements(csblob, out_start, out_length);
|
|
}
|
|
|
|
|
|
/* Retrieve the cached entitlements for a vnode
|
|
* Returns:
|
|
* EINVAL no vnode
|
|
* EBADEXEC invalid code signing data
|
|
* 0 no error occurred
|
|
*
|
|
* Note: the entitlements may be NULL if there is nothing cached.
|
|
*/
|
|
|
|
int
|
|
cs_entitlements_dictionary_copy_vnode(vnode_t vnode, off_t offset, void **entitlements)
|
|
{
|
|
struct cs_blob *csblob;
|
|
|
|
*entitlements = NULL;
|
|
|
|
if (vnode == NULL) {
|
|
return EINVAL;
|
|
}
|
|
|
|
if ((csblob = ubc_cs_blob_get(vnode, -1, -1, offset)) == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
*entitlements = csblob_entitlements_dictionary_copy(csblob);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Retrieve the entitlements blob for a process.
|
|
* Returns:
|
|
* EINVAL no text vnode associated with the process
|
|
* EBADEXEC invalid code signing data
|
|
* 0 no error occurred
|
|
*
|
|
* On success, out_start and out_length will point to the
|
|
* entitlements blob if found; or will be set to NULL/zero
|
|
* if there were no entitlements.
|
|
*/
|
|
int
|
|
cs_entitlements_blob_get(proc_t p, void **out_start, size_t *out_length)
|
|
{
|
|
if ((proc_getcsflags(p) & CS_SIGNED) == 0) {
|
|
return 0;
|
|
}
|
|
|
|
return cs_entitlements_blob_get_vnode(p->p_textvp, p->p_textoff, out_start, out_length);
|
|
}
|
|
|
|
|
|
/* Retrieve the cached entitlements for a process
|
|
* Returns:
|
|
* EINVAL no text vnode associated with the process
|
|
* EBADEXEC invalid code signing data
|
|
* 0 no error occurred
|
|
*
|
|
* Note: the entitlements may be NULL if there is nothing cached.
|
|
*/
|
|
|
|
int
|
|
cs_entitlements_dictionary_copy(proc_t p, void **entitlements)
|
|
{
|
|
struct cs_blob *csblob;
|
|
|
|
*entitlements = NULL;
|
|
|
|
if ((proc_getcsflags(p) & CS_SIGNED) == 0) {
|
|
return 0;
|
|
}
|
|
|
|
if (NULL == p->p_textvp) {
|
|
return EINVAL;
|
|
}
|
|
|
|
if ((csblob = ubc_cs_blob_get(p->p_textvp, -1, -1, p->p_textoff)) == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
*entitlements = csblob_entitlements_dictionary_copy(csblob);
|
|
return 0;
|
|
}
|
|
|
|
/* Retrieve the codesign identity for a process.
|
|
* Returns:
|
|
* NULL an error occured
|
|
* string the cs_identity
|
|
*/
|
|
|
|
const char *
|
|
cs_identity_get(proc_t p)
|
|
{
|
|
struct cs_blob *csblob;
|
|
|
|
if ((proc_getcsflags(p) & CS_SIGNED) == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
if (NULL == p->p_textvp) {
|
|
return NULL;
|
|
}
|
|
|
|
if ((csblob = ubc_cs_blob_get(p->p_textvp, -1, -1, p->p_textoff)) == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return csblob_get_identity(csblob);
|
|
}
|
|
|
|
/*
|
|
* DO NOT USE THIS FUNCTION!
|
|
* Use the properly guarded csproc_get_blob instead.
|
|
*
|
|
* This is currently here to allow detached signatures to work
|
|
* properly. The only user of this function is also checking
|
|
* for CS_VALID.
|
|
*/
|
|
|
|
int
|
|
cs_blob_get(proc_t p, void **out_start, size_t *out_length)
|
|
{
|
|
struct cs_blob *csblob;
|
|
|
|
*out_start = NULL;
|
|
*out_length = 0;
|
|
|
|
if (NULL == p->p_textvp) {
|
|
return EINVAL;
|
|
}
|
|
|
|
if ((csblob = ubc_cs_blob_get(p->p_textvp, -1, -1, p->p_textoff)) == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
*out_start = csblob->csb_mem_kaddr;
|
|
*out_length = csblob->csb_mem_size;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* return cshash of a process, cdhash is of size CS_CDHASH_LEN
|
|
*/
|
|
|
|
uint8_t *
|
|
cs_get_cdhash(struct proc *p)
|
|
{
|
|
struct cs_blob *csblob;
|
|
|
|
if ((proc_getcsflags(p) & CS_SIGNED) == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
if (NULL == p->p_textvp) {
|
|
return NULL;
|
|
}
|
|
|
|
if ((csblob = ubc_cs_blob_get(p->p_textvp, -1, -1, p->p_textoff)) == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return csblob->csb_cdhash;
|
|
}
|
|
|
|
/*
|
|
* return launch type of a process being created.
|
|
*/
|
|
cs_launch_type_t
|
|
launch_constraint_data_get_launch_type(launch_constraint_data_t lcd)
|
|
{
|
|
return lcd->launch_type;
|
|
}
|