diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java index 8e8ca8605343b..bcffd65d34240 100644 --- a/services/core/java/com/android/server/pm/Computer.java +++ b/services/core/java/com/android/server/pm/Computer.java @@ -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. + *

+ * 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); diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java index 24004bc47954f..edbaba5faa2d7 100644 --- a/services/core/java/com/android/server/pm/ComputerEngine.java +++ b/services/core/java/com/android/server/pm/ComputerEngine.java @@ -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 packageStates = + (ArraySet) 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,55 +4263,38 @@ 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)) { - return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; - } - p1SigningDetails = ps.getSigningDetails(); - } else { - return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; - } - } else { - return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; - } - 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; - } - p2SigningDetails = 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; - } - } else { + final SigningDetails p1SigningDetails = getSigningDetailsAndFilterAccess(uid1, callingUid); + final SigningDetails p2SigningDetails = getSigningDetailsAndFilterAccess(uid2, callingUid); + if (p1SigningDetails == null || p2SigningDetails == null) { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } return checkSignaturesInternal(p1SigningDetails, p2SigningDetails); } + 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; + } + if (obj instanceof SharedUserSetting) { + final SharedUserSetting sus = (SharedUserSetting) obj; + if (shouldFilterApplicationIncludingUninstalled(sus, callingUid, callingUserId)) { + return null; + } + return sus.signatures.mSigningDetails; + } else if (obj instanceof PackageSetting) { + final PackageSetting ps = (PackageSetting) obj; + if (shouldFilterApplicationIncludingUninstalled(ps, callingUid, callingUserId)) { + return null; + } + return ps.getSigningDetails(); + } + return null; + } + private int checkSignaturesInternal(SigningDetails p1SigningDetails, SigningDetails p2SigningDetails) { if (p1SigningDetails == null) { @@ -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(); diff --git a/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java b/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java index a7ba45f0c4c2b..d7b442c8eaf8a 100644 --- a/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java +++ b/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java @@ -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()); } } diff --git a/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/AndroidManifest-crossUserPackageVisibility.xml b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/AndroidManifest-crossUserPackageVisibility.xml index 874a1fc5ff3e8..9d38ddfab5f10 100644 --- a/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/AndroidManifest-crossUserPackageVisibility.xml +++ b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/AndroidManifest-crossUserPackageVisibility.xml @@ -17,6 +17,6 @@ - +