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.locksettings;
18 
19 import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH;
20 import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW;
21 import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM;
22 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
23 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
24 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
25 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
26 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
27 
28 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
29 
30 import static junit.framework.Assert.assertEquals;
31 
32 import static org.mockito.ArgumentMatchers.anyInt;
33 import static org.mockito.Mockito.any;
34 import static org.mockito.Mockito.never;
35 import static org.mockito.Mockito.verify;
36 import static org.mockito.Mockito.verifyNoMoreInteractions;
37 import static org.mockito.Mockito.when;
38 
39 import static java.io.FileDescriptor.err;
40 import static java.io.FileDescriptor.in;
41 import static java.io.FileDescriptor.out;
42 
43 import android.app.ActivityManager;
44 import android.app.admin.PasswordMetrics;
45 import android.app.admin.PasswordPolicy;
46 import android.content.Context;
47 import android.os.Binder;
48 import android.os.Handler;
49 import android.os.Looper;
50 import android.os.Process;
51 import android.os.ResultReceiver;
52 import android.os.ShellCallback;
53 import android.os.UserHandle;
54 import android.platform.test.annotations.Presubmit;
55 
56 import androidx.test.InstrumentationRegistry;
57 import androidx.test.filters.SmallTest;
58 import androidx.test.runner.AndroidJUnit4;
59 
60 import com.android.internal.widget.LockPatternUtils;
61 import com.android.internal.widget.LockPatternView;
62 import com.android.internal.widget.LockscreenCredential;
63 
64 import org.junit.Before;
65 import org.junit.Test;
66 import org.junit.runner.RunWith;
67 import org.mockito.Mock;
68 import org.mockito.MockitoAnnotations;
69 
70 import java.util.List;
71 
72 /**
73  * Test class for {@link LockSettingsShellCommand}.
74  *
75  * runtest frameworks-services -c com.android.server.locksettings.LockSettingsShellCommandTest
76  */
77 @SmallTest
78 @Presubmit
79 @RunWith(AndroidJUnit4.class)
80 public class LockSettingsShellCommandTest {
81 
82     private LockSettingsShellCommand mCommand;
83 
84     private @Mock LockPatternUtils mLockPatternUtils;
85     private int mUserId;
86     private final Binder mBinder = new Binder();
87     private final ShellCallback mShellCallback = new ShellCallback();
88     private final ResultReceiver mResultReceiver = new ResultReceiver(
89             new Handler(Looper.getMainLooper()));
90 
91     @Before
setUp()92     public void setUp() throws Exception {
93         MockitoAnnotations.initMocks(this);
94         final Context context = InstrumentationRegistry.getTargetContext();
95         mUserId = ActivityManager.getCurrentUser();
96         mCommand = new LockSettingsShellCommand(mLockPatternUtils, context, 0,
97                 Process.SHELL_UID);
98         when(mLockPatternUtils.hasSecureLockScreen()).thenReturn(true);
99     }
100 
101     @Test
testWrongPassword()102     public void testWrongPassword() throws Exception {
103         when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true);
104         when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false);
105         when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true);
106         when(mLockPatternUtils.checkCredential(
107                 LockscreenCredential.createPassword("1234"), mUserId, null)).thenReturn(false);
108         assertEquals(-1, mCommand.exec(mBinder, in, out, err,
109                 new String[] { "set-pin", "--old", "1234" },
110                 mShellCallback, mResultReceiver));
111         verify(mLockPatternUtils, never()).setLockCredential(any(), any(), anyInt());
112     }
113 
114     @Test
testChangePin()115     public void testChangePin() throws Exception {
116         when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true);
117         when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false);
118         when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true);
119         when(mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId)).thenReturn(
120                 PASSWORD_QUALITY_NUMERIC);
121         when(mLockPatternUtils.checkCredential(
122                 LockscreenCredential.createPin("1234"), mUserId, null)).thenReturn(true);
123         when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
124                 .thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_NUMERIC));
125         assertEquals(0, mCommand.exec(new Binder(), in, out, err,
126                 new String[] { "set-pin", "--old", "1234", "4321" },
127                 mShellCallback, mResultReceiver));
128         verify(mLockPatternUtils).setLockCredential(
129                 LockscreenCredential.createPin("4321"),
130                 LockscreenCredential.createPin("1234"),
131                 mUserId);
132     }
133 
134     @Test
testChangePin_nonCompliant()135     public void testChangePin_nonCompliant() throws Exception {
136         when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true);
137         when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false);
138         when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true);
139         when(mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId)).thenReturn(
140                 PASSWORD_QUALITY_NUMERIC);
141         when(mLockPatternUtils.checkCredential(
142                 LockscreenCredential.createPin("1234"), mUserId, null)).thenReturn(true);
143         when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
144                 .thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_ALPHABETIC));
145         assertEquals(-1, mCommand.exec(new Binder(), in, out, err,
146                 new String[] { "set-pin", "--old", "1234", "4321" },
147                 mShellCallback, mResultReceiver));
148         verify(mLockPatternUtils, never()).setLockCredential(any(), any(), anyInt());
149     }
150 
151     @Test
testChangePin_noLockScreen()152     public void testChangePin_noLockScreen() throws Exception {
153         when(mLockPatternUtils.hasSecureLockScreen()).thenReturn(false);
154         assertEquals(-1, mCommand.exec(new Binder(), in, out, err,
155                 new String[] { "set-pin", "--old", "1234", "4321" },
156                 mShellCallback, mResultReceiver));
157         verify(mLockPatternUtils).hasSecureLockScreen();
158         verifyNoMoreInteractions(mLockPatternUtils);
159     }
160 
161     @Test
testChangePassword()162     public void testChangePassword() throws Exception {
163         when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true);
164         when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false);
165         when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true);
166         when(mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId)).thenReturn(
167                 PASSWORD_QUALITY_ALPHABETIC);
168         when(mLockPatternUtils.checkCredential(
169                 LockscreenCredential.createPassword("1234"), mUserId, null)).thenReturn(true);
170         when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
171                 .thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_ALPHABETIC));
172         assertEquals(0,  mCommand.exec(new Binder(), in, out, err,
173                 new String[] { "set-password", "--old", "1234", "abcd" },
174                 mShellCallback, mResultReceiver));
175         verify(mLockPatternUtils).setLockCredential(
176                 LockscreenCredential.createPassword("abcd"),
177                 LockscreenCredential.createPassword("1234"),
178                 mUserId);
179     }
180 
181     @Test
testChangePassword_nonCompliant()182     public void testChangePassword_nonCompliant() throws Exception {
183         when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true);
184         when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false);
185         when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true);
186         when(mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId)).thenReturn(
187                 PASSWORD_QUALITY_ALPHABETIC);
188         when(mLockPatternUtils.checkCredential(
189                 LockscreenCredential.createPassword("1234"), mUserId, null)).thenReturn(true);
190         when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
191                 .thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_COMPLEX));
192         assertEquals(-1,  mCommand.exec(new Binder(), in, out, err,
193                 new String[] { "set-password", "--old", "1234", "weakpassword" },
194                 mShellCallback, mResultReceiver));
195         verify(mLockPatternUtils, never()).setLockCredential(any(), any(), anyInt());
196     }
197 
198     @Test
testChangePassword_noLockScreen()199     public void testChangePassword_noLockScreen() throws Exception {
200         when(mLockPatternUtils.hasSecureLockScreen()).thenReturn(false);
201         assertEquals(-1,  mCommand.exec(new Binder(), in, out, err,
202                 new String[] { "set-password", "--old", "1234", "4321" },
203                 mShellCallback, mResultReceiver));
204         verify(mLockPatternUtils).hasSecureLockScreen();
205         verifyNoMoreInteractions(mLockPatternUtils);
206     }
207 
208     @Test
testChangePattern()209     public void testChangePattern() throws Exception {
210         when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true);
211         when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(true);
212         when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(false);
213         when(mLockPatternUtils.checkCredential(
214                 LockscreenCredential.createPattern(stringToPattern("1234")),
215                 mUserId, null)).thenReturn(true);
216         when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
217                 .thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_SOMETHING));
218         assertEquals(0, mCommand.exec(new Binder(), in, out, err,
219                 new String[] { "set-pattern", "--old", "1234", "4321" },
220                 mShellCallback, mResultReceiver));
221         verify(mLockPatternUtils).setLockCredential(
222                 LockscreenCredential.createPattern(stringToPattern("4321")),
223                 LockscreenCredential.createPattern(stringToPattern("1234")),
224                 mUserId);
225     }
226 
227     @Test
testChangePattern_nonCompliant()228     public void testChangePattern_nonCompliant() throws Exception {
229         when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true);
230         when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(true);
231         when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(false);
232         when(mLockPatternUtils.checkCredential(
233                 LockscreenCredential.createPattern(stringToPattern("1234")),
234                 mUserId, null)).thenReturn(true);
235         when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
236                 .thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_NUMERIC));
237         assertEquals(-1, mCommand.exec(new Binder(), in, out, err,
238                 new String[] { "set-pattern", "--old", "1234", "4321" },
239                 mShellCallback, mResultReceiver));
240         verify(mLockPatternUtils, never()).setLockCredential(any(), any(), anyInt());
241     }
242 
243     @Test
testChangePattern_noLockScreen()244     public void testChangePattern_noLockScreen() throws Exception {
245         when(mLockPatternUtils.hasSecureLockScreen()).thenReturn(false);
246         assertEquals(-1, mCommand.exec(new Binder(), in, out, err,
247                 new String[] { "set-pattern", "--old", "1234", "4321" },
248                 mShellCallback, mResultReceiver));
249         verify(mLockPatternUtils).hasSecureLockScreen();
250         verifyNoMoreInteractions(mLockPatternUtils);
251     }
252 
253     @Test
testClear()254     public void testClear() throws Exception {
255         when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true);
256         when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(true);
257         when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(false);
258         when(mLockPatternUtils.checkCredential(
259                 LockscreenCredential.createPattern(stringToPattern("1234")),
260                 mUserId, null)).thenReturn(true);
261         when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
262                 .thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_UNSPECIFIED));
263         assertEquals(0, mCommand.exec(new Binder(), in, out, err,
264                 new String[] { "clear", "--old", "1234" },
265                 mShellCallback, mResultReceiver));
266         verify(mLockPatternUtils).setLockCredential(
267                 LockscreenCredential.createNone(),
268                 LockscreenCredential.createPattern(stringToPattern("1234")),
269                 mUserId);
270     }
271 
272     @Test
testClear_nonCompliant()273     public void testClear_nonCompliant() throws Exception {
274         when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true);
275         when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(true);
276         when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(false);
277         when(mLockPatternUtils.checkCredential(
278                 LockscreenCredential.createPattern(stringToPattern("1234")),
279                 mUserId, null)).thenReturn(true);
280         when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
281                 .thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_SOMETHING));
282         assertEquals(-1, mCommand.exec(new Binder(), in, out, err,
283                 new String[] { "clear", "--old", "1234" },
284                 mShellCallback, mResultReceiver));
285         verify(mLockPatternUtils, never()).setLockCredential(any(), any(), anyInt());
286     }
287 
288     @Test
testSetPin_nonCompliantWithComplexity()289     public void testSetPin_nonCompliantWithComplexity() throws Exception {
290         when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true);
291         when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false);
292         when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true);
293         when(mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId)).thenReturn(
294                 PASSWORD_QUALITY_NUMERIC);
295         when(mLockPatternUtils.checkCredential(
296                 LockscreenCredential.createPin("1234"), mUserId, null)).thenReturn(true);
297         when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
298                 .thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_UNSPECIFIED));
299         when(mLockPatternUtils.getRequestedPasswordComplexity(mUserId))
300                 .thenReturn(PASSWORD_COMPLEXITY_MEDIUM);
301 
302         assertEquals(-1, mCommand.exec(new Binder(), in, out, err,
303                 new String[] { "set-pin", "--old", "1234", "4321" },
304                 mShellCallback, mResultReceiver));
305 
306         verify(mLockPatternUtils, never()).setLockCredential(any(), any(), anyInt());
307     }
308 
309     @Test
testSetPin_compliantWithComplexity()310     public void testSetPin_compliantWithComplexity() throws Exception {
311         when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true);
312         when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false);
313         when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true);
314         when(mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId)).thenReturn(
315                 PASSWORD_QUALITY_NUMERIC);
316         when(mLockPatternUtils.checkCredential(
317                 LockscreenCredential.createPin("1234"), mUserId, null)).thenReturn(true);
318         when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
319                 .thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_UNSPECIFIED));
320         when(mLockPatternUtils.getRequestedPasswordComplexity(mUserId))
321                 .thenReturn(PASSWORD_COMPLEXITY_MEDIUM);
322 
323         assertEquals(0, mCommand.exec(new Binder(), in, out, err,
324                 new String[] { "set-pin", "--old", "1234", "4231" },
325                 mShellCallback, mResultReceiver));
326 
327         verify(mLockPatternUtils).setLockCredential(
328                 LockscreenCredential.createPin("4231"),
329                 LockscreenCredential.createPin("1234"),
330                 mUserId);
331     }
332 
333     @Test
testSetPattern_nonCompliantWithComplexity()334     public void testSetPattern_nonCompliantWithComplexity() throws Exception {
335         when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true);
336         when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(true);
337         when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(false);
338         when(mLockPatternUtils.checkCredential(
339                 LockscreenCredential.createPattern(stringToPattern("1234")),
340                 mUserId, null)).thenReturn(true);
341         when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
342                 .thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_UNSPECIFIED));
343         when(mLockPatternUtils.getRequestedPasswordComplexity(mUserId))
344                 .thenReturn(PASSWORD_COMPLEXITY_HIGH);
345 
346         assertEquals(-1, mCommand.exec(new Binder(), in, out, err,
347                 new String[] { "set-pattern", "--old", "1234", "4321" },
348                 mShellCallback, mResultReceiver));
349 
350         verify(mLockPatternUtils, never()).setLockCredential(any(), any(), anyInt());
351     }
352 
353     @Test
testSetPattern_compliantWithComplexity()354     public void testSetPattern_compliantWithComplexity() throws Exception {
355         when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true);
356         when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(true);
357         when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(false);
358         when(mLockPatternUtils.checkCredential(
359                 LockscreenCredential.createPattern(stringToPattern("1234")),
360                 mUserId, null)).thenReturn(true);
361         when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
362                 .thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_UNSPECIFIED));
363         when(mLockPatternUtils.getRequestedPasswordComplexity(mUserId))
364                 .thenReturn(PASSWORD_COMPLEXITY_LOW);
365 
366         assertEquals(0, mCommand.exec(new Binder(), in, out, err,
367                 new String[] { "set-pattern", "--old", "1234", "4321" },
368                 mShellCallback, mResultReceiver));
369 
370         verify(mLockPatternUtils).setLockCredential(
371                 LockscreenCredential.createPattern(stringToPattern("4321")),
372                 LockscreenCredential.createPattern(stringToPattern("1234")),
373                 mUserId);
374     }
375 
376     @Test
testRequireStrongAuth_STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN()377     public void testRequireStrongAuth_STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN() throws Exception {
378         when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true);
379 
380         assertEquals(0, mCommand.exec(new Binder(), in, out, err,
381                 new String[] { "require-strong-auth", "STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN"},
382                 mShellCallback, mResultReceiver));
383 
384         verify(mLockPatternUtils).requireStrongAuth(
385                 STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN,
386                 UserHandle.USER_ALL);
387     }
388 
stringToPattern(String str)389     private List<LockPatternView.Cell> stringToPattern(String str) {
390         return LockPatternUtils.byteArrayToPattern(str.getBytes());
391     }
392 
metricsForAdminQuality(int quality)393     private PasswordMetrics metricsForAdminQuality(int quality) {
394         PasswordPolicy policy = new PasswordPolicy();
395         policy.quality = quality;
396         return policy.getMinMetrics();
397     }
398 }
399