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

Commit 7374d3a4 authored by Adrian Roos's avatar Adrian Roos
Browse files

Credential FRP: Add implementation

- Adds a facility to store a credential handle that survives factory reset
- Adds a method to KeyguardManager for verifying the stored credential for SetupWizard
- Dark launches persisting the primary user's credential as the FRP credential (behind a default-off flag)

Future work:
- Use a separate GK handle / synthetic password for the FRP credential
- Enroll the FRP credential in verifyCredential for the upgrade case

Bug: 36814845
Test: runtest -x core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java && runtest -x services/tests/servicestests/src/com/android/server/LockSettingsStorageTests.java && runtest -x services/tests/servicestests/src/com/android/server/SyntheticPasswordTests.java
Change-Id: Ia739408c5ecb169e5f09670cd9ceaa7febc2b1cc
parent a037d31a
Loading
Loading
Loading
Loading
+84 −1
Original line number Diff line number Diff line
@@ -28,11 +28,12 @@ import android.content.pm.ResolveInfo;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.persistentdata.IPersistentDataBlockService;
import android.util.Log;
import android.view.IOnKeyguardExitResult;
import android.view.IWindowManager;
@@ -40,6 +41,7 @@ import android.view.WindowManager.LayoutParams;
import android.view.WindowManagerGlobal;

import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.widget.LockPatternUtils;

import java.util.List;

@@ -73,6 +75,13 @@ public class KeyguardManager {
    public static final String ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER =
            "android.app.action.CONFIRM_DEVICE_CREDENTIAL_WITH_USER";

    /**
     * Intent used to prompt user for factory reset credentials.
     * @hide
     */
    public static final String ACTION_CONFIRM_FRP_CREDENTIAL =
            "android.app.action.CONFIRM_FRP_CREDENTIAL";

    /**
     * A CharSequence dialog title to show to the user when used with a
     * {@link #ACTION_CONFIRM_DEVICE_CREDENTIAL}.
@@ -87,6 +96,23 @@ public class KeyguardManager {
     */
    public static final String EXTRA_DESCRIPTION = "android.app.extra.DESCRIPTION";

    /**
     * A CharSequence description to show to the user on the alternate button when used with
     * {@link #ACTION_CONFIRM_FRP_CREDENTIAL}.
     * @hide
     */
    public static final String EXTRA_ALTERNATE_BUTTON_LABEL =
            "android.app.extra.ALTERNATE_BUTTON_LABEL";

    /**
     * Result code returned by the activity started by
     * {@link #createConfirmFactoryResetCredentialIntent} indicating that the user clicked the
     * alternate button.
     *
     * @hide
     */
    public static final int RESULT_ALTERNATE = 1;

    /**
     * Get an intent to prompt the user to confirm credentials (pin, pattern or password)
     * for the current user of the device. The caller is expected to launch this activity using
@@ -130,6 +156,63 @@ public class KeyguardManager {
        return intent;
    }

    /**
     * Get an intent to prompt the user to confirm credentials (pin, pattern or password)
     * for the previous owner of the device. The caller is expected to launch this activity using
     * {@link android.app.Activity#startActivityForResult(Intent, int)} and check for
     * {@link android.app.Activity#RESULT_OK} if the user successfully completes the challenge.
     *
     * @param alternateButtonLabel if not empty, a button is provided with the given label. Upon
     *                             clicking this button, the activity returns
     *                             {@link #RESULT_ALTERNATE}
     *
     * @return  the intent for launching the activity or null if the credential of the previous
     * owner can not be verified (e.g. because there was none, or the device does not support
     * verifying credentials after a factory reset, or device setup has already been completed).
     *
     * @hide
     */
    public Intent createConfirmFactoryResetCredentialIntent(
            CharSequence title, CharSequence description, CharSequence alternateButtonLabel) {
        if (!LockPatternUtils.frpCredentialEnabled()) {
            Log.w(TAG, "Factory reset credentials not supported.");
            return null;
        }

        // Cannot verify credential if the device is provisioned
        if (Settings.Global.getInt(mContext.getContentResolver(),
                Settings.Global.DEVICE_PROVISIONED, 0) != 0) {
            Log.e(TAG, "Factory reset credential cannot be verified after provisioning.");
            return null;
        }

        // Make sure we have a credential
        try {
            IPersistentDataBlockService pdb = IPersistentDataBlockService.Stub.asInterface(
                    ServiceManager.getService(Context.PERSISTENT_DATA_BLOCK_SERVICE));
            if (pdb == null) {
                Log.e(TAG, "No persistent data block service");
                return null;
            }
            if (!pdb.hasFrpCredentialHandle()) {
                Log.i(TAG, "The persistent data block does not have a factory reset credential.");
                return null;
            }
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }

        Intent intent = new Intent(ACTION_CONFIRM_FRP_CREDENTIAL);
        intent.putExtra(EXTRA_TITLE, title);
        intent.putExtra(EXTRA_DESCRIPTION, description);
        intent.putExtra(EXTRA_ALTERNATE_BUTTON_LABEL, alternateButtonLabel);

        // explicitly set the package for security
        intent.setPackage(getSettingsPackageForIntent(intent));

        return intent;
    }

    private String getSettingsPackageForIntent(Intent intent) {
        List<ResolveInfo> resolveInfos = mContext.getPackageManager()
                .queryIntentActivities(intent, PackageManager.MATCH_SYSTEM_ONLY);
+6 −0
Original line number Diff line number Diff line
@@ -78,4 +78,10 @@ interface IGateKeeperService {
     * @param uid the Android user id.
     */
    void clearSecureUserId(int uid);

    /**
     * Notifies gatekeeper that device setup has been completed and any potentially still existing
     * state from before a factory reset can be cleaned up (if it has not been already).
     */
    void reportDeviceSetupComplete();
}
+1 −0
Original line number Diff line number Diff line
@@ -36,5 +36,6 @@ interface IPersistentDataBlockService {
    void setOemUnlockEnabled(boolean enabled);
    boolean getOemUnlockEnabled();
    int getFlashLockState();
    boolean hasFrpCredentialHandle();
}
+3 −2
Original line number Diff line number Diff line
@@ -28,7 +28,7 @@ interface ILockSettings {
    boolean getBoolean(in String key, in boolean defaultValue, in int userId);
    long getLong(in String key, in long defaultValue, in int userId);
    String getString(in String key, in String defaultValue, in int userId);
    void setLockCredential(in String credential, int type, in String savedCredential, int userId);
    void setLockCredential(in String credential, int type, in String savedCredential, int requestedQuality, int userId);
    void resetKeyStore(int userId);
    VerifyCredentialResponse checkCredential(in String credential, int type, int userId,
            in ICheckCredentialProgressCallback progressCallback);
@@ -49,6 +49,7 @@ interface ILockSettings {
    long addEscrowToken(in byte[] token, int userId);
    boolean removeEscrowToken(long handle, int userId);
    boolean isEscrowTokenActive(long handle, int userId);
    boolean setLockCredentialWithToken(String credential, int type, long tokenHandle, in byte[] token, int userId);
    boolean setLockCredentialWithToken(String credential, int type, long tokenHandle,
            in byte[] token, int requestedQuality, int userId);
    void unlockUserWithToken(long tokenHandle, in byte[] token, int userId);
}
+44 −10
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.UserInfo;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -34,6 +35,7 @@ import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.IStorageManager;
@@ -65,6 +67,8 @@ public class LockPatternUtils {

    private static final String TAG = "LockPatternUtils";
    private static final boolean DEBUG = false;
    private static final boolean FRP_CREDENTIAL_ENABLED =
            Build.IS_DEBUGGABLE && SystemProperties.getBoolean("debug.frpcredential.enable", false);

    /**
     * The key to identify when the lock pattern enabled flag is being accessed for legacy reasons.
@@ -112,6 +116,11 @@ public class LockPatternUtils {

    public static final int CREDENTIAL_TYPE_PASSWORD = 2;

    /**
     * Special user id for triggering the FRP verification flow.
     */
    public static final int USER_FRP = UserHandle.USER_NULL + 1;

    @Deprecated
    public final static String LOCKOUT_PERMANENT_KEY = "lockscreen.lockedoutpermanently";
    public final static String LOCKOUT_ATTEMPT_DEADLINE = "lockscreen.lockoutattemptdeadline";
@@ -295,24 +304,39 @@ public class LockPatternUtils {
    }

    public void reportFailedPasswordAttempt(int userId) {
        if (userId == USER_FRP && frpCredentialEnabled()) {
            return;
        }
        getDevicePolicyManager().reportFailedPasswordAttempt(userId);
        getTrustManager().reportUnlockAttempt(false /* authenticated */, userId);
    }

    public void reportSuccessfulPasswordAttempt(int userId) {
        if (userId == USER_FRP && frpCredentialEnabled()) {
            return;
        }
        getDevicePolicyManager().reportSuccessfulPasswordAttempt(userId);
        getTrustManager().reportUnlockAttempt(true /* authenticated */, userId);
    }

    public void reportPasswordLockout(int timeoutMs, int userId) {
        if (userId == USER_FRP && frpCredentialEnabled()) {
            return;
        }
        getTrustManager().reportUnlockLockout(timeoutMs, userId);
    }

    public int getCurrentFailedPasswordAttempts(int userId) {
        if (userId == USER_FRP && frpCredentialEnabled()) {
            return 0;
        }
        return getDevicePolicyManager().getCurrentFailedPasswordAttempts(userId);
    }

    public int getMaximumFailedPasswordsForWipe(int userId) {
        if (userId == USER_FRP && frpCredentialEnabled()) {
            return 0;
        }
        return getDevicePolicyManager().getMaximumFailedPasswordsForWipe(
                null /* componentName */, userId);
    }
@@ -586,7 +610,7 @@ public class LockPatternUtils {

        try{
            getLockSettings().setLockCredential(null, CREDENTIAL_TYPE_NONE, savedCredential,
                    userHandle);
                    DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userHandle);
        } catch (RemoteException e) {
            // well, we tried...
        }
@@ -651,7 +675,7 @@ public class LockPatternUtils {

            setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId);
            getLockSettings().setLockCredential(patternToString(pattern), CREDENTIAL_TYPE_PATTERN,
                    savedPattern, userId);
                    savedPattern, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId);

            // Update the device encryption password.
            if (userId == UserHandle.USER_SYSTEM
@@ -765,10 +789,10 @@ public class LockPatternUtils {
     * password.
     * @param password The password to save
     * @param savedPassword The previously saved lock password, or null if none
     * @param quality {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
     * @param requestedQuality {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
     * @param userHandle The userId of the user to change the password for
     */
    public void saveLockPassword(String password, String savedPassword, int quality,
    public void saveLockPassword(String password, String savedPassword, int requestedQuality,
            int userHandle) {
        try {
            if (password == null || password.length() < MIN_LOCK_PASSWORD_SIZE) {
@@ -777,9 +801,9 @@ public class LockPatternUtils {
            }

            final int computedQuality = PasswordMetrics.computeForPassword(password).quality;
            setLong(PASSWORD_TYPE_KEY, Math.max(quality, computedQuality), userHandle);
            setLong(PASSWORD_TYPE_KEY, Math.max(requestedQuality, computedQuality), userHandle);
            getLockSettings().setLockCredential(password, CREDENTIAL_TYPE_PASSWORD, savedPassword,
                    userHandle);
                    requestedQuality, userHandle);

            updateEncryptionPasswordIfNeeded(password, computedQuality, userHandle);
            updatePasswordHistory(password, userHandle);
@@ -1474,12 +1498,13 @@ public class LockPatternUtils {
                }

                final int computedQuality = PasswordMetrics.computeForPassword(credential).quality;
                int quality = Math.max(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC,
                        computedQuality);
                if (!getLockSettings().setLockCredentialWithToken(credential, type, tokenHandle,
                        token, userId)) {
                        token, quality, userId)) {
                    return false;
                }
                setLong(PASSWORD_TYPE_KEY, Math.max(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC,
                        computedQuality), userId);
                setLong(PASSWORD_TYPE_KEY, quality, userId);

                updateEncryptionPasswordIfNeeded(credential, computedQuality, userId);
                updatePasswordHistory(credential, userId);
@@ -1488,7 +1513,8 @@ public class LockPatternUtils {
                    throw new IllegalArgumentException("password must be emtpy for NONE type");
                }
                if (!getLockSettings().setLockCredentialWithToken(null, CREDENTIAL_TYPE_NONE,
                        tokenHandle, token, userId)) {
                        tokenHandle, token, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
                        userId)) {
                    return false;
                }
                setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
@@ -1691,4 +1717,12 @@ public class LockPatternUtils {
    public boolean isSyntheticPasswordEnabled() {
        return getLong(SYNTHETIC_PASSWORD_ENABLED_KEY, 0, UserHandle.USER_SYSTEM) != 0;
    }

    public static boolean userOwnsFrpCredential(UserInfo info) {
        return info != null && info.isPrimary() && info.isAdmin() && frpCredentialEnabled();
    }

    public static boolean frpCredentialEnabled() {
        return FRP_CREDENTIAL_ENABLED;
    }
}
Loading