Loading core/java/android/content/pm/PackageManager.java +10 −0 Original line number Diff line number Diff line Loading @@ -1368,6 +1368,14 @@ public abstract class PackageManager { */ public static final int INSTALL_FAILED_BAD_DEX_METADATA = -117; /** * Installation parse return code: this is passed in the * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if there is any signature problem. * * @hide */ public static final int INSTALL_FAILED_BAD_SIGNATURE = -118; /** @hide */ @IntDef(flag = true, prefix = { "DELETE_" }, value = { DELETE_KEEP_DATA, Loading Loading @@ -6243,6 +6251,7 @@ public abstract class PackageManager { case INSTALL_FAILED_ABORTED: return "INSTALL_FAILED_ABORTED"; case INSTALL_FAILED_BAD_DEX_METADATA: return "INSTALL_FAILED_BAD_DEX_METADATA"; case INSTALL_FAILED_MISSING_SPLIT: return "INSTALL_FAILED_MISSING_SPLIT"; case INSTALL_FAILED_BAD_SIGNATURE: return "INSTALL_FAILED_BAD_SIGNATURE"; default: return Integer.toString(status); } } Loading Loading @@ -6288,6 +6297,7 @@ public abstract class PackageManager { case INSTALL_PARSE_FAILED_MANIFEST_MALFORMED: return PackageInstaller.STATUS_FAILURE_INVALID; case INSTALL_PARSE_FAILED_MANIFEST_EMPTY: return PackageInstaller.STATUS_FAILURE_INVALID; case INSTALL_FAILED_BAD_DEX_METADATA: return PackageInstaller.STATUS_FAILURE_INVALID; case INSTALL_FAILED_BAD_SIGNATURE: return PackageInstaller.STATUS_FAILURE_INVALID; case INSTALL_FAILED_INTERNAL_ERROR: return PackageInstaller.STATUS_FAILURE; case INSTALL_FAILED_USER_RESTRICTED: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE; case INSTALL_FAILED_DUPLICATE_PERMISSION: return PackageInstaller.STATUS_FAILURE_CONFLICT; Loading services/core/java/com/android/server/pm/PackageInstallerSession.java +50 −11 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.server.pm; import static android.content.pm.PackageManager.INSTALL_FAILED_ABORTED; import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_SIGNATURE; import static android.content.pm.PackageManager.INSTALL_FAILED_CONTAINER_ERROR; import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR; Loading Loading @@ -106,6 +107,7 @@ import com.android.server.LocalServices; import com.android.server.pm.Installer.InstallerException; import com.android.server.pm.PackageInstallerService.PackageInstallObserverAdapter; import com.android.server.pm.dex.DexManager; import com.android.server.security.VerityUtils; import libcore.io.IoUtils; Loading Loading @@ -285,6 +287,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private final List<String> mResolvedNativeLibPaths = new ArrayList<>(); @GuardedBy("mLock") private File mInheritedFilesBase; @GuardedBy("mLock") private boolean mVerityFound; private static final FileFilter sAddedFilter = new FileFilter() { @Override Loading @@ -294,6 +298,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { if (file.isDirectory()) return false; if (file.getName().endsWith(REMOVE_SPLIT_MARKER_EXTENSION)) return false; if (DexMetadataHelper.isDexMetadataFile(file)) return false; if (VerityUtils.isFsveritySignatureFile(file)) return false; return true; } }; Loading Loading @@ -1362,6 +1367,16 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mResolvedStagedFiles.clear(); mResolvedInheritedFiles.clear(); // Partial installs must be consistent with existing install if (params.mode == SessionParams.MODE_INHERIT_EXISTING && (pkgInfo == null || pkgInfo.applicationInfo == null)) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "Missing existing base package"); } // Default to require only if existing base has fs-verity. mVerityFound = params.mode == SessionParams.MODE_INHERIT_EXISTING && VerityUtils.hasFsverity(pkgInfo.applicationInfo.getBaseCodePath()); try { resolveStageDirLocked(); } catch (IOException e) { Loading Loading @@ -1425,7 +1440,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } final File targetFile = new File(mResolvedStageDir, targetName); maybeRenameFile(addedFile, targetFile); resolveAndStageFile(addedFile, targetFile); // Base is coming from session if (apk.splitName == null) { Loading @@ -1433,8 +1448,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { baseApk = apk; } mResolvedStagedFiles.add(targetFile); final File dexMetadataFile = DexMetadataHelper.findDexMetadataForFile(addedFile); if (dexMetadataFile != null) { if (!FileUtils.isValidExtFilename(dexMetadataFile.getName())) { Loading @@ -1443,8 +1456,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } final File targetDexMetadataFile = new File(mResolvedStageDir, DexMetadataHelper.buildDexMetadataPathForApk(targetName)); mResolvedStagedFiles.add(targetDexMetadataFile); maybeRenameFile(dexMetadataFile, targetDexMetadataFile); resolveAndStageFile(dexMetadataFile, targetDexMetadataFile); } } Loading Loading @@ -1487,12 +1499,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } else { // Partial installs must be consistent with existing install if (pkgInfo == null || pkgInfo.applicationInfo == null) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "Missing existing base package for " + mPackageName); } final PackageLite existing; final ApkLite existingBase; ApplicationInfo appInfo = pkgInfo.applicationInfo; Loading Loading @@ -1612,6 +1618,39 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } private void resolveAndStageFile(File origFile, File targetFile) throws PackageManagerException { mResolvedStagedFiles.add(targetFile); maybeRenameFile(origFile, targetFile); final File originalSignature = new File( VerityUtils.getFsveritySignatureFilePath(origFile.getPath())); // Make sure .fsv_sig exists when it should, then resolve and stage it. if (originalSignature.exists()) { // mVerityFound can only change from false to true here during the staging loop. Since // all or none of files should have .fsv_sig, this should only happen in the first time // (or never), otherwise bail out. if (!mVerityFound) { mVerityFound = true; if (mResolvedStagedFiles.size() > 1) { throw new PackageManagerException(INSTALL_FAILED_BAD_SIGNATURE, "Some file is missing fs-verity signature"); } } } else { if (!mVerityFound) { return; } throw new PackageManagerException(INSTALL_FAILED_BAD_SIGNATURE, "Missing corresponding fs-verity signature to " + origFile); } final File stagedSignature = new File( VerityUtils.getFsveritySignatureFilePath(targetFile.getPath())); maybeRenameFile(originalSignature, stagedSignature); mResolvedStagedFiles.add(stagedSignature); } @GuardedBy("mLock") private void assertApkConsistentLocked(String tag, ApkLite apk) throws PackageManagerException { Loading services/core/java/com/android/server/pm/PackageManagerService.java +84 −38 Original line number Diff line number Diff line Loading @@ -16552,44 +16552,11 @@ public class PackageManagerService extends IPackageManager.Stub throw new PrepareFailure(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename"); } if (PackageManagerServiceUtils.isApkVerityEnabled()) { String apkPath = null; synchronized (mPackages) { // Note that if the attacker managed to skip verify setup, for example by tampering // with the package settings, upon reboot we will do full apk verification when // verity is not detected. final PackageSetting ps = mSettings.mPackages.get(pkgName); if (ps != null && ps.isPrivileged()) { apkPath = pkg.baseCodePath; } } if (apkPath != null) { final VerityUtils.SetupResult result = VerityUtils.generateApkVeritySetupData(apkPath, null /* signaturePath */, true /* skipSigningBlock */); if (result.isOk()) { if (Build.IS_DEBUGGABLE) Slog.i(TAG, "Enabling apk verity to " + apkPath); FileDescriptor fd = result.getUnownedFileDescriptor(); try { final byte[] signedRootHash = VerityUtils.generateApkVerityRootHash(apkPath); mInstaller.installApkVerity(apkPath, fd, result.getContentSize()); mInstaller.assertFsverityRootHashMatches(apkPath, signedRootHash); } catch (InstallerException | IOException | DigestException | NoSuchAlgorithmException e) { setUpFsVerityIfPossible(pkg); } catch (InstallerException | IOException | DigestException | NoSuchAlgorithmException e) { throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR, "Failed to set up verity: " + e); } finally { IoUtils.closeQuietly(fd); } } else if (result.isFailed()) { throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR, "Failed to generate verity"); } else { // Do nothing if verity is skipped. Will fall back to full apk verification on // reboot. } } } if (!instantApp) { Loading Loading @@ -16887,6 +16854,85 @@ public class PackageManagerService extends IPackageManager.Stub } } /** * Set up fs-verity for the given package if possible. This requires a feature flag of system * property to be enabled only if the kernel supports fs-verity. * * <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(PackageParser.Package pkg) throws InstallerException, PrepareFailure, IOException, DigestException, NoSuchAlgorithmException { if (!PackageManagerServiceUtils.isApkVerityEnabled()) { return; } final boolean legacyMode = PackageManagerServiceUtils.isLegacyApkVerityMode(); // Collect files we care for fs-verity setup. ArrayMap<String, String> fsverityCandidates = new ArrayMap<>(); if (legacyMode) { synchronized (mPackages) { final PackageSetting ps = mSettings.mPackages.get(pkg.packageName); if (ps != null && ps.isPrivileged()) { fsverityCandidates.put(pkg.baseCodePath, null); if (pkg.splitCodePaths != null) { for (String splitPath : pkg.splitCodePaths) { 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.baseCodePath, VerityUtils.getFsveritySignatureFilePath(pkg.baseCodePath)); final String dmPath = DexMetadataHelper.buildDexMetadataPathForApk(pkg.baseCodePath); if (new File(dmPath).exists()) { fsverityCandidates.put(dmPath, VerityUtils.getFsveritySignatureFilePath(dmPath)); } if (pkg.splitCodePaths != null) { for (String path : pkg.splitCodePaths) { fsverityCandidates.put(path, VerityUtils.getFsveritySignatureFilePath(path)); final String splitDmPath = DexMetadataHelper.buildDexMetadataPathForApk(path); if (new File(splitDmPath).exists()) { fsverityCandidates.put(splitDmPath, VerityUtils.getFsveritySignatureFilePath(splitDmPath)); } } } } for (Map.Entry<String, String> entry : fsverityCandidates.entrySet()) { final String filePath = entry.getKey(); final String signaturePath = entry.getValue(); final VerityUtils.SetupResult result = VerityUtils.generateApkVeritySetupData( filePath, signaturePath, legacyMode); if (result.isOk()) { if (Build.IS_DEBUGGABLE) Slog.i(TAG, "Enabling verity to " + filePath); final FileDescriptor fd = result.getUnownedFileDescriptor(); try { mInstaller.installApkVerity(filePath, fd, result.getContentSize()); // In legacy mode, fs-verity can only be enabled by process with CAP_SYS_ADMIN. if (legacyMode) { final byte[] rootHash = VerityUtils.generateApkVerityRootHash(filePath); mInstaller.assertFsverityRootHashMatches(filePath, rootHash); } } finally { IoUtils.closeQuietly(fd); } } else if (result.isFailed()) { throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE, "Failed to generate verity"); } } } private void startIntentFilterVerifications(int userId, boolean replacing, PackageParser.Package pkg) { if (mIntentFilterVerifierComponent == null) { services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +18 −1 Original line number Diff line number Diff line Loading @@ -543,9 +543,26 @@ 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; /** Returns true if APK Verity is enabled. */ static boolean isApkVerityEnabled() { return SystemProperties.getInt("ro.apk_verity.mode", 0) != 0; int mode = SystemProperties.getInt("ro.apk_verity.mode", FSVERITY_DISABLED); return mode == FSVERITY_LEGACY || mode == FSVERITY_ENABLED; } static boolean isLegacyApkVerityMode() { return SystemProperties.getInt("ro.apk_verity.mode", FSVERITY_DISABLED) == FSVERITY_LEGACY; } /** Returns true to force apk verification if the updated package (in /data) is a priv app. */ Loading services/core/java/com/android/server/security/VerityUtils.java +17 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import android.util.apk.VerityBuilder; import libcore.util.HexEncoding; import java.io.File; import java.io.FileDescriptor; import java.io.IOException; import java.io.RandomAccessFile; Loading @@ -49,11 +50,27 @@ import sun.security.pkcs.PKCS7; abstract public class VerityUtils { private static final String TAG = "VerityUtils"; /** * File extension of the signature file. For example, foo.apk.fsv_sig is the signature file of * foo.apk. */ public static final String FSVERITY_SIGNATURE_FILE_EXTENSION = ".fsv_sig"; /** The maximum size of signature file. This is just to avoid potential abuse. */ private static final int MAX_SIGNATURE_FILE_SIZE_BYTES = 8192; private static final boolean DEBUG = false; /** Returns true if the given file looks like containing an fs-verity signature. */ public static boolean isFsveritySignatureFile(File file) { return file.getName().endsWith(FSVERITY_SIGNATURE_FILE_EXTENSION); } /** Returns the fs-verity signature file path of the given file. */ public static String getFsveritySignatureFilePath(String filePath) { return filePath + FSVERITY_SIGNATURE_FILE_EXTENSION; } /** 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, Loading Loading
core/java/android/content/pm/PackageManager.java +10 −0 Original line number Diff line number Diff line Loading @@ -1368,6 +1368,14 @@ public abstract class PackageManager { */ public static final int INSTALL_FAILED_BAD_DEX_METADATA = -117; /** * Installation parse return code: this is passed in the * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if there is any signature problem. * * @hide */ public static final int INSTALL_FAILED_BAD_SIGNATURE = -118; /** @hide */ @IntDef(flag = true, prefix = { "DELETE_" }, value = { DELETE_KEEP_DATA, Loading Loading @@ -6243,6 +6251,7 @@ public abstract class PackageManager { case INSTALL_FAILED_ABORTED: return "INSTALL_FAILED_ABORTED"; case INSTALL_FAILED_BAD_DEX_METADATA: return "INSTALL_FAILED_BAD_DEX_METADATA"; case INSTALL_FAILED_MISSING_SPLIT: return "INSTALL_FAILED_MISSING_SPLIT"; case INSTALL_FAILED_BAD_SIGNATURE: return "INSTALL_FAILED_BAD_SIGNATURE"; default: return Integer.toString(status); } } Loading Loading @@ -6288,6 +6297,7 @@ public abstract class PackageManager { case INSTALL_PARSE_FAILED_MANIFEST_MALFORMED: return PackageInstaller.STATUS_FAILURE_INVALID; case INSTALL_PARSE_FAILED_MANIFEST_EMPTY: return PackageInstaller.STATUS_FAILURE_INVALID; case INSTALL_FAILED_BAD_DEX_METADATA: return PackageInstaller.STATUS_FAILURE_INVALID; case INSTALL_FAILED_BAD_SIGNATURE: return PackageInstaller.STATUS_FAILURE_INVALID; case INSTALL_FAILED_INTERNAL_ERROR: return PackageInstaller.STATUS_FAILURE; case INSTALL_FAILED_USER_RESTRICTED: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE; case INSTALL_FAILED_DUPLICATE_PERMISSION: return PackageInstaller.STATUS_FAILURE_CONFLICT; Loading
services/core/java/com/android/server/pm/PackageInstallerSession.java +50 −11 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.server.pm; import static android.content.pm.PackageManager.INSTALL_FAILED_ABORTED; import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_SIGNATURE; import static android.content.pm.PackageManager.INSTALL_FAILED_CONTAINER_ERROR; import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR; Loading Loading @@ -106,6 +107,7 @@ import com.android.server.LocalServices; import com.android.server.pm.Installer.InstallerException; import com.android.server.pm.PackageInstallerService.PackageInstallObserverAdapter; import com.android.server.pm.dex.DexManager; import com.android.server.security.VerityUtils; import libcore.io.IoUtils; Loading Loading @@ -285,6 +287,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private final List<String> mResolvedNativeLibPaths = new ArrayList<>(); @GuardedBy("mLock") private File mInheritedFilesBase; @GuardedBy("mLock") private boolean mVerityFound; private static final FileFilter sAddedFilter = new FileFilter() { @Override Loading @@ -294,6 +298,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { if (file.isDirectory()) return false; if (file.getName().endsWith(REMOVE_SPLIT_MARKER_EXTENSION)) return false; if (DexMetadataHelper.isDexMetadataFile(file)) return false; if (VerityUtils.isFsveritySignatureFile(file)) return false; return true; } }; Loading Loading @@ -1362,6 +1367,16 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mResolvedStagedFiles.clear(); mResolvedInheritedFiles.clear(); // Partial installs must be consistent with existing install if (params.mode == SessionParams.MODE_INHERIT_EXISTING && (pkgInfo == null || pkgInfo.applicationInfo == null)) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "Missing existing base package"); } // Default to require only if existing base has fs-verity. mVerityFound = params.mode == SessionParams.MODE_INHERIT_EXISTING && VerityUtils.hasFsverity(pkgInfo.applicationInfo.getBaseCodePath()); try { resolveStageDirLocked(); } catch (IOException e) { Loading Loading @@ -1425,7 +1440,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } final File targetFile = new File(mResolvedStageDir, targetName); maybeRenameFile(addedFile, targetFile); resolveAndStageFile(addedFile, targetFile); // Base is coming from session if (apk.splitName == null) { Loading @@ -1433,8 +1448,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { baseApk = apk; } mResolvedStagedFiles.add(targetFile); final File dexMetadataFile = DexMetadataHelper.findDexMetadataForFile(addedFile); if (dexMetadataFile != null) { if (!FileUtils.isValidExtFilename(dexMetadataFile.getName())) { Loading @@ -1443,8 +1456,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } final File targetDexMetadataFile = new File(mResolvedStageDir, DexMetadataHelper.buildDexMetadataPathForApk(targetName)); mResolvedStagedFiles.add(targetDexMetadataFile); maybeRenameFile(dexMetadataFile, targetDexMetadataFile); resolveAndStageFile(dexMetadataFile, targetDexMetadataFile); } } Loading Loading @@ -1487,12 +1499,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } else { // Partial installs must be consistent with existing install if (pkgInfo == null || pkgInfo.applicationInfo == null) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "Missing existing base package for " + mPackageName); } final PackageLite existing; final ApkLite existingBase; ApplicationInfo appInfo = pkgInfo.applicationInfo; Loading Loading @@ -1612,6 +1618,39 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } private void resolveAndStageFile(File origFile, File targetFile) throws PackageManagerException { mResolvedStagedFiles.add(targetFile); maybeRenameFile(origFile, targetFile); final File originalSignature = new File( VerityUtils.getFsveritySignatureFilePath(origFile.getPath())); // Make sure .fsv_sig exists when it should, then resolve and stage it. if (originalSignature.exists()) { // mVerityFound can only change from false to true here during the staging loop. Since // all or none of files should have .fsv_sig, this should only happen in the first time // (or never), otherwise bail out. if (!mVerityFound) { mVerityFound = true; if (mResolvedStagedFiles.size() > 1) { throw new PackageManagerException(INSTALL_FAILED_BAD_SIGNATURE, "Some file is missing fs-verity signature"); } } } else { if (!mVerityFound) { return; } throw new PackageManagerException(INSTALL_FAILED_BAD_SIGNATURE, "Missing corresponding fs-verity signature to " + origFile); } final File stagedSignature = new File( VerityUtils.getFsveritySignatureFilePath(targetFile.getPath())); maybeRenameFile(originalSignature, stagedSignature); mResolvedStagedFiles.add(stagedSignature); } @GuardedBy("mLock") private void assertApkConsistentLocked(String tag, ApkLite apk) throws PackageManagerException { Loading
services/core/java/com/android/server/pm/PackageManagerService.java +84 −38 Original line number Diff line number Diff line Loading @@ -16552,44 +16552,11 @@ public class PackageManagerService extends IPackageManager.Stub throw new PrepareFailure(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename"); } if (PackageManagerServiceUtils.isApkVerityEnabled()) { String apkPath = null; synchronized (mPackages) { // Note that if the attacker managed to skip verify setup, for example by tampering // with the package settings, upon reboot we will do full apk verification when // verity is not detected. final PackageSetting ps = mSettings.mPackages.get(pkgName); if (ps != null && ps.isPrivileged()) { apkPath = pkg.baseCodePath; } } if (apkPath != null) { final VerityUtils.SetupResult result = VerityUtils.generateApkVeritySetupData(apkPath, null /* signaturePath */, true /* skipSigningBlock */); if (result.isOk()) { if (Build.IS_DEBUGGABLE) Slog.i(TAG, "Enabling apk verity to " + apkPath); FileDescriptor fd = result.getUnownedFileDescriptor(); try { final byte[] signedRootHash = VerityUtils.generateApkVerityRootHash(apkPath); mInstaller.installApkVerity(apkPath, fd, result.getContentSize()); mInstaller.assertFsverityRootHashMatches(apkPath, signedRootHash); } catch (InstallerException | IOException | DigestException | NoSuchAlgorithmException e) { setUpFsVerityIfPossible(pkg); } catch (InstallerException | IOException | DigestException | NoSuchAlgorithmException e) { throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR, "Failed to set up verity: " + e); } finally { IoUtils.closeQuietly(fd); } } else if (result.isFailed()) { throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR, "Failed to generate verity"); } else { // Do nothing if verity is skipped. Will fall back to full apk verification on // reboot. } } } if (!instantApp) { Loading Loading @@ -16887,6 +16854,85 @@ public class PackageManagerService extends IPackageManager.Stub } } /** * Set up fs-verity for the given package if possible. This requires a feature flag of system * property to be enabled only if the kernel supports fs-verity. * * <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(PackageParser.Package pkg) throws InstallerException, PrepareFailure, IOException, DigestException, NoSuchAlgorithmException { if (!PackageManagerServiceUtils.isApkVerityEnabled()) { return; } final boolean legacyMode = PackageManagerServiceUtils.isLegacyApkVerityMode(); // Collect files we care for fs-verity setup. ArrayMap<String, String> fsverityCandidates = new ArrayMap<>(); if (legacyMode) { synchronized (mPackages) { final PackageSetting ps = mSettings.mPackages.get(pkg.packageName); if (ps != null && ps.isPrivileged()) { fsverityCandidates.put(pkg.baseCodePath, null); if (pkg.splitCodePaths != null) { for (String splitPath : pkg.splitCodePaths) { 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.baseCodePath, VerityUtils.getFsveritySignatureFilePath(pkg.baseCodePath)); final String dmPath = DexMetadataHelper.buildDexMetadataPathForApk(pkg.baseCodePath); if (new File(dmPath).exists()) { fsverityCandidates.put(dmPath, VerityUtils.getFsveritySignatureFilePath(dmPath)); } if (pkg.splitCodePaths != null) { for (String path : pkg.splitCodePaths) { fsverityCandidates.put(path, VerityUtils.getFsveritySignatureFilePath(path)); final String splitDmPath = DexMetadataHelper.buildDexMetadataPathForApk(path); if (new File(splitDmPath).exists()) { fsverityCandidates.put(splitDmPath, VerityUtils.getFsveritySignatureFilePath(splitDmPath)); } } } } for (Map.Entry<String, String> entry : fsverityCandidates.entrySet()) { final String filePath = entry.getKey(); final String signaturePath = entry.getValue(); final VerityUtils.SetupResult result = VerityUtils.generateApkVeritySetupData( filePath, signaturePath, legacyMode); if (result.isOk()) { if (Build.IS_DEBUGGABLE) Slog.i(TAG, "Enabling verity to " + filePath); final FileDescriptor fd = result.getUnownedFileDescriptor(); try { mInstaller.installApkVerity(filePath, fd, result.getContentSize()); // In legacy mode, fs-verity can only be enabled by process with CAP_SYS_ADMIN. if (legacyMode) { final byte[] rootHash = VerityUtils.generateApkVerityRootHash(filePath); mInstaller.assertFsverityRootHashMatches(filePath, rootHash); } } finally { IoUtils.closeQuietly(fd); } } else if (result.isFailed()) { throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE, "Failed to generate verity"); } } } private void startIntentFilterVerifications(int userId, boolean replacing, PackageParser.Package pkg) { if (mIntentFilterVerifierComponent == null) {
services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +18 −1 Original line number Diff line number Diff line Loading @@ -543,9 +543,26 @@ 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; /** Returns true if APK Verity is enabled. */ static boolean isApkVerityEnabled() { return SystemProperties.getInt("ro.apk_verity.mode", 0) != 0; int mode = SystemProperties.getInt("ro.apk_verity.mode", FSVERITY_DISABLED); return mode == FSVERITY_LEGACY || mode == FSVERITY_ENABLED; } static boolean isLegacyApkVerityMode() { return SystemProperties.getInt("ro.apk_verity.mode", FSVERITY_DISABLED) == FSVERITY_LEGACY; } /** Returns true to force apk verification if the updated package (in /data) is a priv app. */ Loading
services/core/java/com/android/server/security/VerityUtils.java +17 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import android.util.apk.VerityBuilder; import libcore.util.HexEncoding; import java.io.File; import java.io.FileDescriptor; import java.io.IOException; import java.io.RandomAccessFile; Loading @@ -49,11 +50,27 @@ import sun.security.pkcs.PKCS7; abstract public class VerityUtils { private static final String TAG = "VerityUtils"; /** * File extension of the signature file. For example, foo.apk.fsv_sig is the signature file of * foo.apk. */ public static final String FSVERITY_SIGNATURE_FILE_EXTENSION = ".fsv_sig"; /** The maximum size of signature file. This is just to avoid potential abuse. */ private static final int MAX_SIGNATURE_FILE_SIZE_BYTES = 8192; private static final boolean DEBUG = false; /** Returns true if the given file looks like containing an fs-verity signature. */ public static boolean isFsveritySignatureFile(File file) { return file.getName().endsWith(FSVERITY_SIGNATURE_FILE_EXTENSION); } /** Returns the fs-verity signature file path of the given file. */ public static String getFsveritySignatureFilePath(String filePath) { return filePath + FSVERITY_SIGNATURE_FILE_EXTENSION; } /** 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, Loading