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