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);
 }