+#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
+ */
+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->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 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->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_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->fg_data;
+ 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;
+ }
+ ptrauth_utils_auth_blob_generic(csblob->csb_cdhash,
+ sizeof(csblob->csb_cdhash),
+ OS_PTRAUTH_DISCRIMINATOR("cs_blob.csb_cd_signature"),
+ PTRAUTH_ADDR_DIVERSIFY,
+ csblob->csb_cdhash_signature);
+ 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->fg_data;
+ 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;
+ }
+ ptrauth_utils_auth_blob_generic(csblob->csb_cdhash,
+ sizeof(csblob->csb_cdhash),
+ OS_PTRAUTH_DISCRIMINATOR("cs_blob.csb_cd_signature"),
+ PTRAUTH_ADDR_DIVERSIFY,
+ csblob->csb_cdhash_signature);
+ 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->fg_data;
+ 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->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;
+}
+
+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->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_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_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->fg_data;
+ 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->fg_data;
+ 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_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;
+}
+
+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->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_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->fg_data;
+ 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->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, -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
+ *
+ * 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->fg_data;
+
+ /* 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 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 ((p->p_csflags & 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 ((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, -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 ((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, -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 ((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, -1, p->p_textoff)) == NULL) {
+ return NULL;
+ }
+
+ ptrauth_utils_auth_blob_generic(csblob->csb_cdhash,
+ sizeof(csblob->csb_cdhash),
+ OS_PTRAUTH_DISCRIMINATOR("cs_blob.csb_cd_signature"),
+ PTRAUTH_ADDR_DIVERSIFY,
+ csblob->csb_cdhash_signature);
+ return csblob->csb_cdhash;