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