summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/admin/DevicePolicyManagerInternal.java25
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java206
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/Owners.java18
3 files changed, 243 insertions, 6 deletions
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index 840f3a3c6bb8..3a61ca1fbf93 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -271,6 +271,31 @@ public abstract class DevicePolicyManagerInternal {
public abstract void resetOp(int op, String packageName, @UserIdInt int userId);
/**
+ * Checks if the calling process has been granted permission to apply a device policy on a
+ * specific user.
+ *
+ * The given permission will be checked along with its associated cross-user permission, if it
+ * exists and the target user is different to the calling user.
+ *
+ * @param permission The name of the permission being checked.
+ * @param targetUserId The userId of the user which the caller needs permission to act on.
+ * @throws SecurityException If the calling process has not been granted the permission.
+ */
+ public abstract void enforcePermission(String permission, int targetUserId);
+
+ /**
+ * Return whether the calling process has been granted permission to apply a device policy on
+ * a specific user.
+ *
+ * The given permission will be checked along with its associated cross-user
+ * permission, if it exists and the target user is different to the calling user.
+ *
+ * @param permission The name of the permission being checked.
+ * @param targetUserId The userId of the user which the caller needs permission to act on.
+ */
+ public abstract boolean hasPermission(String permission, int targetUserId);
+
+ /**
* Returns whether new "turn off work" behavior is enabled via feature flag.
*/
public abstract boolean isKeepProfilesRunningEnabled();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c42ddf81dfd4..92d6fb194557 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -18,6 +18,10 @@ package com.android.server.devicepolicy;
import static android.Manifest.permission.BIND_DEVICE_ADMIN;
import static android.Manifest.permission.MANAGE_CA_CERTIFICATES;
+import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS;
+import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL;
+import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL;
+import static android.Manifest.permission.QUERY_ADMIN_POLICY;
import static android.Manifest.permission.REQUEST_PASSWORD_COMPLEXITY;
import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
@@ -137,6 +141,7 @@ import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT;
import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE;
import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK;
@@ -13393,6 +13398,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
broadcastIntentToDevicePolicyManagerRoleHolder(intent, parentHandle);
}
+ @Override
+ public void enforcePermission(String permission, int targetUserId) {
+ DevicePolicyManagerService.this.enforcePermission(permission, targetUserId);
+ }
+
+ @Override
+ public boolean hasPermission(String permission, int targetUserId) {
+ return DevicePolicyManagerService.this.hasPermission(permission, targetUserId);
+ }
+
private void broadcastIntentToCrossProfileManifestReceivers(
Intent intent, UserHandle userHandle, boolean requiresPermission) {
final int userId = userHandle.getIdentifier();
@@ -18137,14 +18152,25 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
+
private String getDevicePolicyManagementRoleHolderPackageName(Context context) {
RoleManager roleManager = context.getSystemService(RoleManager.class);
- List<String> roleHolders =
- roleManager.getRoleHolders(RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT);
- if (roleHolders.isEmpty()) {
- return null;
- }
- return roleHolders.get(0);
+
+ // Calling identity needs to be cleared as this method is used in the permissions checks.
+ return mInjector.binderWithCleanCallingIdentity(() -> {
+ List<String> roleHolders =
+ roleManager.getRoleHolders(RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT);
+ if (roleHolders.isEmpty()) {
+ return null;
+ }
+ return roleHolders.get(0);
+ });
+ }
+
+ private boolean isDevicePolicyManagementRoleHolder(CallerIdentity caller) {
+ String devicePolicyManagementRoleHolderPackageName =
+ getDevicePolicyManagementRoleHolderPackageName(mContext);
+ return caller.getPackageName().equals(devicePolicyManagementRoleHolderPackageName);
}
private void resetInteractAcrossProfilesAppOps() {
@@ -19221,6 +19247,174 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
});
}
+ // DPC types
+ private static final int DEFAULT_DEVICE_OWNER = 0;
+ private static final int FINANCED_DEVICE_OWNER = 1;
+ private static final int PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE = 2;
+ private static final int PROFILE_OWNER_ON_USER_0 = 3;
+ private static final int PROFILE_OWNER = 4;
+
+ // Permissions of existing DPC types.
+ private static final List<String> DEFAULT_DEVICE_OWNER_PERMISSIONS = List.of(
+ MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL);
+ private static final List<String> FINANCED_DEVICE_OWNER_PERMISSIONS = List.of(
+ MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL);
+ private static final List<String> PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE_PERMISSIONS =
+ List.of(
+ MANAGE_DEVICE_POLICY_ACROSS_USERS,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL);
+ private static final List<String> PROFILE_OWNER_ON_USER_0_PERMISSIONS = List.of();
+ private static final List<String> PROFILE_OWNER_PERMISSIONS = List.of(
+ MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL);
+
+
+ private static final HashMap<Integer, List<String>> DPC_PERMISSIONS = new HashMap<>();
+ {
+ DPC_PERMISSIONS.put(DEFAULT_DEVICE_OWNER, DEFAULT_DEVICE_OWNER_PERMISSIONS);
+ DPC_PERMISSIONS.put(FINANCED_DEVICE_OWNER, FINANCED_DEVICE_OWNER_PERMISSIONS);
+ DPC_PERMISSIONS.put(PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE,
+ PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE_PERMISSIONS);
+ DPC_PERMISSIONS.put(PROFILE_OWNER_ON_USER_0, PROFILE_OWNER_ON_USER_0_PERMISSIONS);
+ DPC_PERMISSIONS.put(PROFILE_OWNER, PROFILE_OWNER_PERMISSIONS);
+ }
+
+ //TODO(b/254253251) Fill this map in as new permissions are added for policies.
+ private static final HashMap<String, Integer> ACTIVE_ADMIN_POLICIES = new HashMap<>();
+
+ private static final HashMap<String, String> CROSS_USER_PERMISSIONS = new HashMap<>();
+
+ /**
+ * Checks if the calling process has been granted permission to apply a device policy on a
+ * specific user.
+ * The given permission will be checked along with its associated cross-user permission if it
+ * exists and the target user is different to the calling user.
+ *
+ * @param permission The name of the permission being checked.
+ * @param targetUserId The userId of the user which the caller needs permission to act on.
+ * @throws SecurityException if the caller has not been granted the given permission,
+ * the associtated cross-user permission if the caller's user is different to the target user.
+ */
+ private void enforcePermission(String permission, int targetUserId)
+ throws SecurityException {
+ if (!hasPermission(permission, targetUserId)) {
+ throw new SecurityException("Caller does not have the required permissions for "
+ + "this user. Permissions required: {"
+ + permission
+ + ", "
+ + CROSS_USER_PERMISSIONS.get(permission)
+ + "}");
+ }
+ }
+
+ /**
+ * Return whether the calling process has been granted permission to query a device policy on
+ * a specific user.
+ *
+ * @param permission The name of the permission being checked.
+ * @param targetUserId The userId of the user which the caller needs permission to act on.
+ * @throws SecurityException if the caller has not been granted the given permission,
+ * the associatated cross-user permission if the caller's user is different to the target user
+ * and if the user has not been granted {@link QUERY_ADMIN_POLICY}.
+ */
+ private void enforceCanQuery(String permission, int targetUserId) throws SecurityException {
+ if (hasPermission(QUERY_ADMIN_POLICY)) {
+ return;
+ }
+ enforcePermission(permission, targetUserId);
+ }
+
+ /**
+ * Return whether the calling process has been granted permission to apply a device policy on
+ * a specific user.
+ *
+ * @param permission The name of the permission being checked.
+ * @param targetUserId The userId of the user which the caller needs permission to act on.
+ */
+ private boolean hasPermission(String permission, int targetUserId) {
+ boolean hasPermission = hasPermission(permission);
+ if (getCallerIdentity().getUserId() != targetUserId) {
+ hasPermission = hasPermission
+ && hasPermission(CROSS_USER_PERMISSIONS.get(permission));
+ }
+ return hasPermission;
+ }
+
+ /**
+ * Return whether the calling process has been granted the given permission.
+ *
+ * @param permission The name of the permission being checked.
+ */
+ private boolean hasPermission(String permission) {
+ if (permission == null) {
+ return true;
+ }
+
+ CallerIdentity caller = getCallerIdentity();
+
+ // Check if the caller holds the permission
+ if (mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) {
+ return true;
+ }
+ // Check the permissions of DPCs
+ if (isDefaultDeviceOwner(caller)) {
+ return DPC_PERMISSIONS.get(DEFAULT_DEVICE_OWNER).contains(permission);
+ }
+ if (isFinancedDeviceOwner(caller)) {
+ return DPC_PERMISSIONS.get(FINANCED_DEVICE_OWNER).contains(permission);
+ }
+ if (isProfileOwnerOfOrganizationOwnedDevice(caller)) {
+ return DPC_PERMISSIONS.get(PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE).contains(
+ permission);
+ }
+ if (isProfileOwnerOnUser0(caller)) {
+ return DPC_PERMISSIONS.get(PROFILE_OWNER_ON_USER_0).contains(permission);
+ }
+ if (isProfileOwner(caller)) {
+ return DPC_PERMISSIONS.get(PROFILE_OWNER).contains(permission);
+ }
+ // Check the permission for the role-holder
+ if (isDevicePolicyManagementRoleHolder(caller)) {
+ return anyDpcHasPermission(permission, mContext.getUserId());
+ }
+ // Check if the caller is an active admin that uses a certain policy.
+ if (ACTIVE_ADMIN_POLICIES.containsKey(permission)) {
+ return getActiveAdminForCallerLocked(
+ null, ACTIVE_ADMIN_POLICIES.get(permission), false) != null;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns whether there is a DPC on the given user that has been granted the given permission.
+ *
+ * @param permission The name of the permission being checked.
+ * @param userId The id of the user to check.
+ */
+ private boolean anyDpcHasPermission(String permission, int userId) {
+ if (mOwners.isDefaultDeviceOwnerUserId(userId)) {
+ return DPC_PERMISSIONS.get(DEFAULT_DEVICE_OWNER).contains(permission);
+ }
+ if (mOwners.isFinancedDeviceOwnerUserId(userId)) {
+ return DPC_PERMISSIONS.get(FINANCED_DEVICE_OWNER).contains(permission);
+ }
+ if (mOwners.isProfileOwnerOfOrganizationOwnedDevice(userId)) {
+ return DPC_PERMISSIONS.get(PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE).contains(
+ permission);
+ }
+ if (userId == 0 && mOwners.hasProfileOwner(0)) {
+ return DPC_PERMISSIONS.get(PROFILE_OWNER_ON_USER_0).contains(permission);
+ }
+ if (mOwners.hasProfileOwner(userId)) {
+ return DPC_PERMISSIONS.get(PROFILE_OWNER).contains(permission);
+ }
+ return false;
+ }
+
// TODO(b/260560985): properly gate coexistence changes
private boolean isCoexistenceEnabled(CallerIdentity caller) {
return isCoexistenceFlagEnabled()
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 6f172e4515fc..581a19913530 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -19,6 +19,7 @@ package com.android.server.devicepolicy;
import static android.app.admin.DevicePolicyManager.DEPRECATE_USERMANAGERINTERNAL_DEVICEPOLICY_DEFAULT;
import static android.app.admin.DevicePolicyManager.DEPRECATE_USERMANAGERINTERNAL_DEVICEPOLICY_FLAG;
import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT;
+import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
import static com.android.server.devicepolicy.DeviceStateCacheImpl.NO_DEVICE_OWNER;
@@ -455,6 +456,23 @@ class Owners {
}
}
+ boolean isDefaultDeviceOwnerUserId(int userId) {
+ synchronized (mData) {
+ return mData.mDeviceOwner != null
+ && mData.mDeviceOwnerUserId == userId
+ && getDeviceOwnerType(getDeviceOwnerPackageName()) == DEVICE_OWNER_TYPE_DEFAULT;
+ }
+ }
+
+ boolean isFinancedDeviceOwnerUserId(int userId) {
+ synchronized (mData) {
+ return mData.mDeviceOwner != null
+ && mData.mDeviceOwnerUserId == userId
+ && getDeviceOwnerType(getDeviceOwnerPackageName())
+ == DEVICE_OWNER_TYPE_FINANCED;
+ }
+ }
+
boolean hasProfileOwner(int userId) {
synchronized (mData) {
return getProfileOwnerComponent(userId) != null;