/* * 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: */