diff --git a/utility/Makefile b/utility/Makefile index b3f85bfdd5..aef52ad41b 100644 --- a/utility/Makefile +++ b/utility/Makefile @@ -29,6 +29,7 @@ TARGET_NAMES = dumpRSAPublicKey \ gbb_utility \ kernel_utility \ load_kernel_test \ + load_kernel2_test \ sign_image \ signature_digest_utility \ vbutil_firmware \ @@ -55,6 +56,9 @@ ${BUILD_ROOT}/gbb_utility: gbb_utility.cc ${BUILD_ROOT}/load_kernel_test: load_kernel_test.c $(LIBS) $(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(LIBS) -lcrypto +${BUILD_ROOT}/load_kernel2_test: load_kernel2_test.c $(LIBS) + $(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(LIBS) -lcrypto + ${BUILD_ROOT}/kernel_utility: kernel_utility.cc $(LIBS) $(CXX) $(CFLAGS) $(INCLUDES) -ggdb -D__STDC_LIMIT_MACROS $< \ -o $@ $(LIBS) -lcrypto diff --git a/utility/load_kernel2_test.c b/utility/load_kernel2_test.c new file mode 100644 index 0000000000..3d50211b43 --- /dev/null +++ b/utility/load_kernel2_test.c @@ -0,0 +1,145 @@ +/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* Routines for verifying a file's signature. Useful in testing the core + * RSA verification implementation. + */ + +#include /* For PRIu64 macro */ +#include +#include +#include +#include + +#include "load_kernel_fw.h" +#include "boot_device.h" +#include "host_common.h" +#include "rollback_index.h" +#include "utility.h" +#include "vboot_kernel.h" + +/* ANSI Color coding sequences. */ +#define COL_GREEN "\e[1;32m" +#define COL_RED "\e[0;31m" +#define COL_STOP "\e[m" + + +#define LBA_BYTES 512 +#define KERNEL_BUFFER_SIZE 0x600000 + +/* Global variables for stub functions */ +static LoadKernelParams lkp; +static FILE *image_file = NULL; + + +/* Boot device stub implementations to read from the image file */ +int BootDeviceReadLBA(uint64_t lba_start, uint64_t lba_count, void *buffer) { + printf("Read(%" PRIu64 ", %" PRIu64 ")\n", lba_start, lba_count); + + if (lba_start > lkp.ending_lba || + lba_start + lba_count - 1 > lkp.ending_lba) { + fprintf(stderr, "Read overrun: %" PRIu64 " + %" PRIu64 " > %" PRIu64 "\n", + lba_start, lba_count, lkp.ending_lba); + return 1; + } + + fseek(image_file, lba_start * lkp.bytes_per_lba, SEEK_SET); + if (1 != fread(buffer, lba_count * lkp.bytes_per_lba, 1, image_file)) { + fprintf(stderr, "Read error."); + return 1; + } + return 0; +} + + +int BootDeviceWriteLBA(uint64_t lba_start, uint64_t lba_count, + const void *buffer) { + printf("Write(%" PRIu64 ", %" PRIu64 ")\n", lba_start, lba_count); + + if (lba_start > lkp.ending_lba || + lba_start + lba_count - 1 > lkp.ending_lba) { + fprintf(stderr, "Read overrun: %" PRIu64 " + %" PRIu64 " > %" PRIu64 "\n", + lba_start, lba_count, lkp.ending_lba); + return 1; + } + + /* TODO: enable writes, once we're sure it won't trash our example file */ + return 0; + + fseek(image_file, lba_start * lkp.bytes_per_lba, SEEK_SET); + if (1 != fwrite(buffer, lba_count * lkp.bytes_per_lba, 1, image_file)) { + fprintf(stderr, "Read error."); + return 1; + } + return 0; +} + + +/* Main routine */ +int main(int argc, char* argv[]) { + + const char* image_name; + const char* keyfile_name; + int rv; + + Memset(&lkp, 0, sizeof(LoadKernelParams)); + lkp.bytes_per_lba = LBA_BYTES; + + /* Read command line parameters */ + if (3 > argc) { + fprintf(stderr, "usage: %s \n", argv[0]); + return 1; + } + image_name = argv[1]; + keyfile_name = argv[2]; + + /* Read header signing key blob */ + { + uint64_t key_size; + lkp.header_sign_key_blob = ReadFile(keyfile_name, &key_size); + if (!lkp.header_sign_key_blob) { + fprintf(stderr, "Unable to read key file %s\n", keyfile_name); + return 1; + } + } + + /* Get image size */ + printf("Reading from image: %s\n", image_name); + image_file = fopen(image_name, "rb"); + if (!image_file) { + fprintf(stderr, "Unable to open image file %s\n", image_name); + return 1; + } + fseek(image_file, 0, SEEK_END); + lkp.ending_lba = (ftell(image_file) / LBA_BYTES) - 1; + rewind(image_file); + printf("Ending LBA: %" PRIu64 "\n", lkp.ending_lba); + + /* Allocate a buffer for the kernel */ + lkp.kernel_buffer = Malloc(KERNEL_BUFFER_SIZE); + if(!lkp.kernel_buffer) { + fprintf(stderr, "Unable to allocate kernel buffer.\n"); + return 1; + } + + /* TODO: Option for boot mode - developer, recovery */ + /* Need to skip the address check, since we're putting it somewhere on the + * heap instead of its actual target address in the firmware. */ + lkp.boot_flags = BOOT_FLAG_SKIP_ADDR_CHECK; + + /* Call LoadKernel() */ + rv = LoadKernel2(&lkp); + printf("LoadKernel() returned %d\n", rv); + + if (LOAD_KERNEL_SUCCESS == rv) { + printf("Partition number: %" PRIu64 "\n", lkp.partition_number); + printf("Bootloader address: %" PRIu64 "\n", lkp.bootloader_address); + printf("Bootloader size: %" PRIu64 "\n", lkp.bootloader_size); + } + + fclose(image_file); + Free(lkp.kernel_buffer); + return 0; +} diff --git a/vboot_firmware/include/load_kernel_fw.h b/vboot_firmware/include/load_kernel_fw.h index 8dc48fd4a3..bbacbbdfa3 100644 --- a/vboot_firmware/include/load_kernel_fw.h +++ b/vboot_firmware/include/load_kernel_fw.h @@ -22,6 +22,8 @@ /* Boot flags for LoadKernel().boot_flags */ #define BOOT_FLAG_DEVELOPER UINT64_C(0x01) /* Developer switch is on */ #define BOOT_FLAG_RECOVERY UINT64_C(0x02) /* In recovery mode */ +#define BOOT_FLAG_SKIP_ADDR_CHECK UINT64_C(0x04) /* Skip check of kernel + * buffer address */ typedef struct LoadKernelParams { /* Inputs to LoadKernel() */ diff --git a/vboot_firmware/lib/vboot_kernel.c b/vboot_firmware/lib/vboot_kernel.c index 9e5dc52f8a..fd791afe7c 100644 --- a/vboot_firmware/lib/vboot_kernel.c +++ b/vboot_firmware/lib/vboot_kernel.c @@ -8,6 +8,7 @@ #include "vboot_kernel.h" +#include /* For PRIu64 */ #include "boot_device.h" #include "cgptlib.h" #include "load_kernel_fw.h" @@ -135,8 +136,10 @@ int LoadKernel2(LoadKernelParams* params) { * initialized. */ if (0 != GetStoredVersions(KERNEL_VERSIONS, &tpm_key_version, - &tpm_kernel_version)) + &tpm_kernel_version)) { + debug("Unable to get stored version from TPM\n"); return LOAD_KERNEL_RECOVERY; + } } else if (is_dev) { /* In developer mode, we ignore the kernel subkey, and just use * the SHA-512 hash to verify the key block. */ @@ -147,12 +150,16 @@ int LoadKernel2(LoadKernelParams* params) { /* Read GPT data */ gpt.sector_bytes = blba; gpt.drive_sectors = params->ending_lba + 1; - if (0 != AllocAndReadGptData(&gpt)) + if (0 != AllocAndReadGptData(&gpt)) { + debug("Unable to read GPT data\n"); break; + } /* Initialize GPT library */ - if (GPT_SUCCESS != GptInit(&gpt)) + if (GPT_SUCCESS != GptInit(&gpt)) { + debug("Error parsing GPT\n"); break; + } /* Allocate kernel header buffers */ kbuf = (uint8_t*)Malloc(KBUF_SIZE); @@ -167,6 +174,9 @@ int LoadKernel2(LoadKernelParams* params) { uint64_t key_version; uint64_t body_offset; + debug("Found kernel entry at %" PRIu64 " size %" PRIu64 "\n", + part_start, part_size); + /* Found at least one kernel partition. */ found_partitions++; @@ -178,25 +188,33 @@ int LoadKernel2(LoadKernelParams* params) { /* Verify the key block */ key_block = (VbKeyBlockHeader*)kbuf; - if ((0 != KeyBlockVerify(key_block, KBUF_SIZE, kernel_subkey))) + if ((0 != KeyBlockVerify(key_block, KBUF_SIZE, kernel_subkey))) { + debug("Verifying key block failed.\n"); continue; + } /* Check the key block flags against the current boot mode */ if (!(key_block->key_block_flags && ((BOOT_FLAG_DEVELOPER & params->boot_flags) ? - KEY_BLOCK_FLAG_DEVELOPER_1 : KEY_BLOCK_FLAG_DEVELOPER_0))) + KEY_BLOCK_FLAG_DEVELOPER_1 : KEY_BLOCK_FLAG_DEVELOPER_0))) { + debug("Developer flag mismatch.\n"); continue; + } if (!(key_block->key_block_flags && ((BOOT_FLAG_RECOVERY & params->boot_flags) ? - KEY_BLOCK_FLAG_RECOVERY_1 : KEY_BLOCK_FLAG_RECOVERY_0))) + KEY_BLOCK_FLAG_RECOVERY_1 : KEY_BLOCK_FLAG_RECOVERY_0))) { + debug("Recovery flag mismatch.\n"); continue; + } /* Check for rollback of key version. Note this is implicitly * skipped in recovery and developer modes because those set * key_version=0 above. */ key_version = key_block->data_key.key_version; - if (key_version < tpm_key_version) + if (key_version < tpm_key_version) { + debug("Key version too old.\n"); continue; + } /* Get the key for preamble/data verification from the key block */ data_key = PublicKeyToRSA(&key_block->data_key); @@ -208,6 +226,7 @@ int LoadKernel2(LoadKernelParams* params) { if ((0 != VerifyKernelPreamble2(preamble, KBUF_SIZE - key_block->key_block_size, data_key))) { + debug("Preamble verification failed.\n"); RSAPublicKeyFree(data_key); continue; } @@ -217,10 +236,13 @@ int LoadKernel2(LoadKernelParams* params) { * key_version=0 and kernel_version=0 above. */ if (key_version == tpm_key_version && preamble->kernel_version < tpm_kernel_version) { + debug("Kernel version too low.\n"); RSAPublicKeyFree(data_key); continue; } + debug("Kernel preamble is good.\n"); + /* Check for lowest key version from a valid header. */ if (lowest_key_version > key_version) { lowest_key_version = key_version; @@ -238,7 +260,9 @@ int LoadKernel2(LoadKernelParams* params) { continue; /* Verify body load address matches what we expect */ - if (preamble->body_load_address != (size_t)params->kernel_buffer) { + if ((preamble->body_load_address != (size_t)params->kernel_buffer) && + !(params->boot_flags & BOOT_FLAG_SKIP_ADDR_CHECK)) { + debug("Wrong body load address.\n"); RSAPublicKeyFree(data_key); continue; } @@ -246,6 +270,7 @@ int LoadKernel2(LoadKernelParams* params) { /* Verify kernel body starts at a multiple of the sector size. */ body_offset = key_block->key_block_size + preamble->preamble_size; if (0 != body_offset % blba) { + debug("Kernel body not at multiple of sector size.\n"); RSAPublicKeyFree(data_key); continue; } @@ -253,6 +278,7 @@ int LoadKernel2(LoadKernelParams* params) { /* Verify kernel body fits in the partition */ if (body_offset + preamble->body_signature.data_size > part_size * blba) { + debug("Kernel body doesn't fit in partition.\n"); RSAPublicKeyFree(data_key); continue; } @@ -262,6 +288,7 @@ int LoadKernel2(LoadKernelParams* params) { part_start + (body_offset / blba), (preamble->body_signature.data_size + blba - 1) / blba, params->kernel_buffer)) { + debug("Unable to read kernel data.\n"); RSAPublicKeyFree(data_key); continue; } @@ -269,6 +296,7 @@ int LoadKernel2(LoadKernelParams* params) { /* Verify kernel data */ if (0 != VerifyData((const uint8_t*)params->kernel_buffer, &preamble->body_signature, data_key)) { + debug("Kernel data verification failed.\n"); RSAPublicKeyFree(data_key); continue; } @@ -278,25 +306,24 @@ int LoadKernel2(LoadKernelParams* params) { /* If we're still here, the kernel is valid. */ /* Save the first good partition we find; that's the one we'll boot */ - if (-1 == good_partition) { - good_partition = gpt.current_kernel; - params->partition_number = gpt.current_kernel; - params->bootloader_address = preamble->bootloader_address; - params->bootloader_size = preamble->bootloader_size; - /* If we're in developer or recovery mode, there's no rollback - * protection, so we can stop at the first valid kernel. */ - if (!is_normal) - break; + debug("Partiton is good.\n"); + good_partition = gpt.current_kernel; + params->partition_number = gpt.current_kernel; + params->bootloader_address = preamble->bootloader_address; + params->bootloader_size = preamble->bootloader_size; + /* If we're in developer or recovery mode, there's no rollback + * protection, so we can stop at the first valid kernel. */ + if (!is_normal) + break; - /* Otherwise, we're in normal boot mode, so we do care about - * the key index in the TPM. If the good partition's key - * version is the same as the tpm, then the TPM doesn't need - * updating; we can stop now. Otherwise, we'll check all the - * other headers to see if they contain a newer key. */ - if (key_version == tpm_key_version && - preamble->kernel_version == tpm_kernel_version) - break; - } + /* Otherwise, we're in normal boot mode, so we do care about the + * key index in the TPM. If the good partition's key version is + * the same as the tpm, then the TPM doesn't need updating; we + * can stop now. Otherwise, we'll check all the other headers + * to see if they contain a newer key. */ + if (key_version == tpm_key_version && + preamble->kernel_version == tpm_kernel_version) + break; } /* while(GptNextKernelEntry) */ } while(0);