Support dm-verity with verification based on root digest
Currently the only ways to enable dm-verity were relying on its built-in vbmeta image or containing its public key on standalone vbmeta image. Merging this change will support enabling dm-verity based on hashtree descriptor root digest for standalone vbmeta image. Bug: 285855436 Test: Presubmit Test: adb shell /apex/com.android.virt/bin/vm run-microdroid --vendor /vendor/etc/avf/microdroid/microdroid_vendor.img Change-Id: I51eb64cae2ca8b4e97f1c6419b35d45e6f51cacb
This commit is contained in:
parent
7341ffba7c
commit
66dc7b7b99
4 changed files with 133 additions and 55 deletions
|
@ -288,14 +288,82 @@ static bool IsAvbPermissive() {
|
|||
return false;
|
||||
}
|
||||
|
||||
AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta(const FstabEntry& fstab_entry,
|
||||
const std::vector<std::string>& preload_avb_key_blobs) {
|
||||
bool IsPublicKeyMatching(const FstabEntry& fstab_entry, const std::string& public_key_data,
|
||||
const std::vector<std::string>& preload_avb_key_blobs) {
|
||||
// At least one of the following should be provided for public key matching.
|
||||
if (preload_avb_key_blobs.empty() && fstab_entry.avb_keys.empty()) {
|
||||
LERROR << "avb_keys=/path/to/key(s) is missing for " << fstab_entry.mount_point;
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Expected key shouldn't be empty.
|
||||
if (public_key_data.empty()) {
|
||||
LERROR << "public key data shouldn't be empty for " << fstab_entry.mount_point;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Performs key matching for preload_avb_key_blobs first, if it is present.
|
||||
if (!preload_avb_key_blobs.empty()) {
|
||||
if (std::find(preload_avb_key_blobs.begin(), preload_avb_key_blobs.end(),
|
||||
public_key_data) != preload_avb_key_blobs.end()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Performs key matching for fstab_entry.avb_keys if necessary.
|
||||
// Note that it is intentional to match both preload_avb_key_blobs and fstab_entry.avb_keys.
|
||||
// Some keys might only be available before init chroots into /system, e.g., /avb/key1
|
||||
// in the first-stage ramdisk, while other keys might only be available after the chroot,
|
||||
// e.g., /system/etc/avb/key2.
|
||||
// fstab_entry.avb_keys might be either a directory containing multiple keys,
|
||||
// or a string indicating multiple keys separated by ':'.
|
||||
std::vector<std::string> allowed_avb_keys;
|
||||
auto list_avb_keys_in_dir = ListFiles(fstab_entry.avb_keys);
|
||||
if (list_avb_keys_in_dir.ok()) {
|
||||
std::sort(list_avb_keys_in_dir->begin(), list_avb_keys_in_dir->end());
|
||||
allowed_avb_keys = *list_avb_keys_in_dir;
|
||||
} else {
|
||||
allowed_avb_keys = Split(fstab_entry.avb_keys, ":");
|
||||
}
|
||||
return ValidatePublicKeyBlob(public_key_data, allowed_avb_keys);
|
||||
}
|
||||
|
||||
bool IsHashtreeDescriptorRootDigestMatching(const FstabEntry& fstab_entry,
|
||||
const std::vector<VBMetaData>& vbmeta_images,
|
||||
const std::string& ab_suffix,
|
||||
const std::string& ab_other_suffix) {
|
||||
// Read expected value of hashtree descriptor root digest from fstab_entry.
|
||||
std::string root_digest_expected;
|
||||
if (!ReadFileToString(fstab_entry.avb_hashtree_digest, &root_digest_expected)) {
|
||||
LERROR << "Failed to load expected root digest for " << fstab_entry.mount_point;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read actual hashtree descriptor from vbmeta image.
|
||||
std::string partition_name = DeriveAvbPartitionName(fstab_entry, ab_suffix, ab_other_suffix);
|
||||
if (partition_name.empty()) {
|
||||
LERROR << "Failed to find partition name for " << fstab_entry.mount_point;
|
||||
return false;
|
||||
}
|
||||
std::unique_ptr<FsAvbHashtreeDescriptor> hashtree_descriptor =
|
||||
android::fs_mgr::GetHashtreeDescriptor(partition_name, vbmeta_images);
|
||||
if (!hashtree_descriptor) {
|
||||
LERROR << "Not found hashtree descriptor for " << fstab_entry.mount_point;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Performs hashtree descriptor root digest matching.
|
||||
if (hashtree_descriptor->root_digest != root_digest_expected) {
|
||||
LERROR << "root digest (" << hashtree_descriptor->root_digest
|
||||
<< ") is different from expected value (" << root_digest_expected << ")";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta(const FstabEntry& fstab_entry,
|
||||
const std::vector<std::string>& preload_avb_key_blobs) {
|
||||
// Binds allow_verification_error and rollback_protection to device unlock state.
|
||||
bool allow_verification_error = IsAvbPermissive();
|
||||
bool rollback_protection = !allow_verification_error;
|
||||
|
@ -333,40 +401,24 @@ AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta(const FstabEntry& fstab_entry,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
bool public_key_match = false;
|
||||
// Performs key matching for preload_avb_key_blobs first, if it is present.
|
||||
if (!public_key_data.empty() && !preload_avb_key_blobs.empty()) {
|
||||
if (std::find(preload_avb_key_blobs.begin(), preload_avb_key_blobs.end(),
|
||||
public_key_data) != preload_avb_key_blobs.end()) {
|
||||
public_key_match = true;
|
||||
// Verify vbmeta image checking by either public key or hashtree descriptor root digest.
|
||||
if (!preload_avb_key_blobs.empty() || !fstab_entry.avb_keys.empty()) {
|
||||
if (!IsPublicKeyMatching(fstab_entry, public_key_data, preload_avb_key_blobs)) {
|
||||
avb_handle->status_ = AvbHandleStatus::kVerificationError;
|
||||
LWARNING << "Found unknown public key used to sign " << fstab_entry.mount_point;
|
||||
if (!allow_verification_error) {
|
||||
LERROR << "Unknown public key is not allowed";
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Performs key matching for fstab_entry.avb_keys if necessary.
|
||||
// Note that it is intentional to match both preload_avb_key_blobs and fstab_entry.avb_keys.
|
||||
// Some keys might only be availble before init chroots into /system, e.g., /avb/key1
|
||||
// in the first-stage ramdisk, while other keys might only be available after the chroot,
|
||||
// e.g., /system/etc/avb/key2.
|
||||
if (!public_key_data.empty() && !public_key_match) {
|
||||
// fstab_entry.avb_keys might be either a directory containing multiple keys,
|
||||
// or a string indicating multiple keys separated by ':'.
|
||||
std::vector<std::string> allowed_avb_keys;
|
||||
auto list_avb_keys_in_dir = ListFiles(fstab_entry.avb_keys);
|
||||
if (list_avb_keys_in_dir.ok()) {
|
||||
std::sort(list_avb_keys_in_dir->begin(), list_avb_keys_in_dir->end());
|
||||
allowed_avb_keys = *list_avb_keys_in_dir;
|
||||
} else {
|
||||
allowed_avb_keys = Split(fstab_entry.avb_keys, ":");
|
||||
}
|
||||
if (ValidatePublicKeyBlob(public_key_data, allowed_avb_keys)) {
|
||||
public_key_match = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!public_key_match) {
|
||||
} else if (!IsHashtreeDescriptorRootDigestMatching(fstab_entry, avb_handle->vbmeta_images_,
|
||||
avb_handle->slot_suffix_,
|
||||
avb_handle->other_slot_suffix_)) {
|
||||
avb_handle->status_ = AvbHandleStatus::kVerificationError;
|
||||
LWARNING << "Found unknown public key used to sign " << fstab_entry.mount_point;
|
||||
LWARNING << "Found unknown hashtree descriptor root digest used on "
|
||||
<< fstab_entry.mount_point;
|
||||
if (!allow_verification_error) {
|
||||
LERROR << "Unknown public key is not allowed";
|
||||
LERROR << "Verification based on root digest failed. Vbmeta image is not allowed.";
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -286,6 +286,10 @@ bool ParseFsMgrFlags(const std::string& flags, FstabEntry* entry) {
|
|||
}
|
||||
} else if (StartsWith(flag, "avb_keys=")) { // must before the following "avb"
|
||||
entry->avb_keys = arg;
|
||||
} else if (StartsWith(flag, "avb_hashtree_digest=")) {
|
||||
// "avb_hashtree_digest" must before the following "avb"
|
||||
// The path where hex-encoded hashtree descriptor root digest is located.
|
||||
entry->avb_hashtree_digest = arg;
|
||||
} else if (StartsWith(flag, "avb")) {
|
||||
entry->fs_mgr_flags.avb = true;
|
||||
entry->vbmeta_partition = arg;
|
||||
|
|
|
@ -57,6 +57,7 @@ struct FstabEntry {
|
|||
uint64_t zram_backingdev_size = 0;
|
||||
std::string avb_keys;
|
||||
std::string lowerdir;
|
||||
std::string avb_hashtree_digest;
|
||||
|
||||
struct FsMgrFlags {
|
||||
bool wait : 1;
|
||||
|
|
|
@ -732,6 +732,15 @@ bool FirstStageMountVBootV2::GetDmVerityDevices(std::set<std::string>* devices)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool IsHashtreeDisabled(const AvbHandle& vbmeta, std::string mount_point) {
|
||||
if (vbmeta.status() == AvbHandleStatus::kHashtreeDisabled ||
|
||||
vbmeta.status() == AvbHandleStatus::kVerificationDisabled) {
|
||||
LOG(ERROR) << "Top-level vbmeta is disabled, skip Hashtree setup for " << mount_point;
|
||||
return true; // Returns true to mount the partition directly.
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FirstStageMountVBootV2::SetUpDmVerity(FstabEntry* fstab_entry) {
|
||||
AvbHashtreeResult hashtree_result;
|
||||
|
||||
|
@ -740,34 +749,46 @@ bool FirstStageMountVBootV2::SetUpDmVerity(FstabEntry* fstab_entry) {
|
|||
if (!fstab_entry->avb_keys.empty()) {
|
||||
if (!InitAvbHandle()) return false;
|
||||
// Checks if hashtree should be disabled from the top-level /vbmeta.
|
||||
if (avb_handle_->status() == AvbHandleStatus::kHashtreeDisabled ||
|
||||
avb_handle_->status() == AvbHandleStatus::kVerificationDisabled) {
|
||||
LOG(ERROR) << "Top-level vbmeta is disabled, skip Hashtree setup for "
|
||||
<< fstab_entry->mount_point;
|
||||
return true; // Returns true to mount the partition directly.
|
||||
if (IsHashtreeDisabled(*avb_handle_, fstab_entry->mount_point)) {
|
||||
return true;
|
||||
}
|
||||
auto avb_standalone_handle = AvbHandle::LoadAndVerifyVbmeta(
|
||||
*fstab_entry, preload_avb_key_blobs_[fstab_entry->avb_keys]);
|
||||
if (!avb_standalone_handle) {
|
||||
LOG(ERROR) << "Failed to load offline vbmeta for " << fstab_entry->mount_point;
|
||||
// Fallbacks to built-in hashtree if fs_mgr_flags.avb is set.
|
||||
if (!fstab_entry->fs_mgr_flags.avb) return false;
|
||||
LOG(INFO) << "Fallback to built-in hashtree for " << fstab_entry->mount_point;
|
||||
hashtree_result =
|
||||
avb_handle_->SetUpAvbHashtree(fstab_entry, false /* wait_for_verity_dev */);
|
||||
} else {
|
||||
auto avb_standalone_handle = AvbHandle::LoadAndVerifyVbmeta(
|
||||
*fstab_entry, preload_avb_key_blobs_[fstab_entry->avb_keys]);
|
||||
if (!avb_standalone_handle) {
|
||||
LOG(ERROR) << "Failed to load offline vbmeta for " << fstab_entry->mount_point;
|
||||
// Fallbacks to built-in hashtree if fs_mgr_flags.avb is set.
|
||||
if (!fstab_entry->fs_mgr_flags.avb) return false;
|
||||
LOG(INFO) << "Fallback to built-in hashtree for " << fstab_entry->mount_point;
|
||||
hashtree_result =
|
||||
avb_handle_->SetUpAvbHashtree(fstab_entry, false /* wait_for_verity_dev */);
|
||||
} else {
|
||||
// Sets up hashtree via the standalone handle.
|
||||
if (IsStandaloneImageRollback(*avb_handle_, *avb_standalone_handle, *fstab_entry)) {
|
||||
return false;
|
||||
}
|
||||
hashtree_result = avb_standalone_handle->SetUpAvbHashtree(
|
||||
fstab_entry, false /* wait_for_verity_dev */);
|
||||
// Sets up hashtree via the standalone handle.
|
||||
if (IsStandaloneImageRollback(*avb_handle_, *avb_standalone_handle, *fstab_entry)) {
|
||||
return false;
|
||||
}
|
||||
hashtree_result = avb_standalone_handle->SetUpAvbHashtree(
|
||||
fstab_entry, false /* wait_for_verity_dev */);
|
||||
}
|
||||
} else if (fstab_entry->fs_mgr_flags.avb) {
|
||||
if (!InitAvbHandle()) return false;
|
||||
hashtree_result =
|
||||
avb_handle_->SetUpAvbHashtree(fstab_entry, false /* wait_for_verity_dev */);
|
||||
} else if (!fstab_entry->avb_hashtree_digest.empty()) {
|
||||
// When fstab_entry has neither avb_keys nor avb flag, try using
|
||||
// avb_hashtree_digest.
|
||||
if (!InitAvbHandle()) return false;
|
||||
// Checks if hashtree should be disabled from the top-level /vbmeta.
|
||||
if (IsHashtreeDisabled(*avb_handle_, fstab_entry->mount_point)) {
|
||||
return true;
|
||||
}
|
||||
auto avb_standalone_handle = AvbHandle::LoadAndVerifyVbmeta(*fstab_entry);
|
||||
if (!avb_standalone_handle) {
|
||||
LOG(ERROR) << "Failed to load vbmeta based on hashtree descriptor root digest for "
|
||||
<< fstab_entry->mount_point;
|
||||
return false;
|
||||
}
|
||||
hashtree_result = avb_standalone_handle->SetUpAvbHashtree(fstab_entry,
|
||||
false /* wait_for_verity_dev */);
|
||||
} else {
|
||||
return true; // No need AVB, returns true to mount the partition directly.
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue