Loading services/core/java/com/android/server/pm/PackageManagerService.java +20 −8 Original line number Diff line number Diff line Loading @@ -18819,9 +18819,11 @@ public class PackageManagerService extends IPackageManager.Stub final VersionInfo versionInfo = request.versionInfos.get(installPackageName); final boolean compareCompat = isCompatSignatureUpdateNeeded(versionInfo); final boolean compareRecover = isRecoverSignatureUpdateNeeded(versionInfo); final boolean isRollback = installArgs != null && installArgs.installReason == PackageManager.INSTALL_REASON_ROLLBACK; final boolean compatMatch = verifySignatures(signatureCheckPs, disabledPkgSetting, parsedPackage.getSigningDetails(), compareCompat, compareRecover); compareRecover, isRollback); // The new KeySets will be re-added later in the scanning process. if (compatMatch) { removeAppKeySetData = true; Loading Loading @@ -19791,6 +19793,7 @@ public class PackageManagerService extends IPackageManager.Stub final boolean fullApp = ((installFlags & PackageManager.INSTALL_FULL_APP) != 0); final boolean virtualPreload = ((installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0); final boolean isRollback = args.installReason == PackageManager.INSTALL_REASON_ROLLBACK; @ScanFlags int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE; if (args.move != null) { // moving a complete application; perform an initial scan on the new install location Loading Loading @@ -19971,7 +19974,8 @@ public class PackageManagerService extends IPackageManager.Stub parsedPackage); // We don't care about disabledPkgSetting on install for now. final boolean compatMatch = verifySignatures(signatureCheckPs, null, parsedPackage.getSigningDetails(), compareCompat, compareRecover); parsedPackage.getSigningDetails(), compareCompat, compareRecover, isRollback); // The new KeySets will be re-added later in the scanning process. if (compatMatch) { synchronized (mLock) { Loading Loading @@ -20248,17 +20252,25 @@ public class PackageManagerService extends IPackageManager.Stub + pkgName11); } } else { SigningDetails parsedPkgSigningDetails = parsedPackage.getSigningDetails(); SigningDetails oldPkgSigningDetails = oldPackage.getSigningDetails(); // default to original signature matching if (!parsedPackage.getSigningDetails().checkCapability( oldPackage.getSigningDetails(), if (!parsedPkgSigningDetails.checkCapability(oldPkgSigningDetails, SigningDetails.CertCapabilities.INSTALLED_DATA) && !oldPackage.getSigningDetails().checkCapability( parsedPackage.getSigningDetails(), && !oldPkgSigningDetails.checkCapability(parsedPkgSigningDetails, SigningDetails.CertCapabilities.ROLLBACK)) { // Allow the update to proceed if this is a rollback and the parsed // package's current signing key is the current signer or in the lineage // of the old package; this allows a rollback to a previously installed // version after an app's signing key has been rotated without requiring // the rollback capability on the previous signing key. if (!isRollback || !oldPkgSigningDetails.hasAncestorOrSelf( parsedPkgSigningDetails)) { throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "New package has a different signature: " + pkgName11); } } } // don't allow a system upgrade unless the upgrade hash matches if (oldPackage.getRestrictUpdateHash() != null && oldPackage.isSystem()) { services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +8 −1 Original line number Diff line number Diff line Loading @@ -624,7 +624,7 @@ public class PackageManagerServiceUtils { */ public static boolean verifySignatures(PackageSetting pkgSetting, PackageSetting disabledPkgSetting, PackageParser.SigningDetails parsedSignatures, boolean compareCompat, boolean compareRecover) boolean compareCompat, boolean compareRecover, boolean isRollback) throws PackageManagerException { final String packageName = pkgSetting.name; boolean compatMatch = false; Loading Loading @@ -658,6 +658,13 @@ public class PackageManagerServiceUtils { match = matchSignatureInSystem(pkgSetting, disabledPkgSetting); } if (!match && isRollback) { // Since a rollback can only be initiated for an APK previously installed on the // device allow rolling back to a previous signing key even if the rollback // capability has not been granted. match = pkgSetting.signatures.mSigningDetails.hasAncestorOrSelf(parsedSignatures); } if (!match) { throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package " + packageName + Loading services/core/java/com/android/server/rollback/Rollback.java +1 −0 Original line number Diff line number Diff line Loading @@ -563,6 +563,7 @@ class Rollback { params.setRequestDowngrade(true); params.setRequiredInstalledVersionCode( pkgRollbackInfo.getVersionRolledBackFrom().getLongVersionCode()); params.setInstallReason(PackageManager.INSTALL_REASON_ROLLBACK); if (isStaged()) { params.setStaged(); } Loading tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java +40 −0 Original line number Diff line number Diff line Loading @@ -1225,4 +1225,44 @@ public class RollbackTest { InstallUtils.dropShellPermissionIdentity(); } } /** * Tests an app can be rolled back to the previous signing key. * * <p>The rollback capability in the signing lineage allows an app to be updated to an APK * signed with a previous signing key in the lineage; however this often defeats the purpose * of key rotation as a compromised key could then be used to roll an app back to the previous * key. To avoid requiring the rollback capability to support app rollbacks the PackageManager * allows an app to be rolled back to the previous signing key if the rollback install reason * is set. */ @Test public void testRollbackAfterKeyRotation() throws Exception { try { InstallUtils.adoptShellPermissionIdentity( Manifest.permission.INSTALL_PACKAGES, Manifest.permission.DELETE_PACKAGES, Manifest.permission.TEST_MANAGE_ROLLBACKS, Manifest.permission.MANAGE_ROLLBACKS); // Uninstall TestApp.A Uninstall.packages(TestApp.A); assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1); // Install v1 of the app with the original signing key (without rollbacks enabled). Install.single(TestApp.AOriginal1).commit(); assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); // Upgrade from v1 to v2 with the rotated signing key, with rollbacks enabled. Install.single(TestApp.ARotated2).setEnableRollback().commit(); assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2); // Roll back the app. RollbackInfo available = waitForAvailableRollback(TestApp.A); RollbackUtils.rollback(available.getRollbackId()); assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); } finally { InstallUtils.dropShellPermissionIdentity(); } } } Loading
services/core/java/com/android/server/pm/PackageManagerService.java +20 −8 Original line number Diff line number Diff line Loading @@ -18819,9 +18819,11 @@ public class PackageManagerService extends IPackageManager.Stub final VersionInfo versionInfo = request.versionInfos.get(installPackageName); final boolean compareCompat = isCompatSignatureUpdateNeeded(versionInfo); final boolean compareRecover = isRecoverSignatureUpdateNeeded(versionInfo); final boolean isRollback = installArgs != null && installArgs.installReason == PackageManager.INSTALL_REASON_ROLLBACK; final boolean compatMatch = verifySignatures(signatureCheckPs, disabledPkgSetting, parsedPackage.getSigningDetails(), compareCompat, compareRecover); compareRecover, isRollback); // The new KeySets will be re-added later in the scanning process. if (compatMatch) { removeAppKeySetData = true; Loading Loading @@ -19791,6 +19793,7 @@ public class PackageManagerService extends IPackageManager.Stub final boolean fullApp = ((installFlags & PackageManager.INSTALL_FULL_APP) != 0); final boolean virtualPreload = ((installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0); final boolean isRollback = args.installReason == PackageManager.INSTALL_REASON_ROLLBACK; @ScanFlags int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE; if (args.move != null) { // moving a complete application; perform an initial scan on the new install location Loading Loading @@ -19971,7 +19974,8 @@ public class PackageManagerService extends IPackageManager.Stub parsedPackage); // We don't care about disabledPkgSetting on install for now. final boolean compatMatch = verifySignatures(signatureCheckPs, null, parsedPackage.getSigningDetails(), compareCompat, compareRecover); parsedPackage.getSigningDetails(), compareCompat, compareRecover, isRollback); // The new KeySets will be re-added later in the scanning process. if (compatMatch) { synchronized (mLock) { Loading Loading @@ -20248,17 +20252,25 @@ public class PackageManagerService extends IPackageManager.Stub + pkgName11); } } else { SigningDetails parsedPkgSigningDetails = parsedPackage.getSigningDetails(); SigningDetails oldPkgSigningDetails = oldPackage.getSigningDetails(); // default to original signature matching if (!parsedPackage.getSigningDetails().checkCapability( oldPackage.getSigningDetails(), if (!parsedPkgSigningDetails.checkCapability(oldPkgSigningDetails, SigningDetails.CertCapabilities.INSTALLED_DATA) && !oldPackage.getSigningDetails().checkCapability( parsedPackage.getSigningDetails(), && !oldPkgSigningDetails.checkCapability(parsedPkgSigningDetails, SigningDetails.CertCapabilities.ROLLBACK)) { // Allow the update to proceed if this is a rollback and the parsed // package's current signing key is the current signer or in the lineage // of the old package; this allows a rollback to a previously installed // version after an app's signing key has been rotated without requiring // the rollback capability on the previous signing key. if (!isRollback || !oldPkgSigningDetails.hasAncestorOrSelf( parsedPkgSigningDetails)) { throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "New package has a different signature: " + pkgName11); } } } // don't allow a system upgrade unless the upgrade hash matches if (oldPackage.getRestrictUpdateHash() != null && oldPackage.isSystem()) {
services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +8 −1 Original line number Diff line number Diff line Loading @@ -624,7 +624,7 @@ public class PackageManagerServiceUtils { */ public static boolean verifySignatures(PackageSetting pkgSetting, PackageSetting disabledPkgSetting, PackageParser.SigningDetails parsedSignatures, boolean compareCompat, boolean compareRecover) boolean compareCompat, boolean compareRecover, boolean isRollback) throws PackageManagerException { final String packageName = pkgSetting.name; boolean compatMatch = false; Loading Loading @@ -658,6 +658,13 @@ public class PackageManagerServiceUtils { match = matchSignatureInSystem(pkgSetting, disabledPkgSetting); } if (!match && isRollback) { // Since a rollback can only be initiated for an APK previously installed on the // device allow rolling back to a previous signing key even if the rollback // capability has not been granted. match = pkgSetting.signatures.mSigningDetails.hasAncestorOrSelf(parsedSignatures); } if (!match) { throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package " + packageName + Loading
services/core/java/com/android/server/rollback/Rollback.java +1 −0 Original line number Diff line number Diff line Loading @@ -563,6 +563,7 @@ class Rollback { params.setRequestDowngrade(true); params.setRequiredInstalledVersionCode( pkgRollbackInfo.getVersionRolledBackFrom().getLongVersionCode()); params.setInstallReason(PackageManager.INSTALL_REASON_ROLLBACK); if (isStaged()) { params.setStaged(); } Loading
tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java +40 −0 Original line number Diff line number Diff line Loading @@ -1225,4 +1225,44 @@ public class RollbackTest { InstallUtils.dropShellPermissionIdentity(); } } /** * Tests an app can be rolled back to the previous signing key. * * <p>The rollback capability in the signing lineage allows an app to be updated to an APK * signed with a previous signing key in the lineage; however this often defeats the purpose * of key rotation as a compromised key could then be used to roll an app back to the previous * key. To avoid requiring the rollback capability to support app rollbacks the PackageManager * allows an app to be rolled back to the previous signing key if the rollback install reason * is set. */ @Test public void testRollbackAfterKeyRotation() throws Exception { try { InstallUtils.adoptShellPermissionIdentity( Manifest.permission.INSTALL_PACKAGES, Manifest.permission.DELETE_PACKAGES, Manifest.permission.TEST_MANAGE_ROLLBACKS, Manifest.permission.MANAGE_ROLLBACKS); // Uninstall TestApp.A Uninstall.packages(TestApp.A); assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1); // Install v1 of the app with the original signing key (without rollbacks enabled). Install.single(TestApp.AOriginal1).commit(); assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); // Upgrade from v1 to v2 with the rotated signing key, with rollbacks enabled. Install.single(TestApp.ARotated2).setEnableRollback().commit(); assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2); // Roll back the app. RollbackInfo available = waitForAvailableRollback(TestApp.A); RollbackUtils.rollback(available.getRollbackId()); assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); } finally { InstallUtils.dropShellPermissionIdentity(); } } }