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

Commit 762658fa authored by Winson's avatar Winson
Browse files

Enforce isSystem for app-link SysConfig

Only apps that are preloaded on the system are allowed to be auto
approved. This prevents package name takeover for optional packages
that are still configured in the XML.

Bug: 166697328

Test: atest DomainVerificationPackageTest
Test: manual verify pm get-app-links on a device

Change-Id: Ic055db3c07ff876600402c3ed0fe66b30cebe09e
parent 05f4f582
Loading
Loading
Loading
Loading
+13 −17
Original line number Diff line number Diff line
@@ -892,7 +892,7 @@ public class DomainVerificationService extends SystemService

            boolean hasAutoVerifyDomains = newDomainsSize > 0;
            boolean needsBroadcast =
                    applyImmutableState(pkgName, newStateMap, newAutoVerifyDomains);
                    applyImmutableState(newPkgSetting, newStateMap, newAutoVerifyDomains);

            sendBroadcast = hasAutoVerifyDomains && needsBroadcast;

@@ -943,7 +943,8 @@ public class DomainVerificationService extends SystemService
            pkgState = new DomainVerificationPkgState(pkgName, domainSetId, hasAutoVerifyDomains);
        }

        boolean needsBroadcast = applyImmutableState(pkgState, domains);
        boolean needsBroadcast =
                applyImmutableState(newPkgSetting, pkgState.getStateMap(), domains);
        if (needsBroadcast && !isPendingOrRestored) {
            // TODO(b/159952358): Test this behavior
            // Attempt to preserve user experience by automatically verifying all domains from
@@ -990,22 +991,17 @@ public class DomainVerificationService extends SystemService
        }
    }

    private boolean applyImmutableState(@NonNull DomainVerificationPkgState pkgState,
            @NonNull ArraySet<String> autoVerifyDomains) {
        return applyImmutableState(pkgState.getPackageName(), pkgState.getStateMap(),
                autoVerifyDomains);
    }

    /**
     * Applies any immutable state as the final step when adding or migrating state. Currently only
     * applies {@link SystemConfig#getLinkedApps()}, which approves all domains for a package.
     * applies {@link SystemConfig#getLinkedApps()}, which approves all domains for a system app.
     *
     * @return whether or not a broadcast is necessary for this package
     */
    private boolean applyImmutableState(@NonNull String packageName,
    private boolean applyImmutableState(@NonNull PackageSetting pkgSetting,
            @NonNull ArrayMap<String, Integer> stateMap,
            @NonNull ArraySet<String> autoVerifyDomains) {
        if (mSystemConfig.getLinkedApps().contains(packageName)) {
        if (pkgSetting.isSystem()
                && mSystemConfig.getLinkedApps().contains(pkgSetting.getName())) {
            int domainsSize = autoVerifyDomains.size();
            for (int index = 0; index < domainsSize; index++) {
                stateMap.put(autoVerifyDomains.valueAt(index),
@@ -1318,7 +1314,7 @@ public class DomainVerificationService extends SystemService
                        if (pkgSetting == null || pkgSetting.getPkg() == null) {
                            continue;
                        }
                        resetDomainState(pkgState, pkgSetting.getPkg());
                        resetDomainState(pkgState.getStateMap(), pkgSetting);
                    }
                } else {
                    int size = packageNames.size();
@@ -1329,7 +1325,7 @@ public class DomainVerificationService extends SystemService
                        if (pkgSetting == null || pkgSetting.getPkg() == null) {
                            continue;
                        }
                        resetDomainState(pkgState, pkgSetting.getPkg());
                        resetDomainState(pkgState.getStateMap(), pkgSetting);
                    }
                }
            }
@@ -1341,9 +1337,8 @@ public class DomainVerificationService extends SystemService
    /**
     * Reset states that are mutable by the domain verification agent.
     */
    private void resetDomainState(@NonNull DomainVerificationPkgState pkgState,
            @NonNull AndroidPackage pkg) {
        ArrayMap<String, Integer> stateMap = pkgState.getStateMap();
    private void resetDomainState(@NonNull ArrayMap<String, Integer> stateMap,
            @NonNull PackageSetting pkgSetting) {
        int size = stateMap.size();
        for (int index = size - 1; index >= 0; index--) {
            Integer state = stateMap.valueAt(index);
@@ -1363,7 +1358,8 @@ public class DomainVerificationService extends SystemService
            }
        }

        applyImmutableState(pkgState, mCollector.collectValidAutoVerifyDomains(pkg));
        applyImmutableState(pkgSetting, stateMap,
                mCollector.collectValidAutoVerifyDomains(pkgSetting.getPkg()));
    }

    @Override
+1 −0
Original line number Diff line number Diff line
@@ -338,6 +338,7 @@ class DomainVerificationEnforcerTest {
            whenever(readUserState(0)) { PackageUserState() }
            whenever(readUserState(1)) { PackageUserState() }
            whenever(getInstantApp(anyInt())) { false }
            whenever(isSystem()) { false }
        }
    }

+1 −0
Original line number Diff line number Diff line
@@ -527,6 +527,7 @@ class DomainVerificationManagerApiTest {
        whenever(firstInstallTime) { 0L }
        whenever(readUserState(0)) { pkgUserState0() }
        whenever(readUserState(1)) { pkgUserState1() }
        whenever(isSystem()) { false }
    }

    private fun DomainVerificationService.addPackages(vararg pkgSettings: PackageSetting) =
+104 −29
Original line number Diff line number Diff line
@@ -100,6 +100,70 @@ class DomainVerificationPackageTest {
                .containsExactly(pkg1.getName())
    }

    @Test
    fun addPackageSystemConfigured() {
        val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE, SIGNATURE_ONE, isSystemApp = false)
        val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO, SIGNATURE_TWO, isSystemApp = true)

        val service = makeService(
            systemConfiguredPackageNames = ArraySet(setOf(pkg1.getName(), pkg2.getName())),
            pkg1, pkg2
        )
        service.addPackage(pkg1)
        service.addPackage(pkg2)

        service.getInfo(pkg1.getName()).apply {
            assertThat(packageName).isEqualTo(pkg1.getName())
            assertThat(identifier).isEqualTo(pkg1.domainSetId)
            assertThat(hostToStateMap).containsExactlyEntriesIn(
                mapOf(
                    DOMAIN_1 to STATE_NO_RESPONSE,
                    DOMAIN_2 to STATE_NO_RESPONSE,
                )
            )
        }

        service.getUserState(pkg1.getName()).apply {
            assertThat(packageName).isEqualTo(pkg1.getName())
            assertThat(identifier).isEqualTo(pkg1.domainSetId)
            assertThat(isLinkHandlingAllowed).isEqualTo(true)
            assertThat(user.identifier).isEqualTo(USER_ID)
            assertThat(hostToStateMap).containsExactlyEntriesIn(
                mapOf(
                    DOMAIN_1 to DOMAIN_STATE_NONE,
                    DOMAIN_2 to DOMAIN_STATE_NONE,
                )
            )
        }

        service.getInfo(pkg2.getName()).apply {
            assertThat(packageName).isEqualTo(pkg2.getName())
            assertThat(identifier).isEqualTo(pkg2.domainSetId)
            assertThat(hostToStateMap).containsExactlyEntriesIn(
                mapOf(
                    DOMAIN_1 to STATE_UNMODIFIABLE,
                    DOMAIN_2 to STATE_UNMODIFIABLE,
                )
            )
        }

        service.getUserState(pkg2.getName()).apply {
            assertThat(packageName).isEqualTo(pkg2.getName())
            assertThat(identifier).isEqualTo(pkg2.domainSetId)
            assertThat(isLinkHandlingAllowed).isEqualTo(true)
            assertThat(user.identifier).isEqualTo(USER_ID)
            assertThat(hostToStateMap).containsExactlyEntriesIn(
                mapOf(
                    DOMAIN_1 to DOMAIN_STATE_VERIFIED,
                    DOMAIN_2 to DOMAIN_STATE_VERIFIED,
                )
            )
        }

        assertThat(service.queryValidVerificationPackageNames())
                .containsExactly(pkg1.getName(), pkg2.getName())
    }

    @Test
    fun addPackageRestoredMatchingSignature() {
        // language=XML
@@ -457,18 +521,27 @@ class DomainVerificationPackageTest {
            getDomainVerificationUserState(pkgName, USER_ID)
                    .also { assertThat(it).isNotNull() }!!

    private fun makeService(
        systemConfiguredPackageNames: ArraySet<String> = ArraySet(),
        vararg pkgSettings: PackageSetting
    ) = makeService(systemConfiguredPackageNames = systemConfiguredPackageNames) {
        pkgName -> pkgSettings.find { pkgName == it.getName() }
    }

    private fun makeService(vararg pkgSettings: PackageSetting) =
        makeService { pkgName -> pkgSettings.find { pkgName == it.getName() } }

    private fun makeService(pkgSettingFunction: (String) -> PackageSetting? = { null }) =
            DomainVerificationService(mockThrowOnUnmocked {
    private fun makeService(
        systemConfiguredPackageNames: ArraySet<String> = ArraySet(),
        pkgSettingFunction: (String) -> PackageSetting? = { null }
    ) = DomainVerificationService(mockThrowOnUnmocked {
            // Assume the test has every permission necessary
            whenever(enforcePermission(anyString(), anyInt(), anyInt(), anyString()))
            whenever(checkPermission(anyString(), anyInt(), anyInt())) {
                PackageManager.PERMISSION_GRANTED
            }
        }, mockThrowOnUnmocked {
                whenever(linkedApps) { ArraySet<String>() }
            whenever(this.linkedApps) { systemConfiguredPackageNames }
        }, mockThrowOnUnmocked {
            whenever(isChangeEnabledInternalNoLogging(anyLong(), any())) { true }
        }).apply {
@@ -492,7 +565,8 @@ class DomainVerificationPackageTest {
        pkgName: String,
        domainSetId: UUID,
        signature: String,
        domains: List<String> = listOf(DOMAIN_1, DOMAIN_2)
        domains: List<String> = listOf(DOMAIN_1, DOMAIN_2),
        isSystemApp: Boolean = false
    ) = mockThrowOnUnmocked<PackageSetting> {
        val pkg = mockThrowOnUnmocked<AndroidPackage> {
            whenever(packageName) { pkgName }
@@ -528,5 +602,6 @@ class DomainVerificationPackageTest {
        whenever(firstInstallTime) { 0L }
        whenever(readUserState(USER_ID)) { PackageUserState() }
        whenever(signatures) { arrayOf(Signature(signature)) }
        whenever(isSystem) { isSystemApp }
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -238,6 +238,7 @@ class DomainVerificationSettingsMutationTest {
            whenever(readUserState(0)) { PackageUserState() }
            whenever(readUserState(10)) { PackageUserState() }
            whenever(getInstantApp(anyInt())) { false }
            whenever(isSystem()) { false }
        }
    }

Loading