Loading core/java/android/content/pm/PackageParser.java +49 −0 Original line number Diff line number Diff line Loading @@ -6264,6 +6264,55 @@ public class PackageParser { return false; } /** * Returns whether this {@code SigningDetails} has a signer in common with the provided * {@code otherDetails} with the specified {@code flags} capabilities provided by this * signer. * * <p>Note this method allows for the signing lineage to diverge, so this should only be * used for instances where the only requirement is a common signer in the lineage with * the specified capabilities. If the current signer of this instance is an ancestor of * {@code otherDetails} then {@code true} is immediately returned since the current signer * has all capabilities granted. */ public boolean hasCommonSignerWithCapability(SigningDetails otherDetails, @CertCapabilities int flags) { if (this == UNKNOWN || otherDetails == UNKNOWN) { return false; } // If either is signed with more than one signer then both must be signed by the same // signers to consider the capabilities granted. if (signatures.length > 1 || otherDetails.signatures.length > 1) { return signaturesMatchExactly(otherDetails); } // The Signature class does not use the granted capabilities in the hashCode // computation, so a Set can be used to check for a common signer. Set<Signature> otherSignatures = new ArraySet<>(); if (otherDetails.hasPastSigningCertificates()) { otherSignatures.addAll(Arrays.asList(otherDetails.pastSigningCertificates)); } else { otherSignatures.addAll(Arrays.asList(otherDetails.signatures)); } // If the current signer of this instance is an ancestor of the other than return true // since all capabilities are granted to the current signer. if (otherSignatures.contains(signatures[0])) { return true; } if (hasPastSigningCertificates()) { // Since the current signer was checked above and the last signature in the // pastSigningCertificates is the current signer skip checking the last element. for (int i = 0; i < pastSigningCertificates.length - 1; i++) { if (otherSignatures.contains(pastSigningCertificates[i])) { // If the caller specified multiple capabilities ensure all are set. if ((pastSigningCertificates[i].getFlags() & flags) == flags) { return true; } } } } return false; } /** * Determines if the provided {@code oldDetails} is an ancestor of this one, and whether or * not this one grants it the provided capability, represented by the {@code flags} Loading core/tests/coretests/src/android/content/pm/SigningDetailsTest.java +241 −5 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import android.content.pm.PackageParser.SigningDetails; import android.util.ArraySet; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; Loading @@ -35,6 +36,8 @@ import androidx.test.filters.SmallTest; import org.junit.Test; import org.junit.runner.RunWith; import java.util.Set; @RunWith(AndroidJUnit4.class) @SmallTest public class SigningDetailsTest { Loading Loading @@ -208,8 +211,8 @@ public class SigningDetailsTest { SigningDetails result1 = noLineageDetails.mergeLineageWith(lineageDetails); SigningDetails result2 = lineageDetails.mergeLineageWith(noLineageDetails); assertTrue(result1 == lineageDetails); assertTrue(result2 == lineageDetails); assertSigningDetailsContainsLineage(result1, FIRST_SIGNATURE, SECOND_SIGNATURE); assertSigningDetailsContainsLineage(result2, FIRST_SIGNATURE, SECOND_SIGNATURE); } @Test Loading Loading @@ -271,8 +274,10 @@ public class SigningDetailsTest { SigningDetails result1 = singleSignerDetails.mergeLineageWith(fullLineageDetails); SigningDetails result2 = fullLineageDetails.mergeLineageWith(singleSignerDetails); assertTrue(result1 == fullLineageDetails); assertTrue(result2 == fullLineageDetails); assertSigningDetailsContainsLineage(result1, FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE); assertSigningDetailsContainsLineage(result2, FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE); } @Test Loading Loading @@ -605,6 +610,213 @@ public class SigningDetailsTest { assertTrue(secondLineageDetails.hasCommonAncestor(firstLineageDetails)); } @Test public void hasCommonSignerWithCapabilities_singleMatchingSigner_returnsTrue() throws Exception { // The hasCommonSignerWithCapabilities method is intended to grant the specified // capabilities to a requesting package that has a common signer in the lineage (or as the // current signer) even if their signing identities have diverged. This test verifies if the // two SigningDetails have the same single signer then the requested capability can be // granted since the current signer always has all capabilities granted. SigningDetails firstDetails = createSigningDetails(FIRST_SIGNATURE); SigningDetails secondSignerDetails = createSigningDetails(FIRST_SIGNATURE); assertTrue(firstDetails.hasCommonSignerWithCapability(secondSignerDetails, PERMISSION)); } @Test public void hasCommonSignerWithCapabilities_singleDifferentSigners_returnsFalse() throws Exception { // If each package is signed by a single different signer then the method should return // false since there is no shared signer. SigningDetails firstDetails = createSigningDetails(FIRST_SIGNATURE); SigningDetails secondDetails = createSigningDetails(SECOND_SIGNATURE); assertFalse(firstDetails.hasCommonSignerWithCapability(secondDetails, PERMISSION)); assertFalse(secondDetails.hasCommonSignerWithCapability(firstDetails, PERMISSION)); } @Test public void hasCommonSignerWithCapabilities_oneWithMultipleSigners_returnsFalse() throws Exception { // If one of the packages is signed with multiple signers and the other only a single signer // this method should return false since all signers must match exactly for multiple signer // cases. SigningDetails firstDetails = createSigningDetails(FIRST_SIGNATURE, SECOND_SIGNATURE); SigningDetails secondDetails = createSigningDetails(FIRST_SIGNATURE); assertFalse(firstDetails.hasCommonSignerWithCapability(secondDetails, PERMISSION)); assertFalse(secondDetails.hasCommonSignerWithCapability(firstDetails, PERMISSION)); } @Test public void hasCommonSignerWithCapabilities_multipleMatchingSigners_returnsTrue() throws Exception { // if both packages are signed by the same multiple signers then this method should return // true since the current signer is granted all capabilities. SigningDetails firstDetails = createSigningDetails(FIRST_SIGNATURE, SECOND_SIGNATURE); SigningDetails secondDetails = createSigningDetails(SECOND_SIGNATURE, FIRST_SIGNATURE); assertTrue(firstDetails.hasCommonSignerWithCapability(secondDetails, PERMISSION)); assertTrue(secondDetails.hasCommonSignerWithCapability(firstDetails, PERMISSION)); } @Test public void hasCommonSignerWithCapabilities_singleSignerInLineage_returnsTrue() throws Exception { // if a single signer is in the lineage and that previous signer has the requested // capability then this method should return true. SigningDetails lineageDetails = createSigningDetailsWithLineageAndCapabilities( new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE}, new int[]{DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES}); SigningDetails singleSignerDetails = createSigningDetails(FIRST_SIGNATURE); assertTrue(lineageDetails.hasCommonSignerWithCapability(singleSignerDetails, PERMISSION)); } @Test public void hasCommonSignerWithCapabilities_singleSignerInLineageWOCapability_returnsFalse() throws Exception { // If a single signer is in the lineage and that previous signer does not have the requested // capability then this method should return false. SigningDetails lineageDetails = createSigningDetailsWithLineageAndCapabilities( new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE}, new int[]{SHARED_USER_ID, DEFAULT_CAPABILITIES}); SigningDetails singleSignerDetails = createSigningDetails(FIRST_SIGNATURE); assertFalse(lineageDetails.hasCommonSignerWithCapability(singleSignerDetails, PERMISSION)); } @Test public void hasCommonSignerWithCapabilities_singleSignerMatchesCurrentSigner_returnsTrue() throws Exception { // If a requesting app is signed by the same current signer as an app with a lineage the // method should return true since the current signer is granted all capabilities. SigningDetails lineageDetails = createSigningDetailsWithLineageAndCapabilities( new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE}, new int[]{SHARED_USER_ID, DEFAULT_CAPABILITIES}); SigningDetails singleSignerDetails = createSigningDetails(SECOND_SIGNATURE); assertTrue(lineageDetails.hasCommonSignerWithCapability(singleSignerDetails, PERMISSION)); } @Test public void hasCommonSignerWithCapabilities_divergingSignersWithCommonSigner_returnsTrue() throws Exception { // This method is intended to allow granting a capability to another app that has a common // signer in the lineage with the capability still granted; this test verifies when the // current signers diverge but a common ancestor has the requested capability this method // returns true. SigningDetails firstLineageDetails = createSigningDetailsWithLineageAndCapabilities( new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE}, new int[]{DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES}); SigningDetails secondLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, SECOND_SIGNATURE, FOURTH_SIGNATURE); assertTrue(firstLineageDetails.hasCommonSignerWithCapability(secondLineageDetails, PERMISSION)); } @Test public void hasCommonSignerWithCapabilities_divergingSignersOneGrantsCapability_returnsTrue() throws Exception { // If apps have multiple common signers in the lineage with one denying the requested // capability but the other granting it this method should return true. SigningDetails firstLineageDetails = createSigningDetailsWithLineageAndCapabilities( new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE}, new int[]{SHARED_USER_ID, DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES}); SigningDetails secondLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, SECOND_SIGNATURE, FOURTH_SIGNATURE); assertTrue(firstLineageDetails.hasCommonSignerWithCapability(secondLineageDetails, PERMISSION)); } @Test public void hasCommonSignerWithCapabilities_divergingSignersNoneGrantCapability_returnsFalse() throws Exception { // If apps have multiple common signers in the lineage with all denying the requested // capability this method should return false. SigningDetails firstLineageDetails = createSigningDetailsWithLineageAndCapabilities( new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE}, new int[]{SHARED_USER_ID, AUTH, DEFAULT_CAPABILITIES}); SigningDetails secondLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, SECOND_SIGNATURE, FOURTH_SIGNATURE); assertFalse(firstLineageDetails.hasCommonSignerWithCapability(secondLineageDetails, PERMISSION)); } @Test public void hasCommonSignerWithCapabilities_divergingSignersNoneGrantsAllCapabilities_returnsTrue() throws Exception { // If an app has multiple common signers in the lineage, each granting one of the requested // capabilities but neither granting all this method should return false since a single // common ancestor must grant all requested capabilities. SigningDetails firstLineageDetails = createSigningDetailsWithLineageAndCapabilities( new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE}, new int[]{SHARED_USER_ID, PERMISSION, DEFAULT_CAPABILITIES}); SigningDetails secondLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, SECOND_SIGNATURE, FOURTH_SIGNATURE); assertFalse(firstLineageDetails.hasCommonSignerWithCapability(secondLineageDetails, PERMISSION | SHARED_USER_ID)); } @Test public void hasCommonSignerWithCapabilities_currentSignerInLineageOfRequestingApp_returnsTrue() throws Exception { // If the current signer of an app is in the lineage of the requesting app then this method // should return true since the current signer is granted all capabilities. SigningDetails firstLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, SECOND_SIGNATURE); SigningDetails secondLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE); assertTrue(firstLineageDetails.hasCommonSignerWithCapability(secondLineageDetails, PERMISSION)); } @Test public void hasCommonSignerWithCapabilities_currentSignerInLineageOfDeclaringApp_returnsTrue() throws Exception { // If the current signer of a requesting app with a lineage is in the lineage of the // declaring app and that previous signature is granted the requested capability the method // should return true. SigningDetails declaringDetails = createSigningDetailsWithLineageAndCapabilities( new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE}, new int[]{SHARED_USER_ID, DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES}); SigningDetails requestingDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, SECOND_SIGNATURE); assertTrue(declaringDetails.hasCommonSignerWithCapability(requestingDetails, PERMISSION)); } @Test public void hasCommonSignerWithCapabilities_oneSignerNullLineage_returns() throws Exception { // While the pastSigningCertificates should only be null in the case of multiple current // signers there are instances where this can be null with a single signer; verify that a // null pastSigningCertificates array in either SigningDetails does not result in a // NullPointerException. SigningDetails firstDetails = createSigningDetails(true, FIRST_SIGNATURE); SigningDetails secondDetails = createSigningDetails(SECOND_SIGNATURE); assertFalse(firstDetails.hasCommonSignerWithCapability(secondDetails, PERMISSION)); assertFalse(secondDetails.hasCommonSignerWithCapability(firstDetails, PERMISSION)); } @Test public void hasCommonSignerWithCapabilities_unknownSigner_returnsFalse() throws Exception { // An unknown SigningDetails for either instance should immediately result in false being // returned. SigningDetails firstDetails = SigningDetails.UNKNOWN; SigningDetails secondDetails = createSigningDetails(FIRST_SIGNATURE); assertFalse(firstDetails.hasCommonSignerWithCapability(secondDetails, PERMISSION)); assertFalse(secondDetails.hasCommonSignerWithCapability(firstDetails, PERMISSION)); } private SigningDetails createSigningDetailsWithLineage(String... signers) throws Exception { int[] capabilities = new int[signers.length]; for (int i = 0; i < capabilities.length; i++) { Loading @@ -629,10 +841,34 @@ public class SigningDetailsTest { } private SigningDetails createSigningDetails(String... signers) throws Exception { return createSigningDetails(false, signers); } private SigningDetails createSigningDetails(boolean useNullPastSigners, String... signers) throws Exception { Signature[] currentSignatures = new Signature[signers.length]; for (int i = 0; i < signers.length; i++) { currentSignatures[i] = new Signature(signers[i]); } // If there are multiple signers then the pastSigningCertificates should be set to null, but // if there is only a single signer both the current signer and the past signers should be // set to that one signer. if (signers.length > 1) { return new SigningDetails(currentSignatures, SIGNING_BLOCK_V3, null); } return new SigningDetails(currentSignatures, SIGNING_BLOCK_V3, currentSignatures); } private void assertSigningDetailsContainsLineage(SigningDetails details, String... pastSigners) { // This method should only be invoked for results that contain a single signer. assertEquals(1, details.signatures.length); assertTrue(details.signatures[0].toCharsString().equalsIgnoreCase( pastSigners[pastSigners.length - 1])); Set<String> signatures = new ArraySet<>(pastSigners); for (Signature pastSignature : details.pastSigningCertificates) { assertTrue(signatures.remove(pastSignature.toCharsString())); } assertEquals(0, signatures.size()); } } services/core/java/com/android/server/pm/permission/PermissionManagerService.java +3 −2 Original line number Diff line number Diff line Loading @@ -3356,11 +3356,12 @@ public class PermissionManagerService extends IPermissionManager.Stub { // - or its signing certificate was rotated from the source package's certificate // - or its signing certificate is a previous signing certificate of the defining // package, and the defining package still trusts the old certificate for permissions // - or it shares a common signing certificate in its lineage with the defining package, // and the defining package still trusts the old certificate for permissions // - or it shares the above relationships with the system package final PackageParser.SigningDetails sourceSigningDetails = getSourcePackageSigningDetails(bp); return pkg.getSigningDetails().hasAncestorOrSelf(sourceSigningDetails) || sourceSigningDetails.checkCapability( return sourceSigningDetails.hasCommonSignerWithCapability( pkg.getSigningDetails(), PackageParser.SigningDetails.CertCapabilities.PERMISSION) || pkg.getSigningDetails().hasAncestorOrSelf(systemPackage.getSigningDetails()) Loading Loading
core/java/android/content/pm/PackageParser.java +49 −0 Original line number Diff line number Diff line Loading @@ -6264,6 +6264,55 @@ public class PackageParser { return false; } /** * Returns whether this {@code SigningDetails} has a signer in common with the provided * {@code otherDetails} with the specified {@code flags} capabilities provided by this * signer. * * <p>Note this method allows for the signing lineage to diverge, so this should only be * used for instances where the only requirement is a common signer in the lineage with * the specified capabilities. If the current signer of this instance is an ancestor of * {@code otherDetails} then {@code true} is immediately returned since the current signer * has all capabilities granted. */ public boolean hasCommonSignerWithCapability(SigningDetails otherDetails, @CertCapabilities int flags) { if (this == UNKNOWN || otherDetails == UNKNOWN) { return false; } // If either is signed with more than one signer then both must be signed by the same // signers to consider the capabilities granted. if (signatures.length > 1 || otherDetails.signatures.length > 1) { return signaturesMatchExactly(otherDetails); } // The Signature class does not use the granted capabilities in the hashCode // computation, so a Set can be used to check for a common signer. Set<Signature> otherSignatures = new ArraySet<>(); if (otherDetails.hasPastSigningCertificates()) { otherSignatures.addAll(Arrays.asList(otherDetails.pastSigningCertificates)); } else { otherSignatures.addAll(Arrays.asList(otherDetails.signatures)); } // If the current signer of this instance is an ancestor of the other than return true // since all capabilities are granted to the current signer. if (otherSignatures.contains(signatures[0])) { return true; } if (hasPastSigningCertificates()) { // Since the current signer was checked above and the last signature in the // pastSigningCertificates is the current signer skip checking the last element. for (int i = 0; i < pastSigningCertificates.length - 1; i++) { if (otherSignatures.contains(pastSigningCertificates[i])) { // If the caller specified multiple capabilities ensure all are set. if ((pastSigningCertificates[i].getFlags() & flags) == flags) { return true; } } } } return false; } /** * Determines if the provided {@code oldDetails} is an ancestor of this one, and whether or * not this one grants it the provided capability, represented by the {@code flags} Loading
core/tests/coretests/src/android/content/pm/SigningDetailsTest.java +241 −5 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import android.content.pm.PackageParser.SigningDetails; import android.util.ArraySet; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; Loading @@ -35,6 +36,8 @@ import androidx.test.filters.SmallTest; import org.junit.Test; import org.junit.runner.RunWith; import java.util.Set; @RunWith(AndroidJUnit4.class) @SmallTest public class SigningDetailsTest { Loading Loading @@ -208,8 +211,8 @@ public class SigningDetailsTest { SigningDetails result1 = noLineageDetails.mergeLineageWith(lineageDetails); SigningDetails result2 = lineageDetails.mergeLineageWith(noLineageDetails); assertTrue(result1 == lineageDetails); assertTrue(result2 == lineageDetails); assertSigningDetailsContainsLineage(result1, FIRST_SIGNATURE, SECOND_SIGNATURE); assertSigningDetailsContainsLineage(result2, FIRST_SIGNATURE, SECOND_SIGNATURE); } @Test Loading Loading @@ -271,8 +274,10 @@ public class SigningDetailsTest { SigningDetails result1 = singleSignerDetails.mergeLineageWith(fullLineageDetails); SigningDetails result2 = fullLineageDetails.mergeLineageWith(singleSignerDetails); assertTrue(result1 == fullLineageDetails); assertTrue(result2 == fullLineageDetails); assertSigningDetailsContainsLineage(result1, FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE); assertSigningDetailsContainsLineage(result2, FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE); } @Test Loading Loading @@ -605,6 +610,213 @@ public class SigningDetailsTest { assertTrue(secondLineageDetails.hasCommonAncestor(firstLineageDetails)); } @Test public void hasCommonSignerWithCapabilities_singleMatchingSigner_returnsTrue() throws Exception { // The hasCommonSignerWithCapabilities method is intended to grant the specified // capabilities to a requesting package that has a common signer in the lineage (or as the // current signer) even if their signing identities have diverged. This test verifies if the // two SigningDetails have the same single signer then the requested capability can be // granted since the current signer always has all capabilities granted. SigningDetails firstDetails = createSigningDetails(FIRST_SIGNATURE); SigningDetails secondSignerDetails = createSigningDetails(FIRST_SIGNATURE); assertTrue(firstDetails.hasCommonSignerWithCapability(secondSignerDetails, PERMISSION)); } @Test public void hasCommonSignerWithCapabilities_singleDifferentSigners_returnsFalse() throws Exception { // If each package is signed by a single different signer then the method should return // false since there is no shared signer. SigningDetails firstDetails = createSigningDetails(FIRST_SIGNATURE); SigningDetails secondDetails = createSigningDetails(SECOND_SIGNATURE); assertFalse(firstDetails.hasCommonSignerWithCapability(secondDetails, PERMISSION)); assertFalse(secondDetails.hasCommonSignerWithCapability(firstDetails, PERMISSION)); } @Test public void hasCommonSignerWithCapabilities_oneWithMultipleSigners_returnsFalse() throws Exception { // If one of the packages is signed with multiple signers and the other only a single signer // this method should return false since all signers must match exactly for multiple signer // cases. SigningDetails firstDetails = createSigningDetails(FIRST_SIGNATURE, SECOND_SIGNATURE); SigningDetails secondDetails = createSigningDetails(FIRST_SIGNATURE); assertFalse(firstDetails.hasCommonSignerWithCapability(secondDetails, PERMISSION)); assertFalse(secondDetails.hasCommonSignerWithCapability(firstDetails, PERMISSION)); } @Test public void hasCommonSignerWithCapabilities_multipleMatchingSigners_returnsTrue() throws Exception { // if both packages are signed by the same multiple signers then this method should return // true since the current signer is granted all capabilities. SigningDetails firstDetails = createSigningDetails(FIRST_SIGNATURE, SECOND_SIGNATURE); SigningDetails secondDetails = createSigningDetails(SECOND_SIGNATURE, FIRST_SIGNATURE); assertTrue(firstDetails.hasCommonSignerWithCapability(secondDetails, PERMISSION)); assertTrue(secondDetails.hasCommonSignerWithCapability(firstDetails, PERMISSION)); } @Test public void hasCommonSignerWithCapabilities_singleSignerInLineage_returnsTrue() throws Exception { // if a single signer is in the lineage and that previous signer has the requested // capability then this method should return true. SigningDetails lineageDetails = createSigningDetailsWithLineageAndCapabilities( new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE}, new int[]{DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES}); SigningDetails singleSignerDetails = createSigningDetails(FIRST_SIGNATURE); assertTrue(lineageDetails.hasCommonSignerWithCapability(singleSignerDetails, PERMISSION)); } @Test public void hasCommonSignerWithCapabilities_singleSignerInLineageWOCapability_returnsFalse() throws Exception { // If a single signer is in the lineage and that previous signer does not have the requested // capability then this method should return false. SigningDetails lineageDetails = createSigningDetailsWithLineageAndCapabilities( new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE}, new int[]{SHARED_USER_ID, DEFAULT_CAPABILITIES}); SigningDetails singleSignerDetails = createSigningDetails(FIRST_SIGNATURE); assertFalse(lineageDetails.hasCommonSignerWithCapability(singleSignerDetails, PERMISSION)); } @Test public void hasCommonSignerWithCapabilities_singleSignerMatchesCurrentSigner_returnsTrue() throws Exception { // If a requesting app is signed by the same current signer as an app with a lineage the // method should return true since the current signer is granted all capabilities. SigningDetails lineageDetails = createSigningDetailsWithLineageAndCapabilities( new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE}, new int[]{SHARED_USER_ID, DEFAULT_CAPABILITIES}); SigningDetails singleSignerDetails = createSigningDetails(SECOND_SIGNATURE); assertTrue(lineageDetails.hasCommonSignerWithCapability(singleSignerDetails, PERMISSION)); } @Test public void hasCommonSignerWithCapabilities_divergingSignersWithCommonSigner_returnsTrue() throws Exception { // This method is intended to allow granting a capability to another app that has a common // signer in the lineage with the capability still granted; this test verifies when the // current signers diverge but a common ancestor has the requested capability this method // returns true. SigningDetails firstLineageDetails = createSigningDetailsWithLineageAndCapabilities( new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE}, new int[]{DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES}); SigningDetails secondLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, SECOND_SIGNATURE, FOURTH_SIGNATURE); assertTrue(firstLineageDetails.hasCommonSignerWithCapability(secondLineageDetails, PERMISSION)); } @Test public void hasCommonSignerWithCapabilities_divergingSignersOneGrantsCapability_returnsTrue() throws Exception { // If apps have multiple common signers in the lineage with one denying the requested // capability but the other granting it this method should return true. SigningDetails firstLineageDetails = createSigningDetailsWithLineageAndCapabilities( new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE}, new int[]{SHARED_USER_ID, DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES}); SigningDetails secondLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, SECOND_SIGNATURE, FOURTH_SIGNATURE); assertTrue(firstLineageDetails.hasCommonSignerWithCapability(secondLineageDetails, PERMISSION)); } @Test public void hasCommonSignerWithCapabilities_divergingSignersNoneGrantCapability_returnsFalse() throws Exception { // If apps have multiple common signers in the lineage with all denying the requested // capability this method should return false. SigningDetails firstLineageDetails = createSigningDetailsWithLineageAndCapabilities( new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE}, new int[]{SHARED_USER_ID, AUTH, DEFAULT_CAPABILITIES}); SigningDetails secondLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, SECOND_SIGNATURE, FOURTH_SIGNATURE); assertFalse(firstLineageDetails.hasCommonSignerWithCapability(secondLineageDetails, PERMISSION)); } @Test public void hasCommonSignerWithCapabilities_divergingSignersNoneGrantsAllCapabilities_returnsTrue() throws Exception { // If an app has multiple common signers in the lineage, each granting one of the requested // capabilities but neither granting all this method should return false since a single // common ancestor must grant all requested capabilities. SigningDetails firstLineageDetails = createSigningDetailsWithLineageAndCapabilities( new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE}, new int[]{SHARED_USER_ID, PERMISSION, DEFAULT_CAPABILITIES}); SigningDetails secondLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, SECOND_SIGNATURE, FOURTH_SIGNATURE); assertFalse(firstLineageDetails.hasCommonSignerWithCapability(secondLineageDetails, PERMISSION | SHARED_USER_ID)); } @Test public void hasCommonSignerWithCapabilities_currentSignerInLineageOfRequestingApp_returnsTrue() throws Exception { // If the current signer of an app is in the lineage of the requesting app then this method // should return true since the current signer is granted all capabilities. SigningDetails firstLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, SECOND_SIGNATURE); SigningDetails secondLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE); assertTrue(firstLineageDetails.hasCommonSignerWithCapability(secondLineageDetails, PERMISSION)); } @Test public void hasCommonSignerWithCapabilities_currentSignerInLineageOfDeclaringApp_returnsTrue() throws Exception { // If the current signer of a requesting app with a lineage is in the lineage of the // declaring app and that previous signature is granted the requested capability the method // should return true. SigningDetails declaringDetails = createSigningDetailsWithLineageAndCapabilities( new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE}, new int[]{SHARED_USER_ID, DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES}); SigningDetails requestingDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, SECOND_SIGNATURE); assertTrue(declaringDetails.hasCommonSignerWithCapability(requestingDetails, PERMISSION)); } @Test public void hasCommonSignerWithCapabilities_oneSignerNullLineage_returns() throws Exception { // While the pastSigningCertificates should only be null in the case of multiple current // signers there are instances where this can be null with a single signer; verify that a // null pastSigningCertificates array in either SigningDetails does not result in a // NullPointerException. SigningDetails firstDetails = createSigningDetails(true, FIRST_SIGNATURE); SigningDetails secondDetails = createSigningDetails(SECOND_SIGNATURE); assertFalse(firstDetails.hasCommonSignerWithCapability(secondDetails, PERMISSION)); assertFalse(secondDetails.hasCommonSignerWithCapability(firstDetails, PERMISSION)); } @Test public void hasCommonSignerWithCapabilities_unknownSigner_returnsFalse() throws Exception { // An unknown SigningDetails for either instance should immediately result in false being // returned. SigningDetails firstDetails = SigningDetails.UNKNOWN; SigningDetails secondDetails = createSigningDetails(FIRST_SIGNATURE); assertFalse(firstDetails.hasCommonSignerWithCapability(secondDetails, PERMISSION)); assertFalse(secondDetails.hasCommonSignerWithCapability(firstDetails, PERMISSION)); } private SigningDetails createSigningDetailsWithLineage(String... signers) throws Exception { int[] capabilities = new int[signers.length]; for (int i = 0; i < capabilities.length; i++) { Loading @@ -629,10 +841,34 @@ public class SigningDetailsTest { } private SigningDetails createSigningDetails(String... signers) throws Exception { return createSigningDetails(false, signers); } private SigningDetails createSigningDetails(boolean useNullPastSigners, String... signers) throws Exception { Signature[] currentSignatures = new Signature[signers.length]; for (int i = 0; i < signers.length; i++) { currentSignatures[i] = new Signature(signers[i]); } // If there are multiple signers then the pastSigningCertificates should be set to null, but // if there is only a single signer both the current signer and the past signers should be // set to that one signer. if (signers.length > 1) { return new SigningDetails(currentSignatures, SIGNING_BLOCK_V3, null); } return new SigningDetails(currentSignatures, SIGNING_BLOCK_V3, currentSignatures); } private void assertSigningDetailsContainsLineage(SigningDetails details, String... pastSigners) { // This method should only be invoked for results that contain a single signer. assertEquals(1, details.signatures.length); assertTrue(details.signatures[0].toCharsString().equalsIgnoreCase( pastSigners[pastSigners.length - 1])); Set<String> signatures = new ArraySet<>(pastSigners); for (Signature pastSignature : details.pastSigningCertificates) { assertTrue(signatures.remove(pastSignature.toCharsString())); } assertEquals(0, signatures.size()); } }
services/core/java/com/android/server/pm/permission/PermissionManagerService.java +3 −2 Original line number Diff line number Diff line Loading @@ -3356,11 +3356,12 @@ public class PermissionManagerService extends IPermissionManager.Stub { // - or its signing certificate was rotated from the source package's certificate // - or its signing certificate is a previous signing certificate of the defining // package, and the defining package still trusts the old certificate for permissions // - or it shares a common signing certificate in its lineage with the defining package, // and the defining package still trusts the old certificate for permissions // - or it shares the above relationships with the system package final PackageParser.SigningDetails sourceSigningDetails = getSourcePackageSigningDetails(bp); return pkg.getSigningDetails().hasAncestorOrSelf(sourceSigningDetails) || sourceSigningDetails.checkCapability( return sourceSigningDetails.hasCommonSignerWithCapability( pkg.getSigningDetails(), PackageParser.SigningDetails.CertCapabilities.PERMISSION) || pkg.getSigningDetails().hasAncestorOrSelf(systemPackage.getSigningDetails()) Loading