Merge changes from topic "pm_cross_user_package_visibility_6"

* changes:
  Using isUidPrivileged instead of getPrivateFlagsForUid API
  Add package manager internal api checkUidSignaturesForAllUsers
  Fix cross user package visibility leakage for PackageManager (6/n)
This commit is contained in:
Rhed Jao
2022-05-16 04:21:48 +00:00
committed by Android (Google) Code Review
9 changed files with 199 additions and 107 deletions

View File

@@ -24,6 +24,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.os.Binder;
import android.os.UserHandle;
import android.util.ArraySet;
@@ -32,6 +33,7 @@ import android.util.DebugUtils;
import android.util.IndentingPrintWriter;
import com.android.internal.util.XmlUtils;
import com.android.server.LocalServices;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -108,7 +110,7 @@ class BlobAccessMode {
}
if ((mAccessType & ACCESS_TYPE_SAME_SIGNATURE) != 0) {
if (checkSignatures(context, callingUid, committerUid)) {
if (checkSignatures(callingUid, committerUid)) {
return true;
}
}
@@ -133,11 +135,11 @@ class BlobAccessMode {
/**
* Compare signatures for two packages of different users.
*/
private boolean checkSignatures(Context context, int uid1, int uid2) {
private boolean checkSignatures(int uid1, int uid2) {
final long token = Binder.clearCallingIdentity();
try {
return context.getPackageManager().checkSignatures(uid1, uid2)
== PackageManager.SIGNATURE_MATCH;
return LocalServices.getService(PackageManagerInternal.class)
.checkUidSignaturesForAllUsers(uid1, uid2) == PackageManager.SIGNATURE_MATCH;
} finally {
Binder.restoreCallingIdentity(token);
}

View File

@@ -28,6 +28,7 @@ import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.PackageManager.SignatureResult;
import android.content.pm.SigningDetails.CertCapabilities;
import android.content.pm.overlay.OverlayPaths;
import android.os.Bundle;
@@ -1283,4 +1284,15 @@ public abstract class PackageManagerInternal {
public abstract void shutdown();
public abstract DynamicCodeLogger getDynamicCodeLogger();
/**
* Compare the signatures of two packages that are installed in different users.
*
* @param uid1 First UID whose signature will be compared.
* @param uid2 Second UID whose signature will be compared.
* @return {@link PackageManager#SIGNATURE_MATCH} if signatures are matched.
* @throws SecurityException if the caller does not hold the
* {@link android.Manifest.permission#INTERACT_ACROSS_USERS}.
*/
public abstract @SignatureResult int checkUidSignaturesForAllUsers(int uid1, int uid2);
}

View File

@@ -25,6 +25,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.os.Binder;
import android.os.Environment;
import android.os.FileObserver;
import android.os.Handler;
@@ -129,7 +130,7 @@ public class IntentFirewall {
mObserver.startWatching();
}
private PackageManagerInternal getPackageManager() {
PackageManagerInternal getPackageManager() {
if (mPackageManager == null) {
mPackageManager = LocalServices.getService(PackageManagerInternal.class);
}
@@ -623,12 +624,13 @@ public class IntentFirewall {
}
boolean signaturesMatch(int uid1, int uid2) {
final long token = Binder.clearCallingIdentity();
try {
IPackageManager pm = AppGlobals.getPackageManager();
return pm.checkUidSignatures(uid1, uid2) == PackageManager.SIGNATURE_MATCH;
} catch (RemoteException ex) {
Slog.e(TAG, "Remote exception while checking signatures", ex);
return false;
// Compare signatures of two packages for different users.
return getPackageManager()
.checkUidSignaturesForAllUsers(uid1, uid2) == PackageManager.SIGNATURE_MATCH;
} finally {
Binder.restoreCallingIdentity(token);
}
}

View File

@@ -16,14 +16,11 @@
package com.android.server.firewall;
import android.app.AppGlobals;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManagerInternal;
import android.os.Process;
import android.os.RemoteException;
import android.util.Slog;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -37,22 +34,12 @@ class SenderFilter {
private static final String VAL_SYSTEM_OR_SIGNATURE = "system|signature";
private static final String VAL_USER_ID = "userId";
static boolean isPrivilegedApp(int callerUid, int callerPid) {
static boolean isPrivilegedApp(PackageManagerInternal pmi, int callerUid, int callerPid) {
if (callerUid == Process.SYSTEM_UID || callerUid == 0 ||
callerPid == Process.myPid() || callerPid == 0) {
return true;
}
IPackageManager pm = AppGlobals.getPackageManager();
try {
return (pm.getPrivateFlagsForUid(callerUid) & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED)
!= 0;
} catch (RemoteException ex) {
Slog.e(IntentFirewall.TAG, "Remote exception while retrieving uid flags",
ex);
}
return false;
return pmi.isUidPrivileged(callerUid);
}
public static final FilterFactory FACTORY = new FilterFactory("sender") {
@@ -89,7 +76,7 @@ class SenderFilter {
@Override
public boolean matches(IntentFirewall ifw, ComponentName resolvedComponent, Intent intent,
int callerUid, int callerPid, String resolvedType, int receivingUid) {
return isPrivilegedApp(callerUid, callerPid);
return isPrivilegedApp(ifw.getPackageManager(), callerUid, callerPid);
}
};
@@ -97,8 +84,8 @@ class SenderFilter {
@Override
public boolean matches(IntentFirewall ifw, ComponentName resolvedComponent, Intent intent,
int callerUid, int callerPid, String resolvedType, int receivingUid) {
return isPrivilegedApp(callerUid, callerPid) ||
ifw.signaturesMatch(callerUid, receivingUid);
return isPrivilegedApp(ifw.getPackageManager(), callerUid, callerPid)
|| ifw.signaturesMatch(callerUid, receivingUid);
}
};

View File

@@ -228,8 +228,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);
@@ -378,6 +398,8 @@ public interface Computer extends PackageDataSnapshot {
int checkUidSignatures(int uid1, int uid2);
int checkUidSignaturesForAllUsers(int uid1, int uid2);
boolean hasSigningCertificate(@NonNull String packageName, @NonNull byte[] certificate,
@PackageManager.CertificateInputType int type);

View File

@@ -2793,6 +2793,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.
@@ -4253,54 +4273,58 @@ public class ComputerEngine implements Computer {
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, callingUserId);
final SigningDetails p2SigningDetails =
getSigningDetailsAndFilterAccess(uid2, callingUid, callingUserId);
if (p1SigningDetails == null || p2SigningDetails == null) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
return checkSignaturesInternal(p1SigningDetails, p2SigningDetails);
}
@Override
public int checkUidSignaturesForAllUsers(int uid1, int uid2) {
final int callingUid = Binder.getCallingUid();
final int userId1 = UserHandle.getUserId(uid1);
final int userId2 = UserHandle.getUserId(uid2);
enforceCrossUserPermission(callingUid, userId1, false /* requireFullPermission */,
false /* checkShell */, "checkUidSignaturesForAllUsers");
enforceCrossUserPermission(callingUid, userId2, false /* requireFullPermission */,
false /* checkShell */, "checkUidSignaturesForAllUsers");
final SigningDetails p1SigningDetails =
getSigningDetailsAndFilterAccess(uid1, callingUid, userId1);
final SigningDetails p2SigningDetails =
getSigningDetailsAndFilterAccess(uid2, callingUid, userId2);
if (p1SigningDetails == null || p2SigningDetails == null) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
return checkSignaturesInternal(p1SigningDetails, p2SigningDetails);
}
private SigningDetails getSigningDetailsAndFilterAccess(int uid, int callingUid, int userId) {
// Map to base uids.
final int appId = UserHandle.getAppId(uid);
final Object obj = mSettings.getSettingBase(appId);
if (obj == null) {
return null;
}
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
if (shouldFilterApplicationIncludingUninstalled(sus, callingUid, userId)) {
return null;
}
return sus.signatures.mSigningDetails;
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
if (shouldFilterApplicationIncludingUninstalled(ps, callingUid, userId)) {
return null;
}
return ps.getSigningDetails();
}
return null;
}
private int checkSignaturesInternal(SigningDetails p1SigningDetails,
SigningDetails p2SigningDetails) {
if (p1SigningDetails == null) {
@@ -4363,27 +4387,9 @@ public class ComputerEngine implements Computer {
@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, callingUserId);
if (signingDetails == null) {
return false;
}
switch (type) {
@@ -4446,13 +4452,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();
@@ -4481,14 +4487,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();
@@ -4510,7 +4516,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;
}
@@ -4531,13 +4537,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();
@@ -4559,13 +4565,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

@@ -720,6 +720,11 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal {
return snapshot().isUidPrivileged(uid);
}
@Override
public int checkUidSignaturesForAllUsers(int uid1, int uid2) {
return snapshot().checkUidSignaturesForAllUsers(uid1, uid2);
}
@NonNull
@Override
@Deprecated

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>