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:
Seungjae Yoo 2023-12-15 15:44:47 +09:00
parent 7341ffba7c
commit 66dc7b7b99
4 changed files with 133 additions and 55 deletions

View file

@ -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;
}
}

View file

@ -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;

View file

@ -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;

View file

@ -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.
}