]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/kern/kern_cs.c
xnu-6153.141.1.tar.gz
[apple/xnu.git] / bsd / kern / kern_cs.c
index 6ff8c458b6ace72fb33f6e8b417afe3f6c9d8a56..4a9fbc3ffddd784274a9ec31ef37b32e2aa399e4 100644 (file)
@@ -2,7 +2,7 @@
  * 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
  * 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,
@@ -22,7 +22,7 @@
  * 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@
  */
 
@@ -79,18 +79,42 @@ 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;
+/*
+ *  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_enforcement_panic = 0;
+int cs_relax_platform_task_ports = 0;
 
 #if CONFIG_ENFORCE_SIGNED_CODE
-#define DEFAULT_CS_ENFORCEMENT_ENABLE 1
+#define DEFAULT_CS_SYSTEM_ENFORCEMENT_ENABLE 1
+#define DEFAULT_CS_PROCESS_ENFORCEMENT_ENABLE 1
 #else
-#define DEFAULT_CS_ENFORCEMENT_ENABLE 0
+#define DEFAULT_CS_SYSTEM_ENFORCEMENT_ENABLE 1
+#define DEFAULT_CS_PROCESS_ENFORCEMENT_ENABLE 0
 #endif
-SECURITY_READ_ONLY_LATE(int) cs_enforcement_enable = DEFAULT_CS_ENFORCEMENT_ENABLE;
+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
 #define DEFAULT_CS_LIBRARY_VA_ENABLE 1
@@ -107,44 +131,59 @@ static lck_grp_t *cs_lockgrp;
 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_RW | CTLFLAG_LOCKED, &cs_library_val_enable, 0, "");
+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));
+           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;
+       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_debug", &cs_debug, sizeof (cs_debug));
+       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));
+           sizeof(cs_library_val_enable));
 #endif
 #endif /* !SECURE_KERNEL */
 
@@ -159,28 +198,35 @@ 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 
+        * 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. 
+        * 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", 
-                                   p->p_pid);
+       if (0 != mac_proc_check_run_cs_invalid(p)) {
+               if (cs_debug) {
+                       printf("CODE SIGNING: cs_allow_invalid() "
+                           "not allowed: pid %d\n",
+                           p->p_pid);
+               }
                return 0;
        }
-       if(cs_debug) printf("CODE SIGNING: cs_allow_invalid() "
-                           "allowed: pid %d\n", 
-                           p->p_pid);
+       if (cs_debug) {
+               printf("CODE SIGNING: cs_allow_invalid() "
+                   "allowed: pid %d\n",
+                   p->p_pid);
+       }
        proc_lock(p);
        p->p_csflags &= ~(CS_KILL | CS_HARD);
-       if (p->p_csflags & CS_VALID)
-       {
+       if (p->p_csflags & CS_VALID) {
                p->p_csflags |= CS_DEBUGGED;
        }
        proc_unlock(p);
+
+       /* allow a debugged process to hide some (debug-only!) memory */
+       task_set_memory_ownership_transfer(p->task, TRUE);
+
        vm_map_switch_protect(get_task_map(p->task), FALSE);
 #endif
        return (p->p_csflags & (CS_KILL | CS_HARD)) == 0;
@@ -189,23 +235,26 @@ cs_allow_invalid(struct proc *p)
 int
 cs_invalid_page(addr64_t vaddr, boolean_t *cs_killed)
 {
-       struct proc     *p;
-       int             send_kill = 0, retval = 0, verbose = cs_debug;
-       uint32_t        csflags;
+       struct proc     *p;
+       int             send_kill = 0, retval = 0, verbose = cs_debug;
+       uint32_t        csflags;
 
        p = current_proc();
 
-       if (verbose)
+       if (verbose) {
                printf("CODE SIGNING: cs_invalid_page(0x%llx): p=%d[%s]\n",
                    vaddr, p->p_pid, p->p_comm);
+       }
 
        proc_lock(p);
 
        /* XXX for testing */
-       if (cs_force_kill)
+       if (cs_force_kill) {
                p->p_csflags |= CS_KILL;
-       if (cs_force_hard)
+       }
+       if (cs_force_hard) {
                p->p_csflags |= CS_HARD;
+       }
 
        /* CS_KILL triggers a kill signal, and no you can't have the page. Nothing else. */
        if (p->p_csflags & CS_KILL) {
@@ -214,7 +263,7 @@ cs_invalid_page(addr64_t vaddr, boolean_t *cs_killed)
                send_kill = 1;
                retval = 1;
        }
-       
+
        /* CS_HARD means fail the mapping operation so the process stays valid. */
        if (p->p_csflags & CS_HARD) {
                retval = 1;
@@ -228,12 +277,13 @@ cs_invalid_page(addr64_t vaddr, boolean_t *cs_killed)
        csflags = p->p_csflags;
        proc_unlock(p);
 
-       if (verbose)
+       if (verbose) {
                printf("CODE SIGNING: cs_invalid_page(0x%llx): "
-                      "p=%d[%s] final status 0x%x, %s page%s\n",
-                      vaddr, p->p_pid, p->p_comm, p->p_csflags,
-                      retval ? "denying" : "allowing (remove VALID)",
-                      send_kill ? " sending SIGKILL" : "");
+                   "p=%d[%s] final status 0x%x, %s page%s\n",
+                   vaddr, p->p_pid, p->p_comm, p->p_csflags,
+                   retval ? "denying" : "allowing (remove VALID)",
+                   send_kill ? " sending SIGKILL" : "");
+       }
 
        if (send_kill) {
                /* We will set the exit reason for the thread later */
@@ -254,53 +304,82 @@ cs_invalid_page(addr64_t vaddr, boolean_t *cs_killed)
  */
 
 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)
+       }
+
+       if (p == NULL) {
                p = current_proc();
+       }
 
-       if (p != NULL && (p->p_csflags & CS_ENFORCEMENT))
+       if (p != NULL && (p->p_csflags & 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;
+}
+
 /*
  * Returns whether a given process is still valid.
  */
 int
 cs_valid(struct proc *p)
 {
-
-       if (p == NULL)
+       if (p == NULL) {
                p = current_proc();
+       }
 
-       if (p != NULL && (p->p_csflags & CS_VALID))
+       if (p != NULL && (p->p_csflags & CS_VALID)) {
                return 1;
+       }
 
        return 0;
 }
 
 /*
- * Library validation functions 
+ * Library validation functions
  */
 int
 cs_require_lv(struct proc *p)
 {
-       
-       if (cs_library_val_enable)
+       if (cs_library_val_enable) {
                return 1;
+       }
 
-       if (p == NULL)
+       if (p == NULL) {
                p = current_proc();
-       
-       if (p != NULL && (p->p_csflags & CS_REQUIRE_LV))
+       }
+
+       if (p != NULL && (p->p_csflags & CS_REQUIRE_LV)) {
                return 1;
-       
+       }
+
+       return 0;
+}
+
+int
+csproc_forced_lv(struct proc* p)
+{
+       if (p == NULL) {
+               p = current_proc();
+       }
+       if (p != NULL && (p->p_csflags & CS_FORCED_LV)) {
+               return 1;
+       }
        return 0;
 }
 
@@ -317,38 +396,38 @@ cs_system_require_lv(void)
 /*
  * Function: csblob_get_base_offset
  *
- * Description: This function returns the base offset into the Mach-O binary
+ * 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;
+       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;
+       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 blob->csb_mem_kaddr;
+       return blob->csb_mem_kaddr;
 }
 
 /*
@@ -356,26 +435,39 @@ csblob_get_addr(struct cs_blob *blob)
  *
  * 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;
+       if (blob && blob->csb_platform_binary) {
+               return 1;
+       }
+       return 0;
 }
 
 /*
  * 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;
+       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;
 }
 
 /*
@@ -387,17 +479,23 @@ csblob_get_flags(struct cs_blob *blob)
 struct cs_blob *
 csproc_get_blob(struct proc *p)
 {
-       if (NULL == p)
+       if (NULL == p) {
                return NULL;
+       }
 
-       if (NULL == p->p_textvp)
+       if (NULL == p->p_textvp) {
                return NULL;
+       }
+
+       if ((p->p_csflags & CS_SIGNED) == 0) {
+               return NULL;
+       }
 
        return ubc_cs_blob_get(p->p_textvp, -1, p->p_textoff);
 }
 
 /*
- * Function: csproc_get_blob
+ * Function: csvnode_get_blob
  *
  * Description: This function returns the cs_blob
  *             for the vnode vp
@@ -413,7 +511,7 @@ csvnode_get_blob(struct vnode *vp, off_t offset)
  *
  * Description: This function returns a pointer to the
  *             team id of csblob
-*/
+ */
 const char *
 csblob_get_teamid(struct cs_blob *csblob)
 {
@@ -432,11 +530,13 @@ 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)
+       if (cd == NULL) {
                return NULL;
+       }
 
-       if (cd->identOffset == 0)
+       if (cd->identOffset == 0) {
                return NULL;
+       }
 
        return ((const char *)cd) + ntohl(cd->identOffset);
 }
@@ -453,57 +553,95 @@ 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;
+}
+
 void *
 csblob_entitlements_dictionary_copy(struct cs_blob *csblob)
 {
-    if (!csblob->csb_entitlements) return NULL;
-    osobject_retain(csblob->csb_entitlements);
-    return csblob->csb_entitlements;
+       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;
+       assert(csblob->csb_entitlements == NULL);
+       if (entitlements) {
+               osobject_retain(entitlements);
+       }
+       csblob->csb_entitlements = entitlements;
 }
 
 /*
- * Function: csproc_get_teamid 
+ * 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;
+       if (csblob == NULL) {
+               return NULL;
+       }
 
        return csblob_get_teamid(csblob);
 }
 
 /*
- * Function: csvnode_get_teamid 
+ * 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)
+       if (vp == NULL) {
                return NULL;
+       }
 
        csblob = ubc_cs_blob_get(vp, -1, offset);
-       if (csblob == NULL)
-           return NULL;
+       if (csblob == NULL) {
+               return NULL;
+       }
 
        return csblob_get_teamid(csblob);
 }
@@ -522,52 +660,144 @@ csproc_get_platform_binary(struct proc *p)
        csblob = csproc_get_blob(p);
 
        /* If there is no csblob this returns 0 because
-          it is true that it is not a platform binary */
+        *  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 = csproc_get_blob(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
  *
- * Description: This function returns the 
- *             platform binary field for the 
- *             fileglob fg
+ * Description: This function returns the
+ *             platform binary field for the
+ *              fileglob fg
  */
-int 
+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)
+       if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE) {
                return 0;
-       
+       }
+
        vp = (struct vnode *)fg->fg_data;
-       if (vp == NULL)
+       if (vp == NULL) {
                return 0;
+       }
 
        vnode_lock(vp);
-       if (!UBCINFOEXISTS(vp))
+       if (!UBCINFOEXISTS(vp)) {
                goto out;
-       
+       }
+
        uip = vp->v_ubcinfo;
-       if (uip == NULL)
+       if (uip == NULL) {
                goto out;
-       
-       if (uip->cs_blobs == NULL)
+       }
+
+       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 */    
+        *  because all blobs of a vnode must have the same teamid */
        platform_binary = uip->cs_blobs->csb_platform_binary;
 out:
        vnode_unlock(vp);
@@ -580,28 +810,77 @@ csfg_get_cdhash(struct fileglob *fg, uint64_t offset, size_t *cdhash_size)
 {
        vnode_t vp;
 
-       if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE)
+       if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE) {
                return NULL;
+       }
 
        vp = (struct vnode *)fg->fg_data;
-       if (vp == NULL)
+       if (vp == NULL) {
                return NULL;
+       }
 
        struct cs_blob *csblob = NULL;
-       if ((csblob = ubc_cs_blob_get(vp, -1, offset)) == NULL) 
+       if ((csblob = ubc_cs_blob_get(vp, -1, offset)) == NULL) {
                return NULL;
+       }
 
-       if (cdhash_size)
+       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
  *
  * Description: This returns a pointer to
- *             the teamid for the fileglob fg
+ *              the teamid for the fileglob fg
  */
 const char *
 csfg_get_teamid(struct fileglob *fg)
@@ -610,26 +889,31 @@ csfg_get_teamid(struct fileglob *fg)
        const char *str = NULL;
        vnode_t vp;
 
-       if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE)
+       if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE) {
                return NULL;
-       
+       }
+
        vp = (struct vnode *)fg->fg_data;
-       if (vp == NULL)
+       if (vp == NULL) {
                return NULL;
+       }
 
        vnode_lock(vp);
-       if (!UBCINFOEXISTS(vp))
+       if (!UBCINFOEXISTS(vp)) {
                goto out;
-       
+       }
+
        uip = vp->v_ubcinfo;
-       if (uip == NULL)
+       if (uip == NULL) {
                goto out;
-       
-       if (uip->cs_blobs == NULL)
+       }
+
+       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 */    
+        *  because all blobs of a vnode must have the same teamid */
        str = uip->cs_blobs->csb_teamid;
 out:
        vnode_unlock(vp);
@@ -637,10 +921,152 @@ 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);
+       return p->p_csflags & CS_ENTITLEMENT_FLAGS;
 }
 
 int
@@ -649,6 +1075,12 @@ 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
  *
@@ -663,13 +1095,14 @@ csfg_get_path(struct fileglob *fg, char *path, int *len)
 {
        vnode_t vp = NULL;
 
-       if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE)
+       if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE) {
                return -1;
-       
+       }
+
        vp = (struct vnode *)fg->fg_data;
 
        /* vn_getpath returns 0 for success,
-          or an error code */
+        *  or an error code */
        return vn_getpath(vp, path, len);
 }
 
@@ -692,15 +1125,54 @@ cs_entitlements_blob_get(proc_t p, void **out_start, size_t *out_length)
        *out_start = NULL;
        *out_length = 0;
 
-       if (NULL == p->p_textvp)
+       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)
+       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 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 ((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;
+       }
+
+       *entitlements = csblob_entitlements_dictionary_copy(csblob);
+       return 0;
+}
+
 /* Retrieve the codesign identity for a process.
  * Returns:
  *   NULL      an error occured
@@ -712,24 +1184,28 @@ cs_identity_get(proc_t p)
 {
        struct cs_blob *csblob;
 
-       if (NULL == p->p_textvp)
+       if ((p->p_csflags & CS_SIGNED) == 0) {
                return NULL;
+       }
 
-       if ((csblob = ubc_cs_blob_get(p->p_textvp, -1, p->p_textoff)) == 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);
 }
 
-
-/* Retrieve the codesign blob for a process.
- * Returns:
- *   EINVAL    no text vnode associated with the process
- *   0         no error occurred
+/*
+ * DO NOT USE THIS FUNCTION!
+ * Use the properly guarded csproc_get_blob instead.
  *
- * On success, out_start and out_length will point to the
- * cms blob if found; or will be set to NULL/zero
- * if there were no blob.
+ * This is currently here to allow detached signatures to work
+ * properly. The only user of this function is also checking
+ * for CS_VALID.
  */
 
 int
@@ -740,11 +1216,13 @@ cs_blob_get(proc_t p, void **out_start, size_t *out_length)
        *out_start = NULL;
        *out_length = 0;
 
-       if (NULL == p->p_textvp)
+       if (NULL == p->p_textvp) {
                return EINVAL;
+       }
 
-       if ((csblob = ubc_cs_blob_get(p->p_textvp, -1, p->p_textoff)) == NULL)
+       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;
@@ -761,11 +1239,17 @@ cs_get_cdhash(struct proc *p)
 {
        struct cs_blob *csblob;
 
-       if (NULL == p->p_textvp)
+       if ((p->p_csflags & CS_SIGNED) == 0) {
                return NULL;
+       }
 
-       if ((csblob = ubc_cs_blob_get(p->p_textvp, -1, p->p_textoff)) == 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;
 }