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

Commit 9d672e89 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "[ADI][45/N] bypass developer verification in emergency cases" into main

parents 10454e2b 46107aad
Loading
Loading
Loading
Loading
+2 −5
Original line number Diff line number Diff line
@@ -175,7 +175,6 @@ import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
import com.android.server.EventLogTags;
import com.android.server.SystemConfig;
import com.android.server.criticalevents.CriticalEventLog;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.parsing.PackageCacher;
@@ -336,8 +335,7 @@ final class InstallPackageHelper {
        final String oldUpdateOwner =
                pkgAlreadyExists ? oldPkgSetting.getInstallSource().mUpdateOwnerPackageName : null;
        final String updateOwnerFromSysconfig = isApex || !pkgSetting.isSystem() ? null
                : mPm.mInjector.getSystemConfig().getSystemAppUpdateOwnerPackageName(
                        parsedPackage.getPackageName());
                : mPm.getSystemAppUpdateOwnerPackageName(parsedPackage.getPackageName());
        final boolean isUpdateOwnershipDenylisted =
                mUpdateOwnershipHelper.isUpdateOwnershipDenylisted(parsedPackage.getPackageName());
        final boolean isUpdateOwnershipEnabled = oldUpdateOwner != null;
@@ -483,12 +481,11 @@ final class InstallPackageHelper {
        if (listItems != null && !listItems.isEmpty()) {
            mUpdateOwnershipHelper.addToUpdateOwnerDenyList(pkgSetting.getPackageName(),
                    listItems);
            SystemConfig config = SystemConfig.getInstance();
            synchronized (mPm.mLock) {
                for (String unownedPackage : listItems) {
                    PackageSetting unownedSetting = mPm.mSettings.getPackageLPr(unownedPackage);
                    if (unownedSetting != null
                            && config.getSystemAppUpdateOwnerPackageName(unownedPackage) == null) {
                            && mPm.getSystemAppUpdateOwnerPackageName(unownedPackage) == null) {
                        unownedSetting.setUpdateOwnerPackage(null);
                    }
                }
+86 −7
Original line number Diff line number Diff line
@@ -55,6 +55,7 @@ import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFIC
import static android.content.pm.PackageManager.INSTALL_STAGED;
import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
import static android.content.pm.verify.developer.DeveloperVerificationSession.DEVELOPER_VERIFICATION_BYPASSED_REASON_ADB;
import static android.content.pm.verify.developer.DeveloperVerificationSession.DEVELOPER_VERIFICATION_BYPASSED_REASON_EMERGENCY;
import static android.content.pm.verify.developer.DeveloperVerificationSession.DEVELOPER_VERIFICATION_INCOMPLETE_NETWORK_UNAVAILABLE;
import static android.os.Process.INVALID_UID;
import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE;
@@ -3090,18 +3091,63 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
                return false;
            }
        }
        return true;
    }

    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
    boolean shouldAllowDeveloperVerificationEmergencyBypass(String packageName, Computer snapshot) {
        final String verifierPackageName = mDeveloperVerifierController.getVerifierPackageName();
        synchronized (mLock) {
            if (TextUtils.equals(verifierPackageName, mPackageName)) {
                // The verifier itself is being updated. Skip.
                // TODO(b/360129657): log bypass reason and this bypass should only happen if the
                // current verifier cannot be connected or isn't responding.
                Slog.w(TAG, "Skipping verification service because the verifier is being updated");
        if (verifierPackageName == null) {
            // Impossible condition. The verifier must exist because otherwise we wouldn't get
            // here. Added to prevent lint warnings.
            return false;
        }
        if (packageName == null) {
            return false;
        }
        PackageStateInternal ps = snapshot.getPackageStateInternal(packageName, Process.SYSTEM_UID);
        if (ps == null || !ps.isSystem()) {
            // The app being installed must be a system app to be considered a critical app for
            // 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.
            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;
        }
        // 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");
                return true;
            }
        }
        return false;
    }

    private void retryDeveloperVerificationSession(Supplier<Computer> snapshotSupplier) {
        final SigningInfo signingInfo;
@@ -3241,6 +3287,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
                    resumeVerify();
                    return;
                }
                if (shouldAllowDeveloperVerificationEmergencyBypass(
                        getPackageName(), mPm.snapshotComputer())) {
                    // Bypass verification when critical package is being updated and the verifier
                    // cannot be connected.
                    synchronized (mMetrics) {
                        mMetrics.onDeveloperVerificationBypassed(
                                DEVELOPER_VERIFICATION_BYPASSED_REASON_EMERGENCY);
                    }
                    resumeVerify();
                    return;
                }

                mVerificationUserActionNeededReason =
                        DEVELOPER_VERIFICATION_USER_ACTION_NEEDED_REASON_UNKNOWN;
@@ -3267,6 +3324,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
                    resumeVerify();
                    return;
                }
                if (shouldAllowDeveloperVerificationEmergencyBypass(
                        getPackageName(), mPm.snapshotComputer())) {
                    // Bypass verification when critical package is being updated and the verifier
                    // cannot be connected.
                    synchronized (mMetrics) {
                        mMetrics.onDeveloperVerificationBypassed(
                                DEVELOPER_VERIFICATION_BYPASSED_REASON_EMERGENCY);
                    }
                    resumeVerify();
                    return;
                }

                mVerificationUserActionNeededReason =
                        DEVELOPER_VERIFICATION_USER_ACTION_NEEDED_REASON_UNKNOWN;
@@ -3304,6 +3372,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
                    resumeVerify();
                    return;
                }
                if (shouldAllowDeveloperVerificationEmergencyBypass(
                        getPackageName(), mPm.snapshotComputer())) {
                    // Bypass verification when critical package is being updated and the verifier
                    // has timed out.
                    synchronized (mMetrics) {
                        mMetrics.onDeveloperVerificationBypassed(
                                DEVELOPER_VERIFICATION_BYPASSED_REASON_EMERGENCY);
                    }
                    resumeVerify();
                    return;
                }

                mVerificationUserActionNeededReason =
                        DEVELOPER_VERIFICATION_USER_ACTION_NEEDED_REASON_UNKNOWN;
+8 −0
Original line number Diff line number Diff line
@@ -8450,4 +8450,12 @@ public class PackageManagerService implements PackageSender, TestUtilityService
    String getDeveloperVerificationPolicyDelegatePackageName() {
        return mDeveloperVerificationPolicyDelegatePackage;
    }

    /**
     * @return The update-owner of the given package name as specified in the system config file.
     */
    @Nullable
    public String getSystemAppUpdateOwnerPackageName(String packageName) {
        return mInjector.getSystemConfig().getSystemAppUpdateOwnerPackageName(packageName);
    }
}
+141 −2
Original line number Diff line number Diff line
@@ -28,11 +28,14 @@ import android.content.pm.verify.domain.DomainSet
import android.os.Parcel
import android.os.PersistableBundle
import android.os.Process
import android.os.UserHandle
import android.platform.test.annotations.Presubmit
import android.util.AtomicFile
import android.util.Slog
import android.util.Xml
import com.android.internal.os.BackgroundThread
import com.android.internal.pm.parsing.pkg.AndroidPackageInternal
import com.android.server.pm.pkg.PackageStateInternal
import com.android.server.pm.verify.developer.DeveloperVerifierController
import com.android.server.testutils.whenever
import com.google.common.truth.Truth.assertThat
@@ -49,6 +52,7 @@ import org.junit.rules.TemporaryFolder
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyLong
import org.mockito.ArgumentMatchers.anyString
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.MockitoAnnotations
@@ -62,6 +66,7 @@ class PackageInstallerSessionTest {
        private const val TAG_SESSIONS = "sessions"
        private const val TEST_KEY_FOR_EXTENSION_PARAMS = "testKey"
        private const val TEST_VALUE_FOR_EXTENSION_PARAMS = "testValue"
        private const val USER_ID = 456
    }

    @JvmField
@@ -73,6 +78,8 @@ class PackageInstallerSessionTest {

    @Mock
    lateinit var mMockPackageManagerInternal: PackageManagerService
    @Mock
    lateinit var mMockDeveloperVerifierController: DeveloperVerifierController

    @Mock
    lateinit var mSnapshot: Computer
@@ -151,6 +158,138 @@ class PackageInstallerSessionTest {
        writeRestoreAssert(listOf(session, childSession1, childSession2))
    }

    @Test
    fun testShouldAllowDeveloperVerificationEmergencyBypassReturnsFalseForNullPackageName() {
        // Test no package name
        val session = createSession()
        assertThat(session.shouldAllowDeveloperVerificationEmergencyBypass(
            null, mSnapshot)).isFalse()
    }

    @Test
    fun testShouldAllowDeveloperVerificationEmergencyBypassReturnsFalseForNonVerifierPackageName() {
        // Test no verifier package name
        whenever(mMockDeveloperVerifierController.verifierPackageName).thenReturn(null)
        val session = createSession()
        assertThat(session.shouldAllowDeveloperVerificationEmergencyBypass(
            "testPackageName", mSnapshot)).isFalse()
    }

    @Test
    fun testShouldAllowDeveloperVerificationEmergencyBypassReturnsFalseForNonPreinstalledApp() {
        val testPackageName = "testPackageName"
        val session = createSession()
        whenever(mSnapshot.getPackageStateInternal(eq(testPackageName), eq(Process.SYSTEM_UID)))
            .thenReturn(null)
        assertThat(session.shouldAllowDeveloperVerificationEmergencyBypass(
            testPackageName, mSnapshot)).isFalse()
    }

    @Test
    fun testShouldAllowDeveloperVerificationEmergencyBypassReturnsFalseForNonSystemApp() {
        val testPackageName = "testPackageName"
        val session = createSession()
        val mockPs = mock(PackageStateInternal::class.java)
        whenever(mockPs.isSystem).thenReturn(false)
        whenever(mSnapshot.getPackageStateInternal(eq(testPackageName), eq(Process.SYSTEM_UID)))
            .thenReturn(mockPs)
        assertThat(session.shouldAllowDeveloperVerificationEmergencyBypass(
            testPackageName, mSnapshot)).isFalse()
    }

    @Test
    fun testShouldAllowDeveloperVerificationEmergencyBypassForVerifier() {
        val verifierPackageName = "verifierPackageName"
        val mockPs = mock(PackageStateInternal::class.java)
        whenever(mMockDeveloperVerifierController.verifierPackageName).thenReturn(
            verifierPackageName)
        whenever(mockPs.isSystem).thenReturn(true)
        whenever(mSnapshot.getPackageStateInternal(
            eq(verifierPackageName), eq(Process.SYSTEM_UID)))
            .thenReturn(mockPs)
        val session = createSession()
        assertThat(session.shouldAllowDeveloperVerificationEmergencyBypass(
            verifierPackageName, mSnapshot)).isTrue()
    }

    @Test
    fun testShouldAllowDeveloperVerificationEmergencyBypassReturnsFalseForNonUpdateOwner() {
        val verifierPackageName = "verifierPackageName"
        val updateOwnerName = "updateOwnerPackageName"
        val mockPs = mock(PackageStateInternal::class.java)
        whenever(mMockDeveloperVerifierController.verifierPackageName).thenReturn(
            verifierPackageName)
        whenever(mockPs.isSystem).thenReturn(true)
        whenever(mSnapshot.getPackageStateInternal(
            eq(updateOwnerName), eq(Process.SYSTEM_UID)))
            .thenReturn(mockPs)
        whenever(mMockPackageManagerInternal.getSystemAppUpdateOwnerPackageName(
            anyString())).thenReturn(null)
        val session = createSession()
        assertThat(session.shouldAllowDeveloperVerificationEmergencyBypass(
            updateOwnerName, mSnapshot)).isFalse()
    }

    @Test
    fun testShouldAllowDeveloperVerificationEmergencyBypassForUpdateOwner() {
        val verifierPackageName = "verifierPackageName"
        val updateOwnerName = "updateOwnerPackageName"
        val mockPs = mock(PackageStateInternal::class.java)
        whenever(mMockDeveloperVerifierController.verifierPackageName).thenReturn(
            verifierPackageName)
        whenever(mockPs.isSystem).thenReturn(true)
        whenever(mSnapshot.getPackageStateInternal(
            eq(updateOwnerName), eq(Process.SYSTEM_UID)))
            .thenReturn(mockPs)
        whenever(mMockPackageManagerInternal.getSystemAppUpdateOwnerPackageName(
            eq(verifierPackageName))).thenReturn(updateOwnerName)
        val session = createSession()
        assertThat(session.shouldAllowDeveloperVerificationEmergencyBypass(
            updateOwnerName, mSnapshot)).isTrue()
    }

    @Test
    fun testShouldAllowDeveloperVerificationEmergencyBypassForEmergencyInstaller() {
        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 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(mockUpdateOwnerPs.pkg).thenReturn(mockUpdateOwnerPkg)
        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)))
            .thenReturn(PackageManager.PERMISSION_GRANTED)
        whenever(mMockPackageManagerInternal.getSystemAppUpdateOwnerPackageName(
            eq(verifierPackageName))).thenReturn(updateOwnerName)
        val session = createSession()
        assertThat(session.shouldAllowDeveloperVerificationEmergencyBypass(
            emergencyInstallerPackageName, mSnapshot)).isTrue()
    }

    private fun createSession(
        staged: Boolean = false,
        sessionId: Int = 123,
@@ -183,7 +322,7 @@ class PackageInstallerSessionTest {
            /* looper */ BackgroundThread.getHandler().looper,
            /* stagingManager */ null,
            /* sessionId */ sessionId,
            /* userId */ 456,
            /* userId */ USER_ID,
            /* installerUid */ Process.myUid(),
            /* installSource */ installSource,
            /* sessionParams */ params,
@@ -205,7 +344,7 @@ class PackageInstallerSessionTest {
            /* stagedSessionErrorCode */ PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
            /* stagedSessionErrorMessage */ "some error",
            /* preVerifiedDomains */ DomainSet(setOf("com.foo", "com.bar")),
            /* VerifierController */ mock(DeveloperVerifierController::class.java),
            /* VerifierController */ mMockDeveloperVerifierController,
            /* initialVerificationPolicy */ DEVELOPER_VERIFICATION_POLICY_BLOCK_FAIL_OPEN,
            /* currentVerificationPolicy */ DEVELOPER_VERIFICATION_POLICY_BLOCK_FAIL_CLOSED,
            /* installDependencyHelper */ null