diff --git a/services/core/java/com/android/server/security/VerityUtils.java b/services/core/java/com/android/server/security/VerityUtils.java index 514dfed75843b..667d21dc59a0d 100644 --- a/services/core/java/com/android/server/security/VerityUtils.java +++ b/services/core/java/com/android/server/security/VerityUtils.java @@ -16,13 +16,11 @@ package com.android.server.security; -import static android.system.OsConstants.PROT_READ; -import static android.system.OsConstants.PROT_WRITE; - import android.annotation.NonNull; import android.os.SharedMemory; import android.system.ErrnoException; import android.system.Os; +import android.system.OsConstants; import android.util.Pair; import android.util.Slog; import android.util.apk.ApkSignatureVerifier; @@ -56,6 +54,21 @@ abstract public class VerityUtils { private static final boolean DEBUG = false; + /** Returns whether the file has fs-verity enabled. */ + public static boolean hasFsverity(@NonNull String filePath) { + // NB: only measure but not check the actual measurement here. As long as this succeeds, + // the file is on readable if the measurement can be verified against a trusted key, and + // this is good enough for installed apps. + int errno = measureFsverityNative(filePath); + if (errno != 0) { + if (errno != OsConstants.ENODATA) { + Slog.e(TAG, "Failed to measure fs-verity, errno " + errno + ": " + filePath); + } + return false; + } + return true; + } + /** * Generates Merkle tree and fs-verity metadata. * @@ -213,6 +226,7 @@ abstract public class VerityUtils { return md.digest(); } + private static native int measureFsverityNative(@NonNull String filePath); private static native byte[] constructFsveritySignedDataNative(@NonNull byte[] measurement); private static native byte[] constructFsverityDescriptorNative(long fileSize); private static native byte[] constructFsverityExtensionNative(short extensionId, @@ -249,7 +263,7 @@ abstract public class VerityUtils { if (shm == null) { throw new IllegalStateException("Failed to generate verity tree into shared memory"); } - if (!shm.setProtect(PROT_READ)) { + if (!shm.setProtect(OsConstants.PROT_READ)) { throw new SecurityException("Failed to set up shared memory correctly"); } return Pair.create(shm, contentSize); @@ -323,7 +337,7 @@ abstract public class VerityUtils { throw new IllegalStateException("Multiple instantiation from this factory"); } mShm = SharedMemory.create("apkverity", capacity); - if (!mShm.setProtect(PROT_READ | PROT_WRITE)) { + if (!mShm.setProtect(OsConstants.PROT_READ | OsConstants.PROT_WRITE)) { throw new SecurityException("Failed to set protection"); } mBuffer = mShm.mapReadWrite(); diff --git a/services/core/jni/com_android_server_security_VerityUtils.cpp b/services/core/jni/com_android_server_security_VerityUtils.cpp index ec94e3c92ed86..3c87e42d926b8 100644 --- a/services/core/jni/com_android_server_security_VerityUtils.cpp +++ b/services/core/jni/com_android_server_security_VerityUtils.cpp @@ -20,13 +20,22 @@ #include "jni.h" #include +#include +#include #include +#include +#include +#include + +#include // TODO(112037636): Always include once fsverity.h is upstreamed and backported. #define HAS_FSVERITY 0 #if HAS_FSVERITY #include + +const int kSha256Bytes = 32; #endif namespace android { @@ -66,9 +75,26 @@ class JavaByteArrayHolder { jbyte* mElements; }; +int measureFsverity(JNIEnv* env, jobject /* clazz */, jstring filePath) { +#if HAS_FSVERITY + auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_digest) + kSha256Bytes); + fsverity_digest* data = reinterpret_cast(raii->getRaw()); + data->digest_size = kSha256Bytes; // the only input/output parameter + + const char* path = env->GetStringUTFChars(filePath, nullptr); + ::android::base::unique_fd rfd(open(path, O_RDONLY)); + if (ioctl(rfd.get(), FS_IOC_MEASURE_VERITY, data) < 0) { + return errno; + } + return 0; +#else + LOG_ALWAYS_FATAL("fs-verity is used while not enabled"); + return -1; +#endif // HAS_FSVERITY +} + jbyteArray constructFsveritySignedData(JNIEnv* env, jobject /* clazz */, jbyteArray digest) { #if HAS_FSVERITY - const int kSha256Bytes = 32; auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_digest_disk) + kSha256Bytes); fsverity_digest_disk* data = reinterpret_cast(raii->getRaw()); @@ -146,6 +172,7 @@ jbyteArray constructFsverityFooter(JNIEnv* env, jobject /* clazz */, } const JNINativeMethod sMethods[] = { + { "measureFsverityNative", "(Ljava/lang/String;)I", (void *)measureFsverity }, { "constructFsveritySignedDataNative", "([B)[B", (void *)constructFsveritySignedData }, { "constructFsverityDescriptorNative", "(J)[B", (void *)constructFsverityDescriptor }, { "constructFsverityExtensionNative", "(SI)[B", (void *)constructFsverityExtension },