diff options
author | Randall Spangler <rspangler@chromium.org> | 2013-01-29 15:01:12 -0800 |
---|---|---|
committer | ChromeBot <chrome-bot@google.com> | 2013-01-31 09:38:28 -0800 |
commit | 7993f257af87c7c38cdc71b76bc67cde6c3cdbca (patch) | |
tree | 6f0153579ebf4d0ae327e7f11dda902c6494b4ce /firmware/lib/vboot_kernel.c | |
parent | 49cb0d3471e768da11fe76b65769bd57dca38bd7 (diff) | |
download | vboot-7993f257af87c7c38cdc71b76bc67cde6c3cdbca.tar.gz |
Reformat to kernel style
No code changes, just reformatting.
BUG=none
BRANCH=none
TEST=make runtests
Change-Id: Id690c8334147970784db5ac54933ad1f5a58dcc1
Reviewed-on: https://gerrit.chromium.org/gerrit/42263
Tested-by: Randall Spangler <rspangler@chromium.org>
Reviewed-by: Bill Richardson <wfrichar@chromium.org>
Commit-Queue: Randall Spangler <rspangler@chromium.org>
Diffstat (limited to 'firmware/lib/vboot_kernel.c')
-rw-r--r-- | firmware/lib/vboot_kernel.c | 1126 |
1 files changed, 598 insertions, 528 deletions
diff --git a/firmware/lib/vboot_kernel.c b/firmware/lib/vboot_kernel.c index a9a6fdf3..26eba7e7 100644 --- a/firmware/lib/vboot_kernel.c +++ b/firmware/lib/vboot_kernel.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. +/* Copyright (c) 2013 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. * @@ -20,551 +20,621 @@ #define LOWEST_TPM_VERSION 0xffffffff typedef enum BootMode { - kBootRecovery = 0, /* Recovery firmware, regardless of dev switch position */ - kBootNormal = 1, /* Normal boot - kernel must be verified */ - kBootDev = 2 /* Developer boot - self-signed kernel ok */ + kBootRecovery = 0, /* Recovery firmware, any dev switch position */ + kBootNormal = 1, /* Normal boot - kernel must be verified */ + kBootDev = 2 /* Developer boot - self-signed kernel ok */ } BootMode; - -/* Allocates and reads GPT data from the drive. The sector_bytes and - * drive_sectors fields should be filled on input. The primary and - * secondary header and entries are filled on output. +/** + * Allocate and read GPT data from the drive. + * + * The sector_bytes and drive_sectors fields should be filled on input. The + * primary and secondary header and entries are filled on output. * - * Returns 0 if successful, 1 if error. */ -int AllocAndReadGptData(VbExDiskHandle_t disk_handle, GptData* gptdata) { - - uint64_t entries_sectors = TOTAL_ENTRIES_SIZE / gptdata->sector_bytes; - - /* No data to be written yet */ - gptdata->modified = 0; - - /* Allocate all buffers */ - gptdata->primary_header = (uint8_t*)VbExMalloc(gptdata->sector_bytes); - gptdata->secondary_header = (uint8_t*)VbExMalloc(gptdata->sector_bytes); - gptdata->primary_entries = (uint8_t*)VbExMalloc(TOTAL_ENTRIES_SIZE); - gptdata->secondary_entries = (uint8_t*)VbExMalloc(TOTAL_ENTRIES_SIZE); - - if (gptdata->primary_header == NULL || gptdata->secondary_header == NULL || - gptdata->primary_entries == NULL || gptdata->secondary_entries == NULL) - return 1; - - /* Read data from the drive, skipping the protective MBR */ - if (0 != VbExDiskRead(disk_handle, 1, 1, gptdata->primary_header)) - return 1; - if (0 != VbExDiskRead(disk_handle, 2, entries_sectors, - gptdata->primary_entries)) - return 1; - if (0 != VbExDiskRead(disk_handle, - gptdata->drive_sectors - entries_sectors - 1, - entries_sectors, gptdata->secondary_entries)) - return 1; - if (0 != VbExDiskRead(disk_handle, gptdata->drive_sectors - 1, 1, - gptdata->secondary_header)) - return 1; - - return 0; + * Returns 0 if successful, 1 if error. + */ +int AllocAndReadGptData(VbExDiskHandle_t disk_handle, GptData *gptdata) +{ + uint64_t entries_sectors = TOTAL_ENTRIES_SIZE / gptdata->sector_bytes; + + /* No data to be written yet */ + gptdata->modified = 0; + + /* Allocate all buffers */ + gptdata->primary_header = (uint8_t *)VbExMalloc(gptdata->sector_bytes); + gptdata->secondary_header = + (uint8_t *)VbExMalloc(gptdata->sector_bytes); + gptdata->primary_entries = (uint8_t *)VbExMalloc(TOTAL_ENTRIES_SIZE); + gptdata->secondary_entries = (uint8_t *)VbExMalloc(TOTAL_ENTRIES_SIZE); + + if (gptdata->primary_header == NULL || + gptdata->secondary_header == NULL || + gptdata->primary_entries == NULL || + gptdata->secondary_entries == NULL) + return 1; + + /* Read data from the drive, skipping the protective MBR */ + if (0 != VbExDiskRead(disk_handle, 1, 1, gptdata->primary_header)) + return 1; + if (0 != VbExDiskRead(disk_handle, 2, entries_sectors, + gptdata->primary_entries)) + return 1; + if (0 != VbExDiskRead(disk_handle, + gptdata->drive_sectors - entries_sectors - 1, + entries_sectors, gptdata->secondary_entries)) + return 1; + if (0 != VbExDiskRead(disk_handle, gptdata->drive_sectors - 1, 1, + gptdata->secondary_header)) + return 1; + + return 0; } - -/* Writes any changes for the GPT data back to the drive, then frees - * the buffers. +/** + * Write any changes for the GPT data back to the drive, then free the buffers. * - * Returns 0 if successful, 1 if error. */ -int WriteAndFreeGptData(VbExDiskHandle_t disk_handle, GptData* gptdata) { - - int legacy = 0; - uint64_t entries_sectors = TOTAL_ENTRIES_SIZE / gptdata->sector_bytes; - - if (gptdata->primary_header) { - GptHeader* h = (GptHeader*)(gptdata->primary_header); - legacy = !Memcmp(h->signature, GPT_HEADER_SIGNATURE2, - GPT_HEADER_SIGNATURE_SIZE); - if (gptdata->modified & GPT_MODIFIED_HEADER1) { - if (legacy) { - VBDEBUG(("Not updating GPT header 1: legacy mode is enabled.\n")); - } else { - VBDEBUG(("Updating GPT header 1\n")); - if (0 != VbExDiskWrite(disk_handle, 1, 1, gptdata->primary_header)) - return 1; - } - } - VbExFree(gptdata->primary_header); - } - - if (gptdata->primary_entries) { - if (gptdata->modified & GPT_MODIFIED_ENTRIES1) { - if (legacy) { - VBDEBUG(("Not updating GPT entries 1: legacy mode is enabled.\n")); - } else { - VBDEBUG(("Updating GPT entries 1\n")); - if (0 != VbExDiskWrite(disk_handle, 2, entries_sectors, - gptdata->primary_entries)) - return 1; - } - } - VbExFree(gptdata->primary_entries); - } - - if (gptdata->secondary_entries) { - if (gptdata->modified & GPT_MODIFIED_ENTRIES2) { - VBDEBUG(("Updating GPT header 2\n")); - if (0 != VbExDiskWrite(disk_handle, - gptdata->drive_sectors - entries_sectors - 1, - entries_sectors, gptdata->secondary_entries)) - return 1; - } - VbExFree(gptdata->secondary_entries); - } - - if (gptdata->secondary_header) { - if (gptdata->modified & GPT_MODIFIED_HEADER2) { - VBDEBUG(("Updating GPT entries 2\n")); - if (0 != VbExDiskWrite(disk_handle, gptdata->drive_sectors - 1, 1, - gptdata->secondary_header)) - return 1; - } - VbExFree(gptdata->secondary_header); - } - - /* Success */ - return 0; + * Returns 0 if successful, 1 if error. + */ +int WriteAndFreeGptData(VbExDiskHandle_t disk_handle, GptData *gptdata) +{ + int legacy = 0; + uint64_t entries_sectors = TOTAL_ENTRIES_SIZE / gptdata->sector_bytes; + + if (gptdata->primary_header) { + GptHeader *h = (GptHeader *)(gptdata->primary_header); + legacy = !Memcmp(h->signature, GPT_HEADER_SIGNATURE2, + GPT_HEADER_SIGNATURE_SIZE); + if (gptdata->modified & GPT_MODIFIED_HEADER1) { + if (legacy) { + VBDEBUG(("Not updating GPT header 1: " + "legacy mode is enabled.\n")); + } else { + VBDEBUG(("Updating GPT header 1\n")); + if (0 != VbExDiskWrite(disk_handle, 1, 1, + gptdata->primary_header)) + return 1; + } + } + VbExFree(gptdata->primary_header); + } + + if (gptdata->primary_entries) { + if (gptdata->modified & GPT_MODIFIED_ENTRIES1) { + if (legacy) { + VBDEBUG(("Not updating GPT entries 1: " + "legacy mode is enabled.\n")); + } else { + VBDEBUG(("Updating GPT entries 1\n")); + if (0 != VbExDiskWrite(disk_handle, 2, + entries_sectors, + gptdata->primary_entries)) + return 1; + } + } + VbExFree(gptdata->primary_entries); + } + + if (gptdata->secondary_entries) { + if (gptdata->modified & GPT_MODIFIED_ENTRIES2) { + VBDEBUG(("Updating GPT header 2\n")); + if (0 != VbExDiskWrite(disk_handle, + gptdata->drive_sectors - entries_sectors - 1, + entries_sectors, gptdata->secondary_entries)) + return 1; + } + VbExFree(gptdata->secondary_entries); + } + + if (gptdata->secondary_header) { + if (gptdata->modified & GPT_MODIFIED_HEADER2) { + VBDEBUG(("Updating GPT entries 2\n")); + if (0 != VbExDiskWrite(disk_handle, + gptdata->drive_sectors - 1, 1, + gptdata->secondary_header)) + return 1; + } + VbExFree(gptdata->secondary_header); + } + + /* Success */ + return 0; } /* disable MSVC warning on const logical expression (as in } while(0);) */ __pragma(warning(disable: 4127)) - -VbError_t LoadKernel(LoadKernelParams* params) { - VbSharedDataHeader* shared = (VbSharedDataHeader*)params->shared_data_blob; - VbSharedDataKernelCall* shcall = NULL; - VbNvContext* vnc = params->nv_context; - GoogleBinaryBlockHeader* gbb = (GoogleBinaryBlockHeader*)params->gbb_data; - VbPublicKey* kernel_subkey; - GptData gpt; - uint64_t part_start, part_size; - uint64_t blba; - uint64_t kbuf_sectors; - uint8_t* kbuf = NULL; - int found_partitions = 0; - int good_partition = -1; - int good_partition_key_block_valid = 0; - uint32_t lowest_version = LOWEST_TPM_VERSION; - int rec_switch, dev_switch; - BootMode boot_mode; - uint32_t require_official_os = 0; - - VbError_t retval = VBERROR_UNKNOWN; - int recovery = VBNV_RECOVERY_LK_UNSPECIFIED; - - /* Sanity Checks */ - if (!params->bytes_per_lba || - !params->ending_lba) { - VBDEBUG(("LoadKernel() called with invalid params\n")); - retval = VBERROR_INVALID_PARAMETER; - goto LoadKernelExit; - } - - /* Clear output params in case we fail */ - params->partition_number = 0; - params->bootloader_address = 0; - params->bootloader_size = 0; - - /* Calculate switch positions and boot mode */ - rec_switch = (BOOT_FLAG_RECOVERY & params->boot_flags ? 1 : 0); - dev_switch = (BOOT_FLAG_DEVELOPER & params->boot_flags ? 1 : 0); - if (rec_switch) { - boot_mode = kBootRecovery; - } else if (dev_switch) { - boot_mode = kBootDev; - VbNvGet(vnc, VBNV_DEV_BOOT_SIGNED_ONLY, &require_official_os); - } else { - boot_mode = kBootNormal; - } - - /* Set up tracking for this call. This wraps around if called many times, - * so we need to initialize the call entry each time. */ - shcall = shared->lk_calls + (shared->lk_call_count - & (VBSD_MAX_KERNEL_CALLS - 1)); - Memset(shcall, 0, sizeof(VbSharedDataKernelCall)); - shcall->boot_flags = (uint32_t)params->boot_flags; - shcall->boot_mode = boot_mode; - shcall->sector_size = (uint32_t)params->bytes_per_lba; - shcall->sector_count = params->ending_lba + 1; - shared->lk_call_count++; - - /* Initialization */ - blba = params->bytes_per_lba; - kbuf_sectors = KBUF_SIZE / blba; - if (0 == kbuf_sectors) { - VBDEBUG(("LoadKernel() called with sector size > KBUF_SIZE\n")); - retval = VBERROR_INVALID_PARAMETER; - goto LoadKernelExit; - } - - if (kBootRecovery == boot_mode) { - /* Use the recovery key to verify the kernel */ - kernel_subkey = (VbPublicKey*)((uint8_t*)gbb + gbb->recovery_key_offset); - } else { - /* Use the kernel subkey passed from LoadFirmware(). */ - kernel_subkey = &shared->kernel_subkey; - } - - /* Read GPT data */ - gpt.sector_bytes = (uint32_t)blba; - gpt.drive_sectors = params->ending_lba + 1; - if (0 != AllocAndReadGptData(params->disk_handle, &gpt)) { - VBDEBUG(("Unable to read GPT data\n")); - shcall->check_result = VBSD_LKC_CHECK_GPT_READ_ERROR; - goto bad_gpt; - } - - /* Initialize GPT library */ - if (GPT_SUCCESS != GptInit(&gpt)) { - VBDEBUG(("Error parsing GPT\n")); - shcall->check_result = VBSD_LKC_CHECK_GPT_PARSE_ERROR; - goto bad_gpt; - } - - /* Allocate kernel header buffers */ - kbuf = (uint8_t*)VbExMalloc(KBUF_SIZE); - if (!kbuf) - goto bad_gpt; - - /* Loop over candidate kernel partitions */ - while (GPT_SUCCESS == GptNextKernelEntry(&gpt, &part_start, &part_size)) { - VbSharedDataKernelPart* shpart = NULL; - VbKeyBlockHeader* key_block; - VbKernelPreambleHeader* preamble; - RSAPublicKey* data_key = NULL; - uint64_t key_version; - uint32_t combined_version; - uint64_t body_offset; - uint64_t body_offset_sectors; - uint64_t body_sectors; - int key_block_valid = 1; - - VBDEBUG(("Found kernel entry at %" PRIu64 " size %" PRIu64 "\n", - part_start, part_size)); - - /* Set up tracking for this partition. This wraps around if called - * many times, so initialize the partition entry each time. */ - shpart = shcall->parts + (shcall->kernel_parts_found - & (VBSD_MAX_KERNEL_PARTS - 1)); - Memset(shpart, 0, sizeof(VbSharedDataKernelPart)); - shpart->sector_start = part_start; - shpart->sector_count = part_size; - /* TODO: GPT partitions start at 1, but cgptlib starts them at 0. - * Adjust here, until cgptlib is fixed. */ - shpart->gpt_index = (uint8_t)(gpt.current_kernel + 1); - shcall->kernel_parts_found++; - - /* Found at least one kernel partition. */ - found_partitions++; - - /* Read the first part of the kernel partition. */ - if (part_size < kbuf_sectors) { - VBDEBUG(("Partition too small to hold kernel.\n")); - shpart->check_result = VBSD_LKP_CHECK_TOO_SMALL; - goto bad_kernel; - } - - if (0 != VbExDiskRead(params->disk_handle, part_start, kbuf_sectors, - kbuf)) { - VBDEBUG(("Unable to read start of partition.\n")); - shpart->check_result = VBSD_LKP_CHECK_READ_START; - goto bad_kernel; - } +VbError_t LoadKernel(LoadKernelParams *params) +{ + VbSharedDataHeader *shared = + (VbSharedDataHeader *)params->shared_data_blob; + VbSharedDataKernelCall *shcall = NULL; + VbNvContext* vnc = params->nv_context; + GoogleBinaryBlockHeader* gbb = + (GoogleBinaryBlockHeader *)params->gbb_data; + VbPublicKey* kernel_subkey; + GptData gpt; + uint64_t part_start, part_size; + uint64_t blba; + uint64_t kbuf_sectors; + uint8_t* kbuf = NULL; + int found_partitions = 0; + int good_partition = -1; + int good_partition_key_block_valid = 0; + uint32_t lowest_version = LOWEST_TPM_VERSION; + int rec_switch, dev_switch; + BootMode boot_mode; + uint32_t require_official_os = 0; + + VbError_t retval = VBERROR_UNKNOWN; + int recovery = VBNV_RECOVERY_LK_UNSPECIFIED; + + /* Sanity Checks */ + if (!params->bytes_per_lba || + !params->ending_lba) { + VBDEBUG(("LoadKernel() called with invalid params\n")); + retval = VBERROR_INVALID_PARAMETER; + goto LoadKernelExit; + } + + /* Clear output params in case we fail */ + params->partition_number = 0; + params->bootloader_address = 0; + params->bootloader_size = 0; + + /* Calculate switch positions and boot mode */ + rec_switch = (BOOT_FLAG_RECOVERY & params->boot_flags ? 1 : 0); + dev_switch = (BOOT_FLAG_DEVELOPER & params->boot_flags ? 1 : 0); + if (rec_switch) { + boot_mode = kBootRecovery; + } else if (dev_switch) { + boot_mode = kBootDev; + VbNvGet(vnc, VBNV_DEV_BOOT_SIGNED_ONLY, &require_official_os); + } else { + boot_mode = kBootNormal; + } + + /* + * Set up tracking for this call. This wraps around if called many + * times, so we need to initialize the call entry each time. + */ + shcall = shared->lk_calls + (shared->lk_call_count + & (VBSD_MAX_KERNEL_CALLS - 1)); + Memset(shcall, 0, sizeof(VbSharedDataKernelCall)); + shcall->boot_flags = (uint32_t)params->boot_flags; + shcall->boot_mode = boot_mode; + shcall->sector_size = (uint32_t)params->bytes_per_lba; + shcall->sector_count = params->ending_lba + 1; + shared->lk_call_count++; + + /* Initialization */ + blba = params->bytes_per_lba; + kbuf_sectors = KBUF_SIZE / blba; + if (0 == kbuf_sectors) { + VBDEBUG(("LoadKernel() called with sector size > KBUF_SIZE\n")); + retval = VBERROR_INVALID_PARAMETER; + goto LoadKernelExit; + } + + if (kBootRecovery == boot_mode) { + /* Use the recovery key to verify the kernel */ + kernel_subkey = (VbPublicKey*) + ((uint8_t*)gbb + gbb->recovery_key_offset); + } else { + /* Use the kernel subkey passed from LoadFirmware(). */ + kernel_subkey = &shared->kernel_subkey; + } + + /* Read GPT data */ + gpt.sector_bytes = (uint32_t)blba; + gpt.drive_sectors = params->ending_lba + 1; + if (0 != AllocAndReadGptData(params->disk_handle, &gpt)) { + VBDEBUG(("Unable to read GPT data\n")); + shcall->check_result = VBSD_LKC_CHECK_GPT_READ_ERROR; + goto bad_gpt; + } + + /* Initialize GPT library */ + if (GPT_SUCCESS != GptInit(&gpt)) { + VBDEBUG(("Error parsing GPT\n")); + shcall->check_result = VBSD_LKC_CHECK_GPT_PARSE_ERROR; + goto bad_gpt; + } + + /* Allocate kernel header buffers */ + kbuf = (uint8_t*)VbExMalloc(KBUF_SIZE); + if (!kbuf) + goto bad_gpt; + + /* Loop over candidate kernel partitions */ + while (GPT_SUCCESS == + GptNextKernelEntry(&gpt, &part_start, &part_size)) { + VbSharedDataKernelPart *shpart = NULL; + VbKeyBlockHeader *key_block; + VbKernelPreambleHeader *preamble; + RSAPublicKey *data_key = NULL; + uint64_t key_version; + uint32_t combined_version; + uint64_t body_offset; + uint64_t body_offset_sectors; + uint64_t body_sectors; + int key_block_valid = 1; + + VBDEBUG(("Found kernel entry at %" PRIu64 " size %" PRIu64 "\n", + part_start, part_size)); + + /* + * Set up tracking for this partition. This wraps around if + * called many times, so initialize the partition entry each + * time. + */ + shpart = shcall->parts + (shcall->kernel_parts_found + & (VBSD_MAX_KERNEL_PARTS - 1)); + Memset(shpart, 0, sizeof(VbSharedDataKernelPart)); + shpart->sector_start = part_start; + shpart->sector_count = part_size; + /* + * TODO: GPT partitions start at 1, but cgptlib starts them at + * 0. Adjust here, until cgptlib is fixed. + */ + shpart->gpt_index = (uint8_t)(gpt.current_kernel + 1); + shcall->kernel_parts_found++; + + /* Found at least one kernel partition. */ + found_partitions++; + + /* Read the first part of the kernel partition. */ + if (part_size < kbuf_sectors) { + VBDEBUG(("Partition too small to hold kernel.\n")); + shpart->check_result = VBSD_LKP_CHECK_TOO_SMALL; + goto bad_kernel; + } + + if (0 != VbExDiskRead(params->disk_handle, part_start, + kbuf_sectors, kbuf)) { + VBDEBUG(("Unable to read start of partition.\n")); + shpart->check_result = VBSD_LKP_CHECK_READ_START; + goto bad_kernel; + } #if defined(CONFIG_SANDBOX) - /* Silence compiler warnings */ - combined_version = 0; - body_offset = body_offset; - body_offset_sectors = body_offset_sectors; - body_sectors = body_sectors; - kernel_subkey = kernel_subkey; - key_block = key_block; - key_version = key_version; - preamble = preamble; + /* Silence compiler warnings */ + combined_version = 0; + body_offset = body_offset; + body_offset_sectors = body_offset_sectors; + body_sectors = body_sectors; + kernel_subkey = kernel_subkey; + key_block = key_block; + key_version = key_version; + preamble = preamble; #else - /* Verify the key block. */ - key_block = (VbKeyBlockHeader*)kbuf; - if (0 != KeyBlockVerify(key_block, KBUF_SIZE, kernel_subkey, 0)) { - VBDEBUG(("Verifying key block signature failed.\n")); - shpart->check_result = VBSD_LKP_CHECK_KEY_BLOCK_SIG; - - key_block_valid = 0; - - /* If we're not in developer mode, this kernel is bad. */ - if (kBootDev != boot_mode) - goto bad_kernel; - - /* In developer mode, we can explictly disallow self-signed kernels */ - if (require_official_os) { - VBDEBUG(("Self-signed custom kernels are not enabled.\n")); - shpart->check_result = VBSD_LKP_CHECK_SELF_SIGNED; - goto bad_kernel; - } - - /* Allow the kernel if the SHA-512 hash of the key block is valid. */ - if (0 != KeyBlockVerify(key_block, KBUF_SIZE, kernel_subkey, 1)) { - VBDEBUG(("Verifying key block hash failed.\n")); - shpart->check_result = VBSD_LKP_CHECK_KEY_BLOCK_HASH; - goto bad_kernel; - } - } - - /* Check the key block flags against the current boot mode. */ - if (!(key_block->key_block_flags & - (dev_switch ? KEY_BLOCK_FLAG_DEVELOPER_1 : - KEY_BLOCK_FLAG_DEVELOPER_0))) { - VBDEBUG(("Key block developer flag mismatch.\n")); - shpart->check_result = VBSD_LKP_CHECK_DEV_MISMATCH; - key_block_valid = 0; - } - if (!(key_block->key_block_flags & - (rec_switch ? KEY_BLOCK_FLAG_RECOVERY_1 : - KEY_BLOCK_FLAG_RECOVERY_0))) { - VBDEBUG(("Key block recovery flag mismatch.\n")); - shpart->check_result = VBSD_LKP_CHECK_REC_MISMATCH; - key_block_valid = 0; - } - - /* Check for rollback of key version except in recovery mode. */ - key_version = key_block->data_key.key_version; - if (kBootRecovery != boot_mode) { - if (key_version < (shared->kernel_version_tpm >> 16)) { - VBDEBUG(("Key version too old.\n")); - shpart->check_result = VBSD_LKP_CHECK_KEY_ROLLBACK; - key_block_valid = 0; - } - if (key_version > 0xFFFF) { - /* Key version is stored in 16 bits in the TPM, so key versions - * greater than 0xFFFF can't be stored properly. */ - VBDEBUG(("Key version > 0xFFFF.\n")); - shpart->check_result = VBSD_LKP_CHECK_KEY_ROLLBACK; - key_block_valid = 0; - } - } - - /* If we're not in developer mode, require the key block to be valid. */ - if (kBootDev != boot_mode && !key_block_valid) { - VBDEBUG(("Key block is invalid.\n")); - goto bad_kernel; - } - - /* Get the key for preamble/data verification from the key block. */ - data_key = PublicKeyToRSA(&key_block->data_key); - if (!data_key) { - VBDEBUG(("Data key bad.\n")); - shpart->check_result = VBSD_LKP_CHECK_DATA_KEY_PARSE; - goto bad_kernel; - } - - /* Verify the preamble, which follows the key block */ - preamble = (VbKernelPreambleHeader*)(kbuf + key_block->key_block_size); - if ((0 != VerifyKernelPreamble(preamble, - KBUF_SIZE - key_block->key_block_size, - data_key))) { - VBDEBUG(("Preamble verification failed.\n")); - shpart->check_result = VBSD_LKP_CHECK_VERIFY_PREAMBLE; - goto bad_kernel; - } - - /* If the key block is valid and we're not in recovery mode, check for - * rollback of the kernel version. */ - combined_version = (uint32_t)((key_version << 16) | - (preamble->kernel_version & 0xFFFF)); - shpart->combined_version = combined_version; - if (key_block_valid && kBootRecovery != boot_mode) { - if (combined_version < shared->kernel_version_tpm) { - VBDEBUG(("Kernel version too low.\n")); - shpart->check_result = VBSD_LKP_CHECK_KERNEL_ROLLBACK; - /* If we're not in developer mode, kernel version must be valid. */ - if (kBootDev != boot_mode) - goto bad_kernel; - } - } - - VBDEBUG(("Kernel preamble is good.\n")); - shpart->check_result = VBSD_LKP_CHECK_PREAMBLE_VALID; - - /* Check for lowest version from a valid header. */ - if (key_block_valid && lowest_version > combined_version) - lowest_version = combined_version; - else { - VBDEBUG(("Key block valid: %d\n", key_block_valid)); - VBDEBUG(("Combined version: %u\n", (unsigned) combined_version)); - } - - /* If we already have a good kernel, no need to read another - * one; we only needed to look at the versions to check for - * rollback. So skip to the next kernel preamble. */ - if (-1 != good_partition) - continue; - - /* 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) { - VBDEBUG(("Kernel body not at multiple of sector size.\n")); - shpart->check_result = VBSD_LKP_CHECK_BODY_OFFSET; - goto bad_kernel; - } - body_offset_sectors = body_offset / blba; - - body_sectors = (preamble->body_signature.data_size + blba - 1) / blba; - if (!params->kernel_buffer) { - /* Get kernel load address and size from the header. */ - params->kernel_buffer = (void*) ((long)preamble->body_load_address); - params->kernel_buffer_size = body_sectors * blba; - } else { - /* Verify kernel body fits in the buffer */ - if (body_sectors * blba > params->kernel_buffer_size) { - VBDEBUG(("Kernel body doesn't fit in memory.\n")); - shpart->check_result = VBSD_LKP_CHECK_BODY_EXCEEDS_MEM; - goto bad_kernel; - } - } - - /* Verify kernel body fits in the partition */ - if (body_offset_sectors + body_sectors > part_size) { - VBDEBUG(("Kernel body doesn't fit in partition.\n")); - shpart->check_result = VBSD_LKP_CHECK_BODY_EXCEEDS_PART; - goto bad_kernel; - } - - /* Read the kernel data */ - VBPERFSTART("VB_RKD"); - if (0 != VbExDiskRead(params->disk_handle, - part_start + body_offset_sectors, - body_sectors, params->kernel_buffer)) { - VBDEBUG(("Unable to read kernel data.\n")); - VBPERFEND("VB_RKD"); - shpart->check_result = VBSD_LKP_CHECK_READ_DATA; - goto bad_kernel; - } - VBPERFEND("VB_RKD"); - - /* Verify kernel data */ - if (0 != VerifyData((const uint8_t*)params->kernel_buffer, - params->kernel_buffer_size, - &preamble->body_signature, data_key)) { - VBDEBUG(("Kernel data verification failed.\n")); - shpart->check_result = VBSD_LKP_CHECK_VERIFY_DATA; - goto bad_kernel; - } - - /* Done with the kernel signing key, so can free it now */ - RSAPublicKeyFree(data_key); - data_key = NULL; + /* Verify the key block. */ + key_block = (VbKeyBlockHeader*)kbuf; + if (0 != KeyBlockVerify(key_block, KBUF_SIZE, + kernel_subkey, 0)) { + VBDEBUG(("Verifying key block signature failed.\n")); + shpart->check_result = VBSD_LKP_CHECK_KEY_BLOCK_SIG; + key_block_valid = 0; + + /* If not in developer mode, this kernel is bad. */ + if (kBootDev != boot_mode) + goto bad_kernel; + + /* + * In developer mode, we can explictly disallow + * self-signed kernels + */ + if (require_official_os) { + VBDEBUG(("Self-signed kernels not enabled.\n")); + shpart->check_result = + VBSD_LKP_CHECK_SELF_SIGNED; + goto bad_kernel; + } + + /* + * Allow the kernel if the SHA-512 hash of the key + * block is valid. + */ + if (0 != KeyBlockVerify(key_block, KBUF_SIZE, + kernel_subkey, 1)) { + VBDEBUG(("Verifying key block hash failed.\n")); + shpart->check_result = + VBSD_LKP_CHECK_KEY_BLOCK_HASH; + goto bad_kernel; + } + } + + /* Check the key block flags against the current boot mode. */ + if (!(key_block->key_block_flags & + (dev_switch ? KEY_BLOCK_FLAG_DEVELOPER_1 : + KEY_BLOCK_FLAG_DEVELOPER_0))) { + VBDEBUG(("Key block developer flag mismatch.\n")); + shpart->check_result = VBSD_LKP_CHECK_DEV_MISMATCH; + key_block_valid = 0; + } + if (!(key_block->key_block_flags & + (rec_switch ? KEY_BLOCK_FLAG_RECOVERY_1 : + KEY_BLOCK_FLAG_RECOVERY_0))) { + VBDEBUG(("Key block recovery flag mismatch.\n")); + shpart->check_result = VBSD_LKP_CHECK_REC_MISMATCH; + key_block_valid = 0; + } + + /* Check for rollback of key version except in recovery mode. */ + key_version = key_block->data_key.key_version; + if (kBootRecovery != boot_mode) { + if (key_version < (shared->kernel_version_tpm >> 16)) { + VBDEBUG(("Key version too old.\n")); + shpart->check_result = + VBSD_LKP_CHECK_KEY_ROLLBACK; + key_block_valid = 0; + } + if (key_version > 0xFFFF) { + /* + * Key version is stored in 16 bits in the TPM, + * so key versions greater than 0xFFFF can't be + * stored properly. + */ + VBDEBUG(("Key version > 0xFFFF.\n")); + shpart->check_result = + VBSD_LKP_CHECK_KEY_ROLLBACK; + key_block_valid = 0; + } + } + + /* If not in developer mode, key block required to be valid. */ + if (kBootDev != boot_mode && !key_block_valid) { + VBDEBUG(("Key block is invalid.\n")); + goto bad_kernel; + } + + /* Get key for preamble/data verification from the key block. */ + data_key = PublicKeyToRSA(&key_block->data_key); + if (!data_key) { + VBDEBUG(("Data key bad.\n")); + shpart->check_result = VBSD_LKP_CHECK_DATA_KEY_PARSE; + goto bad_kernel; + } + + /* Verify the preamble, which follows the key block */ + preamble = (VbKernelPreambleHeader *) + (kbuf + key_block->key_block_size); + if ((0 != VerifyKernelPreamble( + preamble, + KBUF_SIZE - key_block->key_block_size, + data_key))) { + VBDEBUG(("Preamble verification failed.\n")); + shpart->check_result = VBSD_LKP_CHECK_VERIFY_PREAMBLE; + goto bad_kernel; + } + + /* + * If the key block is valid and we're not in recovery mode, + * check for rollback of the kernel version. + */ + combined_version = (uint32_t)( + (key_version << 16) | + (preamble->kernel_version & 0xFFFF)); + shpart->combined_version = combined_version; + if (key_block_valid && kBootRecovery != boot_mode) { + if (combined_version < shared->kernel_version_tpm) { + VBDEBUG(("Kernel version too low.\n")); + shpart->check_result = + VBSD_LKP_CHECK_KERNEL_ROLLBACK; + /* + * If not in developer mode, kernel version + * must be valid. + */ + if (kBootDev != boot_mode) + goto bad_kernel; + } + } + + VBDEBUG(("Kernel preamble is good.\n")); + shpart->check_result = VBSD_LKP_CHECK_PREAMBLE_VALID; + + /* Check for lowest version from a valid header. */ + if (key_block_valid && lowest_version > combined_version) + lowest_version = combined_version; + else { + VBDEBUG(("Key block valid: %d\n", key_block_valid)); + VBDEBUG(("Combined version: %u\n", + (unsigned) combined_version)); + } + + /* + * If we already have a good kernel, no need to read another + * one; we only needed to look at the versions to check for + * rollback. So skip to the next kernel preamble. + */ + if (-1 != good_partition) + continue; + + /* Verify kernel body starts at multiple of sector size. */ + body_offset = key_block->key_block_size + + preamble->preamble_size; + if (0 != body_offset % blba) { + VBDEBUG(("Kernel body not at multiple of " + "sector size.\n")); + shpart->check_result = VBSD_LKP_CHECK_BODY_OFFSET; + goto bad_kernel; + } + body_offset_sectors = body_offset / blba; + + body_sectors = + (preamble->body_signature.data_size + blba - 1) / blba; + if (!params->kernel_buffer) { + /* Get kernel load address and size from the header. */ + params->kernel_buffer = + (void *)((long)preamble->body_load_address); + params->kernel_buffer_size = body_sectors * blba; + } else { + /* Verify kernel body fits in the buffer */ + if (body_sectors * blba > params->kernel_buffer_size) { + VBDEBUG(("Kernel body doesn't " + "fit in memory.\n")); + shpart->check_result = + VBSD_LKP_CHECK_BODY_EXCEEDS_MEM; + goto bad_kernel; + } + } + + /* Verify kernel body fits in the partition */ + if (body_offset_sectors + body_sectors > part_size) { + VBDEBUG(("Kernel body doesn't fit in partition.\n")); + shpart->check_result = VBSD_LKP_CHECK_BODY_EXCEEDS_PART; + goto bad_kernel; + } + + /* Read the kernel data */ + VBPERFSTART("VB_RKD"); + if (0 != VbExDiskRead(params->disk_handle, + part_start + body_offset_sectors, + body_sectors, params->kernel_buffer)) { + VBDEBUG(("Unable to read kernel data.\n")); + VBPERFEND("VB_RKD"); + shpart->check_result = VBSD_LKP_CHECK_READ_DATA; + goto bad_kernel; + } + VBPERFEND("VB_RKD"); + + /* Verify kernel data */ + if (0 != VerifyData((const uint8_t *)params->kernel_buffer, + params->kernel_buffer_size, + &preamble->body_signature, data_key)) { + VBDEBUG(("Kernel data verification failed.\n")); + shpart->check_result = VBSD_LKP_CHECK_VERIFY_DATA; + goto bad_kernel; + } + + /* Done with the kernel signing key, so can free it now */ + RSAPublicKeyFree(data_key); + data_key = NULL; #endif - /* If we're still here, the kernel is valid. */ - /* Save the first good partition we find; that's the one we'll boot */ - VBDEBUG(("Partition is good.\n")); - shpart->check_result = VBSD_LKP_CHECK_KERNEL_GOOD; - if (key_block_valid) - shpart->flags |= VBSD_LKP_FLAG_KEY_BLOCK_VALID; - - good_partition_key_block_valid = key_block_valid; - /* TODO: GPT partitions start at 1, but cgptlib starts them at 0. - * Adjust here, until cgptlib is fixed. */ - good_partition = gpt.current_kernel + 1; - params->partition_number = gpt.current_kernel + 1; - GetCurrentKernelUniqueGuid(&gpt, ¶ms->partition_guid); - /* TODO: GetCurrentKernelUniqueGuid() should take a destination size, or - * the dest should be a struct, so we know it's big enough. */ + /* + * If we're still here, the kernel is valid. Save the first + * good partition we find; that's the one we'll boot. + */ + VBDEBUG(("Partition is good.\n")); + shpart->check_result = VBSD_LKP_CHECK_KERNEL_GOOD; + if (key_block_valid) + shpart->flags |= VBSD_LKP_FLAG_KEY_BLOCK_VALID; + + good_partition_key_block_valid = key_block_valid; + /* + * TODO: GPT partitions start at 1, but cgptlib starts them at + * 0. Adjust here, until cgptlib is fixed. + */ + good_partition = gpt.current_kernel + 1; + params->partition_number = gpt.current_kernel + 1; + GetCurrentKernelUniqueGuid(&gpt, ¶ms->partition_guid); + /* + * TODO: GetCurrentKernelUniqueGuid() should take a destination + * size, or the dest should be a struct, so we know it's big + * enough. + */ #if defined(CONFIG_SANDBOX) - params->bootloader_address = 0; - params->bootloader_size = 0; + params->bootloader_address = 0; + params->bootloader_size = 0; #else - params->bootloader_address = preamble->bootloader_address; - params->bootloader_size = preamble->bootloader_size; + params->bootloader_address = preamble->bootloader_address; + params->bootloader_size = preamble->bootloader_size; #endif - /* Update GPT to note this is the kernel we're trying */ - GptUpdateKernelEntry(&gpt, GPT_UPDATE_ENTRY_TRY); - - /* If we're in recovery mode or we're about to boot a dev-signed kernel, - * there's no rollback protection, so we can stop at the first valid - * kernel. */ - if (kBootRecovery == boot_mode || !key_block_valid) { - VBDEBUG(("In recovery mode or dev-signed kernel\n")); - break; - } - - /* Otherwise, 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 (combined_version == shared->kernel_version_tpm) { - VBDEBUG(("Same kernel version\n")); - break; - } - - /* Continue, so that we skip the error handling code below */ - continue; - - bad_kernel: - /* Handle errors parsing this kernel */ - if (NULL != data_key) - RSAPublicKeyFree(data_key); - - VBDEBUG(("Marking kernel as invalid.\n")); - GptUpdateKernelEntry(&gpt, GPT_UPDATE_ENTRY_BAD); - - - } /* while(GptNextKernelEntry) */ - - bad_gpt: - - /* Free kernel buffer */ - if (kbuf) - VbExFree(kbuf); - - /* Write and free GPT data */ - WriteAndFreeGptData(params->disk_handle, &gpt); - - /* Handle finding a good partition */ - if (good_partition >= 0) { - VBDEBUG(("Good_partition >= 0\n")); - shcall->check_result = VBSD_LKC_CHECK_GOOD_PARTITION; - shared->kernel_version_lowest = lowest_version; - /* Sanity check - only store a new TPM version if we found one. - * If lowest_version is still at its initial value, we didn't find - * one; for example, we're in developer mode and just didn't look. */ - if (lowest_version != LOWEST_TPM_VERSION && - lowest_version > shared->kernel_version_tpm) - shared->kernel_version_tpm = lowest_version; - - /* Success! */ - retval = VBERROR_SUCCESS; - } else if (found_partitions > 0) { - shcall->check_result = VBSD_LKC_CHECK_INVALID_PARTITIONS; - recovery = VBNV_RECOVERY_RW_INVALID_OS; - retval = VBERROR_INVALID_KERNEL_FOUND; - } else { - shcall->check_result = VBSD_LKC_CHECK_NO_PARTITIONS; - recovery = VBNV_RECOVERY_RW_NO_OS; - retval = VBERROR_NO_KERNEL_FOUND; - } - -LoadKernelExit: - - /* Store recovery request, if any */ - VbNvSet(vnc, VBNV_RECOVERY_REQUEST, VBERROR_SUCCESS != retval ? - recovery : VBNV_RECOVERY_NOT_REQUESTED); - - /* If LoadKernel was called with bad parameters, - * shcall may not be initialized. */ - if (shcall) - shcall->return_code = (uint8_t)retval; - - /* Save whether the good partition's key block was fully verified */ - if (good_partition_key_block_valid) - shared->flags |= VBSD_KERNEL_KEY_VERIFIED; - - /* Store how much shared data we used, if any */ - params->shared_data_size = shared->data_used; - - return retval; + /* Update GPT to note this is the kernel we're trying */ + GptUpdateKernelEntry(&gpt, GPT_UPDATE_ENTRY_TRY); + + /* + * If we're in recovery mode or we're about to boot a + * dev-signed kernel, there's no rollback protection, so we can + * stop at the first valid kernel. + */ + if (kBootRecovery == boot_mode || !key_block_valid) { + VBDEBUG(("In recovery mode or dev-signed kernel\n")); + break; + } + + /* + * Otherwise, 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 (combined_version == shared->kernel_version_tpm) { + VBDEBUG(("Same kernel version\n")); + break; + } + + /* Continue, so that we skip the error handling code below */ + continue; + + bad_kernel: + /* Handle errors parsing this kernel */ + if (NULL != data_key) + RSAPublicKeyFree(data_key); + + VBDEBUG(("Marking kernel as invalid.\n")); + GptUpdateKernelEntry(&gpt, GPT_UPDATE_ENTRY_BAD); + + + } /* while(GptNextKernelEntry) */ + + bad_gpt: + + /* Free kernel buffer */ + if (kbuf) + VbExFree(kbuf); + + /* Write and free GPT data */ + WriteAndFreeGptData(params->disk_handle, &gpt); + + /* Handle finding a good partition */ + if (good_partition >= 0) { + VBDEBUG(("Good_partition >= 0\n")); + shcall->check_result = VBSD_LKC_CHECK_GOOD_PARTITION; + shared->kernel_version_lowest = lowest_version; + /* + * Sanity check - only store a new TPM version if we found one. + * If lowest_version is still at its initial value, we didn't + * find one; for example, we're in developer mode and just + * didn't look. + */ + if (lowest_version != LOWEST_TPM_VERSION && + lowest_version > shared->kernel_version_tpm) + shared->kernel_version_tpm = lowest_version; + + /* Success! */ + retval = VBERROR_SUCCESS; + } else if (found_partitions > 0) { + shcall->check_result = VBSD_LKC_CHECK_INVALID_PARTITIONS; + recovery = VBNV_RECOVERY_RW_INVALID_OS; + retval = VBERROR_INVALID_KERNEL_FOUND; + } else { + shcall->check_result = VBSD_LKC_CHECK_NO_PARTITIONS; + recovery = VBNV_RECOVERY_RW_NO_OS; + retval = VBERROR_NO_KERNEL_FOUND; + } + + LoadKernelExit: + + /* Store recovery request, if any */ + VbNvSet(vnc, VBNV_RECOVERY_REQUEST, VBERROR_SUCCESS != retval ? + recovery : VBNV_RECOVERY_NOT_REQUESTED); + + /* + * If LoadKernel() was called with bad parameters, shcall may not be + * initialized. + */ + if (shcall) + shcall->return_code = (uint8_t)retval; + + /* Save whether the good partition's key block was fully verified */ + if (good_partition_key_block_valid) + shared->flags |= VBSD_KERNEL_KEY_VERIFIED; + + /* Store how much shared data we used, if any */ + params->shared_data_size = shared->data_used; + + return retval; } |