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

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

Merge "Address edge cases for signing certificate lineages in sharedUids" into rvc-dev

parents 7b3b0611 f1744af7
Loading
Loading
Loading
Loading
+207 −1
Original line number Diff line number Diff line
@@ -5968,6 +5968,206 @@ public class PackageParser {
            }
        }

        /**
         * Merges the signing lineage of this instance with the lineage in the provided {@code
         * otherSigningDetails} when one has the same or an ancestor signer of the other.
         *
         * <p>Merging two signing lineages will result in a new {@code SigningDetails} instance
         * containing the longest common lineage with the most restrictive capabilities. If the two
         * lineages contain the same signers with the same capabilities then the instance on which
         * this was invoked is returned without any changes. Similarly if neither instance has a
         * lineage, or if neither has the same or an ancestor signer then this instance is returned.
         *
         * Following are some example results of this method for lineages with signers A, B, C, D:
         * - lineage B merged with lineage A -> B returns lineage A -> B.
         * - lineage A -> B merged with lineage B -> C returns lineage A -> B -> C
         * - lineage A -> B with the {@code PERMISSION} capability revoked for A merged with
         *  lineage A -> B with the {@code SHARED_USER_ID} capability revoked for A returns
         *  lineage A -> B with both capabilities revoked for A.
         * - lineage A -> B -> C merged with lineage A -> B -> D would return the original lineage
         *  A -> B -> C since the current signer of both instances is not the same or in the
         *   lineage of the other.
         */
        public SigningDetails mergeLineageWith(SigningDetails otherSigningDetails) {
            if (!hasPastSigningCertificates()) {
                return otherSigningDetails.hasPastSigningCertificates()
                        && otherSigningDetails.hasAncestorOrSelf(this) ? otherSigningDetails : this;
            }
            if (!otherSigningDetails.hasPastSigningCertificates()) {
                return this;
            }
            // Use the utility method to determine which SigningDetails instance is the descendant
            // and to confirm that the signing lineage does not diverge.
            SigningDetails descendantSigningDetails = getDescendantOrSelf(otherSigningDetails);
            if (descendantSigningDetails == null) {
                return this;
            }
            return descendantSigningDetails == this ? mergeLineageWithAncestorOrSelf(
                    otherSigningDetails) : otherSigningDetails.mergeLineageWithAncestorOrSelf(this);
        }

        /**
         * Merges the signing lineage of this instance with the lineage of the ancestor (or same)
         * signer in the provided {@code otherSigningDetails}.
         */
        private SigningDetails mergeLineageWithAncestorOrSelf(SigningDetails otherSigningDetails) {
            // This method should only be called with instances that contain lineages.
            int index = pastSigningCertificates.length - 1;
            int otherIndex = otherSigningDetails.pastSigningCertificates.length - 1;
            if (index < 0 || otherIndex < 0) {
                return this;
            }

            List<Signature> mergedSignatures = new ArrayList<>();
            boolean capabilitiesModified = false;
            // If this is a descendant lineage then add all of the descendant signer(s) to the
            // merged lineage until the ancestor signer is reached.
            while (index >= 0 && !pastSigningCertificates[index].equals(
                    otherSigningDetails.pastSigningCertificates[otherIndex])) {
                mergedSignatures.add(new Signature(pastSigningCertificates[index--]));
            }
            // If the signing lineage was exhausted then the provided ancestor is not actually an
            // ancestor of this lineage.
            if (index < 0) {
                return this;
            }

            do {
                // Add the common signer to the merged lineage with the most restrictive
                // capabilities of the two lineages.
                Signature signature = pastSigningCertificates[index--];
                Signature ancestorSignature =
                        otherSigningDetails.pastSigningCertificates[otherIndex--];
                Signature mergedSignature = new Signature(signature);
                int mergedCapabilities = signature.getFlags() & ancestorSignature.getFlags();
                if (signature.getFlags() != mergedCapabilities) {
                    capabilitiesModified = true;
                    mergedSignature.setFlags(mergedCapabilities);
                }
                mergedSignatures.add(mergedSignature);
            } while (index >= 0 && otherIndex >= 0 && pastSigningCertificates[index].equals(
                    otherSigningDetails.pastSigningCertificates[otherIndex]));

            // If both lineages still have elements then their lineages have diverged; since this is
            // not supported return the invoking instance.
            if (index >= 0 && otherIndex >= 0) {
                return this;
            }

            // Add any remaining elements from either lineage that is not yet exhausted to the
            // the merged lineage.
            while (otherIndex >= 0) {
                mergedSignatures.add(new Signature(
                        otherSigningDetails.pastSigningCertificates[otherIndex--]));
            }
            while (index >= 0) {
                mergedSignatures.add(new Signature(pastSigningCertificates[index--]));
            }

            // if this lineage already contains all the elements in the ancestor and none of the
            // capabilities were changed then just return this instance.
            if (mergedSignatures.size() == pastSigningCertificates.length
                    && !capabilitiesModified) {
                return this;
            }
            // Since the signatures were added to the merged lineage from newest to oldest reverse
            // the list to ensure the oldest signer is at index 0.
            Collections.reverse(mergedSignatures);
            try {
                return new SigningDetails(new Signature[]{new Signature(signatures[0])},
                        signatureSchemeVersion, mergedSignatures.toArray(new Signature[0]));
            } catch (CertificateException e) {
                Slog.e(TAG, "Caught an exception creating the merged lineage: ", e);
                return this;
            }
        }

        /**
         * Returns whether this and the provided {@code otherSigningDetails} share a common
         * ancestor.
         *
         * <p>The two SigningDetails have a common ancestor if any of the following conditions are
         * met:
         * - If neither has a lineage and their current signer(s) are equal.
         * - If only one has a lineage and the signer of the other is the same or in the lineage.
         * - If both have a lineage and their current signers are the same or one is in the lineage
         * of the other, and their lineages do not diverge to different signers.
         */
        public boolean hasCommonAncestor(SigningDetails otherSigningDetails) {
            if (!hasPastSigningCertificates()) {
                // If this instance does not have a lineage then it must either be in the ancestry
                // of or the same signer of the otherSigningDetails.
                return otherSigningDetails.hasAncestorOrSelf(this);
            }
            if (!otherSigningDetails.hasPastSigningCertificates()) {
                return hasAncestorOrSelf(otherSigningDetails);
            }
            // If both have a lineage then use getDescendantOrSelf to obtain the descendant signing
            // details; a null return from that method indicates there is no common lineage between
            // the two or that they diverge at a point in the lineage.
            return getDescendantOrSelf(otherSigningDetails) != null;
        }

        /**
         * Returns the SigningDetails with a descendant (or same) signer after verifying the
         * descendant has the same, a superset, or a subset of the lineage of the ancestor.
         *
         * <p>If this instance and the provided {@code otherSigningDetails} do not share an
         * ancestry, or if their lineages diverge then null is returned to indicate there is no
         * valid descendant SigningDetails.
         */
        private SigningDetails getDescendantOrSelf(SigningDetails otherSigningDetails) {
            SigningDetails descendantSigningDetails;
            SigningDetails ancestorSigningDetails;
            if (hasAncestorOrSelf(otherSigningDetails)) {
                // If the otherSigningDetails has the same signer or a signer in the lineage of this
                // instance then treat this instance as the descendant.
                descendantSigningDetails = this;
                ancestorSigningDetails = otherSigningDetails;
            } else if (otherSigningDetails.hasAncestor(this)) {
                // The above check confirmed that the two instances do not have the same signer and
                // the signer of otherSigningDetails is not in this instance's lineage; if this
                // signer is in the otherSigningDetails lineage then treat this as the ancestor.
                descendantSigningDetails = otherSigningDetails;
                ancestorSigningDetails = this;
            } else {
                // The signers are not the same and neither has the current signer of the other in
                // its lineage; return null to indicate there is no descendant signer.
                return null;
            }
            // Once the descent (or same) signer is identified iterate through the ancestry until
            // the current signer of the ancestor is found.
            int descendantIndex = descendantSigningDetails.pastSigningCertificates.length - 1;
            int ancestorIndex = ancestorSigningDetails.pastSigningCertificates.length - 1;
            while (descendantIndex >= 0
                    && !descendantSigningDetails.pastSigningCertificates[descendantIndex].equals(
                    ancestorSigningDetails.pastSigningCertificates[ancestorIndex])) {
                descendantIndex--;
            }
            // Since the ancestry was verified above the descendant lineage should never be
            // exhausted, but if for some reason the ancestor signer is not found then return null.
            if (descendantIndex < 0) {
                return null;
            }
            // Once the common ancestor (or same) signer is found iterate over the lineage of both
            // to ensure that they are either the same or one is a subset of the other.
            do {
                descendantIndex--;
                ancestorIndex--;
            } while (descendantIndex >= 0 && ancestorIndex >= 0
                    && descendantSigningDetails.pastSigningCertificates[descendantIndex].equals(
                    ancestorSigningDetails.pastSigningCertificates[ancestorIndex]));

            // If both lineages still have elements then they diverge and cannot be considered a
            // valid common lineage.
            if (descendantIndex >= 0 && ancestorIndex >= 0) {
                return null;
            }
            // Since one or both of the lineages was exhausted they are either the same or one is a
            // subset of the other; return the valid descendant.
            return descendantSigningDetails;
        }

        /** Returns true if the signing details have one or more signatures. */
        public boolean hasSignatures() {
            return signatures != null && signatures.length > 0;
@@ -6284,7 +6484,13 @@ public class PackageParser {
            if (!Arrays.equals(pastSigningCertificates, that.pastSigningCertificates)) {
                return false;
            }

            // The capabilities for the past signing certs must match as well.
            for (int i = 0; i < pastSigningCertificates.length; i++) {
                if (pastSigningCertificates[i].getFlags()
                        != that.pastSigningCertificates[i].getFlags()) {
                    return false;
                }
            }
            return true;
        }

+15 −0
Original line number Diff line number Diff line
@@ -122,6 +122,21 @@ public class Signature implements Parcelable {
        mSignature = sig;
    }

    /**
     * Copy constructor that creates a new instance from the provided {@code other} Signature.
     *
     * @hide
     */
    public Signature(Signature other) {
        mSignature = other.mSignature.clone();
        Certificate[] otherCertificateChain = other.mCertificateChain;
        if (otherCertificateChain != null && otherCertificateChain.length > 1) {
            mCertificateChain = Arrays.copyOfRange(otherCertificateChain, 1,
                    otherCertificateChain.length);
        }
        mFlags = other.mFlags;
    }

    /**
     * Sets the flags representing the capabilities of the past signing certificate.
     * @hide
+508 −17

File changed.

Preview size limit exceeded, changes collapsed.

+10 −4
Original line number Diff line number Diff line
@@ -16267,10 +16267,16 @@ public class PackageManagerService extends IPackageManager.Stub
                    // signing certificate than the existing one, and if so, copy over the new
                    // details
                    if (signatureCheckPs.sharedUser != null) {
                        if (parsedPackage.getSigningDetails().hasAncestor(
                                signatureCheckPs.sharedUser.signatures.mSigningDetails)) {
                            signatureCheckPs.sharedUser.signatures.mSigningDetails =
                                    parsedPackage.getSigningDetails();
                        // Attempt to merge the existing lineage for the shared SigningDetails with
                        // the lineage of the new package; if the shared SigningDetails are not
                        // returned this indicates the new package added new signers to the lineage
                        // and/or changed the capabilities of existing signers in the lineage.
                        SigningDetails sharedSigningDetails =
                                signatureCheckPs.sharedUser.signatures.mSigningDetails;
                        SigningDetails mergedDetails = sharedSigningDetails.mergeLineageWith(
                                signingDetails);
                        if (mergedDetails != sharedSigningDetails) {
                            signatureCheckPs.sharedUser.signatures.mSigningDetails = mergedDetails;
                        }
                        if (signatureCheckPs.sharedUser.signaturesChanged == null) {
                            signatureCheckPs.sharedUser.signaturesChanged = Boolean.FALSE;
+43 −1
Original line number Diff line number Diff line
@@ -611,7 +611,6 @@ public class PackageManagerServiceUtils {
        final String packageName = pkgSetting.name;
        boolean compatMatch = false;
        if (pkgSetting.signatures.mSigningDetails.signatures != null) {

            // Already existing package. Make sure signatures match
            boolean match = parsedSignatures.checkCapability(
                    pkgSetting.signatures.mSigningDetails,
@@ -664,6 +663,13 @@ public class PackageManagerServiceUtils {
                    || pkgSetting.getSharedUser().signatures.mSigningDetails.checkCapability(
                            parsedSignatures,
                            PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID);
            // 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.
            if (!match && pkgSetting.getSharedUser().packages.size() == 1
                    && pkgSetting.getSharedUser().packages.valueAt(0).name.equals(packageName)) {
                match = true;
            }
            if (!match && compareCompat) {
                match = matchSignaturesCompat(
                        packageName, pkgSetting.getSharedUser().signatures, parsedSignatures);
@@ -686,6 +692,42 @@ public class PackageManagerServiceUtils {
                        + " has no signatures that match those in shared user "
                        + pkgSetting.getSharedUser().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 (PackageSetting shUidPkgSetting : pkgSetting.getSharedUser().packages) {
                    // 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.name)) {
                        continue;
                    }
                    PackageParser.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,
                                PackageParser.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.name);
                        }
                    }
                }
            }
            // If the lineage of this package diverges from the lineage of the sharedUserId then
            // do not allow the installation to proceed.
            if (!parsedSignatures.hasCommonAncestor(
                    pkgSetting.getSharedUser().signatures.mSigningDetails)) {
                throw new PackageManagerException(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
                        "Package " + packageName + " has a signing lineage "
                                + "that diverges from the lineage of the sharedUserId");
            }
        }
        return compatMatch;
    }