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

Commit f5f55f41 authored by Rhed Jao's avatar Rhed Jao
Browse files

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

APIs:
- IPackageManager#isPackageSignedByKeySet
- IPackageManager#isPackageSignedByKeySetExactly

Bug: 214394643
Test: atest CrossUserPackageVisibilityTests
Change-Id: I14e4cc5cf27f205353fe709bc2bc8b45778b0764
parent c13b9153
Loading
Loading
Loading
Loading
+6 −4
Original line number Diff line number Diff line
@@ -5284,9 +5284,10 @@ public class ComputerEngine implements Computer {
            return false;
        }
        final AndroidPackage pkg = mPackages.get(packageName);
        final int callingUserId = UserHandle.getUserId(callingUid);
        if (pkg == null
                || shouldFilterApplication(getPackageStateInternal(pkg.getPackageName()),
                callingUid, UserHandle.getUserId(callingUid))) {
                || shouldFilterApplicationIncludingUninstalled(
                        getPackageStateInternal(pkg.getPackageName()), callingUid, callingUserId)) {
            Slog.w(TAG, "KeySet requested for unknown package: " + packageName);
            throw new IllegalArgumentException("Unknown package: " + packageName);
        }
@@ -5308,9 +5309,10 @@ public class ComputerEngine implements Computer {
            return false;
        }
        final AndroidPackage pkg = mPackages.get(packageName);
        final int callingUserId = UserHandle.getUserId(callingUid);
        if (pkg == null
                || shouldFilterApplication(getPackageStateInternal(pkg.getPackageName()),
                callingUid, UserHandle.getUserId(callingUid))) {
                || shouldFilterApplicationIncludingUninstalled(
                        getPackageStateInternal(pkg.getPackageName()), callingUid, callingUserId)) {
            Slog.w(TAG, "KeySet requested for unknown package: " + packageName);
            throw new IllegalArgumentException("Unknown package: " + packageName);
        }
+1 −0
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ android_test {
        "compatibility-device-util-axt",
        "androidx.test.runner",
        "truth-prebuilt",
        "Harrier",
    ],
    platform_apis: true,
    test_suites: ["device-tests"],
+9 −1
Original line number Diff line number Diff line
@@ -16,8 +16,16 @@
  -->

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:tools="http://schemas.android.com/tools"
          package="com.android.server.pm.test.appenumeration">

    <queries>
        <package android:name="com.android.appenumeration.crossuserpackagevisibility" />
    </queries>

    <!-- It's merged from Harrier library. Remove it since this test should not hold it. -->
    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" tools:node="remove" />

    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
                     android:targetPackage="com.android.server.pm.test.appenumeration"
                     android:label="Package Manager Service Tests for app enumeration">
+1 −0
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@
        <option name="push" value="AppEnumerationSyncProviderTestApp.apk->/data/local/tmp/appenumerationtests/AppEnumerationSyncProviderTestApp.apk" />
        <option name="push" value="AppEnumerationHasAppOpPermissionTestApp.apk->/data/local/tmp/appenumerationtests/AppEnumerationHasAppOpPermissionTestApp.apk" />
        <option name="push" value="AppEnumerationSharedUserTestApp.apk->/data/local/tmp/appenumerationtests/AppEnumerationSharedUserTestApp.apk" />
        <option name="push" value="AppEnumerationCrossUserPackageVisibilityTestApp.apk->/data/local/tmp/appenumerationtests/AppEnumerationCrossUserPackageVisibilityTestApp.apk" />
    </target_preparer>

    <option name="test-tag" value="AppEnumerationInternalTest" />
+92 −3
Original line number Diff line number Diff line
@@ -16,30 +16,78 @@

package com.android.server.pm.test.appenumeration;

import static com.android.compatibility.common.util.ShellUtils.runShellCommand;

import static com.google.common.truth.Truth.assertThat;

import static org.junit.Assert.assertThrows;

import android.app.AppGlobals;
import android.app.Instrumentation;
import android.content.Context;
import android.content.pm.IPackageManager;
import android.content.pm.KeySet;
import android.os.UserHandle;

import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;

import com.android.bedstead.harrier.BedsteadJUnit4;
import com.android.bedstead.harrier.DeviceState;
import com.android.bedstead.harrier.annotations.EnsureHasSecondaryUser;
import com.android.bedstead.nene.users.UserReference;

import org.junit.After;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(AndroidJUnit4.class)
import java.io.File;

/**
 * Verify that app without holding the {@link android.Manifest.permission.INTERACT_ACROSS_USERS}
 * can't detect the existence of another app in the different users on the device via the
 * side channel attacks.
 */
@EnsureHasSecondaryUser
@RunWith(BedsteadJUnit4.class)
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 File CROSS_USER_TEST_APK_FILE =
            new File(TEST_DATA_DIR, "AppEnumerationCrossUserPackageVisibilityTestApp.apk");

    @ClassRule
    @Rule
    public static final DeviceState sDeviceState = new DeviceState();

    private Instrumentation mInstrumentation;
    private IPackageManager mIPackageManager;
    private Context mContext;
    private UserReference mOtherUser;

    @Before
    public void setup() {
        mInstrumentation = InstrumentationRegistry.getInstrumentation();
        mIPackageManager = AppGlobals.getPackageManager();
        mContext = mInstrumentation.getContext();

        // Get another user
        final UserReference primaryUser = sDeviceState.primaryUser();
        if (primaryUser.id() == UserHandle.myUserId()) {
            mOtherUser = sDeviceState.secondaryUser();
        } else {
            mOtherUser = primaryUser;
        }

        uninstallPackage(CROSS_USER_TEST_PACKAGE_NAME);
    }

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

    @Test
@@ -49,4 +97,45 @@ public class CrossUserPackageVisibilityTests {
                () -> mIPackageManager.getSplashScreenTheme(
                        mInstrumentation.getContext().getPackageName(), crossUserId));
    }

    @Test
    public void testIsPackageSignedByKeySet_cannotDetectCrossUserPkg() throws Exception {
        final KeySet keySet = mIPackageManager.getSigningKeySet(mContext.getPackageName());
        assertThrows(IllegalArgumentException.class,
                () -> mIPackageManager.isPackageSignedByKeySet(
                        CROSS_USER_TEST_PACKAGE_NAME, keySet));

        installPackageForUser(CROSS_USER_TEST_APK_FILE, mOtherUser);

        assertThrows(IllegalArgumentException.class,
                () -> mIPackageManager.isPackageSignedByKeySet(
                        CROSS_USER_TEST_PACKAGE_NAME, keySet));
    }

    @Test
    public void testIsPackageSignedByKeySetExactly_cannotDetectCrossUserPkg() throws Exception {
        final KeySet keySet = mIPackageManager.getSigningKeySet(mContext.getPackageName());
        assertThrows(IllegalArgumentException.class,
                () -> mIPackageManager.isPackageSignedByKeySetExactly(
                        CROSS_USER_TEST_PACKAGE_NAME, keySet));

        installPackageForUser(CROSS_USER_TEST_APK_FILE, mOtherUser);

        assertThrows(IllegalArgumentException.class,
                () -> mIPackageManager.isPackageSignedByKeySetExactly(
                        CROSS_USER_TEST_PACKAGE_NAME, keySet));
    }

    private static void installPackageForUser(File apk, UserReference user) {
        assertThat(apk.exists()).isTrue();
        final StringBuilder cmd = new StringBuilder("pm install --user ");
        cmd.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);
    }
}
Loading