Fix domain verify restore and add signature check

Verifies that all signatures of the package being restored matches by
SHA256 hash. And fixes a bug where the restored packages weren't being
added to the internal structures.

Bug: 170746586

Test: atest DomainVerificationPackageTest

Change-Id: I2cf2e739a88396475599bc4c1ff27c36e09a8d45
This commit is contained in:
Winson
2021-03-31 14:04:26 -07:00
parent d65f0926dc
commit 3482338bc1
3 changed files with 134 additions and 20 deletions

View File

@@ -925,6 +925,11 @@ public class DomainVerificationService extends SystemService
sendBroadcast = false;
} else {
pkgState = mSettings.removeRestoredState(pkgName);
if (pkgState != null && !Objects.equals(pkgState.getBackupSignatureHash(),
PackageUtils.computeSignaturesSha256Digest(newPkgSetting.getSignatures()))) {
// If restoring and the signatures don't match, drop the state
pkgState = null;
}
}
AndroidPackage pkg = newPkgSetting.getPkg();

View File

@@ -157,16 +157,20 @@ class DomainVerificationSettings {
// mergePkgState runs, without the merge part.
ArrayMap<String, Integer> stateMap = newState.getStateMap();
int size = stateMap.size();
for (int index = 0; index < size; index++) {
for (int index = size - 1; index >= 0; index--) {
Integer stateInteger = stateMap.valueAt(index);
if (stateInteger != null) {
int state = stateInteger;
if (state == DomainVerificationState.STATE_SUCCESS
|| state == DomainVerificationState.STATE_RESTORED) {
stateMap.setValueAt(index, state);
stateMap.setValueAt(index, DomainVerificationState.STATE_RESTORED);
} else {
stateMap.removeAt(index);
}
}
}
mRestoredPkgStates.put(pkgName, newState);
}
}
}

View File

@@ -19,8 +19,10 @@ package com.android.server.pm.test.verify.domain
import android.content.Intent
import android.content.pm.PackageManager
import android.content.pm.PackageUserState
import android.content.pm.Signature
import android.content.pm.parsing.component.ParsedActivity
import android.content.pm.parsing.component.ParsedIntentInfo
import android.content.pm.verify.domain.DomainVerificationInfo.STATE_MODIFIABLE_VERIFIED
import android.content.pm.verify.domain.DomainVerificationInfo.STATE_NO_RESPONSE
import android.content.pm.verify.domain.DomainVerificationInfo.STATE_SUCCESS
import android.content.pm.verify.domain.DomainVerificationInfo.STATE_UNMODIFIABLE
@@ -42,7 +44,6 @@ import com.android.server.testutils.mockThrowOnUnmocked
import com.android.server.testutils.whenever
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.mockito.ArgumentMatchers
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyLong
@@ -56,6 +57,10 @@ class DomainVerificationPackageTest {
private const val PKG_TWO = "com.test.two"
private val UUID_ONE = UUID.fromString("1b041c96-8d37-4932-a858-561bfac5947c")
private val UUID_TWO = UUID.fromString("a3389c16-7f9f-4e86-85e3-500d1249c74c")
private const val SIGNATURE_ONE = "AA"
private const val DIGEST_ONE =
"BCEEF655B5A034911F1C3718CE056531B45EF03B4C7B1F15629E867294011A7D"
private const val SIGNATURE_TWO = "BB"
private val DOMAIN_BASE = DomainVerificationPackageTest::class.java.packageName
private val DOMAIN_1 = "one.$DOMAIN_BASE"
@@ -66,8 +71,8 @@ class DomainVerificationPackageTest {
private const val USER_ID = 0
}
private val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE)
private val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO)
private val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE, SIGNATURE_ONE)
private val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO, SIGNATURE_TWO)
@Test
fun addPackageFirstTime() {
@@ -95,6 +100,104 @@ class DomainVerificationPackageTest {
.containsExactly(pkg1.getName())
}
@Test
fun addPackageRestoredMatchingSignature() {
// language=XML
val xml = """
<?xml?>
<domain-verifications>
<active>
<package-state
packageName="${pkg1.getName()}"
id="${pkg1.domainSetId}"
signature="$DIGEST_ONE"
>
<state>
<domain name="$DOMAIN_1" state="1"/>
</state>
</package-state>
</active>
</domain-verifications>
"""
val service = makeService(pkg1, pkg2)
service.restoreSettings(Xml.resolvePullParser(xml.byteInputStream()))
service.addPackage(pkg1)
val info = service.getInfo(pkg1.getName())
assertThat(info.packageName).isEqualTo(pkg1.getName())
assertThat(info.identifier).isEqualTo(pkg1.domainSetId)
assertThat(info.hostToStateMap).containsExactlyEntriesIn(
mapOf(
DOMAIN_1 to STATE_MODIFIABLE_VERIFIED,
DOMAIN_2 to STATE_NO_RESPONSE,
)
)
val userState = service.getUserState(pkg1.getName())
assertThat(userState.packageName).isEqualTo(pkg1.getName())
assertThat(userState.identifier).isEqualTo(pkg1.domainSetId)
assertThat(userState.isLinkHandlingAllowed).isEqualTo(true)
assertThat(userState.user.identifier).isEqualTo(USER_ID)
assertThat(userState.hostToStateMap).containsExactlyEntriesIn(
mapOf(
DOMAIN_1 to DOMAIN_STATE_VERIFIED,
DOMAIN_2 to DOMAIN_STATE_NONE,
)
)
assertThat(service.queryValidVerificationPackageNames())
.containsExactly(pkg1.getName())
}
@Test
fun addPackageRestoredMismatchSignature() {
// language=XML
val xml = """
<?xml?>
<domain-verifications>
<active>
<package-state
packageName="${pkg1.getName()}"
id="${pkg1.domainSetId}"
signature="INVALID_SIGNATURE"
>
<state>
<domain name="$DOMAIN_1" state="1"/>
</state>
</package-state>
</active>
</domain-verifications>
"""
val service = makeService(pkg1, pkg2)
service.restoreSettings(Xml.resolvePullParser(xml.byteInputStream()))
service.addPackage(pkg1)
val info = service.getInfo(pkg1.getName())
assertThat(info.packageName).isEqualTo(pkg1.getName())
assertThat(info.identifier).isEqualTo(pkg1.domainSetId)
assertThat(info.hostToStateMap).containsExactlyEntriesIn(
mapOf(
DOMAIN_1 to STATE_NO_RESPONSE,
DOMAIN_2 to STATE_NO_RESPONSE,
)
)
val userState = service.getUserState(pkg1.getName())
assertThat(userState.packageName).isEqualTo(pkg1.getName())
assertThat(userState.identifier).isEqualTo(pkg1.domainSetId)
assertThat(userState.isLinkHandlingAllowed).isEqualTo(true)
assertThat(userState.user.identifier).isEqualTo(USER_ID)
assertThat(userState.hostToStateMap).containsExactlyEntriesIn(
mapOf(
DOMAIN_1 to DOMAIN_STATE_NONE,
DOMAIN_2 to DOMAIN_STATE_NONE,
)
)
assertThat(service.queryValidVerificationPackageNames())
.containsExactly(pkg1.getName())
}
@Test
fun addPackageActive() {
// language=XML
@@ -153,10 +256,9 @@ class DomainVerificationPackageTest {
@Test
fun migratePackageDropDomain() {
val pkgName = PKG_ONE
val pkgBefore = mockPkgSetting(pkgName, UUID_ONE,
listOf(DOMAIN_1, DOMAIN_2, DOMAIN_3, DOMAIN_4))
val pkgAfter = mockPkgSetting(pkgName, UUID_TWO,
listOf(DOMAIN_1, DOMAIN_2))
val pkgBefore = mockPkgSetting(pkgName, UUID_ONE, SIGNATURE_ONE,
listOf(DOMAIN_1, DOMAIN_2, DOMAIN_3, DOMAIN_4))
val pkgAfter = mockPkgSetting(pkgName, UUID_TWO, SIGNATURE_TWO, listOf(DOMAIN_1, DOMAIN_2))
// Test 4 domains:
// 1 will be approved and preserved, 2 will be selected and preserved,
@@ -215,8 +317,8 @@ class DomainVerificationPackageTest {
@Test
fun migratePackageDropAll() {
val pkgName = PKG_ONE
val pkgBefore = mockPkgSetting(pkgName, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
val pkgAfter = mockPkgSetting(pkgName, UUID_TWO, emptyList())
val pkgBefore = mockPkgSetting(pkgName, UUID_ONE, SIGNATURE_ONE, listOf(DOMAIN_1, DOMAIN_2))
val pkgAfter = mockPkgSetting(pkgName, UUID_TWO, SIGNATURE_TWO, emptyList())
val map = mutableMapOf<String, PackageSetting>()
val service = makeService { map[it] }
@@ -257,10 +359,9 @@ class DomainVerificationPackageTest {
@Test
fun migratePackageAddDomain() {
val pkgName = PKG_ONE
val pkgBefore = mockPkgSetting(pkgName, UUID_ONE,
listOf(DOMAIN_1, DOMAIN_2))
val pkgAfter = mockPkgSetting(pkgName, UUID_TWO,
listOf(DOMAIN_1, DOMAIN_2, DOMAIN_3))
val pkgBefore = mockPkgSetting(pkgName, UUID_ONE, SIGNATURE_ONE, listOf(DOMAIN_1, DOMAIN_2))
val pkgAfter = mockPkgSetting(pkgName, UUID_TWO, SIGNATURE_TWO,
listOf(DOMAIN_1, DOMAIN_2, DOMAIN_3))
// Test 3 domains:
// 1 will be verified and preserved, 2 will be selected and preserved,
@@ -309,8 +410,8 @@ class DomainVerificationPackageTest {
@Test
fun migratePackageAddAll() {
val pkgName = PKG_ONE
val pkgBefore = mockPkgSetting(pkgName, UUID_ONE, emptyList())
val pkgAfter = mockPkgSetting(pkgName, UUID_TWO, listOf(DOMAIN_1, DOMAIN_2))
val pkgBefore = mockPkgSetting(pkgName, UUID_ONE, SIGNATURE_ONE, emptyList())
val pkgAfter = mockPkgSetting(pkgName, UUID_TWO, SIGNATURE_TWO, listOf(DOMAIN_1, DOMAIN_2))
val map = mutableMapOf<String, PackageSetting>()
val service = makeService { map[it] }
@@ -387,9 +488,12 @@ class DomainVerificationPackageTest {
})
}
private fun mockPkgSetting(pkgName: String, domainSetId: UUID, domains: List<String> = listOf(
DOMAIN_1, DOMAIN_2
)) = mockThrowOnUnmocked<PackageSetting> {
private fun mockPkgSetting(
pkgName: String,
domainSetId: UUID,
signature: String,
domains: List<String> = listOf(DOMAIN_1, DOMAIN_2)
) = mockThrowOnUnmocked<PackageSetting> {
val pkg = mockThrowOnUnmocked<AndroidPackage> {
whenever(packageName) { pkgName }
whenever(targetSdkVersion) { Build.VERSION_CODES.S }
@@ -423,5 +527,6 @@ class DomainVerificationPackageTest {
whenever(getInstantApp(anyInt())) { false }
whenever(firstInstallTime) { 0L }
whenever(readUserState(USER_ID)) { PackageUserState() }
whenever(signatures) { arrayOf(Signature(signature)) }
}
}