X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/fe8ab488e9161c46dd9885d58fc52996dc0249ff..b226f5e54a60dc81db17b1260381d7dbfea3cdf1:/bsd/kern/kern_cs.c?ds=sidebyside diff --git a/bsd/kern/kern_cs.c b/bsd/kern/kern_cs.c index 66af46613..2b40cea3e 100644 --- a/bsd/kern/kern_cs.c +++ b/bsd/kern/kern_cs.c @@ -71,72 +71,125 @@ #include +#include + 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 SECURE_KERNEL -const int cs_enforcement_enable=1; -const int cs_library_val_enable=1; -#else +/* +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.) + */ +const int cs_system_enforcement_enable = 1; +const int cs_process_enforcement_enable = 1; +const int cs_library_val_enable = 1; +#else /* !SECURE_KERNEL */ +int cs_enforcement_panic=0; +int cs_relax_platform_task_ports = 0; + #if CONFIG_ENFORCE_SIGNED_CODE -int cs_enforcement_enable=1; +#define DEFAULT_CS_SYSTEM_ENFORCEMENT_ENABLE 1 +#define DEFAULT_CS_PROCESS_ENFORCEMENT_ENABLE 1 #else -int cs_enforcement_enable=0; -#endif /* CONFIG_ENFORCE_SIGNED_CODE */ +#define DEFAULT_CS_SYSTEM_ENFORCEMENT_ENABLE 1 +#define DEFAULT_CS_PROCESS_ENFORCEMENT_ENABLE 0 +#endif +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; #if CONFIG_ENFORCE_LIBRARY_VALIDATION -int cs_library_val_enable = 1; +#define DEFAULT_CS_LIBRARY_VA_ENABLE 1 #else -int cs_library_val_enable = 0; -#endif /* CONFIG_ENFORCE_LIBRARY_VALIDATION */ +#define DEFAULT_CS_LIBRARY_VA_ENABLE 0 +#endif +SECURITY_READ_ONLY_LATE(int) cs_library_val_enable = DEFAULT_CS_LIBRARY_VA_ENABLE; -int cs_enforcement_panic=0; -#endif /* SECURE_KERNEL */ +#endif /* !SECURE_KERNEL */ int cs_all_vnodes = 0; static lck_grp_t *cs_lockgrp; -static lck_rw_t * SigPUPLock; 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_enforcement, CTLFLAG_RW | CTLFLAG_LOCKED, &cs_enforcement_enable, 0, ""); +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_RW | 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 */ int panic_on_cs_killed = 0; + void cs_init(void) { -#if MACH_ASSERT && __x86_64__ +#if MACH_ASSERT +#if PLATFORM_WatchOS || __x86_64__ panic_on_cs_killed = 1; -#endif /* MACH_ASSERT && __x86_64__ */ +#endif /* watchos || x86_64 */ +#endif /* MACH_ASSERT */ PE_parse_boot_argn("panic_on_cs_killed", &panic_on_cs_killed, sizeof (panic_on_cs_killed)); #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) { - cs_enforcement_enable = 0; + 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 */ + lck_grp_attr_t *attr = lck_grp_attr_alloc_init(); cs_lockgrp = lck_grp_alloc_init("KERNCS", attr); - SigPUPLock = lck_rw_alloc_init(cs_lockgrp, NULL); + lck_grp_attr_free(attr); } int @@ -145,7 +198,7 @@ cs_allow_invalid(struct proc *p) #if MACH_ASSERT lck_mtx_assert(&p->p_mlock, LCK_MTX_ASSERT_NOTOWNED); #endif -#if CONFIG_MACF && CONFIG_ENFORCE_SIGNED_CODE +#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 @@ -162,15 +215,20 @@ cs_allow_invalid(struct proc *p) p->p_pid); proc_lock(p); p->p_csflags &= ~(CS_KILL | CS_HARD); + if (p->p_csflags & CS_VALID) + { + p->p_csflags |= CS_DEBUGGED; + } + proc_unlock(p); + vm_map_switch_protect(get_task_map(p->task), FALSE); #endif return (p->p_csflags & (CS_KILL | CS_HARD)) == 0; } int -cs_invalid_page( - addr64_t vaddr) +cs_invalid_page(addr64_t vaddr, boolean_t *cs_killed) { struct proc *p; int send_kill = 0, retval = 0, verbose = cs_debug; @@ -192,25 +250,12 @@ cs_invalid_page( /* CS_KILL triggers a kill signal, and no you can't have the page. Nothing else. */ if (p->p_csflags & CS_KILL) { - if (panic_on_cs_killed && - vaddr >= SHARED_REGION_BASE && - vaddr < SHARED_REGION_BASE + SHARED_REGION_SIZE) { - panic(" cs_invalid_page(va=0x%llx): killing p=%p\n", (uint64_t) vaddr, p); - } p->p_csflags |= CS_KILLED; cs_procs_killed++; send_kill = 1; retval = 1; } -#if __x86_64__ - if (panic_on_cs_killed && - vaddr >= SHARED_REGION_BASE && - vaddr < SHARED_REGION_BASE + SHARED_REGION_SIZE) { - panic(" cs_invalid_page(va=0x%llx): cs error p=%p\n", (uint64_t) vaddr, p); - } -#endif /* __x86_64__ */ - /* CS_HARD means fail the mapping operation so the process stays valid. */ if (p->p_csflags & CS_HARD) { retval = 1; @@ -231,8 +276,15 @@ cs_invalid_page( retval ? "denying" : "allowing (remove VALID)", send_kill ? " sending SIGKILL" : ""); - if (send_kill) - threadsignal(current_thread(), SIGKILL, EXC_BAD_ACCESS); + 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; @@ -243,10 +295,10 @@ cs_invalid_page( */ int -cs_enforcement(struct proc *p) +cs_process_enforcement(struct proc *p) { - if (cs_enforcement_enable) + if (cs_process_enforcement_enable) return 1; if (p == NULL) @@ -258,300 +310,256 @@ cs_enforcement(struct proc *p) return 0; } -static struct { - struct cscsr_functions *funcs; - vm_map_offset_t csr_map_base; - vm_map_size_t csr_map_size; - int inuse; - int disabled; -} csr_state; +int +cs_process_global_enforcement(void) +{ + return cs_process_enforcement_enable ? 1 : 0; +} -SYSCTL_INT(_vm, OID_AUTO, sigpup_disable, CTLFLAG_RW | CTLFLAG_LOCKED, &csr_state.disabled, 0, ""); +int +cs_system_enforcement(void) +{ + return cs_system_enforcement_enable ? 1 : 0; +} -static int -vnsize(vfs_context_t vfs, vnode_t vp, uint64_t *size) +/* + * Returns whether a given process is still valid. + */ +int +cs_valid(struct proc *p) { - struct vnode_attr va; - int error; - VATTR_INIT(&va); - VATTR_WANTED(&va, va_data_size); + if (p == NULL) + p = current_proc(); + + if (p != NULL && (p->p_csflags & CS_VALID)) + return 1; - error = vnode_getattr(vp, &va, vfs); - if (error) - return error; - *size = va.va_data_size; return 0; } +/* + * Library validation functions + */ int -sigpup_install(user_addr_t argsp) +cs_require_lv(struct proc *p) { - struct sigpup_install_table args; - memory_object_control_t control; - kern_return_t result; - vfs_context_t vfs = NULL; - struct vnode_attr va; - vnode_t vp = NULL; - char *buf = NULL; - uint64_t size; - size_t len = 0; - int error = 0; - if (!cs_enforcement_enable || csr_state.funcs == NULL) - return ENOTSUP; - - lck_rw_lock_exclusive(SigPUPLock); - - if (kauth_cred_issuser(kauth_cred_get()) == 0) { - error = EPERM; - goto cleanup; - } - - if (cs_debug > 10) - printf("sigpup install\n"); - - if (csr_state.csr_map_base != 0 || csr_state.inuse) { - error = EPERM; - goto cleanup; - } - - if (USER_ADDR_NULL == argsp) { - error = EINVAL; - goto cleanup; - } - if ((error = copyin(argsp, &args, sizeof(args))) != 0) - goto cleanup; - - if (cs_debug > 10) - printf("sigpup install with args\n"); - - MALLOC(buf, char *, MAXPATHLEN, M_TEMP, M_WAITOK); - if (buf == NULL) { - error = ENOMEM; - goto cleanup; - } - if ((error = copyinstr((user_addr_t)args.path, buf, MAXPATHLEN, &len)) != 0) - goto cleanup; - - if ((vfs = vfs_context_create(NULL)) == NULL) { - error = ENOMEM; - goto cleanup; - } - - if ((error = vnode_lookup(buf, VNODE_LOOKUP_NOFOLLOW, &vp, vfs)) != 0) - goto cleanup; - - if (cs_debug > 10) - printf("sigpup found file: %s\n", buf); + if (cs_library_val_enable) + return 1; - /* make sure vnode is on the process's root volume */ - if (rootvnode->v_mount != vp->v_mount) { - if (cs_debug) printf("sigpup csr no on root volume\n"); - error = EPERM; - goto cleanup; - } + if (p == NULL) + p = current_proc(); + + if (p != NULL && (p->p_csflags & CS_REQUIRE_LV)) + return 1; + + return 0; +} - /* make sure vnode is owned by "root" */ - VATTR_INIT(&va); - VATTR_WANTED(&va, va_uid); - error = vnode_getattr(vp, &va, vfs); - if (error) - goto cleanup; - - if (va.va_uid != 0) { - if (cs_debug) printf("sigpup: csr file not owned by root\n"); - error = EPERM; - goto cleanup; +int +csproc_forced_lv(struct proc* p) +{ + if (p == NULL) { + p = current_proc(); } - - error = vnsize(vfs, vp, &size); - if (error) - goto cleanup; - - control = ubc_getobject(vp, 0); - if (control == MEMORY_OBJECT_CONTROL_NULL) { - error = EINVAL; - goto cleanup; + if (p != NULL && (p->p_csflags & CS_FORCED_LV)) { + return 1; } + return 0; +} - csr_state.csr_map_size = mach_vm_round_page(size); - - if (cs_debug > 10) - printf("mmap!\n"); - - result = vm_map_enter_mem_object_control(kernel_map, - &csr_state.csr_map_base, - csr_state.csr_map_size, - 0, VM_FLAGS_ANYWHERE, - control, 0 /* file offset */, - 0 /* cow */, - VM_PROT_READ, - VM_PROT_READ, - VM_INHERIT_DEFAULT); - if (result != KERN_SUCCESS) { - error = EINVAL; - goto cleanup; - } +/* + * 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; +} - error = csr_state.funcs->csr_validate_header((const uint8_t *)csr_state.csr_map_base, - csr_state.csr_map_size); - if (error) { - if (cs_debug > 10) - printf("sigpup header invalid, dropping mapping"); - sigpup_drop(); - goto cleanup; - } +/* + * Function: csblob_get_base_offset + * + * Description: This function returns the base offset into the (possibly universal) binary + * for a given blob. +*/ - if (cs_debug > 10) - printf("table loaded %ld bytes\n", (long)csr_state.csr_map_size); +off_t +csblob_get_base_offset(struct cs_blob *blob) +{ + return blob->csb_base_offset; +} -cleanup: - lck_rw_unlock_exclusive(SigPUPLock); +/* + * Function: csblob_get_size + * + * Description: This function returns the size of a given blob. +*/ - if (buf) - FREE(buf, M_TEMP); - if (vp) - (void)vnode_put(vp); - if (vfs) - (void)vfs_context_rele(vfs); - - if (error) - printf("sigpup: load failed with error: %d\n", error); +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. +*/ - return error; +vm_address_t +csblob_get_addr(struct cs_blob *blob) +{ + return blob->csb_mem_kaddr; } +/* + * Function: csblob_get_platform_binary + * + * Description: This function returns true if the binary is + * in the trust cache. +*/ + int -sigpup_drop(void) +csblob_get_platform_binary(struct cs_blob *blob) { + if (blob && blob->csb_platform_binary) + return 1; + return 0; +} - if (kauth_cred_issuser(kauth_cred_get()) == 0) - return EPERM; - - lck_rw_lock_exclusive(SigPUPLock); - - if (csr_state.csr_map_base == 0 || csr_state.inuse) { - printf("failed to unload the sigpup database\n"); - lck_rw_unlock_exclusive(SigPUPLock); - return EINVAL; - } - - if (cs_debug > 10) - printf("sigpup: unloading\n"); - - (void)mach_vm_deallocate(kernel_map, - csr_state.csr_map_base, csr_state.csr_map_size); - - csr_state.csr_map_base = 0; - csr_state.csr_map_size = 0; - - lck_rw_unlock_exclusive(SigPUPLock); +/* + * Function: csblob_get_flags + * + * Description: This function returns the flags for a given blob +*/ - return 0; +unsigned int +csblob_get_flags(struct cs_blob *blob) +{ + return blob->csb_flags; } -void sigpup_attach_vnode(vnode_t); /* XXX */ +/* + * Function: csblob_get_hashtype + * + * Description: This function returns the hash type for a given blob +*/ -void -sigpup_attach_vnode(vnode_t vp) +uint8_t +csblob_get_hashtype(struct cs_blob const * const blob) { - const void *csblob; - size_t cslen; + return blob->csb_hashtype != NULL ? cs_hash_type(blob->csb_hashtype) : 0; +} - if (!cs_enforcement_enable || csr_state.funcs == NULL || csr_state.csr_map_base == 0 || csr_state.disabled) - return; +/* + * 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 the file is not on the root volumes or already been check, skip */ - if (vp->v_mount != rootvnode->v_mount || (vp->v_flag & VNOCS)) - return; + if (NULL == p->p_textvp) + return NULL; - csblob = csr_state.funcs->csr_find_file_codedirectory(vp, (const uint8_t *)csr_state.csr_map_base, - (size_t)csr_state.csr_map_size, &cslen); - if (csblob) { - ubc_cs_sigpup_add(vp, (vm_address_t)csblob, (vm_size_t)cslen); - csr_state.inuse = 1; + if ((p->p_csflags & CS_SIGNED) == 0) { + return NULL; } - vp->v_flag |= VNOCS; -} -void -cs_register_cscsr(struct cscsr_functions *funcs) -{ - if (csr_state.funcs || funcs->csr_version < CSCSR_VERSION) - return; - csr_state.funcs = funcs; + return ubc_cs_blob_get(p->p_textvp, -1, p->p_textoff); } /* - * Library validation functions + * Function: csvnode_get_blob + * + * Description: This function returns the cs_blob + * for the vnode vp */ -int -cs_require_lv(struct proc *p) +struct cs_blob * +csvnode_get_blob(struct vnode *vp, off_t offset) { - - if (cs_library_val_enable) - return 1; - - if (p == NULL) - p = current_proc(); - - if (p != NULL && (p->p_csflags & CS_REQUIRE_LV)) - return 1; - - return 0; + return ubc_cs_blob_get(vp, -1, offset); } /* * Function: csblob_get_teamid * - * Description: This function returns a pointer to the team id - stored within the codedirectory of the csblob. - If the codedirectory predates team-ids, it returns - NULL. - This does not copy the name but returns a pointer to - it within the CD. Subsequently, the CD must be - available when this is used. + * 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; - if ((cd = (const CS_CodeDirectory *)cs_find_blob( - csblob, CSSLOT_CODEDIRECTORY, CSMAGIC_CODEDIRECTORY)) == NULL) - return NULL; - - if (ntohl(cd->version) < CS_SUPPORTSTEAMID) + cd = (const CS_CodeDirectory *)csblob_find_blob(csblob, CSSLOT_CODEDIRECTORY, CSMAGIC_CODEDIRECTORY); + if (cd == NULL) return NULL; - if (ntohl(cd->teamOffset) == 0) + if (cd->identOffset == 0) return NULL; - - const char *name = ((const char *)cd) + ntohl(cd->teamOffset); - if (cs_debug > 1) - printf("found team-id %s in cdblob\n", name); - return name; + return ((const char *)cd) + ntohl(cd->identOffset); } /* - * Function: csproc_get_blob + * Function: csblob_get_cdhash * - * Description: This function returns the cs_blob - * for the process p + * Description: This function returns a pointer to the + * cdhash of csblob (20 byte array) */ -static struct cs_blob * -csproc_get_blob(struct proc *p) +const uint8_t * +csblob_get_cdhash(struct cs_blob *csblob) { - if (NULL == p) - return NULL; + return csblob->csb_cdhash; +} - if (NULL == p->p_textvp) - return NULL; +/* + * 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; +} - return ubc_cs_blob_get(p->p_textvp, -1, p->p_textoff); +void * +csblob_entitlements_dictionary_copy(struct cs_blob *csblob) +{ + if (!csblob->csb_entitlements) return NULL; + osobject_retain(csblob->csb_entitlements); + return csblob->csb_entitlements; +} + +void +csblob_entitlements_dictionary_set(struct cs_blob *csblob, void * entitlements) +{ + assert(csblob->csb_entitlements == NULL); + if (entitlements) osobject_retain(entitlements); + csblob->csb_entitlements = entitlements; } /* @@ -566,8 +574,28 @@ 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); +} + +/* + * 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 == NULL) ? NULL : csblob->csb_teamid; + return csblob_get_signer_type(csblob); } /* @@ -585,8 +613,10 @@ csvnode_get_teamid(struct vnode *vp, off_t offset) return NULL; csblob = ubc_cs_blob_get(vp, -1, offset); + if (csblob == NULL) + return NULL; - return (csblob == NULL) ? NULL : csblob->csb_teamid; + return csblob_get_teamid(csblob); } /* @@ -607,6 +637,100 @@ csproc_get_platform_binary(struct proc *p) 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); + + if (csblob == NULL) { + return; + } + + if (cs_debug) { + printf("clearing platform binary on proc/task: pid = %d\n", p->p_pid); + } + + csblob->csb_platform_binary = 0; + csblob->csb_platform_path = 0; + 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); + p->p_csflags &= (~CS_ENFORCEMENT); + 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); + p->p_csflags |= 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 && (p->p_csflags & 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 ((p->p_csflags & CS_DEV_CODE) == 0); +} + + /* * Function: csfg_get_platform_binary * @@ -648,6 +772,68 @@ out: return platform_binary; } +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->fg_data; + if (vp == NULL) + return NULL; + + struct cs_blob *csblob = NULL; + if ((csblob = ubc_cs_blob_get(vp, -1, offset)) == NULL) + return NULL; + + if (cdhash_size) + *cdhash_size = CS_CDHASH_LEN; + + return csblob->csb_cdhash; +} + +/* + * 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->fg_data; + 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; +} + /* * Function: csfg_get_teamid * @@ -688,12 +874,153 @@ out: return str; } +/* + * 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->fg_data; + 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; +} + +/* + * 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->fg_data; + if (vp == NULL) + return NULL; + + csblob = ubc_cs_blob_get(vp, -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->fg_data; + 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, 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 (p->p_csflags & CS_ENTITLEMENT_FLAGS); } +int +cs_restricted(struct proc *p) +{ + return (p->p_csflags & CS_RESTRICT) ? 1 : 0; +} + +int +csproc_hardened_runtime(struct proc* p) +{ + return (p->p_csflags & CS_RUNTIME) ? 1 : 0; +} + /* * Function: csfg_get_path * @@ -717,3 +1044,110 @@ csfg_get_path(struct fileglob *fg, char *path, int *len) or an error code */ return vn_getpath(vp, path, len); } + +/* 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) +{ + struct cs_blob *csblob; + + *out_start = NULL; + *out_length = 0; + + if ((p->p_csflags & CS_SIGNED) == 0) { + return 0; + } + + if (NULL == p->p_textvp) + return EINVAL; + + if ((csblob = ubc_cs_blob_get(p->p_textvp, -1, p->p_textoff)) == NULL) + return 0; + + return csblob_get_entitlements(csblob, out_start, out_length); +} + +/* 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 ((p->p_csflags & CS_SIGNED) == 0) { + return NULL; + } + + if (NULL == p->p_textvp) + return NULL; + + if ((csblob = ubc_cs_blob_get(p->p_textvp, -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, p->p_textoff)) == NULL) + return 0; + + *out_start = (void *)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 ((p->p_csflags & CS_SIGNED) == 0) { + return NULL; + } + + if (NULL == p->p_textvp) + return NULL; + + if ((csblob = ubc_cs_blob_get(p->p_textvp, -1, p->p_textoff)) == NULL) + return NULL; + + return csblob->csb_cdhash; +}