#include <string.h>
#include <arm/pmap.h>
#include <kern/debug.h>
#include <kern/trustcache.h>
#include <kern/misc_protos.h>
#include <libkern/section_keywords.h>
#include <mach/machine/vm_types.h>
#include <pexpert/device_tree.h>
#include <sys/cdefs.h>
SECURITY_READ_ONLY_LATE(static struct serialized_trust_caches *)pmap_serialized_trust_caches = NULL;
SECURITY_READ_ONLY_LATE(static struct trust_cache_module1 *)pmap_static_trust_cache = NULL;
#if CONFIG_SECOND_STATIC_TRUST_CACHE
SECURITY_READ_ONLY_LATE(static struct trust_cache_module1 *)pmap_secondary_static_trust_cache = NULL;
#endif
extern vm_offset_t segEXTRADATA;
extern unsigned long segSizeEXTRADATA;
void
trust_cache_init(void)
{
size_t const locked_down_dt_size = SecureDTIsLockedDown() ? PE_state.deviceTreeSize : 0;
size_t const len = segSizeEXTRADATA - locked_down_dt_size;
if (len == 0) {
printf("No external trust cache found (region len is 0).");
return;
}
pmap_serialized_trust_caches = (struct serialized_trust_caches*)(segEXTRADATA +
locked_down_dt_size);
uint8_t const *region_end = (uint8_t*)pmap_serialized_trust_caches + len;
if (len < sizeof(struct serialized_trust_caches)) {
panic("short serialized trust cache region: %zu", len);
}
printf("%d external trust cache modules available.\n", pmap_serialized_trust_caches->num_caches);
if (len < (sizeof(struct serialized_trust_caches) +
pmap_serialized_trust_caches->num_caches * sizeof(uint32_t))) {
panic("serialized trust cache region too short for its %d entries: %zu",
pmap_serialized_trust_caches->num_caches, len);
}
uint8_t *module_end = (uint8_t*)pmap_serialized_trust_caches;
for (uint32_t i = 0; i < pmap_serialized_trust_caches->num_caches; i++) {
struct trust_cache_module1 *module = (struct trust_cache_module1*)
((uint8_t*)pmap_serialized_trust_caches + pmap_serialized_trust_caches->offsets[i]);
if ((uint8_t*)module < module_end) {
panic("trust cache module %d overlaps previous module", i);
}
module_end = (uint8_t*)(module + 1);
if (module_end > region_end) {
panic("trust cache module %d too short for header", i);
}
if (module->version != 1) {
panic("trust cache module %d has unsupported version %d", i, module->version);
}
module_end += module->num_entries * sizeof(struct trust_cache_entry1);
if (module_end > region_end) {
panic("trust cache module %d too short for its %u entries", i, module->num_entries);
}
printf("external trust cache module %d with %d entries\n", i, module->num_entries);
if (i == 0) {
pmap_static_trust_cache = module;
}
#if CONFIG_SECOND_STATIC_TRUST_CACHE
else if (i == 1) {
pmap_secondary_static_trust_cache = module;
}
#endif
}
}
bool
lookup_in_trust_cache_module(
struct trust_cache_module1 const * const module,
uint8_t const cdhash[CS_CDHASH_LEN],
uint8_t * const hash_type,
uint8_t * const flags)
{
size_t lim;
struct trust_cache_entry1 const *base = &module->entries[0];
struct trust_cache_entry1 const *entry = NULL;
bool found = false;
for (lim = module->num_entries; lim != 0; lim >>= 1) {
entry = base + (lim >> 1);
int cmp = memcmp(cdhash, entry->cdhash, CS_CDHASH_LEN);
if (cmp == 0) {
found = true;
break;
}
if (cmp > 0) {
base = entry + 1;
lim--;
}
}
if (found) {
*hash_type = entry->hash_type;
*flags = entry->flags;
return true;
}
return false;
}
MARK_AS_PMAP_TEXT uint32_t
lookup_in_static_trust_cache(const uint8_t cdhash[CS_CDHASH_LEN])
{
uint8_t hash_type = 0, flags = 0;
uint32_t engineering_trust_cache_index = 1;
if (pmap_static_trust_cache != NULL) {
if (lookup_in_trust_cache_module(pmap_static_trust_cache, cdhash, &hash_type, &flags)) {
return (hash_type << TC_LOOKUP_HASH_TYPE_SHIFT) |
(flags << TC_LOOKUP_FLAGS_SHIFT) |
(TC_LOOKUP_FOUND << TC_LOOKUP_RESULT_SHIFT);
}
#if CONFIG_SECOND_STATIC_TRUST_CACHE
if (pmap_secondary_static_trust_cache != NULL &&
lookup_in_trust_cache_module(pmap_secondary_static_trust_cache, cdhash, &hash_type, &flags)) {
return (hash_type << TC_LOOKUP_HASH_TYPE_SHIFT) |
(flags << TC_LOOKUP_FLAGS_SHIFT) |
(TC_LOOKUP_FOUND << TC_LOOKUP_RESULT_SHIFT);
}
engineering_trust_cache_index = (pmap_secondary_static_trust_cache != NULL) ? 2 : 1;
#endif
if (pmap_serialized_trust_caches->num_caches > engineering_trust_cache_index) {
for (uint32_t i = engineering_trust_cache_index; i < pmap_serialized_trust_caches->num_caches; i++) {
struct trust_cache_module1 const *module =
(struct trust_cache_module1 const *)(
(uint8_t*)pmap_serialized_trust_caches + pmap_serialized_trust_caches->offsets[i]);
if (lookup_in_trust_cache_module(module, cdhash, &hash_type, &flags)) {
return (hash_type << TC_LOOKUP_HASH_TYPE_SHIFT) |
(flags << TC_LOOKUP_FLAGS_SHIFT) |
(TC_LOOKUP_FOUND << TC_LOOKUP_RESULT_SHIFT);
}
}
}
}
return 0;
}