1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.pm.test.verify.domain 18 19 import android.content.Context 20 import android.content.Intent 21 import android.content.pm.PackageManager 22 import android.content.pm.PackageParser.SigningDetails 23 import android.content.pm.PackageUserState 24 import android.content.pm.parsing.component.ParsedActivity 25 import android.content.pm.parsing.component.ParsedIntentInfo 26 import android.content.pm.verify.domain.DomainVerificationManager 27 import android.content.pm.verify.domain.DomainVerificationState 28 import android.os.Build 29 import android.os.Process 30 import android.util.ArraySet 31 import android.util.IndentingPrintWriter 32 import android.util.SparseArray 33 import androidx.test.platform.app.InstrumentationRegistry 34 import com.android.server.pm.PackageSetting 35 import com.android.server.pm.parsing.pkg.AndroidPackage 36 import com.android.server.pm.test.verify.domain.DomainVerificationTestUtils.mockPackageSettings 37 import com.android.server.pm.verify.domain.DomainVerificationEnforcer 38 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal 39 import com.android.server.pm.verify.domain.DomainVerificationService 40 import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy 41 import com.android.server.testutils.mockThrowOnUnmocked 42 import com.android.server.testutils.spyThrowOnUnmocked 43 import com.android.server.testutils.whenever 44 import org.junit.Test 45 import org.junit.runner.RunWith 46 import org.junit.runners.Parameterized 47 import org.mockito.Mockito.any 48 import org.mockito.Mockito.anyInt 49 import org.mockito.Mockito.anyLong 50 import org.mockito.Mockito.anyString 51 import org.mockito.Mockito.eq 52 import org.mockito.Mockito.mock 53 import org.mockito.Mockito.verifyNoMoreInteractions 54 import java.io.File 55 import java.util.UUID 56 import java.util.concurrent.atomic.AtomicBoolean 57 import java.util.concurrent.atomic.AtomicInteger 58 import kotlin.test.assertFailsWith 59 import kotlin.test.fail 60 61 @RunWith(Parameterized::class) 62 class DomainVerificationEnforcerTest { 63 64 val context: Context = InstrumentationRegistry.getInstrumentation().context 65 66 companion object { 67 private val INTERNAL_UIDS = listOf(Process.ROOT_UID, Process.SHELL_UID, Process.SYSTEM_UID) 68 private const val VERIFIER_UID = Process.FIRST_APPLICATION_UID + 1 69 private const val NON_VERIFIER_UID = Process.FIRST_APPLICATION_UID + 2 70 71 private const val VISIBLE_PKG = "com.test.visible" 72 private val VISIBLE_UUID = UUID.fromString("8db01272-270d-4606-a3db-bb35228ff9a2") 73 private const val INVISIBLE_PKG = "com.test.invisible" 74 private val INVISIBLE_UUID = UUID.fromString("16dcb029-d96c-4a19-833a-4c9d72e2ebc3") 75 76 @JvmStatic 77 @Parameterized.Parameters(name = "{0}") 78 fun parameters(): Array<Any> { 79 val visiblePkg = mockPkg(VISIBLE_PKG) 80 val visiblePkgSetting = mockPkgSetting(VISIBLE_PKG, VISIBLE_UUID) 81 val invisiblePkg = mockPkg(INVISIBLE_PKG) 82 val invisiblePkgSetting = mockPkgSetting(INVISIBLE_PKG, INVISIBLE_UUID) 83 84 val makeEnforcer: (Context) -> DomainVerificationEnforcer = { 85 DomainVerificationEnforcer(it).apply { 86 setCallback(mockThrowOnUnmocked { 87 whenever(filterAppAccess(eq(VISIBLE_PKG), anyInt(), anyInt())) { false } 88 whenever(filterAppAccess(eq(INVISIBLE_PKG), anyInt(), anyInt())) { 89 true 90 } 91 whenever(doesUserExist(anyInt())) { (arguments[0] as Int) <= 1 } 92 }) 93 } 94 } 95 96 val makeService: (Context) -> Triple<AtomicInteger, AtomicInteger, 97 DomainVerificationService> = { 98 val callingUidInt = AtomicInteger(-1) 99 val callingUserIdInt = AtomicInteger(-1) 100 101 val connection: DomainVerificationManagerInternal.Connection = 102 mockThrowOnUnmocked { 103 whenever(callingUid) { callingUidInt.get() } 104 whenever(callingUserId) { callingUserIdInt.get() } 105 mockPackageSettings { 106 when (it) { 107 VISIBLE_PKG -> visiblePkgSetting 108 INVISIBLE_PKG -> invisiblePkgSetting 109 else -> null 110 } 111 } 112 whenever(schedule(anyInt(), any())) 113 whenever(scheduleWriteSettings()) 114 whenever(filterAppAccess(eq(VISIBLE_PKG), anyInt(), anyInt())) { false } 115 whenever(filterAppAccess(eq(INVISIBLE_PKG), anyInt(), anyInt())) { 116 true 117 } 118 whenever(doesUserExist(anyInt())) { (arguments[0] as Int) <= 1 } 119 } 120 val service = DomainVerificationService( 121 it, 122 mockThrowOnUnmocked { whenever(linkedApps) { ArraySet<String>() } }, 123 mockThrowOnUnmocked { 124 whenever(isChangeEnabledInternalNoLogging(anyLong(), any())) { true } 125 }).apply { 126 setConnection(connection) 127 } 128 129 Triple(callingUidInt, callingUserIdInt, service) 130 } 131 132 fun enforcer( 133 type: Type, 134 name: String, 135 block: DomainVerificationEnforcer.(Params.Input<DomainVerificationEnforcer>) -> Any? 136 ) = Params(type, makeEnforcer, name) { 137 it.target.block(it) 138 } 139 140 fun service( 141 type: Type, 142 name: String, 143 block: DomainVerificationService.(Params.Input<Triple<AtomicInteger, AtomicInteger, DomainVerificationService>>) -> Any? 144 ) = Params(type, makeService, name) { 145 val (callingUidInt, callingUserIdInt, service) = it.target 146 callingUidInt.set(it.callingUid) 147 callingUserIdInt.set(it.callingUserId) 148 service.proxy = it.proxy 149 service.addPackage(visiblePkgSetting) 150 service.addPackage(invisiblePkgSetting) 151 service.block(it) 152 } 153 154 return arrayOf( 155 enforcer(Type.INTERNAL, "internal") { 156 assertInternal(it.callingUid) 157 }, 158 enforcer(Type.QUERENT, "approvedQuerent") { 159 assertApprovedQuerent(it.callingUid, it.proxy) 160 }, 161 enforcer(Type.VERIFIER, "approvedVerifier") { 162 assertApprovedVerifier(it.callingUid, it.proxy) 163 }, 164 enforcer( 165 Type.SELECTION_QUERENT, 166 "approvedUserStateQuerent" 167 ) { 168 assertApprovedUserStateQuerent( 169 it.callingUid, it.callingUserId, 170 it.targetPackageName, it.userId 171 ) 172 }, 173 enforcer( 174 Type.SELECTOR, 175 "approvedUserSelector" 176 ) { 177 assertApprovedUserSelector( 178 it.callingUid, it.callingUserId, 179 it.targetPackageName, it.userId 180 ) 181 }, 182 service(Type.INTERNAL, "setStatusInternalPackageName") { 183 setDomainVerificationStatusInternal( 184 it.targetPackageName, 185 DomainVerificationState.STATE_SUCCESS, 186 ArraySet(setOf("example.com")) 187 ) 188 }, 189 service(Type.INTERNAL, "setUserStateInternal") { 190 setDomainVerificationUserSelectionInternal( 191 it.userId, 192 it.targetPackageName, 193 false, 194 ArraySet(setOf("example.com")) 195 ) 196 }, 197 service(Type.INTERNAL, "verifyPackages") { 198 verifyPackages(listOf(it.targetPackageName), true) 199 }, 200 service(Type.INTERNAL, "clearState") { 201 clearDomainVerificationState(listOf(it.targetPackageName)) 202 }, 203 service(Type.INTERNAL, "clearUserStates") { 204 clearUserStates(listOf(it.targetPackageName), it.userId) 205 }, 206 service(Type.VERIFIER, "queryValidPackageNames") { 207 queryValidVerificationPackageNames() 208 }, 209 service(Type.QUERENT, "getInfo") { 210 getDomainVerificationInfo(it.targetPackageName) 211 }, 212 service(Type.QUERENT, "printState") { 213 printState(mock(IndentingPrintWriter::class.java), null, null) 214 }, 215 service(Type.QUERENT, "printStateInternal") { 216 printState(mock(IndentingPrintWriter::class.java), null, null) { 217 mockPkgSetting(it, UUID.randomUUID()) 218 } 219 }, 220 service(Type.VERIFIER, "setStatus") { 221 setDomainVerificationStatus( 222 it.targetDomainSetId, 223 setOf("example.com"), 224 DomainVerificationState.STATE_SUCCESS 225 ) 226 }, 227 service(Type.VERIFIER, "setStatusInternalUid") { 228 setDomainVerificationStatusInternal( 229 it.callingUid, 230 it.targetDomainSetId, 231 setOf("example.com"), 232 DomainVerificationState.STATE_SUCCESS 233 ) 234 }, 235 service(Type.SELECTOR_USER, "setLinkHandlingAllowedUserId") { 236 setDomainVerificationLinkHandlingAllowed(it.targetPackageName, true, it.userId) 237 }, 238 service(Type.SELECTION_QUERENT, "getUserStateUserId") { 239 getDomainVerificationUserState(it.targetPackageName, it.userId) 240 }, 241 service(Type.SELECTOR_USER, "setUserStateUserId") { 242 setDomainVerificationUserSelection( 243 it.targetDomainSetId, 244 setOf("example.com"), 245 true, 246 it.userId 247 ) 248 }, 249 service(Type.LEGACY_SELECTOR, "setLegacyUserState") { 250 setLegacyUserState( 251 it.targetPackageName, it.userId, 252 PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER 253 ) 254 }, 255 service(Type.LEGACY_QUERENT, "getLegacyUserState") { 256 getLegacyState(it.targetPackageName, it.userId) 257 }, 258 service(Type.OWNER_QUERENT_USER, "getOwnersForDomainUserId") { 259 // Re-use package name, since the result itself isn't relevant 260 getOwnersForDomain(it.targetPackageName, it.userId) 261 }, 262 ) 263 } 264 265 data class Params<T : Any>( 266 val type: Type, 267 val construct: (context: Context) -> T, 268 val name: String, 269 private val method: (Input<T>) -> Any? 270 ) { 271 override fun toString() = "${type}_$name" 272 273 fun runMethod( 274 target: Any, 275 callingUid: Int, 276 callingUserId: Int, 277 userId: Int, 278 targetPackageName: String, 279 targetDomainSetId: UUID, 280 proxy: DomainVerificationProxy 281 ): Any? = method( 282 Input( 283 @Suppress("UNCHECKED_CAST") 284 target as T, 285 callingUid, 286 callingUserId, 287 userId, 288 targetPackageName, 289 targetDomainSetId, 290 proxy 291 ) 292 ) 293 294 data class Input<T>( 295 val target: T, 296 val callingUid: Int, 297 val callingUserId: Int, 298 val userId: Int, 299 val targetPackageName: String, 300 val targetDomainSetId: UUID, 301 val proxy: DomainVerificationProxy 302 ) 303 } 304 305 fun mockPkg(packageName: String) = mockThrowOnUnmocked<AndroidPackage> { 306 whenever(this.packageName) { packageName } 307 whenever(targetSdkVersion) { Build.VERSION_CODES.S } 308 whenever(isEnabled) { true } 309 whenever(activities) { 310 listOf( 311 ParsedActivity().apply { 312 addIntent( 313 ParsedIntentInfo().apply { 314 autoVerify = true 315 addAction(Intent.ACTION_VIEW) 316 addCategory(Intent.CATEGORY_BROWSABLE) 317 addCategory(Intent.CATEGORY_DEFAULT) 318 addDataScheme("https") 319 addDataAuthority("example.com", null) 320 } 321 ) 322 } 323 ) 324 } 325 whenever(signingDetails) { SigningDetails.UNKNOWN } 326 } 327 328 fun mockPkgSetting(packageName: String, domainSetId: UUID) = spyThrowOnUnmocked( 329 PackageSetting( 330 packageName, 331 packageName, 332 File("/test"), 333 null, 334 null, 335 null, 336 null, 337 1, 338 0, 339 0, 340 0, 341 null, 342 null, 343 null, 344 domainSetId 345 ) 346 ) { 347 whenever(getName()) { packageName } 348 whenever(getPkg()) { mockPkg(packageName) } 349 whenever(this.domainSetId) { domainSetId } 350 whenever(readUserState(0)) { PackageUserState() } 351 whenever(readUserState(1)) { PackageUserState() } 352 whenever(getInstantApp(anyInt())) { false } 353 whenever(isSystem()) { false } 354 whenever(signingDetails) { SigningDetails.UNKNOWN } 355 } 356 } 357 358 @Parameterized.Parameter(0) 359 lateinit var params: Params<*> 360 361 private val proxy: DomainVerificationProxy = mockThrowOnUnmocked { 362 whenever(isCallerVerifier(VERIFIER_UID)) { true } 363 whenever(isCallerVerifier(NON_VERIFIER_UID)) { false } 364 whenever(sendBroadcastForPackages(any())) 365 } 366 367 @Test 368 fun verify() { 369 when (params.type) { 370 Type.INTERNAL -> internal() 371 Type.QUERENT -> approvedQuerent() 372 Type.VERIFIER -> approvedVerifier() 373 Type.SELECTION_QUERENT -> approvedUserStateQuerent(verifyCrossUser = true) 374 Type.SELECTOR -> approvedUserSelector(verifyCrossUser = false) 375 Type.SELECTOR_USER -> approvedUserSelector(verifyCrossUser = true) 376 Type.LEGACY_QUERENT -> legacyQuerent() 377 Type.LEGACY_SELECTOR -> legacyUserSelector() 378 Type.OWNER_QUERENT -> ownerQuerent(verifyCrossUser = false) 379 Type.OWNER_QUERENT_USER -> ownerQuerent(verifyCrossUser = true) 380 }.run { /*exhaust*/ } 381 } 382 383 private fun internal() { 384 val context: Context = mockThrowOnUnmocked() 385 val target = params.construct(context) 386 387 // Internal doesn't care about visibility 388 listOf(true, false).forEach { visible -> 389 INTERNAL_UIDS.forEach { runMethod(target, it, visible) } 390 assertFails { runMethod(target, VERIFIER_UID, visible) } 391 assertFails { 392 runMethod(target, NON_VERIFIER_UID, visible) 393 } 394 } 395 } 396 397 private fun approvedQuerent() { 398 val allowUserState = AtomicBoolean(false) 399 val allowPreferredApps = AtomicBoolean(false) 400 val allowQueryAll = AtomicBoolean(false) 401 val allowDump = AtomicBoolean(false) 402 val context: Context = mockThrowOnUnmocked { 403 initPermission( 404 allowUserState, 405 android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION 406 ) 407 initPermission( 408 allowPreferredApps, 409 android.Manifest.permission.SET_PREFERRED_APPLICATIONS 410 ) 411 initPermission(allowQueryAll, android.Manifest.permission.QUERY_ALL_PACKAGES) 412 initPermission(allowDump, android.Manifest.permission.DUMP) 413 } 414 val target = params.construct(context) 415 416 INTERNAL_UIDS.forEach { runMethod(target, it) } 417 418 verifyNoMoreInteractions(context) 419 420 assertFails { runMethod(target, VERIFIER_UID) } 421 assertFails { runMethod(target, NON_VERIFIER_UID) } 422 423 // Check that the verifier only needs QUERY_ALL to pass 424 allowQueryAll.set(true) 425 runMethod(target, VERIFIER_UID) 426 allowQueryAll.set(false) 427 428 allowPreferredApps.set(true) 429 430 assertFails { runMethod(target, NON_VERIFIER_UID) } 431 432 allowUserState.set(true) 433 434 assertFails { runMethod(target, NON_VERIFIER_UID) } 435 436 allowQueryAll.set(true) 437 438 assertFails { runMethod(target, NON_VERIFIER_UID) } 439 440 allowDump.set(true) 441 442 runMethod(target, NON_VERIFIER_UID) 443 } 444 445 private fun approvedVerifier() { 446 val allowDomainVerificationAgent = AtomicBoolean(false) 447 val allowIntentVerificationAgent = AtomicBoolean(false) 448 val allowQueryAll = AtomicBoolean(false) 449 val context: Context = mockThrowOnUnmocked { 450 initPermission( 451 allowDomainVerificationAgent, 452 android.Manifest.permission.DOMAIN_VERIFICATION_AGENT 453 ) 454 initPermission( 455 allowIntentVerificationAgent, 456 android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT 457 ) 458 initPermission(allowQueryAll, android.Manifest.permission.QUERY_ALL_PACKAGES) 459 } 460 val target = params.construct(context) 461 462 INTERNAL_UIDS.forEach { runMethod(target, it) } 463 464 verifyNoMoreInteractions(context) 465 466 assertFails { runMethod(target, VERIFIER_UID) } 467 assertFails { runMethod(target, NON_VERIFIER_UID) } 468 469 allowDomainVerificationAgent.set(true) 470 471 assertFails { runMethod(target, VERIFIER_UID) } 472 assertFails { runMethod(target, NON_VERIFIER_UID) } 473 474 allowQueryAll.set(true) 475 476 runMethod(target, VERIFIER_UID) 477 assertFails { runMethod(target, NON_VERIFIER_UID) } 478 479 // Check that v1 verifiers are also allowed through 480 allowDomainVerificationAgent.set(false) 481 allowIntentVerificationAgent.set(true) 482 483 runMethod(target, VERIFIER_UID) 484 assertFails { runMethod(target, NON_VERIFIER_UID) } 485 } 486 487 private fun approvedUserStateQuerent(verifyCrossUser: Boolean) { 488 val allowInteractAcrossUsers = AtomicBoolean(false) 489 val context: Context = mockThrowOnUnmocked { 490 initPermission( 491 allowInteractAcrossUsers, 492 android.Manifest.permission.INTERACT_ACROSS_USERS 493 ) 494 } 495 val target = params.construct(context) 496 497 fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) { 498 // User selector makes no distinction by UID 499 val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID 500 runCrossUserMethod(allUids, target, callingUserId, targetUserId, throws) 501 } 502 503 val callingUserId = 0 504 val notCallingUserId = 1 505 506 runTestCases(callingUserId, callingUserId, throws = false) 507 if (verifyCrossUser) { 508 runTestCases(callingUserId, notCallingUserId, throws = true) 509 } 510 511 allowInteractAcrossUsers.set(true) 512 513 runTestCases(callingUserId, callingUserId, throws = false) 514 if (verifyCrossUser) { 515 runTestCases(callingUserId, notCallingUserId, throws = false) 516 } 517 } 518 519 private fun approvedUserSelector(verifyCrossUser: Boolean) { 520 val allowUserState = AtomicBoolean(false) 521 val allowInteractAcrossUsers = AtomicBoolean(false) 522 val context: Context = mockThrowOnUnmocked { 523 initPermission( 524 allowUserState, 525 android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION 526 ) 527 initPermission( 528 allowInteractAcrossUsers, 529 android.Manifest.permission.INTERACT_ACROSS_USERS 530 ) 531 } 532 val target = params.construct(context) 533 534 fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) { 535 // User selector makes no distinction by UID 536 val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID 537 runCrossUserMethod(allUids, target, callingUserId, targetUserId, throws) 538 } 539 540 val callingUserId = 0 541 val notCallingUserId = 1 542 543 runTestCases(callingUserId, callingUserId, throws = true) 544 if (verifyCrossUser) { 545 runTestCases(callingUserId, notCallingUserId, throws = true) 546 } 547 548 allowUserState.set(true) 549 550 runTestCases(callingUserId, callingUserId, throws = false) 551 if (verifyCrossUser) { 552 runTestCases(callingUserId, notCallingUserId, throws = true) 553 } 554 555 allowInteractAcrossUsers.set(true) 556 557 runTestCases(callingUserId, callingUserId, throws = false) 558 if (verifyCrossUser) { 559 runTestCases(callingUserId, notCallingUserId, throws = false) 560 } 561 } 562 563 private fun legacyUserSelector() { 564 val allowInteractAcrossUsers = AtomicBoolean(false) 565 val allowPreferredApps = AtomicBoolean(false) 566 val context: Context = mockThrowOnUnmocked { 567 initPermission( 568 allowInteractAcrossUsers, 569 android.Manifest.permission.INTERACT_ACROSS_USERS 570 ) 571 initPermission( 572 allowPreferredApps, 573 android.Manifest.permission.SET_PREFERRED_APPLICATIONS 574 ) 575 } 576 val target = params.construct(context) 577 578 fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) { 579 // Legacy makes no distinction by UID 580 val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID 581 // The legacy selector does a silent failure when the user IDs don't match, so it 582 // cannot verify the non-existent user ID check, as it will not throw an Exception. 583 runCrossUserMethod(allUids, target, callingUserId, targetUserId, throws, 584 verifyUserIdCheck = false) 585 } 586 587 val callingUserId = 0 588 val notCallingUserId = 1 589 590 runTestCases(callingUserId, callingUserId, throws = true) 591 runTestCases(callingUserId, notCallingUserId, throws = true) 592 593 allowPreferredApps.set(true) 594 595 runTestCases(callingUserId, callingUserId, throws = false) 596 runTestCases(callingUserId, notCallingUserId, throws = true) 597 598 allowInteractAcrossUsers.set(true) 599 600 runTestCases(callingUserId, callingUserId, throws = false) 601 runTestCases(callingUserId, notCallingUserId, throws = false) 602 } 603 604 private fun legacyQuerent() { 605 val allowInteractAcrossUsers = AtomicBoolean(false) 606 val allowInteractAcrossUsersFull = AtomicBoolean(false) 607 val context: Context = mockThrowOnUnmocked { 608 initPermission( 609 allowInteractAcrossUsers, 610 android.Manifest.permission.INTERACT_ACROSS_USERS 611 ) 612 initPermission( 613 allowInteractAcrossUsersFull, 614 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL 615 ) 616 } 617 val target = params.construct(context) 618 619 // Legacy code can return PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED 620 // as an error code. This is distinct from the class level assertFails as unfortunately 621 // the same number, 0, is used in opposite contexts, where it does represent a failure 622 // for this legacy case, but not for the modern APIs. 623 fun assertFailsLegacy(block: () -> Any?) { 624 try { 625 val value = block() 626 if ((value as? Int) 627 != PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED 628 ) { 629 throw AssertionError("Expected call to return false, was $value") 630 } 631 } catch (e: SecurityException) { 632 } catch (e: PackageManager.NameNotFoundException) { 633 // Any of these 2 exceptions are considered failures, which is expected 634 } 635 } 636 637 fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) { 638 // Legacy makes no distinction by UID 639 val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID 640 runCrossUserMethod(allUids, target, callingUserId, targetUserId, throws, 641 assertFailsMethod = ::assertFailsLegacy) 642 } 643 644 val callingUserId = 0 645 val notCallingUserId = 1 646 647 runTestCases(callingUserId, callingUserId, throws = false) 648 runTestCases(callingUserId, notCallingUserId, throws = true) 649 650 // Legacy requires the _FULL permission, so this should continue to fail 651 allowInteractAcrossUsers.set(true) 652 runTestCases(callingUserId, callingUserId, throws = false) 653 runTestCases(callingUserId, notCallingUserId, throws = true) 654 655 allowInteractAcrossUsersFull.set(true) 656 runTestCases(callingUserId, callingUserId, throws = false) 657 runTestCases(callingUserId, notCallingUserId, throws = false) 658 } 659 660 private fun ownerQuerent(verifyCrossUser: Boolean) { 661 val allowQueryAll = AtomicBoolean(false) 662 val allowUserState = AtomicBoolean(false) 663 val allowInteractAcrossUsers = AtomicBoolean(false) 664 val context: Context = mockThrowOnUnmocked { 665 initPermission( 666 allowQueryAll, 667 android.Manifest.permission.QUERY_ALL_PACKAGES 668 ) 669 initPermission( 670 allowUserState, 671 android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION 672 ) 673 initPermission( 674 allowInteractAcrossUsers, 675 android.Manifest.permission.INTERACT_ACROSS_USERS 676 ) 677 } 678 val target = params.construct(context) 679 680 fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) { 681 // Owner querent makes no distinction by UID 682 val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID 683 runCrossUserMethod(allUids, target, callingUserId, targetUserId, throws, 684 verifyInvisiblePkg = false) 685 } 686 687 val callingUserId = 0 688 val notCallingUserId = 1 689 690 runTestCases(callingUserId, callingUserId, throws = true) 691 if (verifyCrossUser) { 692 runTestCases(callingUserId, notCallingUserId, throws = true) 693 } 694 695 allowQueryAll.set(true) 696 697 runTestCases(callingUserId, callingUserId, throws = true) 698 if (verifyCrossUser) { 699 runTestCases(callingUserId, notCallingUserId, throws = true) 700 } 701 702 allowUserState.set(true) 703 704 runTestCases(callingUserId, callingUserId, throws = false) 705 if (verifyCrossUser) { 706 runTestCases(callingUserId, notCallingUserId, throws = true) 707 } 708 709 allowQueryAll.set(false) 710 711 runTestCases(callingUserId, callingUserId, throws = true) 712 if (verifyCrossUser) { 713 runTestCases(callingUserId, notCallingUserId, throws = true) 714 } 715 716 allowQueryAll.set(true) 717 allowInteractAcrossUsers.set(true) 718 719 runTestCases(callingUserId, callingUserId, throws = false) 720 if (verifyCrossUser) { 721 runTestCases(callingUserId, notCallingUserId, throws = false) 722 } 723 } 724 725 private fun Context.initPermission(boolean: AtomicBoolean, permission: String) { 726 whenever(enforcePermission(eq(permission), anyInt(), anyInt(), anyString())) { 727 if (!boolean.get()) { 728 throw SecurityException() 729 } 730 } 731 whenever(checkPermission(eq(permission), anyInt(), anyInt())) { 732 if (boolean.get()) { 733 PackageManager.PERMISSION_GRANTED 734 } else { 735 PackageManager.PERMISSION_DENIED 736 } 737 } 738 } 739 740 private fun runMethod( 741 target: Any, 742 callingUid: Int, 743 visible: Boolean = true, 744 callingUserId: Int = 0, 745 userId: Int = 0 746 ): Any? { 747 val packageName = if (visible) VISIBLE_PKG else INVISIBLE_PKG 748 val uuid = if (visible) VISIBLE_UUID else INVISIBLE_UUID 749 return params.runMethod(target, callingUid, callingUserId, userId, packageName, uuid, proxy) 750 } 751 752 private fun runCrossUserMethod( 753 allUids: Iterable<Int>, 754 target: Any, 755 callingUserId: Int, 756 targetUserId: Int, 757 throws: Boolean, 758 verifyUserIdCheck: Boolean = true, 759 verifyInvisiblePkg: Boolean = true, 760 assertFailsMethod: (() -> Any?) -> Unit = ::assertFails, 761 ) { 762 if (throws) { 763 allUids.forEach { 764 assertFailsMethod { 765 // When testing a non-user ID failure, send an invalid user ID. 766 // This ensures the failure occurs before the user ID check is run. 767 try { 768 runMethod(target, it, visible = true, callingUserId, 100) 769 } catch (e: SecurityException) { 770 if (verifyUserIdCheck) { 771 e.message?.let { 772 if (it.contains("user ID", ignoreCase = true) 773 || it.contains("100")) { 774 fail( 775 "Method should not check user existence before permissions" 776 ) 777 } 778 } 779 } 780 781 // Rethrow to allow normal fail checking logic to run 782 throw e 783 } 784 } 785 } 786 } else { 787 allUids.forEach { 788 runMethod(target, it, visible = true, callingUserId, targetUserId) 789 } 790 } 791 792 if (verifyInvisiblePkg) { 793 allUids.forEach { 794 assertFailsMethod { 795 runMethod(target, it, visible = false, callingUserId, targetUserId) 796 } 797 } 798 } 799 800 if (verifyUserIdCheck) { 801 // An invalid target user ID should always fail 802 allUids.forEach { 803 assertFailsWith(SecurityException::class) { 804 runMethod(target, it, visible = true, callingUserId, 100) 805 } 806 } 807 808 // An invalid calling user ID should always fail, although this cannot happen in prod 809 allUids.forEach { 810 assertFailsWith(SecurityException::class) { 811 runMethod(target, it, visible = true, 100, targetUserId) 812 } 813 } 814 } 815 } 816 817 private fun assertFails(block: () -> Any?) { 818 try { 819 val value = block() 820 // Some methods return false or an error rather than throwing, so check that as well 821 val valueAsBoolean = value as? Boolean 822 if (valueAsBoolean == false) { 823 // Expected failure, do not throw 824 return 825 } 826 827 val valueAsInt = value as? Int 828 if (valueAsInt != null) { 829 if (valueAsInt == DomainVerificationManager.STATUS_OK) { 830 throw AssertionError("Expected call to return false, was $value") 831 } 832 } else { 833 throw AssertionError("Expected call to fail") 834 } 835 } catch (e: SecurityException) { 836 } catch (e: PackageManager.NameNotFoundException) { 837 // Any of these 2 exceptions are considered failures, which is expected 838 } 839 } 840 841 enum class Type { 842 // System/shell only 843 INTERNAL, 844 845 // INTERNAL || non-legacy domain verification agent || DUMP permission 846 QUERENT, 847 848 // INTERNAL || domain verification agent 849 VERIFIER, 850 851 // No permissions, allows all apps to view domain state for visible packages 852 SELECTION_QUERENT, 853 854 // Holding the user setting permission 855 SELECTOR, 856 857 // Holding the user setting permission, but targeting cross user 858 SELECTOR_USER, 859 860 // Legacy required no permissions except when cross-user 861 LEGACY_QUERENT, 862 863 // Holding the legacy preferred apps permission 864 LEGACY_SELECTOR, 865 866 // Holding user setting permission, but not targeting a package 867 OWNER_QUERENT, 868 869 // Holding user setting permission, but not targeting a package, but targeting cross user 870 OWNER_QUERENT_USER, 871 } 872 } 873