Enable fs-verity to all APKs on install

Previously, we only enable fs-verity to an APK if it comes with a
trusted signature (.fsv_sig). With this change, we'll enable fs-verity
in integrity-only mode if there's no signature.

The biggest benefit is O(1) measurement of the APK content, and can be
useful to some use cases.

Note that integrity-only does not imply security, since without a
signature, an attacker can also enable fs-verity on arbitrary files.

Bug: 249158715
Test: CtsAppSecurityHostTestCases:android.appsecurity.cts.ApkVerityInstallTest
Change-Id: I119e5189603af888dfa1ece2bee9e7635120854b
This commit is contained in:
Victor Hsieh
2022-10-05 17:14:06 -07:00
parent d86a626d26
commit 314dd4c075
3 changed files with 47 additions and 31 deletions

View File

@@ -17,6 +17,7 @@
package com.android.internal.security; package com.android.internal.security;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Build; import android.os.Build;
import android.os.SystemProperties; import android.os.SystemProperties;
import android.system.Os; import android.system.Os;
@@ -41,6 +42,7 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory; import java.security.cert.CertificateFactory;
@@ -77,17 +79,23 @@ public abstract class VerityUtils {
return filePath + FSVERITY_SIGNATURE_FILE_EXTENSION; return filePath + FSVERITY_SIGNATURE_FILE_EXTENSION;
} }
/** Enables fs-verity for the file with a PKCS#7 detached signature file. */ /** Enables fs-verity for the file with an optional PKCS#7 detached signature file. */
public static void setUpFsverity(@NonNull String filePath, @NonNull String signaturePath) public static void setUpFsverity(@NonNull String filePath, @Nullable String signaturePath)
throws IOException { throws IOException {
if (Files.size(Paths.get(signaturePath)) > MAX_SIGNATURE_FILE_SIZE_BYTES) { byte[] rawSignature = null;
throw new SecurityException("Signature file is unexpectedly large: " + signaturePath); if (signaturePath != null) {
Path path = Paths.get(signaturePath);
if (Files.size(path) > MAX_SIGNATURE_FILE_SIZE_BYTES) {
throw new SecurityException("Signature file is unexpectedly large: "
+ signaturePath);
} }
setUpFsverity(filePath, Files.readAllBytes(Paths.get(signaturePath))); rawSignature = Files.readAllBytes(path);
}
setUpFsverity(filePath, rawSignature);
} }
/** Enables fs-verity for the file with a PKCS#7 detached signature bytes. */ /** Enables fs-verity for the file with an optional PKCS#7 detached signature bytes. */
public static void setUpFsverity(@NonNull String filePath, @NonNull byte[] pkcs7Signature) public static void setUpFsverity(@NonNull String filePath, @Nullable byte[] pkcs7Signature)
throws IOException { throws IOException {
// This will fail if the public key is not already in .fs-verity kernel keyring. // This will fail if the public key is not already in .fs-verity kernel keyring.
int errno = enableFsverityNative(filePath, pkcs7Signature); int errno = enableFsverityNative(filePath, pkcs7Signature);
@@ -227,7 +235,7 @@ public abstract class VerityUtils {
} }
private static native int enableFsverityNative(@NonNull String filePath, private static native int enableFsverityNative(@NonNull String filePath,
@NonNull byte[] pkcs7Signature); @Nullable byte[] pkcs7Signature);
private static native int measureFsverityNative(@NonNull String filePath, private static native int measureFsverityNative(@NonNull String filePath,
@NonNull byte[] digest); @NonNull byte[] digest);
private static native int statxForFsverityNative(@NonNull String filePath); private static native int statxForFsverityNative(@NonNull String filePath);

View File

@@ -48,10 +48,6 @@ int enableFsverity(JNIEnv *env, jobject /* clazz */, jstring filePath, jbyteArra
if (rfd.get() < 0) { if (rfd.get() < 0) {
return errno; return errno;
} }
ScopedByteArrayRO signature_bytes(env, signature);
if (signature_bytes.get() == nullptr) {
return EINVAL;
}
fsverity_enable_arg arg = {}; fsverity_enable_arg arg = {};
arg.version = 1; arg.version = 1;
@@ -59,8 +55,18 @@ int enableFsverity(JNIEnv *env, jobject /* clazz */, jstring filePath, jbyteArra
arg.block_size = 4096; arg.block_size = 4096;
arg.salt_size = 0; arg.salt_size = 0;
arg.salt_ptr = reinterpret_cast<uintptr_t>(nullptr); arg.salt_ptr = reinterpret_cast<uintptr_t>(nullptr);
if (signature != nullptr) {
ScopedByteArrayRO signature_bytes(env, signature);
if (signature_bytes.get() == nullptr) {
return EINVAL;
}
arg.sig_size = signature_bytes.size(); arg.sig_size = signature_bytes.size();
arg.sig_ptr = reinterpret_cast<uintptr_t>(signature_bytes.get()); arg.sig_ptr = reinterpret_cast<uintptr_t>(signature_bytes.get());
} else {
arg.sig_size = 0;
arg.sig_ptr = reinterpret_cast<uintptr_t>(nullptr);
}
if (ioctl(rfd.get(), FS_IOC_ENABLE_VERITY, &arg) < 0) { if (ioctl(rfd.get(), FS_IOC_ENABLE_VERITY, &arg) < 0) {
return errno; return errno;

View File

@@ -1399,7 +1399,7 @@ final class InstallPackageHelper {
doRenameLI(request, parsedPackage); doRenameLI(request, parsedPackage);
try { try {
setUpFsVerityIfPossible(parsedPackage); setUpFsVerity(parsedPackage);
} catch (Installer.InstallerException | IOException | DigestException } catch (Installer.InstallerException | IOException | DigestException
| NoSuchAlgorithmException e) { | NoSuchAlgorithmException e) {
throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR, throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,
@@ -1796,13 +1796,10 @@ final class InstallPackageHelper {
} }
/** /**
* Set up fs-verity for the given package if possible. This requires a feature flag of system * Set up fs-verity for the given package. For older devices that do not support fs-verity,
* property to be enabled only if the kernel supports fs-verity. * this is a no-op.
*
* <p>When the feature flag is set to legacy mode, only APK is supported (with some experimental
* kernel patches). In normal mode, all file format can be supported.
*/ */
private void setUpFsVerityIfPossible(AndroidPackage pkg) throws Installer.InstallerException, private void setUpFsVerity(AndroidPackage pkg) throws Installer.InstallerException,
PrepareFailure, IOException, DigestException, NoSuchAlgorithmException { PrepareFailure, IOException, DigestException, NoSuchAlgorithmException {
if (!PackageManagerServiceUtils.isApkVerityEnabled()) { if (!PackageManagerServiceUtils.isApkVerityEnabled()) {
return; return;
@@ -1837,20 +1834,25 @@ final class InstallPackageHelper {
} }
for (Map.Entry<String, String> entry : fsverityCandidates.entrySet()) { for (Map.Entry<String, String> entry : fsverityCandidates.entrySet()) {
final String filePath = entry.getKey();
final String signaturePath = entry.getValue();
// fs-verity is optional for now. Only set up if signature is provided.
if (new File(signaturePath).exists() && !VerityUtils.hasFsverity(filePath)) {
try { try {
VerityUtils.setUpFsverity(filePath, signaturePath); final String filePath = entry.getKey();
if (VerityUtils.hasFsverity(filePath)) {
continue;
}
// Set up fs-verity with optional signature.
final String signaturePath = entry.getValue();
String optionalSignaturePath = null;
if (new File(signaturePath).exists()) {
optionalSignaturePath = signaturePath;
}
VerityUtils.setUpFsverity(filePath, optionalSignaturePath);
} catch (IOException e) { } catch (IOException e) {
throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE, throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE,
"Failed to enable fs-verity: " + e); "Failed to enable fs-verity: " + e);
} }
} }
} }
}
private PackageFreezer freezePackageForInstall(String packageName, int installFlags, private PackageFreezer freezePackageForInstall(String packageName, int installFlags,
String killReason) { String killReason) {