Files
Cédric Bosdonnat 9fdffbbab3 Fix misleading indentation warnings
Gcc6 build reports misleading indentation as warnings. Fix a few
warnings in stubdom.

Signed-off-by: Cédric Bosdonnat <cbosdonnat@suse.com>
Acked-by: Daniel De Graaf <dgdegra@tycho.nsa.gov>
Acked-by: Quan Xu <xuquan8@huawei.com>
Release-acked-by: Wei Liu <wei.liu2@citrix.com>
2016-11-11 02:02:15 +00:00

620 lines
16 KiB
C

#include <console.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <inttypes.h>
#include <stdlib.h>
#include <stdbool.h>
#include <mini-os/byteorder.h>
#include "vtpm_manager.h"
#include "log.h"
#include "uuid.h"
#include "vtpmmgr.h"
#include "vtpm_disk.h"
#include "disk_tpm.h"
#include "disk_io.h"
#include "disk_crypto.h"
#include "disk_format.h"
static int disk_read_crypt_sector(void *data, size_t size, sector_t block, const struct mem_tpm_mgr *mgr)
{
struct disk_crypt_sector_plain *sector = disk_read_sector(block);
if (!sector)
return 2;
if (aes_cmac_verify(&sector->mac, sector->data, sizeof(sector->data), &mgr->tm_key_e))
return 2;
aes_decrypt_ctr(data, size, sector->iv_data, sizeof(sector->iv_data), &mgr->tm_key_e);
return 0;
}
static void group_free(struct mem_group *group)
{
int i, j;
if (!group)
return;
if (group->data) {
for (i = 0; i < group->nr_pages; i++) {
for (j = 0; j < group->data[i].size; j++) {
free(group->data[i].vtpms[j]);
}
}
free(group->data);
}
free(group->seals);
free(group);
}
static void mgr_free(struct mem_tpm_mgr *mgr)
{
int i;
if (!mgr)
return;
if (mgr->groups) {
for(i=0; i < mgr->nr_groups; i++)
group_free(mgr->groups[i].v);
free(mgr->groups);
}
free(mgr);
}
/* Open the group keys from one of the sealed strutures */
static int find_group_key(struct mem_group *dst,
const struct disk_group_sector *group,
const struct mem_tpm_mgr *parent)
{
int i, rc, rv = 1;
unsigned int olen;
struct hash160 buf;
struct disk_group_sealed_data sealed;
dst->nr_seals = be32_native(group->v.boot_configs.nr_cfgs);
if (dst->nr_seals > NR_SEALS_PER_GROUP)
return 3; // TODO support spill to extra pages
dst->seals = calloc(dst->nr_seals, sizeof(dst->seals[0]));
if (!dst->seals) {
vtpmlogerror(VTPM_LOG_VTPM, "find_group_key alloc %x\n", dst->nr_seals);
return 2;
}
for(i=0; i < dst->nr_seals; i++) {
const struct disk_seal_entry *cfg = &group->v.boot_configs.entry[i];
dst->seals[i].pcr_selection = cfg->pcr_selection;
memcpy(&dst->seals[i].digest_release, &cfg->digest_release, 20);
TPM_pcr_digest(&buf, cfg->pcr_selection);
if (memcmp(&buf, &cfg->digest_release, 20))
continue;
/*TPM 2.0 unbind | TPM 1.x unseal*/
if (hw_is_tpm2())
rc = TPM2_disk_unbind(&sealed, &olen, cfg);
else
rc = TPM_disk_unseal(&sealed, sizeof(sealed), cfg);
if (rc)
continue;
if (memcmp(&sealed.magic, DISK_GROUP_BOUND_MAGIC, 4))
continue;
if (memcmp(sealed.tpm_manager_uuid, parent->uuid, 16))
continue;
memcpy(&dst->rollback_mac_key, &sealed.rollback_mac_key, 16);
memcpy(&dst->group_key, &sealed.group_key, 16);
memcpy(&dst->aik_authdata, &sealed.aik_authdata, 20);
rv = 0;
}
// cache the list to allow writes without touching the TPM
memcpy(&dst->seal_bits, &group->v.boot_configs, sizeof(dst->seal_bits));
dst->flags |= MEM_GROUP_FLAG_SEAL_VALID;
return rv;
}
static int parse_root_key(struct mem_tpm_mgr *dst, struct disk_seal_entry *src)
{
int rc;
unsigned int olen;
struct disk_root_sealed_data sealed;
/*TPM 2.0 unbind | TPM 1.x unseal*/
if (hw_is_tpm2())
rc = TPM2_disk_unbind(&sealed, &olen, src);
else
rc = TPM_disk_unseal(&sealed, sizeof(sealed), src);
if (rc)
return rc;
if (memcmp(&sealed.magic, DISK_ROOT_BOUND_MAGIC, 4))
return 1;
rc = TPM_disk_nvread(&dst->nv_key, 16, sealed.nvram_slot, sealed.nvram_auth);
if (rc)
return rc;
// TODO when an NV slot in the physical TPM is used to populate nv_key,
// that value should be used to mask the master key so that the value
// can be changed to revoke old disk state
#if 0
aes_decrypt_one(&dst->tm_key, &sealed.tm_key, &dst->nv_key);
#else
memcpy(&dst->tm_key, &sealed.tm_key, 16);
#endif
memcpy(dst->uuid, sealed.tpm_manager_uuid, 16);
dst->nvram_slot = sealed.nvram_slot;
memcpy(&dst->nvram_auth, &sealed.nvram_auth, sizeof(struct tpm_authdata));
dst->counter_index = sealed.counter_index;
memcpy(&dst->counter_auth, &sealed.counter_auth, sizeof(struct tpm_authdata));
return 0;
}
static struct mem_tpm_mgr *find_root_key(int active_root)
{
sector_t seal_list = native_be32(active_root);
struct disk_seal_list *seal = disk_read_sector(seal_list);
struct hash160 buf;
int i, rc, nr;
struct mem_tpm_mgr *dst;
if (memcmp(seal->hdr.magic, TPM_MGR_MAGIC, 12))
return NULL;
if (be32_native(seal->hdr.version) != TPM_MGR_VERSION)
return NULL;
dst = calloc(1, sizeof(*dst));
dst->active_root = active_root;
for (nr = 0; nr < 100; nr++) {
disk_set_used(seal_list, dst);
uint32_t nr_seals = be32_native(seal->length);
if (nr_seals > SEALS_PER_ROOT_SEAL_LIST)
break;
for (i = 0; i < nr_seals; i++) {
struct disk_seal_entry *src = &seal->entry[i];
TPM_pcr_digest(&buf, src->pcr_selection);
if (memcmp(&buf, &src->digest_release, 20))
continue;
rc = parse_root_key(dst, src);
if (rc)
continue;
return dst;
}
seal_list = seal->next;
if (seal_list.value == 0)
break;
seal = disk_read_sector(seal_list);
}
mgr_free(dst);
return NULL;
}
/* Load and verify one sector's worth of vTPMs. This loads all the vTPM entries
* and decrypts their state data into memory.
*/
static int load_verify_vtpm_page(struct mem_vtpm_page *dst, int base,
const struct mem_tpm_mgr *mgr, const aes_context *group_key)
{
struct disk_vtpm_sector pt;
int i, rc;
disk_set_used(dst->disk_loc, mgr);
rc = disk_read_crypt_sector(&pt, sizeof(pt), dst->disk_loc, mgr);
if (rc) {
printk("Malformed sector %d\n", be32_native(dst->disk_loc));
return rc;
}
rc = sha256_verify(&dst->disk_hash, &pt, sizeof(pt));
if (rc) {
printk("Hash mismatch in sector %d\n", be32_native(dst->disk_loc));
return rc;
}
if (!group_key)
return 0;
aes_decrypt_ctr(pt.data, sizeof(pt.data), &pt.iv, sizeof(pt.data) + 16, group_key);
for (i = 0; i < dst->size; i++) {
struct mem_vtpm *vtpm = calloc(1, sizeof(*vtpm));
dst->vtpms[i] = vtpm;
memcpy(vtpm->uuid, pt.header[i].uuid, 16);
memcpy(vtpm->data, pt.data[i].data, 64);
vtpm->flags = be32_native(pt.header[i].flags);
vtpm->index_in_parent = i + base;
}
return 0;
}
static int load_verify_vtpm_pages(struct mem_group *group, int base, int size,
const struct hash256 *hash, const sector_t *loc,
const struct mem_tpm_mgr *mgr, const aes_context *group_key)
{
int i, rc;
struct mem_vtpm_page *page = group->data + base;
/* base was in terms of sectors; convert to vtpms */
base *= VTPMS_PER_SECTOR;
for (i = 0; i < size; i++) {
page->disk_hash = hash[i];
page->disk_loc = loc[i];
if (group->nr_vtpms - base > VTPMS_PER_SECTOR)
page->size = VTPMS_PER_SECTOR;
else
page->size = group->nr_vtpms - base;
rc = load_verify_vtpm_page(page, base, mgr, group_key);
if (rc)
return rc;
base += VTPMS_PER_SECTOR;
}
return 0;
}
static int load_verify_vtpm_itree(struct mem_group_hdr *hdr, int base, int nr_entries,
const struct hash256 *hash, const sector_t *loc, int hsize,
const struct mem_tpm_mgr *mgr, const aes_context *group_key);
static int load_verify_vtpm_itree(struct mem_group_hdr *hdr, int base, int nr_entries,
const struct hash256 *hash, const sector_t *loc, int hsize,
const struct mem_tpm_mgr *mgr, const aes_context *group_key)
{
int i, rc, incr = 1, inuse_base = hdr->disk_nr_inuse, lsize;
// increase tree depth until all entries fit
while (nr_entries > incr * hsize)
incr *= NR_ENTRIES_PER_ITREE;
// save the list of used sectors (itree and vtpm) in the header
lsize = 1 + (nr_entries - 1) / incr;
hdr->disk_nr_inuse += lsize;
hdr->disk_inuse = realloc(hdr->disk_inuse, hdr->disk_nr_inuse * sizeof(sector_t));
memcpy(&hdr->disk_inuse[inuse_base], loc, lsize * sizeof(sector_t));
// if the entries already fit, process vtpm pages
if (nr_entries <= hsize)
return load_verify_vtpm_pages(hdr->v, base, nr_entries, hash, loc, mgr, group_key);
for (i = 0; i * incr < nr_entries; i++) {
struct disk_itree_sector pt;
int child_entries = incr;
// the last sector is not completely full
if (nr_entries - i * incr < incr)
child_entries = nr_entries - i * incr;
disk_set_used(loc[i], mgr);
hdr->disk_inuse[inuse_base++] = loc[i];
rc = disk_read_crypt_sector(&pt, sizeof(pt), loc[i], mgr);
if (rc) {
printk("Malformed sector %d\n", be32_native(loc[i]));
return rc;
}
rc = sha256_verify(&hash[i], pt.hash, sizeof(pt.hash));
if (rc) {
printk("Hash mismatch in sector %d\n", be32_native(loc[i]));
return rc;
}
rc = load_verify_vtpm_itree(hdr, base, child_entries, pt.hash, pt.location,
NR_ENTRIES_PER_ITREE, mgr, group_key);
if (rc)
return rc;
base += incr;
}
return 0;
}
/* Load and verify one group's data structure, including its vTPMs.
*/
static int load_verify_group(struct mem_group_hdr *dst, const struct mem_tpm_mgr *mgr)
{
struct mem_group *group;
struct disk_group_sector disk;
int rc;
aes_context key_e;
aes_context *opened_key = NULL;
disk_set_used(dst->disk_loc, mgr);
rc = disk_read_crypt_sector(&disk, sizeof(disk), dst->disk_loc, mgr);
if (rc) {
printk("Malformed sector %d\n", be32_native(dst->disk_loc));
return rc;
}
rc = sha256_verify(&dst->disk_hash, &disk.v, sizeof(disk.v) + sizeof(disk.group_mac));
if (rc) {
printk("Hash mismatch in sector %d\n", be32_native(dst->disk_loc));
return rc;
}
dst->v = group = calloc(1, sizeof(*group));
rc = find_group_key(group, &disk, mgr);
if (rc == 0) {
opened_key = &key_e;
/* Verify the group with the group's own key */
aes_setup(opened_key, &group->group_key);
if (aes_cmac_verify(&disk.group_mac, &disk.v, sizeof(disk.v), opened_key)) {
printk("Group CMAC failed\n");
return 2;
}
memcpy(&group->id_data, &disk.v.id_data, sizeof(group->id_data));
memcpy(&group->details, &disk.v.details, sizeof(group->details));
} else if (rc == 1) {
// still need to walk the vtpm list
rc = 0;
} else {
printk("Group key unsealing failed\n");
return rc;
}
group->nr_vtpms = be32_native(disk.v.nr_vtpms);
group->nr_pages = (group->nr_vtpms + VTPMS_PER_SECTOR - 1) / VTPMS_PER_SECTOR;
group->data = calloc(group->nr_pages, sizeof(group->data[0]));
rc = load_verify_vtpm_itree(dst, 0, group->nr_pages, disk.v.vtpm_hash,
disk.vtpm_location, NR_ENTRIES_PER_GROUP_BASE, mgr, opened_key);
if (!opened_key) {
/* remove the struct */
free(group->data);
free(group->seals);
free(group);
dst->v = NULL;
}
return rc;
}
static int load_root_pre(struct disk_root_sector *root, struct mem_tpm_mgr *dst)
{
int rc;
aes_setup(&dst->tm_key_e, &dst->tm_key);
rc = disk_read_crypt_sector(root, sizeof(*root), root_loc(dst), dst);
if (rc) {
vtpmloginfo(VTPM_LOG_VTPM, "root cmac verify failed in slot %d\n", dst->active_root);
return 2;
}
dst->root_seals_valid = 1 + dst->active_root;
dst->sequence = be64_native(root->v.sequence);
return 0;
}
static int load_verify_group_itree(struct mem_tpm_mgr *dst, int base, int nr_entries,
const struct hash256 *hash, const sector_t *loc, int hsize);
static int load_verify_group_itree(struct mem_tpm_mgr *dst, int base, int nr_entries,
const struct hash256 *hash, const sector_t *loc, int hsize)
{
int i, rc, incr = 1;
if (nr_entries <= hsize) {
for(i=0; i < nr_entries; i++) {
struct mem_group_hdr *group = dst->groups + base + i;
group->disk_loc = loc[i];
memcpy(&group->disk_hash, &hash[i], sizeof(group->disk_hash));
rc = load_verify_group(group, dst);
if (rc) {
printk("Error loading group %d\n", base + i);
return rc;
}
}
return 0;
}
// increase tree depth until all entries fit
while (nr_entries > incr * hsize)
incr *= NR_ENTRIES_PER_ITREE;
for (i = 0; i * incr < nr_entries; i++) {
struct disk_itree_sector pt;
int child_entries = incr;
// the last sector is not completely full
if (nr_entries - i * incr < incr)
child_entries = nr_entries - i * incr;
disk_set_used(loc[i], dst);
rc = disk_read_crypt_sector(&pt, sizeof(pt), loc[i], dst);
if (rc) {
printk("Malformed sector %d\n", be32_native(loc[i]));
return rc;
}
rc = sha256_verify(&hash[i], pt.hash, sizeof(pt.hash));
if (rc) {
printk("Hash mismatch in sector %d\n", be32_native(loc[i]));
return rc;
}
rc = load_verify_group_itree(dst, base, child_entries, pt.hash, pt.location, NR_ENTRIES_PER_ITREE);
if (rc)
return rc;
base += incr;
}
return 0;
}
static int load_root_post(struct mem_tpm_mgr *dst, const struct disk_root_sector *root)
{
int rc, i, j;
uint32_t nr_disk_rbs = be32_native(root->nr_rb_macs);
rc = TPM_disk_check_counter(dst->counter_index, dst->counter_auth,
root->v.tpm_counter_value);
if (rc)
return 2;
dst->counter_value = root->v.tpm_counter_value;
dst->nr_groups = be32_native(root->v.nr_groups);
dst->groups = calloc(sizeof(dst->groups[0]), dst->nr_groups);
if (!dst->groups) {
vtpmlogerror(VTPM_LOG_VTPM, "load_root_post alloc %x\n", dst->nr_groups);
return 2;
}
rc = load_verify_group_itree(dst, 0, dst->nr_groups,
root->v.group_hash, root->group_loc, NR_ENTRIES_PER_ROOT);
if (rc)
return rc;
/* Sanity check: group0 must be open */
if (!dst->groups[0].v) {
printk("Error opening group 0\n");
return 2;
}
/* TODO support for spilling rollback list */
if (nr_disk_rbs > NR_RB_MACS_PER_ROOT)
return 3;
i = 0;
j = 0;
while (i < dst->nr_groups) {
aes_context key_e;
struct mem_group_hdr *group = &dst->groups[i];
struct mem_group *groupv = group->v;
const struct disk_rb_mac_entry *ent = &root->rb_macs[j];
if (!groupv) {
i++;
// this group is not open - no need to verify now
continue;
}
if (be32_native(ent->id) < i) {
// this entry is for a group that is not open
j++;
continue;
}
if (j >= nr_disk_rbs || be32_native(ent->id) != i) {
// TODO allow delegation
if (!(groupv->details.flags.value & FLAG_ROLLBACK_DETECTED)) {
groupv->details.flags.value |= FLAG_ROLLBACK_DETECTED;
group->disk_loc.value = 0;
}
i++;
continue;
}
aes_setup(&key_e, &groupv->rollback_mac_key);
if (aes_cmac_verify(&ent->mac, &root->v, sizeof(root->v), &key_e)) {
if (!(groupv->details.flags.value & FLAG_ROLLBACK_DETECTED)) {
groupv->details.flags.value |= FLAG_ROLLBACK_DETECTED;
group->disk_loc.value = 0;
}
}
i++; j++;
}
return 0;
}
int vtpm_load_disk(void)
{
struct disk_root_sector root1, root2;
int rc = 0;
TPM_read_pcrs();
printk("TPM Manager - disk format %d\n", TPM_MGR_VERSION);
printk(" root seal: %zu; sector of %d: %zu\n",
sizeof(struct disk_root_sealed_data), SEALS_PER_ROOT_SEAL_LIST, sizeof(struct disk_seal_list));
printk(" root: %zu v=%zu\n", sizeof(root1), sizeof(root1.v));
printk(" itree: %u; sector of %d: %zu\n",
4 + 32, NR_ENTRIES_PER_ITREE, sizeof(struct disk_itree_sector));
printk(" group: %zu v=%zu id=%zu md=%zu\n",
sizeof(struct disk_group_sector), sizeof(struct disk_group_sector_mac3_area),
sizeof(struct group_id_data), sizeof(struct group_details));
printk(" group seal: %zu; %d in parent: %zu; sector of %d: %zu\n",
sizeof(struct disk_group_sealed_data), NR_SEALS_PER_GROUP, sizeof(struct disk_group_boot_config_list),
SEALS_PER_GROUP_SEAL_LIST, sizeof(struct disk_group_seal_list));
printk(" vtpm: %zu+%zu; sector of %d: %zu\n",
sizeof(struct disk_vtpm_plain), sizeof(struct disk_vtpm_secret),
VTPMS_PER_SECTOR, sizeof(struct disk_vtpm_sector));
struct mem_tpm_mgr *mgr1 = find_root_key(0);
struct mem_tpm_mgr *mgr2 = find_root_key(1);
rc = mgr1 ? load_root_pre(&root1, mgr1) : 0;
if (rc) {
mgr_free(mgr1);
mgr1 = NULL;
}
rc = mgr2 ? load_root_pre(&root2, mgr2) : 0;
if (rc) {
mgr_free(mgr2);
mgr2 = NULL;
}
printk("load_root_pre: %c/%c\n", mgr1 ? 'y' : 'n', mgr2 ? 'y' : 'n');
if (!mgr1 && !mgr2)
return 2;
if (mgr1 && mgr2 && mgr2->sequence > mgr1->sequence) {
rc = load_root_post(mgr2, &root2);
if (rc) {
mgr_free(mgr2);
mgr2 = NULL;
} else {
mgr_free(mgr1);
g_mgr = mgr2;
return 0;
}
}
if (mgr1) {
rc = load_root_post(mgr1, &root1);
if (rc) {
mgr_free(mgr1);
} else {
mgr_free(mgr2);
g_mgr = mgr1;
return 0;
}
}
if (mgr2) {
rc = load_root_post(mgr2, &root2);
if (rc) {
mgr_free(mgr2);
} else {
g_mgr = mgr2;
return 0;
}
}
printk("Could not read vTPM disk\n");
return 2;
}