installd: implement measureFsverityRootHash

Test: Observed to ioctl, will soon integrate with the actual ioctl
Bug: 30972906

Change-Id: I30729abbdc540093d2b33d0451a29c5bbf0fb83d
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 1627756..bc91285 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -18,6 +18,7 @@
 
 #define ATRACE_TAG ATRACE_TAG_PACKAGE_MANAGER
 
+#include <algorithm>
 #include <errno.h>
 #include <fstream>
 #include <fts.h>
@@ -90,6 +91,7 @@
 // fsverity assumes the page size is always 4096. If not, the feature can not be
 // enabled.
 static constexpr int kVerityPageSize = 4096;
+static constexpr size_t kSha256Size = 32;
 static constexpr const char* kPropApkVerityMode = "ro.apk_verity.mode";
 
 // NOTE: keep in sync with Installer
@@ -2357,6 +2359,14 @@
     __u64 flags;
 };
 
+struct fsverity_root_hash {
+    short root_hash_algorithm;
+    short flags;
+    __u8 reserved[4];
+    __u8 root_hash[64];
+};
+
+#define FS_IOC_MEASURE_FSVERITY        _IOW('f', 2733, struct fsverity_root_hash)
 #define FS_IOC_SET_FSVERITY            _IOW('f', 2734, struct fsverity_set)
 
 #define FSVERITY_FLAG_ENABLED          0x0001
@@ -2423,6 +2433,36 @@
     return ok();
 }
 
+binder::Status InstalldNativeService::assertFsverityRootHashMatches(const std::string& filePath,
+        const std::vector<uint8_t>& expectedHash) {
+    ENFORCE_UID(AID_SYSTEM);
+    if (!android::base::GetBoolProperty(kPropApkVerityMode, false)) {
+        return ok();
+    }
+    // TODO: also check fsverity support in the current file system if compiled with DEBUG.
+    if (expectedHash.size() != kSha256Size) {
+        return error("verity hash size should be " + std::to_string(kSha256Size) + " but is " +
+                     std::to_string(expectedHash.size()));
+    }
+
+    // TODO(71871109): Validate filePath.
+    ::android::base::unique_fd fd(open(filePath.c_str(), O_RDONLY));
+    if (fd.get() < 0) {
+        return error("Failed to open " + filePath + ": " + strerror(errno));
+    }
+
+    struct fsverity_root_hash config;
+    memset(&config, 0, sizeof(config));
+    config.root_hash_algorithm = 0;  // SHA256
+    memcpy(config.root_hash, expectedHash.data(), std::min(sizeof(config.root_hash), kSha256Size));
+    if (ioctl(fd.get(), FS_IOC_MEASURE_FSVERITY, &config) < 0) {
+        // This includes an expected failure case with no FSVerity setup. It normally happens when
+        // the apk does not contains the Merkle tree root hash.
+        return error("Failed to measure fsverity on " + filePath + ": " + strerror(errno));
+    }
+    return ok();  // hashes match
+}
+
 binder::Status InstalldNativeService::reconcileSecondaryDexFile(
         const std::string& dexPath, const std::string& packageName, int32_t uid,
         const std::vector<std::string>& isas, const std::unique_ptr<std::string>& volumeUuid,
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index cef62cd..887a2fc 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -120,6 +120,8 @@
             const std::unique_ptr<std::string>& outputPath);
     binder::Status installApkVerity(const std::string& filePath,
             const ::android::base::unique_fd& verityInput);
+    binder::Status assertFsverityRootHashMatches(const std::string& filePath,
+            const std::vector<uint8_t>& expectedHash);
     binder::Status reconcileSecondaryDexFile(const std::string& dexPath,
         const std::string& packageName, int32_t uid, const std::vector<std::string>& isa,
         const std::unique_ptr<std::string>& volumeUuid, int32_t storage_flag, bool* _aidl_return);
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index c819e98..3725fc0 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -82,6 +82,7 @@
     void deleteOdex(@utf8InCpp String apkPath, @utf8InCpp String instructionSet,
             @nullable @utf8InCpp String outputPath);
     void installApkVerity(@utf8InCpp String filePath, in FileDescriptor verityInput);
+    void assertFsverityRootHashMatches(@utf8InCpp String filePath, in byte[] expectedHash);
 
     boolean reconcileSecondaryDexFile(@utf8InCpp String dexPath, @utf8InCpp String pkgName,
         int uid, in @utf8InCpp String[] isas, @nullable @utf8InCpp String volume_uuid,