/* SPDX-License-Identifier: GPL-2.0-only */ #include #include #include #include #include #include #include #define RSA_PUBLICKEY_FILE_NAME "vboot_public_key.bin" #if CONFIG(VENDORCODE_ELTAN_VBOOT_USE_SHA512) #define DIGEST_SIZE VB2_SHA512_DIGEST_SIZE #define HASH_ALG VB2_HASH_SHA512 #else #define DIGEST_SIZE VB2_SHA256_DIGEST_SIZE #define HASH_ALG VB2_HASH_SHA256 #endif int verified_boot_check_manifest(void) { uint8_t *buffer; struct vb2_context *ctx; struct vb2_kernel_preamble *pre; static struct vb2_shared_data *sd; size_t size; uint8_t wb_buffer[3000]; if (vb2api_init(&wb_buffer, sizeof(wb_buffer), &ctx)) { goto fail; } sd = vb2_get_sd(ctx); buffer = cbfs_boot_map_with_leak(RSA_PUBLICKEY_FILE_NAME, CBFS_TYPE_RAW, &size); if (!buffer || !size) { printk(BIOS_ERR, "ERROR: Public key not found!\n"); goto fail; } if ((size != CONFIG_VENDORCODE_ELTAN_VBOOT_KEY_SIZE) || (buffer != (void *)CONFIG_VENDORCODE_ELTAN_VBOOT_KEY_LOCATION)) { printk(BIOS_ERR, "ERROR: Illegal public key!\n"); goto fail; } /* * Check if all items will fit into workbuffer: * vb2_shared data, Public Key, Preamble data */ if ((sd->workbuf_used + size + sizeof(struct vb2_kernel_preamble) + ((CONFIG_VENDORCODE_ELTAN_OEM_MANIFEST_ITEMS * DIGEST_SIZE) + (2048/8))) > sizeof(wb_buffer)) { printk(BIOS_ERR, "ERROR: Work buffer too small\n"); goto fail; } /* Add public key */ sd->data_key_offset = sd->workbuf_used; sd->data_key_size = size; sd->workbuf_used += sd->data_key_size; memcpy((void *)((void *)sd + (long)sd->data_key_offset), (uint8_t *)buffer, size); /* Fill preamble area */ sd->preamble_size = sizeof(struct vb2_kernel_preamble); sd->preamble_offset = sd->data_key_offset + sd->data_key_size; sd->workbuf_used += sd->preamble_size; pre = (struct vb2_kernel_preamble *)((void *)sd + (long)sd->preamble_offset); pre->flags = VB2_FIRMWARE_PREAMBLE_DISALLOW_HWCRYPTO; /* Fill body_signature (vb2_structure). RSA2048 key is used */ cbfs_boot_map_with_leak("oemmanifest.bin", CBFS_TYPE_RAW, &size); if (size != ((CONFIG_VENDORCODE_ELTAN_OEM_MANIFEST_ITEMS * DIGEST_SIZE) + (2048/8))) { printk(BIOS_ERR, "ERROR: Incorrect manifest size!\n"); goto fail; } pre->body_signature.data_size = CONFIG_VENDORCODE_ELTAN_OEM_MANIFEST_ITEMS * DIGEST_SIZE; pre->body_signature.sig_offset = sizeof(struct vb2_signature) + pre->body_signature.data_size; pre->body_signature.sig_size = size - pre->body_signature.data_size; sd->workbuf_used += size; memcpy((void *)((void *)&pre->body_signature + (long)sizeof(struct vb2_signature)), (uint8_t *)CONFIG_VENDORCODE_ELTAN_OEM_MANIFEST_LOC, size); if (vb2api_verify_kernel_data(ctx, (void *)CONFIG_VENDORCODE_ELTAN_OEM_MANIFEST_LOC, pre->body_signature.data_size)) goto fail; printk(BIOS_INFO, "%s: Successfully verified hash_table signature.\n", __func__); return 0; fail: die("ERROR: HASH table verification failed!\n"); return -1; } /* * * measure_item * * extends the defined pcr using the hash calculated by the verified boot * routines. * * @param[in] pcr PCR to extend * @param[in] *hashData Pointer to the hash data * @param[in] hashDataLen Length of the hash data * @param[in] *event_msg Message to log or display * @param[in] eventType Event type to use when logging * @retval TPM_SUCCESS Operation completed successfully. * @retval TPM_E_IOERROR Unexpected device behavior. */ static int measure_item(uint32_t pcr, uint8_t *hashData, uint32_t hashDataLen, int8_t *event_msg, TCG_EVENTTYPE eventType) { int status = TPM_SUCCESS; TCG_PCR_EVENT2_HDR tcgEventHdr; memset(&tcgEventHdr, 0, sizeof(tcgEventHdr)); tcgEventHdr.pcrIndex = pcr; tcgEventHdr.eventType = eventType; if (event_msg) { status = mboot_hash_extend_log(MBOOT_HASH_PROVIDED, hashData, hashDataLen, &tcgEventHdr, (uint8_t *)event_msg); if (status == TPM_SUCCESS) printk(BIOS_INFO, "%s: Success! %s measured to pcr %d.\n", __func__, event_msg, pcr); } return status; } static void verified_boot_check_buffer(const char *name, void *start, size_t size, uint32_t hash_index, int32_t pcr) { uint8_t digest[DIGEST_SIZE]; vb2_error_t status; printk(BIOS_DEBUG, "%s: %s HASH verification buffer %p size %d\n", __func__, name, start, (int)size); if (start && size) { status = vb2_digest_buffer((const uint8_t *)start, size, HASH_ALG, digest, DIGEST_SIZE); if ((CONFIG(VENDORCODE_ELTAN_VBOOT) && memcmp((void *)( (uint8_t *)CONFIG_VENDORCODE_ELTAN_OEM_MANIFEST_LOC + sizeof(digest) * hash_index), digest, sizeof(digest))) || status) { printk(BIOS_DEBUG, "%s: buffer hash\n", __func__); hexdump(digest, sizeof(digest)); printk(BIOS_DEBUG, "%s: manifest hash\n", __func__); hexdump((void *)( (uint8_t *)CONFIG_VENDORCODE_ELTAN_OEM_MANIFEST_LOC + sizeof(digest) * hash_index), sizeof(digest)); printk(BIOS_EMERG, "%s ", name); die("HASH verification failed!\n"); } else { if (!ENV_BOOTBLOCK && CONFIG(VENDORCODE_ELTAN_MBOOT)) { if (pcr != -1) { printk(BIOS_DEBUG, "%s: measuring %s\n", __func__, name); if (measure_item(pcr, digest, sizeof(digest), (int8_t *)name, 0)) printk(BIOS_DEBUG, "%s: measuring failed!\n", __func__); } } if (CONFIG(VENDORCODE_ELTAN_VBOOT)) printk(BIOS_DEBUG, "%s HASH verification success\n", name); } } else { printk(BIOS_EMERG, "Invalid buffer\n"); die("HASH verification failed!\n"); } } void verified_boot_check_cbfsfile(const char *name, uint32_t type, uint32_t hash_index, void **buffer, uint32_t *filesize, int32_t pcr) { void *start; size_t size; start = cbfs_boot_map_with_leak(name, type & ~VERIFIED_BOOT_COPY_BLOCK, &size); if (start && size) { /* Speed up processing by copying the file content to memory first */ if (!ENV_ROMSTAGE_OR_BEFORE && (type & VERIFIED_BOOT_COPY_BLOCK)) { if ((buffer) && (*buffer) && (*filesize >= size) && ((uint32_t) start > (uint32_t)(~(CONFIG_CBFS_SIZE-1)))) { /* Use the buffer passed in if possible */ printk(BIOS_DEBUG, "%s: move buffer to memory\n", __func__); /* Move the file to memory buffer passed in */ memcpy(*buffer, start, size); start = *buffer; printk(BIOS_DEBUG, "%s: done\n", __func__); } else if (ENV_RAMSTAGE) { /* Try to allocate a buffer from boot_mem */ void *local_buffer = bootmem_allocate_buffer(size); if (local_buffer) { /* Use the allocated buffer */ printk(BIOS_DEBUG, "%s: move file to memory\n", __func__); memcpy(local_buffer, start, size); start = local_buffer; printk(BIOS_DEBUG, "%s: done\n", __func__); } } } verified_boot_check_buffer(name, start, size, hash_index, pcr); } else { printk(BIOS_EMERG, "CBFS Failed to get file content for %s\n", name); die("HASH verification failed!\n"); } if (buffer) *buffer = start; if (filesize) *filesize = size; } void process_verify_list(const verify_item_t list[]) { int i = 0; while (list[i].type != VERIFY_TERMINATOR) { switch (list[i].type) { case VERIFY_FILE: verified_boot_check_cbfsfile(list[i].name, list[i].data.file.cbfs_type, list[i].hash_index, NULL, NULL, list[i].pcr); if (list[i].data.file.related_items) { printk(BIOS_SPEW, "process related items\n"); process_verify_list( (verify_item_t *)list[i].data.file.related_items); } break; case VERIFY_BLOCK: verified_boot_check_buffer(list[i].name, (void *)list[i].data.block.start, list[i].data.block.size, list[i].hash_index, list[i].pcr); break; default: printk(BIOS_EMERG, "INVALID TYPE IN VERIFY LIST 0x%x\n", list[i].type); die("HASH verification failed!\n"); } i++; } } /* * BOOTBLOCK */ void verified_boot_bootblock_check(void) { printk(BIOS_SPEW, "%s: processing bootblock items\n", __func__); if (CONFIG(VENDORCODE_ELTAN_VBOOT_SIGNED_MANIFEST)) { printk(BIOS_SPEW, "%s: check the manifest\n", __func__); if (verified_boot_check_manifest() != 0) die("invalid manifest"); } printk(BIOS_SPEW, "%s: process bootblock verify list\n", __func__); process_verify_list(bootblock_verify_list); } /* * ROMSTAGE */ void verified_boot_early_check(void) { printk(BIOS_SPEW, "%s: processing early items\n", __func__); if (CONFIG(VENDORCODE_ELTAN_MBOOT)) { printk(BIOS_DEBUG, "mb_measure returned 0x%x\n", mb_measure(platform_is_resuming())); } printk(BIOS_SPEW, "%s: process early verify list\n", __func__); process_verify_list(romstage_verify_list); } /* * RAM STAGE */ static int process_oprom_list(const verify_item_t list[], struct rom_header *rom_header) { int i = 0; struct pci_data *rom_data; uint32_t viddevid = 0; if (le32_to_cpu(rom_header->signature) != PCI_ROM_HDR) { printk(BIOS_ERR, "Incorrect expansion ROM header signature %04x DONT START\n", le32_to_cpu(rom_header->signature)); return 0; } rom_data = (((void *)rom_header) + le32_to_cpu(rom_header->data)); viddevid |= (rom_data->vendor << 16); viddevid |= rom_data->device; while (list[i].type != VERIFY_TERMINATOR) { switch (list[i].type) { case VERIFY_OPROM: if (viddevid == list[i].data.oprom.viddev) { verified_boot_check_buffer(list[i].name, (void *)rom_header, rom_header->size * 512, list[i].hash_index, list[i].pcr); if (list[i].data.oprom.related_items) { printk(BIOS_SPEW, "%s: process related items\n", __func__); process_verify_list( (verify_item_t *)list[i].data.oprom.related_items); } printk(BIOS_SPEW, "%s: option rom can be started\n", __func__); return 1; } break; default: printk(BIOS_EMERG, "%s: INVALID TYPE IN OPTION ROM LIST 0x%x\n", __func__, list[i].type); die("HASH verification failed!\n"); } i++; } printk(BIOS_ERR, "%s: option rom not in list DONT START\n", __func__); return 0; } int verified_boot_should_run_oprom(struct rom_header *rom_header) { return process_oprom_list(oprom_verify_list, rom_header); } int prog_locate_hook(struct prog *prog) { if (ENV_BOOTBLOCK) verified_boot_bootblock_check(); if (ENV_ROMSTAGE) { if (prog->type == PROG_REFCODE) verified_boot_early_check(); if (CONFIG(POSTCAR_STAGE) && prog->type == PROG_POSTCAR) process_verify_list(postcar_verify_list); if (!CONFIG(POSTCAR_STAGE) && prog->type == PROG_RAMSTAGE) process_verify_list(ramstage_verify_list); } if (ENV_POSTCAR && prog->type == PROG_RAMSTAGE) process_verify_list(ramstage_verify_list); if (ENV_RAMSTAGE && prog->type == PROG_PAYLOAD) process_verify_list(payload_verify_list); return 0; }