diff --git a/Android.bp b/Android.bp index 3e14003..48480d1 100644 --- a/Android.bp +++ b/Android.bp @@ -114,6 +114,7 @@ cc_library_static { "libavb_ab/avb_ab_flow.c", "libavb_user/avb_ops_user.c", "libavb_user/avb_user_verity.c", + "libavb_user/avb_user_verification.c", ], } diff --git a/libavb/avb_slot_verify.c b/libavb/avb_slot_verify.c index 7ba4075..f7b74a6 100644 --- a/libavb/avb_slot_verify.c +++ b/libavb/avb_slot_verify.c @@ -244,6 +244,99 @@ static AvbSlotVerifyResult load_and_verify_hash_partition( return ret; } +static AvbSlotVerifyResult load_requested_partitions( + AvbOps* ops, + const char* const* requested_partitions, + const char* ab_suffix, + AvbSlotVerifyData* slot_data) { + AvbSlotVerifyResult ret; + uint8_t* image_buf = NULL; + size_t n; + + if (ops->get_size_of_partition == NULL) { + avb_error("get_size_of_partition() not implemented.\n"); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT; + goto out; + } + + for (n = 0; requested_partitions[n] != NULL; n++) { + char part_name[PART_NAME_MAX_SIZE]; + AvbIOResult io_ret; + uint64_t image_size; + size_t part_num_read; + AvbPartitionData* loaded_partition; + + if (!avb_str_concat(part_name, + sizeof part_name, + requested_partitions[n], + avb_strlen(requested_partitions[n]), + 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; + } + + io_ret = ops->get_size_of_partition(ops, part_name, &image_size); + 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(part_name, ": Error determining partition size.\n", NULL); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; + goto out; + } + avb_debugv(part_name, ": Loading entire partition.\n", NULL); + + image_buf = avb_malloc(image_size); + if (image_buf == NULL) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } + + io_ret = ops->read_from_partition( + ops, part_name, 0 /* offset */, image_size, image_buf, &part_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(part_name, ": Error loading data from partition.\n", NULL); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; + goto out; + } + if (part_num_read != image_size) { + avb_errorv(part_name, ": Read fewer than requested bytes.\n", NULL); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; + goto out; + } + + /* Move to slot_data. */ + 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; + } + loaded_partition = + &slot_data->loaded_partitions[slot_data->num_loaded_partitions++]; + loaded_partition->partition_name = avb_strdup(requested_partitions[n]); + if (loaded_partition->partition_name == NULL) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } + loaded_partition->data_size = image_size; + loaded_partition->data = image_buf; + image_buf = NULL; + } + + ret = AVB_SLOT_VERIFY_RESULT_OK; + +out: + if (image_buf != NULL) { + avb_free(image_buf); + } + return ret; +} + static AvbSlotVerifyResult load_and_verify_vbmeta( AvbOps* ops, const char* const* requested_partitions, @@ -561,6 +654,27 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( vbmeta_header.auxiliary_data_block_size; vbmeta_image_data->verify_result = vbmeta_ret; + /* If verification has been disabled by setting a bit in the image, + * we're done... except that we need to load the entirety of the + * requested partitions. + */ + if (vbmeta_header.flags & AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED) { + AvbSlotVerifyResult sub_ret; + avb_debugv( + full_partition_name, ": VERIFICATION_DISABLED bit is set.\n", NULL); + /* If load_requested_partitions() fail it is always a fatal + * failure (e.g. ERROR_INVALID_ARGUMENT, ERROR_OOM, etc.) rather + * than recoverable (e.g. one where result_should_continue() + * returns true) and we want to convey that error. + */ + sub_ret = load_requested_partitions( + ops, requested_partitions, ab_suffix, slot_data); + if (sub_ret != AVB_SLOT_VERIFY_RESULT_OK) { + ret = sub_ret; + } + goto out; + } + /* Now go through all descriptors and take the appropriate action: * * - hash descriptor: Load data from partition, calculate hash, and @@ -954,6 +1068,168 @@ static int cmdline_append_hex(AvbSlotVerifyData* slot_data, return ret; } +static AvbSlotVerifyResult append_options( + AvbOps* ops, + AvbSlotVerifyData* slot_data, + AvbVBMetaImageHeader* toplevel_vbmeta, + AvbAlgorithmType algorithm_type, + AvbHashtreeErrorMode hashtree_error_mode) { + AvbSlotVerifyResult ret; + const char* verity_mode; + bool is_device_unlocked; + AvbIOResult io_ret; + + /* 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 out; + } + + /* 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 out; + } + + /* Set androidboot.avb.device_state to "locked" or "unlocked". */ + io_ret = ops->read_is_device_unlocked(ops, &is_device_unlocked); + 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_error("Error getting device state.\n"); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; + goto out; + } + if (!cmdline_append_option(slot_data, + "androidboot.vbmeta.device_state", + is_device_unlocked ? "unlocked" : "locked")) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } + + /* Set androidboot.vbmeta.{hash_alg, size, digest} - use same hash + * function as is used to sign vbmeta. + */ + switch (algorithm_type) { + /* Explicit fallthrough. */ + case AVB_ALGORITHM_TYPE_NONE: + case AVB_ALGORITHM_TYPE_SHA256_RSA2048: + case AVB_ALGORITHM_TYPE_SHA256_RSA4096: + case AVB_ALGORITHM_TYPE_SHA256_RSA8192: { + AvbSHA256Ctx ctx; + size_t n, total_size = 0; + avb_sha256_init(&ctx); + 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; + goto out; + } + } break; + /* Explicit fallthrough. */ + case AVB_ALGORITHM_TYPE_SHA512_RSA2048: + case AVB_ALGORITHM_TYPE_SHA512_RSA4096: + case AVB_ALGORITHM_TYPE_SHA512_RSA8192: { + AvbSHA512Ctx ctx; + size_t n, total_size = 0; + avb_sha512_init(&ctx); + 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; + goto out; + } + } break; + case _AVB_ALGORITHM_NUM_TYPES: + avb_assert_not_reached(); + break; + } + + /* Set androidboot.veritymode and androidboot.vbmeta.invalidate_on_error */ + if (toplevel_vbmeta->flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED) { + verity_mode = "disabled"; + } else { + const char* dm_verity_mode; + char* new_ret; + + switch (hashtree_error_mode) { + case AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE: + if (!cmdline_append_option( + slot_data, "androidboot.vbmeta.invalidate_on_error", "yes")) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } + verity_mode = "enforcing"; + dm_verity_mode = "restart_on_corruption"; + break; + case AVB_HASHTREE_ERROR_MODE_RESTART: + verity_mode = "enforcing"; + dm_verity_mode = "restart_on_corruption"; + break; + case AVB_HASHTREE_ERROR_MODE_EIO: + verity_mode = "eio"; + /* For now there's no option to specify the EIO mode. So + * just use 'ignore_zero_blocks' since that's already set + * and dm-verity-target.c supports specifying this multiple + * times. + */ + dm_verity_mode = "ignore_zero_blocks"; + break; + case AVB_HASHTREE_ERROR_MODE_LOGGING: + verity_mode = "logging"; + dm_verity_mode = "ignore_corruption"; + break; + } + new_ret = avb_replace( + slot_data->cmdline, "$(ANDROID_VERITY_MODE)", dm_verity_mode); + avb_free(slot_data->cmdline); + slot_data->cmdline = new_ret; + if (slot_data->cmdline == NULL) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } + } + if (!cmdline_append_option( + slot_data, "androidboot.veritymode", verity_mode)) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } + + ret = AVB_SLOT_VERIFY_RESULT_OK; + +out: + + return ret; +} + AvbSlotVerifyResult avb_slot_verify(AvbOps* ops, const char* const* requested_partitions, const char* ab_suffix, @@ -963,10 +1239,8 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops, AvbSlotVerifyResult ret; AvbSlotVerifyData* slot_data = NULL; AvbAlgorithmType algorithm_type = AVB_ALGORITHM_TYPE_NONE; - AvbIOResult io_ret; bool using_boot_for_vbmeta = false; AvbVBMetaImageHeader toplevel_vbmeta; - const char* verity_mode; bool allow_verification_error = (flags & AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR); @@ -1050,21 +1324,35 @@ 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; + /* If verification is disabled, we are done ... we specifically + * don't want to add any androidboot.* options since verification + * is disabled. + */ + if (toplevel_vbmeta.flags & AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED) { + /* Since verification is disabled we didn't process any + * descriptors and thus there's no cmdline... so set root= such + * that the system partition is mounted. + */ + avb_assert(slot_data->cmdline == NULL); + slot_data->cmdline = + avb_strdup("root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)"); + if (slot_data->cmdline == NULL) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto fail; + } + } else { + /* Add options - any failure in append_options() is either an + * I/O or OOM error. + */ + AvbSlotVerifyResult sub_ret = append_options(ops, + slot_data, + &toplevel_vbmeta, + algorithm_type, + hashtree_error_mode); + if (sub_ret != AVB_SLOT_VERIFY_RESULT_OK) { + ret = sub_ret; + goto fail; + } } /* Substitute $(ANDROID_SYSTEM_PARTUUID) and friends. */ @@ -1080,133 +1368,6 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops, slot_data->cmdline = new_cmdline; } - /* Set androidboot.avb.device_state to "locked" or "unlocked". */ - bool is_device_unlocked; - io_ret = ops->read_is_device_unlocked(ops, &is_device_unlocked); - if (io_ret == AVB_IO_RESULT_ERROR_OOM) { - ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; - goto fail; - } else if (io_ret != AVB_IO_RESULT_OK) { - avb_error("Error getting device state.\n"); - ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; - goto fail; - } - if (!cmdline_append_option(slot_data, - "androidboot.vbmeta.device_state", - is_device_unlocked ? "unlocked" : "locked")) { - ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; - goto fail; - } - - /* Set androidboot.vbmeta.{hash_alg, size, digest} - use same hash - * function as is used to sign vbmeta. - */ - switch (algorithm_type) { - /* Explicit fallthrough. */ - case AVB_ALGORITHM_TYPE_NONE: - case AVB_ALGORITHM_TYPE_SHA256_RSA2048: - case AVB_ALGORITHM_TYPE_SHA256_RSA4096: - case AVB_ALGORITHM_TYPE_SHA256_RSA8192: { - AvbSHA256Ctx ctx; - size_t n, total_size = 0; - avb_sha256_init(&ctx); - 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; - goto fail; - } - } break; - /* Explicit fallthrough. */ - case AVB_ALGORITHM_TYPE_SHA512_RSA2048: - case AVB_ALGORITHM_TYPE_SHA512_RSA4096: - case AVB_ALGORITHM_TYPE_SHA512_RSA8192: { - AvbSHA512Ctx ctx; - size_t n, total_size = 0; - avb_sha512_init(&ctx); - 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; - goto fail; - } - } break; - case _AVB_ALGORITHM_NUM_TYPES: - avb_assert_not_reached(); - break; - } - - /* Set androidboot.veritymode and androidboot.vbmeta.invalidate_on_error */ - if (toplevel_vbmeta.flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED) { - verity_mode = "disabled"; - } else { - const char* dm_verity_mode; - char* new_ret; - - switch (hashtree_error_mode) { - case AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE: - if (!cmdline_append_option( - slot_data, "androidboot.vbmeta.invalidate_on_error", "yes")) { - ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; - goto fail; - } - verity_mode = "enforcing"; - dm_verity_mode = "restart_on_corruption"; - break; - case AVB_HASHTREE_ERROR_MODE_RESTART: - verity_mode = "enforcing"; - dm_verity_mode = "restart_on_corruption"; - break; - case AVB_HASHTREE_ERROR_MODE_EIO: - verity_mode = "eio"; - /* For now there's no option to specify the EIO mode. So - * just use 'ignore_zero_blocks' since that's already set - * and dm-verity-target.c supports specifying this multiple - * times. - */ - dm_verity_mode = "ignore_zero_blocks"; - break; - case AVB_HASHTREE_ERROR_MODE_LOGGING: - verity_mode = "logging"; - dm_verity_mode = "ignore_corruption"; - break; - } - new_ret = avb_replace( - slot_data->cmdline, "$(ANDROID_VERITY_MODE)", dm_verity_mode); - avb_free(slot_data->cmdline); - slot_data->cmdline = new_ret; - if (slot_data->cmdline == NULL) { - goto fail; - } - } - if (!cmdline_append_option( - slot_data, "androidboot.veritymode", verity_mode)) { - ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; - goto fail; - } - if (out_data != NULL) { *out_data = slot_data; } else { diff --git a/libavb/avb_slot_verify.h b/libavb/avb_slot_verify.h index 8794b98..d8de8fb 100644 --- a/libavb/avb_slot_verify.h +++ b/libavb/avb_slot_verify.h @@ -192,7 +192,8 @@ typedef struct { * option depending on the value of |hashtree_error_mode|. * * Additionally, the |cmdline| field will have the following kernel - * command-line options set: + * command-line options set (unless verification is disabled, see + * below): * * androidboot.veritymode: This is set to 'disabled' if the * AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED flag is set in top-level @@ -227,14 +228,20 @@ typedef struct { * necessarily the same version number of the on-disk metadata for * the slot that was verified. * - * Note that neither androidboot.slot_suffix nor androidboot.slot are - * set in the |cmdline| field in |AvbSlotVerifyData| - you will have - * to pass these yourself. - * - * Also note that androidboot.veritymode is set by libavb and since - * AVB only supports 'enforcing' and 'disabled' values, the boot - * loader is relieved of managing any state related to dm-verity or - * setting this cmdline parameter. + * Note that androidboot.slot_suffix is not set in the |cmdline| field + * in |AvbSlotVerifyData| - you will have to set this yourself. + * + * If the |AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED| flag is set + * in the top-level vbmeta struct then only the top-level vbmeta + * struct is verified and descriptors will not processed. The return + * value will be set accordingly (if this flag is set via 'avbctl + * disable-verification' then the return value will be + * |AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION|) and + * |AvbSlotVerifyData| is returned. Additionally all partitions in the + * |requested_partitions| are loaded and the |cmdline| field is set to + * "root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)" and the GUID for the + * appropriate system partition is substituted in. Note that none of + * the androidboot.* options mentioned above will be set. * * This struct may grow in the future without it being considered an * ABI break. diff --git a/libavb/avb_vbmeta_image.h b/libavb/avb_vbmeta_image.h index 90c3a54..d0c9f15 100644 --- a/libavb/avb_vbmeta_image.h +++ b/libavb/avb_vbmeta_image.h @@ -52,9 +52,13 @@ extern "C" { * * AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED: If this flag is set, * hashtree image verification will be disabled. + * + * AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED: If this flag is set, + * verification will be disabled and descriptors will not be parsed. */ typedef enum { - AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = (1 << 0) + AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = (1 << 0), + AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED = (1 << 1) } AvbVBMetaImageFlags; /* Binary format for header of the vbmeta image. diff --git a/libavb_ab/avb_ab_flow.c b/libavb_ab/avb_ab_flow.c index e0661fa..bf6eab1 100644 --- a/libavb_ab/avb_ab_flow.c +++ b/libavb_ab/avb_ab_flow.c @@ -515,6 +515,7 @@ const char* avb_ab_flow_result_to_string(AvbABFlowResult result) { case AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS: ret = "ERROR_NO_BOOTABLE_SLOTS"; break; + case AVB_AB_FLOW_RESULT_ERROR_INVALID_ARGUMENT: ret = "ERROR_INVALID_ARGUMENT"; break; diff --git a/libavb_user/avb_user_verification.c b/libavb_user/avb_user_verification.c new file mode 100644 index 0000000..f572128 --- /dev/null +++ b/libavb_user/avb_user_verification.c @@ -0,0 +1,214 @@ +/* + * 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_verification.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_verification_get(AvbOps* ops, + const char* ab_suffix, + bool* out_verification_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 VERIFICATION_DISABLED bit, as requested. */ + header = (AvbVBMetaImageHeader*)vbmeta_image; + flags = avb_be32toh(header->flags); + + if (out_verification_enabled != NULL) { + *out_verification_enabled = + !(flags & AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED); + } + + ret = true; + +out: + return ret; +} + +bool avb_user_verification_set(AvbOps* ops, + const char* ab_suffix, + bool enable_verification) { + 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 VERIFICATION_DISABLED bit, as requested. */ + header = (AvbVBMetaImageHeader*)vbmeta_image; + flags = avb_be32toh(header->flags); + flags &= ~AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED; + if (!enable_verification) { + flags |= AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_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; +} diff --git a/libavb_user/avb_user_verification.h b/libavb_user/avb_user_verification.h new file mode 100644 index 0000000..7a33c8d --- /dev/null +++ b/libavb_user/avb_user_verification.h @@ -0,0 +1,63 @@ +/* + * 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. + */ + +#ifndef AVB_USER_VERIFICATION_H_ +#define AVB_USER_VERIFICATION_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Function to enable or disable verification for an entire slot. The + * passed in |ops| should be obtained via avb_ops_user_new(). The + * |ab_suffix| parameter should specify the slot to modify including + * the leading underscore (e.g. "_a" or "_b"). The + * |enable_verification| parameter should be set to |true| to enable + * dm-verification and |false| to disable. + * + * Returns |true| if the operation succeeded, otherwise |false|. + */ +bool avb_user_verification_set(AvbOps* ops, + const char* ab_suffix, + bool enable_verification); + +/* Gets whether verification is enabled for an entire slot. The passed + * in |ops| should be obtained via avb_ops_user_new(). The |ab_suffix| + * parameter should specify the slot to query including the leading + * underscore (e.g. "_a" or "_b"). The result is returned in the + * |out_verification_enabled| parameter. + * + * Returns |true| if the operation succeeded, otherwise |false|. + */ +bool avb_user_verification_get(AvbOps* ops, + const char* ab_suffix, + bool* out_verification_enabled); + +#ifdef __cplusplus +} +#endif + +#endif /* AVB_USER_VERIFICATION_H_ */ diff --git a/libavb_user/avb_user_verity.c b/libavb_user/avb_user_verity.c index 8098909..ecf0043 100644 --- a/libavb_user/avb_user_verity.c +++ b/libavb_user/avb_user_verity.c @@ -37,7 +37,7 @@ * from is returned in |out_partition_name| and the offset on said * partition is returned in |out_vbmeta_offset|. */ -bool load_top_level_vbmeta_header( +static bool load_top_level_vbmeta_header( AvbOps* ops, const char* ab_suffix, uint8_t vbmeta_image[AVB_VBMETA_IMAGE_HEADER_SIZE], diff --git a/libavb_user/libavb_user.h b/libavb_user/libavb_user.h index b9d10ab..a8b8c55 100644 --- a/libavb_user/libavb_user.h +++ b/libavb_user/libavb_user.h @@ -34,6 +34,7 @@ #define AVB_INSIDE_LIBAVB_USER_H #include "avb_ops_user.h" +#include "avb_user_verification.h" #include "avb_user_verity.h" #undef AVB_INSIDE_LIBAVB_USER_H diff --git a/test/avb_slot_verify_unittest.cc b/test/avb_slot_verify_unittest.cc index 8348bf2..88b87f4 100644 --- a/test/avb_slot_verify_unittest.cc +++ b/test/avb_slot_verify_unittest.cc @@ -46,6 +46,7 @@ class AvbSlotVerifyTest : public BaseAvbToolTest { void CmdlineWithHashtreeVerification(bool hashtree_verification_on); void CmdlineWithChainedHashtreeVerification(bool hashtree_verification_on); + void VerificationDisabled(bool use_avbctl); FakeAvbOps ops_; }; @@ -1677,6 +1678,154 @@ TEST_F(AvbSlotVerifyTest, CmdlineWithChainedHashtreeVerificationOn) { CmdlineWithChainedHashtreeVerification(true); } +void AvbSlotVerifyTest::VerificationDisabled(bool use_avbctl) { + const size_t boot_part_size = 32 * 1024 * 1024; + const size_t dtbo_part_size = 4 * 1024 * 1024; + const size_t rootfs_size = 1028 * 1024; + const size_t partition_size = 1536 * 1024; + + // Generate boot_a.img and dtbo_a.img since avb_slot_verify() will + // attempt to load them upon encountering the VERIFICATION_DISABLED + // flag. + base::FilePath boot_path = GenerateImage("boot_a.img", boot_part_size); + const size_t DTBO_DATA_OFFSET = 42; + base::FilePath dtbo_path = + GenerateImage("dtbo_a.img", dtbo_part_size, DTBO_DATA_OFFSET); + + // Generate a 1028 KiB file with known content. + std::vector rootfs; + rootfs.resize(rootfs_size); + for (size_t n = 0; n < rootfs_size; n++) + rootfs[n] = uint8_t(n); + base::FilePath rootfs_path = testdir_.Append("rootfs.bin"); + EXPECT_EQ(rootfs_size, + static_cast( + base::WriteFile(rootfs_path, + reinterpret_cast(rootfs.data()), + rootfs.size()))); + + EXPECT_COMMAND(0, + "./avbtool add_hashtree_footer --salt d00df00d --image %s " + "--partition_size %d --partition_name foobar " + "--algorithm SHA256_RSA2048 " + "--key test/data/testkey_rsa2048.pem " + "--internal_release_string \"\" " + "--do_not_generate_fec", + rootfs_path.value().c_str(), + (int)partition_size); + + // Check that we correctly generate dm-verity kernel cmdline + // snippets, if requested. + GenerateVBMetaImage( + "vbmeta_a.img", + "SHA256_RSA2048", + 4, + base::FilePath("test/data/testkey_rsa2048.pem"), + base::StringPrintf( + "--setup_rootfs_from_kernel %s " + "--kernel_cmdline should_be_in_both=1 " + "--algorithm SHA256_RSA2048 " + "--flags %d " + "--internal_release_string \"\"", + rootfs_path.value().c_str(), + use_avbctl ? 0 : AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED)); + + EXPECT_EQ( + base::StringPrintf( + "Minimum libavb version: 1.0\n" + "Header Block: 256 bytes\n" + "Authentication Block: 320 bytes\n" + "Auxiliary Block: 960 bytes\n" + "Algorithm: SHA256_RSA2048\n" + "Rollback Index: 4\n" + "Flags: %d\n" + "Release String: ''\n" + "Descriptors:\n" + " Kernel Cmdline descriptor:\n" + " Flags: 1\n" + " Kernel Cmdline: 'dm=\"1 vroot none ro 1,0 2056 verity " + "1 PARTUUID=$(ANDROID_SYSTEM_PARTUUID) " + "PARTUUID=$(ANDROID_SYSTEM_PARTUUID) 4096 4096 257 257 sha1 " + "e811611467dcd6e8dc4324e45f706c2bdd51db67 d00df00d 2 " + "$(ANDROID_VERITY_MODE) ignore_zero_blocks\" root=/dev/dm-0'\n" + " Kernel Cmdline descriptor:\n" + " Flags: 2\n" + " Kernel Cmdline: " + "'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'\n" + " Kernel Cmdline descriptor:\n" + " Flags: 0\n" + " Kernel Cmdline: 'should_be_in_both=1'\n", + use_avbctl ? 0 : AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED), + InfoImage(vbmeta_image_path_)); + + ops_.set_expected_public_key( + PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem"))); + + // Manually set the flag the same way 'avbctl disable-verification' + // would do it. + if (use_avbctl) { + uint32_t flags_data; + flags_data = avb_htobe32(AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED); + EXPECT_EQ(AVB_IO_RESULT_OK, + ops_.avb_ops()->write_to_partition( + ops_.avb_ops(), + "vbmeta_a", + offsetof(AvbVBMetaImageHeader, flags), + sizeof flags_data, + &flags_data)); + } + + // Check that avb_slot_verify() doesn't return any of the + // descriptors and instead return a kernel command-line with + // root=PARTUUID= and none of the + // androidboot.vbmeta.* options are set. Also ensure all the + // requested partitions are loaded. + // + // Also if we modified via avbctl we should expect + // ERROR_VERIFICATION instead of OK. + // + AvbSlotVerifyResult expected_result = AVB_SLOT_VERIFY_RESULT_OK; + if (use_avbctl) { + expected_result = AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION; + } + AvbSlotVerifyData* slot_data = NULL; + const char* requested_partitions[] = {"boot", "dtbo", NULL}; + EXPECT_EQ(expected_result, + avb_slot_verify(ops_.avb_ops(), + requested_partitions, + "_a", + AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR, + AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, + &slot_data)); + EXPECT_NE(nullptr, slot_data); + EXPECT_EQ("root=PARTUUID=1234-fake-guid-for:system_a", + std::string(slot_data->cmdline)); + // Also make sure that it actually loads the boot and dtbo partitions. + EXPECT_EQ(size_t(2), slot_data->num_loaded_partitions); + EXPECT_EQ("boot", + std::string(slot_data->loaded_partitions[0].partition_name)); + EXPECT_EQ(boot_part_size, slot_data->loaded_partitions[0].data_size); + for (size_t n = 0; n < boot_part_size; n++) { + EXPECT_EQ(uint8_t(n), slot_data->loaded_partitions[0].data[n]); + } + EXPECT_EQ("dtbo", + std::string(slot_data->loaded_partitions[1].partition_name)); + EXPECT_EQ(dtbo_part_size, slot_data->loaded_partitions[1].data_size); + for (size_t n = 0; n < dtbo_part_size; n++) { + EXPECT_EQ(uint8_t(n + DTBO_DATA_OFFSET), + slot_data->loaded_partitions[1].data[n]); + } + avb_slot_verify_data_free(slot_data); +} + +TEST_F(AvbSlotVerifyTest, VerificationDisabledUnmodified) { + VerificationDisabled(false); // use_avbctl +} + +TEST_F(AvbSlotVerifyTest, VerificationDisabledModified) { + VerificationDisabled(true); // use_avbctl +} + // In the event that there's no vbmeta partition, we treat the vbmeta // struct from 'boot' as the top-level partition. Check that this // works. diff --git a/tools/avbctl/avbctl.cc b/tools/avbctl/avbctl.cc index 9d83f0b..d0922b3 100644 --- a/tools/avbctl/avbctl.cc +++ b/tools/avbctl/avbctl.cc @@ -41,10 +41,17 @@ void usage(FILE* where, int /* argc */, char* argv[]) { " %s COMMAND\n" "\n" "Commands:\n" - " %s get-verity - Prints whether verity is enabled in " + " %s get-verity - Prints whether verity is enabled in " "current slot.\n" - " %s disable-verity - Disable verity in current slot.\n" - " %s enable-verity - Enable verity in current slot.\n", + " %s disable-verity - Disable verity in current slot.\n" + " %s enable-verity - Enable verity in current slot.\n" + " %s get-verification - Prints whether verification is enabled " + "in current slot.\n" + " %s disable-verification - Disable verification in current slot.\n" + " %s enable-verification - Enable verification in current slot.\n", + argv[0], + argv[0], + argv[0], argv[0], argv[0], argv[0], @@ -52,6 +59,71 @@ void usage(FILE* where, int /* argc */, char* argv[]) { argv[0]); } +/* Function to enable and disable verification. The |ops| parameter + * should be an |AvbOps| from libavb_user. + */ +int do_set_verification(AvbOps* ops, + const std::string& ab_suffix, + bool enable_verification) { + bool verification_enabled; + + if (!avb_user_verification_get( + ops, ab_suffix.c_str(), &verification_enabled)) { + fprintf(stderr, "Error getting whether verification is enabled.\n"); + return EX_SOFTWARE; + } + + if ((verification_enabled && enable_verification) || + (!verification_enabled && !enable_verification)) { + fprintf(stdout, + "verification is already %s", + verification_enabled ? "enabled" : "disabled"); + if (ab_suffix != "") { + fprintf(stdout, " on slot with suffix %s", ab_suffix.c_str()); + } + fprintf(stdout, ".\n"); + return EX_OK; + } + + if (!avb_user_verification_set(ops, ab_suffix.c_str(), enable_verification)) { + fprintf(stderr, "Error setting verification.\n"); + return EX_SOFTWARE; + } + + fprintf(stdout, + "Successfully %s verification", + enable_verification ? "enabled" : "disabled"); + if (ab_suffix != "") { + fprintf(stdout, " on slot with suffix %s", ab_suffix.c_str()); + } + fprintf(stdout, ". Reboot the device for changes to take effect.\n"); + + return EX_OK; +} + +/* Function to query if verification. The |ops| parameter should be an + * |AvbOps| from libavb_user. + */ +int do_get_verification(AvbOps* ops, const std::string& ab_suffix) { + bool verification_enabled; + + if (!avb_user_verification_get( + ops, ab_suffix.c_str(), &verification_enabled)) { + fprintf(stderr, "Error getting whether verification is enabled.\n"); + return EX_SOFTWARE; + } + + fprintf(stdout, + "verification is %s", + verification_enabled ? "enabled" : "disabled"); + if (ab_suffix != "") { + fprintf(stdout, " on slot with suffix %s", ab_suffix.c_str()); + } + fprintf(stdout, ".\n"); + + return EX_OK; +} + /* Function to enable and disable dm-verity. The |ops| parameter * should be an |AvbOps| from libavb_user. */ @@ -158,6 +230,12 @@ int main(int argc, char* argv[]) { ret = do_set_verity(ops, ab_suffix, true); } else if (strcmp(argv[1], "get-verity") == 0) { ret = do_get_verity(ops, ab_suffix); + } else if (strcmp(argv[1], "disable-verification") == 0) { + ret = do_set_verification(ops, ab_suffix, false); + } else if (strcmp(argv[1], "enable-verification") == 0) { + ret = do_set_verification(ops, ab_suffix, true); + } else if (strcmp(argv[1], "get-verification") == 0) { + ret = do_get_verification(ops, ab_suffix); } else { usage(stderr, argc, argv); ret = EX_USAGE;