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

This commit is contained in:
Michael Groover
2021-02-01 20:16:03 +00:00
committed by Android (Google) Code Review
4 changed files with 69 additions and 9 deletions

View File

@@ -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,15 +20252,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);
}
}
}

View File

@@ -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 +

View File

@@ -563,6 +563,7 @@ class Rollback {
params.setRequestDowngrade(true);
params.setRequiredInstalledVersionCode(
pkgRollbackInfo.getVersionRolledBackFrom().getLongVersionCode());
params.setInstallReason(PackageManager.INSTALL_REASON_ROLLBACK);
if (isStaged()) {
params.setStaged();
}

View File

@@ -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();
}
}
}