summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
authorJeff Sharkey <jsharkey@android.com>2014-11-12 12:18:11 -0800
committerJeff Sharkey <jsharkey@android.com>2014-11-12 13:16:06 -0800
commitd68b87cdd402d46013170d9316a31c82be4e4816 (patch)
tree39447a9c63de992c85ae6b750a3cbc5fb21ef0ba /services
parent8d88f19bfe8ec0afb27593c0dea9b547d8c80b48 (diff)
downloadframeworks_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.java49
-rw-r--r--services/core/java/com/android/server/pm/Settings.java10
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;