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

APIs:
- IPackageManager#isPackageSignedByKeySet
- IPackageManager#isPackageSignedByKeySetExactly

Bug: 214394643
Test: atest CrossUserPackageVisibilityTests
Change-Id: I14e4cc5cf27f205353fe709bc2bc8b45778b0764
This commit is contained in:
Rhed Jao
2022-04-13 17:53:56 +08:00
parent c13b915317
commit f5f55f4171
7 changed files with 145 additions and 8 deletions

View File

@@ -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);
}

View File

@@ -30,6 +30,7 @@ android_test {
"compatibility-device-util-axt",
"androidx.test.runner",
"truth-prebuilt",
"Harrier",
],
platform_apis: true,
test_suites: ["device-tests"],

View File

@@ -16,7 +16,15 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.server.pm.test.appenumeration">
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"

View File

@@ -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" />

View File

@@ -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);
}
}

View File

@@ -62,3 +62,17 @@ android_test_helper_app {
test_suites: ["device-tests"],
platform_apis: true,
}
android_test_helper_app {
name: "AppEnumerationCrossUserPackageVisibilityTestApp",
srcs: ["src/**/*.java"],
manifest: "AndroidManifest-crossUserPackageVisibility.xml",
dex_preopt: {
enabled: false,
},
optimize: {
enabled: false,
},
test_suites: ["device-tests"],
platform_apis: true,
}

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2022 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.appenumeration.crossuserpackagevisibility">
<application>
</application>
</manifest>