Allow APK rollback without rollback capability on previous key
Currently if an APK rollback is requested after an update to an APK that just rotated its signing key the rollback will only be allowed if the previous signing key in the lineage has the rollback capability granted. This commit allows a rollback to the previously installed APK without requiring the rollback capability if the rollback API is used. This allows apps to take advantage of APK rollbacks without losing the benefit of key rotation by needing to set the rollback capability on the previous signing key. Bug: 175231724 Test: atest RollbackTest Test: atest RollbackManaagerTest Test: atest PkgInstallSignatureVerificationTest Change-Id: I460ef4e7d4c221509b6442f2e8d3079229ad07c8
This commit is contained in:
@@ -18654,9 +18654,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;
|
||||
@@ -19625,6 +19627,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
|
||||
@@ -19805,7 +19808,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) {
|
||||
@@ -20082,15 +20086,23 @@ 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)) {
|
||||
throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
|
||||
"New package has a different signature: " + pkgName11);
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -621,7 +621,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;
|
||||
@@ -655,6 +655,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 +
|
||||
|
||||
@@ -563,6 +563,7 @@ class Rollback {
|
||||
params.setRequestDowngrade(true);
|
||||
params.setRequiredInstalledVersionCode(
|
||||
pkgRollbackInfo.getVersionRolledBackFrom().getLongVersionCode());
|
||||
params.setInstallReason(PackageManager.INSTALL_REASON_ROLLBACK);
|
||||
if (isStaged()) {
|
||||
params.setStaged();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user