Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 39e92b88 authored by Michael Groover's avatar Michael Groover Committed by Automerger Merge Worker
Browse files

Merge "Allow APK rollback without rollback capability on previous key" into sc-dev am: d1833c1b

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/13417833

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: I84ac63a4b68b1b89a76a3df66ca20aaea64bb859
parents 98765821 d1833c1b
Loading
Loading
Loading
Loading
+20 −8
Original line number Diff line number Diff line
@@ -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;
@@ -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
@@ -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) {
@@ -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()) {
+8 −1
Original line number Diff line number Diff line
@@ -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;
@@ -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 +
+1 −0
Original line number Diff line number Diff line
@@ -563,6 +563,7 @@ class Rollback {
                params.setRequestDowngrade(true);
                params.setRequiredInstalledVersionCode(
                        pkgRollbackInfo.getVersionRolledBackFrom().getLongVersionCode());
                params.setInstallReason(PackageManager.INSTALL_REASON_ROLLBACK);
                if (isStaged()) {
                    params.setStaged();
                }
+40 −0
Original line number Diff line number Diff line
@@ -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();
        }
    }
}