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,