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
This commit is contained in:
Rhed Jao
2022-05-03 19:12:46 +08:00
parent 3d3d2c950e
commit 5321abef41
4 changed files with 138 additions and 79 deletions

View File

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

View File

@@ -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,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();

View File

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

View File

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