/*
* Copyright (C) 2017 The Android Open Source Project
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "avb_user_verity.h"
/* Maximum allow length (in bytes) of a partition name, including
* ab_suffix.
*/
#define AVB_PART_NAME_MAX_SIZE 32
/* Loads the toplevel AvbVBMetaImageHeader from the slot denoted by
* |ab_suffix| into |vbmeta_image|. No validation, verification, or
* byteswapping is performed.
*
* If successful, |true| is returned and the partition it was loaded
* from is returned in |out_partition_name| and the offset on said
* partition is returned in |out_vbmeta_offset|.
*/
static bool load_top_level_vbmeta_header(
AvbOps* ops,
const char* ab_suffix,
uint8_t vbmeta_image[AVB_VBMETA_IMAGE_HEADER_SIZE],
char out_partition_name[AVB_PART_NAME_MAX_SIZE],
uint64_t* out_vbmeta_offset) {
uint64_t vbmeta_offset = 0;
size_t num_read;
bool ret = false;
AvbIOResult io_res;
/* Construct full partition name. */
if (!avb_str_concat(out_partition_name,
AVB_PART_NAME_MAX_SIZE,
"vbmeta",
6,
ab_suffix,
avb_strlen(ab_suffix))) {
avb_error("Partition name and suffix does not fit.\n");
goto out;
}
/* Only read the header, not the entire struct. */
io_res = ops->read_from_partition(ops,
out_partition_name,
vbmeta_offset,
AVB_VBMETA_IMAGE_HEADER_SIZE,
vbmeta_image,
&num_read);
if (io_res == AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION) {
AvbFooter footer;
/* Try looking for the vbmeta struct in 'boot' via the footer. */
if (!avb_str_concat(out_partition_name,
AVB_PART_NAME_MAX_SIZE,
"boot",
4,
ab_suffix,
avb_strlen(ab_suffix))) {
avb_error("Partition name and suffix does not fit.\n");
goto out;
}
io_res = ops->read_from_partition(ops,
out_partition_name,
-AVB_FOOTER_SIZE,
AVB_FOOTER_SIZE,
&footer,
&num_read);
if (io_res != AVB_IO_RESULT_OK) {
avb_errorv("Error loading footer from partition '",
out_partition_name,
"'\n",
NULL);
goto out;
}
if (avb_memcmp(footer.magic, AVB_FOOTER_MAGIC, AVB_FOOTER_MAGIC_LEN) != 0) {
avb_errorv("Data from '",
out_partition_name,
"' does not look like a vbmeta footer.\n",
NULL);
goto out;
}
vbmeta_offset = avb_be64toh(footer.vbmeta_offset);
io_res = ops->read_from_partition(ops,
out_partition_name,
vbmeta_offset,
AVB_VBMETA_IMAGE_HEADER_SIZE,
vbmeta_image,
&num_read);
}
if (io_res != AVB_IO_RESULT_OK) {
avb_errorv(
"Error loading from partition '", out_partition_name, "'\n", NULL);
goto out;
}
if (out_vbmeta_offset != NULL) {
*out_vbmeta_offset = vbmeta_offset;
}
ret = true;
out:
return ret;
}
bool avb_user_verity_get(AvbOps* ops,
const char* ab_suffix,
bool* out_verity_enabled) {
uint8_t vbmeta_image[AVB_VBMETA_IMAGE_HEADER_SIZE]; /* 256 bytes. */
char partition_name[AVB_PART_NAME_MAX_SIZE]; /* 32 bytes. */
AvbVBMetaImageHeader* header;
uint32_t flags;
bool ret = false;
if (!load_top_level_vbmeta_header(
ops, ab_suffix, vbmeta_image, partition_name, NULL)) {
goto out;
}
if (avb_memcmp(vbmeta_image, AVB_MAGIC, AVB_MAGIC_LEN) != 0) {
avb_errorv("Data from '",
partition_name,
"' does not look like a vbmeta header.\n",
NULL);
goto out;
}
/* Set/clear the HASHTREE_DISABLED bit, as requested. */
header = (AvbVBMetaImageHeader*)vbmeta_image;
flags = avb_be32toh(header->flags);
if (out_verity_enabled != NULL) {
*out_verity_enabled = !(flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
}
ret = true;
out:
return ret;
}
bool avb_user_verity_set(AvbOps* ops,
const char* ab_suffix,
bool enable_verity) {
uint8_t vbmeta_image[AVB_VBMETA_IMAGE_HEADER_SIZE]; /* 256 bytes. */
char partition_name[AVB_PART_NAME_MAX_SIZE]; /* 32 bytes. */
uint64_t vbmeta_offset;
AvbIOResult io_res;
AvbVBMetaImageHeader* header;
uint32_t flags;
bool ret = false;
if (!load_top_level_vbmeta_header(
ops, ab_suffix, vbmeta_image, partition_name, &vbmeta_offset)) {
goto out;
}
if (avb_memcmp(vbmeta_image, AVB_MAGIC, AVB_MAGIC_LEN) != 0) {
avb_errorv("Data from '",
partition_name,
"' does not look like a vbmeta header.\n",
NULL);
goto out;
}
/* Set/clear the HASHTREE_DISABLED bit, as requested. */
header = (AvbVBMetaImageHeader*)vbmeta_image;
flags = avb_be32toh(header->flags);
flags &= ~AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED;
if (!enable_verity) {
flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED;
}
header->flags = avb_htobe32(flags);
/* Write the header. */
io_res = ops->write_to_partition(ops,
partition_name,
vbmeta_offset,
AVB_VBMETA_IMAGE_HEADER_SIZE,
vbmeta_image);
if (io_res != AVB_IO_RESULT_OK) {
avb_errorv("Error writing to partition '", partition_name, "'\n", NULL);
goto out;
}
ret = true;
out:
return ret;
}