]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/kern/ubc_subr.c
xnu-4570.1.46.tar.gz
[apple/xnu.git] / bsd / kern / ubc_subr.c
index 27a8cc5116e0b695db3155190d736713942e3c55..e05d13c3e64f09411c3a136efb0200fe4a1a7306 100644 (file)
@@ -65,6 +65,7 @@
 #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 */
 
@@ -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;
 
 /*