diff --git a/fs_mgr/libfs_avb/fs_avb.cpp b/fs_mgr/libfs_avb/fs_avb.cpp index 8770a6b35..50de42c06 100644 --- a/fs_mgr/libfs_avb/fs_avb.cpp +++ b/fs_mgr/libfs_avb/fs_avb.cpp @@ -266,8 +266,10 @@ AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta( return avb_handle; } -AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta(const FstabEntry& fstab_entry) { - if (fstab_entry.avb_keys.empty()) { +AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta(const FstabEntry& fstab_entry, + const std::vector& 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; } @@ -309,18 +311,36 @@ AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta(const FstabEntry& fstab_entry) { return nullptr; } - // fstab_entry.avb_keys might be either a directory containing multiple keys, - // or a string indicating multiple keys separated by ':'. - std::vector allowed_avb_keys; - auto list_avb_keys_in_dir = ListFiles(fstab_entry.avb_keys); - if (list_avb_keys_in_dir) { - 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, ":"); + 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; + } + } + // 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 allowed_avb_keys; + auto list_avb_keys_in_dir = ListFiles(fstab_entry.avb_keys); + if (list_avb_keys_in_dir) { + 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 (!ValidatePublicKeyBlob(public_key_data, allowed_avb_keys)) { + if (!public_key_match) { avb_handle->status_ = AvbHandleStatus::kVerificationError; LWARNING << "Found unknown public key used to sign " << fstab_entry.mount_point; if (!allow_verification_error) { diff --git a/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h b/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h index 521f2d582..4702e6807 100644 --- a/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h +++ b/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h @@ -85,8 +85,15 @@ class AvbHandle { // TODO(bowgotsai): remove Open() and switch to LoadAndVerifyVbmeta(). static AvbUniquePtr Open(); // loads inline vbmeta, via libavb. static AvbUniquePtr LoadAndVerifyVbmeta(); // loads inline vbmeta. - static AvbUniquePtr LoadAndVerifyVbmeta( - const FstabEntry& fstab_entry); // loads offline vbmeta. + + // The caller can specify optional preload_avb_key_blobs for public key matching. + // This is mostly for init to preload AVB keys before chroot into /system. + // Both preload_avb_key_blobs and fstab_entry.avb_keys (file paths) will be used + // for public key matching. + static AvbUniquePtr LoadAndVerifyVbmeta( // loads offline vbmeta. + const FstabEntry& fstab_entry, + const std::vector& preload_avb_key_blobs = {}); + static AvbUniquePtr LoadAndVerifyVbmeta( // loads offline vbmeta. const std::string& partition_name, const std::string& ab_suffix, const std::string& ab_other_suffix, const std::string& expected_public_key, diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp index d8c4843be..21663e6e6 100644 --- a/init/first_stage_mount.cpp +++ b/init/first_stage_mount.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -29,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -45,7 +47,9 @@ #include "uevent_listener.h" #include "util.h" +using android::base::ReadFileToString; using android::base::Split; +using android::base::StringPrintf; using android::base::Timer; using android::fiemap::IImageManager; using android::fs_mgr::AvbHandle; @@ -95,6 +99,7 @@ class FirstStageMount { void GetDmLinearMetadataDevice(std::set* devices); bool InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata); void UseDsuIfPresent(); + void PreloadAvbKeys(); ListenerAction UeventCallback(const Uevent& uevent, std::set* required_devices); @@ -110,6 +115,9 @@ class FirstStageMount { std::string super_partition_name_; std::unique_ptr device_handler_; UeventListener uevent_listener_; + // Reads all AVB keys before chroot into /system, as they might be used + // later when mounting other partitions, e.g., /vendor and /product. + std::map> preload_avb_key_blobs_; }; class FirstStageMountVBootV1 : public FirstStageMount { @@ -508,11 +516,57 @@ bool FirstStageMount::MountPartition(const Fstab::iterator& begin, bool erase_sa return mounted; } +void FirstStageMount::PreloadAvbKeys() { + for (const auto& entry : fstab_) { + // No need to cache the key content if it's empty, or is already cached. + if (entry.avb_keys.empty() || preload_avb_key_blobs_.count(entry.avb_keys)) { + continue; + } + + // Determines all key paths first. + std::vector key_paths; + if (is_dir(entry.avb_keys.c_str())) { // fstab_keys might be a dir, e.g., /avb. + const char* avb_key_dir = entry.avb_keys.c_str(); + std::unique_ptr dir(opendir(avb_key_dir), closedir); + if (!dir) { + LOG(ERROR) << "Failed to opendir: " << dir; + continue; + } + // Gets all key pathes under the dir. + struct dirent* de; + while ((de = readdir(dir.get()))) { + if (de->d_type != DT_REG) continue; + std::string full_path = StringPrintf("%s/%s", avb_key_dir, de->d_name); + key_paths.emplace_back(std::move(full_path)); + } + std::sort(key_paths.begin(), key_paths.end()); + } else { + // avb_keys are key paths separated by ":", if it's not a dir. + key_paths = Split(entry.avb_keys, ":"); + } + + // Reads the key content then cache it. + std::vector key_blobs; + for (const auto& path : key_paths) { + std::string key_value; + if (!ReadFileToString(path, &key_value)) { + continue; + } + key_blobs.emplace_back(std::move(key_value)); + } + + // Maps entry.avb_keys to actual key blobs. + preload_avb_key_blobs_[entry.avb_keys] = std::move(key_blobs); + } +} + // If system is in the fstab then we're not a system-as-root device, and in // this case, we mount system first then pivot to it. From that point on, // we are effectively identical to a system-as-root device. bool FirstStageMount::TrySwitchSystemAsRoot() { UseDsuIfPresent(); + // Preloading all AVB keys from the ramdisk before switching root to /system. + PreloadAvbKeys(); auto system_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) { return entry.mount_point == "/system"; @@ -776,7 +830,8 @@ bool FirstStageMountVBootV2::SetUpDmVerity(FstabEntry* fstab_entry) { << fstab_entry->mount_point; return true; // Returns true to mount the partition directly. } else { - auto avb_standalone_handle = AvbHandle::LoadAndVerifyVbmeta(*fstab_entry); + 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.