Merge "Allow revoked capabilities to be restored in sharedUserId lineage" into tm-dev

This commit is contained in:
Michael Groover
2022-04-21 17:00:08 +00:00
committed by Android (Google) Code Review
3 changed files with 260 additions and 55 deletions

View File

@@ -112,6 +112,29 @@ public final class SigningDetails implements Parcelable {
int AUTH = 16; 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. */ /** A representation of unknown signing details. Use instead of null. */
public static final SigningDetails UNKNOWN = new SigningDetails(/* signatures */ null, public static final SigningDetails UNKNOWN = new SigningDetails(/* signatures */ null,
SignatureSchemeVersion.UNKNOWN, /* keys */ null, /* pastSigningCertificates */ 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 * 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 * <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 * containing the longest common lineage with differences in capabilities for shared signers
* lineages contain the same signers with the same capabilities then the instance on which * resolved using the provided {@code mergeRule}. If the two lineages contain the same signers
* this was invoked is returned without any changes. Similarly if neither instance has a * with the same capabilities then the instance on which this was invoked is returned without
* lineage, or if neither has the same or an ancestor signer then this instance is returned. * 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: * 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. * <ul>
* - lineage A -> B merged with lineage B -> C returns lineage A -> B -> C * <li>lineage B merged with lineage A -> B returns lineage A -> B.
* - lineage A -> B with the {@code PERMISSION} capability revoked for A merged with * <li>lineage A -> B merged with lineage B -> C returns lineage A -> B -> C
* lineage A -> B with the {@code SHARED_USER_ID} capability revoked for A returns * <li>lineage A -> B with the {@code PERMISSION} capability revoked for A merged with
* lineage A -> B with both capabilities revoked for A. * lineage A -> B with the {@code SHARED_USER_ID} capability revoked for A returns the
* - lineage A -> B -> C merged with lineage A -> B -> D would return the original lineage * following based on the {@code mergeRule}:
* A -> B -> C since the current signer of both instances is not the same or in the * <ul>
* lineage of the other. * <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 * @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 * of the other. If neither instance has a lineage, or if neither has the same or an
* ancestor signer then this instance is returned. * 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()) { if (!hasPastSigningCertificates()) {
return otherSigningDetails.hasPastSigningCertificates() return otherSigningDetails.hasPastSigningCertificates()
&& otherSigningDetails.hasAncestorOrSelf(this) ? otherSigningDetails : this; && otherSigningDetails.hasAncestorOrSelf(this) ? otherSigningDetails : this;
@@ -201,19 +254,43 @@ public final class SigningDetails implements Parcelable {
if (descendantSigningDetails == null) { if (descendantSigningDetails == null) {
return this; return this;
} }
return descendantSigningDetails == this ? mergeLineageWithAncestorOrSelf( SigningDetails mergedDetails = this;
otherSigningDetails) : otherSigningDetails.mergeLineageWithAncestorOrSelf(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) * Merges the signing lineage of this instance with the lineage of the ancestor (or same)
* signer in the provided {@code otherSigningDetails}. * 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. * @return Merged {@code SigningDetails} instance.
*/ */
private @NonNull SigningDetails mergeLineageWithAncestorOrSelf( private @NonNull SigningDetails mergeLineageWithAncestorOrSelf(
@NonNull SigningDetails otherSigningDetails) { @NonNull SigningDetails otherSigningDetails, @CapabilityMergeRule int mergeRule) {
// This method should only be called with instances that contain lineages. // This method should only be called with instances that contain lineages.
int index = mPastSigningCertificates.length - 1; int index = mPastSigningCertificates.length - 1;
int otherIndex = otherSigningDetails.mPastSigningCertificates.length - 1; int otherIndex = otherSigningDetails.mPastSigningCertificates.length - 1;
@@ -236,16 +313,26 @@ public final class SigningDetails implements Parcelable {
} }
do { do {
// Add the common signer to the merged lineage with the most restrictive // Add the common signer to the merged lineage and resolve any differences in
// capabilities of the two lineages. // capabilites with the merge rule.
Signature signature = mPastSigningCertificates[index--]; Signature signature = mPastSigningCertificates[index--];
Signature ancestorSignature = Signature ancestorSignature =
otherSigningDetails.mPastSigningCertificates[otherIndex--]; otherSigningDetails.mPastSigningCertificates[otherIndex--];
Signature mergedSignature = new Signature(signature); Signature mergedSignature = new Signature(signature);
int mergedCapabilities = signature.getFlags() & ancestorSignature.getFlags(); if (signature.getFlags() != ancestorSignature.getFlags()) {
if (signature.getFlags() != mergedCapabilities) {
capabilitiesModified = true; 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); mergedSignatures.add(mergedSignature);
} while (index >= 0 && otherIndex >= 0 && mPastSigningCertificates[index].equals( } 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! // DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code // CHECKSTYLE:OFF Generated code
@@ -914,10 +1001,10 @@ public final class SigningDetails implements Parcelable {
} }
@DataClass.Generated( @DataClass.Generated(
time = 1616984092921L, time = 1650058974710L,
codegenVersion = "1.0.22", codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/content/pm/SigningDetails.java", 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 @Deprecated
private void __metadata() {} private void __metadata() {}

View File

@@ -15,6 +15,9 @@
*/ */
package android.content.pm; 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.AUTH;
import static android.content.pm.SigningDetails.CertCapabilities.INSTALLED_DATA; import static android.content.pm.SigningDetails.CertCapabilities.INSTALLED_DATA;
import static android.content.pm.SigningDetails.CertCapabilities.PERMISSION; import static android.content.pm.SigningDetails.CertCapabilities.PERMISSION;
@@ -45,6 +48,7 @@ import java.util.Set;
public class SigningDetailsTest { public class SigningDetailsTest {
private static final int DEFAULT_CAPABILITIES = private static final int DEFAULT_CAPABILITIES =
INSTALLED_DATA | SHARED_USER_ID | PERMISSION | AUTH; 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 // 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. // public key for the SigningDetails; the following are all DER encoded EC X.509 certificates.
@@ -368,10 +372,10 @@ public class SigningDetailsTest {
} }
@Test @Test
public void mergeLineageWith_sameLineageDifferentCaps_returnsLineageWithModifiedCaps() public void mergeLineageWith_sameLineageDifferentCaps_returnsLineageWithProvidedCaps()
throws Exception { throws Exception {
// This test verifies when two lineages consist of the same signers but have different // 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, SigningDetails defaultCapabilitiesDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
SECOND_SIGNATURE, THIRD_SIGNATURE); SECOND_SIGNATURE, THIRD_SIGNATURE);
SigningDetails modifiedCapabilitiesDetails = createSigningDetailsWithLineageAndCapabilities( SigningDetails modifiedCapabilitiesDetails = createSigningDetailsWithLineageAndCapabilities(
@@ -384,68 +388,135 @@ public class SigningDetailsTest {
defaultCapabilitiesDetails); defaultCapabilitiesDetails);
assertEquals(modifiedCapabilitiesDetails, result1); 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 @Test
public void mergeLineageWith_overlappingLineageDiffCaps_returnsFullLineageWithModifiedCaps() public void mergeLineageWith_overlappingLineageDiffCaps_returnsFullLineageWithModifiedCaps()
throws Exception { throws Exception {
// This test verifies the following scenario: // This test verifies the merge of two lineages with overlapping signers and modified caps
// - First lineage has signers A -> B with modified capabilities for A and B // returns the full lineage with expected capabilities based on the provided merge rule.
// - 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.
int[] firstCapabilities = int[] firstCapabilities =
new int[]{INSTALLED_DATA | AUTH, INSTALLED_DATA | SHARED_USER_ID | PERMISSION}; new int[]{INSTALLED_DATA | AUTH, INSTALLED_DATA | SHARED_USER_ID | PERMISSION};
int[] secondCapabilities = new int[]{INSTALLED_DATA | SHARED_USER_ID | AUTH, int[] secondCapabilities = new int[]{INSTALLED_DATA | SHARED_USER_ID | AUTH,
INSTALLED_DATA | SHARED_USER_ID | AUTH}; INSTALLED_DATA | SHARED_USER_ID | AUTH};
int[] expectedCapabilities = int[] expectedRestrictedCapabilities =
new int[]{firstCapabilities[0], firstCapabilities[1] & secondCapabilities[0], new int[]{firstCapabilities[0], firstCapabilities[1] & secondCapabilities[0],
secondCapabilities[1]}; 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( SigningDetails firstDetails = createSigningDetailsWithLineageAndCapabilities(
new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE}, firstCapabilities); new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE}, firstCapabilities);
SigningDetails secondDetails = createSigningDetailsWithLineageAndCapabilities( SigningDetails secondDetails = createSigningDetailsWithLineageAndCapabilities(
new String[]{SECOND_SIGNATURE, THIRD_SIGNATURE}, secondCapabilities); new String[]{SECOND_SIGNATURE, THIRD_SIGNATURE}, secondCapabilities);
SigningDetails expectedDetails = createSigningDetailsWithLineageAndCapabilities( SigningDetails expectedRestrictedDetails = createSigningDetailsWithLineageAndCapabilities(
new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE}, 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 result1 = firstDetails.mergeLineageWith(secondDetails,
SigningDetails result2 = secondDetails.mergeLineageWith(firstDetails); 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(expectedDetails1, result1);
assertEquals(expectedDetails, result2); assertEquals(expectedDetails1, result2);
assertEquals(expectedDetails2, result3);
assertEquals(expectedDetails2, result4);
assertEquals(expectedRestrictedDetails, result5);
assertEquals(expectedRestrictedDetails, result6);
} }
@Test @Test
public void mergeLineageWith_subLineageModifiedCaps_returnsFullLineageWithModifiedCaps() public void mergeLineageWith_subLineageModifiedCaps_returnsFullLineageWithModifiedCaps()
throws Exception { throws Exception {
// This test verifies the following scenario: // This test verifies the merge of a full lineage and a subset of that lineage with
// - First lineage has signers B -> C with modified capabilities // modified caps returns the full lineage with expected capabilities based on the
// - Second lineage has signers A -> B -> C -> D with modified capabilities // provided merge rule.
// The merged lineage should be A -> B -> C -> D with the most restrictive capabilities for
// B and C since they are in both lineages.
int[] subCapabilities = new int[]{INSTALLED_DATA | SHARED_USER_ID | PERMISSION, int[] subCapabilities = new int[]{INSTALLED_DATA | SHARED_USER_ID | PERMISSION,
DEFAULT_CAPABILITIES | ROLLBACK}; DEFAULT_CAPABILITIES | ROLLBACK};
int[] fullCapabilities = int[] fullCapabilities =
new int[]{0, SHARED_USER_ID, DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES}; new int[]{0, SHARED_USER_ID, DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES};
int[] expectedCapabilities = int[] expectedRestrictedCapabilities =
new int[]{fullCapabilities[0], subCapabilities[0] & fullCapabilities[1], new int[]{fullCapabilities[0], subCapabilities[0] & fullCapabilities[1],
subCapabilities[1] & fullCapabilities[2], fullCapabilities[3]}; 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( SigningDetails subLineageDetails = createSigningDetailsWithLineageAndCapabilities(
new String[]{SECOND_SIGNATURE, THIRD_SIGNATURE}, subCapabilities); new String[]{SECOND_SIGNATURE, THIRD_SIGNATURE}, subCapabilities);
SigningDetails fullLineageDetails = createSigningDetailsWithLineageAndCapabilities( SigningDetails fullLineageDetails = createSigningDetailsWithLineageAndCapabilities(
new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE, FOURTH_SIGNATURE}, new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE, FOURTH_SIGNATURE},
fullCapabilities); fullCapabilities);
SigningDetails expectedDetails = createSigningDetailsWithLineageAndCapabilities( SigningDetails expectedRestrictedDetails = createSigningDetailsWithLineageAndCapabilities(
new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE, FOURTH_SIGNATURE}, 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 result1 = subLineageDetails.mergeLineageWith(fullLineageDetails,
SigningDetails result2 = fullLineageDetails.mergeLineageWith(subLineageDetails); 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(expectedDetails1, result1);
assertEquals(expectedDetails, result2); assertEquals(expectedDetails1, result2);
assertEquals(expectedDetails2, result3);
assertEquals(expectedDetails2, result4);
assertEquals(expectedRestrictedDetails, result5);
assertEquals(expectedRestrictedDetails, result6);
} }
@Test @Test
@@ -465,6 +536,39 @@ public class SigningDetailsTest {
assertTrue(result2 == secondLineageDetails); 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 @Test
public void hasCommonAncestor_noLineageSameSingleSigner_returnsTrue() throws Exception { public void hasCommonAncestor_noLineageSameSingleSigner_returnsTrue() throws Exception {
// If neither SigningDetails have a lineage but they have the same single signer then // 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_FAILED_UPDATE_INCOMPATIBLE;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; 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_BOOTING;
import static com.android.server.pm.PackageManagerService.SCAN_DONT_KILL_APP; import static com.android.server.pm.PackageManagerService.SCAN_DONT_KILL_APP;
@@ -176,6 +177,19 @@ final class ReconcilePackageUtils {
SigningDetails mergedDetails = sharedSigningDetails.mergeLineageWith( SigningDetails mergedDetails = sharedSigningDetails.mergeLineageWith(
signingDetails); signingDetails);
if (mergedDetails != sharedSigningDetails) { 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 = sharedUserSetting.signatures.mSigningDetails =
mergedDetails; mergedDetails;
} }