diff options
author | Jeff Sharkey <jsharkey@android.com> | 2014-11-12 12:18:11 -0800 |
---|---|---|
committer | Jeff Sharkey <jsharkey@android.com> | 2014-11-12 13:16:06 -0800 |
commit | d68b87cdd402d46013170d9316a31c82be4e4816 (patch) | |
tree | 39447a9c63de992c85ae6b750a3cbc5fb21ef0ba /services | |
parent | 8d88f19bfe8ec0afb27593c0dea9b547d8c80b48 (diff) | |
download | frameworks_base-d68b87cdd402d46013170d9316a31c82be4e4816.zip frameworks_base-d68b87cdd402d46013170d9316a31c82be4e4816.tar.gz frameworks_base-d68b87cdd402d46013170d9316a31c82be4e4816.tar.bz2 |
Recover apps with malformed certificates.
There was a window of time in Lollipop where we persisted certificates
after they had passed through a decode/encode cycle. The well-written
OpenSSL library was liberal when decoding (allowing slightly malformed
certs to be parsed), but then strict when encoding, giving us
different bytes for effectively the same certificate.
A related libcore change (0c990ab4a90b8a5492a67b2b728ac9a4a1ccfa1b)
now returns the original bytes verbatim, fixing both pre-Lollipop
installs and installs after that change.
This change recovers any apps that had been installed during the
window of time described above by doing a one-time check to see if
the certs are effectively equal.
Bug: 18228011
Change-Id: Ib82bd6db718d0490d7a26c9c1014b7c8457a7f2d
Diffstat (limited to 'services')
-rw-r--r-- | services/core/java/com/android/server/pm/PackageManagerService.java | 49 | ||||
-rw-r--r-- | services/core/java/com/android/server/pm/Settings.java | 10 |
2 files changed, 56 insertions, 3 deletions
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index b79e157..3e1647e 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -2860,6 +2860,38 @@ public class PackageManagerService extends IPackageManager.Stub { return PackageManager.SIGNATURE_NO_MATCH; } + private boolean isRecoverSignatureUpdateNeeded(PackageParser.Package scannedPkg) { + if (isExternal(scannedPkg)) { + return mSettings.isExternalDatabaseVersionOlderThan( + DatabaseVersion.SIGNATURE_MALFORMED_RECOVER); + } else { + return mSettings.isInternalDatabaseVersionOlderThan( + DatabaseVersion.SIGNATURE_MALFORMED_RECOVER); + } + } + + private int compareSignaturesRecover(PackageSignatures existingSigs, + PackageParser.Package scannedPkg) { + if (!isRecoverSignatureUpdateNeeded(scannedPkg)) { + return PackageManager.SIGNATURE_NO_MATCH; + } + + String msg = null; + try { + if (Signature.areEffectiveMatch(existingSigs.mSignatures, scannedPkg.mSignatures)) { + logCriticalInfo(Log.INFO, "Recovered effectively matching certificates for " + + scannedPkg.packageName); + return PackageManager.SIGNATURE_MATCH; + } + } catch (CertificateException e) { + msg = e.getMessage(); + } + + logCriticalInfo(Log.INFO, + "Failed to recover certificates for " + scannedPkg.packageName + ": " + msg); + return PackageManager.SIGNATURE_NO_MATCH; + } + @Override public String[] getPackagesForUid(int uid) { uid = UserHandle.getAppId(uid); @@ -4148,7 +4180,8 @@ public class PackageManagerService extends IPackageManager.Stub { if (ps != null && ps.codePath.equals(srcFile) && ps.timeStamp == srcFile.lastModified() - && !isCompatSignatureUpdateNeeded(pkg)) { + && !isCompatSignatureUpdateNeeded(pkg) + && !isRecoverSignatureUpdateNeeded(pkg)) { long mSigningKeySetId = ps.keySetData.getProperSigningKeySet(); if (ps.signatures.mSignatures != null && ps.signatures.mSignatures.length != 0 @@ -4423,6 +4456,10 @@ public class PackageManagerService extends IPackageManager.Stub { == PackageManager.SIGNATURE_MATCH; } if (!match) { + match = compareSignaturesRecover(pkgSetting.signatures, pkg) + == PackageManager.SIGNATURE_MATCH; + } + if (!match) { throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package " + pkg.packageName + " signatures do not match the " + "previously installed version; ignoring!"); @@ -4439,6 +4476,10 @@ public class PackageManagerService extends IPackageManager.Stub { == PackageManager.SIGNATURE_MATCH; } if (!match) { + match = compareSignaturesRecover(pkgSetting.sharedUser.signatures, pkg) + == PackageManager.SIGNATURE_MATCH; + } + if (!match) { throw new PackageManagerException(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE, "Package " + pkg.packageName + " has no signatures that match those in shared user " @@ -5386,6 +5427,9 @@ public class PackageManagerService extends IPackageManager.Stub { if (!pkgSetting.keySetData.isUsingUpgradeKeySets() || pkgSetting.sharedUser != null) { try { verifySignaturesLP(pkgSetting, pkg); + // We just determined the app is signed correctly, so bring + // over the latest parsed certs. + pkgSetting.signatures.mSignatures = pkg.mSignatures; } catch (PackageManagerException e) { if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) { throw e; @@ -5418,7 +5462,8 @@ public class PackageManagerService extends IPackageManager.Stub { + pkg.packageName + " upgrade keys do not match the " + "previously installed version"); } else { - // signatures may have changed as result of upgrade + // We just determined the app is signed correctly, so bring + // over the latest parsed certs. pkgSetting.signatures.mSignatures = pkg.mSignatures; } } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 7de56c8..699adef 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -103,7 +103,7 @@ final class Settings { * Note that care should be taken to make sure all database upgrades are * idempotent. */ - private static final int CURRENT_DATABASE_VERSION = DatabaseVersion.SIGNATURE_END_ENTITY; + private static final int CURRENT_DATABASE_VERSION = DatabaseVersion.SIGNATURE_MALFORMED_RECOVER; /** * This class contains constants that can be referred to from upgrade code. @@ -121,6 +121,14 @@ final class Settings { * just the signing certificate. */ public static final int SIGNATURE_END_ENTITY = 2; + + /** + * There was a window of time in + * {@link android.os.Build.VERSION_CODES#LOLLIPOP} where we persisted + * certificates after potentially mutating them. To switch back to the + * original untouched certificates, we need to force a collection pass. + */ + public static final int SIGNATURE_MALFORMED_RECOVER = 3; } private static final boolean DEBUG_STOPPED = false; |