diff options
8 files changed, 24 insertions, 420 deletions
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java index c7d9b9c4ab3e..c8c1fd4eba21 100644 --- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java +++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java @@ -407,20 +407,6 @@ public class ApkSignatureSchemeV2Verifier { } } - static byte[] generateApkVerityRootHash(String apkPath) - throws IOException, SignatureNotFoundException, DigestException, - NoSuchAlgorithmException { - try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) { - SignatureInfo signatureInfo = findSignature(apk); - VerifiedSigner vSigner = verify(apk, false); - if (vSigner.verityRootHash == null) { - return null; - } - return VerityBuilder.generateApkVerityRootHash( - apk, ByteBuffer.wrap(vSigner.verityRootHash), signatureInfo); - } - } - /** * Verified APK Signature Scheme v2 signer. * diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java index 7e65d6117a9d..3287ce8636f5 100644 --- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java +++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java @@ -545,20 +545,6 @@ public class ApkSignatureSchemeV3Verifier { } } - static byte[] generateApkVerityRootHash(String apkPath) - throws NoSuchAlgorithmException, DigestException, IOException, - SignatureNotFoundException { - try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) { - SignatureInfo signatureInfo = findSignature(apk); - VerifiedSigner vSigner = verify(apk, false); - if (vSigner.verityRootHash == null) { - return null; - } - return VerityBuilder.generateApkVerityRootHash( - apk, ByteBuffer.wrap(vSigner.verityRootHash), signatureInfo); - } - } - /** * Verified APK Signature Scheme v3 signer, including the proof of rotation structure. * diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java index bff5426f9c64..d2a18dd84313 100644 --- a/core/java/android/util/apk/ApkSignatureVerifier.java +++ b/core/java/android/util/apk/ApkSignatureVerifier.java @@ -569,27 +569,6 @@ public class ApkSignatureVerifier { } /** - * Generates the FSVerity root hash from FSVerity header, extensions and Merkle tree root hash - * in Signing Block. - * - * @return FSverity root hash - */ - public static byte[] generateApkVerityRootHash(String apkPath) - throws NoSuchAlgorithmException, DigestException, IOException { - // first try v3 - try { - return ApkSignatureSchemeV3Verifier.generateApkVerityRootHash(apkPath); - } catch (SignatureNotFoundException e) { - // try older version - } - try { - return ApkSignatureSchemeV2Verifier.generateApkVerityRootHash(apkPath); - } catch (SignatureNotFoundException e) { - return null; - } - } - - /** * Extended signing details. * @hide for internal use only. */ diff --git a/core/java/android/util/apk/VerityBuilder.java b/core/java/android/util/apk/VerityBuilder.java index c7c465d30dad..adf53c213f8c 100644 --- a/core/java/android/util/apk/VerityBuilder.java +++ b/core/java/android/util/apk/VerityBuilder.java @@ -143,25 +143,6 @@ public abstract class VerityBuilder { return generateFsVerityTreeInternal(apk, salt, levelOffset, tree); } } - /** - * Calculates the apk-verity root hash for integrity measurement. This needs to be consistent - * to what kernel returns. - */ - @NonNull - static byte[] generateApkVerityRootHash(@NonNull RandomAccessFile apk, - @NonNull ByteBuffer apkDigest, @NonNull SignatureInfo signatureInfo) - throws NoSuchAlgorithmException, DigestException, IOException { - assertSigningBlockAlignedAndHasFullPages(signatureInfo); - - ByteBuffer footer = ByteBuffer.allocate(CHUNK_SIZE_BYTES).order(ByteOrder.LITTLE_ENDIAN); - generateApkVerityFooter(apk, signatureInfo, footer); - footer.flip(); - - MessageDigest md = MessageDigest.getInstance(JCA_DIGEST_ALGORITHM); - md.update(footer); - md.update(apkDigest); - return md.digest(); - } /** * Generates the apk-verity header and hash tree to be used by kernel for the given apk. This diff --git a/core/java/com/android/internal/security/VerityUtils.java b/core/java/com/android/internal/security/VerityUtils.java index 877026777ce3..76f7b2180b34 100644 --- a/core/java/com/android/internal/security/VerityUtils.java +++ b/core/java/com/android/internal/security/VerityUtils.java @@ -18,28 +18,15 @@ package com.android.internal.security; import android.annotation.NonNull; import android.os.Build; -import android.os.SharedMemory; import android.os.SystemProperties; -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; -import android.util.apk.ByteBufferFactory; -import android.util.apk.SignatureNotFoundException; - -import libcore.util.HexEncoding; import java.io.File; -import java.io.FileDescriptor; import java.io.IOException; -import java.nio.ByteBuffer; import java.nio.file.Files; import java.nio.file.Paths; -import java.security.DigestException; -import java.security.NoSuchAlgorithmException; -import java.util.Arrays; /** Provides fsverity related operations. */ public abstract class VerityUtils { @@ -57,8 +44,6 @@ public abstract class VerityUtils { /** SHA256 hash size. */ private static final int HASH_SIZE_BYTES = 32; - private static final boolean DEBUG = false; - public static boolean isFsVeritySupported() { return Build.VERSION.DEVICE_INITIAL_SDK_INT >= Build.VERSION_CODES.R || SystemProperties.getInt("ro.apk_verity.mode", 0) == 2; @@ -123,204 +108,4 @@ public abstract class VerityUtils { private static native int measureFsverityNative(@NonNull String filePath, @NonNull byte[] digest); private static native int statxForFsverityNative(@NonNull String filePath); - - /** - * Generates legacy Merkle tree and fs-verity metadata with Signing Block skipped. - * - * @deprecated This is only used for previous fs-verity implementation, and should never be used - * on new devices. - * @return {@code SetupResult} that contains the result code, and when success, the - * {@code FileDescriptor} to read all the data from. - */ - @Deprecated - public static SetupResult generateApkVeritySetupData(@NonNull String apkPath) { - if (DEBUG) { - Slog.d(TAG, "Trying to install legacy apk verity to " + apkPath); - } - SharedMemory shm = null; - try { - final byte[] signedVerityHash = ApkSignatureVerifier.getVerityRootHash(apkPath); - if (signedVerityHash == null) { - if (DEBUG) { - Slog.d(TAG, "Skip verity tree generation since there is no signed root hash"); - } - return SetupResult.skipped(); - } - - Pair<SharedMemory, Integer> result = - generateFsVerityIntoSharedMemory(apkPath, signedVerityHash); - shm = result.first; - int contentSize = result.second; - FileDescriptor rfd = shm.getFileDescriptor(); - if (rfd == null || !rfd.valid()) { - return SetupResult.failed(); - } - return SetupResult.ok(Os.dup(rfd), contentSize); - } catch (IOException | SecurityException | DigestException | NoSuchAlgorithmException - | SignatureNotFoundException | ErrnoException e) { - Slog.e(TAG, "Failed to set up apk verity: ", e); - return SetupResult.failed(); - } finally { - if (shm != null) { - shm.close(); - } - } - } - - /** - * {@see ApkSignatureVerifier#generateApkVerityRootHash(String)}. - * @deprecated This is only used for previous fs-verity implementation, and should never be used - * on new devices. - */ - @Deprecated - public static byte[] generateApkVerityRootHash(@NonNull String apkPath) - throws NoSuchAlgorithmException, DigestException, IOException { - return ApkSignatureVerifier.generateApkVerityRootHash(apkPath); - } - - /** - * {@see ApkSignatureVerifier#getVerityRootHash(String)}. - * @deprecated This is only used for previous fs-verity implementation, and should never be used - * on new devices. - */ - @Deprecated - public static byte[] getVerityRootHash(@NonNull String apkPath) - throws IOException, SignatureNotFoundException { - return ApkSignatureVerifier.getVerityRootHash(apkPath); - } - - /** - * Returns a pair of {@code SharedMemory} and {@code Integer}. The {@code SharedMemory} contains - * Merkle tree and fsverity headers for the given apk, in the form that can immediately be used - * for fsverity setup. The data is aligned to the beginning of {@code SharedMemory}, and has - * length equals to the returned {@code Integer}. - */ - private static Pair<SharedMemory, Integer> generateFsVerityIntoSharedMemory(String apkPath, - @NonNull byte[] expectedRootHash) - throws IOException, DigestException, NoSuchAlgorithmException, - SignatureNotFoundException { - TrackedShmBufferFactory shmBufferFactory = new TrackedShmBufferFactory(); - byte[] generatedRootHash = - ApkSignatureVerifier.generateApkVerity(apkPath, shmBufferFactory); - // We only generate Merkle tree once here, so it's important to make sure the root hash - // matches the signed one in the apk. - if (!Arrays.equals(expectedRootHash, generatedRootHash)) { - throw new SecurityException("verity hash mismatch: " - + bytesToString(generatedRootHash) + " != " + bytesToString(expectedRootHash)); - } - - int contentSize = shmBufferFactory.getBufferLimit(); - SharedMemory shm = shmBufferFactory.releaseSharedMemory(); - if (shm == null) { - throw new IllegalStateException("Failed to generate verity tree into shared memory"); - } - if (!shm.setProtect(OsConstants.PROT_READ)) { - throw new SecurityException("Failed to set up shared memory correctly"); - } - return Pair.create(shm, contentSize); - } - - private static String bytesToString(byte[] bytes) { - return HexEncoding.encodeToString(bytes); - } - - /** - * @deprecated This is only used for previous fs-verity implementation, and should never be used - * on new devices. - */ - @Deprecated - public static class SetupResult { - /** Result code if verity is set up correctly. */ - private static final int RESULT_OK = 1; - - /** Result code if signature is not provided. */ - private static final int RESULT_SKIPPED = 2; - - /** Result code if the setup failed. */ - private static final int RESULT_FAILED = 3; - - private final int mCode; - private final FileDescriptor mFileDescriptor; - private final int mContentSize; - - /** @deprecated */ - @Deprecated - public static SetupResult ok(@NonNull FileDescriptor fileDescriptor, int contentSize) { - return new SetupResult(RESULT_OK, fileDescriptor, contentSize); - } - - /** @deprecated */ - @Deprecated - public static SetupResult skipped() { - return new SetupResult(RESULT_SKIPPED, null, -1); - } - - /** @deprecated */ - @Deprecated - public static SetupResult failed() { - return new SetupResult(RESULT_FAILED, null, -1); - } - - private SetupResult(int code, FileDescriptor fileDescriptor, int contentSize) { - this.mCode = code; - this.mFileDescriptor = fileDescriptor; - this.mContentSize = contentSize; - } - - public boolean isFailed() { - return mCode == RESULT_FAILED; - } - - public boolean isOk() { - return mCode == RESULT_OK; - } - - public @NonNull FileDescriptor getUnownedFileDescriptor() { - return mFileDescriptor; - } - - public int getContentSize() { - return mContentSize; - } - } - - /** A {@code ByteBufferFactory} that creates a shared memory backed {@code ByteBuffer}. */ - private static class TrackedShmBufferFactory implements ByteBufferFactory { - private SharedMemory mShm; - private ByteBuffer mBuffer; - - @Override - public ByteBuffer create(int capacity) { - try { - if (DEBUG) Slog.d(TAG, "Creating shared memory for apk verity"); - // NB: This method is supposed to be called once according to the contract with - // ApkSignatureSchemeV2Verifier. - if (mBuffer != null) { - throw new IllegalStateException("Multiple instantiation from this factory"); - } - mShm = SharedMemory.create("apkverity", capacity); - if (!mShm.setProtect(OsConstants.PROT_READ | OsConstants.PROT_WRITE)) { - throw new SecurityException("Failed to set protection"); - } - mBuffer = mShm.mapReadWrite(); - return mBuffer; - } catch (ErrnoException e) { - throw new SecurityException("Failed to set protection", e); - } - } - - public SharedMemory releaseSharedMemory() { - if (mBuffer != null) { - SharedMemory.unmap(mBuffer); - mBuffer = null; - } - SharedMemory tmp = mShm; - mShm = null; - return tmp; - } - - public int getBufferLimit() { - return mBuffer == null ? -1 : mBuffer.limit(); - } - } } diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index d98626fb73aa..b636c8e8df1c 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -168,10 +168,7 @@ import com.android.server.utils.WatchedLongSparseArray; import dalvik.system.VMRuntime; -import libcore.io.IoUtils; - import java.io.File; -import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.IOException; import java.security.DigestException; @@ -1788,9 +1785,7 @@ final class InstallPackageHelper { */ private void setUpFsVerityIfPossible(AndroidPackage pkg) throws Installer.InstallerException, PrepareFailure, IOException, DigestException, NoSuchAlgorithmException { - final boolean standardMode = PackageManagerServiceUtils.isApkVerityEnabled(); - final boolean legacyMode = PackageManagerServiceUtils.isLegacyApkVerityEnabled(); - if (!standardMode && !legacyMode) { + if (!PackageManagerServiceUtils.isApkVerityEnabled()) { return; } @@ -1801,36 +1796,24 @@ final class InstallPackageHelper { // Collect files we care for fs-verity setup. ArrayMap<String, String> fsverityCandidates = new ArrayMap<>(); - if (legacyMode) { - synchronized (mPm.mLock) { - final PackageSetting ps = mPm.mSettings.getPackageLPr(pkg.getPackageName()); - if (ps != null && ps.isPrivileged()) { - fsverityCandidates.put(pkg.getBaseApkPath(), null); - for (String splitPath : pkg.getSplitCodePaths()) { - fsverityCandidates.put(splitPath, null); - } - } - } - } else { - // NB: These files will become only accessible if the signing key is loaded in kernel's - // .fs-verity keyring. - fsverityCandidates.put(pkg.getBaseApkPath(), - VerityUtils.getFsveritySignatureFilePath(pkg.getBaseApkPath())); + // NB: These files will become only accessible if the signing key is loaded in kernel's + // .fs-verity keyring. + fsverityCandidates.put(pkg.getBaseApkPath(), + VerityUtils.getFsveritySignatureFilePath(pkg.getBaseApkPath())); - final String dmPath = DexMetadataHelper.buildDexMetadataPathForApk( - pkg.getBaseApkPath()); - if (new File(dmPath).exists()) { - fsverityCandidates.put(dmPath, VerityUtils.getFsveritySignatureFilePath(dmPath)); - } + final String dmPath = DexMetadataHelper.buildDexMetadataPathForApk( + pkg.getBaseApkPath()); + if (new File(dmPath).exists()) { + fsverityCandidates.put(dmPath, VerityUtils.getFsveritySignatureFilePath(dmPath)); + } - for (String path : pkg.getSplitCodePaths()) { - fsverityCandidates.put(path, VerityUtils.getFsveritySignatureFilePath(path)); + for (String path : pkg.getSplitCodePaths()) { + fsverityCandidates.put(path, VerityUtils.getFsveritySignatureFilePath(path)); - final String splitDmPath = DexMetadataHelper.buildDexMetadataPathForApk(path); - if (new File(splitDmPath).exists()) { - fsverityCandidates.put(splitDmPath, - VerityUtils.getFsveritySignatureFilePath(splitDmPath)); - } + final String splitDmPath = DexMetadataHelper.buildDexMetadataPathForApk(path); + if (new File(splitDmPath).exists()) { + fsverityCandidates.put(splitDmPath, + VerityUtils.getFsveritySignatureFilePath(splitDmPath)); } } @@ -1839,43 +1822,14 @@ final class InstallPackageHelper { final String filePath = entry.getKey(); final String signaturePath = entry.getValue(); - if (!legacyMode) { - // fs-verity is optional for now. Only set up if signature is provided. - if (new File(signaturePath).exists() && !VerityUtils.hasFsverity(filePath)) { - try { - VerityUtils.setUpFsverity(filePath, signaturePath); - } catch (IOException e) { - throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE, - "Failed to enable fs-verity: " + e); - } - } - continue; - } - - // In legacy mode, fs-verity can only be enabled by process with CAP_SYS_ADMIN. - final VerityUtils.SetupResult result = VerityUtils.generateApkVeritySetupData(filePath); - if (result.isOk()) { - if (Build.IS_DEBUGGABLE) Slog.i(TAG, "Enabling verity to " + filePath); - final FileDescriptor fd = result.getUnownedFileDescriptor(); + // fs-verity is optional for now. Only set up if signature is provided. + if (new File(signaturePath).exists() && !VerityUtils.hasFsverity(filePath)) { try { - final byte[] rootHash = VerityUtils.generateApkVerityRootHash(filePath); - try { - // A file may already have fs-verity, e.g. when reused during a split - // install. If the measurement succeeds, no need to attempt to set up. - mPm.mInstaller.assertFsverityRootHashMatches(packageName, filePath, - rootHash); - } catch (Installer.InstallerException e) { - mPm.mInstaller.installApkVerity(packageName, filePath, fd, - result.getContentSize()); - mPm.mInstaller.assertFsverityRootHashMatches(packageName, filePath, - rootHash); - } - } finally { - IoUtils.closeQuietly(fd); + VerityUtils.setUpFsverity(filePath, signaturePath); + } catch (IOException e) { + throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE, + "Failed to enable fs-verity: " + e); } - } else if (result.isFailed()) { - throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE, - "Failed to generate verity"); } } } @@ -3959,14 +3913,14 @@ final class InstallPackageHelper { */ private boolean canSkipForcedPackageVerification(AndroidPackage pkg) { final String packageName = pkg.getPackageName(); - if (!canSkipForcedApkVerification(packageName, pkg.getBaseApkPath())) { + if (!VerityUtils.hasFsverity(pkg.getBaseApkPath())) { return false; } // TODO: Allow base and splits to be verified individually. String[] splitCodePaths = pkg.getSplitCodePaths(); if (!ArrayUtils.isEmpty(splitCodePaths)) { for (int i = 0; i < splitCodePaths.length; i++) { - if (!canSkipForcedApkVerification(packageName, splitCodePaths[i])) { + if (!VerityUtils.hasFsverity(splitCodePaths[i])) { return false; } } @@ -3975,34 +3929,6 @@ final class InstallPackageHelper { } /** - * Returns if forced apk verification can be skipped, depending on current FSVerity setup and - * whether the apk contains signed root hash. Note that the signer's certificate still needs to - * match one in a trusted source, and should be done separately. - */ - private boolean canSkipForcedApkVerification(String packageName, String apkPath) { - if (!PackageManagerServiceUtils.isLegacyApkVerityEnabled()) { - return VerityUtils.hasFsverity(apkPath); - } - - try { - final byte[] rootHashObserved = VerityUtils.generateApkVerityRootHash(apkPath); - if (rootHashObserved == null) { - return false; // APK does not contain Merkle tree root hash. - } - synchronized (mPm.mInstallLock) { - // Returns whether the observed root hash matches what kernel has. - mPm.mInstaller.assertFsverityRootHashMatches(packageName, apkPath, - rootHashObserved); - return true; - } - } catch (Installer.InstallerException | IOException | DigestException - | NoSuchAlgorithmException e) { - Slog.w(TAG, "Error in fsverity check. Fallback to full apk verification.", e); - } - return false; - } - - /** * Clear the package profile if this was an upgrade and the package * version was updated. */ diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index 47be7e66db9e..c4389a741857 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -39,7 +39,6 @@ import com.android.server.SystemService; import dalvik.system.BlockGuard; import dalvik.system.VMRuntime; -import java.io.FileDescriptor; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -726,34 +725,6 @@ public class Installer extends SystemService { } } - /** - * Enables legacy apk-verity for an apk. - */ - public void installApkVerity(String packageName, String filePath, FileDescriptor verityInput, - int contentSize) throws InstallerException { - if (!checkBeforeRemote()) return; - BlockGuard.getVmPolicy().onPathAccess(filePath); - try { - mInstalld.installApkVerity(packageName, filePath, verityInput, contentSize); - } catch (Exception e) { - throw InstallerException.from(e); - } - } - - /** - * Checks if provided hash matches the file's fs-verity merkle tree root hash. - */ - public void assertFsverityRootHashMatches(String packageName, String filePath, - @NonNull byte[] expectedHash) throws InstallerException { - if (!checkBeforeRemote()) return; - BlockGuard.getVmPolicy().onPathAccess(filePath); - try { - mInstalld.assertFsverityRootHashMatches(packageName, filePath, expectedHash); - } catch (Exception e) { - throw InstallerException.from(e); - } - } - public boolean reconcileSecondaryDexFile(String apkPath, String packageName, int uid, String[] isas, @Nullable String volumeUuid, int flags) throws InstallerException { for (int i = 0; i < isas.length; i++) { diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java index 1d2b8290402b..dcc4386a0dc7 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java @@ -482,12 +482,6 @@ public class PackageManagerServiceUtils { /** Default is to not use fs-verity since it depends on kernel support. */ private static final int FSVERITY_DISABLED = 0; - /** - * Experimental implementation targeting priv apps, with Android specific kernel patches to - * extend fs-verity. - */ - private static final int FSVERITY_LEGACY = 1; - /** Standard fs-verity. */ private static final int FSVERITY_ENABLED = 2; @@ -498,10 +492,6 @@ public class PackageManagerServiceUtils { == FSVERITY_ENABLED; } - static boolean isLegacyApkVerityEnabled() { - return SystemProperties.getInt("ro.apk_verity.mode", FSVERITY_DISABLED) == FSVERITY_LEGACY; - } - /** Returns true to force apk verification if the package is considered privileged. */ static boolean isApkVerificationForced(@Nullable PackageSetting ps) { // TODO(b/154310064): re-enable. |