#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 */
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,
.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,
.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,
.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,
};
#endif
-static struct cs_hash *
+static struct cs_hash const *
cs_find_md(uint8_t type)
{
if (type == CS_HASHTYPE_SHA1) {
* 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,
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;
*/
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) {
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);
* 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_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);
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) {
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)
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;
/*
* 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);
*/
#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 (blob->csb_flags & CS_PLATFORM_BINARY) {
if (cs_debug > 1)
printf("check_signature[pid: %d]: platform binary\n", current_proc()->p_pid);
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;
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;
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;
}
#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;
/*