diff options
| -rw-r--r-- | services/core/java/com/android/server/pm/PackageManagerService.java | 20 | ||||
| -rw-r--r-- | services/core/java/com/android/server/pm/PackageManagerServiceUtils.java | 45 |
2 files changed, 56 insertions, 9 deletions
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index f7f78b9cfcfe..c75507a37687 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -8193,12 +8193,12 @@ Slog.e("TODD", } private void collectCertificatesLI(PackageSetting ps, PackageParser.Package pkg, - final @ParseFlags int parseFlags) throws PackageManagerException { + final @ParseFlags int parseFlags, boolean forceCollect) throws PackageManagerException { // When upgrading from pre-N MR1, verify the package time stamp using the package // directory and not the APK file. final long lastModifiedTime = mIsPreNMR1Upgrade ? new File(pkg.codePath).lastModified() : getLastModifiedTime(pkg); - if (ps != null + if (ps != null && !forceCollect && ps.codePathString.equals(pkg.codePath) && ps.timeStamp == lastModifiedTime && !isCompatSignatureUpdateNeeded(pkg) @@ -8221,7 +8221,8 @@ Slog.e("TODD", Slog.w(TAG, "PackageSetting for " + ps.name + " is missing signatures. Collecting certs again to recover them."); } else { - Slog.i(TAG, toString() + " changed; collecting certs"); + Slog.i(TAG, pkg.codePath + " changed; collecting certs" + + (forceCollect ? " (forced)" : "")); } try { @@ -8530,8 +8531,11 @@ Slog.e("TODD", + " better than this " + pkg.getLongVersionCode()); } - // verify certificates against what was last scanned - collectCertificatesLI(pkgSetting, pkg, parseFlags); + // Verify certificates against what was last scanned. If it is an updated priv app, we will + // force the verification. Full apk verification will happen unless apk verity is set up for + // the file. In that case, only small part of the apk is verified upfront. + collectCertificatesLI(pkgSetting, pkg, parseFlags, + PackageManagerServiceUtils.isApkVerificationForced(disabledPkgSetting)); boolean shouldHideSystemApp = false; // A new application appeared on /system, but, we already have a copy of @@ -9835,6 +9839,7 @@ Slog.e("TODD", final @ScanFlags int scanFlags = request.scanFlags; final PackageSetting oldPkgSetting = request.oldPkgSetting; final PackageSetting originalPkgSetting = request.originalPkgSetting; + final PackageSetting disabledPkgSetting = request.disabledPkgSetting; final UserHandle user = request.user; final String realPkgName = request.realPkgName; final PackageSetting pkgSetting = result.pkgSetting; @@ -9917,7 +9922,7 @@ Slog.e("TODD", try { final boolean compareCompat = isCompatSignatureUpdateNeeded(pkg); final boolean compareRecover = isRecoverSignatureUpdateNeeded(pkg); - final boolean compatMatch = verifySignatures(signatureCheckPs, + final boolean compatMatch = verifySignatures(signatureCheckPs, disabledPkgSetting, pkg.mSigningDetails, compareCompat, compareRecover); // The new KeySets will be re-added later in the scanning process. if (compatMatch) { @@ -16687,8 +16692,9 @@ Slog.e("TODD", try { final boolean compareCompat = isCompatSignatureUpdateNeeded(pkg); final boolean compareRecover = isRecoverSignatureUpdateNeeded(pkg); + // We don't care about disabledPkgSetting on install for now. final boolean compatMatch = verifySignatures( - signatureCheckPs, pkg.mSigningDetails, compareCompat, + signatureCheckPs, null, pkg.mSigningDetails, compareCompat, compareRecover); // The new KeySets will be re-added later in the scanning process. if (compatMatch) { diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java index df836de21a80..7b96ca67d38f 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java @@ -33,10 +33,12 @@ import com.android.server.pm.dex.DexManager; import com.android.server.pm.dex.PackageDexUsage; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.AppGlobals; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.PackageParser; +import android.content.pm.PackageParser.PackageParserException; import android.content.pm.ResolveInfo; import android.content.pm.Signature; import android.os.Build; @@ -45,6 +47,7 @@ import android.os.Environment; import android.os.FileUtils; import android.os.Process; import android.os.RemoteException; +import android.os.SystemProperties; import android.os.UserHandle; import android.service.pm.PackageServiceDumpProto; import android.system.ErrnoException; @@ -547,13 +550,46 @@ public class PackageManagerServiceUtils { } /** + * Make sure the updated priv app is signed with the same key as the original APK file on the + * /system partition. + * + * <p>The rationale is that {@code disabledPkg} is a PackageSetting backed by xml files in /data + * and is not tamperproof. + */ + private static boolean matchSignatureInSystem(PackageSetting pkgSetting, + PackageSetting disabledPkgSetting) { + try { + PackageParser.collectCertificates(disabledPkgSetting.pkg, + PackageParser.PARSE_IS_SYSTEM_DIR); + if (compareSignatures(pkgSetting.signatures.mSignatures, + disabledPkgSetting.signatures.mSignatures) + != PackageManager.SIGNATURE_MATCH) { + logCriticalInfo(Log.ERROR, "Updated system app mismatches cert on /system: " + + pkgSetting.name); + return false; + } + } catch (PackageParserException e) { + logCriticalInfo(Log.ERROR, "Failed to collect cert for " + pkgSetting.name + ": " + + e.getMessage()); + return false; + } + return true; + } + + /** Returns true to force apk verification if the updated package (in /data) is a priv app. */ + static boolean isApkVerificationForced(@Nullable PackageSetting disabledPs) { + return disabledPs != null && disabledPs.isPrivileged() && + SystemProperties.getInt("ro.apk_verity.mode", 0) != 0; + } + + /** * Verifies that signatures match. * @returns {@code true} if the compat signatures were matched; otherwise, {@code false}. * @throws PackageManagerException if the signatures did not match. */ public static boolean verifySignatures(PackageSetting pkgSetting, - PackageParser.SigningDetails parsedSignatures, boolean compareCompat, - boolean compareRecover) + PackageSetting disabledPkgSetting, PackageParser.SigningDetails parsedSignatures, + boolean compareCompat, boolean compareRecover) throws PackageManagerException { final String packageName = pkgSetting.name; boolean compatMatch = false; @@ -572,6 +608,11 @@ public class PackageManagerServiceUtils { packageName, pkgSetting.signatures.mSignatures, parsedSignatures.signatures); } + + if (!match && isApkVerificationForced(disabledPkgSetting)) { + match = matchSignatureInSystem(pkgSetting, disabledPkgSetting); + } + if (!match) { throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package " + packageName + |