Remove dependency between apex_file and apex_preinstalled_data

The only reason that dependency was there is to get a trusted public key
when validating vbmeta of an apex. This responsibility is now delegated
to the caller of ValidateApexVerity.

Test: atest ApexTestCases
Bug: 165948777
Change-Id: Ibfff81c4dfd078546d3dcf3167ad624e8028a52b
Merged-In: Ibfff81c4dfd078546d3dcf3167ad624e8028a52b
(cherry picked from commit 814ca221e8862ad9db72838a23fa673c4201d8a0)
diff --git a/apexd/Android.bp b/apexd/Android.bp
index 4f0d765..e9e5470 100644
--- a/apexd/Android.bp
+++ b/apexd/Android.bp
@@ -207,6 +207,7 @@
   static: {
     whole_static_libs: ["libc++fs"],
   },
+  cpp_std: "experimental",
   shared: {
     static_libs: ["libc++fs"],
   },
diff --git a/apexd/apex_file.cpp b/apexd/apex_file.cpp
index 36fd7c9..282595a 100644
--- a/apexd/apex_file.cpp
+++ b/apexd/apex_file.cpp
@@ -23,6 +23,7 @@
 
 #include <filesystem>
 #include <fstream>
+#include <span>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -31,18 +32,16 @@
 #include <android-base/unique_fd.h>
 #include <google/protobuf/util/message_differencer.h>
 #include <libavb/libavb.h>
+#include <ziparchive/zip_archive.h>
 
 #include "apex_constants.h"
-#include "apex_preinstalled_data.h"
 #include "apexd_utils.h"
-#include "string_log.h"
 
 using android::base::borrowed_fd;
 using android::base::EndsWith;
 using android::base::Error;
 using android::base::ReadFullyAtOffset;
 using android::base::Result;
-using android::base::StartsWith;
 using android::base::unique_fd;
 using google::protobuf::util::MessageDifferencer;
 
@@ -148,7 +147,7 @@
   }
 
   return ApexFile(path, image_offset, image_size, std::move(*manifest), pubkey,
-                  isPathForBuiltinApexes(path), *fs_type);
+                  *fs_type);
 }
 
 // AVB-related code.
@@ -214,8 +213,10 @@
          memcmp(&public_key_content[0], key, length) == 0;
 }
 
-Result<void> verifyVbMetaSignature(const ApexFile& apex, const uint8_t* data,
-                                   size_t length) {
+// Verifies correctness of vbmeta and returns public key it was signed with.
+Result<std::span<const uint8_t>> verifyVbMetaSignature(const ApexFile& apex,
+                                                       const uint8_t* data,
+                                                       size_t length) {
   const uint8_t* pk;
   size_t pk_len;
   AvbVBMetaVerifyResult res;
@@ -236,31 +237,16 @@
       return Error() << "Error verifying " << apex.GetPath() << ": "
                      << "unsupported version";
     default:
-      return Errorf("Unknown vmbeta_image_verify return value");
+      return Error() << "Unknown vmbeta_image_verify return value : " << res;
   }
 
-  auto& instance = ApexPreinstalledData::GetInstance();
-  // TODO(b/165948777): extract into verifier.
-  Result<const std::string> public_key =
-      instance.GetPublicKey(apex.GetManifest().name());
-  if (public_key.ok()) {
-    // TODO(b/115718846)
-    // We need to decide whether we need rollback protection, and whether
-    // we can use the rollback protection provided by libavb.
-    if (!CompareKeys(pk, pk_len, *public_key)) {
-      return Error() << "Error verifying " << apex.GetPath() << ": "
-                     << "public key doesn't match the pre-installed one";
-    }
-  } else {
-    return public_key.error();
-  }
-  LOG(VERBOSE) << apex.GetPath() << ": public key matches.";
-  return {};
+  return std::span<const uint8_t>(pk, pk_len);
 }
 
 Result<std::unique_ptr<uint8_t[]>> verifyVbMeta(const ApexFile& apex,
                                                 const unique_fd& fd,
-                                                const AvbFooter& footer) {
+                                                const AvbFooter& footer,
+                                                const std::string& public_key) {
   if (footer.vbmeta_size > kVbMetaMaxSize) {
     return Errorf("VbMeta size in footer exceeds kVbMetaMaxSize.");
   }
@@ -272,12 +258,17 @@
     return ErrnoError() << "Couldn't read AVB meta-data";
   }
 
-  Result<void> st =
+  Result<std::span<const uint8_t>> st =
       verifyVbMetaSignature(apex, vbmeta_buf.get(), footer.vbmeta_size);
   if (!st.ok()) {
     return st.error();
   }
 
+  if (!CompareKeys(st->data(), st->size(), public_key)) {
+    return Error() << "Error verifying " << apex.GetPath() << " : "
+                   << "public key doesn't match the pre-installed one";
+  }
+
   return vbmeta_buf;
 }
 
@@ -329,7 +320,8 @@
 
 }  // namespace
 
-Result<ApexVerityData> ApexFile::VerifyApexVerity() const {
+Result<ApexVerityData> ApexFile::VerifyApexVerity(
+    const std::string& public_key) const {
   ApexVerityData verityData;
 
   unique_fd fd(open(GetPath().c_str(), O_RDONLY | O_CLOEXEC));
@@ -343,7 +335,7 @@
   }
 
   Result<std::unique_ptr<uint8_t[]>> vbmeta_data =
-      verifyVbMeta(*this, fd, **footer);
+      verifyVbMeta(*this, fd, **footer, public_key);
   if (!vbmeta_data.ok()) {
     return vbmeta_data.error();
   }
@@ -420,14 +412,5 @@
   return ReadDir(path, filter_fn);
 }
 
-bool isPathForBuiltinApexes(const std::string& path) {
-  for (const auto& dir : kApexPackageBuiltinDirs) {
-    if (StartsWith(path, dir)) {
-      return true;
-    }
-  }
-  return false;
-}
-
 }  // namespace apex
 }  // namespace android
diff --git a/apexd/apex_file.h b/apexd/apex_file.h
index 88914a9..8647807 100644
--- a/apexd/apex_file.h
+++ b/apexd/apex_file.h
@@ -23,9 +23,7 @@
 
 #include <android-base/result.h>
 #include <libavb/libavb.h>
-#include <ziparchive/zip_archive.h>
 
-#include "apex_constants.h"
 #include "apex_manifest.h"
 
 namespace android {
@@ -52,23 +50,22 @@
   size_t GetImageSize() const { return image_size_; }
   const ApexManifest& GetManifest() const { return manifest_; }
   const std::string& GetBundledPublicKey() const { return apex_pubkey_; }
-  bool IsBuiltin() const { return is_builtin_; }
   const std::string& GetFsType() const { return fs_type_; }
-  android::base::Result<ApexVerityData> VerifyApexVerity() const;
+  android::base::Result<ApexVerityData> VerifyApexVerity(
+      const std::string& public_key) const;
+  // TODO(b/165948777): this doesn't seem to belong to ApexFile?
   android::base::Result<void> VerifyManifestMatches(
       const std::string& mount_path) const;
 
  private:
   ApexFile(const std::string& apex_path, int32_t image_offset,
            size_t image_size, ApexManifest manifest,
-           const std::string& apex_pubkey, bool is_builtin,
-           const std::string& fs_type)
+           const std::string& apex_pubkey, const std::string& fs_type)
       : apex_path_(apex_path),
         image_offset_(image_offset),
         image_size_(image_size),
         manifest_(std::move(manifest)),
         apex_pubkey_(apex_pubkey),
-        is_builtin_(is_builtin),
         fs_type_(fs_type) {}
 
   std::string apex_path_;
@@ -76,17 +73,15 @@
   size_t image_size_;
   ApexManifest manifest_;
   std::string apex_pubkey_;
-  bool is_builtin_;
   std::string fs_type_;
 };
 
+// TODO(b/165948777): this doesn't seem to belong to apex_file.h
 android::base::Result<std::vector<std::string>> FindApexes(
     const std::vector<std::string>& paths);
 android::base::Result<std::vector<std::string>> FindApexFilesByName(
     const std::string& path);
 
-bool isPathForBuiltinApexes(const std::string& path);
-
 }  // namespace apex
 }  // namespace android
 
diff --git a/apexd/apex_file_test.cpp b/apexd/apex_file_test.cpp
index 8555e47..2cf7d5d 100644
--- a/apexd/apex_file_test.cpp
+++ b/apexd/apex_file_test.cpp
@@ -26,7 +26,6 @@
 #include <ziparchive/zip_archive.h>
 
 #include "apex_file.h"
-#include "apex_preinstalled_data.h"
 
 using android::base::Result;
 
@@ -93,13 +92,11 @@
 }
 
 TEST_P(ApexFileTest, VerifyApexVerity) {
-  ApexPreinstalledData& instance = ApexPreinstalledData::GetInstance();
-  ASSERT_RESULT_OK(instance.Initialize({"/system_ext/apex"}));
   const std::string filePath = testDataDir + GetParam().prefix + ".apex";
   Result<ApexFile> apexFile = ApexFile::Open(filePath);
   ASSERT_RESULT_OK(apexFile);
 
-  auto verity_or = apexFile->VerifyApexVerity();
+  auto verity_or = apexFile->VerifyApexVerity(apexFile->GetBundledPublicKey());
   ASSERT_RESULT_OK(verity_or);
 
   const ApexVerityData& data = *verity_or;
@@ -118,13 +115,12 @@
   EXPECT_EQ(std::string(rootDigest), data.root_digest);
 }
 
-TEST_P(ApexFileTest, VerifyApexVerityNoKeyInst) {
-  const std::string filePath =
-      testDataDir + GetParam().prefix + "_no_inst_key.apex";
+TEST_P(ApexFileTest, VerifyApexVerityWrongKey) {
+  const std::string filePath = testDataDir + GetParam().prefix + ".apex";
   Result<ApexFile> apexFile = ApexFile::Open(filePath);
   ASSERT_RESULT_OK(apexFile);
 
-  auto verity_or = apexFile->VerifyApexVerity();
+  auto verity_or = apexFile->VerifyApexVerity("wrong-key");
   ASSERT_FALSE(verity_or.ok());
 }
 
@@ -146,7 +142,7 @@
   const std::string apex_path = testDataDir + "corrupted_b146895998.apex";
   Result<ApexFile> apex = ApexFile::Open(apex_path);
   ASSERT_RESULT_OK(apex);
-  ASSERT_FALSE(apex->VerifyApexVerity());
+  ASSERT_FALSE(apex->VerifyApexVerity("ignored"));
 }
 
 TEST_P(ApexFileTest, RetrieveFsType) {
diff --git a/apexd/apex_preinstalled_data.cpp b/apexd/apex_preinstalled_data.cpp
index dea5336..827566f 100644
--- a/apexd/apex_preinstalled_data.cpp
+++ b/apexd/apex_preinstalled_data.cpp
@@ -110,5 +110,13 @@
   return data_.find(name) != data_.end();
 }
 
+bool ApexPreinstalledData::IsPreInstalledApex(const ApexFile& apex) const {
+  auto it = data_.find(apex.GetManifest().name());
+  if (it == data_.end()) {
+    return false;
+  }
+  return it->second.path == apex.GetPath();
+}
+
 }  // namespace apex
 }  // namespace android
diff --git a/apexd/apex_preinstalled_data.h b/apexd/apex_preinstalled_data.h
index 59aeebc..44ac32b 100644
--- a/apexd/apex_preinstalled_data.h
+++ b/apexd/apex_preinstalled_data.h
@@ -19,6 +19,7 @@
 #include <string>
 #include <unordered_map>
 #include <vector>
+#include "apex_file.h"
 
 #include <android-base/result.h>
 
@@ -60,6 +61,9 @@
   // |name|.
   bool HasPreInstalledVersion(const std::string& name) const;
 
+  // Checks if given |apex| is pre-installed.
+  bool IsPreInstalledApex(const ApexFile& apex) const;
+
  private:
   // Non-copyable && non-moveable.
   ApexPreinstalledData(const ApexPreinstalledData&) = delete;
diff --git a/apexd/apex_preinstalled_data_test.cpp b/apexd/apex_preinstalled_data_test.cpp
index 0e9de06..8efe68a 100644
--- a/apexd/apex_preinstalled_data_test.cpp
+++ b/apexd/apex_preinstalled_data_test.cpp
@@ -106,5 +106,29 @@
               HasSubstr("does not match with previously scanned key"));
 }
 
+TEST(ApexPreinstalledData, IsPreInstalledApex) {
+  // Prepare test data.
+  TemporaryDir td;
+  fs::copy(GetTestFile("apex.apexd_test.apex"), td.path);
+
+  ApexPreinstalledData instance;
+  ASSERT_TRUE(IsOk(instance.Initialize({td.path})));
+
+  auto apex1 = ApexFile::Open(StringPrintf("%s/apex.apexd_test.apex", td.path));
+  ASSERT_TRUE(IsOk(apex1));
+  ASSERT_TRUE(instance.IsPreInstalledApex(*apex1));
+
+  // It's same apex, but path is different. Shouldn't be treated as
+  // pre-installed.
+  auto apex2 = ApexFile::Open(GetTestFile("apex.apexd_test.apex"));
+  ASSERT_TRUE(IsOk(apex2));
+  ASSERT_FALSE(instance.IsPreInstalledApex(*apex2));
+
+  auto apex3 =
+      ApexFile::Open(GetTestFile("apex.apexd_test_different_app.apex"));
+  ASSERT_TRUE(IsOk(apex3));
+  ASSERT_FALSE(instance.IsPreInstalledApex(*apex3));
+}
+
 }  // namespace apex
 }  // namespace android
diff --git a/apexd/apexd.cpp b/apexd/apexd.cpp
index 0a359e1..3bfaca8 100644
--- a/apexd/apexd.cpp
+++ b/apexd/apexd.cpp
@@ -406,7 +406,14 @@
   }
   LOG(VERBOSE) << "Loopback device created: " << loopbackDevice.name;
 
-  auto verityData = apex.VerifyApexVerity();
+  auto& instance = ApexPreinstalledData::GetInstance();
+
+  auto public_key = instance.GetPublicKey(apex.GetManifest().name());
+  if (!public_key.ok()) {
+    return public_key.error();
+  }
+
+  auto verityData = apex.VerifyApexVerity(*public_key);
   if (!verityData.ok()) {
     return Error() << "Failed to verify Apex Verity data for " << full_path
                    << ": " << verityData.error();
@@ -421,7 +428,7 @@
   // dm-verity because they are already in the dm-verity protected partition;
   // system. However, note that we don't skip verification to ensure that APEXes
   // are correctly signed.
-  const bool mountOnVerity = !isPathForBuiltinApexes(full_path);
+  const bool mountOnVerity = !instance.IsPreInstalledApex(apex);
   DmVerityDevice verityDev;
   loop::LoopbackDeviceUniqueFd loop_for_hash;
   if (mountOnVerity) {
@@ -675,7 +682,13 @@
 // This function should only verification checks that are necessary to run on
 // each boot. Try to avoid putting expensive checks inside this function.
 Result<void> VerifyPackageBoot(const ApexFile& apex_file) {
-  Result<ApexVerityData> verity_or = apex_file.VerifyApexVerity();
+  // TODO(ioffe): why do we need this here?
+  auto& instance = ApexPreinstalledData::GetInstance();
+  auto public_key = instance.GetPublicKey(apex_file.GetManifest().name());
+  if (!public_key.ok()) {
+    return public_key.error();
+  }
+  Result<ApexVerityData> verity_or = apex_file.VerifyApexVerity(*public_key);
   if (!verity_or.ok()) {
     return verity_or.error();
   }
@@ -701,7 +714,6 @@
   if (!verify_package_boot_status.ok()) {
     return verify_package_boot_status;
   }
-  Result<ApexVerityData> verity_or = apex_file.VerifyApexVerity();
 
   constexpr const auto kSuccessFn = [](const std::string& /*mount_point*/) {
     return Result<void>{};
@@ -1121,9 +1133,10 @@
   std::vector<com::android::apex::ApexInfo> apexInfos;
 
   auto convertToAutogen = [&apexInfos](const ApexFile& apex, bool isActive) {
+    auto& instance = ApexPreinstalledData::GetInstance();
+
     auto preinstalledPath =
-        ApexPreinstalledData::GetInstance().GetPreinstalledPath(
-            apex.GetManifest().name());
+        instance.GetPreinstalledPath(apex.GetManifest().name());
     std::optional<std::string> preinstalledModulePath;
     if (preinstalledPath.ok()) {
       preinstalledModulePath = *preinstalledPath;
@@ -1131,7 +1144,7 @@
     com::android::apex::ApexInfo apexInfo(
         apex.GetManifest().name(), apex.GetPath(), preinstalledModulePath,
         apex.GetManifest().version(), apex.GetManifest().versionname(),
-        apex.IsBuiltin(), isActive);
+        instance.IsPreInstalledApex(apex), isActive);
     apexInfos.emplace_back(apexInfo);
   };
 
@@ -1832,11 +1845,12 @@
   LOG(DEBUG) << "unstagePackages() for " << Join(paths, ',');
 
   for (const std::string& path : paths) {
-    if (isPathForBuiltinApexes(path)) {
-      return Error() << "Can't uninstall pre-installed apex " << path;
+    auto apex = ApexFile::Open(path);
+    if (!apex.ok()) {
+      return apex.error();
     }
-    if (access(path.c_str(), F_OK) != 0) {
-      return ErrnoError() << "Can't access " << path;
+    if (ApexPreinstalledData::GetInstance().IsPreInstalledApex(*apex)) {
+      return Error() << "Can't uninstall pre-installed apex " << path;
     }
   }
 
diff --git a/apexd/apexd_verity_test.cpp b/apexd/apexd_verity_test.cpp
index 33fd782..f431bd3 100644
--- a/apexd/apexd_verity_test.cpp
+++ b/apexd/apexd_verity_test.cpp
@@ -25,7 +25,6 @@
 #include <gtest/gtest.h>
 
 #include "apex_file.h"
-#include "apex_preinstalled_data.h"
 #include "apexd_test_utils.h"
 #include "apexd_verity.h"
 
@@ -45,13 +44,11 @@
 }
 
 TEST(ApexdVerityTest, ReusesHashtree) {
-  ApexPreinstalledData& instance = ApexPreinstalledData::GetInstance();
-  ASSERT_TRUE(IsOk(instance.Initialize({"/system_ext/apex"})));
   TemporaryDir td;
 
   auto apex = ApexFile::Open(GetTestFile("apex.apexd_test_no_hashtree.apex"));
   ASSERT_TRUE(IsOk(apex));
-  auto verity_data = apex->VerifyApexVerity();
+  auto verity_data = apex->VerifyApexVerity(apex->GetBundledPublicKey());
   ASSERT_TRUE(IsOk(verity_data));
 
   auto hashtree_file = StringPrintf("%s/hashtree", td.path);
@@ -79,13 +76,11 @@
 }
 
 TEST(ApexdVerityTest, RegenerateHashree) {
-  ApexPreinstalledData& instance = ApexPreinstalledData::GetInstance();
-  ASSERT_TRUE(IsOk(instance.Initialize({"/system_ext/apex"})));
   TemporaryDir td;
 
   auto apex = ApexFile::Open(GetTestFile("apex.apexd_test_no_hashtree.apex"));
   ASSERT_TRUE(IsOk(apex));
-  auto verity_data = apex->VerifyApexVerity();
+  auto verity_data = apex->VerifyApexVerity(apex->GetBundledPublicKey());
   ASSERT_TRUE(IsOk(verity_data));
 
   auto hashtree_file = StringPrintf("%s/hashtree", td.path);
@@ -100,7 +95,7 @@
   auto apex2 =
       ApexFile::Open(GetTestFile("apex.apexd_test_no_hashtree_2.apex"));
   ASSERT_TRUE(IsOk(apex2));
-  auto verity_data2 = apex2->VerifyApexVerity();
+  auto verity_data2 = apex2->VerifyApexVerity(apex2->GetBundledPublicKey());
   ASSERT_TRUE(IsOk(verity_data2));
 
   // Now call PrepareHashTree again. Since digest doesn't match, hashtree
diff --git a/apexd/apexservice.cpp b/apexd/apexservice.cpp
index 75bead0..c900bb7 100644
--- a/apexd/apexservice.cpp
+++ b/apexd/apexservice.cpp
@@ -267,16 +267,16 @@
 }
 
 static ApexInfo getApexInfo(const ApexFile& package) {
+  auto& instance = ApexPreinstalledData::GetInstance();
   ApexInfo out;
   out.moduleName = package.GetManifest().name();
   out.modulePath = package.GetPath();
   out.versionCode = package.GetManifest().version();
   out.versionName = package.GetManifest().versionname();
-  out.isFactory = package.IsBuiltin();
+  out.isFactory = instance.IsPreInstalledApex(package);
   out.isActive = false;
   Result<std::string> preinstalledPath =
-      ApexPreinstalledData::GetInstance().GetPreinstalledPath(
-          package.GetManifest().name());
+      instance.GetPreinstalledPath(package.GetManifest().name());
   if (preinstalledPath.ok()) {
     out.preinstalledModulePath = *preinstalledPath;
   }
diff --git a/apexd/apexservice_test.cpp b/apexd/apexservice_test.cpp
index a66df5f..17c4438 100644
--- a/apexd/apexservice_test.cpp
+++ b/apexd/apexservice_test.cpp
@@ -1384,7 +1384,13 @@
   ASSERT_TRUE(factoryPackages->size() > 0);
 
   for (const ApexInfo& package : *factoryPackages) {
-    ASSERT_TRUE(isPathForBuiltinApexes(package.modulePath));
+    bool is_builtin = false;
+    for (const auto& dir : kApexPackageBuiltinDirs) {
+      if (StartsWith(package.modulePath, dir)) {
+        is_builtin = true;
+      }
+    }
+    ASSERT_TRUE(is_builtin);
   }
 }