Merge changes I14e4cc5c,I2875765b

* changes:
  Fix cross user package visibility leakage for PackageManager (1/n)
  Add filtering uninstalled package flag to the shouldFilterApplication
This commit is contained in:
Rhed Jao
2022-04-18 03:05:50 +00:00
committed by Android (Google) Code Review
9 changed files with 185 additions and 22 deletions

View File

@@ -215,6 +215,9 @@ public interface Computer extends PackageDataSnapshot {
boolean isInstantApp(String packageName, int userId);
boolean isInstantAppInternal(String packageName, @UserIdInt int userId, int callingUid);
boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId);
boolean shouldFilterApplication(@Nullable PackageStateInternal ps, int callingUid,
@Nullable ComponentName component, @PackageManager.ComponentType int componentType,
int userId, boolean filterUninstall);
boolean shouldFilterApplication(@Nullable PackageStateInternal ps, int callingUid,
@Nullable ComponentName component, @PackageManager.ComponentType int componentType,
int userId);
@@ -222,6 +225,8 @@ public interface Computer extends PackageDataSnapshot {
int userId);
boolean shouldFilterApplication(@NonNull SharedUserSetting sus, int callingUid,
int userId);
boolean shouldFilterApplicationIncludingUninstalled(@Nullable PackageStateInternal ps,
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);

View File

@@ -2677,7 +2677,7 @@ public class ComputerEngine implements Computer {
*/
public final boolean shouldFilterApplication(@Nullable PackageStateInternal ps,
int callingUid, @Nullable ComponentName component,
@PackageManager.ComponentType int componentType, int userId) {
@PackageManager.ComponentType int componentType, int userId, boolean filterUninstall) {
if (Process.isSdkSandboxUid(callingUid)) {
int clientAppUid = Process.getAppUidForSdkSandboxUid(callingUid);
// SDK sandbox should be able to see it's client app
@@ -2691,9 +2691,11 @@ public class ComputerEngine implements Computer {
}
final String instantAppPkgName = getInstantAppPackageName(callingUid);
final boolean callerIsInstantApp = instantAppPkgName != null;
if (ps == null) {
// pretend the application exists, but, needs to be filtered
return callerIsInstantApp;
if (ps == null
|| (filterUninstall && !ps.getUserStateOrDefault(userId).isInstalled())) {
// If caller is instant app and ps is null, pretend the application exists,
// but, needs to be filtered
return (callerIsInstantApp || filterUninstall);
}
// if the target and caller are the same application, don't filter
if (isCallerSameApp(ps.getPackageName(), callingUid)) {
@@ -2738,15 +2740,26 @@ public class ComputerEngine implements Computer {
}
/**
* @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int)
* @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean)
*/
public final boolean shouldFilterApplication(
@Nullable PackageStateInternal ps, int callingUid, int userId) {
return shouldFilterApplication(ps, callingUid, null, TYPE_UNKNOWN, userId);
public final boolean shouldFilterApplication(@Nullable PackageStateInternal ps,
int callingUid, @Nullable ComponentName component,
@PackageManager.ComponentType int componentType, int userId) {
return shouldFilterApplication(
ps, callingUid, component, componentType, userId, false /* filterUninstall */);
}
/**
* @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int)
* @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean)
*/
public final boolean shouldFilterApplication(
@Nullable PackageStateInternal ps, int callingUid, int userId) {
return shouldFilterApplication(
ps, callingUid, null, TYPE_UNKNOWN, userId, false /* filterUninstall */);
}
/**
* @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean)
*/
public final boolean shouldFilterApplication(@NonNull SharedUserSetting sus,
int callingUid, int userId) {
@@ -2754,12 +2767,21 @@ public class ComputerEngine implements Computer {
final ArraySet<PackageStateInternal> packageStates =
(ArraySet<PackageStateInternal>) sus.getPackageStates();
for (int index = packageStates.size() - 1; index >= 0 && filterApp; index--) {
filterApp &= shouldFilterApplication(packageStates.valueAt(index),
callingUid, /* component */ null, TYPE_UNKNOWN, userId);
filterApp &= shouldFilterApplication(packageStates.valueAt(index), callingUid,
null /* component */, TYPE_UNKNOWN, userId, false /* filterUninstall */);
}
return filterApp;
}
/**
* @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean)
*/
public final boolean shouldFilterApplicationIncludingUninstalled(
@Nullable PackageStateInternal ps, int callingUid, int userId) {
return shouldFilterApplication(
ps, callingUid, null, TYPE_UNKNOWN, userId, true /* filterUninstall */);
}
/**
* Verification statuses are ordered from the worse to the best, except for
* INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER, which is the worse.
@@ -5264,9 +5286,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);
}
@@ -5288,9 +5311,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

@@ -6636,9 +6636,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService
@UserIdInt int userId) {
PackageStateInternal packageState =
computer.getPackageStateInternal(packageName, callingUid);
if (packageState == null
|| computer.shouldFilterApplication(packageState, callingUid, userId)
|| !packageState.getUserStateOrDefault(userId).isInstalled()) {
if (computer.shouldFilterApplicationIncludingUninstalled(
packageState, callingUid, userId)) {
return null;
} else {
return packageState;

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>