Allow revoked capabilities to be restored in sharedUserId lineage

When APK key rotation was initially introduced in P, an update to the
capabilities of a previous signer in the sharedUserId lineage only
took effect when the signing key of the package being updated was
changed. Android R addressed this by always merging the lineage of
a package being installed / updated in the sharedUserId with the
existing sharedUserId lineage; however, this approach always used
the most restrictive capabilities in the lineage, so once a
capability was revoked from a previous signer, it could not be
restored. This commit allows a capability to be restored by
initially applying the capabilities of the package being installed
with those in the sharedUserId; if a change in the signers or
capabilities is detected, then the most restrictive capabilities
from all packages in the sharedUserId are used to update the shared
lineage. This allows a package to restore a previously revoked
capability if no other packages have revoked the capability; however,
if a package in the sharedUserId has revoked a capability and a newly
installed package restores this capability, the restrictive rule will
ensure the capability is still revoked.

Bug: 227823594
Test: atest PkgInstallSignatureVerificationTest
Test: atest SigningDetailsTest
Change-Id: Id53a2cd235c7a557822b9a1bfc2f431801d415e4
This commit is contained in:
Michael Groover
2022-04-15 16:57:29 -07:00
parent b497365a27
commit 148fa19a60
3 changed files with 260 additions and 55 deletions

View File

@@ -112,6 +112,29 @@ public final class SigningDetails implements Parcelable {
int AUTH = 16;
}
@IntDef(value = {CapabilityMergeRule.MERGE_SELF_CAPABILITY,
CapabilityMergeRule.MERGE_OTHER_CAPABILITY,
CapabilityMergeRule.MERGE_RESTRICTED_CAPABILITY})
public @interface CapabilityMergeRule {
/**
* When capabilities are different for a common signer in the lineage, use the capabilities
* from this instance.
*/
int MERGE_SELF_CAPABILITY = 0;
/**
* When capabilities are different for a common signer in the lineage, use the capabilites
* from the other instance.
*/
int MERGE_OTHER_CAPABILITY = 1;
/**
* When capabilities are different for a common signer in the lineage, use the most
* restrictive between the two signers.
*/
int MERGE_RESTRICTED_CAPABILITY = 2;
}
/** A representation of unknown signing details. Use instead of null. */
public static final SigningDetails UNKNOWN = new SigningDetails(/* signatures */ null,
SignatureSchemeVersion.UNKNOWN, /* keys */ null, /* pastSigningCertificates */ null);
@@ -164,30 +187,60 @@ public final class SigningDetails implements Parcelable {
/**
* 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.
* otherSigningDetails} using {@link CapabilityMergeRule#MERGE_OTHER_CAPABILITY} as the merge
* rule.
*
* @param otherSigningDetails the {@code SigningDetails} with which to merge
* @return Merged {@code SigningDetails} instance when one has the same or an ancestor signer
* of the other. If neither instance has a lineage, or if neither has the same or an
* ancestor signer then this instance is returned.
* @see #mergeLineageWith(SigningDetails, int)
*/
public @NonNull SigningDetails mergeLineageWith(@NonNull SigningDetails otherSigningDetails) {
return mergeLineageWith(otherSigningDetails, CapabilityMergeRule.MERGE_OTHER_CAPABILITY);
}
/**
* 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 using the
* provided {@code mergeRule} to handle differences in capabilities for shared signers.
*
* <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.
* containing the longest common lineage with differences in capabilities for shared signers
* resolved using the provided {@code mergeRule}. 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.
* <ul>
* <li>lineage B merged with lineage A -> B returns lineage A -> B.
* <li>lineage A -> B merged with lineage B -> C returns lineage A -> B -> C
* <li>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 the
* following based on the {@code mergeRule}:
* <ul>
* <li>{@code MERGE_SELF_CAPABILITY} - lineage A -> B with {@code PERMISSION} revoked
* for A.
* <li>{@code MERGE_OTHER_CAPABILITY} - lineage A -> B with {@code SHARED_USER_ID}
* revoked for A.
* <li>{@code MERGE_RESTRICTED_CAPABILITY} - lineage A -> B with {@code PERMISSION} and
* {@code SHARED_USER_ID} revoked for A.
* </ul>
* <li>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.
* </ul>
*
* @param otherSigningDetails The {@code SigningDetails} you would like to merge with.
* @param otherSigningDetails the {@code SigningDetails} with which to merge
* @param mergeRule the {@link CapabilityMergeRule} to use when resolving differences in
* capabilities for shared signers
* @return Merged {@code SigningDetails} instance when one has the same or an ancestor signer
* of the other. If neither instance has a lineage, or if neither has the same or an
* ancestor signer then this instance is returned.
*/
public @NonNull SigningDetails mergeLineageWith(@NonNull SigningDetails otherSigningDetails) {
public @NonNull SigningDetails mergeLineageWith(@NonNull SigningDetails otherSigningDetails,
@CapabilityMergeRule int mergeRule) {
if (!hasPastSigningCertificates()) {
return otherSigningDetails.hasPastSigningCertificates()
&& otherSigningDetails.hasAncestorOrSelf(this) ? otherSigningDetails : this;
@@ -201,19 +254,43 @@ public final class SigningDetails implements Parcelable {
if (descendantSigningDetails == null) {
return this;
}
return descendantSigningDetails == this ? mergeLineageWithAncestorOrSelf(
otherSigningDetails) : otherSigningDetails.mergeLineageWithAncestorOrSelf(this);
SigningDetails mergedDetails = this;
if (descendantSigningDetails == this) {
// If this instance is the descendant then the merge will also be invoked against this
// instance and the provided mergeRule can be used as is.
mergedDetails = mergeLineageWithAncestorOrSelf(otherSigningDetails, mergeRule);
} else {
// If the provided instance is the descendant then the merge will be invoked against the
// other instance and a self or other merge rule will need to be flipped.
switch (mergeRule) {
case CapabilityMergeRule.MERGE_SELF_CAPABILITY:
mergedDetails = otherSigningDetails.mergeLineageWithAncestorOrSelf(this,
CapabilityMergeRule.MERGE_OTHER_CAPABILITY);
break;
case CapabilityMergeRule.MERGE_OTHER_CAPABILITY:
mergedDetails = otherSigningDetails.mergeLineageWithAncestorOrSelf(this,
CapabilityMergeRule.MERGE_SELF_CAPABILITY);
break;
case CapabilityMergeRule.MERGE_RESTRICTED_CAPABILITY:
mergedDetails = otherSigningDetails.mergeLineageWithAncestorOrSelf(this,
mergeRule);
break;
}
}
return mergedDetails;
}
/**
* Merges the signing lineage of this instance with the lineage of the ancestor (or same)
* signer in the provided {@code otherSigningDetails}.
*
* @param otherSigningDetails The {@code SigningDetails} you would like to merge with.
* @param otherSigningDetails the {@code SigningDetails} with which to merge
* @param mergeRule the {@link CapabilityMergeRule} to use when resolving differences in
* capabilities for shared signers
* @return Merged {@code SigningDetails} instance.
*/
private @NonNull SigningDetails mergeLineageWithAncestorOrSelf(
@NonNull SigningDetails otherSigningDetails) {
@NonNull SigningDetails otherSigningDetails, @CapabilityMergeRule int mergeRule) {
// This method should only be called with instances that contain lineages.
int index = mPastSigningCertificates.length - 1;
int otherIndex = otherSigningDetails.mPastSigningCertificates.length - 1;
@@ -236,16 +313,26 @@ public final class SigningDetails implements Parcelable {
}
do {
// Add the common signer to the merged lineage with the most restrictive
// capabilities of the two lineages.
// Add the common signer to the merged lineage and resolve any differences in
// capabilites with the merge rule.
Signature signature = mPastSigningCertificates[index--];
Signature ancestorSignature =
otherSigningDetails.mPastSigningCertificates[otherIndex--];
Signature mergedSignature = new Signature(signature);
int mergedCapabilities = signature.getFlags() & ancestorSignature.getFlags();
if (signature.getFlags() != mergedCapabilities) {
if (signature.getFlags() != ancestorSignature.getFlags()) {
capabilitiesModified = true;
mergedSignature.setFlags(mergedCapabilities);
switch (mergeRule) {
case CapabilityMergeRule.MERGE_SELF_CAPABILITY:
mergedSignature.setFlags(signature.getFlags());
break;
case CapabilityMergeRule.MERGE_OTHER_CAPABILITY:
mergedSignature.setFlags(ancestorSignature.getFlags());
break;
case CapabilityMergeRule.MERGE_RESTRICTED_CAPABILITY:
mergedSignature.setFlags(
signature.getFlags() & ancestorSignature.getFlags());
break;
}
}
mergedSignatures.add(mergedSignature);
} while (index >= 0 && otherIndex >= 0 && mPastSigningCertificates[index].equals(
@@ -858,7 +945,7 @@ public final class SigningDetails implements Parcelable {
// Code below generated by codegen v1.0.22.
// Code below generated by codegen v1.0.23.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -914,10 +1001,10 @@ public final class SigningDetails implements Parcelable {
}
@DataClass.Generated(
time = 1616984092921L,
codegenVersion = "1.0.22",
time = 1650058974710L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/content/pm/SigningDetails.java",
inputSignatures = "private static final java.lang.String TAG\nprivate final @android.annotation.Nullable android.content.pm.Signature[] mSignatures\nprivate final @android.content.pm.SigningDetails.SignatureSchemeVersion int mSignatureSchemeVersion\nprivate final @android.annotation.Nullable android.util.ArraySet<java.security.PublicKey> mPublicKeys\nprivate final @android.annotation.Nullable android.content.pm.Signature[] mPastSigningCertificates\nprivate static final int PAST_CERT_EXISTS\npublic static final android.content.pm.SigningDetails UNKNOWN\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.SigningDetails> CREATOR\npublic @android.annotation.NonNull android.content.pm.SigningDetails mergeLineageWith(android.content.pm.SigningDetails)\nprivate @android.annotation.NonNull android.content.pm.SigningDetails mergeLineageWithAncestorOrSelf(android.content.pm.SigningDetails)\npublic boolean hasCommonAncestor(android.content.pm.SigningDetails)\npublic boolean hasAncestorOrSelfWithDigest(java.util.Set<java.lang.String>)\nprivate @android.annotation.Nullable android.content.pm.SigningDetails getDescendantOrSelf(android.content.pm.SigningDetails)\npublic boolean hasSignatures()\npublic boolean hasPastSigningCertificates()\npublic boolean hasAncestorOrSelf(android.content.pm.SigningDetails)\npublic boolean hasAncestor(android.content.pm.SigningDetails)\npublic boolean hasCommonSignerWithCapability(android.content.pm.SigningDetails,int)\npublic boolean checkCapability(android.content.pm.SigningDetails,int)\npublic boolean checkCapabilityRecover(android.content.pm.SigningDetails,int)\npublic boolean hasCertificate(android.content.pm.Signature)\npublic boolean hasCertificate(android.content.pm.Signature,int)\npublic boolean hasCertificate(byte[])\nprivate boolean hasCertificateInternal(android.content.pm.Signature,int)\npublic boolean checkCapability(java.lang.String,int)\npublic boolean hasSha256Certificate(byte[])\npublic boolean hasSha256Certificate(byte[],int)\nprivate boolean hasSha256CertificateInternal(byte[],int)\npublic boolean signaturesMatchExactly(android.content.pm.SigningDetails)\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\npublic @java.lang.Override boolean equals(java.lang.Object)\npublic @java.lang.Override int hashCode()\npublic static android.util.ArraySet<java.security.PublicKey> toSigningKeys(android.content.pm.Signature[])\nclass SigningDetails extends java.lang.Object implements [android.os.Parcelable]\nprivate @android.annotation.NonNull android.content.pm.Signature[] mSignatures\nprivate @android.content.pm.SigningDetails.SignatureSchemeVersion int mSignatureSchemeVersion\nprivate @android.annotation.Nullable android.content.pm.Signature[] mPastSigningCertificates\npublic android.content.pm.SigningDetails.Builder setSignatures(android.content.pm.Signature[])\npublic android.content.pm.SigningDetails.Builder setSignatureSchemeVersion(int)\npublic android.content.pm.SigningDetails.Builder setPastSigningCertificates(android.content.pm.Signature[])\nprivate void checkInvariants()\npublic android.content.pm.SigningDetails build()\nclass Builder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false, genParcelable=true, genAidl=false)")
inputSignatures = "private static final java.lang.String TAG\nprivate final @android.annotation.Nullable android.content.pm.Signature[] mSignatures\nprivate final @android.content.pm.SigningDetails.SignatureSchemeVersion int mSignatureSchemeVersion\nprivate final @android.annotation.Nullable android.util.ArraySet<java.security.PublicKey> mPublicKeys\nprivate final @android.annotation.Nullable android.content.pm.Signature[] mPastSigningCertificates\nprivate static final int PAST_CERT_EXISTS\npublic static final android.content.pm.SigningDetails UNKNOWN\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.SigningDetails> CREATOR\npublic @android.annotation.NonNull android.content.pm.SigningDetails mergeLineageWith(android.content.pm.SigningDetails)\npublic @android.annotation.NonNull android.content.pm.SigningDetails mergeLineageWith(android.content.pm.SigningDetails,int)\nprivate @android.annotation.NonNull android.content.pm.SigningDetails mergeLineageWithAncestorOrSelf(android.content.pm.SigningDetails,int)\npublic boolean hasCommonAncestor(android.content.pm.SigningDetails)\npublic boolean hasAncestorOrSelfWithDigest(java.util.Set<java.lang.String>)\nprivate @android.annotation.Nullable android.content.pm.SigningDetails getDescendantOrSelf(android.content.pm.SigningDetails)\npublic boolean hasSignatures()\npublic boolean hasPastSigningCertificates()\npublic boolean hasAncestorOrSelf(android.content.pm.SigningDetails)\npublic boolean hasAncestor(android.content.pm.SigningDetails)\npublic boolean hasCommonSignerWithCapability(android.content.pm.SigningDetails,int)\npublic boolean checkCapability(android.content.pm.SigningDetails,int)\npublic boolean checkCapabilityRecover(android.content.pm.SigningDetails,int)\npublic boolean hasCertificate(android.content.pm.Signature)\npublic boolean hasCertificate(android.content.pm.Signature,int)\npublic boolean hasCertificate(byte[])\nprivate boolean hasCertificateInternal(android.content.pm.Signature,int)\npublic boolean checkCapability(java.lang.String,int)\npublic boolean hasSha256Certificate(byte[])\npublic boolean hasSha256Certificate(byte[],int)\nprivate boolean hasSha256CertificateInternal(byte[],int)\npublic boolean signaturesMatchExactly(android.content.pm.SigningDetails)\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\npublic @java.lang.Override boolean equals(java.lang.Object)\npublic @java.lang.Override int hashCode()\npublic static android.util.ArraySet<java.security.PublicKey> toSigningKeys(android.content.pm.Signature[])\nclass SigningDetails extends java.lang.Object implements [android.os.Parcelable]\nprivate @android.annotation.NonNull android.content.pm.Signature[] mSignatures\nprivate @android.content.pm.SigningDetails.SignatureSchemeVersion int mSignatureSchemeVersion\nprivate @android.annotation.Nullable android.content.pm.Signature[] mPastSigningCertificates\npublic android.content.pm.SigningDetails.Builder setSignatures(android.content.pm.Signature[])\npublic android.content.pm.SigningDetails.Builder setSignatureSchemeVersion(int)\npublic android.content.pm.SigningDetails.Builder setPastSigningCertificates(android.content.pm.Signature[])\nprivate void checkInvariants()\npublic android.content.pm.SigningDetails build()\nclass Builder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false, genParcelable=true, genAidl=false)")
@Deprecated
private void __metadata() {}

View File

@@ -15,6 +15,9 @@
*/
package android.content.pm;
import static android.content.pm.SigningDetails.CapabilityMergeRule.MERGE_OTHER_CAPABILITY;
import static android.content.pm.SigningDetails.CapabilityMergeRule.MERGE_RESTRICTED_CAPABILITY;
import static android.content.pm.SigningDetails.CapabilityMergeRule.MERGE_SELF_CAPABILITY;
import static android.content.pm.SigningDetails.CertCapabilities.AUTH;
import static android.content.pm.SigningDetails.CertCapabilities.INSTALLED_DATA;
import static android.content.pm.SigningDetails.CertCapabilities.PERMISSION;
@@ -45,6 +48,7 @@ import java.util.Set;
public class SigningDetailsTest {
private static final int DEFAULT_CAPABILITIES =
INSTALLED_DATA | SHARED_USER_ID | PERMISSION | AUTH;
private static final int CURRENT_SIGNER_CAPABILITIES = DEFAULT_CAPABILITIES | ROLLBACK;
// Some of the tests in this class require valid certificate encodings from which to pull the
// public key for the SigningDetails; the following are all DER encoded EC X.509 certificates.
@@ -368,10 +372,10 @@ public class SigningDetailsTest {
}
@Test
public void mergeLineageWith_sameLineageDifferentCaps_returnsLineageWithModifiedCaps()
public void mergeLineageWith_sameLineageDifferentCaps_returnsLineageWithProvidedCaps()
throws Exception {
// This test verifies when two lineages consist of the same signers but have different
// capabilities the more restrictive capabilities are returned.
// capabilities, the capabilities of the provided lineage are returned.
SigningDetails defaultCapabilitiesDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
SECOND_SIGNATURE, THIRD_SIGNATURE);
SigningDetails modifiedCapabilitiesDetails = createSigningDetailsWithLineageAndCapabilities(
@@ -384,68 +388,135 @@ public class SigningDetailsTest {
defaultCapabilitiesDetails);
assertEquals(modifiedCapabilitiesDetails, result1);
assertTrue(result2 == modifiedCapabilitiesDetails);
assertEquals(defaultCapabilitiesDetails, result2);
}
@Test
public void
mergeLineageWith_sameLineageDifferentCapsRestrictedRule_returnsLineageWithModifiedCaps()
throws Exception {
// This test verifies when two lineages consist of the same signers but have different
// capabilities, and the restricted merge rule is used, the more restrictive capabilities
// are returned.
SigningDetails defaultCapabilitiesDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
SECOND_SIGNATURE, THIRD_SIGNATURE);
SigningDetails modifiedCapabilitiesDetails = createSigningDetailsWithLineageAndCapabilities(
new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE},
new int[]{INSTALLED_DATA, INSTALLED_DATA, INSTALLED_DATA});
SigningDetails result1 = defaultCapabilitiesDetails.mergeLineageWith(
modifiedCapabilitiesDetails, MERGE_RESTRICTED_CAPABILITY);
SigningDetails result2 = modifiedCapabilitiesDetails.mergeLineageWith(
defaultCapabilitiesDetails, MERGE_RESTRICTED_CAPABILITY);
assertEquals(modifiedCapabilitiesDetails, result1);
assertEquals(modifiedCapabilitiesDetails, result2);
}
@Test
public void mergeLineageWith_overlappingLineageDiffCaps_returnsFullLineageWithModifiedCaps()
throws Exception {
// This test verifies the following scenario:
// - First lineage has signers A -> B with modified capabilities for A and B
// - Second lineage has signers B -> C with modified capabilities for B and C
// The merged lineage should be A -> B -> C with the most restrictive capabilities for B
// since it is in both lineages.
// This test verifies the merge of two lineages with overlapping signers and modified caps
// returns the full lineage with expected capabilities based on the provided merge rule.
int[] firstCapabilities =
new int[]{INSTALLED_DATA | AUTH, INSTALLED_DATA | SHARED_USER_ID | PERMISSION};
int[] secondCapabilities = new int[]{INSTALLED_DATA | SHARED_USER_ID | AUTH,
INSTALLED_DATA | SHARED_USER_ID | AUTH};
int[] expectedCapabilities =
int[] expectedRestrictedCapabilities =
new int[]{firstCapabilities[0], firstCapabilities[1] & secondCapabilities[0],
secondCapabilities[1]};
int[] expectedCapabilities1 =
new int[]{firstCapabilities[0], secondCapabilities[0], secondCapabilities[1]};
int[] expectedCapabilities2 =
new int[]{firstCapabilities[0], firstCapabilities[1], secondCapabilities[1]};
SigningDetails firstDetails = createSigningDetailsWithLineageAndCapabilities(
new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE}, firstCapabilities);
SigningDetails secondDetails = createSigningDetailsWithLineageAndCapabilities(
new String[]{SECOND_SIGNATURE, THIRD_SIGNATURE}, secondCapabilities);
SigningDetails expectedDetails = createSigningDetailsWithLineageAndCapabilities(
SigningDetails expectedRestrictedDetails = createSigningDetailsWithLineageAndCapabilities(
new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE},
expectedCapabilities);
expectedRestrictedCapabilities);
SigningDetails expectedDetails1 = createSigningDetailsWithLineageAndCapabilities(
new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE},
expectedCapabilities1);
SigningDetails expectedDetails2 = createSigningDetailsWithLineageAndCapabilities(
new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE},
expectedCapabilities2);
SigningDetails result1 = firstDetails.mergeLineageWith(secondDetails);
SigningDetails result2 = secondDetails.mergeLineageWith(firstDetails);
SigningDetails result1 = firstDetails.mergeLineageWith(secondDetails,
MERGE_OTHER_CAPABILITY);
SigningDetails result2 = secondDetails.mergeLineageWith(firstDetails,
MERGE_SELF_CAPABILITY);
SigningDetails result3 = firstDetails.mergeLineageWith(secondDetails,
MERGE_SELF_CAPABILITY);
SigningDetails result4 = secondDetails.mergeLineageWith(firstDetails,
MERGE_OTHER_CAPABILITY);
SigningDetails result5 = firstDetails.mergeLineageWith(secondDetails,
MERGE_RESTRICTED_CAPABILITY);
SigningDetails result6 = secondDetails.mergeLineageWith(firstDetails,
MERGE_RESTRICTED_CAPABILITY);
assertEquals(expectedDetails, result1);
assertEquals(expectedDetails, result2);
assertEquals(expectedDetails1, result1);
assertEquals(expectedDetails1, result2);
assertEquals(expectedDetails2, result3);
assertEquals(expectedDetails2, result4);
assertEquals(expectedRestrictedDetails, result5);
assertEquals(expectedRestrictedDetails, result6);
}
@Test
public void mergeLineageWith_subLineageModifiedCaps_returnsFullLineageWithModifiedCaps()
throws Exception {
// This test verifies the following scenario:
// - First lineage has signers B -> C with modified capabilities
// - Second lineage has signers A -> B -> C -> D with modified capabilities
// The merged lineage should be A -> B -> C -> D with the most restrictive capabilities for
// B and C since they are in both lineages.
// This test verifies the merge of a full lineage and a subset of that lineage with
// modified caps returns the full lineage with expected capabilities based on the
// provided merge rule.
int[] subCapabilities = new int[]{INSTALLED_DATA | SHARED_USER_ID | PERMISSION,
DEFAULT_CAPABILITIES | ROLLBACK};
int[] fullCapabilities =
new int[]{0, SHARED_USER_ID, DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES};
int[] expectedCapabilities =
int[] expectedRestrictedCapabilities =
new int[]{fullCapabilities[0], subCapabilities[0] & fullCapabilities[1],
subCapabilities[1] & fullCapabilities[2], fullCapabilities[3]};
int[] expectedCapabilities1 =
new int[]{fullCapabilities[0], fullCapabilities[1], fullCapabilities[2],
fullCapabilities[3]};
int[] expectedCapabilities2 =
new int[]{fullCapabilities[0], subCapabilities[0], subCapabilities[1],
fullCapabilities[3]};
SigningDetails subLineageDetails = createSigningDetailsWithLineageAndCapabilities(
new String[]{SECOND_SIGNATURE, THIRD_SIGNATURE}, subCapabilities);
SigningDetails fullLineageDetails = createSigningDetailsWithLineageAndCapabilities(
new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE, FOURTH_SIGNATURE},
fullCapabilities);
SigningDetails expectedDetails = createSigningDetailsWithLineageAndCapabilities(
SigningDetails expectedRestrictedDetails = createSigningDetailsWithLineageAndCapabilities(
new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE, FOURTH_SIGNATURE},
expectedCapabilities);
expectedRestrictedCapabilities);
SigningDetails expectedDetails1 = createSigningDetailsWithLineageAndCapabilities(
new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE, FOURTH_SIGNATURE},
expectedCapabilities1);
SigningDetails expectedDetails2 = createSigningDetailsWithLineageAndCapabilities(
new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE, FOURTH_SIGNATURE},
expectedCapabilities2);
SigningDetails result1 = subLineageDetails.mergeLineageWith(fullLineageDetails);
SigningDetails result2 = fullLineageDetails.mergeLineageWith(subLineageDetails);
SigningDetails result1 = subLineageDetails.mergeLineageWith(fullLineageDetails,
MERGE_OTHER_CAPABILITY);
SigningDetails result2 = fullLineageDetails.mergeLineageWith(subLineageDetails,
MERGE_SELF_CAPABILITY);
SigningDetails result3 = subLineageDetails.mergeLineageWith(fullLineageDetails,
MERGE_SELF_CAPABILITY);
SigningDetails result4 = fullLineageDetails.mergeLineageWith(subLineageDetails,
MERGE_OTHER_CAPABILITY);
SigningDetails result5 = subLineageDetails.mergeLineageWith(fullLineageDetails,
MERGE_RESTRICTED_CAPABILITY);
SigningDetails result6 = fullLineageDetails.mergeLineageWith(subLineageDetails,
MERGE_RESTRICTED_CAPABILITY);
assertEquals(expectedDetails, result1);
assertEquals(expectedDetails, result2);
assertEquals(expectedDetails1, result1);
assertEquals(expectedDetails1, result2);
assertEquals(expectedDetails2, result3);
assertEquals(expectedDetails2, result4);
assertEquals(expectedRestrictedDetails, result5);
assertEquals(expectedRestrictedDetails, result6);
}
@Test
@@ -465,6 +536,39 @@ public class SigningDetailsTest {
assertTrue(result2 == secondLineageDetails);
}
@Test
public void mergeLineageWith_modifiedCaps_returnsCapsFromProvidedLineage()
throws Exception {
// By default, when merging two lineage instances, the initial instance should represent a
// shared lineage while the provided lineage represents that of a newly installed / updated
// package. The shared lineage should contain any previous capability modifications from
// the default while the provided lineage has an opportunity to modify what was previously
// set. Initially, the most restrictive capabilities were always retained by the returned
// lineage, so apps had no mechanism to roll back a restriction to a previous signer. To
// allow this, a merge rule can be specified to indicate how differences in capabilities
// in common signers should be handled with the default using the capabilities from the
// provided lineage.
int[] firstCapabilities = new int[]{INSTALLED_DATA | PERMISSION | AUTH,
CURRENT_SIGNER_CAPABILITIES};
int[] secondCapabilities =
new int[]{DEFAULT_CAPABILITIES, CURRENT_SIGNER_CAPABILITIES};
SigningDetails firstDetails = createSigningDetailsWithLineageAndCapabilities(
new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE}, firstCapabilities);
SigningDetails secondDetails = createSigningDetailsWithLineageAndCapabilities(
new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE}, secondCapabilities);
// By default, the resulting capabilities should be that of the provided lineage.
SigningDetails expectedDetails1 = createSigningDetailsWithLineageAndCapabilities(
new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE}, secondCapabilities);
SigningDetails expectedDetails2 = createSigningDetailsWithLineageAndCapabilities(
new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE}, firstCapabilities);
SigningDetails result1 = firstDetails.mergeLineageWith(secondDetails);
SigningDetails result2 = secondDetails.mergeLineageWith(firstDetails);
assertEquals(expectedDetails1, result1);
assertEquals(expectedDetails2, result2);
}
@Test
public void hasCommonAncestor_noLineageSameSingleSigner_returnsTrue() throws Exception {
// If neither SigningDetails have a lineage but they have the same single signer then

View File

@@ -18,6 +18,7 @@ package com.android.server.pm;
import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
import static android.content.pm.SigningDetails.CapabilityMergeRule.MERGE_RESTRICTED_CAPABILITY;
import static com.android.server.pm.PackageManagerService.SCAN_BOOTING;
import static com.android.server.pm.PackageManagerService.SCAN_DONT_KILL_APP;
@@ -176,6 +177,19 @@ final class ReconcilePackageUtils {
SigningDetails mergedDetails = sharedSigningDetails.mergeLineageWith(
signingDetails);
if (mergedDetails != sharedSigningDetails) {
// Use the restricted merge rule with the signing lineages from the
// other packages in the sharedUserId to ensure if any revoke a
// capability from a previous signer then this is reflected in the
// shared lineage.
for (AndroidPackage androidPackage : sharedUserSetting.getPackages()) {
if (androidPackage.getPackageName() != null
&& !androidPackage.getPackageName().equals(
parsedPackage.getPackageName())) {
mergedDetails = mergedDetails.mergeLineageWith(
androidPackage.getSigningDetails(),
MERGE_RESTRICTED_CAPABILITY);
}
}
sharedUserSetting.signatures.mSigningDetails =
mergedDetails;
}