Loading services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +91 −78 Original line number Diff line number Diff line Loading @@ -1620,6 +1620,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mContext.getSystemService(PowerManager.class).reboot(reason); } void recoverySystemRebootWipeUserData(boolean shutdown, String reason, boolean force) throws IOException { RecoverySystem.rebootWipeUserData(mContext, shutdown, reason, force); } boolean systemPropertiesGetBoolean(String key, boolean def) { return SystemProperties.getBoolean(key, def); } Loading Loading @@ -5138,7 +5143,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } private void wipeDataNoLock(boolean wipeExtRequested, String reason, boolean force) { private void forceWipeDeviceNoLock(boolean wipeExtRequested, String reason) { wtfIfInLock(); if (wipeExtRequested) { Loading @@ -5147,46 +5152,69 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { sm.wipeAdoptableDisks(); } try { RecoverySystem.rebootWipeUserData(mContext, false /* shutdown */, reason, force); mInjector.recoverySystemRebootWipeUserData( /*shutdown=*/ false, reason, /*force=*/ true); } catch (IOException | SecurityException e) { Slog.w(LOG_TAG, "Failed requesting data wipe", e); } } private void forceWipeUser(int userId) { try { IActivityManager am = mInjector.getIActivityManager(); if (am.getCurrentUser().id == userId) { am.switchUser(UserHandle.USER_SYSTEM); } boolean userRemoved = mUserManagerInternal.removeUserEvenWhenDisallowed(userId); if (!userRemoved) { Slog.w(LOG_TAG, "Couldn't remove user " + userId); } else if (isManagedProfile(userId)) { sendWipeProfileNotification(); } } catch (RemoteException re) { // Shouldn't happen } } @Override public void wipeData(int flags) { if (!mHasFeature) { return; } final int userHandle = mInjector.userHandleGetCallingUserId(); enforceFullCrossUsersPermission(userHandle); enforceFullCrossUsersPermission(mInjector.userHandleGetCallingUserId()); final String source; final ActiveAdmin admin; synchronized (this) { // This API can only be called by an active device admin, // so try to retrieve it to check that the caller is one. final ActiveAdmin admin = getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_WIPE_DATA); source = admin.info.getComponent().flattenToShortString(); admin = getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_WIPE_DATA); } String reason = "DevicePolicyManager.wipeData() from " + admin.info.getComponent().flattenToShortString(); wipeDataNoLock( admin.info.getComponent(), flags, reason, admin.getUserHandle().getIdentifier()); } private void wipeDataNoLock(ComponentName admin, int flags, String reason, int userId) { wtfIfInLock(); long ident = mInjector.binderClearCallingIdentity(); try { // First check whether the admin is allowed to wipe the device/user/profile. final String restriction; if (userHandle == UserHandle.USER_SYSTEM) { if (userId == UserHandle.USER_SYSTEM) { restriction = UserManager.DISALLOW_FACTORY_RESET; } else if (isManagedProfile(userHandle)) { } else if (isManagedProfile(userId)) { restriction = UserManager.DISALLOW_REMOVE_MANAGED_PROFILE; } else { restriction = UserManager.DISALLOW_REMOVE_USER; } if (isAdminAffectedByRestriction( admin.info.getComponent(), restriction, userHandle)) { if (isAdminAffectedByRestriction(admin, restriction, userId)) { throw new SecurityException("Cannot wipe data. " + restriction + " restriction is set for user " + userHandle); + " restriction is set for user " + userId); } if ((flags & WIPE_RESET_PROTECTION_DATA) != 0) { if (!isDeviceOwner(admin.info.getComponent(), userHandle)) { if (!isDeviceOwner(admin, userId)) { throw new SecurityException( "Only device owner admins can set WIPE_RESET_PROTECTION_DATA"); } Loading @@ -5197,44 +5225,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } } finally { mInjector.binderRestoreCallingIdentity(ident); } } final boolean wipeExtRequested = (flags & WIPE_EXTERNAL_STORAGE) != 0; wipeDeviceNoLock(wipeExtRequested, userHandle, "DevicePolicyManager.wipeData() from " + source, /*force=*/ true); } private void wipeDeviceNoLock( boolean wipeExtRequested, final int userHandle, String reason, boolean force) { wtfIfInLock(); long ident = mInjector.binderClearCallingIdentity(); try { // TODO If split user is enabled and the device owner is set in the primary user (rather // than system), we should probably trigger factory reset. Current code just remove // that user (but still clears FRP...) if (userHandle == UserHandle.USER_SYSTEM) { wipeDataNoLock(wipeExtRequested, reason, force); // TODO If split user is enabled and the device owner is set in the primary user // (rather than system), we should probably trigger factory reset. Current code just // removes that user (but still clears FRP...) if (userId == UserHandle.USER_SYSTEM) { forceWipeDeviceNoLock(/*wipeExtRequested=*/ (flags & WIPE_EXTERNAL_STORAGE) != 0, reason); } else { try { IActivityManager am = mInjector.getIActivityManager(); if (am.getCurrentUser().id == userHandle) { am.switchUser(UserHandle.USER_SYSTEM); } boolean userRemoved = force ? mUserManagerInternal.removeUserEvenWhenDisallowed(userHandle) : mUserManager.removeUser(userHandle); if (!userRemoved) { Slog.w(LOG_TAG, "Couldn't remove user " + userHandle); } else if (isManagedProfile(userHandle)) { sendWipeProfileNotification(); } } catch (RemoteException re) { // Shouldn't happen } forceWipeUser(userId); } } finally { mInjector.binderRestoreCallingIdentity(ident); Loading Loading @@ -5374,25 +5372,21 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.BIND_DEVICE_ADMIN, null); boolean wipeData = false; ActiveAdmin strictestAdmin = null; final long ident = mInjector.binderClearCallingIdentity(); try { boolean wipeData = false; int identifier = 0; synchronized (this) { DevicePolicyData policy = getUserData(userHandle); policy.mFailedPasswordAttempts++; saveSettingsLocked(userHandle); if (mHasFeature) { ActiveAdmin strictestAdmin = getAdminWithMinimumFailedPasswordsForWipeLocked( strictestAdmin = getAdminWithMinimumFailedPasswordsForWipeLocked( userHandle, /* parent */ false); int max = strictestAdmin != null ? strictestAdmin.maximumFailedPasswordsForWipe : 0; if (max > 0 && policy.mFailedPasswordAttempts >= max) { // Wipe the user/profile associated with the policy that was violated. This // is not necessarily calling user: if the policy that fired was from a // managed profile rather than the main user profile, we wipe former only. wipeData = true; identifier = strictestAdmin.getUserHandle().getIdentifier(); } sendAdminCommandForLockscreenPoliciesLocked( Loading @@ -5400,14 +5394,33 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, userHandle); } } if (wipeData) { // Call without holding lock. wipeDeviceNoLock(false, identifier, "reportFailedPasswordAttempt()", false); } } finally { mInjector.binderRestoreCallingIdentity(ident); } if (wipeData && strictestAdmin != null) { final int userId = strictestAdmin.getUserHandle().getIdentifier(); Slog.i(LOG_TAG, "Max failed password attempts policy reached for admin: " + strictestAdmin.info.getComponent().flattenToShortString() + ". Calling wipeData for user " + userId); // Attempt to wipe the device/user/profile associated with the admin, as if the // admin had called wipeData(). That way we can check whether the admin is actually // allowed to wipe the device (e.g. a regular device admin shouldn't be able to wipe the // device if the device owner has set DISALLOW_FACTORY_RESET, but the DO should be // able to do so). // IMPORTANT: Call without holding the lock to prevent deadlock. try { wipeDataNoLock(strictestAdmin.info.getComponent(), /*flags=*/ 0, /*reason=*/ "reportFailedPasswordAttempt()", userId); } catch (SecurityException e) { Slog.w(LOG_TAG, "Failed to wipe user " + userId + " after max failed password attempts reached.", e); } } if (mInjector.securityLogIsLoggingEnabled()) { SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, /*result*/ 0, /*method strength*/ 1); Loading services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java +7 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ import android.view.IWindowManager; import com.android.internal.widget.LockPatternUtils; import java.io.File; import java.io.IOException; import java.util.Map; /** Loading Loading @@ -263,6 +264,12 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi context.powerManager.reboot(reason); } @Override void recoverySystemRebootWipeUserData(boolean shutdown, String reason, boolean force) throws IOException { context.recoverySystem.rebootWipeUserData(shutdown, reason, force); } @Override boolean systemPropertiesGetBoolean(String key, boolean def) { return context.systemProperties.getBoolean(key, def); Loading services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +135 −0 Original line number Diff line number Diff line Loading @@ -85,6 +85,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; /** Loading Loading @@ -3344,6 +3345,140 @@ public class DevicePolicyManagerTest extends DpmTestBase { } } public void testWipeDataDeviceOwner() throws Exception { setDeviceOwner(); when(mContext.userManager.getUserRestrictionSource( UserManager.DISALLOW_FACTORY_RESET, UserHandle.SYSTEM)) .thenReturn(UserManager.RESTRICTION_SOURCE_DEVICE_OWNER); dpm.wipeData(0); verify(mContext.recoverySystem).rebootWipeUserData( /*shutdown=*/ eq(false), anyString(), /*force=*/ eq(true)); } public void testWipeDataDeviceOwnerDisallowed() throws Exception { setDeviceOwner(); when(mContext.userManager.getUserRestrictionSource( UserManager.DISALLOW_FACTORY_RESET, UserHandle.SYSTEM)) .thenReturn(UserManager.RESTRICTION_SOURCE_SYSTEM); try { // The DO is not allowed to wipe the device if the user restriction was set // by the system dpm.wipeData(0); fail("SecurityException not thrown"); } catch (SecurityException expected) { } } public void testMaximumFailedPasswordAttemptsReachedManagedProfile() throws Exception { final int MANAGED_PROFILE_USER_ID = 15; final int MANAGED_PROFILE_ADMIN_UID = UserHandle.getUid(MANAGED_PROFILE_USER_ID, 19436); addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1); // Even if the caller is the managed profile, the current user is the user 0 when(mContext.iactivityManager.getCurrentUser()) .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0)); when(mContext.userManager.getUserRestrictionSource( UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, UserHandle.of(MANAGED_PROFILE_USER_ID))) .thenReturn(UserManager.RESTRICTION_SOURCE_PROFILE_OWNER); mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID; dpm.setMaximumFailedPasswordsForWipe(admin1, 3); mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; mContext.callerPermissions.add(permission.BIND_DEVICE_ADMIN); // Failed password attempts on the parent user are taken into account, as there isn't a // separate work challenge. dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM); dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM); dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM); // The profile should be wiped even if DISALLOW_REMOVE_MANAGED_PROFILE is enabled, because // both the user restriction and the policy were set by the PO. verify(mContext.userManagerInternal).removeUserEvenWhenDisallowed( MANAGED_PROFILE_USER_ID); verifyZeroInteractions(mContext.recoverySystem); } public void testMaximumFailedPasswordAttemptsReachedManagedProfileDisallowed() throws Exception { final int MANAGED_PROFILE_USER_ID = 15; final int MANAGED_PROFILE_ADMIN_UID = UserHandle.getUid(MANAGED_PROFILE_USER_ID, 19436); addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1); // Even if the caller is the managed profile, the current user is the user 0 when(mContext.iactivityManager.getCurrentUser()) .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0)); when(mContext.userManager.getUserRestrictionSource( UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, UserHandle.of(MANAGED_PROFILE_USER_ID))) .thenReturn(UserManager.RESTRICTION_SOURCE_SYSTEM); mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID; dpm.setMaximumFailedPasswordsForWipe(admin1, 3); mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; mContext.callerPermissions.add(permission.BIND_DEVICE_ADMIN); // Failed password attempts on the parent user are taken into account, as there isn't a // separate work challenge. dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM); dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM); dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM); // DISALLOW_REMOVE_MANAGED_PROFILE was set by the system, not the PO, so the profile is // not wiped. verify(mContext.userManagerInternal, never()) .removeUserEvenWhenDisallowed(anyInt()); verifyZeroInteractions(mContext.recoverySystem); } public void testMaximumFailedPasswordAttemptsReachedDeviceOwner() throws Exception { setDeviceOwner(); when(mContext.userManager.getUserRestrictionSource( UserManager.DISALLOW_FACTORY_RESET, UserHandle.SYSTEM)) .thenReturn(UserManager.RESTRICTION_SOURCE_DEVICE_OWNER); dpm.setMaximumFailedPasswordsForWipe(admin1, 3); mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; mContext.callerPermissions.add(permission.BIND_DEVICE_ADMIN); dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM); dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM); dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM); // The device should be wiped even if DISALLOW_FACTORY_RESET is enabled, because both the // user restriction and the policy were set by the DO. verify(mContext.recoverySystem).rebootWipeUserData( /*shutdown=*/ eq(false), anyString(), /*force=*/ eq(true)); } public void testMaximumFailedPasswordAttemptsReachedDeviceOwnerDisallowed() throws Exception { setDeviceOwner(); when(mContext.userManager.getUserRestrictionSource( UserManager.DISALLOW_FACTORY_RESET, UserHandle.SYSTEM)) .thenReturn(UserManager.RESTRICTION_SOURCE_SYSTEM); dpm.setMaximumFailedPasswordsForWipe(admin1, 3); mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; mContext.callerPermissions.add(permission.BIND_DEVICE_ADMIN); dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM); dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM); dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM); // DISALLOW_FACTORY_RESET was set by the system, not the DO, so the device is not wiped. verifyZeroInteractions(mContext.recoverySystem); verify(mContext.userManagerInternal, never()) .removeUserEvenWhenDisallowed(anyInt()); } public void testGetPermissionGrantState() throws Exception { final String permission = "some.permission"; final String app1 = "com.example.app1"; Loading services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java +9 −0 Original line number Diff line number Diff line Loading @@ -55,6 +55,7 @@ import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; Loading Loading @@ -158,6 +159,12 @@ public class DpmMockContext extends MockContext { } } public static class RecoverySystemForMock { public void rebootWipeUserData( boolean shutdown, String reason, boolean force) throws IOException { } } public static class SystemPropertiesForMock { public boolean getBoolean(String key, boolean def) { return false; Loading Loading @@ -267,6 +274,7 @@ public class DpmMockContext extends MockContext { public final UserManagerForMock userManagerForMock; public final PowerManagerForMock powerManager; public final PowerManagerInternal powerManagerInternal; public final RecoverySystemForMock recoverySystem; public final NotificationManager notificationManager; public final IIpConnectivityMetrics iipConnectivityMetrics; public final IWindowManager iwindowManager; Loading Loading @@ -312,6 +320,7 @@ public class DpmMockContext extends MockContext { packageManagerInternal = mock(PackageManagerInternal.class); powerManager = mock(PowerManagerForMock.class); powerManagerInternal = mock(PowerManagerInternal.class); recoverySystem = mock(RecoverySystemForMock.class); notificationManager = mock(NotificationManager.class); iipConnectivityMetrics = mock(IIpConnectivityMetrics.class); iwindowManager = mock(IWindowManager.class); Loading Loading
services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +91 −78 Original line number Diff line number Diff line Loading @@ -1620,6 +1620,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mContext.getSystemService(PowerManager.class).reboot(reason); } void recoverySystemRebootWipeUserData(boolean shutdown, String reason, boolean force) throws IOException { RecoverySystem.rebootWipeUserData(mContext, shutdown, reason, force); } boolean systemPropertiesGetBoolean(String key, boolean def) { return SystemProperties.getBoolean(key, def); } Loading Loading @@ -5138,7 +5143,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } private void wipeDataNoLock(boolean wipeExtRequested, String reason, boolean force) { private void forceWipeDeviceNoLock(boolean wipeExtRequested, String reason) { wtfIfInLock(); if (wipeExtRequested) { Loading @@ -5147,46 +5152,69 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { sm.wipeAdoptableDisks(); } try { RecoverySystem.rebootWipeUserData(mContext, false /* shutdown */, reason, force); mInjector.recoverySystemRebootWipeUserData( /*shutdown=*/ false, reason, /*force=*/ true); } catch (IOException | SecurityException e) { Slog.w(LOG_TAG, "Failed requesting data wipe", e); } } private void forceWipeUser(int userId) { try { IActivityManager am = mInjector.getIActivityManager(); if (am.getCurrentUser().id == userId) { am.switchUser(UserHandle.USER_SYSTEM); } boolean userRemoved = mUserManagerInternal.removeUserEvenWhenDisallowed(userId); if (!userRemoved) { Slog.w(LOG_TAG, "Couldn't remove user " + userId); } else if (isManagedProfile(userId)) { sendWipeProfileNotification(); } } catch (RemoteException re) { // Shouldn't happen } } @Override public void wipeData(int flags) { if (!mHasFeature) { return; } final int userHandle = mInjector.userHandleGetCallingUserId(); enforceFullCrossUsersPermission(userHandle); enforceFullCrossUsersPermission(mInjector.userHandleGetCallingUserId()); final String source; final ActiveAdmin admin; synchronized (this) { // This API can only be called by an active device admin, // so try to retrieve it to check that the caller is one. final ActiveAdmin admin = getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_WIPE_DATA); source = admin.info.getComponent().flattenToShortString(); admin = getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_WIPE_DATA); } String reason = "DevicePolicyManager.wipeData() from " + admin.info.getComponent().flattenToShortString(); wipeDataNoLock( admin.info.getComponent(), flags, reason, admin.getUserHandle().getIdentifier()); } private void wipeDataNoLock(ComponentName admin, int flags, String reason, int userId) { wtfIfInLock(); long ident = mInjector.binderClearCallingIdentity(); try { // First check whether the admin is allowed to wipe the device/user/profile. final String restriction; if (userHandle == UserHandle.USER_SYSTEM) { if (userId == UserHandle.USER_SYSTEM) { restriction = UserManager.DISALLOW_FACTORY_RESET; } else if (isManagedProfile(userHandle)) { } else if (isManagedProfile(userId)) { restriction = UserManager.DISALLOW_REMOVE_MANAGED_PROFILE; } else { restriction = UserManager.DISALLOW_REMOVE_USER; } if (isAdminAffectedByRestriction( admin.info.getComponent(), restriction, userHandle)) { if (isAdminAffectedByRestriction(admin, restriction, userId)) { throw new SecurityException("Cannot wipe data. " + restriction + " restriction is set for user " + userHandle); + " restriction is set for user " + userId); } if ((flags & WIPE_RESET_PROTECTION_DATA) != 0) { if (!isDeviceOwner(admin.info.getComponent(), userHandle)) { if (!isDeviceOwner(admin, userId)) { throw new SecurityException( "Only device owner admins can set WIPE_RESET_PROTECTION_DATA"); } Loading @@ -5197,44 +5225,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } } finally { mInjector.binderRestoreCallingIdentity(ident); } } final boolean wipeExtRequested = (flags & WIPE_EXTERNAL_STORAGE) != 0; wipeDeviceNoLock(wipeExtRequested, userHandle, "DevicePolicyManager.wipeData() from " + source, /*force=*/ true); } private void wipeDeviceNoLock( boolean wipeExtRequested, final int userHandle, String reason, boolean force) { wtfIfInLock(); long ident = mInjector.binderClearCallingIdentity(); try { // TODO If split user is enabled and the device owner is set in the primary user (rather // than system), we should probably trigger factory reset. Current code just remove // that user (but still clears FRP...) if (userHandle == UserHandle.USER_SYSTEM) { wipeDataNoLock(wipeExtRequested, reason, force); // TODO If split user is enabled and the device owner is set in the primary user // (rather than system), we should probably trigger factory reset. Current code just // removes that user (but still clears FRP...) if (userId == UserHandle.USER_SYSTEM) { forceWipeDeviceNoLock(/*wipeExtRequested=*/ (flags & WIPE_EXTERNAL_STORAGE) != 0, reason); } else { try { IActivityManager am = mInjector.getIActivityManager(); if (am.getCurrentUser().id == userHandle) { am.switchUser(UserHandle.USER_SYSTEM); } boolean userRemoved = force ? mUserManagerInternal.removeUserEvenWhenDisallowed(userHandle) : mUserManager.removeUser(userHandle); if (!userRemoved) { Slog.w(LOG_TAG, "Couldn't remove user " + userHandle); } else if (isManagedProfile(userHandle)) { sendWipeProfileNotification(); } } catch (RemoteException re) { // Shouldn't happen } forceWipeUser(userId); } } finally { mInjector.binderRestoreCallingIdentity(ident); Loading Loading @@ -5374,25 +5372,21 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.BIND_DEVICE_ADMIN, null); boolean wipeData = false; ActiveAdmin strictestAdmin = null; final long ident = mInjector.binderClearCallingIdentity(); try { boolean wipeData = false; int identifier = 0; synchronized (this) { DevicePolicyData policy = getUserData(userHandle); policy.mFailedPasswordAttempts++; saveSettingsLocked(userHandle); if (mHasFeature) { ActiveAdmin strictestAdmin = getAdminWithMinimumFailedPasswordsForWipeLocked( strictestAdmin = getAdminWithMinimumFailedPasswordsForWipeLocked( userHandle, /* parent */ false); int max = strictestAdmin != null ? strictestAdmin.maximumFailedPasswordsForWipe : 0; if (max > 0 && policy.mFailedPasswordAttempts >= max) { // Wipe the user/profile associated with the policy that was violated. This // is not necessarily calling user: if the policy that fired was from a // managed profile rather than the main user profile, we wipe former only. wipeData = true; identifier = strictestAdmin.getUserHandle().getIdentifier(); } sendAdminCommandForLockscreenPoliciesLocked( Loading @@ -5400,14 +5394,33 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, userHandle); } } if (wipeData) { // Call without holding lock. wipeDeviceNoLock(false, identifier, "reportFailedPasswordAttempt()", false); } } finally { mInjector.binderRestoreCallingIdentity(ident); } if (wipeData && strictestAdmin != null) { final int userId = strictestAdmin.getUserHandle().getIdentifier(); Slog.i(LOG_TAG, "Max failed password attempts policy reached for admin: " + strictestAdmin.info.getComponent().flattenToShortString() + ". Calling wipeData for user " + userId); // Attempt to wipe the device/user/profile associated with the admin, as if the // admin had called wipeData(). That way we can check whether the admin is actually // allowed to wipe the device (e.g. a regular device admin shouldn't be able to wipe the // device if the device owner has set DISALLOW_FACTORY_RESET, but the DO should be // able to do so). // IMPORTANT: Call without holding the lock to prevent deadlock. try { wipeDataNoLock(strictestAdmin.info.getComponent(), /*flags=*/ 0, /*reason=*/ "reportFailedPasswordAttempt()", userId); } catch (SecurityException e) { Slog.w(LOG_TAG, "Failed to wipe user " + userId + " after max failed password attempts reached.", e); } } if (mInjector.securityLogIsLoggingEnabled()) { SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, /*result*/ 0, /*method strength*/ 1); Loading
services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java +7 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ import android.view.IWindowManager; import com.android.internal.widget.LockPatternUtils; import java.io.File; import java.io.IOException; import java.util.Map; /** Loading Loading @@ -263,6 +264,12 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi context.powerManager.reboot(reason); } @Override void recoverySystemRebootWipeUserData(boolean shutdown, String reason, boolean force) throws IOException { context.recoverySystem.rebootWipeUserData(shutdown, reason, force); } @Override boolean systemPropertiesGetBoolean(String key, boolean def) { return context.systemProperties.getBoolean(key, def); Loading
services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +135 −0 Original line number Diff line number Diff line Loading @@ -85,6 +85,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; /** Loading Loading @@ -3344,6 +3345,140 @@ public class DevicePolicyManagerTest extends DpmTestBase { } } public void testWipeDataDeviceOwner() throws Exception { setDeviceOwner(); when(mContext.userManager.getUserRestrictionSource( UserManager.DISALLOW_FACTORY_RESET, UserHandle.SYSTEM)) .thenReturn(UserManager.RESTRICTION_SOURCE_DEVICE_OWNER); dpm.wipeData(0); verify(mContext.recoverySystem).rebootWipeUserData( /*shutdown=*/ eq(false), anyString(), /*force=*/ eq(true)); } public void testWipeDataDeviceOwnerDisallowed() throws Exception { setDeviceOwner(); when(mContext.userManager.getUserRestrictionSource( UserManager.DISALLOW_FACTORY_RESET, UserHandle.SYSTEM)) .thenReturn(UserManager.RESTRICTION_SOURCE_SYSTEM); try { // The DO is not allowed to wipe the device if the user restriction was set // by the system dpm.wipeData(0); fail("SecurityException not thrown"); } catch (SecurityException expected) { } } public void testMaximumFailedPasswordAttemptsReachedManagedProfile() throws Exception { final int MANAGED_PROFILE_USER_ID = 15; final int MANAGED_PROFILE_ADMIN_UID = UserHandle.getUid(MANAGED_PROFILE_USER_ID, 19436); addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1); // Even if the caller is the managed profile, the current user is the user 0 when(mContext.iactivityManager.getCurrentUser()) .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0)); when(mContext.userManager.getUserRestrictionSource( UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, UserHandle.of(MANAGED_PROFILE_USER_ID))) .thenReturn(UserManager.RESTRICTION_SOURCE_PROFILE_OWNER); mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID; dpm.setMaximumFailedPasswordsForWipe(admin1, 3); mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; mContext.callerPermissions.add(permission.BIND_DEVICE_ADMIN); // Failed password attempts on the parent user are taken into account, as there isn't a // separate work challenge. dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM); dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM); dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM); // The profile should be wiped even if DISALLOW_REMOVE_MANAGED_PROFILE is enabled, because // both the user restriction and the policy were set by the PO. verify(mContext.userManagerInternal).removeUserEvenWhenDisallowed( MANAGED_PROFILE_USER_ID); verifyZeroInteractions(mContext.recoverySystem); } public void testMaximumFailedPasswordAttemptsReachedManagedProfileDisallowed() throws Exception { final int MANAGED_PROFILE_USER_ID = 15; final int MANAGED_PROFILE_ADMIN_UID = UserHandle.getUid(MANAGED_PROFILE_USER_ID, 19436); addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1); // Even if the caller is the managed profile, the current user is the user 0 when(mContext.iactivityManager.getCurrentUser()) .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0)); when(mContext.userManager.getUserRestrictionSource( UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, UserHandle.of(MANAGED_PROFILE_USER_ID))) .thenReturn(UserManager.RESTRICTION_SOURCE_SYSTEM); mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID; dpm.setMaximumFailedPasswordsForWipe(admin1, 3); mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; mContext.callerPermissions.add(permission.BIND_DEVICE_ADMIN); // Failed password attempts on the parent user are taken into account, as there isn't a // separate work challenge. dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM); dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM); dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM); // DISALLOW_REMOVE_MANAGED_PROFILE was set by the system, not the PO, so the profile is // not wiped. verify(mContext.userManagerInternal, never()) .removeUserEvenWhenDisallowed(anyInt()); verifyZeroInteractions(mContext.recoverySystem); } public void testMaximumFailedPasswordAttemptsReachedDeviceOwner() throws Exception { setDeviceOwner(); when(mContext.userManager.getUserRestrictionSource( UserManager.DISALLOW_FACTORY_RESET, UserHandle.SYSTEM)) .thenReturn(UserManager.RESTRICTION_SOURCE_DEVICE_OWNER); dpm.setMaximumFailedPasswordsForWipe(admin1, 3); mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; mContext.callerPermissions.add(permission.BIND_DEVICE_ADMIN); dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM); dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM); dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM); // The device should be wiped even if DISALLOW_FACTORY_RESET is enabled, because both the // user restriction and the policy were set by the DO. verify(mContext.recoverySystem).rebootWipeUserData( /*shutdown=*/ eq(false), anyString(), /*force=*/ eq(true)); } public void testMaximumFailedPasswordAttemptsReachedDeviceOwnerDisallowed() throws Exception { setDeviceOwner(); when(mContext.userManager.getUserRestrictionSource( UserManager.DISALLOW_FACTORY_RESET, UserHandle.SYSTEM)) .thenReturn(UserManager.RESTRICTION_SOURCE_SYSTEM); dpm.setMaximumFailedPasswordsForWipe(admin1, 3); mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; mContext.callerPermissions.add(permission.BIND_DEVICE_ADMIN); dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM); dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM); dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM); // DISALLOW_FACTORY_RESET was set by the system, not the DO, so the device is not wiped. verifyZeroInteractions(mContext.recoverySystem); verify(mContext.userManagerInternal, never()) .removeUserEvenWhenDisallowed(anyInt()); } public void testGetPermissionGrantState() throws Exception { final String permission = "some.permission"; final String app1 = "com.example.app1"; Loading
services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java +9 −0 Original line number Diff line number Diff line Loading @@ -55,6 +55,7 @@ import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; Loading Loading @@ -158,6 +159,12 @@ public class DpmMockContext extends MockContext { } } public static class RecoverySystemForMock { public void rebootWipeUserData( boolean shutdown, String reason, boolean force) throws IOException { } } public static class SystemPropertiesForMock { public boolean getBoolean(String key, boolean def) { return false; Loading Loading @@ -267,6 +274,7 @@ public class DpmMockContext extends MockContext { public final UserManagerForMock userManagerForMock; public final PowerManagerForMock powerManager; public final PowerManagerInternal powerManagerInternal; public final RecoverySystemForMock recoverySystem; public final NotificationManager notificationManager; public final IIpConnectivityMetrics iipConnectivityMetrics; public final IWindowManager iwindowManager; Loading Loading @@ -312,6 +320,7 @@ public class DpmMockContext extends MockContext { packageManagerInternal = mock(PackageManagerInternal.class); powerManager = mock(PowerManagerForMock.class); powerManagerInternal = mock(PowerManagerInternal.class); recoverySystem = mock(RecoverySystemForMock.class); notificationManager = mock(NotificationManager.class); iipConnectivityMetrics = mock(IIpConnectivityMetrics.class); iwindowManager = mock(IWindowManager.class); Loading