Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 18bd9c45 authored by Michael Groover's avatar Michael Groover Committed by Android (Google) Code Review
Browse files

Merge "Add support for rotating compromised key in sharedUserId"

parents 6ef3babc 2bdac3b6
Loading
Loading
Loading
Loading
+110 −50
Original line number Diff line number Diff line
@@ -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
     * @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;
    }

    /**
+3 −2
Original line number Diff line number Diff line
@@ -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.