From e6a81b38d61972bbc1c37f191874e8a50a4fb9e8 Mon Sep 17 00:00:00 2001 From: Alex Kershaw Date: Tue, 19 May 2020 20:30:16 +0100 Subject: [PATCH] Prevent DPCs from requesting INTERACT_ACROSS_PROFILES Fixes: 156745686 Test: atest com.android.cts.devicepolicy.CrossProfileAppsPermissionHostSideTest#testCanRequestInteractAcrossProfiles_profileOwner_returnsFalse Test: atest services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java --verbose Change-Id: Ia72573991ecccd9a9d01285ba55059e7f9ea14b0 --- .../admin/DevicePolicyManagerInternal.java | 5 +++ .../pm/CrossProfileAppsServiceImpl.java | 26 +++++++++++++ .../DevicePolicyManagerService.java | 5 +++ .../CrossProfileAppsServiceImplRoboTest.java | 38 +++++++++++++++++++ 4 files changed, 74 insertions(+) diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java index 41f04f73aa873..8f5dbc45bf1b1 100644 --- a/core/java/android/app/admin/DevicePolicyManagerInternal.java +++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java @@ -217,4 +217,9 @@ public abstract class DevicePolicyManagerInternal { */ public abstract void broadcastIntentToCrossProfileManifestReceiversAsUser(Intent intent, UserHandle parentHandle, boolean requiresPermission); + + /** + * Returns the profile owner component for the given user, or {@code null} if there is not one. + */ + public abstract ComponentName getProfileOwnerAsUser(int userHandle); } diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java index 28c8642d3e608..b9729f02d78bf 100644 --- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java +++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java @@ -31,6 +31,7 @@ import android.app.AppOpsManager; import android.app.AppOpsManager.Mode; import android.app.IApplicationThread; import android.app.admin.DevicePolicyEventLogger; +import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyManagerInternal; import android.content.ComponentName; import android.content.Context; @@ -255,6 +256,9 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { if (enabledProfileIds.length < 2) { return false; } + if (isProfileOwner(packageName, enabledProfileIds)) { + return false; + } return hasRequestedAppOpPermission( AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), packageName); } @@ -554,6 +558,9 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { if (profileIds.length < 2) { return false; } + if (isProfileOwner(packageName, profileIds)) { + return false; + } return hasRequestedAppOpPermission( AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), packageName) && !isPlatformSignedAppWithNonUserConfigurablePermission(packageName, profileIds); @@ -677,6 +684,25 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { packageName); } + private boolean isProfileOwner(String packageName, int[] userIds) { + for (int userId : userIds) { + if (isProfileOwner(packageName, userId)) { + return true; + } + } + return false; + } + + private boolean isProfileOwner(String packageName, final @UserIdInt int userId) { + final ComponentName profileOwner = + mInjector.withCleanCallingIdentity(() -> + mInjector.getDevicePolicyManagerInternal().getProfileOwnerAsUser(userId)); + if (profileOwner == null) { + return false; + } + return profileOwner.getPackageName().equals(packageName); + } + private static class InjectorImpl implements Injector { private Context mContext; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 7e4c8f3e48e58..34be681d1e471 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -12683,6 +12683,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Binder.restoreCallingIdentity(ident); } } + + @Override + public ComponentName getProfileOwnerAsUser(int userHandle) { + return DevicePolicyManagerService.this.getProfileOwnerAsUser(userHandle); + } } private Intent createShowAdminSupportIntent(ComponentName admin, int userId) { diff --git a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java index f8d197acf8833..65183f15b0fda 100644 --- a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java +++ b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java @@ -37,7 +37,9 @@ import android.annotation.UserIdInt; import android.app.ActivityManagerInternal; import android.app.AppOpsManager; import android.app.AppOpsManager.Mode; +import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyManagerInternal; +import android.content.ComponentName; import android.content.ContextWrapper; import android.content.Intent; import android.content.pm.ActivityInfo; @@ -96,6 +98,7 @@ public class CrossProfileAppsServiceImplRoboTest { private static final int WORK_PROFILE_USER_ID = 10; private static final int WORK_PROFILE_UID = 3333; private static final int OTHER_PROFILE_WITHOUT_CROSS_PROFILE_APP_USER_ID = 20; + private static final int OUTSIDE_PROFILE_GROUP_USER_ID = 30; private final ContextWrapper mContext = ApplicationProvider.getApplicationContext(); private final UserManager mUserManager = mContext.getSystemService(UserManager.class); @@ -224,6 +227,7 @@ public class CrossProfileAppsServiceImplRoboTest { PERSONAL_PROFILE_USER_ID, WORK_PROFILE_USER_ID, OTHER_PROFILE_WITHOUT_CROSS_PROFILE_APP_USER_ID); + shadowUserManager.addProfileIds(OUTSIDE_PROFILE_GROUP_USER_ID); } @Before @@ -474,6 +478,36 @@ public class CrossProfileAppsServiceImplRoboTest { .isFalse(); } + @Test + public void canUserAttemptToConfigureInteractAcrossProfiles_profileOwnerWorkProfile_returnsFalse() { + when(mDevicePolicyManagerInternal.getProfileOwnerAsUser(WORK_PROFILE_USER_ID)) + .thenReturn(buildCrossProfileComponentName()); + assertThat(mCrossProfileAppsServiceImpl + .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME)) + .isFalse(); + } + + @Test + public void canUserAttemptToConfigureInteractAcrossProfiles_profileOwnerOtherProfile_returnsFalse() { + // Normally, the DPC would not be a profile owner of the personal profile, but for the + // purposes of this test, it is just a profile owner of any profile within the profile + // group. + when(mDevicePolicyManagerInternal.getProfileOwnerAsUser(PERSONAL_PROFILE_USER_ID)) + .thenReturn(buildCrossProfileComponentName()); + assertThat(mCrossProfileAppsServiceImpl + .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME)) + .isFalse(); + } + + @Test + public void canUserAttemptToConfigureInteractAcrossProfiles_profileOwnerOutsideProfileGroup_returnsTrue() { + when(mDevicePolicyManagerInternal.getProfileOwnerAsUser(OUTSIDE_PROFILE_GROUP_USER_ID)) + .thenReturn(buildCrossProfileComponentName()); + assertThat(mCrossProfileAppsServiceImpl + .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME)) + .isTrue(); + } + @Test public void canUserAttemptToConfigureInteractAcrossProfiles_returnsTrue() { assertThat(mCrossProfileAppsServiceImpl @@ -578,6 +612,10 @@ public class CrossProfileAppsServiceImplRoboTest { .hideAsParsed()).hideAsFinal()); } + private ComponentName buildCrossProfileComponentName() { + return new ComponentName(CROSS_PROFILE_APP_PACKAGE_NAME, "testClassName"); + } + private class TestInjector implements CrossProfileAppsServiceImpl.Injector { @Override