Merge "Add support for rotating compromised key in sharedUserId"

This commit is contained in:
Michael Groover
2023-01-12 01:53:56 +00:00
committed by Android (Google) Code Review
2 changed files with 113 additions and 52 deletions

View File

@@ -33,6 +33,7 @@ import static com.android.server.pm.PackageManagerService.RANDOM_DIR_PREFIX;
import static com.android.server.pm.PackageManagerService.STUB_SUFFIX;
import static com.android.server.pm.PackageManagerService.TAG;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -113,6 +114,8 @@ import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.file.Path;
import java.security.SecureRandom;
import java.security.cert.CertificateEncodingException;
@@ -147,6 +150,29 @@ public class PackageManagerServiceUtils {
public static final ThreadLocal<Boolean> DISABLE_ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS =
ThreadLocal.withInitial(() -> false);
/**
* Type used with {@link #canJoinSharedUserId(String, SigningDetails, SharedUserSetting, int)}
* when the package attempting to join the sharedUserId is a new install.
*/
public static final int SHARED_USER_ID_JOIN_TYPE_INSTALL = 0;
/**
* Type used with {@link #canJoinSharedUserId(String, SigningDetails, SharedUserSetting, int)}
* when the package attempting to join the sharedUserId is an update.
*/
public static final int SHARED_USER_ID_JOIN_TYPE_UPDATE = 1;
/**
* Type used with {@link #canJoinSharedUserId(String, SigningDetails, SharedUserSetting, int)}
* when the package attempting to join the sharedUserId is a part of the system image.
*/
public static final int SHARED_USER_ID_JOIN_TYPE_SYSTEM = 2;
@IntDef(prefix = { "TYPE_" }, value = {
SHARED_USER_ID_JOIN_TYPE_INSTALL,
SHARED_USER_ID_JOIN_TYPE_UPDATE,
SHARED_USER_ID_JOIN_TYPE_SYSTEM,
})
@Retention(RetentionPolicy.SOURCE)
public @interface SharedUserIdJoinType {}
/**
* Components of apps targeting Android T and above will stop receiving intents from
* external callers that do not match its declared intent filters.
@@ -575,17 +601,9 @@ 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 = 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.
final ArraySet<PackageStateInternal> susPackageStates =
(ArraySet<PackageStateInternal>) sharedUserSetting.getPackageStates();
if (!match && susPackageStates.size() == 1
&& susPackageStates.valueAt(0).getPackageName().equals(packageName)) {
match = true;
}
boolean match = canJoinSharedUserId(packageName, parsedSignatures, sharedUserSetting,
pkgSetting.getSigningDetails().getSignatures() != null
? SHARED_USER_ID_JOIN_TYPE_UPDATE : SHARED_USER_ID_JOIN_TYPE_INSTALL);
if (!match && compareCompat) {
match = matchSignaturesCompat(
packageName, sharedUserSetting.signatures, parsedSignatures);
@@ -608,36 +626,6 @@ public class PackageManagerServiceUtils {
+ " has no signatures that match those in shared user "
+ sharedUserSetting.name + "; ignoring!");
}
// It is possible that this package contains a lineage that blocks sharedUserId access
// to an already installed package in the sharedUserId signed with a previous key.
// Iterate over all of the packages in the sharedUserId and ensure any that are signed
// with a key in this package's lineage have the SHARED_USER_ID capability granted.
if (parsedSignatures.hasPastSigningCertificates()) {
for (int i = 0; i < susPackageStates.size(); i++) {
PackageStateInternal shUidPkgSetting = susPackageStates.valueAt(i);
// if the current package in the sharedUserId is the package being updated then
// skip this check as the update may revoke the sharedUserId capability from
// the key with which this app was previously signed.
if (packageName.equals(shUidPkgSetting.getPackageName())) {
continue;
}
SigningDetails shUidSigningDetails =
shUidPkgSetting.getSigningDetails();
// The capability check only needs to be performed against the package if it is
// signed with a key that is in the lineage of the package being installed.
if (parsedSignatures.hasAncestor(shUidSigningDetails)) {
if (!parsedSignatures.checkCapability(shUidSigningDetails,
SigningDetails.CertCapabilities.SHARED_USER_ID)) {
throw new PackageManagerException(
INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
"Package " + packageName
+ " revoked the sharedUserId capability from the"
+ " signing key used to sign "
+ shUidPkgSetting.getPackageName());
}
}
}
}
// If the lineage of this package diverges from the lineage of the sharedUserId then
// do not allow the installation to proceed.
if (!parsedSignatures.hasCommonAncestor(
@@ -651,25 +639,97 @@ public class PackageManagerServiceUtils {
}
/**
* Returns whether the package with {@code packageSigningDetails} can join the sharedUserId
* with {@code sharedUserSigningDetails}.
* Returns whether the package {@code packageName} can join the sharedUserId based on the
* settings in {@code sharedUserSetting}.
* <p>
* 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.
* in the lineage. In the case of a key compromise, an app signed with a lineage revoking
* this capability from a previous signing key can still join the sharedUserId with another
* app signed with this previous key if the joining app is being updated; however, a new
* install will not be allowed until all apps have rotated off the key with the capability
* revoked.
*
* @param packageName the name of the package seeking to join the sharedUserId
* @param packageSigningDetails the {@code SigningDetails} of the package seeking to join the
* sharedUserId
* @param sharedUserSigningDetails the {@code SigningDetails} of the sharedUserId
* sharedUserId
* @param sharedUserSetting the {@code SharedUserSetting} for the sharedUserId {@code
* packageName} is seeking to join
* @param joinType the type of join (install, update, system, etc)
* @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);
public static boolean canJoinSharedUserId(@NonNull String packageName,
@NonNull SigningDetails packageSigningDetails,
@NonNull SharedUserSetting sharedUserSetting, @SharedUserIdJoinType int joinType) {
SigningDetails sharedUserSigningDetails = sharedUserSetting.getSigningDetails();
boolean capabilityGranted =
packageSigningDetails.checkCapability(sharedUserSigningDetails, SHARED_USER_ID)
|| sharedUserSigningDetails.checkCapability(packageSigningDetails,
SHARED_USER_ID);
// If the current signer for either the package or the sharedUserId is the current signer
// of the other or in the lineage of the other with the SHARED_USER_ID capability granted,
// then a system and update join type can proceed; an install join type is not allowed here
// since the sharedUserId may contain packages that are signed with a key untrusted by
// the new package.
if (capabilityGranted && joinType != SHARED_USER_ID_JOIN_TYPE_INSTALL) {
return true;
}
// If the package is signed with a key that is no longer trusted by the sharedUserId, then
// the join should not be allowed unless this is a system join type; system packages can
// join the sharedUserId as long as they share a common lineage.
if (!capabilityGranted && sharedUserSigningDetails.hasAncestor(packageSigningDetails)) {
if (joinType == SHARED_USER_ID_JOIN_TYPE_SYSTEM) {
return true;
}
return false;
}
// If the package is signed with a rotated key that no longer trusts the sharedUserId key,
// then allow system and update join types to rotate away from an untrusted key; install
// join types are not allowed since a new package that doesn't trust a previous key
// shouldn't be allowed to join until all packages in the sharedUserId have rotated off the
// untrusted key.
if (!capabilityGranted && packageSigningDetails.hasAncestor(sharedUserSigningDetails)) {
if (joinType != SHARED_USER_ID_JOIN_TYPE_INSTALL) {
return true;
}
return false;
}
// If the capability is not granted and the package signatures are not an ancestor
// or descendant of the sharedUserId signatures, then do not allow any join type to join
// the sharedUserId since there are no common signatures.
if (!capabilityGranted) {
return false;
}
// At this point this is a new install with the capability granted; ensure the current
// packages in the sharedUserId are all signed by a key trusted by the new package.
final ArraySet<PackageStateInternal> susPackageStates =
(ArraySet<PackageStateInternal>) sharedUserSetting.getPackageStates();
if (packageSigningDetails.hasPastSigningCertificates()) {
for (PackageStateInternal shUidPkgSetting : susPackageStates) {
SigningDetails shUidSigningDetails = shUidPkgSetting.getSigningDetails();
// The capability check only needs to be performed against the package if it is
// signed with a key that is in the lineage of the package being installed.
if (packageSigningDetails.hasAncestor(shUidSigningDetails)) {
if (!packageSigningDetails.checkCapability(shUidSigningDetails,
SigningDetails.CertCapabilities.SHARED_USER_ID)) {
Slog.d(TAG, "Package " + packageName
+ " revoked the sharedUserId capability from the"
+ " signing key used to sign "
+ shUidPkgSetting.getPackageName());
return false;
}
}
}
}
return true;
}
/**

View File

@@ -213,8 +213,9 @@ final class ReconcilePackageUtils {
if (sharedUserSetting != null) {
if (sharedUserSetting.signaturesChanged != null
&& !PackageManagerServiceUtils.canJoinSharedUserId(
parsedPackage.getSigningDetails(),
sharedUserSetting.getSigningDetails())) {
parsedPackage.getPackageName(), parsedPackage.getSigningDetails(),
sharedUserSetting,
PackageManagerServiceUtils.SHARED_USER_ID_JOIN_TYPE_SYSTEM)) {
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.