From 9e47e84774aa5e4bdff6a1d7d25914103bb1e51d Mon Sep 17 00:00:00 2001 From: Michael Groover Date: Fri, 13 May 2022 14:13:24 -0700 Subject: [PATCH] Use key rotation aware check when sharedUID signatures change on OTA The platform supports all packages in a sharedUserId changing their signatures during an OTA; if there is more than one package in the sharedUserId, the signing details of the first signer are used as the shared signing details for the sharedUserId, and all other packages in the sharedUserId must exactly match these signatures. This works in the absence of key rotation, but if one of the packages has a rotated signing key but still grants the previous signer the SHARED_USER_ID capability, then this check would fail if another package is in the sharedUserId and signed by the original signing key since the exact signature comparision would fail, resulting in the device boot looping. This commit updates this signature check when the signing details change for a sharedUserId to instead use a rotation aware check that allows the new signing details if the package being checked is signed by the same signer as the shared signing details, or if the current signer of one is in the lineage of the other with the SHARED_USER_ID capability granted to it. Fixes: 232476481 Test: Added two new packages in a sharedUserId, changed the signatures of both in the system image, one with a rotated key, and verified the platform recognized the new signers. Change-Id: Idaf923783ac6b5ee3af130955044e3e61bbcfa76 --- .../server/pm/PackageManagerServiceUtils.java | 32 +++++++++++++++---- .../server/pm/ReconcilePackageUtils.java | 10 ++---- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java index 4d11b13510e99..eea793f5fb3fb 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java @@ -19,6 +19,7 @@ package com.android.server.pm; import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE; import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE; import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE; +import static android.content.pm.SigningDetails.CertCapabilities.SHARED_USER_ID; import static android.system.OsConstants.O_CREAT; import static android.system.OsConstants.O_RDWR; @@ -564,13 +565,8 @@ public class PackageManagerServiceUtils { // the older ones. We check to see if either the new package is signed by an older cert // with which the current sharedUser is ok, or if it is signed by a newer one, and is ok // with being sharedUser with the existing signing cert. - boolean match = - parsedSignatures.checkCapability( - sharedUserSetting.getSigningDetails(), - SigningDetails.CertCapabilities.SHARED_USER_ID) - || sharedUserSetting.getSigningDetails().checkCapability( - parsedSignatures, - SigningDetails.CertCapabilities.SHARED_USER_ID); + boolean match = canJoinSharedUserId(parsedSignatures, + sharedUserSetting.getSigningDetails()); // Special case: if the sharedUserId capability check failed it could be due to this // being the only package in the sharedUserId so far and the lineage being updated to // deny the sharedUserId capability of the previous key in the lineage. @@ -644,6 +640,28 @@ public class PackageManagerServiceUtils { return compatMatch; } + /** + * Returns whether the package with {@code packageSigningDetails} can join the sharedUserId + * with {@code sharedUserSigningDetails}. + *

+ * A sharedUserId maintains a shared {@link SigningDetails} containing the full lineage and + * capabilities for each package in the sharedUserId. A package can join the sharedUserId if + * its current signer is the same as the shared signer, or if the current signer of either + * is in the signing lineage of the other with the {@link + * SigningDetails.CertCapabilities#SHARED_USER_ID} capability granted to that previous signer + * in the lineage. + * + * @param packageSigningDetails the {@code SigningDetails} of the package seeking to join the + * sharedUserId + * @param sharedUserSigningDetails the {@code SigningDetails} of the sharedUserId + * @return true if the package seeking to join the sharedUserId meets the requirements + */ + public static boolean canJoinSharedUserId(@NonNull SigningDetails packageSigningDetails, + @NonNull SigningDetails sharedUserSigningDetails) { + return packageSigningDetails.checkCapability(sharedUserSigningDetails, SHARED_USER_ID) + || sharedUserSigningDetails.checkCapability(packageSigningDetails, SHARED_USER_ID); + } + /** * Extract native libraries to a target path */ diff --git a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java index 5fc916f888f3f..d6a133e437896 100644 --- a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java +++ b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java @@ -22,11 +22,9 @@ import static android.content.pm.SigningDetails.CapabilityMergeRule.MERGE_RESTRI import static com.android.server.pm.PackageManagerService.SCAN_BOOTING; import static com.android.server.pm.PackageManagerService.SCAN_DONT_KILL_APP; -import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures; import android.content.pm.PackageManager; import android.content.pm.SharedLibraryInfo; -import android.content.pm.Signature; import android.content.pm.SigningDetails; import android.os.SystemProperties; import android.util.ArrayMap; @@ -212,12 +210,10 @@ final class ReconcilePackageUtils { // the signatures on the first package scanned for the shared user (i.e. if the // signaturesChanged state hasn't been initialized yet in SharedUserSetting). if (sharedUserSetting != null) { - final Signature[] sharedUserSignatures = sharedUserSetting - .signatures.mSigningDetails.getSignatures(); if (sharedUserSetting.signaturesChanged != null - && compareSignatures(sharedUserSignatures, - parsedPackage.getSigningDetails().getSignatures()) - != PackageManager.SIGNATURE_MATCH) { + && !PackageManagerServiceUtils.canJoinSharedUserId( + parsedPackage.getSigningDetails(), + sharedUserSetting.getSigningDetails())) { if (SystemProperties.getInt("ro.product.first_api_level", 0) <= 29) { // Mismatched signatures is an error and silently skipping system // packages will likely break the device in unforeseen ways.