Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a182b9cb authored by Pavel Grafov's avatar Pavel Grafov
Browse files

Respect admin requirements when changing password via adb command

Test: manual, with TestDPC and adb
Test: atest com.android.server.locksettings.LockSettingsShellCommandTest
Bug: 70227258
Change-Id: Ic771e709c6acfcbe84ef0a856865a4dd7f51cd25
parent 22eafd17
Loading
Loading
Loading
Loading
+64 −23
Original line number Diff line number Diff line
@@ -16,14 +16,22 @@

package com.android.server.locksettings;

import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;

import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;

import android.app.ActivityManager;
import android.app.admin.PasswordMetrics;
import android.os.ShellCommand;

import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternUtils.RequestThrottledException;
import com.android.internal.widget.LockscreenCredential;
import com.android.internal.widget.PasswordValidationError;

import java.io.PrintWriter;
import java.util.List;

class LockSettingsShellCommand extends ShellCommand {

@@ -70,18 +78,19 @@ class LockSettingsShellCommand extends ShellCommand {
            if (!checkCredential()) {
                return -1;
            }
            boolean success = true;
            switch (cmd) {
                case COMMAND_SET_PATTERN:
                    runSetPattern();
                    success = runSetPattern();
                    break;
                case COMMAND_SET_PASSWORD:
                    runSetPassword();
                    success = runSetPassword();
                    break;
                case COMMAND_SET_PIN:
                    runSetPin();
                    success = runSetPin();
                    break;
                case COMMAND_CLEAR:
                    runClear();
                    success = runClear();
                    break;
                case COMMAND_SP:
                    runChangeSp();
@@ -102,7 +111,7 @@ class LockSettingsShellCommand extends ShellCommand {
                    getErrPrintWriter().println("Unknown command: " + cmd);
                    break;
            }
            return 0;
            return success ? 0 : -1;
        } catch (Exception e) {
            getErrPrintWriter().println("Error while executing command: " + cmd);
            e.printStackTrace(getErrPrintWriter());
@@ -201,34 +210,66 @@ class LockSettingsShellCommand extends ShellCommand {
        }
    }

    private void runSetPattern() {
        mLockPatternUtils.setLockCredential(
                LockscreenCredential.createPattern(LockPatternUtils.byteArrayToPattern(
                        mNew.getBytes())),
                getOldCredential(),
                mCurrentUserId);
    private boolean runSetPattern() {
        final LockscreenCredential pattern = LockscreenCredential.createPattern(
                LockPatternUtils.byteArrayToPattern(mNew.getBytes()));
        if (!isNewCredentialSufficient(pattern)) {
            return false;
        }
        mLockPatternUtils.setLockCredential(pattern, getOldCredential(), mCurrentUserId);
        getOutPrintWriter().println("Pattern set to '" + mNew + "'");
        return true;
    }

    private void runSetPassword() {
        mLockPatternUtils.setLockCredential(LockscreenCredential.createPassword(mNew),
                getOldCredential(),
                mCurrentUserId);
    private boolean runSetPassword() {
        final LockscreenCredential password = LockscreenCredential.createPassword(mNew);
        if (!isNewCredentialSufficient(password)) {
            return false;
        }
        mLockPatternUtils.setLockCredential(password, getOldCredential(), mCurrentUserId);
        getOutPrintWriter().println("Password set to '" + mNew + "'");
        return true;
    }

    private void runSetPin() {
        mLockPatternUtils.setLockCredential(LockscreenCredential.createPin(mNew),
                getOldCredential(),
                mCurrentUserId);
    private boolean runSetPin() {
        final LockscreenCredential pin = LockscreenCredential.createPin(mNew);
        if (!isNewCredentialSufficient(pin)) {
            return false;
        }
        mLockPatternUtils.setLockCredential(pin, getOldCredential(), mCurrentUserId);
        getOutPrintWriter().println("Pin set to '" + mNew + "'");
        return true;
    }

    private void runClear() {
        mLockPatternUtils.setLockCredential(LockscreenCredential.createNone(),
                getOldCredential(),
                mCurrentUserId);
    private boolean runClear() {
        LockscreenCredential none = LockscreenCredential.createNone();
        if (!isNewCredentialSufficient(none)) {
            return false;
        }
        mLockPatternUtils.setLockCredential(none, getOldCredential(), mCurrentUserId);
        getOutPrintWriter().println("Lock credential cleared");
        return true;
    }

    private boolean isNewCredentialSufficient(LockscreenCredential credential) {
        final PasswordMetrics requiredMetrics =
                mLockPatternUtils.getRequestedPasswordMetrics(mCurrentUserId);
        final List<PasswordValidationError> errors;
        if (credential.isPassword() || credential.isPin()) {
            errors = PasswordMetrics.validatePassword(requiredMetrics, PASSWORD_COMPLEXITY_NONE,
                    credential.isPin(), credential.getCredential());
        } else {
            PasswordMetrics metrics = new PasswordMetrics(
                    credential.isPattern() ? CREDENTIAL_TYPE_PATTERN : CREDENTIAL_TYPE_NONE);
            errors = PasswordMetrics.validatePasswordMetrics(
                    requiredMetrics, PASSWORD_COMPLEXITY_NONE, false /* isPin */, metrics);
        }
        if (!errors.isEmpty()) {
            getOutPrintWriter().println(
                    "New credential doesn't satisfy admin policies: " + errors.get(0));
            return false;
        }
        return true;
    }

    private void runSetDisabled() {
+88 −5
Original line number Diff line number Diff line
@@ -17,11 +17,13 @@
package com.android.server.locksettings;

import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;

import static junit.framework.Assert.assertEquals;

import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.never;
@@ -34,6 +36,8 @@ import static java.io.FileDescriptor.in;
import static java.io.FileDescriptor.out;

import android.app.ActivityManager;
import android.app.admin.PasswordMetrics;
import android.app.admin.PasswordPolicy;
import android.content.Context;
import android.os.Binder;
import android.os.Handler;
@@ -96,8 +100,7 @@ public class LockSettingsShellCommandTest {
        assertEquals(-1, mCommand.exec(mBinder, in, out, err,
                new String[] { "set-pin", "--old", "1234" },
                mShellCallback, mResultReceiver));
        verify(mLockPatternUtils, never()).setLockCredential(any(), any(),
                anyInt(), anyBoolean());
        verify(mLockPatternUtils, never()).setLockCredential(any(), any(), anyInt());
    }

    @Test
@@ -109,6 +112,8 @@ public class LockSettingsShellCommandTest {
                PASSWORD_QUALITY_NUMERIC);
        when(mLockPatternUtils.checkCredential(
                LockscreenCredential.createPin("1234"), mUserId, null)).thenReturn(true);
        when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
                .thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_NUMERIC));
        assertEquals(0, mCommand.exec(new Binder(), in, out, err,
                new String[] { "set-pin", "--old", "1234", "4321" },
                mShellCallback, mResultReceiver));
@@ -118,6 +123,23 @@ public class LockSettingsShellCommandTest {
                mUserId);
    }

    @Test
    public void testChangePin_nonCompliant() throws Exception {
        when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true);
        when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false);
        when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true);
        when(mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId)).thenReturn(
                PASSWORD_QUALITY_NUMERIC);
        when(mLockPatternUtils.checkCredential(
                LockscreenCredential.createPin("1234"), mUserId, null)).thenReturn(true);
        when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
                .thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_ALPHABETIC));
        assertEquals(-1, mCommand.exec(new Binder(), in, out, err,
                new String[] { "set-pin", "--old", "1234", "4321" },
                mShellCallback, mResultReceiver));
        verify(mLockPatternUtils, never()).setLockCredential(any(), any(), anyInt());
    }

    @Test
    public void testChangePin_noLockScreen() throws Exception {
        when(mLockPatternUtils.hasSecureLockScreen()).thenReturn(false);
@@ -137,15 +159,34 @@ public class LockSettingsShellCommandTest {
                PASSWORD_QUALITY_ALPHABETIC);
        when(mLockPatternUtils.checkCredential(
                LockscreenCredential.createPassword("1234"), mUserId, null)).thenReturn(true);
        when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
                .thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_ALPHABETIC));
        assertEquals(0,  mCommand.exec(new Binder(), in, out, err,
                new String[] { "set-password", "--old", "1234", "4321" },
                new String[] { "set-password", "--old", "1234", "abcd" },
                mShellCallback, mResultReceiver));
        verify(mLockPatternUtils).setLockCredential(
                LockscreenCredential.createPassword("4321"),
                LockscreenCredential.createPassword("abcd"),
                LockscreenCredential.createPassword("1234"),
                mUserId);
    }

    @Test
    public void testChangePassword_nonCompliant() throws Exception {
        when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true);
        when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false);
        when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true);
        when(mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId)).thenReturn(
                PASSWORD_QUALITY_ALPHABETIC);
        when(mLockPatternUtils.checkCredential(
                LockscreenCredential.createPassword("1234"), mUserId, null)).thenReturn(true);
        when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
                .thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_COMPLEX));
        assertEquals(-1,  mCommand.exec(new Binder(), in, out, err,
                new String[] { "set-password", "--old", "1234", "weakpassword" },
                mShellCallback, mResultReceiver));
        verify(mLockPatternUtils, never()).setLockCredential(any(), any(), anyInt());
    }

    @Test
    public void testChangePassword_noLockScreen() throws Exception {
        when(mLockPatternUtils.hasSecureLockScreen()).thenReturn(false);
@@ -164,6 +205,8 @@ public class LockSettingsShellCommandTest {
        when(mLockPatternUtils.checkCredential(
                LockscreenCredential.createPattern(stringToPattern("1234")),
                mUserId, null)).thenReturn(true);
        when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
                .thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_SOMETHING));
        assertEquals(0, mCommand.exec(new Binder(), in, out, err,
                new String[] { "set-pattern", "--old", "1234", "4321" },
                mShellCallback, mResultReceiver));
@@ -173,6 +216,22 @@ public class LockSettingsShellCommandTest {
                mUserId);
    }

    @Test
    public void testChangePattern_nonCompliant() throws Exception {
        when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true);
        when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(true);
        when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(false);
        when(mLockPatternUtils.checkCredential(
                LockscreenCredential.createPattern(stringToPattern("1234")),
                mUserId, null)).thenReturn(true);
        when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
                .thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_NUMERIC));
        assertEquals(-1, mCommand.exec(new Binder(), in, out, err,
                new String[] { "set-pattern", "--old", "1234", "4321" },
                mShellCallback, mResultReceiver));
        verify(mLockPatternUtils, never()).setLockCredential(any(), any(), anyInt());
    }

    @Test
    public void testChangePattern_noLockScreen() throws Exception {
        when(mLockPatternUtils.hasSecureLockScreen()).thenReturn(false);
@@ -191,6 +250,8 @@ public class LockSettingsShellCommandTest {
        when(mLockPatternUtils.checkCredential(
                LockscreenCredential.createPattern(stringToPattern("1234")),
                mUserId, null)).thenReturn(true);
        when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
                .thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_UNSPECIFIED));
        assertEquals(0, mCommand.exec(new Binder(), in, out, err,
                new String[] { "clear", "--old", "1234" },
                mShellCallback, mResultReceiver));
@@ -200,7 +261,29 @@ public class LockSettingsShellCommandTest {
                mUserId);
    }

    @Test
    public void testClear_nonCompliant() throws Exception {
        when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true);
        when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(true);
        when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(false);
        when(mLockPatternUtils.checkCredential(
                LockscreenCredential.createPattern(stringToPattern("1234")),
                mUserId, null)).thenReturn(true);
        when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
                .thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_SOMETHING));
        assertEquals(-1, mCommand.exec(new Binder(), in, out, err,
                new String[] { "clear", "--old", "1234" },
                mShellCallback, mResultReceiver));
        verify(mLockPatternUtils, never()).setLockCredential(any(), any(), anyInt());
    }

    private List<LockPatternView.Cell> stringToPattern(String str) {
        return LockPatternUtils.byteArrayToPattern(str.getBytes());
    }

    private PasswordMetrics metricsForAdminQuality(int quality) {
        PasswordPolicy policy = new PasswordPolicy();
        policy.quality = quality;
        return policy.getMinMetrics();
    }
}