diff options
Diffstat (limited to 'lib/libavb/avb_slot_verify.c')
-rw-r--r-- | lib/libavb/avb_slot_verify.c | 652 |
1 files changed, 505 insertions, 147 deletions
diff --git a/lib/libavb/avb_slot_verify.c b/lib/libavb/avb_slot_verify.c index a941850d93..5d400b38aa 100644 --- a/lib/libavb/avb_slot_verify.c +++ b/lib/libavb/avb_slot_verify.c @@ -24,6 +24,14 @@ /* Maximum size of a vbmeta image - 64 KiB. */ #define VBMETA_MAX_SIZE (64 * 1024) +static AvbSlotVerifyResult initialize_persistent_digest( + AvbOps* ops, + const char* part_name, + const char* persistent_value_name, + size_t digest_size, + const uint8_t* initial_digest, + uint8_t* out_digest); + /* 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. @@ -114,9 +122,26 @@ static AvbSlotVerifyResult load_full_partition(AvbOps* ops, return AVB_SLOT_VERIFY_RESULT_OK; } +/* Reads a persistent digest stored as a named persistent value corresponding to + * the given |part_name|. The value is returned in |out_digest| which must point + * to |expected_digest_size| bytes. If there is no digest stored for |part_name| + * it can be initialized by providing a non-NULL |initial_digest| of length + * |expected_digest_size|. This automatic initialization will only occur if the + * device is currently locked. The |initial_digest| may be NULL. + * + * Returns AVB_SLOT_VERIFY_RESULT_OK on success, otherwise returns an + * AVB_SLOT_VERIFY_RESULT_ERROR_* error code. + * + * If the value does not exist, is not supported, or is not populated, and + * |initial_digest| is NULL, returns + * AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA. If |expected_digest_size| does + * not match the stored digest size, also returns + * AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA. + */ static AvbSlotVerifyResult read_persistent_digest(AvbOps* ops, const char* part_name, size_t expected_digest_size, + const uint8_t* initial_digest, uint8_t* out_digest) { char* persistent_value_name = NULL; AvbIOResult io_ret = AVB_IO_RESULT_OK; @@ -131,30 +156,106 @@ static AvbSlotVerifyResult read_persistent_digest(AvbOps* ops, if (persistent_value_name == NULL) { return AVB_SLOT_VERIFY_RESULT_ERROR_OOM; } + io_ret = ops->read_persistent_value(ops, persistent_value_name, expected_digest_size, out_digest, &stored_digest_size); + + // If no such named persistent value exists and an initial digest value was + // given, initialize the named persistent value with the given digest. If + // initialized successfully, this will recurse into this function but with a + // NULL initial_digest. + if (io_ret == AVB_IO_RESULT_ERROR_NO_SUCH_VALUE && initial_digest) { + AvbSlotVerifyResult ret = + initialize_persistent_digest(ops, + part_name, + persistent_value_name, + expected_digest_size, + initial_digest, + out_digest); + avb_free(persistent_value_name); + return ret; + } avb_free(persistent_value_name); + if (io_ret == AVB_IO_RESULT_ERROR_OOM) { return AVB_SLOT_VERIFY_RESULT_ERROR_OOM; } else if (io_ret == AVB_IO_RESULT_ERROR_NO_SUCH_VALUE) { + // Treat a missing persistent value as a verification error, which is + // ignoreable, rather than a metadata error which is not. avb_errorv(part_name, ": Persistent digest does not exist.\n", NULL); - return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; + return AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION; } else if (io_ret == AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE || - io_ret == AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE || - expected_digest_size != stored_digest_size) { + io_ret == AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE) { avb_errorv( part_name, ": Persistent digest is not of expected size.\n", NULL); return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; } else if (io_ret != AVB_IO_RESULT_OK) { avb_errorv(part_name, ": Error reading persistent digest.\n", NULL); return AVB_SLOT_VERIFY_RESULT_ERROR_IO; + } else if (expected_digest_size != stored_digest_size) { + avb_errorv( + part_name, ": Persistent digest is not of expected size.\n", NULL); + return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; } return AVB_SLOT_VERIFY_RESULT_OK; } +static AvbSlotVerifyResult initialize_persistent_digest( + AvbOps* ops, + const char* part_name, + const char* persistent_value_name, + size_t digest_size, + const uint8_t* initial_digest, + uint8_t* out_digest) { + AvbSlotVerifyResult ret; + AvbIOResult io_ret = AVB_IO_RESULT_OK; + bool is_device_unlocked = true; + + io_ret = ops->read_is_device_unlocked(ops, &is_device_unlocked); + if (io_ret == AVB_IO_RESULT_ERROR_OOM) { + return AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + } else if (io_ret != AVB_IO_RESULT_OK) { + avb_error("Error getting device lock state.\n"); + return AVB_SLOT_VERIFY_RESULT_ERROR_IO; + } + + if (is_device_unlocked) { + avb_debugv(part_name, + ": Digest does not exist, device unlocked so not initializing " + "digest.\n", + NULL); + return AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION; + } + + // Device locked; initialize digest with given initial value. + avb_debugv(part_name, + ": Digest does not exist, initializing persistent digest.\n", + NULL); + io_ret = ops->write_persistent_value( + ops, persistent_value_name, digest_size, initial_digest); + if (io_ret == AVB_IO_RESULT_ERROR_OOM) { + return AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + } else if (io_ret != AVB_IO_RESULT_OK) { + avb_errorv(part_name, ": Error initializing persistent digest.\n", NULL); + return AVB_SLOT_VERIFY_RESULT_ERROR_IO; + } + + // To ensure that the digest value was written successfully - and avoid a + // scenario where the digest is simply 'initialized' on every verify - recurse + // into read_persistent_digest to read back the written value. The NULL + // initial_digest ensures that this will not recurse again. + ret = read_persistent_digest(ops, part_name, digest_size, NULL, out_digest); + if (ret != AVB_SLOT_VERIFY_RESULT_OK) { + avb_errorv(part_name, + ": Reading back initialized persistent digest failed!\n", + NULL); + } + return ret; +} + static AvbSlotVerifyResult load_and_verify_hash_partition( AvbOps* ops, const char* const* requested_partitions, @@ -248,24 +349,16 @@ static AvbSlotVerifyResult load_and_verify_hash_partition( */ image_size = hash_desc.image_size; if (allow_verification_error) { - if (ops->get_size_of_partition == NULL) { - avb_errorv(part_name, - ": The get_size_of_partition() operation is " - "not implemented so we may not load the entire partition. " - "Please implement.", - NULL); - } else { - 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); + 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); } ret = load_full_partition( @@ -273,19 +366,27 @@ static AvbSlotVerifyResult load_and_verify_hash_partition( if (ret != AVB_SLOT_VERIFY_RESULT_OK) { goto out; } - + // Although only one of the type might be used, we have to defined the + // structure here so that they would live outside the 'if/else' scope to be + // used later. + AvbSHA256Ctx sha256_ctx; + AvbSHA512Ctx sha512_ctx; + size_t image_size_to_hash = hash_desc.image_size; + // If we allow verification error and the whole partition is smaller than + // image size in hash descriptor, we just hash the whole partition. + if (image_size_to_hash > image_size) { + image_size_to_hash = image_size; + } if (avb_strcmp((const char*)hash_desc.hash_algorithm, "sha256") == 0) { - AvbSHA256Ctx sha256_ctx; avb_sha256_init(&sha256_ctx); avb_sha256_update(&sha256_ctx, desc_salt, hash_desc.salt_len); - avb_sha256_update(&sha256_ctx, image_buf, hash_desc.image_size); + avb_sha256_update(&sha256_ctx, image_buf, image_size_to_hash); digest = avb_sha256_final(&sha256_ctx); digest_len = AVB_SHA256_DIGEST_SIZE; } else if (avb_strcmp((const char*)hash_desc.hash_algorithm, "sha512") == 0) { - AvbSHA512Ctx sha512_ctx; avb_sha512_init(&sha512_ctx); avb_sha512_update(&sha512_ctx, desc_salt, hash_desc.salt_len); - avb_sha512_update(&sha512_ctx, image_buf, hash_desc.image_size); + avb_sha512_update(&sha512_ctx, image_buf, image_size_to_hash); digest = avb_sha512_final(&sha512_ctx); digest_len = AVB_SHA512_DIGEST_SIZE; } else { @@ -295,18 +396,21 @@ static AvbSlotVerifyResult load_and_verify_hash_partition( } if (hash_desc.digest_len == 0) { - // Expect a match to a persistent digest. + /* Expect a match to a persistent digest. */ avb_debugv(part_name, ": No digest, using persistent digest.\n", NULL); expected_digest_len = digest_len; expected_digest = expected_digest_buf; avb_assert(expected_digest_len <= sizeof(expected_digest_buf)); - ret = - read_persistent_digest(ops, part_name, digest_len, expected_digest_buf); + /* Pass |digest| as the |initial_digest| so devices not yet initialized get + * initialized to the current partition digest. + */ + ret = read_persistent_digest( + ops, part_name, digest_len, digest, expected_digest_buf); if (ret != AVB_SLOT_VERIFY_RESULT_OK) { goto out; } } else { - // Expect a match to the digest in the descriptor. + /* Expect a match to the digest in the descriptor. */ expected_digest_len = hash_desc.digest_len; expected_digest = desc_digest; } @@ -365,12 +469,6 @@ static AvbSlotVerifyResult load_requested_partitions( bool image_preloaded = false; 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[AVB_PART_NAME_MAX_SIZE]; AvbIOResult io_ret; @@ -441,6 +539,7 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( AvbOps* ops, const char* const* requested_partitions, const char* ab_suffix, + AvbSlotVerifyFlags flags, bool allow_verification_error, AvbVBMetaImageFlags toplevel_vbmeta_flags, int rollback_index_location, @@ -467,7 +566,7 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( size_t num_descriptors; size_t n; bool is_main_vbmeta; - bool is_vbmeta_partition; + bool look_for_vbmeta_footer; AvbVBMetaData* vbmeta_image_data = NULL; ret = AVB_SLOT_VERIFY_RESULT_OK; @@ -478,8 +577,20 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( * 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); + is_main_vbmeta = false; + if (rollback_index_location == 0) { + if ((flags & AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION) == 0) { + is_main_vbmeta = true; + } + } + + /* Don't use footers for vbmeta partitions ('vbmeta' or + * 'vbmeta_<partition_name>'). + */ + look_for_vbmeta_footer = true; + if (avb_strncmp(partition_name, "vbmeta", avb_strlen("vbmeta")) == 0) { + look_for_vbmeta_footer = false; + } if (!avb_validate_utf8((const uint8_t*)partition_name, partition_name_len)) { avb_error("Partition name is not valid UTF-8.\n"); @@ -487,7 +598,7 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( goto out; } - /* Construct full partition name. */ + /* Construct full partition name e.g. system_a. */ if (!avb_str_concat(full_partition_name, sizeof full_partition_name, partition_name, @@ -499,19 +610,15 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( goto out; } - 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 we're loading from the main vbmeta partition, the vbmeta struct is in + * the beginning. Otherwise we may have to locate it via a footer... if no + * footer is found, we look in the beginning to support e.g. vbmeta_<org> + * partitions holding data for e.g. super partitions (b/80195851 for + * rationale). */ - if (is_vbmeta_partition) { - vbmeta_offset = 0; - vbmeta_size = VBMETA_MAX_SIZE; - } else { + vbmeta_offset = 0; + vbmeta_size = VBMETA_MAX_SIZE; + if (look_for_vbmeta_footer) { uint8_t footer_buf[AVB_FOOTER_SIZE]; size_t footer_num_read; AvbFooter footer; @@ -534,21 +641,17 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( if (!avb_footer_validate_and_byteswap((const AvbFooter*)footer_buf, &footer)) { - avb_errorv(full_partition_name, ": Error validating footer.\n", NULL); - ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; - goto out; - } - - /* 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); - ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; - goto out; + avb_debugv(full_partition_name, ": No footer detected.\n", NULL); + } else { + /* 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); + } else { + vbmeta_offset = footer.vbmeta_offset; + vbmeta_size = footer.vbmeta_size; + } } - - vbmeta_offset = footer.vbmeta_offset; - vbmeta_size = footer.vbmeta_size; } vbmeta_buf = avb_malloc(vbmeta_size); @@ -557,6 +660,18 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( goto out; } + if (vbmeta_offset != 0) { + avb_debugv("Loading vbmeta struct in footer from partition '", + full_partition_name, + "'.\n", + NULL); + } else { + avb_debugv("Loading vbmeta struct from partition '", + full_partition_name, + "'.\n", + NULL); + } + io_ret = ops->read_from_partition(ops, full_partition_name, vbmeta_offset, @@ -571,13 +686,14 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( * 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) { + !look_for_vbmeta_footer) { avb_debugv(full_partition_name, ": No such partition. Trying 'boot' instead.\n", NULL); ret = load_and_verify_vbmeta(ops, requested_partitions, ab_suffix, + flags, allow_verification_error, 0 /* toplevel_vbmeta_flags */, 0 /* rollback_index_location */, @@ -655,6 +771,8 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( } } + uint32_t rollback_index_location_to_use = rollback_index_location; + /* Check if key used to make signature matches what is expected. */ if (pk_data != NULL) { if (expected_public_key != NULL) { @@ -682,9 +800,27 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( 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 we're not using a vbmeta partition, need to use another AvbOps... + if (flags & AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION) { + io_ret = ops->validate_public_key_for_partition( + ops, + full_partition_name, + pk_data, + pk_len, + pk_metadata, + pk_metadata_len, + &key_is_trusted, + &rollback_index_location_to_use); + } else { + 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; @@ -709,7 +845,7 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( /* Check rollback index. */ io_ret = ops->read_rollback_index( - ops, rollback_index_location, &stored_rollback_index); + ops, rollback_index_location_to_use, &stored_rollback_index); if (io_ret == AVB_IO_RESULT_ERROR_OOM) { ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; goto out; @@ -735,7 +871,9 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( if (is_main_vbmeta) { avb_assert(slot_data->num_vbmeta_images == 0); } else { - avb_assert(slot_data->num_vbmeta_images > 0); + if (!(flags & AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION)) { + 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); @@ -859,6 +997,7 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( load_and_verify_vbmeta(ops, requested_partitions, ab_suffix, + flags, allow_verification_error, toplevel_vbmeta_flags, chain_desc.rollback_index_location, @@ -1019,7 +1158,11 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( goto out; } - ret = read_persistent_digest(ops, part_name, digest_len, digest_buf); + ret = read_persistent_digest(ops, + part_name, + digest_len, + NULL /* initial_digest */, + digest_buf); if (ret != AVB_SLOT_VERIFY_RESULT_OK) { goto out; } @@ -1043,7 +1186,8 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( } } - if (rollback_index_location >= AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS) { + if (rollback_index_location < 0 || + 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; @@ -1072,6 +1216,130 @@ out: return ret; } +static AvbIOResult avb_manage_hashtree_error_mode( + AvbOps* ops, + AvbSlotVerifyFlags flags, + AvbSlotVerifyData* data, + AvbHashtreeErrorMode* out_hashtree_error_mode) { + AvbHashtreeErrorMode ret = AVB_HASHTREE_ERROR_MODE_RESTART; + AvbIOResult io_ret = AVB_IO_RESULT_OK; + uint8_t vbmeta_digest_sha256[AVB_SHA256_DIGEST_SIZE]; + uint8_t stored_vbmeta_digest_sha256[AVB_SHA256_DIGEST_SIZE]; + size_t num_bytes_read; + + avb_assert(out_hashtree_error_mode != NULL); + avb_assert(ops->read_persistent_value != NULL); + avb_assert(ops->write_persistent_value != NULL); + + // If we're rebooting because of dm-verity corruption, make a note of + // the vbmeta hash so we can stay in 'eio' mode until things change. + if (flags & AVB_SLOT_VERIFY_FLAGS_RESTART_CAUSED_BY_HASHTREE_CORRUPTION) { + avb_debug( + "Rebooting because of dm-verity corruption - " + "recording OS instance and using 'eio' mode.\n"); + avb_slot_verify_data_calculate_vbmeta_digest( + data, AVB_DIGEST_TYPE_SHA256, vbmeta_digest_sha256); + io_ret = ops->write_persistent_value(ops, + AVB_NPV_MANAGED_VERITY_MODE, + AVB_SHA256_DIGEST_SIZE, + vbmeta_digest_sha256); + if (io_ret != AVB_IO_RESULT_OK) { + avb_error("Error writing to " AVB_NPV_MANAGED_VERITY_MODE ".\n"); + goto out; + } + ret = AVB_HASHTREE_ERROR_MODE_EIO; + io_ret = AVB_IO_RESULT_OK; + goto out; + } + + // See if we're in 'eio' mode. + io_ret = ops->read_persistent_value(ops, + AVB_NPV_MANAGED_VERITY_MODE, + AVB_SHA256_DIGEST_SIZE, + stored_vbmeta_digest_sha256, + &num_bytes_read); + if (io_ret == AVB_IO_RESULT_ERROR_NO_SUCH_VALUE || + (io_ret == AVB_IO_RESULT_OK && num_bytes_read == 0)) { + // This is the usual case ('eio' mode not set). + avb_debug("No dm-verity corruption - using in 'restart' mode.\n"); + ret = AVB_HASHTREE_ERROR_MODE_RESTART; + io_ret = AVB_IO_RESULT_OK; + goto out; + } else if (io_ret != AVB_IO_RESULT_OK) { + avb_error("Error reading from " AVB_NPV_MANAGED_VERITY_MODE ".\n"); + goto out; + } + if (num_bytes_read != AVB_SHA256_DIGEST_SIZE) { + avb_error( + "Unexpected number of bytes read from " AVB_NPV_MANAGED_VERITY_MODE + ".\n"); + io_ret = AVB_IO_RESULT_ERROR_IO; + goto out; + } + + // OK, so we're currently in 'eio' mode and the vbmeta digest of the OS + // that caused this is in |stored_vbmeta_digest_sha256| ... now see if + // the OS we're dealing with now is the same. + avb_slot_verify_data_calculate_vbmeta_digest( + data, AVB_DIGEST_TYPE_SHA256, vbmeta_digest_sha256); + if (avb_memcmp(vbmeta_digest_sha256, + stored_vbmeta_digest_sha256, + AVB_SHA256_DIGEST_SIZE) == 0) { + // It's the same so we're still in 'eio' mode. + avb_debug("Same OS instance detected - staying in 'eio' mode.\n"); + ret = AVB_HASHTREE_ERROR_MODE_EIO; + io_ret = AVB_IO_RESULT_OK; + } else { + // It did change! + avb_debug( + "New OS instance detected - changing from 'eio' to 'restart' mode.\n"); + io_ret = + ops->write_persistent_value(ops, + AVB_NPV_MANAGED_VERITY_MODE, + 0, // This clears the persistent property. + vbmeta_digest_sha256); + if (io_ret != AVB_IO_RESULT_OK) { + avb_error("Error clearing " AVB_NPV_MANAGED_VERITY_MODE ".\n"); + goto out; + } + ret = AVB_HASHTREE_ERROR_MODE_RESTART; + io_ret = AVB_IO_RESULT_OK; + } + +out: + *out_hashtree_error_mode = ret; + return io_ret; +} + +static bool has_system_partition(AvbOps* ops, const char* ab_suffix) { + char part_name[AVB_PART_NAME_MAX_SIZE]; + char* system_part_name = "system"; + char guid_buf[37]; + AvbIOResult io_ret; + + if (!avb_str_concat(part_name, + sizeof part_name, + system_part_name, + avb_strlen(system_part_name), + ab_suffix, + avb_strlen(ab_suffix))) { + avb_error("System partition name and suffix does not fit.\n"); + return false; + } + + io_ret = ops->get_unique_guid_for_partition( + ops, part_name, guid_buf, sizeof guid_buf); + if (io_ret == AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION) { + avb_debug("No system partition.\n"); + return false; + } else if (io_ret != AVB_IO_RESULT_OK) { + avb_error("Error getting unique GUID for system partition.\n"); + return false; + } + + return true; +} + AvbSlotVerifyResult avb_slot_verify(AvbOps* ops, const char* const* requested_partitions, const char* ab_suffix, @@ -1087,14 +1355,10 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops, (flags & AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR); AvbCmdlineSubstList* additional_cmdline_subst = NULL; - /* Fail early if we're missing the AvbOps needed for slot verification. - * - * For now, handle get_size_of_partition() not being implemented. In - * a later release we may change that. - */ + /* Fail early if we're missing the AvbOps needed for slot verification. */ avb_assert(ops->read_is_device_unlocked != NULL); avb_assert(ops->read_from_partition != NULL); - avb_assert(ops->validate_vbmeta_public_key != NULL); + avb_assert(ops->get_size_of_partition != NULL); avb_assert(ops->read_rollback_index != NULL); avb_assert(ops->get_unique_guid_for_partition != NULL); @@ -1112,6 +1376,36 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops, goto fail; } + /* Make sure passed-in AvbOps support persistent values if + * asking for libavb to manage verity state. + */ + if (hashtree_error_mode == AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO) { + if (ops->read_persistent_value == NULL || + ops->write_persistent_value == NULL) { + avb_error( + "Persistent values required for " + "AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO " + "but are not implemented in given AvbOps.\n"); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT; + goto fail; + } + } + + /* Make sure passed-in AvbOps support verifying public keys and getting + * rollback index location if not using a vbmeta partition. + */ + if (flags & AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION) { + if (ops->validate_public_key_for_partition == NULL) { + avb_error( + "AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION was passed but the " + "validate_public_key_for_partition() operation isn't implemented.\n"); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT; + goto fail; + } + } else { + avb_assert(ops->validate_vbmeta_public_key != NULL); + } + slot_data = avb_calloc(sizeof(AvbSlotVerifyData)); if (slot_data == NULL) { ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; @@ -1136,99 +1430,163 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops, goto fail; } - 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, - additional_cmdline_subst); - if (!allow_verification_error && ret != AVB_SLOT_VERIFY_RESULT_OK) { + if (flags & AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION) { + if (requested_partitions == NULL || requested_partitions[0] == NULL) { + avb_fatal( + "Requested partitions cannot be empty when using " + "AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION"); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT; + goto fail; + } + + /* No vbmeta partition, go through each of the requested partitions... */ + for (size_t n = 0; requested_partitions[n] != NULL; n++) { + ret = load_and_verify_vbmeta(ops, + requested_partitions, + ab_suffix, + flags, + allow_verification_error, + 0 /* toplevel_vbmeta_flags */, + 0 /* rollback_index_location */, + requested_partitions[n], + avb_strlen(requested_partitions[n]), + NULL /* expected_public_key */, + 0 /* expected_public_key_length */, + slot_data, + &algorithm_type, + additional_cmdline_subst); + if (!allow_verification_error && ret != AVB_SLOT_VERIFY_RESULT_OK) { + goto fail; + } + } + + } else { + /* Usual path, load "vbmeta"... */ + ret = load_and_verify_vbmeta(ops, + requested_partitions, + ab_suffix, + flags, + 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, + additional_cmdline_subst); + if (!allow_verification_error && ret != AVB_SLOT_VERIFY_RESULT_OK) { + goto fail; + } + } + + if (!result_should_continue(ret)) { goto fail; } /* If things check out, mangle the kernel command-line as needed. */ - if (result_should_continue(ret)) { + if (!(flags & AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION)) { 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; } + } - /* Byteswap top-level vbmeta header since we'll need it below. */ - avb_vbmeta_image_header_to_host_byte_order( - (const AvbVBMetaImageHeader*)slot_data->vbmeta_images[0].vbmeta_data, - &toplevel_vbmeta); + /* Byteswap top-level vbmeta header since we'll need it below. */ + avb_vbmeta_image_header_to_host_byte_order( + (const AvbVBMetaImageHeader*)slot_data->vbmeta_images[0].vbmeta_data, + &toplevel_vbmeta); - /* Fill in |ab_suffix| field. */ - slot_data->ab_suffix = avb_strdup(ab_suffix); - if (slot_data->ab_suffix == NULL) { - ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; - goto fail; - } + /* Fill in |ab_suffix| field. */ + slot_data->ab_suffix = avb_strdup(ab_suffix); + if (slot_data->ab_suffix == NULL) { + 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 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. */ - 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); + avb_assert(slot_data->cmdline == NULL); + // Devices with dynamic partitions won't have system partition. + // Instead, it has a large super partition to accommodate *.img files. + // See b/119551429 for details. + if (has_system_partition(ops, ab_suffix)) { 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 avb_append_options() is either an - * I/O or OOM error. - */ - AvbSlotVerifyResult sub_ret = avb_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; - } + // The |cmdline| field should be a NUL-terminated string. + slot_data->cmdline = avb_strdup(""); } - - /* Substitute $(ANDROID_SYSTEM_PARTUUID) and friends. */ - if (slot_data->cmdline != NULL) { - char* new_cmdline; - new_cmdline = avb_sub_cmdline(ops, - slot_data->cmdline, - ab_suffix, - using_boot_for_vbmeta, - additional_cmdline_subst); - if (new_cmdline != slot_data->cmdline) { - if (new_cmdline == NULL) { + if (slot_data->cmdline == NULL) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto fail; + } + } else { + /* If requested, manage dm-verity mode... */ + AvbHashtreeErrorMode resolved_hashtree_error_mode = hashtree_error_mode; + if (hashtree_error_mode == + AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO) { + AvbIOResult io_ret; + io_ret = avb_manage_hashtree_error_mode( + ops, flags, slot_data, &resolved_hashtree_error_mode); + if (io_ret != AVB_IO_RESULT_OK) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; + if (io_ret == AVB_IO_RESULT_ERROR_OOM) { ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; - goto fail; } - avb_free(slot_data->cmdline); - slot_data->cmdline = new_cmdline; + goto fail; } } + slot_data->resolved_hashtree_error_mode = resolved_hashtree_error_mode; - if (out_data != NULL) { - *out_data = slot_data; - } else { - avb_slot_verify_data_free(slot_data); + /* Add options... */ + AvbSlotVerifyResult sub_ret; + sub_ret = avb_append_options(ops, + flags, + slot_data, + &toplevel_vbmeta, + algorithm_type, + hashtree_error_mode, + resolved_hashtree_error_mode); + if (sub_ret != AVB_SLOT_VERIFY_RESULT_OK) { + ret = sub_ret; + goto fail; + } + } + + /* Substitute $(ANDROID_SYSTEM_PARTUUID) and friends. */ + if (slot_data->cmdline != NULL && avb_strlen(slot_data->cmdline) != 0) { + char* new_cmdline; + new_cmdline = avb_sub_cmdline(ops, + slot_data->cmdline, + ab_suffix, + using_boot_for_vbmeta, + additional_cmdline_subst); + if (new_cmdline != slot_data->cmdline) { + if (new_cmdline == NULL) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto fail; + } + avb_free(slot_data->cmdline); + slot_data->cmdline = new_cmdline; } } + if (out_data != NULL) { + *out_data = slot_data; + } else { + avb_slot_verify_data_free(slot_data); + } + avb_free_cmdline_subst_list(additional_cmdline_subst); additional_cmdline_subst = NULL; |