From 4c87a1868835d05f1cadae7b8ad6a7c95d9d9c0e Mon Sep 17 00:00:00 2001 From: Ross Philipson Date: Tue, 14 Mar 2017 15:40:33 -0400 Subject: Initial commit of EFI TBOOT work from internal project. Signed-off-by: Ross Philipson --- tboot/policy.c | 879 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 879 insertions(+) create mode 100644 tboot/policy.c (limited to 'tboot/policy.c') diff --git a/tboot/policy.c b/tboot/policy.c new file mode 100644 index 0000000..3c4d7d1 --- /dev/null +++ b/tboot/policy.c @@ -0,0 +1,879 @@ +/* + * policy.c: support functions for tboot verification launch + * + * Copyright (c) 2006-2014, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of the Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define PRINT printk +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * policy actions + */ +typedef enum { + TB_POLACT_CONTINUE, + TB_POLACT_UNMEASURED_LAUNCH, + TB_POLACT_HALT, +} tb_policy_action_t; + +/* policy map types */ +typedef struct { + tb_error_t error; + tb_policy_action_t action; +} tb_policy_map_entry_t; + +typedef struct { + uint8_t policy_type; + tb_policy_action_t default_action; + tb_policy_map_entry_t exception_action_table[TB_ERR_MAX]; + /* have TB_ERR_NONE as last entry */ +} tb_policy_map_t; + +/* map */ +static const tb_policy_map_t g_policy_map[] = { + { TB_POLTYPE_CONT_NON_FATAL, TB_POLACT_CONTINUE, + { + {TB_ERR_FATAL, TB_POLACT_HALT}, + {TB_ERR_TPM_NOT_READY, TB_POLACT_UNMEASURED_LAUNCH}, + {TB_ERR_SMX_NOT_SUPPORTED, TB_POLACT_UNMEASURED_LAUNCH}, + {TB_ERR_VMX_NOT_SUPPORTED, TB_POLACT_UNMEASURED_LAUNCH}, + {TB_ERR_TXT_NOT_SUPPORTED, TB_POLACT_UNMEASURED_LAUNCH}, + {TB_ERR_SINIT_NOT_PRESENT, TB_POLACT_UNMEASURED_LAUNCH}, + {TB_ERR_ACMOD_VERIFY_FAILED, TB_POLACT_UNMEASURED_LAUNCH}, + {TB_ERR_NONE, TB_POLACT_CONTINUE}, + } + }, + + { TB_POLTYPE_CONT_VERIFY_FAIL, TB_POLACT_HALT, + { + {TB_ERR_MODULE_VERIFICATION_FAILED, TB_POLACT_CONTINUE}, + {TB_ERR_NV_VERIFICATION_FAILED, TB_POLACT_CONTINUE}, + {TB_ERR_POLICY_NOT_PRESENT, TB_POLACT_CONTINUE}, + {TB_ERR_POLICY_INVALID, TB_POLACT_CONTINUE}, + {TB_ERR_NONE, TB_POLACT_CONTINUE}, + } + }, + + { TB_POLTYPE_HALT, TB_POLACT_HALT, + { + {TB_ERR_NONE, TB_POLACT_CONTINUE}, + } + }, +}; + +/* buffer for policy as read from TPM NV */ +#define MAX_POLICY_SIZE \ + (( MAX_TB_POLICY_SIZE > sizeof(lcp_policy_t) ) \ + ? MAX_TB_POLICY_SIZE \ + : sizeof(lcp_policy_t) ) +static uint8_t _policy_index_buf[MAX_POLICY_SIZE]; + +/* default policy */ +static const tb_policy_t _def_policy = { + version : 2, + policy_type : TB_POLTYPE_CONT_NON_FATAL, + hash_alg : TB_HALG_SHA1, + policy_control : TB_POLCTL_EXTEND_PCR17, + num_entries : 3, + entries : { + { /* mod 0 is extended to PCR 18 by default, so don't re-extend it */ + mod_num : 0, + pcr : TB_POL_PCR_NONE, + hash_type : TB_HTYPE_ANY, + num_hashes : 0 + }, + { /* all other modules are extended to PCR 19 */ + mod_num : TB_POL_MOD_NUM_ANY, + pcr : 19, + hash_type : TB_HTYPE_ANY, + num_hashes : 0 + }, + { /* NV index for geo-tagging will be extended to PCR 22 */ + mod_num : TB_POL_MOD_NUM_NV_RAW, + pcr : 22, + hash_type : TB_HTYPE_ANY, + nv_index : 0x40000010, + num_hashes : 0 + } + } +}; + +/* default policy for Details/Authorities pcr mapping */ +static const tb_policy_t _def_policy_da = { + version : 2, + policy_type : TB_POLTYPE_CONT_NON_FATAL, + hash_alg : TB_HALG_SHA1, + policy_control : TB_POLCTL_EXTEND_PCR17, + num_entries : 3, + entries : { + { /* mod 0 is extended to PCR 17 by default, so don't re-extend it */ + mod_num : 0, + pcr : TB_POL_PCR_NONE, + hash_type : TB_HTYPE_ANY, + num_hashes : 0 + }, + { /* all other modules are extended to PCR 17 */ + mod_num : TB_POL_MOD_NUM_ANY, + pcr : 17, + hash_type : TB_HTYPE_ANY, + num_hashes : 0 + }, + { /* NV index for geo-tagging will be extended to PCR 22 */ + mod_num : TB_POL_MOD_NUM_NV_RAW, + pcr : 22, + hash_type : TB_HTYPE_ANY, + nv_index : 0x40000010, + num_hashes : 0 + } + } +}; + +/* current policy */ +static const tb_policy_t* g_policy = &_def_policy; + +/* + * read_policy_from_tpm + * + * read policy from TPM NV into buffer + * + * policy_index_size is in/out + */ +static bool read_policy_from_tpm(uint32_t index, void* policy_index, size_t *policy_index_size) +{ +#define NV_READ_SEG_SIZE 256 + unsigned int offset = 0; + unsigned int data_size = 0; + uint32_t ret, index_size; + + if ( policy_index_size == NULL ) { + printk(TBOOT_ERR"size is NULL\n"); + return false; + } + + ret = g_tpm->get_nvindex_size(g_tpm, g_tpm->cur_loc, index, &index_size); + if ( !ret ) + return false; + + if ( index_size > *policy_index_size ) { + printk(TBOOT_WARN"policy in TPM NV %x was too big for buffer\n", index); + index_size = *policy_index_size; + } + + + do { + /* get data_size */ + if ( (index_size - offset) > NV_READ_SEG_SIZE ) + data_size = NV_READ_SEG_SIZE; + else + data_size = (uint32_t)(index_size - offset); + + /* read! */ + ret = g_tpm->nv_read(g_tpm, g_tpm->cur_loc, index, offset, + (uint8_t *)policy_index + offset, &data_size); + if ( !ret || data_size == 0 ) + break; + + /* adjust offset */ + offset += data_size; + } while ( offset < index_size ); + + if ( offset == 0 && !ret ) { + printk(TBOOT_ERR"Error: read TPM error: 0x%x from index %x.\n", ret, index); + return false; + } + + *policy_index_size = offset; + + return true; +} + +/* + * unwrap_lcp_policy + * + * unwrap custom element in lcp policy into tb policy + * assume sinit has already verified lcp policy and lcp policy data. + */ +static bool unwrap_lcp_policy(void) +{ + void* lcp_base; + uint32_t lcp_size; + const efi_file_t *lcp; + + // scaffolding + printk(TBOOT_INFO"in unwrap_lcp_policy\n"); + + if ( txt_is_launched() ) { + txt_heap_t *txt_heap = get_txt_heap(); + os_sinit_data_t *os_sinit_data = get_os_sinit_data_start(txt_heap); + + lcp_base = (void *)(unsigned long long)os_sinit_data->lcp_po_base; + lcp_size = (uint32_t)os_sinit_data->lcp_po_size; + } + else { + lcp = efi_get_lcp(); + if ( lcp == NULL ) + return false; + lcp_base = lcp->u.buffer; + lcp_size = lcp->size; + } + + /* if lcp policy data version is 2+ */ + if ( memcmp((void *)lcp->u.buffer, LCP_POLICY_DATA_FILE_SIGNATURE, + LCP_FILE_SIG_LENGTH) == 0 ) { + lcp_policy_data_t *poldata = (lcp_policy_data_t *)lcp_base; + lcp_policy_list_t *pollist = &poldata->policy_lists[0]; + + for ( int i = 0; i < poldata->num_lists; i++ ) { + lcp_policy_element_t *elt = pollist->policy_elements; + uint32_t elts_size = 0; + + while ( elt ) { + /* check element type */ + if ( elt->type == LCP_POLELT_TYPE_CUSTOM || + elt->type == LCP_POLELT_TYPE_CUSTOM2 ) { + lcp_custom_element_t *custom = + (lcp_custom_element_t *)&elt->data; + + /* check uuid in custom element */ + if ( are_uuids_equal(&custom->uuid, + &((uuid_t)LCP_CUSTOM_ELEMENT_TBOOT_UUID)) ) { + memcpy(_policy_index_buf, &custom->data, + elt->size - sizeof(*elt) - sizeof(uuid_t)); + return true; /* find tb policy */ + } + } + + elts_size += elt->size; + if ( elts_size >= pollist->policy_elements_size ) + break; + + elt = (void *)elt + elt->size; + } + if ( pollist->version == LCP_TPM12_POLICY_LIST_VERSION ) + pollist = (void *)pollist + get_tpm12_policy_list_size(pollist); + else if ( pollist->version == LCP_TPM20_POLICY_LIST_VERSION ) + pollist = (void *)pollist + get_tpm20_policy_list_size( + (lcp_policy_list_t2 *)pollist); + } + } + + return false; +} + +/* + * set_policy + * + * load policy from TPM NV and validate it, else use default + * + */ +tb_error_t set_policy(void) +{ + /* try to read tboot policy from TB_POLICY_INDEX in TPM NV */ + size_t policy_index_size = sizeof(_policy_index_buf); + printk(TBOOT_INFO"reading Verified Launch Policy from TPM NV...\n"); + if ( read_policy_from_tpm(g_tpm->tb_policy_index, + _policy_index_buf, &policy_index_size) ) { + printk(TBOOT_DETA"\t:%lu bytes read\n", policy_index_size); + if ( verify_policy((tb_policy_t *)_policy_index_buf, + policy_index_size, true) ) { + goto policy_found; + } + } + printk(TBOOT_WARN"\t:reading failed\n"); + + /* tboot policy not found in TB_POLICY_INDEX, so see if it is wrapped + * in a custom element in the PO policy; if so, SINIT will have verified + * the policy and policy data for us; we just need to ensure the policy + * type is LCP_POLTYPE_LIST (since we could have been give a policy data + * file even though the policy was not a LIST */ + printk(TBOOT_INFO"reading Launch Control Policy from TPM NV...\n"); + if ( read_policy_from_tpm(g_tpm->lcp_own_index, + _policy_index_buf, &policy_index_size) ) { + printk(TBOOT_DETA"\t:%lu bytes read\n", policy_index_size); + /* assume lcp policy has been verified by sinit already */ + lcp_policy_t *pol = (lcp_policy_t *)_policy_index_buf; + if ( pol->version == LCP_DEFAULT_POLICY_VERSION_2 && + pol->policy_type == LCP_POLTYPE_LIST && unwrap_lcp_policy() ) { + if ( verify_policy((tb_policy_t *)_policy_index_buf, + calc_policy_size((tb_policy_t *)_policy_index_buf), + true) ) + goto policy_found; + } + lcp_policy_t2 *pol2 = (lcp_policy_t2 *)_policy_index_buf; + if ( pol2->version == LCP_DEFAULT_POLICY_VERSION && + pol2->policy_type == LCP_POLTYPE_LIST && unwrap_lcp_policy() ) { + if ( verify_policy((tb_policy_t *)_policy_index_buf, + calc_policy_size((tb_policy_t *)_policy_index_buf), + true) ) + goto policy_found; + } + } + printk(TBOOT_WARN"\t:reading failed\n"); + + /* either no policy in TPM NV or policy is invalid, so use default */ + printk(TBOOT_WARN"failed to read policy from TPM NV, using default\n"); + g_policy = g_using_da ? &_def_policy_da : &_def_policy; + policy_index_size = calc_policy_size(g_policy); + + /* sanity check; but if it fails something is really wrong */ + if ( !verify_policy(g_policy, policy_index_size, true) ) + return TB_ERR_FATAL; + else + return TB_ERR_POLICY_NOT_PRESENT; + +policy_found: + /* compatible with tb_policy tools for TPM 1.2 */ + { + tb_policy_t *tmp_policy = (tb_policy_t *)_policy_index_buf; + if (tmp_policy->hash_alg == 0) + tmp_policy->hash_alg = TB_HALG_SHA1; + } + g_policy = (tb_policy_t *)_policy_index_buf; + return TB_ERR_NONE; +} + +/* hash current policy */ +bool hash_policy(tb_hash_t *hash, uint16_t hash_alg) +{ + if ( hash == NULL ) { + printk(TBOOT_ERR"Error: input parameter is wrong.\n"); + return false; + } + + return hash_buffer((unsigned char *)g_policy, calc_policy_size(g_policy), + hash, hash_alg); +} + +/* generate hash by hashing cmdline and module image */ +static bool hash_module(hash_list_t *hl, + const char* cmdline, void *base, + size_t size) +{ + if ( hl == NULL ) { + printk(TBOOT_ERR"Error: input parameter is wrong.\n"); + return false; + } + + /* final hash is SHA-1( SHA-1(cmdline) | SHA-1(image) ) */ + /* where cmdline is first stripped of leading spaces, file name, then */ + /* any spaces until the next non-space char */ + /* (e.g. " /foo/bar baz" -> "baz"; "/foo/bar" -> "") */ + + /* hash command line */ + if ( cmdline == NULL ) + cmdline = ""; + // else + // cmdline = skip_filename(cmdline); + + switch (g_tpm->extpol) { + case TB_EXTPOL_FIXED: + hl->count = 1; + hl->entries[0].alg = g_tpm->cur_alg; + + if ( !hash_buffer((const unsigned char *)cmdline, strlen(cmdline), + &hl->entries[0].hash, g_tpm->cur_alg) ) + return false; + /* hash image and extend into cmdline hash */ + tb_hash_t img_hash; + if ( !hash_buffer(base, size, &img_hash, g_tpm->cur_alg) ) + return false; + if ( !extend_hash(&hl->entries[0].hash, &img_hash, g_tpm->cur_alg) ) + return false; + + break; + + case TB_EXTPOL_AGILE: + { + hash_list_t img_hl, final_hl; + if ( !g_tpm->hash(g_tpm, 2, (const unsigned char *)cmdline, + strlen(cmdline), hl) ) { + if ( !g_tpm->hash(g_tpm, 2, base, size, hl) ) + return false; + return true; + } + + uint8_t buf[128]; + + if ( !g_tpm->hash(g_tpm, 2, base, size, &img_hl) ) + return false; + for (unsigned int i=0; icount; i++) { + for (unsigned int j=0; jentries[i].alg == img_hl.entries[j].alg) { + copy_hash((tb_hash_t *)buf, &hl->entries[i].hash, + hl->entries[i].alg); + copy_hash((tb_hash_t *)buf + get_hash_size(hl->entries[i].alg), + &img_hl.entries[j].hash, hl->entries[i].alg); + if ( !g_tpm->hash(g_tpm, 2, buf, + 2*get_hash_size(hl->entries[i].alg), &final_hl) ) + return false; + + for (unsigned int k=0; kentries[i].alg == final_hl.entries[k].alg) { + copy_hash(&hl->entries[i].hash, + &final_hl.entries[k].hash, + hl->entries[i].alg); + break; + } + } + + break; + } + } + } + + break; + } + + case TB_EXTPOL_EMBEDDED: + { + tb_hash_t img_hash; + hl->count = g_tpm->alg_count; + for (unsigned int i=0; icount; i++) { + hl->entries[i].alg = g_tpm->algs[i]; + if ( !hash_buffer((const unsigned char *)cmdline, strlen(cmdline), + &hl->entries[i].hash, g_tpm->algs[i]) ) + return false; + + if ( !hash_buffer(base, size, &img_hash, g_tpm->algs[i]) ) + return false; + if ( !extend_hash(&hl->entries[i].hash, &img_hash, g_tpm->algs[i]) ) + return false; + } + + break; + } + + default: + return false; + } + + return true; +} + +static bool is_hash_in_policy_entry(const tb_policy_entry_t *pol_entry, + tb_hash_t *hash, uint16_t hash_alg) +{ + /* assumes policy entry has been validated */ + + if ( pol_entry == NULL || hash == NULL) { + printk(TBOOT_ERR"Error: input parameter is wrong.\n"); + return false; + } + + if ( pol_entry->hash_type == TB_HTYPE_ANY ) + return true; + else if ( pol_entry->hash_type == TB_HTYPE_IMAGE ) { + for ( int i = 0; i < pol_entry->num_hashes; i++ ) { + if ( are_hashes_equal(get_policy_entry_hash(pol_entry, hash_alg, + i), hash, hash_alg) ) + return true; + } + } + + return false; +} + +/* + * map policy type + error -> action + */ +static tb_policy_action_t evaluate_error(tb_error_t error) +{ + tb_policy_action_t action = TB_POLACT_HALT; + + if ( error == TB_ERR_NONE ) + return TB_POLACT_CONTINUE; + + for ( unsigned int i = 0; i < ARRAY_SIZE(g_policy_map); i++ ) { + if ( g_policy_map[i].policy_type == g_policy->policy_type ) { + action = g_policy_map[i].default_action; + for ( unsigned int j = 0; + j < ARRAY_SIZE(g_policy_map[i].exception_action_table); + j++ ) { + if ( g_policy_map[i].exception_action_table[j].error == + error ) + action = g_policy_map[i].exception_action_table[j].action; + if ( g_policy_map[i].exception_action_table[j].error == + TB_ERR_NONE ) + break; + } + } + } + + return action; +} + +/* + * apply policy according to error happened. + */ +void apply_policy(tb_error_t error) +{ + tb_policy_action_t action; + + /* save the error to TPM NV */ + write_tb_error_code(error); + + if ( error != TB_ERR_NONE ) + print_tb_error_msg(error); + + action = evaluate_error(error); + switch ( action ) { + case TB_POLACT_CONTINUE: + return; + case TB_POLACT_UNMEASURED_LAUNCH: + /* restore mtrr state saved before */ + restore_mtrrs(NULL); + if ( s3_flag ) + s3_launch(); + else + efi_launch_kernel(); + break; /* if launch xen fails, do halt at the end */ + case TB_POLACT_HALT: + break; /* do halt at the end */ + default: + printk(TBOOT_ERR"Error: invalid policy action (%d)\n", action); + /* do halt at the end */ + } + + _tboot_shared.shutdown_type = TB_SHUTDOWN_HALT; + shutdown(); +} + +#define VL_ENTRIES(i) g_pre_k_s3_state.vl_entries[i] +#define NUM_VL_ENTRIES g_pre_k_s3_state.num_vl_entries + +/* + * verify modules against Verified Launch policy and save hash + * if pol_entry is NULL, assume it is for module 0, which gets extended + * to PCR 18 + */ +static tb_error_t verify_module(void *base, size_t size, + tb_policy_entry_t *pol_entry, + uint16_t hash_alg) +{ + /* assumes module is valid */ + + char *cmdline = NULL; /* TODO get from configs get_module_cmd(g_ldr_ctx, module);*/ + + if ( pol_entry != NULL ) { + /* chunk the command line into 80 byte chunks */ +#define CHUNK_SIZE 80 + int cmdlen = strlen(cmdline); + char *cptr = cmdline; + char cmdchunk[CHUNK_SIZE+1]; + printk(TBOOT_INFO"verifying module \""); + while (cmdlen > 0) { + strncpy(cmdchunk, cptr, CHUNK_SIZE); + cmdchunk[CHUNK_SIZE] = 0; + printk(TBOOT_INFO"\n%s", cmdchunk); + cmdlen -= CHUNK_SIZE; + cptr += CHUNK_SIZE; + } + printk(TBOOT_INFO"\"...\n"); + } + + hash_list_t hl; + if ( !hash_module(&hl, cmdline, base, size) ) { + printk(TBOOT_ERR"\t hash cannot be generated.\n"); + return TB_ERR_MODULE_VERIFICATION_FAILED; + } + + /* add new hash to list (unless it doesn't get put in a PCR) + we'll just drop it if the list is full, but that will mean S3 resume + PCRs won't match pre-S3 + NULL pol_entry means this is module 0 which is extended to PCR 18 */ + if ( NUM_VL_ENTRIES >= MAX_VL_HASHES ) + printk(TBOOT_WARN"\t too many hashes to save\n"); + else if ( pol_entry == NULL || pol_entry->pcr != TB_POL_PCR_NONE ) { + uint8_t pcr = (pol_entry == NULL ) ? + (g_using_da ? 17 : 18) : pol_entry->pcr; + VL_ENTRIES(NUM_VL_ENTRIES).pcr = pcr; + VL_ENTRIES(NUM_VL_ENTRIES++).hl = hl; + } + + if ( g_tpm->extpol != TB_EXTPOL_FIXED ) + return TB_ERR_NONE; + + if ( pol_entry != NULL && + !is_hash_in_policy_entry(pol_entry, &hl.entries[0].hash, hash_alg) ) { + printk(TBOOT_ERR"\t verification failed\n"); + return TB_ERR_MODULE_VERIFICATION_FAILED; + } + + if ( pol_entry != NULL ) { + printk(TBOOT_DETA"\t OK : "); print_hash(&hl.entries[0].hash, hash_alg); + } + + return TB_ERR_NONE; +} + +static void verify_g_policy(void) +{ + /* assumes mbi is valid */ + printk(TBOOT_INFO"verifying policy \n"); + + /* add entry for policy control field and (optionally) policy */ + /* hash will be | */ + /* where will be 0s if TB_POLCTL_EXTEND_PCR17 is clear */ + static uint8_t buf[sizeof(tb_hash_t) + sizeof(g_policy->policy_control)]; + memset(buf, 0, sizeof(buf)); + memcpy(buf, &g_policy->policy_control, sizeof(g_policy->policy_control)); + if ( g_policy->policy_control & TB_POLCTL_EXTEND_PCR17 ) { + if ( !hash_policy((tb_hash_t *)&buf[sizeof(g_policy->policy_control)], + g_tpm->cur_alg) ) { + printk(TBOOT_ERR"policy hash failed\n"); + apply_policy(TB_ERR_MODULE_VERIFICATION_FAILED); + } + } + + u32 size = get_hash_size(g_tpm->cur_alg) + sizeof(g_policy->policy_control); + switch (g_tpm->extpol) { + case TB_EXTPOL_FIXED: + VL_ENTRIES(NUM_VL_ENTRIES).hl.count = 1; + VL_ENTRIES(NUM_VL_ENTRIES).hl.entries[0].alg = g_tpm->cur_alg; + if ( !hash_buffer(buf, size, + &VL_ENTRIES(NUM_VL_ENTRIES).hl.entries[0].hash, g_tpm->cur_alg) ) + apply_policy(TB_ERR_MODULE_VERIFICATION_FAILED); + + break; + + case TB_EXTPOL_AGILE: + if ( !g_tpm->hash(g_tpm, 2, buf, size, &VL_ENTRIES(NUM_VL_ENTRIES).hl) ) + apply_policy(TB_ERR_MODULE_VERIFICATION_FAILED); + break; + + case TB_EXTPOL_EMBEDDED: + { + VL_ENTRIES(NUM_VL_ENTRIES).hl.count = g_tpm->alg_count; + for (int i=0; ialg_count; i++) { + VL_ENTRIES(NUM_VL_ENTRIES).hl.entries[i].alg = g_tpm->algs[i]; + if ( !hash_buffer(buf, size, &VL_ENTRIES(NUM_VL_ENTRIES).hl.entries[i].hash, + g_tpm->algs[i]) ) + return; + } + + break; + } + + default: + apply_policy(TB_ERR_MODULE_VERIFICATION_FAILED); + break; + } + + VL_ENTRIES(NUM_VL_ENTRIES++).pcr = 17; + if ( g_using_da ) { + /* copying hash of policy_control into PCR 18 */ + if ( NUM_VL_ENTRIES >= MAX_VL_HASHES ) + printk(TBOOT_ERR"\t too many hashes to save for DA\n"); + else { + VL_ENTRIES(NUM_VL_ENTRIES).hl = VL_ENTRIES(NUM_VL_ENTRIES-1).hl; + VL_ENTRIES(NUM_VL_ENTRIES++).pcr = 18; + } + } +} + +void verify_all_modules(void) +{ + /* assumes mbi is valid */ + verify_g_policy(); + + /* TODO this will be done after ebs with xen passing back the module + * information - the modules it loaded. */ + + printk(TBOOT_INFO"all modules are verified\n"); +} + +static int find_first_nvpolicy_entry(const tb_policy_t *policy) +{ + if ( policy == NULL ) { + PRINT(TBOOT_ERR"Error: policy pointer is NULL\n"); + return -1; + } + + for ( int i = 0; i < policy->num_entries; i++ ) { + tb_policy_entry_t *pol_entry = get_policy_entry(policy, i); + if ( pol_entry == NULL ) + return -1; + + if ( pol_entry->mod_num == TB_POL_MOD_NUM_NV || + pol_entry->mod_num == TB_POL_MOD_NUM_NV_RAW ) + return i; + } + + return -1; +} + +static int find_next_nvpolicy_entry(const tb_policy_t *policy, int i) +{ + if ( policy == NULL || i < 0 || i >= policy->num_entries ) + return -1; + + for ( i++; i < policy->num_entries; i++ ) { + tb_policy_entry_t *pol_entry = get_policy_entry(policy, i); + if ( pol_entry == NULL ) + return -1; + + if ( pol_entry->mod_num == TB_POL_MOD_NUM_NV || + pol_entry->mod_num == TB_POL_MOD_NUM_NV_RAW ) + return i; + } + + return -1; +} + +static uint8_t nv_buf[4096]; + +static tb_error_t verify_nvindex(tb_policy_entry_t *pol_entry, + uint16_t hash_alg) +{ + size_t nv_size = sizeof(nv_buf); + tb_hash_t digest; + uint32_t attribute; + + if ( pol_entry == NULL ) + return TB_ERR_NV_VERIFICATION_FAILED; + + printk(TBOOT_INFO"verifying nv index 0x%08X\n", pol_entry->nv_index); + + /* check nv attribute */ + if ( !g_tpm->get_nvindex_permission(g_tpm, 0, pol_entry->nv_index, + &attribute) ) { + printk(TBOOT_ERR"\t :reading nv index permission failed\n"); + return TB_ERR_NV_VERIFICATION_FAILED; + } + if ( !(attribute & (TPM_NV_PER_OWNERWRITE | TPM_NV_PER_AUTHWRITE)) ) { + printk(TBOOT_ERR"\t :nv index should be OWNERWRITE or AUTHWRITE, bad permission!\n"); + return TB_ERR_NV_VERIFICATION_FAILED; + } + + /* get nv content */ + memset(nv_buf, 0, sizeof(nv_buf)); + if ( !read_policy_from_tpm(pol_entry->nv_index, + nv_buf, &nv_size) ) { + printk(TBOOT_ERR"\t :reading nv index 0x%08X failed\n", + pol_entry->nv_index); + return TB_ERR_NV_VERIFICATION_FAILED; + } + + /* hash the buffer if needed */ + switch ( pol_entry->mod_num ) { + case TB_POL_MOD_NUM_NV: + if ( !hash_buffer((const uint8_t*)nv_buf, nv_size, &digest, + TB_HALG_SHA1) ) { + printk(TBOOT_ERR"\t :nv content hash failed\n"); + return TB_ERR_NV_VERIFICATION_FAILED; + } + break; + case TB_POL_MOD_NUM_NV_RAW: + if ( nv_size != sizeof(digest.sha1) ) { + printk(TBOOT_ERR"\t :raw nv with wrong size (%d), should be %d\n", + (int)nv_size, sizeof(digest.sha1)); + return TB_ERR_NV_VERIFICATION_FAILED; + } + memcpy(digest.sha1, nv_buf, nv_size); + break; + default: + printk(TBOOT_ERR"\t :bad mod number for NV measuring in policy entry: %d\n", + pol_entry->mod_num); + return TB_ERR_NV_VERIFICATION_FAILED; + } + + /* add new hash to list (unless it doesn't get put in a PCR) + we'll just drop it if the list is full, but that will mean S3 resume + PCRs won't match pre-S3 */ + if ( NUM_VL_ENTRIES >= MAX_VL_HASHES ) + printk(TBOOT_WARN"\t too many hashes to save\n"); + else if ( pol_entry->pcr != TB_POL_PCR_NONE ) { + VL_ENTRIES(NUM_VL_ENTRIES).pcr = pol_entry->pcr; + VL_ENTRIES(NUM_VL_ENTRIES).hl.count = 1; + VL_ENTRIES(NUM_VL_ENTRIES).hl.entries[0].alg = TB_HALG_SHA1; + memcpy(VL_ENTRIES(NUM_VL_ENTRIES++).hl.entries[0].hash.sha1, + digest.sha1, SHA1_LENGTH); + } + + /* verify nv content */ + if ( !is_hash_in_policy_entry(pol_entry, &digest, hash_alg) ) { + printk(TBOOT_ERR"\t verification failed\n"); + return TB_ERR_NV_VERIFICATION_FAILED; + } + + printk(TBOOT_DETA"\t OK : "); print_hash(&digest, hash_alg); + return TB_ERR_NONE; +} + +void verify_all_nvindices(void) +{ + /* go through nv policies in tb policy */ + for ( int i = find_first_nvpolicy_entry(g_policy); + i >= 0; + i = find_next_nvpolicy_entry(g_policy, i) ) { + tb_policy_entry_t *pol_entry = get_policy_entry(g_policy, i); + apply_policy(verify_nvindex(pol_entry, g_policy->hash_alg)); + } +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ -- cgit v1.2.3