#include <kern/kalloc.h>
#include <kern/zalloc.h>
#include <kern/thread.h>
+#include <vm/pmap.h>
#include <vm/vm_kern.h>
#include <vm/vm_protos.h> /* last */
boolean_t *);
extern kern_return_t memory_object_signed(memory_object_control_t control,
boolean_t is_signed);
-extern boolean_t memory_object_is_slid(memory_object_control_t control);
extern boolean_t memory_object_is_signed(memory_object_control_t);
+/* XXX Same for those. */
+
extern void Debugger(const char *message);
static void ubc_cs_free(struct ubc_info *uip);
static boolean_t ubc_cs_supports_multilevel_hash(struct cs_blob *blob);
-static void ubc_cs_convert_to_multilevel_hash(struct cs_blob *blob);
+static kern_return_t ubc_cs_convert_to_multilevel_hash(struct cs_blob *blob);
struct zone *ubc_info_zone;
static uint32_t cs_blob_generation_count = 1;
cs_md_final cs_final;
};
+uint8_t cs_hash_type(
+ struct cs_hash const * const cs_hash)
+{
+ return cs_hash->cs_type;
+}
+
static const struct cs_hash cs_hash_sha1 = {
.cs_type = CS_HASHTYPE_SHA1,
.cs_size = CS_SHA1_LEN,
static int
cs_validate_csblob(
const uint8_t *addr,
- size_t *blob_size_p,
+ const size_t blob_size,
const CS_CodeDirectory **rcd,
const CS_GenericBlob **rentitlements)
{
const CS_GenericBlob *blob;
int error;
- size_t length, blob_size;
+ size_t length;
*rcd = NULL;
*rentitlements = NULL;
blob = (const CS_GenericBlob *)(const void *)addr;
- blob_size = *blob_size_p;
length = blob_size;
error = cs_validate_blob(blob, length);
uint32_t n, count;
const CS_CodeDirectory *best_cd = NULL;
unsigned int best_rank = 0;
+#if PLATFORM_WatchOS
+ const CS_CodeDirectory *sha1_cd = NULL;
+#endif
if (length < sizeof(CS_SuperBlob))
return EBADEXEC;
printf("multiple hash=%d CodeDirectories in signature; rejecting\n", best_cd->hashType);
return EBADEXEC;
}
+#if PLATFORM_WatchOS
+ if (candidate->hashType == CS_HASHTYPE_SHA1) {
+ if (sha1_cd != NULL) {
+ printf("multiple sha1 CodeDirectories in signature; rejecting\n");
+ return EBADEXEC;
+ }
+ sha1_cd = candidate;
+ }
+#endif
} else if (type == CSSLOT_ENTITLEMENTS) {
if (ntohl(subBlob->magic) != CSMAGIC_EMBEDDED_ENTITLEMENTS) {
return EBADEXEC;
}
}
+#if PLATFORM_WatchOS
+ /* To keep watchOS fast enough, we have to resort to sha1 for
+ * some code.
+ *
+ * At the time of writing this comment, known sha1 attacks are
+ * collision attacks (not preimage or second preimage
+ * attacks), which do not apply to platform binaries since
+ * they have a fixed hash in the trust cache. Given this
+ * property, we only prefer sha1 code directories for adhoc
+ * signatures, which always have to be in a trust cache to be
+ * valid (can-load-cdhash does not exist for watchOS). Those
+ * are, incidentally, also the platform binaries, for which we
+ * care about the performance hit that sha256 would bring us.
+ *
+ * Platform binaries may still contain a (not chosen) sha256
+ * code directory, which keeps software updates that switch to
+ * sha256-only small.
+ */
+
+ if (*rcd != NULL && sha1_cd != NULL && (ntohl(sha1_cd->flags) & CS_ADHOC)) {
+ if (sha1_cd->flags != (*rcd)->flags) {
+ printf("mismatched flags between hash %d (flags: %#x) and sha1 (flags: %#x) cd.\n",
+ (int)(*rcd)->hashType, (*rcd)->flags, sha1_cd->flags);
+ *rcd = NULL;
+ return EBADEXEC;
+ }
+
+ *rcd = sha1_cd;
+ }
+#endif
+
} else if (ntohl(blob->magic) == CSMAGIC_CODEDIRECTORY) {
if ((error = cs_validate_codedirectory((const CS_CodeDirectory *)(const void *)addr, length)) != 0)
if (*rcd == NULL)
return EBADEXEC;
- *blob_size_p = blob_size;
-
return 0;
}
* zero the tail of this page if it's currently
* present in the cache
*/
- kret = ubc_create_upl(vp, lastpg, PAGE_SIZE, &upl, &pl, UPL_SET_LITE);
+ kret = ubc_create_upl_kernel(vp, lastpg, PAGE_SIZE, &upl, &pl, UPL_SET_LITE, VM_KERN_MEMORY_FILE);
if (kret != KERN_SUCCESS)
panic("ubc_setsize: ubc_create_upl (error = %d)\n", kret);
return (MEMORY_OBJECT_CONTROL_NULL);
}
-boolean_t
-ubc_strict_uncached_IO(struct vnode *vp)
-{
- boolean_t result = FALSE;
-
- if (UBCINFOEXISTS(vp)) {
- result = memory_object_is_slid(vp->v_ubcinfo->ui_control);
- }
- return result;
-}
-
/*
* ubc_blktooff
*
* ubc_upl_abort(), or ubc_upl_abort_range().
*/
kern_return_t
-ubc_create_upl(
+ubc_create_upl_external(
struct vnode *vp,
off_t f_offset,
int bufsize,
upl_t *uplp,
upl_page_info_t **plp,
int uplflags)
+{
+ return (ubc_create_upl_kernel(vp, f_offset, bufsize, uplp, plp, uplflags, vm_tag_bt()));
+}
+
+kern_return_t
+ubc_create_upl_kernel(
+ struct vnode *vp,
+ off_t f_offset,
+ int bufsize,
+ upl_t *uplp,
+ upl_page_info_t **plp,
+ int uplflags,
+ vm_tag_t tag)
{
memory_object_control_t control;
kern_return_t kr;
if (control == MEMORY_OBJECT_CONTROL_NULL)
return KERN_INVALID_ARGUMENT;
- kr = memory_object_upl_request(control, f_offset, bufsize, uplp, NULL, NULL, uplflags);
+ kr = memory_object_upl_request(control, f_offset, bufsize, uplp, NULL, NULL, uplflags, tag);
if (kr == KERN_SUCCESS && plp != NULL)
*plp = UPL_GET_INTERNAL_PAGE_LIST(*uplp);
return kr;
vm_offset_t *blob_addr_p,
vm_size_t *blob_size_p)
{
- kern_return_t kr;
+ kern_return_t kr = KERN_FAILURE;
- *blob_addr_p = (vm_offset_t) kalloc_tag(*blob_size_p, VM_KERN_MEMORY_SECURITY);
- if (*blob_addr_p == 0) {
- kr = KERN_NO_SPACE;
- } else {
- kr = KERN_SUCCESS;
+ {
+ *blob_addr_p = (vm_offset_t) kalloc_tag(*blob_size_p, VM_KERN_MEMORY_SECURITY);
+
+ if (*blob_addr_p == 0) {
+ kr = KERN_NO_SPACE;
+ } else {
+ kr = KERN_SUCCESS;
+ }
}
+
return kr;
}
vm_offset_t blob_addr,
vm_size_t blob_size)
{
- kfree((void *) blob_addr, blob_size);
+#if PMAP_CS
+ if (blob_size > pmap_cs_blob_limit) {
+ kmem_free(kernel_map, blob_addr, blob_size);
+ } else
+#endif
+ {
+ kfree((void *) blob_addr, blob_size);
+ }
}
/*
{
const CS_CodeDirectory *cd;
+
/*
* Only applies to binaries that ship as part of the OS,
* primarily the shared cache.
}
/*
- * All state and preconditions were checked before, so this
- * function cannot fail.
+ * Given a cs_blob with an already chosen best code directory, this
+ * function allocates memory and copies into it only the blobs that
+ * will be needed by the kernel, namely the single chosen code
+ * directory (and not any of its alternatives) and the entitlement
+ * blob.
+ *
+ * This saves significant memory with agile signatures, and additional
+ * memory for 3rd Party Code because we also omit the CMS blob.
+ *
+ * To support multilevel and other potential code directory rewriting,
+ * the size of a new code directory can be specified. Since that code
+ * directory will replace the existing code directory,
+ * ubc_cs_reconstitute_code_signature does not copy the original code
+ * directory when a size is given, and the caller must fill it in.
*/
-static void
-ubc_cs_convert_to_multilevel_hash(struct cs_blob *blob)
+static int
+ubc_cs_reconstitute_code_signature(struct cs_blob const *blob, vm_size_t optional_new_cd_size,
+ vm_address_t *new_blob_addr_p, vm_size_t *new_blob_size_p,
+ CS_CodeDirectory **new_cd_p, CS_GenericBlob const **new_entitlements_p)
{
const CS_CodeDirectory *old_cd, *cd;
CS_CodeDirectory *new_cd;
vm_size_t new_cdsize;
kern_return_t kr;
int error;
- size_t length;
-
- uint32_t hashes_per_new_hash_shift = (uint32_t)(PAGE_SHIFT - blob->csb_hash_pageshift);
-
- if (cs_debug > 1) {
- printf("CODE SIGNING: Attempting to convert Code Directory for %lu -> %lu page shift\n",
- (unsigned long)blob->csb_hash_pageshift, (unsigned long)PAGE_SHIFT);
- }
old_cd = blob->csb_cd;
- /* Up to the hashes, we can copy all data */
- new_cdsize = ntohl(old_cd->hashOffset);
- new_cdsize += (ntohl(old_cd->nCodeSlots) >> hashes_per_new_hash_shift) * old_cd->hashSize;
+ new_cdsize = optional_new_cd_size != 0 ? optional_new_cd_size : htonl(old_cd->length);
new_blob_size = sizeof(CS_SuperBlob);
new_blob_size += sizeof(CS_BlobIndex);
printf("CODE SIGNING: Failed to allocate memory for new Code Signing Blob: %d\n",
kr);
}
- return;
+ return ENOMEM;
}
CS_SuperBlob *new_superblob;
new_cd = (CS_CodeDirectory *)(new_blob_addr + cd_offset);
} else {
- vm_size_t cd_offset;
+ // Blob is the code directory, directly.
+ new_cd = (CS_CodeDirectory *)new_blob_addr;
+ }
- cd_offset = sizeof(CS_SuperBlob) + 1 * sizeof(CS_BlobIndex);
+ if (optional_new_cd_size == 0) {
+ // Copy code directory, and revalidate.
+ memcpy(new_cd, old_cd, new_cdsize);
- new_superblob->count = htonl(1);
- new_superblob->index[0].type = htonl(CSSLOT_CODEDIRECTORY);
- new_superblob->index[0].offset = htonl((uint32_t)cd_offset);
+ vm_size_t length = new_blob_size;
- new_cd = (CS_CodeDirectory *)new_blob_addr;
+ error = cs_validate_csblob((const uint8_t *)new_blob_addr, length, &cd, &entitlements);
+
+ if (error) {
+ printf("CODE SIGNING: Failed to validate new Code Signing Blob: %d\n",
+ error);
+
+ ubc_cs_blob_deallocate(new_blob_addr, new_blob_size);
+ return error;
+ }
+ *new_entitlements_p = entitlements;
+ } else {
+ // Caller will fill out and validate code directory.
+ memset(new_cd, 0, new_cdsize);
+ *new_entitlements_p = NULL;
+ }
+
+ *new_blob_addr_p = new_blob_addr;
+ *new_blob_size_p = new_blob_size;
+ *new_cd_p = new_cd;
+
+ return 0;
+}
+
+static int
+ubc_cs_convert_to_multilevel_hash(struct cs_blob *blob)
+{
+ const CS_CodeDirectory *old_cd, *cd;
+ CS_CodeDirectory *new_cd;
+ const CS_GenericBlob *entitlements;
+ vm_offset_t new_blob_addr;
+ vm_size_t new_blob_size;
+ vm_size_t new_cdsize;
+ int error;
+
+ uint32_t hashes_per_new_hash_shift = (uint32_t)(PAGE_SHIFT - blob->csb_hash_pageshift);
+
+ if (cs_debug > 1) {
+ printf("CODE SIGNING: Attempting to convert Code Directory for %lu -> %lu page shift\n",
+ (unsigned long)blob->csb_hash_pageshift, (unsigned long)PAGE_SHIFT);
+ }
+
+ old_cd = blob->csb_cd;
+
+ /* Up to the hashes, we can copy all data */
+ new_cdsize = ntohl(old_cd->hashOffset);
+ new_cdsize += (ntohl(old_cd->nCodeSlots) >> hashes_per_new_hash_shift) * old_cd->hashSize;
+
+ error = ubc_cs_reconstitute_code_signature(blob, new_cdsize,
+ &new_blob_addr, &new_blob_size, &new_cd,
+ &entitlements);
+ if (error != 0) {
+ printf("CODE SIGNING: Failed to reconsitute code signature: %d\n", error);
+ return error;
}
memcpy(new_cd, old_cd, ntohl(old_cd->hashOffset));
blob->csb_hashtype->cs_final(dst, &mdctx);
}
- length = new_blob_size;
- error = cs_validate_csblob((const uint8_t *)new_blob_addr, &length, &cd, &entitlements);
- assert(length == new_blob_size);
- if (error) {
+ error = cs_validate_csblob((const uint8_t *)new_blob_addr, new_blob_size, &cd, &entitlements);
+ if (error != 0) {
- if (cs_debug > 1) {
- printf("CODE SIGNING: Failed to validate new Code Signing Blob: %d\n",
- error);
- }
+ printf("CODE SIGNING: Failed to validate new Code Signing Blob: %d\n",
+ error);
ubc_cs_blob_deallocate(new_blob_addr, new_blob_size);
- return;
+ return error;
}
- /* New Code Directory is ready for use, swap it out in the blob structure */
+ /* New Code Directory is ready for use, swap it out in the blob structure */
ubc_cs_blob_deallocate(blob->csb_mem_kaddr, blob->csb_mem_size);
blob->csb_mem_size = new_blob_size;
} else {
blob->csb_start_offset = 0;
}
+
+ return 0;
}
+/*
+ * Validate the code signature blob, create a struct cs_blob wrapper
+ * and return it together with a pointer to the chosen code directory
+ * and entitlements blob.
+ *
+ * Note that this takes ownership of the memory as addr, mainly because
+ * this function can actually replace the passed in blob with another
+ * one, e.g. when performing multilevel hashing optimization.
+ */
int
-ubc_cs_blob_add(
- struct vnode *vp,
- cpu_type_t cputype,
- off_t base_offset,
- vm_address_t *addr,
- vm_size_t size,
- struct image_params *imgp,
- __unused int flags,
- struct cs_blob **ret_blob)
+cs_blob_create_validated(
+ vm_address_t * const addr,
+ vm_size_t size,
+ struct cs_blob ** const ret_blob,
+ CS_CodeDirectory const ** const ret_cd)
{
- kern_return_t kr;
- struct ubc_info *uip;
- struct cs_blob *blob, *oblob;
- int error;
+ struct cs_blob *blob;
+ int error = EINVAL;
const CS_CodeDirectory *cd;
const CS_GenericBlob *entitlements;
- off_t blob_start_offset, blob_end_offset;
union cs_hash_union mdctx;
- boolean_t record_mtime;
size_t length;
- record_mtime = FALSE;
if (ret_blob)
*ret_blob = NULL;
}
/* fill in the new blob */
- blob->csb_cpu_type = cputype;
- blob->csb_base_offset = base_offset;
blob->csb_mem_size = size;
blob->csb_mem_offset = 0;
blob->csb_mem_kaddr = *addr;
blob->csb_flags = 0;
+ blob->csb_signer_type = CS_SIGNER_TYPE_UNKNOWN;
blob->csb_platform_binary = 0;
blob->csb_platform_path = 0;
blob->csb_teamid = NULL;
blob->csb_entitlements_blob = NULL;
blob->csb_entitlements = NULL;
-
+ blob->csb_reconstituted = false;
+
/* Transfer ownership. Even on error, this function will deallocate */
*addr = 0;
*/
length = (size_t) size;
error = cs_validate_csblob((const uint8_t *)blob->csb_mem_kaddr,
- &length, &cd, &entitlements);
+ length, &cd, &entitlements);
if (error) {
if (cs_debug)
uint8_t hash[CS_HASH_MAX_SIZE];
int md_size;
- size = (vm_size_t) length;
- assert(size <= blob->csb_mem_size);
- if (size < blob->csb_mem_size) {
- vm_address_t new_blob_addr;
- const CS_CodeDirectory *new_cd;
- const CS_GenericBlob *new_entitlements;
-
- kr = ubc_cs_blob_allocate(&new_blob_addr, &size);
- if (kr != KERN_SUCCESS) {
- if (cs_debug > 1) {
- printf("CODE SIGNING: failed to "
- "re-allocate blob (size "
- "0x%llx->0x%llx) error 0x%x\n",
- (uint64_t)blob->csb_mem_size,
- (uint64_t)size,
- kr);
- }
- } else {
- memcpy(new_blob_addr, blob->csb_mem_kaddr, size);
- if (cd == NULL) {
- new_cd = NULL;
- } else {
- new_cd = ((uintptr_t)cd
- - (uintptr_t)blob->csb_mem_kaddr
- + (uintptr_t)new_blob_addr);
- }
- if (entitlements == NULL) {
- new_entitlements = NULL;
- } else {
- new_entitlements = ((uintptr_t)entitlements
- - (uintptr_t)blob->csb_mem_kaddr
- + (uintptr_t)new_blob_addr);
- }
-// printf("CODE SIGNING: %s:%d kaddr 0x%llx cd %p ents %p -> blob 0x%llx cd %p ents %p\n", __FUNCTION__, __LINE__, (uint64_t)blob->csb_mem_kaddr, cd, entitlements, (uint64_t)new_blob_addr, new_cd, new_entitlements);
- ubc_cs_blob_deallocate(blob->csb_mem_kaddr,
- blob->csb_mem_size);
- blob->csb_mem_kaddr = new_blob_addr;
- blob->csb_mem_size = size;
- cd = new_cd;
- entitlements = new_entitlements;
- }
- }
-
blob->csb_cd = cd;
blob->csb_entitlements_blob = entitlements; /* may be NULL, not yet validated */
blob->csb_hashtype = cs_find_md(cd->hashType);
memcpy(blob->csb_cdhash, hash, CS_CDHASH_LEN);
}
- /*
+ error = 0;
+
+out:
+ if (error != 0) {
+ cs_blob_free(blob);
+ blob = NULL;
+ cd = NULL;
+ }
+
+ if (ret_blob != NULL) {
+ *ret_blob = blob;
+ }
+ if (ret_cd != NULL) {
+ *ret_cd = cd;
+ }
+
+ return error;
+}
+
+/*
+ * Free a cs_blob previously created by cs_blob_create_validated.
+ */
+void
+cs_blob_free(
+ struct cs_blob * const blob)
+{
+ if (blob != NULL) {
+ if (blob->csb_mem_kaddr) {
+ ubc_cs_blob_deallocate(blob->csb_mem_kaddr, blob->csb_mem_size);
+ blob->csb_mem_kaddr = 0;
+ }
+ if (blob->csb_entitlements != NULL) {
+ osobject_release(blob->csb_entitlements);
+ blob->csb_entitlements = NULL;
+ }
+ kfree(blob, sizeof (*blob));
+ }
+}
+
+int
+ubc_cs_blob_add(
+ struct vnode *vp,
+ cpu_type_t cputype,
+ off_t base_offset,
+ vm_address_t *addr,
+ vm_size_t size,
+ struct image_params *imgp,
+ __unused int flags,
+ struct cs_blob **ret_blob)
+{
+ kern_return_t kr;
+ struct ubc_info *uip;
+ struct cs_blob *blob, *oblob;
+ int error;
+ CS_CodeDirectory const *cd;
+ off_t blob_start_offset, blob_end_offset;
+ boolean_t record_mtime;
+
+ record_mtime = FALSE;
+ if (ret_blob)
+ *ret_blob = NULL;
+
+ /* Create the struct cs_blob wrapper that will be attached to the vnode.
+ * Validates the passed in blob in the process. */
+ error = cs_blob_create_validated(addr, size, &blob, &cd);
+
+ if (error != 0) {
+ printf("malform code signature blob: %d\n", error);
+ return error;
+ }
+
+ blob->csb_cpu_type = cputype;
+ blob->csb_base_offset = base_offset;
+
+ /*
* Let policy module check whether the blob's signature is accepted.
*/
#if CONFIG_MACF
unsigned int cs_flags = blob->csb_flags;
- error = mac_vnode_check_signature(vp, blob, imgp, &cs_flags, flags);
+ unsigned int signer_type = blob->csb_signer_type;
+ error = mac_vnode_check_signature(vp, blob, imgp, &cs_flags, &signer_type, flags);
blob->csb_flags = cs_flags;
+ blob->csb_signer_type = signer_type;
if (error) {
if (cs_debug)
error = EPERM;
goto out;
}
-#endif
-
+#endif
+
+#if CONFIG_ENFORCE_SIGNED_CODE
+ /*
+ * Reconstitute code signature
+ */
+ {
+ vm_address_t new_mem_kaddr = 0;
+ vm_size_t new_mem_size = 0;
+
+ CS_CodeDirectory *new_cd = NULL;
+ CS_GenericBlob const *new_entitlements = NULL;
+
+ error = ubc_cs_reconstitute_code_signature(blob, 0,
+ &new_mem_kaddr, &new_mem_size,
+ &new_cd, &new_entitlements);
+
+ if (error != 0) {
+ printf("failed code signature reconstitution: %d\n", error);
+ goto out;
+ }
+
+ ubc_cs_blob_deallocate(blob->csb_mem_kaddr, blob->csb_mem_size);
+
+ blob->csb_mem_kaddr = new_mem_kaddr;
+ blob->csb_mem_size = new_mem_size;
+ blob->csb_cd = new_cd;
+ blob->csb_entitlements_blob = new_entitlements;
+ blob->csb_reconstituted = true;
+ }
+
+#endif
+
+
if (blob->csb_flags & CS_PLATFORM_BINARY) {
if (cs_debug > 1)
printf("check_signature[pid: %d]: platform binary\n", current_proc()->p_pid);
}
if (ubc_cs_supports_multilevel_hash(blob)) {
- ubc_cs_convert_to_multilevel_hash(blob);
+ error = ubc_cs_convert_to_multilevel_hash(blob);
+ if (error != 0) {
+ printf("failed multilevel hash conversion: %d\n", error);
+ goto out;
+ }
+ blob->csb_reconstituted = true;
}
vnode_lock(vp);
oblob = oblob->csb_next) {
off_t oblob_start_offset, oblob_end_offset;
- /* check for conflicting teamid */
- if (blob->csb_platform_binary) { //platform binary needs to be the same for app slices
+ if (blob->csb_signer_type != oblob->csb_signer_type) { // signer type needs to be the same for slices
+ vnode_unlock(vp);
+ error = EALREADY;
+ goto out;
+ } else if (blob->csb_platform_binary) { //platform binary needs to be the same for app slices
if (!oblob->csb_platform_binary) {
vnode_unlock(vp);
error = EALREADY;
*/
oblob->csb_cpu_type = cputype;
}
+
+ /* The signature is still accepted, so update the
+ * generation count. */
+ uip->cs_add_gen = cs_blob_generation_count;
+
vnode_unlock(vp);
if (ret_blob)
*ret_blob = oblob;
if (cs_debug)
printf("check_signature[pid: %d]: error = %d\n", current_proc()->p_pid, error);
- /* we failed; release what we allocated */
- if (blob) {
- if (blob->csb_mem_kaddr) {
- ubc_cs_blob_deallocate(blob->csb_mem_kaddr, blob->csb_mem_size);
- blob->csb_mem_kaddr = 0;
- }
- if (blob->csb_entitlements != NULL) {
- osobject_release(blob->csb_entitlements);
- blob->csb_entitlements = NULL;
- }
- kfree(blob, sizeof (*blob));
- blob = NULL;
- }
+ cs_blob_free(blob);
}
if (error == EAGAIN) {
blob != NULL;
blob = next_blob) {
next_blob = blob->csb_next;
- if (blob->csb_mem_kaddr != 0) {
- ubc_cs_blob_deallocate(blob->csb_mem_kaddr,
- blob->csb_mem_size);
- blob->csb_mem_kaddr = 0;
- }
- if (blob->csb_entitlements != NULL) {
- osobject_release(blob->csb_entitlements);
- blob->csb_entitlements = NULL;
- }
OSAddAtomic(-1, &cs_blob_count);
OSAddAtomic((SInt32) -blob->csb_mem_size, &cs_blob_size);
- kfree(blob, sizeof (*blob));
+ cs_blob_free(blob);
}
#if CHECK_CS_VALIDATION_BITMAP
ubc_cs_validation_bitmap_deallocate( uip->ui_vnode );
size = blob->csb_mem_size;
error = cs_validate_csblob((const uint8_t *)blob->csb_mem_kaddr,
- &size, &cd, &entitlements);
+ size, &cd, &entitlements);
if (error) {
if (cs_debug) {
printf("CODESIGNING: csblob invalid: %d\n", error);
}
goto out;
}
- assert(size == blob->csb_mem_size);
unsigned int cs_flags = (ntohl(cd->flags) & CS_ALLOWED_MACHO) | CS_VALID;
-
+ unsigned int signer_type = CS_SIGNER_TYPE_UNKNOWN;
+
+ if (blob->csb_reconstituted) {
+ /*
+ * Code signatures that have been modified after validation
+ * cannot be revalidated inline from their in-memory blob.
+ *
+ * That's okay, though, because the only path left that relies
+ * on revalidation of existing in-memory blobs is the legacy
+ * detached signature database path, which only exists on macOS,
+ * which does not do reconstitution of any kind.
+ */
+ if (cs_debug) {
+ printf("CODESIGNING: revalidate: not inline revalidating reconstituted signature.\n");
+ }
+
+ /*
+ * EAGAIN tells the caller that they may reread the code
+ * signature and try attaching it again, which is the same
+ * thing they would do if there was no cs_blob yet in the
+ * first place.
+ *
+ * Conveniently, after ubc_cs_blob_add did a successful
+ * validation, it will detect that a matching cs_blob (cdhash,
+ * offset, arch etc.) already exists, and return success
+ * without re-adding a cs_blob to the vnode.
+ */
+ return EAGAIN;
+ }
+
/* callout to mac_vnode_check_signature */
#if CONFIG_MACF
- error = mac_vnode_check_signature(vp, blob, imgp, &cs_flags, flags);
+ error = mac_vnode_check_signature(vp, blob, imgp, &cs_flags, &signer_type, flags);
if (cs_debug && error) {
printf("revalidate: check_signature[pid: %d], error = %d\n", current_proc()->p_pid, error);
}
#else
(void)flags;
+ (void)signer_type;
#endif
/* update generation number if success */
vnode_lock_spin(vp);
blob->csb_flags = cs_flags;
+ blob->csb_signer_type = signer_type;
if (UBCINFOEXISTS(vp)) {
if (error == 0)
vp->v_ubcinfo->cs_add_gen = cs_blob_generation_count;
}
#if CHECK_CS_VALIDATION_BITMAP
-#define stob(s) ((atop_64((s)) + 07) >> 3)
+#define stob(s) (((atop_64(round_page_64(s))) + 07) >> 3)
extern boolean_t root_fs_upgrade_try;
/*
return;
}
#endif /* CHECK_CS_VALIDATION_BITMAP */
+
+#if PMAP_CS
+kern_return_t
+cs_associate_blob_with_mapping(
+ void *pmap,
+ vm_map_offset_t start,
+ vm_map_size_t size,
+ vm_object_offset_t offset,
+ void *blobs_p)
+{
+ off_t blob_start_offset, blob_end_offset;
+ kern_return_t kr;
+ struct cs_blob *blobs, *blob;
+ vm_offset_t kaddr;
+ struct pmap_cs_code_directory *cd_entry = NULL;
+
+ if (!pmap_cs) {
+ return KERN_NOT_SUPPORTED;
+ }
+
+ blobs = (struct cs_blob *)blobs_p;
+
+ for (blob = blobs;
+ blob != NULL;
+ blob = blob->csb_next) {
+ blob_start_offset = (blob->csb_base_offset +
+ blob->csb_start_offset);
+ blob_end_offset = (blob->csb_base_offset +
+ blob->csb_end_offset);
+ if ((off_t) offset < blob_start_offset ||
+ (off_t) offset >= blob_end_offset ||
+ (off_t) (offset + size) <= blob_start_offset ||
+ (off_t) (offset + size) > blob_end_offset) {
+ continue;
+ }
+ kaddr = blob->csb_mem_kaddr;
+ if (kaddr == 0) {
+ /* blob data has been released */
+ continue;
+ }
+ cd_entry = blob->csb_pmap_cs_entry;
+ if (cd_entry == NULL) {
+ continue;
+ }
+
+ break;
+ }
+
+ if (cd_entry != NULL) {
+ kr = pmap_cs_associate(pmap,
+ cd_entry,
+ start,
+ size);
+ } else {
+ kr = KERN_CODESIGN_ERROR;
+ }
+#if 00
+ printf("FBDP %d[%s] pmap_cs_associate(%p,%p,0x%llx,0x%llx) -> kr=0x%x\n", proc_selfpid(), &(current_proc()->p_comm[0]), pmap, cd_entry, (uint64_t)start, (uint64_t)size, kr);
+ kr = KERN_SUCCESS;
+#endif
+ return kr;
+}
+#endif /* PMAP_CS */