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

Commit 5321abef authored by Rhed Jao's avatar Rhed Jao
Browse files

Fix cross user package visibility leakage for PackageManager (6/n)

APIs:
- PackageManager#checkUidSignatures
- PackageManager#hasUidSigningCertificate
- PackageManager#getNameForUid
- PackageManager#getNamesForUids
- IPackageManager#getUidForSharedUser
- IPackageManager#getFlagsForUid
- IPackageManager#getPrivateFlagsForUid

Bug: 229684723
Test: atest CrossUserPackageVisibilityTests
Change-Id: I3f4585e4dbe563dd513ed30b63dcfbfb435ecd3e
parent 3d3d2c95
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -227,8 +227,28 @@ public interface Computer extends PackageDataSnapshot {
            int userId);
    boolean shouldFilterApplication(@NonNull SharedUserSetting sus, int callingUid,
            int userId);
    /**
     * Different form {@link #shouldFilterApplication(PackageStateInternal, int, int)}, the function
     * returns {@code true} if the target package is not found in the device or uninstalled in the
     * current user. Unless the caller's function needs to handle the package's uninstalled state
     * by itself, using this function to keep the consistent behavior between conditions of package
     * uninstalled and visibility not allowed to avoid the side channel leakage of package
     * existence.
     * <p>
     * Package with {@link PackageManager#SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_HIDDEN} is not
     * treated as an uninstalled package for the carrier apps customization.
     */
    boolean shouldFilterApplicationIncludingUninstalled(@Nullable PackageStateInternal ps,
            int callingUid, int userId);
    /**
     * Different from {@link #shouldFilterApplication(SharedUserSetting, int, int)}, the function
     * returns {@code true} if packages with the same shared user are all uninstalled in the current
     * user.
     *
     * @see #shouldFilterApplicationIncludingUninstalled(PackageStateInternal, int, int)
     */
    boolean shouldFilterApplicationIncludingUninstalled(@NonNull SharedUserSetting sus,
            int callingUid, int userId);
    int checkUidPermission(String permName, int uid);
    int getPackageUidInternal(String packageName, long flags, int userId, int callingUid);
    long updateFlagsForApplication(long flags, int userId);
+57 −74
Original line number Diff line number Diff line
@@ -2784,6 +2784,26 @@ public class ComputerEngine implements Computer {
                ps, callingUid, null, TYPE_UNKNOWN, userId, true /* filterUninstall */);
    }

    /**
     * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean)
     */
    public final boolean shouldFilterApplicationIncludingUninstalled(
            @NonNull SharedUserSetting sus, int callingUid, int userId) {
        if (shouldFilterApplication(sus, callingUid, userId)) {
            return true;
        }
        final ArraySet<PackageStateInternal> packageStates =
                (ArraySet<PackageStateInternal>) sus.getPackageStates();
        for (int index = 0; index < packageStates.size(); index++) {
            final PackageStateInternal ps = packageStates.valueAt(index);
            if (ps.getUserStateOrDefault(userId).isInstalled() || ps.isHiddenUntilInstalled()) {
                return false;
            }
        }
        // Filter it, all packages with the same shared uid are uninstalled.
        return true;
    }

    /**
     * Verification statuses are ordered from the worse to the best, except for
     * INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER, which is the worse.
@@ -4243,53 +4263,36 @@ public class ComputerEngine implements Computer {
    @Override
    public int checkUidSignatures(int uid1, int uid2) {
        final int callingUid = Binder.getCallingUid();
        final int callingUserId = UserHandle.getUserId(callingUid);
        // Map to base uids.
        final int appId1 = UserHandle.getAppId(uid1);
        final int appId2 = UserHandle.getAppId(uid2);
        SigningDetails p1SigningDetails;
        SigningDetails p2SigningDetails;
        Object obj = mSettings.getSettingBase(appId1);
        if (obj != null) {
            if (obj instanceof SharedUserSetting) {
                final SharedUserSetting sus = (SharedUserSetting) obj;
                if (shouldFilterApplication(sus, callingUid, callingUserId)) {
                    return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
                }
                p1SigningDetails = sus.signatures.mSigningDetails;
            } else if (obj instanceof PackageSetting) {
                final PackageSetting ps = (PackageSetting) obj;
                if (shouldFilterApplication(ps, callingUid, callingUserId)) {
        final SigningDetails p1SigningDetails = getSigningDetailsAndFilterAccess(uid1, callingUid);
        final SigningDetails p2SigningDetails = getSigningDetailsAndFilterAccess(uid2, callingUid);
        if (p1SigningDetails == null || p2SigningDetails == null) {
            return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
        }
                p1SigningDetails = ps.getSigningDetails();
            } else {
                return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
        return checkSignaturesInternal(p1SigningDetails, p2SigningDetails);
    }
        } else {
            return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;

    private SigningDetails getSigningDetailsAndFilterAccess(int uid, int callingUid) {
        // Map to base uids.
        final int appId = UserHandle.getAppId(uid);
        final int callingUserId = UserHandle.getUserId(callingUid);
        final Object obj = mSettings.getSettingBase(appId);
        if (obj == null) {
            return null;
        }
        obj = mSettings.getSettingBase(appId2);
        if (obj != null) {
        if (obj instanceof SharedUserSetting) {
            final SharedUserSetting sus = (SharedUserSetting) obj;
                if (shouldFilterApplication(sus, callingUid, callingUserId)) {
                    return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
            if (shouldFilterApplicationIncludingUninstalled(sus, callingUid, callingUserId)) {
                return null;
            }
                p2SigningDetails = sus.signatures.mSigningDetails;
            return sus.signatures.mSigningDetails;
        } else if (obj instanceof PackageSetting) {
            final PackageSetting ps = (PackageSetting) obj;
                if (shouldFilterApplication(ps, callingUid, callingUserId)) {
                    return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
                }
                p2SigningDetails = ps.getSigningDetails();
            } else {
                return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
            if (shouldFilterApplicationIncludingUninstalled(ps, callingUid, callingUserId)) {
                return null;
            }
        } else {
            return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
            return ps.getSigningDetails();
        }
        return checkSignaturesInternal(p1SigningDetails, p2SigningDetails);
        return null;
    }

    private int checkSignaturesInternal(SigningDetails p1SigningDetails,
@@ -4353,28 +4356,8 @@ public class ComputerEngine implements Computer {
    public boolean hasUidSigningCertificate(int uid, @NonNull byte[] certificate,
            @PackageManager.CertificateInputType int type) {
        final int callingUid = Binder.getCallingUid();
        final int callingUserId = UserHandle.getUserId(callingUid);
        // Map to base uids.
        final int appId = UserHandle.getAppId(uid);
        final SigningDetails signingDetails;
        final Object obj = mSettings.getSettingBase(appId);
        if (obj != null) {
            if (obj instanceof SharedUserSetting) {
                final SharedUserSetting sus = (SharedUserSetting) obj;
                if (shouldFilterApplication(sus, callingUid, callingUserId)) {
                    return false;
                }
                signingDetails = sus.signatures.mSigningDetails;
            } else if (obj instanceof PackageSetting) {
                final PackageSetting ps = (PackageSetting) obj;
                if (shouldFilterApplication(ps, callingUid, callingUserId)) {
                    return false;
                }
                signingDetails = ps.getSigningDetails();
            } else {
                return false;
            }
        } else {
        final SigningDetails signingDetails = getSigningDetailsAndFilterAccess(uid, callingUid);
        if (signingDetails == null) {
            return false;
        }
        switch (type) {
@@ -4437,13 +4420,13 @@ public class ComputerEngine implements Computer {
        final Object obj = mSettings.getSettingBase(appId);
        if (obj instanceof SharedUserSetting) {
            final SharedUserSetting sus = (SharedUserSetting) obj;
            if (shouldFilterApplication(sus, callingUid, callingUserId)) {
            if (shouldFilterApplicationIncludingUninstalled(sus, callingUid, callingUserId)) {
                return null;
            }
            return sus.name + ":" + sus.mAppId;
        } else if (obj instanceof PackageSetting) {
            final PackageSetting ps = (PackageSetting) obj;
            if (shouldFilterApplication(ps, callingUid, callingUserId)) {
            if (shouldFilterApplicationIncludingUninstalled(ps, callingUid, callingUserId)) {
                return null;
            }
            return ps.getPackageName();
@@ -4472,14 +4455,14 @@ public class ComputerEngine implements Computer {
            final Object obj = mSettings.getSettingBase(appId);
            if (obj instanceof SharedUserSetting) {
                final SharedUserSetting sus = (SharedUserSetting) obj;
                if (shouldFilterApplication(sus, callingUid, callingUserId)) {
                if (shouldFilterApplicationIncludingUninstalled(sus, callingUid, callingUserId)) {
                    names[i] = null;
                } else {
                    names[i] = "shared:" + sus.name;
                }
            } else if (obj instanceof PackageSetting) {
                final PackageSetting ps = (PackageSetting) obj;
                if (shouldFilterApplication(ps, callingUid, callingUserId)) {
                if (shouldFilterApplicationIncludingUninstalled(ps, callingUid, callingUserId)) {
                    names[i] = null;
                } else {
                    names[i] = ps.getPackageName();
@@ -4501,7 +4484,7 @@ public class ComputerEngine implements Computer {
            return Process.INVALID_UID;
        }
        final SharedUserSetting suid = mSettings.getSharedUserFromId(sharedUserName);
        if (suid != null && !shouldFilterApplication(suid, callingUid,
        if (suid != null && !shouldFilterApplicationIncludingUninstalled(suid, callingUid,
                UserHandle.getUserId(callingUid))) {
            return suid.mAppId;
        }
@@ -4522,13 +4505,13 @@ public class ComputerEngine implements Computer {
        final Object obj = mSettings.getSettingBase(appId);
        if (obj instanceof SharedUserSetting) {
            final SharedUserSetting sus = (SharedUserSetting) obj;
            if (shouldFilterApplication(sus, callingUid, callingUserId)) {
            if (shouldFilterApplicationIncludingUninstalled(sus, callingUid, callingUserId)) {
                return 0;
            }
            return sus.getFlags();
        } else if (obj instanceof PackageSetting) {
            final PackageSetting ps = (PackageSetting) obj;
            if (shouldFilterApplication(ps, callingUid, callingUserId)) {
            if (shouldFilterApplicationIncludingUninstalled(ps, callingUid, callingUserId)) {
                return 0;
            }
            return ps.getFlags();
@@ -4550,13 +4533,13 @@ public class ComputerEngine implements Computer {
        final Object obj = mSettings.getSettingBase(appId);
        if (obj instanceof SharedUserSetting) {
            final SharedUserSetting sus = (SharedUserSetting) obj;
            if (shouldFilterApplication(sus, callingUid, callingUserId)) {
            if (shouldFilterApplicationIncludingUninstalled(sus, callingUid, callingUserId)) {
                return 0;
            }
            return sus.getPrivateFlags();
        } else if (obj instanceof PackageSetting) {
            final PackageSetting ps = (PackageSetting) obj;
            if (shouldFilterApplication(ps, callingUid, callingUserId)) {
            if (shouldFilterApplicationIncludingUninstalled(ps, callingUid, callingUserId)) {
                return 0;
            }
            return ps.getPrivateFlags();
+59 −3
Original line number Diff line number Diff line
@@ -27,6 +27,8 @@ import android.app.Instrumentation;
import android.content.Context;
import android.content.pm.IPackageManager;
import android.content.pm.KeySet;
import android.content.pm.PackageManager;
import android.os.Process;
import android.os.UserHandle;

import androidx.test.platform.app.InstrumentationRegistry;
@@ -56,8 +58,12 @@ public class CrossUserPackageVisibilityTests {
    private static final String TEST_DATA_DIR = "/data/local/tmp/appenumerationtests";
    private static final String CROSS_USER_TEST_PACKAGE_NAME =
            "com.android.appenumeration.crossuserpackagevisibility";
    private static final String SHARED_USER_TEST_PACKAGE_NAME =
            "com.android.appenumeration.shareduid";
    private static final File CROSS_USER_TEST_APK_FILE =
            new File(TEST_DATA_DIR, "AppEnumerationCrossUserPackageVisibilityTestApp.apk");
    private static final File SHARED_USER_TEST_APK_FILE =
            new File(TEST_DATA_DIR, "AppEnumerationSharedUserTestApp.apk");

    @ClassRule
    @Rule
@@ -66,6 +72,7 @@ public class CrossUserPackageVisibilityTests {
    private Instrumentation mInstrumentation;
    private IPackageManager mIPackageManager;
    private Context mContext;
    private UserReference mCurrentUser;
    private UserReference mOtherUser;

    @Before
@@ -77,17 +84,21 @@ public class CrossUserPackageVisibilityTests {
        // Get another user
        final UserReference primaryUser = sDeviceState.primaryUser();
        if (primaryUser.id() == UserHandle.myUserId()) {
            mCurrentUser = primaryUser;
            mOtherUser = sDeviceState.secondaryUser();
        } else {
            mCurrentUser = sDeviceState.secondaryUser();
            mOtherUser = primaryUser;
        }

        uninstallPackage(CROSS_USER_TEST_PACKAGE_NAME);
        uninstallPackage(SHARED_USER_TEST_PACKAGE_NAME);
    }

    @After
    public void tearDown() {
        uninstallPackage(CROSS_USER_TEST_PACKAGE_NAME);
        uninstallPackage(SHARED_USER_TEST_PACKAGE_NAME);
    }

    @Test
@@ -151,16 +162,61 @@ public class CrossUserPackageVisibilityTests {
        assertThat(e1.getMessage()).isEqualTo(e2.getMessage());
    }

    @Test
    public void testGetFlagsForUid_cannotDetectCrossUserPkg() throws Exception {
        installPackage(CROSS_USER_TEST_APK_FILE);
        final int uid = mContext.getPackageManager().getPackageUid(
                CROSS_USER_TEST_PACKAGE_NAME, PackageManager.PackageInfoFlags.of(0));

        uninstallPackageForUser(CROSS_USER_TEST_PACKAGE_NAME, mCurrentUser);

        assertThat(mIPackageManager.getFlagsForUid(uid)).isEqualTo(0);
    }

    @Test
    public void testGetUidForSharedUser_cannotDetectSharedUserPkg() throws Exception {
        assertThat(mIPackageManager.getUidForSharedUser(SHARED_USER_TEST_PACKAGE_NAME))
                .isEqualTo(Process.INVALID_UID);

        installPackageForUser(SHARED_USER_TEST_APK_FILE, mOtherUser, true /* forceQueryable */);

        assertThat(mIPackageManager.getUidForSharedUser(SHARED_USER_TEST_PACKAGE_NAME))
                .isEqualTo(Process.INVALID_UID);
    }

    private static void installPackage(File apk) {
        installPackageForUser(apk, null, false /* forceQueryable */);
    }

    private static void installPackageForUser(File apk, UserReference user) {
        installPackageForUser(apk, user, false /* forceQueryable */);
    }

    private static void installPackageForUser(File apk, UserReference user,
            boolean forceQueryable) {
        assertThat(apk.exists()).isTrue();
        final StringBuilder cmd = new StringBuilder("pm install --user ");
        cmd.append(user.id()).append(" ");
        final StringBuilder cmd = new StringBuilder("pm install -t ");
        if (forceQueryable) {
            cmd.append("--force-queryable ");
        }
        if (user != null) {
            cmd.append("--user ").append(user.id()).append(" ");
        }
        cmd.append(apk.getPath());
        final String result = runShellCommand(cmd.toString());
        assertThat(result.trim()).contains("Success");
    }

    private static void uninstallPackage(String packageName) {
        runShellCommand("pm uninstall " + packageName);
        uninstallPackageForUser(packageName, null /* user */);
    }

    private static void uninstallPackageForUser(String packageName, UserReference user) {
        final StringBuilder cmd = new StringBuilder("pm uninstall ");
        if (user != null) {
            cmd.append("--user ").append(user.id()).append(" ");
        }
        cmd.append(packageName);
        runShellCommand(cmd.toString());
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -17,6 +17,6 @@

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.appenumeration.crossuserpackagevisibility">
    <application>
    <application android:testOnly="true">
    </application>
</manifest>