1/* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17package com.android.server.locksettings; 18 19import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC; 20import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX; 21import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; 22import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; 23 24import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE; 25import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD; 26import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN; 27 28import android.os.RemoteException; 29import android.os.UserHandle; 30import android.service.gatekeeper.GateKeeperResponse; 31 32import com.android.internal.widget.LockPatternUtils; 33import com.android.internal.widget.VerifyCredentialResponse; 34import com.android.server.locksettings.LockSettingsStorage.CredentialHash; 35import com.android.server.locksettings.FakeGateKeeperService.VerifyHandle; 36 37/** 38 * runtest frameworks-services -c com.android.server.locksettings.LockSettingsServiceTests 39 */ 40public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { 41 42 @Override 43 protected void setUp() throws Exception { 44 super.setUp(); 45 } 46 47 @Override 48 protected void tearDown() throws Exception { 49 super.tearDown(); 50 } 51 52 public void testCreatePasswordPrimaryUser() throws RemoteException { 53 testCreateCredential(PRIMARY_USER_ID, "password", CREDENTIAL_TYPE_PASSWORD, 54 PASSWORD_QUALITY_ALPHABETIC); 55 } 56 57 public void testCreatePatternPrimaryUser() throws RemoteException { 58 testCreateCredential(PRIMARY_USER_ID, "123456789", CREDENTIAL_TYPE_PATTERN, 59 PASSWORD_QUALITY_SOMETHING); 60 } 61 62 public void testChangePasswordPrimaryUser() throws RemoteException { 63 testChangeCredentials(PRIMARY_USER_ID, "78963214", CREDENTIAL_TYPE_PATTERN, 64 "asdfghjk", CREDENTIAL_TYPE_PASSWORD, PASSWORD_QUALITY_ALPHABETIC); 65 } 66 67 public void testChangePatternPrimaryUser() throws RemoteException { 68 testChangeCredentials(PRIMARY_USER_ID, "!£$%^&*(())", CREDENTIAL_TYPE_PASSWORD, 69 "1596321", CREDENTIAL_TYPE_PATTERN, PASSWORD_QUALITY_SOMETHING); 70 } 71 72 public void testChangePasswordFailPrimaryUser() throws RemoteException { 73 final long sid = 1234; 74 final String FAILED_MESSAGE = "Failed to enroll password"; 75 initializeStorageWithCredential(PRIMARY_USER_ID, "password", CREDENTIAL_TYPE_PASSWORD, sid); 76 77 try { 78 mService.setLockCredential("newpwd", CREDENTIAL_TYPE_PASSWORD, "badpwd", 79 PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID); 80 fail("Did not fail when enrolling using incorrect credential"); 81 } catch (RemoteException expected) { 82 assertTrue(expected.getMessage().equals(FAILED_MESSAGE)); 83 } 84 assertVerifyCredentials(PRIMARY_USER_ID, "password", CREDENTIAL_TYPE_PASSWORD, sid); 85 } 86 87 public void testClearPasswordPrimaryUser() throws RemoteException { 88 final String PASSWORD = "password"; 89 initializeStorageWithCredential(PRIMARY_USER_ID, PASSWORD, CREDENTIAL_TYPE_PASSWORD, 1234); 90 mService.setLockCredential(null, CREDENTIAL_TYPE_NONE, PASSWORD, 91 PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID); 92 assertFalse(mService.havePassword(PRIMARY_USER_ID)); 93 assertFalse(mService.havePattern(PRIMARY_USER_ID)); 94 assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); 95 } 96 97 public void testManagedProfileUnifiedChallenge() throws RemoteException { 98 final String firstUnifiedPassword = "testManagedProfileUnifiedChallenge-pwd-1"; 99 final String secondUnifiedPassword = "testManagedProfileUnifiedChallenge-pwd-2"; 100 mService.setLockCredential(firstUnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 101 null, PASSWORD_QUALITY_COMPLEX, PRIMARY_USER_ID); 102 mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null); 103 final long primarySid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID); 104 final long profileSid = mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID); 105 final long turnedOffProfileSid = 106 mGateKeeperService.getSecureUserId(TURNED_OFF_PROFILE_USER_ID); 107 assertTrue(primarySid != 0); 108 assertTrue(profileSid != 0); 109 assertTrue(profileSid != primarySid); 110 assertTrue(turnedOffProfileSid != 0); 111 assertTrue(turnedOffProfileSid != primarySid); 112 assertTrue(turnedOffProfileSid != profileSid); 113 114 // clear auth token and wait for verify challenge from primary user to re-generate it. 115 mGateKeeperService.clearAuthToken(MANAGED_PROFILE_USER_ID); 116 mGateKeeperService.clearAuthToken(TURNED_OFF_PROFILE_USER_ID); 117 // verify credential 118 assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( 119 firstUnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) 120 .getResponseCode()); 121 122 // Verify that we have a new auth token for the profile 123 assertNotNull(mGateKeeperService.getAuthToken(MANAGED_PROFILE_USER_ID)); 124 assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID)); 125 126 // Verify that profile which aren't running (e.g. turn off work) don't get unlocked 127 assertNull(mGateKeeperService.getAuthToken(TURNED_OFF_PROFILE_USER_ID)); 128 129 /* Currently in LockSettingsService.setLockCredential, unlockUser() is called with the new 130 * credential as part of verifyCredential() before the new credential is committed in 131 * StorageManager. So we relax the check in our mock StorageManager to allow that. 132 */ 133 mStorageManager.setIgnoreBadUnlock(true); 134 // Change primary password and verify that profile SID remains 135 mService.setLockCredential(secondUnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 136 firstUnifiedPassword, PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID); 137 mStorageManager.setIgnoreBadUnlock(false); 138 assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID)); 139 assertNull(mGateKeeperService.getAuthToken(TURNED_OFF_PROFILE_USER_ID)); 140 141 // Clear unified challenge 142 mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, 143 secondUnifiedPassword, PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID); 144 assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); 145 assertEquals(0, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID)); 146 assertEquals(0, mGateKeeperService.getSecureUserId(TURNED_OFF_PROFILE_USER_ID)); 147 } 148 149 public void testManagedProfileSeparateChallenge() throws RemoteException { 150 final String primaryPassword = "testManagedProfileSeparateChallenge-primary"; 151 final String profilePassword = "testManagedProfileSeparateChallenge-profile"; 152 mService.setLockCredential(primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, 153 PASSWORD_QUALITY_COMPLEX, PRIMARY_USER_ID); 154 /* Currently in LockSettingsService.setLockCredential, unlockUser() is called with the new 155 * credential as part of verifyCredential() before the new credential is committed in 156 * StorageManager. So we relax the check in our mock StorageManager to allow that. 157 */ 158 mStorageManager.setIgnoreBadUnlock(true); 159 mService.setLockCredential(profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, 160 PASSWORD_QUALITY_COMPLEX, MANAGED_PROFILE_USER_ID); 161 mStorageManager.setIgnoreBadUnlock(false); 162 163 final long primarySid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID); 164 final long profileSid = mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID); 165 assertTrue(primarySid != 0); 166 assertTrue(profileSid != 0); 167 assertTrue(profileSid != primarySid); 168 169 // clear auth token and make sure verify challenge from primary user does not regenerate it. 170 mGateKeeperService.clearAuthToken(MANAGED_PROFILE_USER_ID); 171 // verify primary credential 172 assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( 173 primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) 174 .getResponseCode()); 175 assertNull(mGateKeeperService.getAuthToken(MANAGED_PROFILE_USER_ID)); 176 177 // verify profile credential 178 assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( 179 profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, 180 MANAGED_PROFILE_USER_ID).getResponseCode()); 181 assertNotNull(mGateKeeperService.getAuthToken(MANAGED_PROFILE_USER_ID)); 182 assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID)); 183 184 // Change primary credential and make sure we don't affect profile 185 mStorageManager.setIgnoreBadUnlock(true); 186 mService.setLockCredential("pwd", LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 187 primaryPassword, PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID); 188 mStorageManager.setIgnoreBadUnlock(false); 189 assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( 190 profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, 191 MANAGED_PROFILE_USER_ID).getResponseCode()); 192 assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID)); 193 } 194 195 private void testCreateCredential(int userId, String credential, int type, int quality) 196 throws RemoteException { 197 mService.setLockCredential(credential, type, null, quality, userId); 198 assertVerifyCredentials(userId, credential, type, -1); 199 } 200 201 private void testChangeCredentials(int userId, String newCredential, int newType, 202 String oldCredential, int oldType, int quality) throws RemoteException { 203 final long sid = 1234; 204 initializeStorageWithCredential(userId, oldCredential, oldType, sid); 205 mService.setLockCredential(newCredential, newType, oldCredential, quality, userId); 206 assertVerifyCredentials(userId, newCredential, newType, sid); 207 } 208 209 private void assertVerifyCredentials(int userId, String credential, int type, long sid) 210 throws RemoteException{ 211 final long challenge = 54321; 212 VerifyCredentialResponse response = mService.verifyCredential(credential, type, challenge, 213 userId); 214 215 assertEquals(GateKeeperResponse.RESPONSE_OK, response.getResponseCode()); 216 if (sid != -1) assertEquals(sid, mGateKeeperService.getSecureUserId(userId)); 217 final int incorrectType; 218 if (type == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD) { 219 assertTrue(mService.havePassword(userId)); 220 assertFalse(mService.havePattern(userId)); 221 incorrectType = LockPatternUtils.CREDENTIAL_TYPE_PATTERN; 222 } else if (type == LockPatternUtils.CREDENTIAL_TYPE_PATTERN){ 223 assertFalse(mService.havePassword(userId)); 224 assertTrue(mService.havePattern(userId)); 225 incorrectType = LockPatternUtils.CREDENTIAL_TYPE_PASSWORD; 226 } else { 227 assertFalse(mService.havePassword(userId)); 228 assertFalse(mService.havePassword(userId)); 229 incorrectType = LockPatternUtils.CREDENTIAL_TYPE_PASSWORD; 230 } 231 // check for bad type 232 assertEquals(GateKeeperResponse.RESPONSE_ERROR, mService.verifyCredential(credential, 233 incorrectType, challenge, userId).getResponseCode()); 234 // check for bad credential 235 assertEquals(GateKeeperResponse.RESPONSE_ERROR, mService.verifyCredential("0" + credential, 236 type, challenge, userId).getResponseCode()); 237 } 238 239 private void initializeStorageWithCredential(int userId, String credential, int type, long sid) 240 throws RemoteException { 241 byte[] oldHash = new VerifyHandle(credential.getBytes(), sid).toBytes(); 242 if (mService.shouldMigrateToSyntheticPasswordLocked(userId)) { 243 mService.initializeSyntheticPasswordLocked(oldHash, credential, type, 244 type == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD ? PASSWORD_QUALITY_ALPHABETIC 245 : PASSWORD_QUALITY_SOMETHING, userId); 246 } else { 247 if (type == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD) { 248 mStorage.writeCredentialHash(CredentialHash.create(oldHash, 249 LockPatternUtils.CREDENTIAL_TYPE_PASSWORD), userId); 250 } else { 251 mStorage.writeCredentialHash(CredentialHash.create(oldHash, 252 LockPatternUtils.CREDENTIAL_TYPE_PATTERN), userId); 253 } 254 } 255 } 256} 257