diff options
| author | 2023-01-04 13:16:33 +0000 | |
|---|---|---|
| committer | 2023-01-04 13:16:33 +0000 | |
| commit | 05bda46809a7e8ef93e66e32b1317dc80674effc (patch) | |
| tree | 977e51022309d9f9bf36eac39df6d101c0d668e8 | |
| parent | 5d7d1378dfc94b23551101dd6a02c402eef74b4a (diff) | |
| parent | 10cee472fb23c185c8d0b20f9123ab15f4fd7535 (diff) | |
Merge changes from topic "create_manage_cross_user_permissions"
* changes:
Add permission checks for auto time APIs
Create permission check methods in DPMS
Create new permissions for managing policies across users.
6 files changed, 409 insertions, 44 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 326a8e781a60..878a63ec200b 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -122,6 +122,10 @@ package android { field public static final String LOADER_USAGE_STATS = "android.permission.LOADER_USAGE_STATS"; field public static final String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE"; field public static final String MANAGE_DEVICE_LOCK_STATE = "android.permission.MANAGE_DEVICE_LOCK_STATE"; + field public static final String MANAGE_DEVICE_POLICY_ACROSS_USERS = "android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS"; + field public static final String MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL = "android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL"; + field public static final String MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL = "android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL"; + field public static final String MANAGE_DEVICE_POLICY_TIME = "android.permission.MANAGE_DEVICE_POLICY_TIME"; field public static final String MANAGE_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS"; field public static final String MANAGE_EXTERNAL_STORAGE = "android.permission.MANAGE_EXTERNAL_STORAGE"; field public static final String MANAGE_MEDIA = "android.permission.MANAGE_MEDIA"; @@ -7499,9 +7503,9 @@ package android.app.admin { method @Nullable public String getAlwaysOnVpnPackage(@NonNull android.content.ComponentName); method @NonNull @WorkerThread public android.os.Bundle getApplicationRestrictions(@Nullable android.content.ComponentName, String); method @Deprecated @Nullable public String getApplicationRestrictionsManagingPackage(@NonNull android.content.ComponentName); - method public boolean getAutoTimeEnabled(@NonNull android.content.ComponentName); + method @RequiresPermission(anyOf={android.Manifest.permission.SET_TIME, "android.permission.QUERY_ADMIN_POLICY"}, conditional=true) public boolean getAutoTimeEnabled(@NonNull android.content.ComponentName); method @Deprecated public boolean getAutoTimeRequired(); - method public boolean getAutoTimeZoneEnabled(@NonNull android.content.ComponentName); + method @RequiresPermission(anyOf={android.Manifest.permission.SET_TIME_ZONE, "android.permission.QUERY_ADMIN_POLICY"}, conditional=true) public boolean getAutoTimeZoneEnabled(@NonNull android.content.ComponentName); method @NonNull public java.util.List<android.os.UserHandle> getBindDeviceAdminTargetUsers(@NonNull android.content.ComponentName); method public boolean getBluetoothContactSharingDisabled(@NonNull android.content.ComponentName); method public boolean getCameraDisabled(@Nullable android.content.ComponentName); @@ -7648,9 +7652,9 @@ package android.app.admin { method public boolean setApplicationHidden(@NonNull android.content.ComponentName, String, boolean); method @WorkerThread public void setApplicationRestrictions(@Nullable android.content.ComponentName, String, android.os.Bundle); method @Deprecated public void setApplicationRestrictionsManagingPackage(@NonNull android.content.ComponentName, @Nullable String) throws android.content.pm.PackageManager.NameNotFoundException; - method public void setAutoTimeEnabled(@NonNull android.content.ComponentName, boolean); + method @RequiresPermission(value=android.Manifest.permission.SET_TIME, conditional=true) public void setAutoTimeEnabled(@NonNull android.content.ComponentName, boolean); method @Deprecated public void setAutoTimeRequired(@NonNull android.content.ComponentName, boolean); - method public void setAutoTimeZoneEnabled(@NonNull android.content.ComponentName, boolean); + method @RequiresPermission(value=android.Manifest.permission.SET_TIME_ZONE, conditional=true) public void setAutoTimeZoneEnabled(@NonNull android.content.ComponentName, boolean); method public void setBackupServiceEnabled(@NonNull android.content.ComponentName, boolean); method public void setBluetoothContactSharingDisabled(@NonNull android.content.ComponentName, boolean); method public void setCameraDisabled(@NonNull android.content.ComponentName, boolean); @@ -7729,8 +7733,8 @@ package android.app.admin { method @Deprecated public int setStorageEncryption(@NonNull android.content.ComponentName, boolean); method public void setSystemSetting(@NonNull android.content.ComponentName, @NonNull String, String); method public void setSystemUpdatePolicy(@NonNull android.content.ComponentName, android.app.admin.SystemUpdatePolicy); - method public boolean setTime(@NonNull android.content.ComponentName, long); - method public boolean setTimeZone(@NonNull android.content.ComponentName, String); + method @RequiresPermission(value=android.Manifest.permission.SET_TIME, conditional=true) public boolean setTime(@NonNull android.content.ComponentName, long); + method @RequiresPermission(value=android.Manifest.permission.SET_TIME_ZONE, conditional=true) public boolean setTimeZone(@NonNull android.content.ComponentName, String); method public void setTrustAgentConfiguration(@NonNull android.content.ComponentName, @NonNull android.content.ComponentName, android.os.PersistableBundle); method public void setUninstallBlocked(@Nullable android.content.ComponentName, String, boolean); method public void setUsbDataSignalingEnabled(boolean); diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index e729e7d8f9be..585c014baf25 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -16,6 +16,9 @@ package android.app.admin; +import static android.Manifest.permission.QUERY_ADMIN_POLICY; +import static android.Manifest.permission.SET_TIME; +import static android.Manifest.permission.SET_TIME_ZONE; import static android.content.Intent.LOCAL_FLAG_FROM_SYSTEM; import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1; @@ -8466,8 +8469,10 @@ public class DevicePolicyManager { } /** - * Called by a device owner, a profile owner for the primary user or a profile - * owner of an organization-owned managed profile to turn auto time on and off. + * Called by a device owner, a profile owner for the primary user, a profile + * owner of an organization-owned managed profile or, starting from Android + * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, holders of the permission + * {@link android.Manifest.permission#SET_TIME} to turn auto time on and off. * Callers are recommended to use {@link UserManager#DISALLOW_CONFIG_DATE_TIME} * to prevent the user from changing this setting. * <p> @@ -8478,8 +8483,10 @@ public class DevicePolicyManager { * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @param enabled Whether time should be obtained automatically from the network or not. * @throws SecurityException if caller is not a device owner, a profile owner for the - * primary user, or a profile owner of an organization-owned managed profile. + * primary user, or a profile owner of an organization-owned managed profile or a holder of the + * permission {@link android.Manifest.permission#SET_TIME}. */ + @RequiresPermission(value = SET_TIME, conditional = true) public void setAutoTimeEnabled(@NonNull ComponentName admin, boolean enabled) { if (mService != null) { try { @@ -8491,10 +8498,18 @@ public class DevicePolicyManager { } /** + * Returns true if auto time is enabled on the device. + * + * <p> Starting from Android {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, callers + * are also able to call this method if they hold the permission + *{@link android.Manifest.permission#SET_TIME}. + * * @return true if auto time is enabled on the device. - * @throws SecurityException if caller is not a device owner, a profile owner for the - * primary user, or a profile owner of an organization-owned managed profile. + * @throws SecurityException if the caller is not a device owner, a profile + * owner for the primary user, or a profile owner of an organization-owned managed profile or a + * holder of the permission {@link android.Manifest.permission#SET_TIME}. */ + @RequiresPermission(anyOf = {SET_TIME, QUERY_ADMIN_POLICY}, conditional = true) public boolean getAutoTimeEnabled(@NonNull ComponentName admin) { if (mService != null) { try { @@ -8507,8 +8522,10 @@ public class DevicePolicyManager { } /** - * Called by a device owner, a profile owner for the primary user or a profile - * owner of an organization-owned managed profile to turn auto time zone on and off. + * Called by a device owner, a profile owner for the primary user, a profile + * owner of an organization-owned managed profile or, starting from Android + * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, holders of the permission + * {@link android.Manifest.permission#SET_TIME} to turn auto time zone on and off. * Callers are recommended to use {@link UserManager#DISALLOW_CONFIG_DATE_TIME} * to prevent the user from changing this setting. * <p> @@ -8519,8 +8536,10 @@ public class DevicePolicyManager { * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @param enabled Whether time zone should be obtained automatically from the network or not. * @throws SecurityException if caller is not a device owner, a profile owner for the - * primary user, or a profile owner of an organization-owned managed profile. + * primary user, or a profile owner of an organization-owned managed profile or a holder of the + * permission {@link android.Manifest.permission#SET_TIME_ZONE}. */ + @RequiresPermission(value = SET_TIME_ZONE, conditional = true) public void setAutoTimeZoneEnabled(@NonNull ComponentName admin, boolean enabled) { throwIfParentInstance("setAutoTimeZone"); if (mService != null) { @@ -8533,10 +8552,18 @@ public class DevicePolicyManager { } /** + * Returns true if auto time zone is enabled on the device. + * + * <p> Starting from Android {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, callers + * are also able to call this method if they hold the permission + *{@link android.Manifest.permission#SET_TIME}. + * * @return true if auto time zone is enabled on the device. - * @throws SecurityException if caller is not a device owner, a profile owner for the - * primary user, or a profile owner of an organization-owned managed profile. + * @throws SecurityException if the caller is not a device owner, a profile + * owner for the primary user, or a profile owner of an organization-owned managed profile or a + * holder of the permission {@link android.Manifest.permission#SET_TIME_ZONE}. */ + @RequiresPermission(anyOf = {SET_TIME_ZONE, QUERY_ADMIN_POLICY}, conditional = true) public boolean getAutoTimeZoneEnabled(@NonNull ComponentName admin) { throwIfParentInstance("getAutoTimeZone"); if (mService != null) { @@ -11875,17 +11902,21 @@ public class DevicePolicyManager { } /** - * Called by a device owner or a profile owner of an organization-owned managed - * profile to set the system wall clock time. This only takes effect if called when - * {@link android.provider.Settings.Global#AUTO_TIME} is 0, otherwise {@code false} - * will be returned. + * Called by a device owner, a profile owner of an organization-owned managed + * profile or, starting from Android {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, + * holders of the permission {@link android.Manifest.permission#SET_TIME} to set the system wall + * clock time. This only takes effect if called when + * {@link android.provider.Settings.Global#AUTO_TIME} is 0, otherwise {@code false} will be + * returned. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with * @param millis time in milliseconds since the Epoch * @return {@code true} if set time succeeded, {@code false} otherwise. * @throws SecurityException if {@code admin} is not a device owner or a profile owner - * of an organization-owned managed profile. + * of an organization-owned managed profile or a holder of the permission + * {@link android.Manifest.permission#SET_TIME}. */ + @RequiresPermission(value = SET_TIME, conditional = true) public boolean setTime(@NonNull ComponentName admin, long millis) { throwIfParentInstance("setTime"); if (mService != null) { @@ -11899,10 +11930,12 @@ public class DevicePolicyManager { } /** - * Called by a device owner or a profile owner of an organization-owned managed - * profile to set the system's persistent default time zone. This only takes - * effect if called when {@link android.provider.Settings.Global#AUTO_TIME_ZONE} - * is 0, otherwise {@code false} will be returned. + * Called by a device owner, a profile owner of an organization-owned managed + * profile or, starting from Android {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, + * holders of the permission {@link android.Manifest.permission#SET_TIME_ZONE} to set the + * system's persistent default time zone. This only take effect if called when + * {@link android.provider.Settings.Global#AUTO_TIME_ZONE} is 0, otherwise {@code false} will be + * returned. * * @see android.app.AlarmManager#setTimeZone(String) * @param admin Which {@link DeviceAdminReceiver} this request is associated with @@ -11910,8 +11943,10 @@ public class DevicePolicyManager { * {@link java.util.TimeZone#getAvailableIDs} * @return {@code true} if set timezone succeeded, {@code false} otherwise. * @throws SecurityException if {@code admin} is not a device owner or a profile owner - * of an organization-owned managed profile. + * of an organization-owned managed profile or a holder of the permissions + * {@link android.Manifest.permission#SET_TIME_ZONE}. */ + @RequiresPermission(value = SET_TIME_ZONE, conditional = true) public boolean setTimeZone(@NonNull ComponentName admin, String timeZone) { throwIfParentInstance("setTimeZone"); if (mService != null) { 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/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index a7c48f3a4e8f..5f1a444d6cf6 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3121,6 +3121,34 @@ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_APP_EXEMPTIONS" android:protectionLevel="signature|role" /> + <!-- Allows an application to manage date and time device policy. --> + <permission android:name="android.permission.MANAGE_DEVICE_POLICY_TIME" + android:protectionLevel="internal|role" /> + + <!-- Allows an application to set device policies outside the current user + that are critical for securing data within the current user. + <p>Holding this permission allows the use of other held MANAGE_DEVICE_POLICY_* + permissions across all users on the device provided they are required for securing data + within the current user.--> + <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL" + android:protectionLevel="internal|role" /> + + <!-- Allows an application to set device policies outside the current user + that are required for securing device ownership without accessing user data. + <p>Holding this permission allows the use of other held MANAGE_DEVICE_POLICY_* + permissions across all users on the device provided they do not grant access to user + data. --> + <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS" + android:protectionLevel="internal|role" /> + + <!-- Allows an application to set device policies outside the current user. + <p>Fuller form of {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} + that removes the restriction on accessing user data. + <p>Holding this permission allows the use of any other held MANAGE_DEVICE_POLICY_* + permissions across all users on the device.--> + <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL" + android:protectionLevel="internal|role" /> + <!-- @SystemApi @hide Allows an application to set a device owner on retail demo devices.--> <permission android:name="android.permission.PROVISION_DEMO_DEVICE" android:protectionLevel="signature|setup" /> diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 8c2065e7f764..7e93522c5d65 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -18,7 +18,13 @@ 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.Manifest.permission.SET_TIME; +import static android.Manifest.permission.SET_TIME_ZONE; import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK; import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; import static android.app.AppOpsManager.MODE_ALLOWED; @@ -137,6 +143,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; @@ -715,7 +722,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { + "management app's authentication policy"; private static final String NOT_SYSTEM_CALLER_MSG = "Only the system can %s"; + private static final String PERMISSION_BASED_ACCESS_EXPERIMENT_FLAG = + "enable_permission_based_access"; private static final String ENABLE_COEXISTENCE_FLAG = "enable_coexistence"; + private static final boolean DEFAULT_VALUE_PERMISSION_BASED_ACCESS_FLAG = false; private static final boolean DEFAULT_ENABLE_COEXISTENCE_FLAG = false; // TODO(b/258425381) remove the flag after rollout. @@ -8033,9 +8043,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller) - || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner(caller)); + if (isPermissionCheckFlagEnabled()) { + // The effect of this policy is device-wide. + enforcePermission(SET_TIME, UserHandle.USER_ALL); + } else { + Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller) + || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner( + caller)); + } mInjector.binderWithCleanCallingIdentity(() -> mInjector.settingsGlobalPutInt(Settings.Global.AUTO_TIME, enabled ? 1 : 0)); @@ -8057,8 +8073,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller) - || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner(caller)); + + if (isPermissionCheckFlagEnabled()) { + enforceCanQuery(SET_TIME, UserHandle.USER_ALL); + } else { + Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller) + || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner( + caller)); + } return mInjector.settingsGlobalGetInt(Global.AUTO_TIME, 0) > 0; } @@ -8074,8 +8096,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller) - || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner(caller)); + + if (isPermissionCheckFlagEnabled()) { + // The effect of this policy is device-wide. + enforcePermission(SET_TIME_ZONE, UserHandle.USER_ALL); + } else { + Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller) + || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner( + caller)); + } if (isCoexistenceEnabled(caller)) { mDevicePolicyEngine.setGlobalPolicy( @@ -8107,8 +8136,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller) - || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner(caller)); + + if (isPermissionCheckFlagEnabled()) { + // The effect of this policy is device-wide. + enforceCanQuery(SET_TIME_ZONE, UserHandle.USER_ALL); + } else { + Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller) + || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner( + caller)); + } return mInjector.settingsGlobalGetInt(Global.AUTO_TIME_ZONE, 0) > 0; } @@ -13031,8 +13067,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization( - isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)); + if (isPermissionCheckFlagEnabled()) { + // This is a global action. + enforcePermission(SET_TIME, UserHandle.USER_ALL); + } else { + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) + || isProfileOwnerOfOrganizationOwnedDevice(caller)); + } // Don't allow set time when auto time is on. if (mInjector.settingsGlobalGetInt(Global.AUTO_TIME, 0) == 1) { @@ -13051,8 +13093,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization( - isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)); + if (isPermissionCheckFlagEnabled()) { + // This is a global action. + enforcePermission(SET_TIME_ZONE, UserHandle.USER_ALL); + } else { + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) + || isProfileOwnerOfOrganizationOwnedDevice(caller)); + } // Don't allow set timezone when auto timezone is on. if (mInjector.settingsGlobalGetInt(Global.AUTO_TIME_ZONE, 0) == 1) { @@ -13657,6 +13705,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(); @@ -18408,14 +18466,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() { @@ -19513,6 +19582,192 @@ 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, + SET_TIME, + SET_TIME_ZONE); + 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, + SET_TIME, + SET_TIME_ZONE); + private static final List<String> PROFILE_OWNER_ON_USER_0_PERMISSIONS = List.of( + SET_TIME, + SET_TIME_ZONE); + 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<>(); + { + // Auto time is intrinsically global so there is no cross-user permission. + CROSS_USER_PERMISSIONS.put(SET_TIME, null); + CROSS_USER_PERMISSIONS.put(SET_TIME_ZONE, null); + } + + /** + * 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 hasPermissionOnOwnUser = hasPermission(permission); + boolean hasPermissionOnTargetUser = true; + if (hasPermissionOnOwnUser & getCallerIdentity().getUserId() != targetUserId) { + hasPermissionOnTargetUser = hasPermission(CROSS_USER_PERMISSIONS.get(permission)); + } + return hasPermissionOnOwnUser && hasPermissionOnTargetUser; + } + + /** + * 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; + } + + private boolean isPermissionCheckFlagEnabled() { + return DeviceConfig.getBoolean( + NAMESPACE_DEVICE_POLICY_MANAGER, + PERMISSION_BASED_ACCESS_EXPERIMENT_FLAG, + DEFAULT_VALUE_PERMISSION_BASED_ACCESS_FLAG); + } + // 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; |