Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1a83a583 authored by Badhri Jagan Sridharan's avatar Badhri Jagan Sridharan Committed by Amit Pundir
Browse files

ANDROID: dm: android-verity: Verify header before fetching table

Move header validation logic before reading the verity_table as
an invalid header implies the table is invalid as well.

(Cherry-picked from:
https://partner-android-review.git.corp.google.com/#/c/625203

)

BUG: 29940612
Signed-off-by: default avatarBadhri Jagan Sridharan <Badhri@google.com>
Change-Id: Ib34d25c0854202f3e70df0a6d0ef1d96f0250c8e
parent 2cced7fc
Loading
Loading
Loading
Loading
+71 −69
Original line number Original line Diff line number Diff line
@@ -365,12 +365,38 @@ static int find_size(dev_t dev, u64 *device_size)
	return 0;
	return 0;
}
}


static struct android_metadata *extract_metadata(dev_t dev,
static int verify_header(struct android_metadata_header *header)
				struct fec_header *fec)
{
	int retval = -EINVAL;

	if (is_userdebug() && le32_to_cpu(header->magic_number) ==
			VERITY_METADATA_MAGIC_DISABLE)
		return VERITY_STATE_DISABLE;

	if (!(le32_to_cpu(header->magic_number) ==
			VERITY_METADATA_MAGIC_NUMBER) ||
			(le32_to_cpu(header->magic_number) ==
			VERITY_METADATA_MAGIC_DISABLE)) {
		DMERR("Incorrect magic number");
		return retval;
	}

	if (le32_to_cpu(header->protocol_version) !=
			VERITY_METADATA_VERSION) {
		DMERR("Unsupported version %u",
			le32_to_cpu(header->protocol_version));
		return retval;
	}

	return 0;
}

static int extract_metadata(dev_t dev, struct fec_header *fec,
				struct android_metadata **metadata,
				bool *verity_enabled)
{
{
	struct block_device *bdev;
	struct block_device *bdev;
	struct android_metadata_header *header;
	struct android_metadata_header *header;
	struct android_metadata *uninitialized_var(metadata);
	int i;
	int i;
	u32 table_length, copy_length, offset;
	u32 table_length, copy_length, offset;
	u64 metadata_offset;
	u64 metadata_offset;
@@ -381,7 +407,7 @@ static struct android_metadata *extract_metadata(dev_t dev,


	if (IS_ERR_OR_NULL(bdev)) {
	if (IS_ERR_OR_NULL(bdev)) {
		DMERR("blkdev_get_by_dev failed");
		DMERR("blkdev_get_by_dev failed");
		return ERR_CAST(bdev);
		return -ENODEV;
	}
	}


	find_metadata_offset(fec, bdev, &metadata_offset);
	find_metadata_offset(fec, bdev, &metadata_offset);
@@ -399,7 +425,6 @@ static struct android_metadata *extract_metadata(dev_t dev,
		(1 << SECTOR_SHIFT), VERITY_METADATA_SIZE);
		(1 << SECTOR_SHIFT), VERITY_METADATA_SIZE);
	if (err) {
	if (err) {
		DMERR("Error while reading verity metadata");
		DMERR("Error while reading verity metadata");
		metadata = ERR_PTR(err);
		goto blkdev_release;
		goto blkdev_release;
	}
	}


@@ -418,24 +443,42 @@ static struct android_metadata *extract_metadata(dev_t dev,
		le32_to_cpu(header->protocol_version),
		le32_to_cpu(header->protocol_version),
		le32_to_cpu(header->table_length));
		le32_to_cpu(header->table_length));


	metadata = kzalloc(sizeof(*metadata), GFP_KERNEL);
	err = verify_header(header);
	if (!metadata) {

	if (err == VERITY_STATE_DISABLE) {
		DMERR("Mounting root with verity disabled");
		*verity_enabled = false;
		/* we would still have to read the metadata to figure out
		 * the data blocks size. Or may be could map the entire
		 * partition similar to mounting the device.
		 *
		 * Reset error as well as the verity_enabled flag is changed.
		 */
		err = 0;
	} else if (err)
		goto free_header;

	*metadata = kzalloc(sizeof(**metadata), GFP_KERNEL);
	if (!*metadata) {
		DMERR("kzalloc for metadata failed");
		DMERR("kzalloc for metadata failed");
		err = -ENOMEM;
		err = -ENOMEM;
		goto free_header;
		goto free_header;
	}
	}


	metadata->header = header;
	(*metadata)->header = header;
	table_length = le32_to_cpu(header->table_length);
	table_length = le32_to_cpu(header->table_length);


	if (table_length == 0 ||
	if (table_length == 0 ||
		table_length > (VERITY_METADATA_SIZE -
		table_length > (VERITY_METADATA_SIZE -
			sizeof(struct android_metadata_header)))
			sizeof(struct android_metadata_header))) {
		DMERR("table_length too long");
		err = -EINVAL;
		goto free_metadata;
		goto free_metadata;
	}


	metadata->verity_table = kzalloc(table_length + 1, GFP_KERNEL);
	(*metadata)->verity_table = kzalloc(table_length + 1, GFP_KERNEL);


	if (!metadata->verity_table) {
	if (!(*metadata)->verity_table) {
		DMERR("kzalloc verity_table failed");
		DMERR("kzalloc verity_table failed");
		err = -ENOMEM;
		err = -ENOMEM;
		goto free_metadata;
		goto free_metadata;
@@ -443,13 +486,15 @@ static struct android_metadata *extract_metadata(dev_t dev,


	if (sizeof(struct android_metadata_header) +
	if (sizeof(struct android_metadata_header) +
			table_length <= PAGE_SIZE) {
			table_length <= PAGE_SIZE) {
		memcpy(metadata->verity_table, page_address(payload.page_io[0])
		memcpy((*metadata)->verity_table,
			page_address(payload.page_io[0])
			+ sizeof(struct android_metadata_header),
			+ sizeof(struct android_metadata_header),
			table_length);
			table_length);
	} else {
	} else {
		copy_length = PAGE_SIZE -
		copy_length = PAGE_SIZE -
			sizeof(struct android_metadata_header);
			sizeof(struct android_metadata_header);
		memcpy(metadata->verity_table, page_address(payload.page_io[0])
		memcpy((*metadata)->verity_table,
			page_address(payload.page_io[0])
			+ sizeof(struct android_metadata_header),
			+ sizeof(struct android_metadata_header),
			copy_length);
			copy_length);
		table_length -= copy_length;
		table_length -= copy_length;
@@ -457,13 +502,13 @@ static struct android_metadata *extract_metadata(dev_t dev,
		i = 1;
		i = 1;
		while (table_length != 0) {
		while (table_length != 0) {
			if (table_length > PAGE_SIZE) {
			if (table_length > PAGE_SIZE) {
				memcpy(metadata->verity_table + offset,
				memcpy((*metadata)->verity_table + offset,
					page_address(payload.page_io[i]),
					page_address(payload.page_io[i]),
					PAGE_SIZE);
					PAGE_SIZE);
				offset += PAGE_SIZE;
				offset += PAGE_SIZE;
				table_length -= PAGE_SIZE;
				table_length -= PAGE_SIZE;
			} else {
			} else {
				memcpy(metadata->verity_table + offset,
				memcpy((*metadata)->verity_table + offset,
					page_address(payload.page_io[i]),
					page_address(payload.page_io[i]),
					table_length);
					table_length);
				table_length = 0;
				table_length = 0;
@@ -471,25 +516,23 @@ static struct android_metadata *extract_metadata(dev_t dev,
			i++;
			i++;
		}
		}
	}
	}
	metadata->verity_table[table_length] = '\0';
	(*metadata)->verity_table[table_length] = '\0';


	DMINFO("verity_table: %s", (*metadata)->verity_table);
	goto free_payload;
	goto free_payload;


free_metadata:
free_metadata:
	kfree(metadata);
	kfree(*metadata);
free_header:
free_header:
	kfree(header);
	kfree(header);
	metadata = ERR_PTR(err);
free_payload:
free_payload:
	for (i = 0; i < payload.number_of_pages; i++)
	for (i = 0; i < payload.number_of_pages; i++)
		if (payload.page_io[i])
		if (payload.page_io[i])
			__free_page(payload.page_io[i]);
			__free_page(payload.page_io[i]);
	kfree(payload.page_io);
	kfree(payload.page_io);

	DMINFO("verity_table: %s", metadata->verity_table);
blkdev_release:
blkdev_release:
	blkdev_put(bdev, FMODE_READ);
	blkdev_put(bdev, FMODE_READ);
	return metadata;
	return err;
}
}


/* helper functions to extract properties from dts */
/* helper functions to extract properties from dts */
@@ -522,34 +565,6 @@ static int verity_mode(void)
	return DM_VERITY_MODE_EIO;
	return DM_VERITY_MODE_EIO;
}
}


static int verify_header(struct android_metadata_header *header)
{
	int retval = -EINVAL;

	if (is_userdebug() && le32_to_cpu(header->magic_number) ==
		VERITY_METADATA_MAGIC_DISABLE) {
		retval = VERITY_STATE_DISABLE;
		return retval;
	}

	if (!(le32_to_cpu(header->magic_number) ==
		VERITY_METADATA_MAGIC_NUMBER) ||
		(le32_to_cpu(header->magic_number) ==
		VERITY_METADATA_MAGIC_DISABLE)) {
		DMERR("Incorrect magic number");
		return retval;
	}

	if (le32_to_cpu(header->protocol_version) !=
		VERITY_METADATA_VERSION) {
		DMERR("Unsupported version %u",
			le32_to_cpu(header->protocol_version));
		return retval;
	}

	return 0;
}

static int verify_verity_signature(char *key_id,
static int verify_verity_signature(char *key_id,
		struct android_metadata *metadata)
		struct android_metadata *metadata)
{
{
@@ -649,7 +664,7 @@ static int add_as_linear_device(struct dm_target *ti, char *dev)
static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
{
{
	dev_t uninitialized_var(dev);
	dev_t uninitialized_var(dev);
	struct android_metadata *uninitialized_var(metadata);
	struct android_metadata *metadata = NULL;
	int err = 0, i, mode;
	int err = 0, i, mode;
	char *key_id, *table_ptr, dummy, *target_device,
	char *key_id, *table_ptr, dummy, *target_device,
	*verity_table_args[VERITY_TABLE_ARGS + 2 + VERITY_TABLE_OPT_FEC_ARGS];
	*verity_table_args[VERITY_TABLE_ARGS + 2 + VERITY_TABLE_OPT_FEC_ARGS];
@@ -717,26 +732,11 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
		return -EINVAL;
		return -EINVAL;
	}
	}


	metadata = extract_metadata(dev, &fec);
	err = extract_metadata(dev, &fec, &metadata, &verity_enabled);


	if (IS_ERR(metadata)) {
	if (err) {
		DMERR("Error while extracting metadata");
		DMERR("Error while extracting metadata");
		handle_error();
		handle_error();
		return -EINVAL;
	}

	err = verify_header(metadata->header);

	if (err == VERITY_STATE_DISABLE) {
		DMERR("Mounting root with verity disabled");
		verity_enabled = false;
		/* we would still have to parse the args to figure out
		 * the data blocks size. Or may be could map the entire
		 * partition similar to mounting the device.
		 */
	} else if (err) {
		DMERR("Verity header handle error");
		handle_error();
		goto free_metadata;
		goto free_metadata;
	}
	}


@@ -869,8 +869,10 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
	}
	}


free_metadata:
free_metadata:
	if (metadata) {
		kfree(metadata->header);
		kfree(metadata->header);
		kfree(metadata->verity_table);
		kfree(metadata->verity_table);
	}
	kfree(metadata);
	kfree(metadata);
	return err;
	return err;
}
}