Loading core/java/android/content/pm/PackageParser.java +207 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; } Loading core/java/android/content/pm/Signature.java +15 −0 Original line number Diff line number Diff line Loading @@ -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 Loading core/tests/coretests/src/android/content/pm/SigningDetailsTest.java +508 −17 File changed.Preview size limit exceeded, changes collapsed. Show changes services/core/java/com/android/server/pm/PackageManagerService.java +10 −4 Original line number Diff line number Diff line Loading @@ -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; services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +43 −1 Original line number Diff line number Diff line Loading @@ -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, Loading Loading @@ -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); Loading @@ -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; } Loading Loading
core/java/android/content/pm/PackageParser.java +207 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; } Loading
core/java/android/content/pm/Signature.java +15 −0 Original line number Diff line number Diff line Loading @@ -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 Loading
core/tests/coretests/src/android/content/pm/SigningDetailsTest.java +508 −17 File changed.Preview size limit exceeded, changes collapsed. Show changes
services/core/java/com/android/server/pm/PackageManagerService.java +10 −4 Original line number Diff line number Diff line Loading @@ -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;
services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +43 −1 Original line number Diff line number Diff line Loading @@ -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, Loading Loading @@ -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); Loading @@ -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; } Loading