X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/3903760236c30e3b5ace7a4eefac3a269d68957c..5ba3f43ea354af8ad55bea84372a2bc834d8757c:/bsd/kern/ubc_subr.c diff --git a/bsd/kern/ubc_subr.c b/bsd/kern/ubc_subr.c index 27a8cc511..e05d13c3e 100644 --- a/bsd/kern/ubc_subr.c +++ b/bsd/kern/ubc_subr.c @@ -65,6 +65,7 @@ #include #include #include +#include #include #include /* last */ @@ -158,7 +159,13 @@ struct cs_hash { cs_md_final cs_final; }; -static struct cs_hash cs_hash_sha1 = { +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, .cs_digest_size = SHA_DIGEST_LENGTH, @@ -167,7 +174,7 @@ static struct cs_hash cs_hash_sha1 = { .cs_final = (cs_md_final)SHA1Final, }; #if CRYPTO_SHA2 -static struct cs_hash cs_hash_sha256 = { +static const struct cs_hash cs_hash_sha256 = { .cs_type = CS_HASHTYPE_SHA256, .cs_size = SHA256_DIGEST_LENGTH, .cs_digest_size = SHA256_DIGEST_LENGTH, @@ -175,7 +182,7 @@ static struct cs_hash cs_hash_sha256 = { .cs_update = (cs_md_update)SHA256_Update, .cs_final = (cs_md_final)SHA256_Final, }; -static struct cs_hash cs_hash_sha256_truncate = { +static const struct cs_hash cs_hash_sha256_truncate = { .cs_type = CS_HASHTYPE_SHA256_TRUNCATED, .cs_size = CS_SHA256_TRUNCATED_LEN, .cs_digest_size = SHA256_DIGEST_LENGTH, @@ -183,7 +190,7 @@ static struct cs_hash cs_hash_sha256_truncate = { .cs_update = (cs_md_update)SHA256_Update, .cs_final = (cs_md_final)SHA256_Final, }; -static struct cs_hash cs_hash_sha384 = { +static const struct cs_hash cs_hash_sha384 = { .cs_type = CS_HASHTYPE_SHA384, .cs_size = SHA384_DIGEST_LENGTH, .cs_digest_size = SHA384_DIGEST_LENGTH, @@ -193,7 +200,7 @@ static struct cs_hash cs_hash_sha384 = { }; #endif -static struct cs_hash * +static struct cs_hash const * cs_find_md(uint8_t type) { if (type == CS_HASHTYPE_SHA1) { @@ -221,7 +228,7 @@ union cs_hash_union { * Choose among different hash algorithms. * Higher is better, 0 => don't use at all. */ -static uint32_t hashPriorities[] = { +static const uint32_t hashPriorities[] = { CS_HASHTYPE_SHA1, CS_HASHTYPE_SHA256_TRUNCATED, CS_HASHTYPE_SHA256, @@ -354,7 +361,7 @@ hashes( static int cs_validate_codedirectory(const CS_CodeDirectory *cd, size_t length) { - struct cs_hash *hashtype; + struct cs_hash const *hashtype; if (length < sizeof(*cd)) return EBADEXEC; @@ -468,20 +475,26 @@ cs_validate_blob(const CS_GenericBlob *blob, size_t length) */ static int -cs_validate_csblob(const uint8_t *addr, size_t length, - const CS_CodeDirectory **rcd, - const CS_GenericBlob **rentitlements) +cs_validate_csblob( + const uint8_t *addr, + size_t *blob_size_p, + const CS_CodeDirectory **rcd, + const CS_GenericBlob **rentitlements) { - const CS_GenericBlob *blob = (const CS_GenericBlob *)(const void *)addr; + const CS_GenericBlob *blob; int error; + size_t length, blob_size; *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); if (error) return error; - length = ntohl(blob->length); if (ntohl(blob->magic) == CSMAGIC_EMBEDDED_SIGNATURE) { @@ -489,6 +502,9 @@ cs_validate_csblob(const uint8_t *addr, size_t 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; @@ -537,6 +553,15 @@ cs_validate_csblob(const uint8_t *addr, size_t length, 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; @@ -549,6 +574,37 @@ cs_validate_csblob(const uint8_t *addr, size_t length, } } +#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) @@ -561,6 +617,8 @@ cs_validate_csblob(const uint8_t *addr, size_t length, if (*rcd == NULL) return EBADEXEC; + *blob_size_p = blob_size; + return 0; } @@ -1072,7 +1130,7 @@ errno_t ubc_setsize_ex(struct vnode *vp, off_t nsize, ubc_setsize_opts_t opts) * 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); @@ -2277,13 +2335,26 @@ ubc_range_op( * 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; @@ -2343,7 +2414,7 @@ ubc_create_upl( 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; @@ -2878,6 +2949,7 @@ ubc_cs_convert_to_multilevel_hash(struct cs_blob *blob) 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); @@ -2994,7 +3066,9 @@ ubc_cs_convert_to_multilevel_hash(struct cs_blob *blob) blob->csb_hashtype->cs_final(dst, &mdctx); } - error = cs_validate_csblob((const uint8_t *)new_blob_addr, new_blob_size, &cd, &entitlements); + length = new_blob_size; + error = cs_validate_csblob((const uint8_t *)new_blob_addr, &length, &cd, &entitlements); + assert(length == new_blob_size); if (error) { if (cs_debug > 1) { @@ -3051,6 +3125,7 @@ ubc_cs_blob_add( 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) @@ -3068,6 +3143,7 @@ ubc_cs_blob_add( 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; @@ -3080,20 +3156,66 @@ ubc_cs_blob_add( /* * Validate the blob's contents */ - - error = cs_validate_csblob((const uint8_t *)blob->csb_mem_kaddr, size, &cd, &entitlements); + length = (size_t) size; + error = cs_validate_csblob((const uint8_t *)blob->csb_mem_kaddr, + &length, &cd, &entitlements); if (error) { - if (cs_debug) + if (cs_debug) printf("CODESIGNING: csblob invalid: %d\n", error); - /* The vnode checker can't make the rest of this function succeed if csblob validation failed, so bail */ - goto out; + /* + * The vnode checker can't make the rest of this function + * succeed if csblob validation failed, so bail */ + goto out; } else { const unsigned char *md_base; 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((void *)new_blob_addr, (void *)blob->csb_mem_kaddr, size); + if (cd == NULL) { + new_cd = NULL; + } else { + new_cd = (void *)(((uintptr_t)cd + - (uintptr_t)blob->csb_mem_kaddr + + (uintptr_t)new_blob_addr)); + } + if (entitlements == NULL) { + new_entitlements = NULL; + } else { + new_entitlements = (void *)(((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); @@ -3129,8 +3251,10 @@ ubc_cs_blob_add( */ #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) @@ -3143,8 +3267,8 @@ ubc_cs_blob_add( error = EPERM; goto out; } -#endif - +#endif + if (blob->csb_flags & CS_PLATFORM_BINARY) { if (cs_debug > 1) printf("check_signature[pid: %d]: platform binary\n", current_proc()->p_pid); @@ -3194,8 +3318,11 @@ ubc_cs_blob_add( 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; @@ -3501,32 +3628,38 @@ ubc_cs_blob_revalidate( int error = 0; const CS_CodeDirectory *cd = NULL; const CS_GenericBlob *entitlements = NULL; + size_t size; assert(vp != NULL); assert(blob != NULL); - error = cs_validate_csblob((const uint8_t *)blob->csb_mem_kaddr, blob->csb_mem_size, &cd, &entitlements); + size = blob->csb_mem_size; + error = cs_validate_csblob((const uint8_t *)blob->csb_mem_kaddr, + &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; /* 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; @@ -3612,7 +3745,7 @@ cs_validate_hash( unsigned *tainted) { union cs_hash_union mdctx; - struct cs_hash *hashtype = NULL; + struct cs_hash const *hashtype = NULL; unsigned char actual_hash[CS_HASH_MAX_SIZE]; unsigned char expected_hash[CS_HASH_MAX_SIZE]; boolean_t found_hash; @@ -3895,7 +4028,7 @@ ubc_cs_is_range_codesigned( } #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; /*