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:
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user