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

Commit 0dd5c836 authored by Song Chun Fan's avatar Song Chun Fan
Browse files

[ADI][60/N] Update emergency-bypass conditions

* Emergency bypass is allowed for the following conditions:
1. The verifier itself is being updated by its update owner as specified
   in the sysconfig.
2. The update owner of the verifier, as specified in the sysconfig, is
   being updated by itself.
3. The emergency installer of the sysconfig-specified update-owner of
   the verifier is being updated by the same sysconfig-specified
update-owner of the verifier.
4. The sysconfig-specified update-owner of the verifier is being updated
   by its emergency installer.

* For emergency bypass, negative developer verification results will be
  bypassed, so the installation will always proceed to the next stage,
regardless of the developer verification results.

BUG: 360129657
FLAG: android.content.pm.verification_service
Test: atest CtsRootDeveloperVerificationEmergencyBypassTestCases
Test: atest com.android.server.pm.PackageInstallerSessionTest

Change-Id: I8faaa546818767fc3ac821f3122b74a143e516cb
parent dbd006eb
Loading
Loading
Loading
Loading
+100 −35
Original line number Diff line number Diff line
@@ -1088,16 +1088,24 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
            return false;
        }
        // Only system installers can have an emergency installer
        if (PackageManager.PERMISSION_GRANTED
                != snapshot.checkUidPermission(Manifest.permission.INSTALL_PACKAGES, uid)
                && PackageManager.PERMISSION_GRANTED
                != snapshot.checkUidPermission(Manifest.permission.INSTALL_PACKAGE_UPDATES, uid)
                && PackageManager.PERMISSION_GRANTED
                != snapshot.checkUidPermission(Manifest.permission.INSTALL_SELF_UPDATES, uid)) {
        if (!hasSystemInstallerPermissions(snapshot, uid)) {
            return false;
        }
        return (snapshot.checkUidPermission(Manifest.permission.EMERGENCY_INSTALL_PACKAGES,
                installerUid) == PackageManager.PERMISSION_GRANTED);
        return hasEmergencyInstallerPermission(snapshot, installerUid);
    }

    private static boolean hasSystemInstallerPermissions(Computer snapshot, int uid) {
        return (PackageManager.PERMISSION_GRANTED
                == snapshot.checkUidPermission(Manifest.permission.INSTALL_PACKAGES, uid)
                || PackageManager.PERMISSION_GRANTED
                == snapshot.checkUidPermission(Manifest.permission.INSTALL_PACKAGE_UPDATES, uid)
                || PackageManager.PERMISSION_GRANTED
                == snapshot.checkUidPermission(Manifest.permission.INSTALL_SELF_UPDATES, uid));
    }

    private static boolean hasEmergencyInstallerPermission(Computer snapshot, int installerUid) {
        return snapshot.checkUidPermission(Manifest.permission.EMERGENCY_INSTALL_PACKAGES,
                installerUid) == PackageManager.PERMISSION_GRANTED;
    }

    private static final int USER_ACTION_NOT_NEEDED = 0;
@@ -3123,40 +3131,75 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
            // the emergency bypass.
            return false;
        }
        // Check if app being installed is the verifier itself.
        if (TextUtils.equals(verifierPackageName, packageName)) {
            Slog.d(TAG, "Bypassing developer verification because the verifier is being updated");
            return true;
        }
        // Check if app being installed is the sysconfig-specified update-owner of the verifier.
        final String updateOwnerPackageName = mPm.getSystemAppUpdateOwnerPackageName(
                verifierPackageName);
        if (updateOwnerPackageName == null) {
            // No sysconfig-specified update-owner for the verifier. No need to check further.
            // The verifier has not specified an update-owner in sysconfig.
            return false;
        }
        if (TextUtils.equals(updateOwnerPackageName, packageName)) {
            Slog.d(TAG, "Bypassing verification service because the sysconfig-specified "
                    + "update owner of the verifier is being updated");
            return true;
        final String installerPackageName = getInstallerPackageName();
        if (installerPackageName == null) {
            return false;
        }
        // Check if app being installed is the emergency installer of the sysconfig-specified
        // update-owner of the verifier.
        if (isEmergencyInstallerEnabled(updateOwnerPackageName, snapshot, userId, ps.getAppId())) {
        final PackageStateInternal psUpdateOwner = snapshot.getPackageStateInternal(
                updateOwnerPackageName, Process.SYSTEM_UID);
        if (psUpdateOwner == null || psUpdateOwner.getPkg() == null) {
                // Impossible condition, because the if clause above already checked this.
                // Added to prevent lint warnings.
                return false;
        }
            String emergencyInstallerPackageName = psUpdateOwner.getPkg().getEmergencyInstaller();
            if (emergencyInstallerPackageName != null
                    && TextUtils.equals(emergencyInstallerPackageName, packageName)) {
                Slog.d(TAG, "Bypassing verification service because the "
                        + "emergency installer of the verifier's update owner is being updated");
        // Validate the permissions of the sysconfig-specified update-owner
        if (!hasSystemInstallerPermissions(snapshot, psUpdateOwner.getAppId())) {
            return false;
        }
        // Check if the verifier is being updated, and the installer is the verifier's
        // sysconfig-specified update-owner.
        if (TextUtils.equals(verifierPackageName, packageName)
                && TextUtils.equals(updateOwnerPackageName, installerPackageName)) {
            Slog.d(TAG, "Bypassing developer verification because the verifier is being updated.");
            return true;
        }
        // Check if the verifier's sysconfig-specified update-owner is being updated, and the
        // installer is the verifier's sysconfig-specified update-owner.
        if (TextUtils.equals(updateOwnerPackageName, packageName)
                && TextUtils.equals(updateOwnerPackageName, installerPackageName)) {
            Slog.d(TAG, "Bypassing developer verification because the update-owner of the"
                    + " verifier which is specified in the sysconfig is being updated.");
            return true;
        }
        String emergencyInstallerOfUpdateOwner = psUpdateOwner.getPkg().getEmergencyInstaller();
        if (emergencyInstallerOfUpdateOwner == null) {
            // The rest of the bypass checks involve the emergency installer of the update-owner.
            // If this is no such emergency installer, no need to check further.
            return false;
        }
        final PackageStateInternal psEmergencyInstallerOfUpdateOwner =
                snapshot.getPackageStateInternal(emergencyInstallerOfUpdateOwner,
                        Process.SYSTEM_UID);
        if (psEmergencyInstallerOfUpdateOwner == null
                || psEmergencyInstallerOfUpdateOwner.getPkg() == null) {
            return false;
        }
        // Check the permission of the emergency installer
        if (!hasEmergencyInstallerPermission(
                snapshot, psEmergencyInstallerOfUpdateOwner.getAppId())) {
            return false;
        }
        // Check if the emergency installer of the verifier's sysconfig-specified update-owner is
        // being updated, and the installer is the verifier's sysconfig-specified update-owner.
        if (TextUtils.equals(emergencyInstallerOfUpdateOwner, packageName)
                && TextUtils.equals(updateOwnerPackageName, installerPackageName)) {
            Slog.d(TAG, "Bypassing developer verification because the emergency installer of"
                    + " the update-owner of the verifier which is specified in the sysconfig is"
                    + " being updated.");
            return true;
        }
        // If the app being installed is the sysconfig-specified update-owner, also allow bypassing
        // if the installer is the app's emergency-installer.
        if (TextUtils.equals(updateOwnerPackageName, packageName)
                && TextUtils.equals(emergencyInstallerOfUpdateOwner, installerPackageName)) {
            Slog.d(TAG, "Bypassing developer verification because the update-owner of the"
                    + " verifier which is specified in the sysconfig is being updated by its"
                    + " emergency installer.");
            return true;
        }
        return false;
    }
@@ -3460,6 +3503,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
                    resumeVerify();
                    return;
                }
                if (shouldAllowDeveloperVerificationEmergencyBypass(
                        getPackageName(), mPm.snapshotComputer())) {
                    // Bypass verification when critical package is being updated and the
                    // verification result is not verified.
                    synchronized (mMetrics) {
                        mMetrics.onDeveloperVerificationBypassed(
                                DEVELOPER_VERIFICATION_BYPASSED_REASON_EMERGENCY);
                    }
                    resumeVerify();
                    return;
                }

                // Package is blocked.
                mVerificationUserActionNeededReason =
@@ -3495,6 +3549,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
                    resumeVerify();
                    return;
                }
                if (shouldAllowDeveloperVerificationEmergencyBypass(
                        getPackageName(), mPm.snapshotComputer())) {
                    // Bypass verification when critical package is being updated and the
                    // verification is incomplete.
                    synchronized (mMetrics) {
                        mMetrics.onDeveloperVerificationBypassed(
                                DEVELOPER_VERIFICATION_BYPASSED_REASON_EMERGENCY);
                    }
                    resumeVerify();
                    return;
                }

                StringBuilder sb = new StringBuilder(
                        "Verification cannot be completed because of ");
+91 −29
Original line number Diff line number Diff line
@@ -17,12 +17,12 @@ package com.android.server.pm

import android.content.Context
import android.content.pm.PackageInstaller
import android.content.pm.PackageInstaller.DEVELOPER_VERIFICATION_POLICY_BLOCK_FAIL_CLOSED
import android.content.pm.PackageInstaller.DEVELOPER_VERIFICATION_POLICY_BLOCK_FAIL_OPEN
import android.content.pm.PackageInstaller.SessionParams
import android.content.pm.PackageInstaller.SessionParams.PERMISSION_STATE_DEFAULT
import android.content.pm.PackageInstaller.SessionParams.PERMISSION_STATE_DENIED
import android.content.pm.PackageInstaller.SessionParams.PERMISSION_STATE_GRANTED
import android.content.pm.PackageInstaller.DEVELOPER_VERIFICATION_POLICY_BLOCK_FAIL_CLOSED
import android.content.pm.PackageInstaller.DEVELOPER_VERIFICATION_POLICY_BLOCK_FAIL_OPEN
import android.content.pm.PackageManager
import android.content.pm.verify.domain.DomainSet
import android.os.Parcel
@@ -200,14 +200,27 @@ class PackageInstallerSessionTest {
    @Test
    fun testShouldAllowDeveloperVerificationEmergencyBypassForVerifier() {
        val verifierPackageName = "verifierPackageName"
        val updateOwnerName = "updateOwnerPackageName"
        val mockPs = mock(PackageStateInternal::class.java)
        val mockUpdateOwnerPs = mock(PackageStateInternal::class.java)
        val mockUpdateOwnerPkg = mock(AndroidPackageInternal::class.java)
        val updateOwnerUid = 10200
        whenever(mMockDeveloperVerifierController.verifierPackageName).thenReturn(
            verifierPackageName)
        whenever(mockPs.isSystem).thenReturn(true)
        whenever(mSnapshot.getPackageStateInternal(
            eq(verifierPackageName), eq(Process.SYSTEM_UID)))
            .thenReturn(mockPs)
        val session = createSession()
        whenever(mMockPackageManagerInternal.getSystemAppUpdateOwnerPackageName(
            anyString())).thenReturn(updateOwnerName)
        whenever(mSnapshot.getPackageStateInternal(
            eq(updateOwnerName), eq(Process.SYSTEM_UID)))
            .thenReturn(mockUpdateOwnerPs)
        whenever(mockUpdateOwnerPs.pkg).thenReturn(mockUpdateOwnerPkg)
        whenever(mockUpdateOwnerPs.appId).thenReturn(updateOwnerUid)
        whenever(mSnapshot.checkUidPermission(anyString(), eq(updateOwnerUid)))
            .thenReturn(PackageManager.PERMISSION_GRANTED)
        val session = createSession(installerPackageName = updateOwnerName)
        assertThat(session.shouldAllowDeveloperVerificationEmergencyBypass(
            verifierPackageName, mSnapshot)).isTrue()
    }
@@ -224,8 +237,8 @@ class PackageInstallerSessionTest {
            eq(updateOwnerName), eq(Process.SYSTEM_UID)))
            .thenReturn(mockPs)
        whenever(mMockPackageManagerInternal.getSystemAppUpdateOwnerPackageName(
            anyString())).thenReturn(null)
        val session = createSession()
        eq(verifierPackageName))).thenReturn(null)
        val session = createSession(installerPackageName = updateOwnerName)
        assertThat(session.shouldAllowDeveloperVerificationEmergencyBypass(
            updateOwnerName, mSnapshot)).isFalse()
    }
@@ -234,16 +247,22 @@ class PackageInstallerSessionTest {
    fun testShouldAllowDeveloperVerificationEmergencyBypassForUpdateOwner() {
        val verifierPackageName = "verifierPackageName"
        val updateOwnerName = "updateOwnerPackageName"
        val mockPs = mock(PackageStateInternal::class.java)
        val mockUpdateOwnerPs = mock(PackageStateInternal::class.java)
        val mockUpdateOwnerPkg = mock(AndroidPackageInternal::class.java)
        val updateOwnerUid = 10200
        whenever(mMockDeveloperVerifierController.verifierPackageName).thenReturn(
            verifierPackageName)
        whenever(mockPs.isSystem).thenReturn(true)
        whenever(mockUpdateOwnerPs.isSystem).thenReturn(true)
        whenever(mSnapshot.getPackageStateInternal(
            eq(updateOwnerName), eq(Process.SYSTEM_UID)))
            .thenReturn(mockPs)
            .thenReturn(mockUpdateOwnerPs)
        whenever(mMockPackageManagerInternal.getSystemAppUpdateOwnerPackageName(
            eq(verifierPackageName))).thenReturn(updateOwnerName)
        val session = createSession()
        whenever(mockUpdateOwnerPs.pkg).thenReturn(mockUpdateOwnerPkg)
        whenever(mockUpdateOwnerPs.appId).thenReturn(updateOwnerUid)
        whenever(mSnapshot.checkUidPermission(anyString(), eq(updateOwnerUid)))
            .thenReturn(PackageManager.PERMISSION_GRANTED)
        val session = createSession(installerPackageName = updateOwnerName)
        assertThat(session.shouldAllowDeveloperVerificationEmergencyBypass(
            updateOwnerName, mSnapshot)).isTrue()
    }
@@ -253,41 +272,83 @@ class PackageInstallerSessionTest {
        val verifierPackageName = "verifierPackageName"
        val updateOwnerName = "updateOwnerPackageName"
        val emergencyInstallerPackageName = "emergencyInstallerPackageName"
        val mockPs = mock(PackageStateInternal::class.java)
        val mockUpdateOwnerPs = mock(PackageStateInternal::class.java)
        val mockUpdateOwnerPkg = mock(AndroidPackageInternal::class.java)
        val mockUid = 10001
        val mockEmergencyInstallerPs = mock(PackageStateInternal::class.java)
        val mockEmergencyInstallerPkg = mock(AndroidPackageInternal::class.java)
        val mockEmergencyInstallerUid = 10001
        val mockUpdateOwnerUid = 10200
        whenever(mockPs.appId).thenReturn(mockUid)
        whenever(mockUpdateOwnerPs.appId).thenReturn(mockUpdateOwnerUid)
        whenever(mockUpdateOwnerPkg.emergencyInstaller).thenReturn(
            emergencyInstallerPackageName)

        whenever(mMockDeveloperVerifierController.verifierPackageName).thenReturn(
            verifierPackageName)
        whenever(mockPs.isSystem).thenReturn(true)
        whenever(mockUpdateOwnerPs.isSystem).thenReturn(true)
        whenever(mMockPackageManagerInternal.getSystemAppUpdateOwnerPackageName(
            eq(verifierPackageName))).thenReturn(updateOwnerName)

        whenever(mockUpdateOwnerPs.pkg).thenReturn(mockUpdateOwnerPkg)
        whenever(mockUpdateOwnerPs.appId).thenReturn(mockUpdateOwnerUid)
        whenever(mockUpdateOwnerPkg.emergencyInstaller).thenReturn(
            emergencyInstallerPackageName)
        whenever(mSnapshot.getPackageStateInternal(
            eq(updateOwnerName), eq(Process.SYSTEM_UID)))
            .thenReturn(mockUpdateOwnerPs)
        whenever(mSnapshot.getPackageStateInternal(
            eq(updateOwnerName)))
            .thenReturn(mockUpdateOwnerPs)
        whenever(mSnapshot.getPackageStateInternal(
            eq(emergencyInstallerPackageName), eq(Process.SYSTEM_UID)))
            .thenReturn(mockPs)
        whenever(mSnapshot.getPackagesForUid(eq(mockUid))).thenReturn(
            listOf(emergencyInstallerPackageName).toTypedArray())
        whenever(mSnapshot.checkUidPermission(anyString(),
            eq(UserHandle.getUid(USER_ID, mockUpdateOwnerUid))))
            .thenReturn(PackageManager.PERMISSION_GRANTED)
        whenever(mSnapshot.checkUidPermission(anyString(), eq(mockUid)))

        whenever(mSnapshot.getPackageStateInternal(
            eq(emergencyInstallerPackageName), eq(Process.SYSTEM_UID)))
            .thenReturn(mockEmergencyInstallerPs)
        whenever(mockEmergencyInstallerPs.pkg).thenReturn(mockEmergencyInstallerPkg)
        whenever(mockEmergencyInstallerPs.appId).thenReturn(mockEmergencyInstallerUid)
        whenever(mockEmergencyInstallerPs.isSystem).thenReturn(true)
        whenever(mSnapshot.checkUidPermission(anyString(), eq(mockEmergencyInstallerUid)))
            .thenReturn(PackageManager.PERMISSION_GRANTED)

        val session = createSession(installerPackageName = updateOwnerName)
        assertThat(session.shouldAllowDeveloperVerificationEmergencyBypass(
            emergencyInstallerPackageName, mSnapshot)).isTrue()
    }

    @Test
    fun testShouldAllowDeveloperVerificationEmergencyBypassForUpdaterOwnerByEmergencyInstaller() {
        val verifierPackageName = "verifierPackageName"
        val updateOwnerName = "updateOwnerPackageName"
        val emergencyInstallerPackageName = "emergencyInstallerPackageName"
        val mockUpdateOwnerPs = mock(PackageStateInternal::class.java)
        val mockUpdateOwnerPkg = mock(AndroidPackageInternal::class.java)
        val mockEmergencyInstallerPs = mock(PackageStateInternal::class.java)
        val mockEmergencyInstallerPkg = mock(AndroidPackageInternal::class.java)
        val mockEmergencyInstallerUid = 10001
        val mockUpdateOwnerUid = 10200

        whenever(mMockDeveloperVerifierController.verifierPackageName).thenReturn(
            verifierPackageName)
        whenever(mMockPackageManagerInternal.getSystemAppUpdateOwnerPackageName(
            eq(verifierPackageName))).thenReturn(updateOwnerName)
        val session = createSession()

        whenever(mSnapshot.getPackageStateInternal(
            eq(updateOwnerName), eq(Process.SYSTEM_UID)))
            .thenReturn(mockUpdateOwnerPs)
        whenever(mockUpdateOwnerPs.appId).thenReturn(mockUpdateOwnerUid)
        whenever(mockUpdateOwnerPs.isSystem).thenReturn(true)
        whenever(mockUpdateOwnerPs.pkg).thenReturn(mockUpdateOwnerPkg)
        whenever(mSnapshot.checkUidPermission(anyString(),
            eq(UserHandle.getUid(USER_ID, mockUpdateOwnerUid))))
            .thenReturn(PackageManager.PERMISSION_GRANTED)

        whenever(mockUpdateOwnerPkg.emergencyInstaller).thenReturn(
            emergencyInstallerPackageName)
        whenever(mSnapshot.getPackageStateInternal(
            eq(emergencyInstallerPackageName), eq(Process.SYSTEM_UID)))
            .thenReturn(mockEmergencyInstallerPs)
        whenever(mockEmergencyInstallerPs.pkg).thenReturn(mockEmergencyInstallerPkg)
        whenever(mockEmergencyInstallerPs.appId).thenReturn(mockEmergencyInstallerUid)
        whenever(mSnapshot.checkUidPermission(anyString(), eq(mockEmergencyInstallerUid)))
            .thenReturn(PackageManager.PERMISSION_GRANTED)

        val session = createSession(installerPackageName = emergencyInstallerPackageName)
        assertThat(session.shouldAllowDeveloperVerificationEmergencyBypass(
            emergencyInstallerPackageName, mSnapshot)).isTrue()
            updateOwnerName, mSnapshot)).isTrue()
    }

    private fun createSession(
@@ -296,6 +357,7 @@ class PackageInstallerSessionTest {
        multiPackage: Boolean = false,
        parentSessionId: Int = PackageInstaller.SessionInfo.INVALID_ID,
        childSessionIds: List<Int> = emptyList(),
        installerPackageName: String = "testInstaller",
        block: (SessionParams) -> Unit = {},
    ): PackageInstallerSession {
        val bundle = PersistableBundle()
@@ -309,7 +371,7 @@ class PackageInstallerSessionTest {

        val installSource = InstallSource.create(
            "testInstallInitiator",
            "testInstallOriginator", "testInstaller", -1, "testUpdateOwner",
            "testInstallOriginator", installerPackageName, -1, "testUpdateOwner",
            "testAttributionTag", PackageInstaller.PACKAGE_SOURCE_UNSPECIFIED
        )