diff --git a/fs_mgr/libfs_avb/avb_util.cpp b/fs_mgr/libfs_avb/avb_util.cpp index fa9080eea..80fa5c402 100644 --- a/fs_mgr/libfs_avb/avb_util.cpp +++ b/fs_mgr/libfs_avb/avb_util.cpp @@ -34,6 +34,19 @@ using android::base::unique_fd; namespace android { namespace fs_mgr { +std::string GetAvbPropertyDescriptor(const std::string& key, + const std::vector& vbmeta_images) { + size_t value_size; + for (const auto& vbmeta : vbmeta_images) { + const char* value = avb_property_lookup(vbmeta.data(), vbmeta.size(), key.data(), + key.size(), &value_size); + if (value != nullptr) { + return {value, value_size}; + } + } + return ""; +} + // Constructs dm-verity arguments for sending DM_TABLE_LOAD ioctl to kernel. // See the following link for more details: // https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity diff --git a/fs_mgr/libfs_avb/avb_util.h b/fs_mgr/libfs_avb/avb_util.h index 9babd8802..5f413e33d 100644 --- a/fs_mgr/libfs_avb/avb_util.h +++ b/fs_mgr/libfs_avb/avb_util.h @@ -37,6 +37,9 @@ struct ChainInfo { : partition_name(chain_partition_name), public_key_blob(chain_public_key_blob) {} }; +std::string GetAvbPropertyDescriptor(const std::string& key, + const std::vector& vbmeta_images); + // AvbHashtreeDescriptor to dm-verity table setup. std::unique_ptr GetHashtreeDescriptor( const std::string& partition_name, const std::vector& vbmeta_images); diff --git a/fs_mgr/libfs_avb/fs_avb.cpp b/fs_mgr/libfs_avb/fs_avb.cpp index 938e149df..02902f008 100644 --- a/fs_mgr/libfs_avb/fs_avb.cpp +++ b/fs_mgr/libfs_avb/fs_avb.cpp @@ -263,6 +263,69 @@ AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta( return avb_handle; } +AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta(const FstabEntry& fstab_entry) { + if (fstab_entry.avb_key.empty()) { + LERROR << "avb_key=/path/to/key is missing for " << fstab_entry.mount_point; + return nullptr; + } + + // Binds allow_verification_error and rollback_protection to device unlock state. + bool allow_verification_error = IsDeviceUnlocked(); + bool rollback_protection = !allow_verification_error; + + std::string expected_key_blob; + if (!ReadFileToString(fstab_entry.avb_key, &expected_key_blob)) { + if (!allow_verification_error) { + LERROR << "Failed to load avb_key: " << fstab_entry.avb_key + << " for mount point: " << fstab_entry.mount_point; + return nullptr; + } + LWARNING << "Allowing no expected key blob when verification error is permitted"; + expected_key_blob.clear(); + } + + bool verification_disabled = false; + VBMetaVerifyResult verify_result = VBMetaVerifyResult::kError; + std::unique_ptr vbmeta = LoadAndVerifyVbmetaByPath( + fstab_entry.blk_device, "" /* partition_name, no need for a standalone path */, + expected_key_blob, allow_verification_error, rollback_protection, + false /* not is_chained_vbmeta */, nullptr /* out_public_key_data */, + &verification_disabled, &verify_result); + + if (!vbmeta) { + LERROR << "Failed to load vbmeta: " << fstab_entry.blk_device; + return nullptr; + } + + AvbUniquePtr avb_handle(new AvbHandle()); + if (!avb_handle) { + LERROR << "Failed to allocate AvbHandle"; + return nullptr; + } + avb_handle->vbmeta_images_.emplace_back(std::move(*vbmeta)); + + switch (verify_result) { + case VBMetaVerifyResult::kSuccess: + avb_handle->status_ = AvbHandleStatus::kSuccess; + break; + case VBMetaVerifyResult::kErrorVerification: + avb_handle->status_ = AvbHandleStatus::kVerificationError; + break; + default: + LERROR << "LoadAndVerifyVbmetaByPath failed, result: " << verify_result; + return nullptr; + } + + if (verification_disabled) { + LINFO << "AVB verification disabled on: " << fstab_entry.mount_point; + avb_handle->status_ = AvbHandleStatus::kVerificationDisabled; + } + + LINFO << "Returning avb_handle for '" << fstab_entry.mount_point + << "' with status: " << avb_handle->status_; + return avb_handle; +} + AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta() { // Loads inline vbmeta images, starting from /vbmeta. return LoadAndVerifyVbmeta("vbmeta", fs_mgr_get_slot_suffix(), fs_mgr_get_other_slot_suffix(), @@ -358,52 +421,12 @@ AvbUniquePtr AvbHandle::Open() { AvbHashtreeResult AvbHandle::SetUpStandaloneAvbHashtree(FstabEntry* fstab_entry, bool wait_for_verity_dev) { - if (fstab_entry->avb_key.empty()) { - LERROR << "avb_key=/path/to/key is missing for " << fstab_entry->mount_point; + auto avb_handle = LoadAndVerifyVbmeta(*fstab_entry); + if (!avb_handle) { return AvbHashtreeResult::kFail; } - // Binds allow_verification_error and rollback_protection to device unlock state. - bool allow_verification_error = IsDeviceUnlocked(); - bool rollback_protection = !allow_verification_error; - - std::string expected_key_blob; - if (!ReadFileToString(fstab_entry->avb_key, &expected_key_blob)) { - if (!allow_verification_error) { - LERROR << "Failed to load avb_key: " << fstab_entry->avb_key - << " for mount point: " << fstab_entry->mount_point; - return AvbHashtreeResult::kFail; - } - LWARNING << "Allowing no expected key blob when verification error is permitted"; - expected_key_blob.clear(); - } - - bool verification_disabled = false; - std::unique_ptr vbmeta = LoadAndVerifyVbmetaByPath( - fstab_entry->blk_device, "" /* partition_name, no need for a standalone path */, - expected_key_blob, allow_verification_error, rollback_protection, - false /* not is_chained_vbmeta */, nullptr /* out_public_key_data */, - &verification_disabled, nullptr /* out_verify_result */); - - if (!vbmeta) { - LERROR << "Failed to load vbmeta: " << fstab_entry->blk_device; - return AvbHashtreeResult::kFail; - } - - if (verification_disabled) { - LINFO << "AVB verification disabled on: " << fstab_entry->mount_point; - return AvbHashtreeResult::kDisabled; - } - - // Puts the vbmeta into a vector, for LoadAvbHashtreeToEnableVerity() to use. - std::vector vbmeta_images; - vbmeta_images.emplace_back(std::move(*vbmeta)); - if (!LoadAvbHashtreeToEnableVerity(fstab_entry, wait_for_verity_dev, vbmeta_images, - fs_mgr_get_slot_suffix(), fs_mgr_get_other_slot_suffix())) { - return AvbHashtreeResult::kFail; - } - - return AvbHashtreeResult::kSuccess; + return avb_handle->SetUpAvbHashtree(fstab_entry, wait_for_verity_dev); } AvbHashtreeResult AvbHandle::SetUpAvbHashtree(FstabEntry* fstab_entry, bool wait_for_verity_dev) { @@ -425,5 +448,19 @@ AvbHashtreeResult AvbHandle::SetUpAvbHashtree(FstabEntry* fstab_entry, bool wait return AvbHashtreeResult::kSuccess; } +std::string AvbHandle::GetSecurityPatchLevel(const FstabEntry& fstab_entry) const { + if (vbmeta_images_.size() < 1) { + return ""; + } + std::string avb_partition_name = DeriveAvbPartitionName(fstab_entry, fs_mgr_get_slot_suffix(), + fs_mgr_get_other_slot_suffix()); + auto avb_prop_name = "com.android.build." + avb_partition_name + ".security_patch"; + return GetAvbPropertyDescriptor(avb_prop_name, vbmeta_images_); +} + +bool AvbHandle::IsDeviceUnlocked() { + return android::fs_mgr::IsDeviceUnlocked(); +} + } // namespace fs_mgr } // namespace android 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 d02672211..7127fa6ea 100644 --- a/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h +++ b/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h @@ -85,6 +85,8 @@ 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. 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, @@ -108,6 +110,10 @@ class AvbHandle { static AvbHashtreeResult SetUpStandaloneAvbHashtree(FstabEntry* fstab_entry, bool wait_for_verity_dev = true); + static bool IsDeviceUnlocked(); + + std::string GetSecurityPatchLevel(const FstabEntry& fstab_entry) const; + const std::string& avb_version() const { return avb_version_; } const VBMetaInfo& vbmeta_info() const { return vbmeta_info_; } AvbHandleStatus status() const { return status_; } diff --git a/fs_mgr/libfs_avb/tests/avb_util_test.cpp b/fs_mgr/libfs_avb/tests/avb_util_test.cpp index 835410f78..e4213b7b6 100644 --- a/fs_mgr/libfs_avb/tests/avb_util_test.cpp +++ b/fs_mgr/libfs_avb/tests/avb_util_test.cpp @@ -30,6 +30,7 @@ using android::fs_mgr::AvbPartitionToDevicePatition; using android::fs_mgr::DeriveAvbPartitionName; using android::fs_mgr::FstabEntry; using android::fs_mgr::GetAvbFooter; +using android::fs_mgr::GetAvbPropertyDescriptor; using android::fs_mgr::GetChainPartitionInfo; using android::fs_mgr::GetTotalSize; using android::fs_mgr::LoadAndVerifyVbmetaByPartition; @@ -268,6 +269,67 @@ TEST_F(AvbUtilTest, GetAvbFooterInsufficientSize) { EXPECT_EQ(nullptr, footer); } +TEST_F(AvbUtilTest, GetAvbPropertyDescriptor_Basic) { + // Makes a vbmeta.img with some properties. + GenerateVBMetaImage("vbmeta.img", "SHA256_RSA4096", 0, data_dir_.Append("testkey_rsa4096.pem"), + {}, /* include_descriptor_image_paths */ + {}, /* chain_partitions */ + "--prop foo:android " + "--prop bar:treble " + "--internal_release_string \"unit test\" "); + auto vbmeta = LoadVBMetaData("vbmeta.img"); + + // Puts the vbmeta into a vector, for GetAvbPropertyDescriptor to use. + std::vector vbmeta_images; + vbmeta_images.emplace_back(std::move(vbmeta)); + + EXPECT_EQ("android", GetAvbPropertyDescriptor("foo", vbmeta_images)); + EXPECT_EQ("treble", GetAvbPropertyDescriptor("bar", vbmeta_images)); + EXPECT_EQ("", GetAvbPropertyDescriptor("non-existent", vbmeta_images)); +} + +TEST_F(AvbUtilTest, GetAvbPropertyDescriptor_SecurityPatchLevel) { + // Generates a raw boot.img + const size_t boot_image_size = 5 * 1024 * 1024; + const size_t boot_partition_size = 10 * 1024 * 1024; + base::FilePath boot_path = GenerateImage("boot.img", boot_image_size); + // Adds AVB Hash Footer. + AddAvbFooter(boot_path, "hash", "boot", boot_partition_size, "SHA256_RSA2048", 10, + data_dir_.Append("testkey_rsa2048.pem"), "d00df00d", + "--internal_release_string \"unit test\""); + + // Generates a raw system.img, use a smaller size to speed-up unit test. + const size_t system_image_size = 10 * 1024 * 1024; + const size_t system_partition_size = 15 * 1024 * 1024; + base::FilePath system_path = GenerateImage("system.img", system_image_size); + // Adds AVB Hashtree Footer. + AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA4096", 20, + data_dir_.Append("testkey_rsa4096.pem"), "d00df00d", + "--prop com.android.build.system.security_patch:2019-04-05 " + "--internal_release_string \"unit test\""); + + // Generates chain partition descriptors. + base::FilePath rsa4096_public_key = + ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem")); + + // Makes a vbmeta.img including the 'system' chained descriptor. + GenerateVBMetaImage("vbmeta.img", "SHA256_RSA4096", 0, data_dir_.Append("testkey_rsa4096.pem"), + {boot_path}, /* include_descriptor_image_paths */ + {{"system", 3, rsa4096_public_key}}, /* chain_partitions */ + "--internal_release_string \"unit test\""); + + auto vbmeta = LoadVBMetaData("vbmeta.img"); + auto system_vbmeta = ExtractAndLoadVBMetaData(system_path, "system-vbmeta.img"); + + // Puts the vbmeta into a vector, for GetAvbPropertyDescriptor to use. + std::vector vbmeta_images; + vbmeta_images.emplace_back(std::move(vbmeta)); + vbmeta_images.emplace_back(std::move(system_vbmeta)); + + EXPECT_EQ("2019-04-05", + GetAvbPropertyDescriptor("com.android.build.system.security_patch", vbmeta_images)); +} + TEST_F(AvbUtilTest, GetVBMetaHeader) { // Generates a raw boot.img const size_t image_size = 5 * 1024 * 1024; diff --git a/fs_mgr/libfs_avb/tests/fs_avb_device_test.cpp b/fs_mgr/libfs_avb/tests/fs_avb_device_test.cpp index fc4eb5f8e..4631330cf 100644 --- a/fs_mgr/libfs_avb/tests/fs_avb_device_test.cpp +++ b/fs_mgr/libfs_avb/tests/fs_avb_device_test.cpp @@ -15,6 +15,7 @@ */ #include +#include #include #include #include @@ -22,6 +23,8 @@ #include #include +using android::fs_mgr::AvbHandle; +using android::fs_mgr::AvbHandleStatus; using android::fs_mgr::Fstab; using android::fs_mgr::FstabEntry; using android::fs_mgr::VBMetaData; @@ -31,7 +34,7 @@ namespace fs_avb_device_test { // system vbmeta might not be at the end of /system when dynamic partition is // enabled. Therefore, disable it by default. -TEST(PublicFsAvbDeviceTest, DISABLED_LoadAndVerifyVbmeta_SystemVbmeta) { +TEST(FsAvbUtilTest, DISABLED_LoadAndVerifyVbmeta_SystemVbmeta) { Fstab fstab; EXPECT_TRUE(ReadDefaultFstab(&fstab)); @@ -51,7 +54,7 @@ TEST(PublicFsAvbDeviceTest, DISABLED_LoadAndVerifyVbmeta_SystemVbmeta) { EXPECT_NE("", out_public_key_data); } -TEST(PublicFsAvbDeviceTest, GetHashtreeDescriptor_SystemOther) { +TEST(FsAvbUtilTest, GetHashtreeDescriptor_SystemOther) { // Non-A/B device doesn't have system_other partition. if (fs_mgr_get_slot_suffix() == "") return; @@ -90,4 +93,55 @@ TEST(PublicFsAvbDeviceTest, GetHashtreeDescriptor_SystemOther) { EXPECT_NE(nullptr, hashtree_desc); } +TEST(AvbHandleTest, LoadAndVerifyVbmeta_SystemOther) { + // Non-A/B device doesn't have system_other partition. + if (fs_mgr_get_slot_suffix() == "") return; + + // Skip running this test if system_other is a logical partition. + // Note that system_other is still a physical partition on "retrofit" devices. + if (android::base::GetBoolProperty("ro.boot.dynamic_partitions", false) && + !android::base::GetBoolProperty("ro.boot.dynamic_partitions_retrofit", false)) { + return; + } + + Fstab fstab; + EXPECT_TRUE(ReadFstabFromFile("/system/etc/fstab.postinstall", &fstab)); + + // It should have two lines in the fstab, the first for logical system_other, + // the other for physical system_other. + EXPECT_EQ(2UL, fstab.size()); + + // Use the 2nd fstab entry, which is for physical system_other partition. + FstabEntry* system_other_entry = &fstab[1]; + // Assign the default key if it's not specified in the fstab. + if (system_other_entry->avb_key.empty()) { + system_other_entry->avb_key = "/system/etc/security/avb/system_other.avbpubkey"; + } + auto avb_handle = AvbHandle::LoadAndVerifyVbmeta(*system_other_entry); + EXPECT_NE(nullptr, avb_handle) << "Failed to load system_other vbmeta. Try 'adb root'?"; + EXPECT_EQ(AvbHandleStatus::kSuccess, avb_handle->status()); +} + +TEST(AvbHandleTest, GetSecurityPatchLevel) { + Fstab fstab; + EXPECT_TRUE(ReadDefaultFstab(&fstab)); + + auto avb_handle = AvbHandle::LoadAndVerifyVbmeta(); + EXPECT_NE(nullptr, avb_handle) << "Failed to load inline vbmeta. Try 'adb root'?"; + EXPECT_EQ(AvbHandleStatus::kSuccess, avb_handle->status()); + + // Gets security patch level with format: YYYY-MM-DD (e.g., 2019-04-05). + FstabEntry* system_entry = GetEntryForMountPoint(&fstab, "/system"); + EXPECT_NE(nullptr, system_entry); + EXPECT_EQ(10UL, avb_handle->GetSecurityPatchLevel(*system_entry).length()); + + FstabEntry* vendor_entry = GetEntryForMountPoint(&fstab, "/vendor"); + EXPECT_NE(nullptr, vendor_entry); + EXPECT_EQ(10UL, avb_handle->GetSecurityPatchLevel(*vendor_entry).length()); + + FstabEntry* product_entry = GetEntryForMountPoint(&fstab, "/product"); + EXPECT_NE(nullptr, product_entry); + EXPECT_EQ(10UL, avb_handle->GetSecurityPatchLevel(*product_entry).length()); +} + } // namespace fs_avb_device_test diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp index 898e28e99..6e55c11a2 100644 --- a/init/first_stage_mount.cpp +++ b/init/first_stage_mount.cpp @@ -189,6 +189,29 @@ static bool GetRootEntry(FstabEntry* root_entry) { return true; } +static bool IsStandaloneImageRollback(const AvbHandle& builtin_vbmeta, + const AvbHandle& standalone_vbmeta, + const FstabEntry& fstab_entry) { + std::string old_spl = builtin_vbmeta.GetSecurityPatchLevel(fstab_entry); + std::string new_spl = standalone_vbmeta.GetSecurityPatchLevel(fstab_entry); + + bool rollbacked = false; + if (old_spl.empty() || new_spl.empty() || new_spl < old_spl) { + rollbacked = true; + } + + if (rollbacked) { + LOG(ERROR) << "Image rollback detected for " << fstab_entry.mount_point + << ", SPL switches from '" << old_spl << "' to '" << new_spl << "'"; + if (AvbHandle::IsDeviceUnlocked()) { + LOG(INFO) << "Allowing rollbacked standalone image when the device is unlocked"; + return false; + } + } + + return rollbacked; +} + // Class Definitions // ----------------- FirstStageMount::FirstStageMount(Fstab fstab) @@ -746,7 +769,15 @@ bool FirstStageMountVBootV2::SetUpDmVerity(FstabEntry* fstab_entry) { << fstab_entry->mount_point; return true; // Returns true to mount the partition directly. } else { - hashtree_result = AvbHandle::SetUpStandaloneAvbHashtree( + auto avb_standalone_handle = AvbHandle::LoadAndVerifyVbmeta(*fstab_entry); + if (!avb_standalone_handle) { + LOG(ERROR) << "Failed to load offline vbmeta for " << fstab_entry->mount_point; + return false; + } + 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 {