diff options
Diffstat (limited to 'lib/avb/libavb/avb_slot_verify.c')
-rw-r--r-- | lib/avb/libavb/avb_slot_verify.c | 727 |
1 files changed, 529 insertions, 198 deletions
diff --git a/lib/avb/libavb/avb_slot_verify.c b/lib/avb/libavb/avb_slot_verify.c index 57477d2..d00b9b9 100644 --- a/lib/avb/libavb/avb_slot_verify.c +++ b/lib/avb/libavb/avb_slot_verify.c @@ -30,6 +30,8 @@ #include "avb_sha.h" #include "avb_util.h" #include "avb_vbmeta_image.h" +#include "avb_version.h" +#include <common.h> /* Maximum allow length (in bytes) of a partition name, including * ab_suffix. @@ -39,14 +41,43 @@ /* Maximum number of partitions that can be loaded with avb_slot_verify(). */ #define MAX_NUMBER_OF_LOADED_PARTITIONS 32 +/* Maximum number of vbmeta images that can be loaded with avb_slot_verify(). */ +#define MAX_NUMBER_OF_VBMETA_IMAGES 32 + /* Maximum size of a vbmeta image - 64 KiB. */ #define VBMETA_MAX_SIZE (64 * 1024) +/* Helper function to see if we should continue with verification in + * allow_verification_error=true mode if something goes wrong. See the + * comments for the avb_slot_verify() function for more information. + */ +static inline bool result_should_continue(AvbSlotVerifyResult result) { + switch (result) { + case AVB_SLOT_VERIFY_RESULT_ERROR_OOM: + case AVB_SLOT_VERIFY_RESULT_ERROR_IO: + case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA: + case AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION: + return false; + + case AVB_SLOT_VERIFY_RESULT_OK: + case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION: + case AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX: + case AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED: + return true; + } + + return false; +} + static AvbSlotVerifyResult load_and_verify_hash_partition( - AvbOps* ops, const char* const* requested_partitions, const char* ab_suffix, - const AvbDescriptor* descriptor, AvbSlotVerifyData* slot_data) { + AvbOps* ops, + const char* const* requested_partitions, + const char* ab_suffix, + bool allow_verification_error, + const AvbDescriptor* descriptor, + AvbSlotVerifyData* slot_data) { AvbHashDescriptor hash_desc; - const uint8_t* desc_partition_name; + const uint8_t* desc_partition_name = NULL; const uint8_t* desc_salt; const uint8_t* desc_digest; char part_name[PART_NAME_MAX_SIZE]; @@ -75,9 +106,12 @@ static AvbSlotVerifyResult load_and_verify_hash_partition( goto out; } - if (!avb_str_concat( - part_name, sizeof part_name, (const char*)desc_partition_name, - hash_desc.partition_name_len, ab_suffix, avb_strlen(ab_suffix))) { + if (!avb_str_concat(part_name, + sizeof part_name, + (const char*)desc_partition_name, + hash_desc.partition_name_len, + ab_suffix, + avb_strlen(ab_suffix))) { avb_error("Partition name and suffix does not fit.\n"); ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; goto out; @@ -89,9 +123,12 @@ static AvbSlotVerifyResult load_and_verify_hash_partition( goto out; } - io_ret = - ops->read_from_partition(ops, part_name, 0 /* offset */, - hash_desc.image_size, image_buf, &part_num_read); + io_ret = ops->read_from_partition(ops, + part_name, + 0 /* offset */, + hash_desc.image_size, + image_buf, + &part_num_read); if (io_ret == AVB_IO_RESULT_ERROR_OOM) { ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; goto out; @@ -127,41 +164,46 @@ static AvbSlotVerifyResult load_and_verify_hash_partition( } if (digest_len != hash_desc.digest_len) { - avb_errorv(part_name, ": Digest in descriptor not of expected size.\n", - NULL); + avb_errorv( + part_name, ": Digest in descriptor not of expected size.\n", NULL); ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; goto out; } if (avb_safe_memcmp(digest, desc_digest, digest_len) != 0) { avb_errorv(part_name, - ": Hash of data does not match digest in descriptor.\n", NULL); + ": Hash of data does not match digest in descriptor.\n", + NULL); ret = AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION; goto out; } ret = AVB_SLOT_VERIFY_RESULT_OK; - /* If this is the requested partition, copy to slot_data. */ - found = - avb_strv_find_str(requested_partitions, (const char*)desc_partition_name, - hash_desc.partition_name_len); - if (found != NULL) { - AvbPartitionData* loaded_partition; - if (slot_data->num_loaded_partitions == MAX_NUMBER_OF_LOADED_PARTITIONS) { - avb_errorv(part_name, ": Too many loaded partitions.\n", NULL); - ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; - goto out; +out: + + if (ret == AVB_SLOT_VERIFY_RESULT_OK || result_should_continue(ret)) { + /* If this is the requested partition, copy to slot_data. */ + found = avb_strv_find_str(requested_partitions, + (const char*)desc_partition_name, + hash_desc.partition_name_len); + if (found != NULL) { + AvbPartitionData* loaded_partition; + if (slot_data->num_loaded_partitions == MAX_NUMBER_OF_LOADED_PARTITIONS) { + avb_errorv(part_name, ": Too many loaded partitions.\n", NULL); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto fail; + } + loaded_partition = + &slot_data->loaded_partitions[slot_data->num_loaded_partitions++]; + loaded_partition->partition_name = avb_strdup(found); + loaded_partition->data_size = hash_desc.image_size; + loaded_partition->data = image_buf; + image_buf = NULL; } - loaded_partition = - &slot_data->loaded_partitions[slot_data->num_loaded_partitions++]; - loaded_partition->partition_name = avb_strdup(found); - loaded_partition->data_size = hash_desc.image_size; - loaded_partition->data = image_buf; - image_buf = NULL; } -out: +fail: if (image_buf != NULL) { avb_free(image_buf); } @@ -169,10 +211,17 @@ out: } static AvbSlotVerifyResult load_and_verify_vbmeta( - AvbOps* ops, const char* const* requested_partitions, const char* ab_suffix, - int rollback_index_slot, const char* partition_name, - size_t partition_name_len, const uint8_t* expected_public_key, - size_t expected_public_key_length, AvbSlotVerifyData* slot_data, + AvbOps* ops, + const char* const* requested_partitions, + const char* ab_suffix, + bool allow_verification_error, + AvbVBMetaImageFlags toplevel_vbmeta_flags, + int rollback_index_location, + const char* partition_name, + size_t partition_name_len, + const uint8_t* expected_public_key, + size_t expected_public_key_length, + AvbSlotVerifyData* slot_data, AvbAlgorithmType* out_algorithm_type) { char full_partition_name[PART_NAME_MAX_SIZE]; AvbSlotVerifyResult ret; @@ -189,11 +238,20 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( const AvbDescriptor** descriptors = NULL; size_t num_descriptors; size_t n; - int is_main_vbmeta; + bool is_main_vbmeta; + bool is_vbmeta_partition; + AvbVBMetaData* vbmeta_image_data = NULL; + + ret = AVB_SLOT_VERIFY_RESULT_OK; avb_assert(slot_data != NULL); - is_main_vbmeta = (avb_strcmp(partition_name, "vbmeta") == 0); + /* Since we allow top-level vbmeta in 'boot', use + * rollback_index_location to determine whether we're the main + * vbmeta struct. + */ + is_main_vbmeta = (rollback_index_location == 0); + is_vbmeta_partition = (avb_strcmp(partition_name, "vbmeta") == 0); if (!avb_validate_utf8((const uint8_t*)partition_name, partition_name_len)) { avb_error("Partition name is not valid UTF-8.\n"); @@ -202,22 +260,27 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( } /* Construct full partition name. */ - if (!avb_str_concat(full_partition_name, sizeof full_partition_name, - partition_name, partition_name_len, ab_suffix, + if (!avb_str_concat(full_partition_name, + sizeof full_partition_name, + partition_name, + partition_name_len, + ab_suffix, avb_strlen(ab_suffix))) { avb_error("Partition name and suffix does not fit.\n"); ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; goto out; } - avb_debugv("Loading vbmeta struct from partition '", full_partition_name, - "'.\n", NULL); + avb_debugv("Loading vbmeta struct from partition '", + full_partition_name, + "'.\n", + NULL); /* If we're loading from the main vbmeta partition, the vbmeta * struct is in the beginning. Otherwise we have to locate it via a * footer. */ - if (is_main_vbmeta) { + if (is_vbmeta_partition) { vbmeta_offset = 0; vbmeta_size = VBMETA_MAX_SIZE; } else { @@ -225,9 +288,12 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( size_t footer_num_read; AvbFooter footer; - io_ret = - ops->read_from_partition(ops, full_partition_name, -AVB_FOOTER_SIZE, - AVB_FOOTER_SIZE, footer_buf, &footer_num_read); + io_ret = ops->read_from_partition(ops, + full_partition_name, + -AVB_FOOTER_SIZE, + AVB_FOOTER_SIZE, + footer_buf, + &footer_num_read); if (io_ret == AVB_IO_RESULT_ERROR_OOM) { ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; goto out; @@ -247,8 +313,8 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( /* Basic footer sanity check since the data is untrusted. */ if (footer.vbmeta_size > VBMETA_MAX_SIZE) { - avb_errorv(full_partition_name, ": Invalid vbmeta size in footer.\n", - NULL); + avb_errorv( + full_partition_name, ": Invalid vbmeta size in footer.\n", NULL); ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; goto out; } @@ -263,15 +329,42 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( goto out; } - io_ret = ops->read_from_partition(ops, full_partition_name, vbmeta_offset, - vbmeta_size, vbmeta_buf, &vbmeta_num_read); + io_ret = ops->read_from_partition(ops, + full_partition_name, + vbmeta_offset, + vbmeta_size, + vbmeta_buf, + &vbmeta_num_read); if (io_ret == AVB_IO_RESULT_ERROR_OOM) { ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; goto out; } else if (io_ret != AVB_IO_RESULT_OK) { - avb_errorv(full_partition_name, ": Error loading vbmeta data.\n", NULL); - ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; - goto out; + /* If we're looking for 'vbmeta' but there is no such partition, + * go try to get it from the boot partition instead. + */ + if (is_main_vbmeta && io_ret == AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION && + is_vbmeta_partition) { + avb_debugv(full_partition_name, + ": No such partition. Trying 'boot' instead.\n", + NULL); + ret = load_and_verify_vbmeta(ops, + requested_partitions, + ab_suffix, + allow_verification_error, + 0 /* toplevel_vbmeta_flags */, + 0 /* rollback_index_location */, + "boot", + avb_strlen("boot"), + NULL /* expected_public_key */, + 0 /* expected_public_key_length */, + slot_data, + out_algorithm_type); + goto out; + } else { + avb_errorv(full_partition_name, ": Error loading vbmeta data.\n", NULL); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; + goto out; + } } avb_assert(vbmeta_num_read <= vbmeta_size); @@ -280,60 +373,121 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( */ vbmeta_ret = avb_vbmeta_image_verify(vbmeta_buf, vbmeta_num_read, &pk_data, &pk_len); - if (vbmeta_ret != AVB_VBMETA_VERIFY_RESULT_OK) { - avb_errorv(full_partition_name, ": Error verifying vbmeta image.\n", NULL); - ret = AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION; - goto out; - } + switch (vbmeta_ret) { + case AVB_VBMETA_VERIFY_RESULT_OK: + avb_assert(pk_data != NULL && pk_len > 0); + break; - /* Check if key used to make signature matches what is expected. */ - if (expected_public_key != NULL) { - avb_assert(!is_main_vbmeta); - if (expected_public_key_length != pk_len || - avb_safe_memcmp(expected_public_key, pk_data, pk_len) != 0) { + case AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED: + case AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH: + case AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH: + ret = AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION; avb_errorv(full_partition_name, - ": Public key used to sign data does not match key in chain " - "partition descriptor.\n", + ": Error verifying vbmeta image: ", + avb_vbmeta_verify_result_to_string(vbmeta_ret), + "\n", NULL); - ret = AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED; - goto out; - } - } else { - bool key_is_trusted = false; + if (!allow_verification_error) { + goto out; + } + break; - avb_assert(is_main_vbmeta); - io_ret = - ops->validate_vbmeta_public_key(ops, pk_data, pk_len, &key_is_trusted); - if (io_ret == AVB_IO_RESULT_ERROR_OOM) { - ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; - goto out; - } else if (io_ret != AVB_IO_RESULT_OK) { + case AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER: + /* No way to continue this case. */ + ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; avb_errorv(full_partition_name, - ": Error while checking public key used to sign data.\n", + ": Error verifying vbmeta image: invalid vbmeta header\n", NULL); - ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; goto out; - } - if (!key_is_trusted) { + + case AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION: + /* No way to continue this case. */ + ret = AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION; avb_errorv(full_partition_name, - ": Public key used to sign data rejected.\n", NULL); - ret = AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED; + ": Error verifying vbmeta image: unsupported AVB version\n", + NULL); goto out; - } } + /* Byteswap the header. */ avb_vbmeta_image_header_to_host_byte_order((AvbVBMetaImageHeader*)vbmeta_buf, &vbmeta_header); + /* If we're the toplevel, assign flags so they'll be passed down. */ + if (is_main_vbmeta) { + toplevel_vbmeta_flags = (AvbVBMetaImageFlags)vbmeta_header.flags; + } else { + if (vbmeta_header.flags != 0) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; + avb_errorv(full_partition_name, + ": chained vbmeta image has non-zero flags\n", + NULL); + goto out; + } + } + + /* Check if key used to make signature matches what is expected. */ + if (pk_data != NULL) { + if (expected_public_key != NULL) { + avb_assert(!is_main_vbmeta); + if (expected_public_key_length != pk_len || + avb_safe_memcmp(expected_public_key, pk_data, pk_len) != 0) { + avb_errorv(full_partition_name, + ": Public key used to sign data does not match key in chain " + "partition descriptor.\n", + NULL); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED; + if (!allow_verification_error) { + goto out; + } + } + } else { + bool key_is_trusted = true; + const uint8_t* pk_metadata = NULL; + size_t pk_metadata_len = 0; + + if (vbmeta_header.public_key_metadata_size > 0) { + pk_metadata = vbmeta_buf + sizeof(AvbVBMetaImageHeader) + + vbmeta_header.authentication_data_block_size + + vbmeta_header.public_key_metadata_offset; + pk_metadata_len = vbmeta_header.public_key_metadata_size; + } + + avb_assert(is_main_vbmeta); + io_ret = ops->validate_vbmeta_public_key( + ops, pk_data, pk_len, pk_metadata, pk_metadata_len, &key_is_trusted); + if (io_ret == AVB_IO_RESULT_ERROR_OOM) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } else if (io_ret != AVB_IO_RESULT_OK) { + avb_errorv(full_partition_name, + ": Error while checking public key used to sign data.\n", + NULL); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; + goto out; + } + if (!key_is_trusted) { + avb_errorv(full_partition_name, + ": Public key used to sign data rejected.\n", + NULL); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED; + if (!allow_verification_error) { + goto out; + } + } + } + } + /* Check rollback index. */ - io_ret = ops->read_rollback_index(ops, rollback_index_slot, - &stored_rollback_index); + io_ret = ops->read_rollback_index( + ops, rollback_index_location, &stored_rollback_index); if (io_ret == AVB_IO_RESULT_ERROR_OOM) { ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; goto out; } else if (io_ret != AVB_IO_RESULT_OK) { avb_errorv(full_partition_name, - ": Error getting rollback index for slot.\n", NULL); + ": Error getting rollback index for location.\n", + NULL); ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; goto out; } @@ -343,8 +497,35 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( ": Image rollback index is less than the stored rollback index.\n", NULL); ret = AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX; + if (!allow_verification_error) { + goto out; + } + } + + /* Copy vbmeta to vbmeta_images before recursing. */ + if (is_main_vbmeta) { + avb_assert(slot_data->num_vbmeta_images == 0); + } else { + avb_assert(slot_data->num_vbmeta_images > 0); + } + if (slot_data->num_vbmeta_images == MAX_NUMBER_OF_VBMETA_IMAGES) { + avb_errorv(full_partition_name, ": Too many vbmeta images.\n", NULL); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; goto out; } + vbmeta_image_data = &slot_data->vbmeta_images[slot_data->num_vbmeta_images++]; + vbmeta_image_data->partition_name = avb_strdup(partition_name); + vbmeta_image_data->vbmeta_data = vbmeta_buf; + /* Note that |vbmeta_buf| is actually |vbmeta_num_read| bytes long + * and this includes data past the end of the image. Pass the + * actual size of the vbmeta image. Also, no need to use + * avb_safe_add() since the header has already been verified. + */ + vbmeta_image_data->vbmeta_size = + sizeof(AvbVBMetaImageHeader) + + vbmeta_header.authentication_data_block_size + + vbmeta_header.auxiliary_data_block_size; + vbmeta_image_data->verify_result = vbmeta_ret; /* Now go through all descriptors and take the appropriate action: * @@ -372,11 +553,17 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( switch (desc.tag) { case AVB_DESCRIPTOR_TAG_HASH: { AvbSlotVerifyResult sub_ret; - sub_ret = load_and_verify_hash_partition( - ops, requested_partitions, ab_suffix, descriptors[n], slot_data); + sub_ret = load_and_verify_hash_partition(ops, + requested_partitions, + ab_suffix, + allow_verification_error, + descriptors[n], + slot_data); if (sub_ret != AVB_SLOT_VERIFY_RESULT_OK) { ret = sub_ret; - goto out; + if (!allow_verification_error || !result_should_continue(ret)) { + goto out; + } } } break; @@ -398,7 +585,17 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( if (!avb_chain_partition_descriptor_validate_and_byteswap( (AvbChainPartitionDescriptor*)descriptors[n], &chain_desc)) { avb_errorv(full_partition_name, - ": Chain partition descriptor is invalid.\n", NULL); + ": Chain partition descriptor is invalid.\n", + NULL); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; + goto out; + } + + if (chain_desc.rollback_index_location == 0) { + avb_errorv(full_partition_name, + ": Chain partition has invalid " + "rollback_index_location field.\n", + NULL); ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; goto out; } @@ -407,27 +604,37 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( sizeof(AvbChainPartitionDescriptor); chain_public_key = chain_partition_name + chain_desc.partition_name_len; - sub_ret = load_and_verify_vbmeta( - ops, requested_partitions, ab_suffix, - chain_desc.rollback_index_slot, (const char*)chain_partition_name, - chain_desc.partition_name_len, chain_public_key, - chain_desc.public_key_len, slot_data, - NULL /* out_algorithm_type */); + sub_ret = load_and_verify_vbmeta(ops, + requested_partitions, + ab_suffix, + allow_verification_error, + toplevel_vbmeta_flags, + chain_desc.rollback_index_location, + (const char*)chain_partition_name, + chain_desc.partition_name_len, + chain_public_key, + chain_desc.public_key_len, + slot_data, + NULL /* out_algorithm_type */); if (sub_ret != AVB_SLOT_VERIFY_RESULT_OK) { ret = sub_ret; - goto out; + if (!result_should_continue(ret)) { + goto out; + } } } break; case AVB_DESCRIPTOR_TAG_KERNEL_CMDLINE: { const uint8_t* kernel_cmdline; AvbKernelCmdlineDescriptor kernel_cmdline_desc; + bool apply_cmdline; if (!avb_kernel_cmdline_descriptor_validate_and_byteswap( (AvbKernelCmdlineDescriptor*)descriptors[n], &kernel_cmdline_desc)) { avb_errorv(full_partition_name, - ": Kernel cmdline descriptor is invalid.\n", NULL); + ": Kernel cmdline descriptor is invalid.\n", + NULL); ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; goto out; } @@ -438,36 +645,59 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( if (!avb_validate_utf8(kernel_cmdline, kernel_cmdline_desc.kernel_cmdline_length)) { avb_errorv(full_partition_name, - ": Kernel cmdline is not valid UTF-8.\n", NULL); + ": Kernel cmdline is not valid UTF-8.\n", + NULL); ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; goto out; } - if (slot_data->cmdline == NULL) { - slot_data->cmdline = - avb_calloc(kernel_cmdline_desc.kernel_cmdline_length + 1); - if (slot_data->cmdline == NULL) { - ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; - goto out; + /* Compare the flags for top-level VBMeta struct with flags in + * the command-line descriptor so command-line snippets only + * intended for a certain mode (dm-verity enabled/disabled) + * are skipped if applicable. + */ + apply_cmdline = true; + if (toplevel_vbmeta_flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED) { + if (kernel_cmdline_desc.flags & + AVB_KERNEL_CMDLINE_FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED) { + apply_cmdline = false; } - avb_memcpy(slot_data->cmdline, kernel_cmdline, - kernel_cmdline_desc.kernel_cmdline_length); } else { - /* new cmdline is: <existing_cmdline> + ' ' + <newcmdline> + '\0' */ - size_t orig_size = avb_strlen(slot_data->cmdline); - size_t new_size = - orig_size + 1 + kernel_cmdline_desc.kernel_cmdline_length + 1; - char* new_cmdline = avb_calloc(new_size); - if (new_cmdline == NULL) { - ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; - goto out; + if (kernel_cmdline_desc.flags & + AVB_KERNEL_CMDLINE_FLAGS_USE_ONLY_IF_HASHTREE_DISABLED) { + apply_cmdline = false; + } + } + + if (apply_cmdline) { + if (slot_data->cmdline == NULL) { + slot_data->cmdline = + avb_calloc(kernel_cmdline_desc.kernel_cmdline_length + 1); + if (slot_data->cmdline == NULL) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } + avb_memcpy(slot_data->cmdline, + kernel_cmdline, + kernel_cmdline_desc.kernel_cmdline_length); + } else { + /* new cmdline is: <existing_cmdline> + ' ' + <newcmdline> + '\0' */ + size_t orig_size = avb_strlen(slot_data->cmdline); + size_t new_size = + orig_size + 1 + kernel_cmdline_desc.kernel_cmdline_length + 1; + char* new_cmdline = avb_calloc(new_size); + if (new_cmdline == NULL) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } + avb_memcpy(new_cmdline, slot_data->cmdline, orig_size); + new_cmdline[orig_size] = ' '; + avb_memcpy(new_cmdline + orig_size + 1, + kernel_cmdline, + kernel_cmdline_desc.kernel_cmdline_length); + avb_free(slot_data->cmdline); + slot_data->cmdline = new_cmdline; } - avb_memcpy(new_cmdline, slot_data->cmdline, orig_size); - new_cmdline[orig_size] = ' '; - avb_memcpy(new_cmdline + orig_size + 1, kernel_cmdline, - kernel_cmdline_desc.kernel_cmdline_length); - avb_free(slot_data->cmdline); - slot_data->cmdline = new_cmdline; } } break; @@ -479,32 +709,14 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( } } - ret = AVB_SLOT_VERIFY_RESULT_OK; - - /* So far, so good. Copy needed data to user, if requested. */ - if (is_main_vbmeta) { - if (slot_data->vbmeta_data != NULL) { - avb_free(slot_data->vbmeta_data); - } - /* Note that |vbmeta_buf| is actually |vbmeta_num_read| bytes long - * and this includes data past the end of the image. Pass the - * actual size of the vbmeta image. Also, no need to use - * avb_safe_add() since the header has already been verified. - */ - slot_data->vbmeta_size = sizeof(AvbVBMetaImageHeader) + - vbmeta_header.authentication_data_block_size + - vbmeta_header.auxiliary_data_block_size; - slot_data->vbmeta_data = vbmeta_buf; - vbmeta_buf = NULL; - } - - if (rollback_index_slot >= AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_SLOTS) { - avb_errorv(full_partition_name, ": Invalid rollback_index_slot.\n", NULL); + if (rollback_index_location >= AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS) { + avb_errorv( + full_partition_name, ": Invalid rollback_index_location.\n", NULL); ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; goto out; } - slot_data->rollback_indexes[rollback_index_slot] = + slot_data->rollback_indexes[rollback_index_location] = vbmeta_header.rollback_index; if (out_algorithm_type != NULL) { @@ -512,8 +724,13 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( } out: - if (vbmeta_buf != NULL) { - avb_free(vbmeta_buf); + /* If |vbmeta_image_data| isn't NULL it means that it adopted + * |vbmeta_buf| so in that case don't free it here. + */ + if (vbmeta_image_data == NULL) { + if (vbmeta_buf != NULL) { + avb_free(vbmeta_buf); + } } if (descriptors != NULL) { avb_free(descriptors); @@ -521,35 +738,47 @@ out: return ret; } -#define NUM_GUIDS 2 +#define NUM_GUIDS 3 /* Substitutes all variables (e.g. $(ANDROID_SYSTEM_PARTUUID)) with * values. Returns NULL on OOM, otherwise the cmdline with values * replaced. */ -static char* sub_cmdline(AvbOps* ops, const char* cmdline, - const char* ab_suffix) { - const char* part_name_str[NUM_GUIDS] = {"system", "boot"}; +static char* sub_cmdline(AvbOps* ops, + const char* cmdline, + const char* ab_suffix, + bool using_boot_for_vbmeta) { + const char* part_name_str[NUM_GUIDS] = {"system", "boot", "vbmeta"}; const char* replace_str[NUM_GUIDS] = {"$(ANDROID_SYSTEM_PARTUUID)", - "$(ANDROID_BOOT_PARTUUID)"}; + "$(ANDROID_BOOT_PARTUUID)", + "$(ANDROID_VBMETA_PARTUUID)"}; char* ret = NULL; AvbIOResult io_ret; + /* Special-case for when the top-level vbmeta struct is in the boot + * partition. + */ + if (using_boot_for_vbmeta) { + part_name_str[2] = "boot"; + } + /* Replace unique partition GUIDs */ for (size_t n = 0; n < NUM_GUIDS; n++) { char part_name[PART_NAME_MAX_SIZE]; char guid_buf[37]; - char* new_ret; - if (!avb_str_concat(part_name, sizeof part_name, part_name_str[n], - avb_strlen(part_name_str[n]), ab_suffix, + if (!avb_str_concat(part_name, + sizeof part_name, + part_name_str[n], + avb_strlen(part_name_str[n]), + ab_suffix, avb_strlen(ab_suffix))) { avb_error("Partition name and suffix does not fit.\n"); goto fail; } - io_ret = ops->get_unique_guid_for_partition(ops, part_name, guid_buf, - sizeof guid_buf); + io_ret = ops->get_unique_guid_for_partition( + ops, part_name, guid_buf, sizeof guid_buf); if (io_ret == AVB_IO_RESULT_ERROR_OOM) { return NULL; } else if (io_ret != AVB_IO_RESULT_OK) { @@ -558,14 +787,15 @@ static char* sub_cmdline(AvbOps* ops, const char* cmdline, } if (ret == NULL) { - new_ret = avb_replace(cmdline, replace_str[n], guid_buf); + ret = avb_replace(cmdline, replace_str[n], guid_buf); } else { - new_ret = avb_replace(ret, replace_str[n], guid_buf); + char* new_ret = avb_replace(ret, replace_str[n], guid_buf); + avb_free(ret); + ret = new_ret; } - if (new_ret == NULL) { + if (ret == NULL) { goto fail; } - ret = new_ret; } return ret; @@ -577,7 +807,8 @@ fail: return NULL; } -static int cmdline_append_option(AvbSlotVerifyData* slot_data, const char* key, +static int cmdline_append_option(AvbSlotVerifyData* slot_data, + const char* key, const char* value) { size_t offset, key_len, value_len; char* new_cmdline; @@ -612,14 +843,17 @@ static int cmdline_append_option(AvbSlotVerifyData* slot_data, const char* key, return 1; } -static int cmdline_append_uint64_base10(AvbSlotVerifyData* slot_data, - const char* key, uint64_t value) { - const int MAX_DIGITS = 32; - char rev_digits[MAX_DIGITS]; - char digits[MAX_DIGITS]; +#define AVB_MAX_DIGITS_UINT64 32 + +/* Writes |value| to |digits| in base 10 followed by a NUL byte. + * Returns number of characters written excluding the NUL byte. + */ +static size_t uint64_to_base10(uint64_t value, + char digits[AVB_MAX_DIGITS_UINT64]) { + char rev_digits[AVB_MAX_DIGITS_UINT64]; size_t n, num_digits; - for (num_digits = 0; num_digits < MAX_DIGITS - 1;) { + for (num_digits = 0; num_digits < AVB_MAX_DIGITS_UINT64 - 1;) { rev_digits[num_digits++] = (value % 10) + '0'; value /= 10; if (value == 0) { @@ -631,19 +865,49 @@ static int cmdline_append_uint64_base10(AvbSlotVerifyData* slot_data, digits[n] = rev_digits[num_digits - 1 - n]; } digits[n] = '\0'; + return n; +} + +static int cmdline_append_version(AvbSlotVerifyData* slot_data, + const char* key, + uint64_t major_version, + uint64_t minor_version) { + char major_digits[AVB_MAX_DIGITS_UINT64]; + char minor_digits[AVB_MAX_DIGITS_UINT64]; + char combined[AVB_MAX_DIGITS_UINT64 * 2 + 1]; + size_t num_major_digits, num_minor_digits; + + num_major_digits = uint64_to_base10(major_version, major_digits); + num_minor_digits = uint64_to_base10(minor_version, minor_digits); + avb_memcpy(combined, major_digits, num_major_digits); + combined[num_major_digits] = '.'; + avb_memcpy(combined + num_major_digits + 1, minor_digits, num_minor_digits); + combined[num_major_digits + 1 + num_minor_digits] = '\0'; + + return cmdline_append_option(slot_data, key, combined); +} +static int cmdline_append_uint64_base10(AvbSlotVerifyData* slot_data, + const char* key, + uint64_t value) { + char digits[AVB_MAX_DIGITS_UINT64]; + uint64_to_base10(value, digits); return cmdline_append_option(slot_data, key, digits); } -static int cmdline_append_hex(AvbSlotVerifyData* slot_data, const char* key, - const uint8_t* data, size_t data_len) { +static int cmdline_append_hex(AvbSlotVerifyData* slot_data, + const char* key, + const uint8_t* data, + size_t data_len) { char hex_digits[17] = "0123456789abcdef"; char* hex_data; int ret; size_t n; hex_data = avb_malloc(data_len * 2 + 1); - if (hex_data == NULL) return 0; + if (hex_data == NULL) { + return 0; + } for (n = 0; n < data_len; n++) { hex_data[n * 2] = hex_digits[data[n] >> 4]; @@ -659,11 +923,13 @@ static int cmdline_append_hex(AvbSlotVerifyData* slot_data, const char* key, AvbSlotVerifyResult avb_slot_verify(AvbOps* ops, const char* const* requested_partitions, const char* ab_suffix, + bool allow_verification_error, AvbSlotVerifyData** out_data) { AvbSlotVerifyResult ret; AvbSlotVerifyData* slot_data = NULL; AvbAlgorithmType algorithm_type = AVB_ALGORITHM_TYPE_NONE; AvbIOResult io_ret; + bool using_boot_for_vbmeta = false; if (out_data != NULL) { *out_data = NULL; @@ -674,6 +940,12 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops, ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; goto fail; } + slot_data->vbmeta_images = + avb_calloc(sizeof(AvbVBMetaData) * MAX_NUMBER_OF_VBMETA_IMAGES); + if (slot_data->vbmeta_images == NULL) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto fail; + } slot_data->loaded_partitions = avb_calloc(sizeof(AvbPartitionData) * MAX_NUMBER_OF_LOADED_PARTITIONS); if (slot_data->loaded_partitions == NULL) { @@ -681,13 +953,30 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops, goto fail; } - ret = load_and_verify_vbmeta( - ops, requested_partitions, ab_suffix, 0 /* rollback_index_slot */, - "vbmeta", avb_strlen("vbmeta"), NULL /* expected_public_key */, - 0 /* expected_public_key_length */, slot_data, &algorithm_type); + ret = load_and_verify_vbmeta(ops, + requested_partitions, + ab_suffix, + allow_verification_error, + 0 /* toplevel_vbmeta_flags */, + 0 /* rollback_index_location */, + "vbmeta", + avb_strlen("vbmeta"), + NULL /* expected_public_key */, + 0 /* expected_public_key_length */, + slot_data, + &algorithm_type); + if (!allow_verification_error && ret != AVB_SLOT_VERIFY_RESULT_OK) { + goto fail; + } + + if (avb_strcmp(slot_data->vbmeta_images[0].partition_name, "vbmeta") != 0) { + avb_assert(avb_strcmp(slot_data->vbmeta_images[0].partition_name, "boot") == + 0); + using_boot_for_vbmeta = true; + } /* If things check out, mangle the kernel command-line as needed. */ - if (ret == AVB_SLOT_VERIFY_RESULT_OK) { + if (result_should_continue(ret)) { /* Fill in |ab_suffix| field. */ slot_data->ab_suffix = avb_strdup(ab_suffix); if (slot_data->ab_suffix == NULL) { @@ -695,9 +984,28 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops, goto fail; } + /* Add androidboot.vbmeta.device option. */ + if (!cmdline_append_option(slot_data, + "androidboot.vbmeta.device", + "PARTUUID=$(ANDROID_VBMETA_PARTUUID)")) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto fail; + } + + /* Add androidboot.vbmeta.avb_version option. */ + if (!cmdline_append_version(slot_data, + "androidboot.vbmeta.avb_version", + AVB_VERSION_MAJOR, + AVB_VERSION_MINOR)) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto fail; + } + /* Substitute $(ANDROID_SYSTEM_PARTUUID) and friends. */ if (slot_data->cmdline != NULL) { - char* new_cmdline = sub_cmdline(ops, slot_data->cmdline, ab_suffix); + char* new_cmdline; + new_cmdline = sub_cmdline( + ops, slot_data->cmdline, ab_suffix, using_boot_for_vbmeta); if (new_cmdline == NULL) { ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; goto fail; @@ -706,15 +1014,6 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops, slot_data->cmdline = new_cmdline; } - /* Add androidboot.slot_suffix, if applicable. */ - if (avb_strlen(ab_suffix) > 0) { - if (!cmdline_append_option(slot_data, "androidboot.slot_suffix", - ab_suffix)) { - ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; - goto fail; - } - } - /* Set androidboot.avb.device_state to "locked" or "unlocked". */ bool is_device_unlocked; io_ret = ops->read_is_device_unlocked(ops, &is_device_unlocked); @@ -726,7 +1025,8 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops, ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; goto fail; } - if (!cmdline_append_option(slot_data, "androidboot.vbmeta.device_state", + if (!cmdline_append_option(slot_data, + "androidboot.vbmeta.device_state", is_device_unlocked ? "unlocked" : "locked")) { ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; goto fail; @@ -742,13 +1042,20 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops, case AVB_ALGORITHM_TYPE_SHA256_RSA4096: case AVB_ALGORITHM_TYPE_SHA256_RSA8192: { AvbSHA256Ctx ctx; + size_t n, total_size = 0; avb_sha256_init(&ctx); - avb_sha256_update(&ctx, slot_data->vbmeta_data, slot_data->vbmeta_size); - if (!cmdline_append_option(slot_data, "androidboot.vbmeta.hash_alg", - "sha256") || - !cmdline_append_uint64_base10(slot_data, "androidboot.vbmeta.size", - slot_data->vbmeta_size) || - !cmdline_append_hex(slot_data, "androidboot.vbmeta.digest", + for (n = 0; n < slot_data->num_vbmeta_images; n++) { + avb_sha256_update(&ctx, + slot_data->vbmeta_images[n].vbmeta_data, + slot_data->vbmeta_images[n].vbmeta_size); + total_size += slot_data->vbmeta_images[n].vbmeta_size; + } + if (!cmdline_append_option( + slot_data, "androidboot.vbmeta.hash_alg", "sha256") || + !cmdline_append_uint64_base10( + slot_data, "androidboot.vbmeta.size", total_size) || + !cmdline_append_hex(slot_data, + "androidboot.vbmeta.digest", avb_sha256_final(&ctx), AVB_SHA256_DIGEST_SIZE)) { ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; @@ -760,13 +1067,20 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops, case AVB_ALGORITHM_TYPE_SHA512_RSA4096: case AVB_ALGORITHM_TYPE_SHA512_RSA8192: { AvbSHA512Ctx ctx; + size_t n, total_size = 0; avb_sha512_init(&ctx); - avb_sha512_update(&ctx, slot_data->vbmeta_data, slot_data->vbmeta_size); - if (!cmdline_append_option(slot_data, "androidboot.vbmeta.hash_alg", - "sha512") || - !cmdline_append_uint64_base10(slot_data, "androidboot.vbmeta.size", - slot_data->vbmeta_size) || - !cmdline_append_hex(slot_data, "androidboot.vbmeta.digest", + for (n = 0; n < slot_data->num_vbmeta_images; n++) { + avb_sha512_update(&ctx, + slot_data->vbmeta_images[n].vbmeta_data, + slot_data->vbmeta_images[n].vbmeta_size); + total_size += slot_data->vbmeta_images[n].vbmeta_size; + } + if (!cmdline_append_option( + slot_data, "androidboot.vbmeta.hash_alg", "sha512") || + !cmdline_append_uint64_base10( + slot_data, "androidboot.vbmeta.size", total_size) || + !cmdline_append_hex(slot_data, + "androidboot.vbmeta.digest", avb_sha512_final(&ctx), AVB_SHA512_DIGEST_SIZE)) { ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; @@ -785,6 +1099,10 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops, } } + if (!allow_verification_error) { + avb_assert(ret == AVB_SLOT_VERIFY_RESULT_OK); + } + return ret; fail: @@ -798,12 +1116,22 @@ void avb_slot_verify_data_free(AvbSlotVerifyData* data) { if (data->ab_suffix != NULL) { avb_free(data->ab_suffix); } - if (data->vbmeta_data != NULL) { - avb_free(data->vbmeta_data); - } if (data->cmdline != NULL) { avb_free(data->cmdline); } + if (data->vbmeta_images != NULL) { + size_t n; + for (n = 0; n < data->num_vbmeta_images; n++) { + AvbVBMetaData* vbmeta_image = &data->vbmeta_images[n]; + if (vbmeta_image->partition_name != NULL) { + avb_free(vbmeta_image->partition_name); + } + if (vbmeta_image->vbmeta_data != NULL) { + avb_free(vbmeta_image->vbmeta_data); + } + } + avb_free(data->vbmeta_images); + } if (data->loaded_partitions != NULL) { size_t n; for (n = 0; n < data->num_loaded_partitions; n++) { @@ -845,6 +1173,9 @@ const char* avb_slot_verify_result_to_string(AvbSlotVerifyResult result) { case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA: ret = "ERROR_INVALID_METADATA"; break; + case AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION: + ret = "ERROR_UNSUPPORTED_VERSION"; + break; /* Do not add a 'default:' case here because of -Wswitch. */ } |