vboot: Add validation checks for AVB verification data In vb2_load_android, the result of avb_slot_verify is used to determine verification success. To ensure the returned verification data matches the expected boot state, introduce verify_avb_data() to perform additional checks. BRANCH=none BUG=b:518008064 TEST=Build and boot test Change-Id: I93842945dda245da8176bbc805e9ad7556bcd7d4 Signed-off-by: Grzegorz Bernacki <bernacki@google.com> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/vboot_reference/+/8063940 Reviewed-by: Jakub "Kuba" Czapiga <czapiga@google.com> Reviewed-by: Julius Werner <jwerner@chromium.org>
diff --git a/firmware/2lib/2load_android_kernel.c b/firmware/2lib/2load_android_kernel.c index 2063369..1e2c1d6 100644 --- a/firmware/2lib/2load_android_kernel.c +++ b/firmware/2lib/2load_android_kernel.c
@@ -317,6 +317,134 @@ return VB2_SUCCESS; } +static void print_avb_slot_verify_data(const AvbSlotVerifyData *data) +{ + if (!data) { + VB2_DEBUG("AvbSlotVerifyData is NULL\n"); + return; + } + VB2_DEBUG("AvbSlotVerifyData:\n"); + VB2_DEBUG(" ab_suffix: %s\n", data->ab_suffix); + for (size_t i = 0; i < data->num_vbmeta_images; i++) { + const AvbVBMetaData *vbmeta = &data->vbmeta_images[i]; + VB2_DEBUG(" vbmeta '%s' verify_result: %d\n", + vbmeta->partition_name, vbmeta->verify_result); + } + for (size_t i = 0; i < data->num_loaded_partitions; i++) { + const AvbPartitionData *part = &data->loaded_partitions[i]; + VB2_DEBUG(" loaded '%s' data_size: %zu " + "preloaded: %d verify_result: %#x\n", + part->partition_name, part->data_size, + part->preloaded, part->verify_result); + } + + VB2_DEBUG(" rollback_index: %"PRIu64"\n", (uint64_t)data->rollback_indexes[0]); +} + +static vb2_error_t verify_avb_data(const AvbSlotVerifyData *verify_data, + AvbOps *avb_ops, + const char *expected_slot_suffix, + const char **requested_partitions, + bool need_verification) +{ + if (!verify_data) { + VB2_DEBUG("ERROR: verify_data is NULL\n"); + return VB2_ERROR_AVB_ERROR_VERIFICATION; + } + + /* Verify suffix */ + if (!verify_data->ab_suffix || + strcmp(verify_data->ab_suffix, expected_slot_suffix) != 0) { + VB2_DEBUG("ERROR: suffix mismatch. Expected %s, got %s\n", + expected_slot_suffix, + verify_data->ab_suffix ? verify_data->ab_suffix : "(null)"); + return VB2_ERROR_ANDROID_INVALID_SLOT_SUFFIX; + } + + /* Verify all requested partitions are loaded and preloaded */ + for (size_t i = 0; requested_partitions[i] != NULL; i++) { + const char *part_name = requested_partitions[i]; + const AvbPartitionData *part = NULL; + bool found = false; + for (size_t j = 0; j < verify_data->num_loaded_partitions; j++) { + part = &verify_data->loaded_partitions[j]; + if (part->partition_name && + strcmp(part->partition_name, part_name) == 0) { + found = true; + break; + } + } + + if (!found) { + VB2_DEBUG("ERROR: partition %s not found in loaded partitions\n", + part_name); + return VB2_ERROR_AVB_ERROR_VERIFICATION; + } + + if (!part->preloaded) { + VB2_DEBUG("ERROR: Partition %s is not preloaded\n", + part_name); + return VB2_ERROR_AVB_ERROR_IO; + } + + enum GptPartition gpt_part = GPT_ANDROID_PRELOADED_NUM; + for (size_t k = 0; k < GPT_ANDROID_PRELOADED_NUM; k++) { + if (strcmp(part_name, GptPartitionNames[k]) == 0) { + gpt_part = (enum GptPartition)k; + break; + } + } + if (gpt_part == GPT_ANDROID_PRELOADED_NUM) { + VB2_DEBUG("ERROR: Partition %s is not in preloaded list\n", + part_name); + return VB2_ERROR_AVB_ERROR_IO; + } + + /* Verify that libavb used our reserved preloaded buffer. */ + void *buffer; + size_t size; + AvbIOResult rv = vb2_android_get_buffer(avb_ops, gpt_part, &buffer, &size); + if (rv != AVB_IO_RESULT_OK || buffer != part->data || + size != part->data_size) { + VB2_DEBUG("ERROR: Partition %s buffer verification failed\n", + part_name); + return VB2_ERROR_AVB_ERROR_IO; + } + + if (!need_verification) + continue; + + if (part->verify_result != AVB_SLOT_VERIFY_RESULT_OK) { + VB2_DEBUG("ERROR: '%s' verification failed: %d\n", + part_name, part->verify_result); + return VB2_ERROR_AVB_ERROR_VERIFICATION; + } + + if (!part->digest || part->digest_size == 0) { + VB2_DEBUG("ERROR: Partition %s has no digest\n", + part_name); + return VB2_ERROR_AVB_ERROR_VERIFICATION; + } + } + + if (!need_verification) + goto out; + + /* Verify vbmeta images if verification is needed */ + for (size_t i = 0; i < verify_data->num_vbmeta_images; i++) { + const AvbVBMetaData *vbmeta = &verify_data->vbmeta_images[i]; + if (vbmeta->verify_result != AVB_VBMETA_VERIFY_RESULT_OK) { + VB2_DEBUG("ERROR: VBMeta image %s verification failed: %d\n", + vbmeta->partition_name ? vbmeta->partition_name : "(null)", + vbmeta->verify_result); + return VB2_ERROR_AVB_ERROR_VERIFICATION; + } + } + +out: + return VB2_SUCCESS; +} + vb2_error_t vb2_load_android(struct vb2_context *ctx, GptData *gpt, GptEntry *entry, struct vb2_kernel_params *params, vb2ex_disk_handle_t disk_handle, uint32_t *kernel_version) @@ -399,6 +527,13 @@ if (rv != VB2_SUCCESS) goto out; + print_avb_slot_verify_data(verify_data); + + rv = verify_avb_data(verify_data, avb_ops, slot_suffix, boot_partitions, + need_verification); + if (rv != VB2_SUCCESS) + goto out; + /* Fetch kernel version */ if (verify_data->rollback_indexes[0] & 0xFFFFFFFF00000000ULL) { rv = VB2_ERROR_ANDROID_ROLLBACK_VERSION_RANGE;
diff --git a/tests/vb2_load_android_tests.c b/tests/vb2_load_android_tests.c index 09d9b74..91e3d7f 100644 --- a/tests/vb2_load_android_tests.c +++ b/tests/vb2_load_android_tests.c
@@ -107,18 +107,49 @@ size_t num_bytes, part_size_bytes; part_size_bytes = PART_SIZE * BYTES_PER_LBA; - ops->get_preloaded_partition(ops, "boot_a", part_size_bytes, &out_pointer, &num_bytes); - if (!init_boot_missing) - ops->get_preloaded_partition(ops, "init_boot_a", part_size_bytes, &out_pointer, - &num_bytes); - if (!vendor_boot_missing) - ops->get_preloaded_partition(ops, "vendor_boot_a", part_size_bytes, &out_pointer, - &num_bytes); + + size_t num_loaded = 0; + for (size_t i = 0; requested_partitions[i] != NULL; i++) { + if (strcmp(requested_partitions[i], "init_boot") == 0 && init_boot_missing) + continue; + if (strcmp(requested_partitions[i], "vendor_boot") == 0 && vendor_boot_missing) + continue; + num_loaded++; + } verify_data = malloc(sizeof(*verify_data)); memset(verify_data, 0, sizeof(*verify_data)); verify_data->rollback_indexes[0] = rollback_value; verify_data->cmdline = (char *)""; + verify_data->ab_suffix = strdup(ab_suffix); + verify_data->num_loaded_partitions = num_loaded; + verify_data->loaded_partitions = calloc(num_loaded, sizeof(AvbPartitionData)); + + size_t idx = 0; + for (size_t i = 0; requested_partitions[i] != NULL; i++) { + if (strcmp(requested_partitions[i], "init_boot") == 0 && init_boot_missing) + continue; + if (strcmp(requested_partitions[i], "vendor_boot") == 0 && vendor_boot_missing) + continue; + AvbPartitionData *part = &verify_data->loaded_partitions[idx++]; + part->partition_name = strdup(requested_partitions[i]); + part->preloaded = true; + part->verify_result = AVB_SLOT_VERIFY_RESULT_OK; + part->digest = malloc(32); + memset(part->digest, 0xaa, 32); + part->digest_size = 32; + + char full_name[64]; + snprintf(full_name, sizeof(full_name), "%s%s", requested_partitions[i], + ab_suffix); + if (ops->get_preloaded_partition(ops, full_name, part_size_bytes, + &out_pointer, + &num_bytes) == AVB_IO_RESULT_OK) { + part->data = out_pointer; + part->data_size = num_bytes; + } + } + *out_data = verify_data; switch (avb_verification_fails) { @@ -134,6 +165,14 @@ void avb_slot_verify_data_free(AvbSlotVerifyData *data) { + if (!data) + return; + free(data->ab_suffix); + for (size_t i = 0; i < data->num_loaded_partitions; i++) { + free(data->loaded_partitions[i].partition_name); + free(data->loaded_partitions[i].digest); + } + free(data->loaded_partitions); free(data); }