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

Commit f68d67c4 authored by Eric Biggers's avatar Eric Biggers Committed by Android (Google) Code Review
Browse files

Merge "Add a software rate-limiter to LockSettingsService" into main

parents 873be4a6 ec3c325b
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -41,6 +41,13 @@ flag {
    bug: "325129836"
}

flag {
    name: "software_ratelimiter"
    namespace: "security"
    description: "Enable support for SoftwareRateLimiter in LockSettingsService"
    bug: "395976735"
}

flag {
    name: "frp_enforcement"
    is_exported: true
+10 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.util.Slog;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.time.Duration;

/**
 * Response object for a ILockSettings credential verification request.
@@ -113,6 +114,11 @@ public final class VerifyCredentialResponse implements Parcelable {
                0L /* gatekeeperPasswordHandle */);
    }

    /** Like {@link #fromTimeout(int)}, but takes a Duration instead of a raw milliseconds value. */
    public static VerifyCredentialResponse fromTimeout(Duration timeout) {
        return fromTimeout((int) Math.min(timeout.toMillis(), (long) Integer.MAX_VALUE));
    }

    /**
     * Since error (incorrect password) should never result in any of the other fields from
     * being populated, provide a default method to return a VerifyCredentialResponse.
@@ -167,6 +173,10 @@ public final class VerifyCredentialResponse implements Parcelable {
        return mTimeout;
    }

    public Duration getTimeoutAsDuration() {
        return Duration.ofMillis(mTimeout);
    }

    public @ResponseCode int getResponseCode() {
        return mResponseCode;
    }
+8 −0
Original line number Diff line number Diff line
@@ -1546,6 +1546,14 @@
         necessary, though it is still better than not using Weaver at all. -->
    <bool name="config_disableWeaverOnUnsecuredUsers">false</bool>

    <!-- If true, then the Android system server will rate-limit guesses of lock screen knowledge
         factors such as PINs. This does not replace the hardware (Gatekeeper or Weaver)
         rate-limiter, but rather applies concurrently to it. The software rate-limiter uses
         exponential backoff and allows up to 20 guesses in theory, but practically about 10-15
         before the delays get very long. The software rate-limiter also does not count duplicate
         wrong guesses, improving usability. -->
    <bool name="config_softwareLskfRateLimiterEnforcing">false</bool>

    <!-- Control the behavior when the user long presses the home button.
            0 - Nothing
            1 - Launch all apps intent
+1 −0
Original line number Diff line number Diff line
@@ -4149,6 +4149,7 @@

  <java-symbol type="bool" name="config_enableCredentialFactoryResetProtection" />
  <java-symbol type="bool" name="config_disableWeaverOnUnsecuredUsers" />
  <java-symbol type="bool" name="config_softwareLskfRateLimiterEnforcing" />

  <!-- ETWS primary messages -->
  <java-symbol type="string" name="etws_primary_default_message_earthquake" />
+102 −4
Original line number Diff line number Diff line
@@ -171,6 +171,7 @@ import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
@@ -277,6 +278,7 @@ public class LockSettingsService extends ILockSettings.Stub {
    private final SynchronizedStrongAuthTracker mStrongAuthTracker;
    private final BiometricDeferredQueue mBiometricDeferredQueue;
    private final LongSparseArray<byte[]> mGatekeeperPasswords;
    private final SoftwareRateLimiter mSoftwareRateLimiter;

    private final NotificationManager mNotificationManager;
    protected final UserManager mUserManager;
@@ -659,6 +661,34 @@ public class LockSettingsService extends ILockSettings.Stub {
        }
    }

    private class SoftwareRateLimiterInjector implements SoftwareRateLimiter.Injector {

        @Override
        public int readWrongGuessCounter(LskfIdentifier id) {
            return mSpManager.readWrongGuessCounter(id);
        }

        @Override
        public void writeWrongGuessCounter(LskfIdentifier id, int count) {
            mSpManager.writeWrongGuessCounter(id, count);
        }

        @Override
        public Duration getTimeSinceBoot() {
            return Duration.ofMillis(SystemClock.elapsedRealtime());
        }

        @Override
        public void removeCallbacksAndMessages(Object token) {
            Handler.getMain().removeCallbacksAndMessages(token);
        }

        @Override
        public void postDelayed(Runnable runnable, Object token, long delayMillis) {
            Handler.getMain().postDelayed(runnable, token, delayMillis);
        }
    }

    public LockSettingsService(Context context) {
        this(new Injector(context));
    }
@@ -674,6 +704,14 @@ public class LockSettingsService extends ILockSettings.Stub {
        mStrongAuth = injector.getStrongAuth();
        mActivityManager = injector.getActivityManager();

        boolean enforcing =
                mContext.getResources()
                        .getBoolean(
                                com.android.internal.R.bool
                                        .config_softwareLskfRateLimiterEnforcing);
        mSoftwareRateLimiter =
                new SoftwareRateLimiter(new SoftwareRateLimiterInjector(), enforcing);

        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_USER_STARTING);
        filter.addAction(Intent.ACTION_LOCALE_CHANGED);
@@ -2403,6 +2441,24 @@ public class LockSettingsService extends ILockSettings.Stub {
        VerifyCredentialResponse response;

        synchronized (mSpManager) {
            final long protectorId =
                    isSpecialUserId(userId)
                            ? SyntheticPasswordManager.NULL_PROTECTOR_ID
                            : getCurrentLskfBasedProtectorId(userId);
            final LskfIdentifier lskfId = new LskfIdentifier(userId, protectorId);
            if (android.security.Flags.softwareRatelimiter()) {
                SoftwareRateLimiterResult res = mSoftwareRateLimiter.apply(lskfId, credential);
                switch (res.code) {
                    case SoftwareRateLimiterResult.CONTINUE_TO_HARDWARE:
                        break;
                    case SoftwareRateLimiterResult.RATE_LIMITED:
                        return VerifyCredentialResponse.fromTimeout(res.remainingDelay);
                    case SoftwareRateLimiterResult.CREDENTIAL_TOO_SHORT:
                    case SoftwareRateLimiterResult.DUPLICATE_WRONG_GUESS:
                    default:
                        return VerifyCredentialResponse.fromError();
                }
            }
            if (isSpecialUserId(userId)) {
                response = mSpManager.verifySpecialUserCredential(userId, getGateKeeperService(),
                        credential, progressCallback);
@@ -2410,13 +2466,11 @@ public class LockSettingsService extends ILockSettings.Stub {
                        && userId == USER_FRP) {
                    mStorage.deactivateFactoryResetProtectionWithoutSecret();
                }
                return response;
                return reportResultToSoftwareRateLimiter(response, lskfId, credential);
            }

            long protectorId = getCurrentLskfBasedProtectorId(userId);
            authResult = mSpManager.unlockLskfBasedProtector(
                    getGateKeeperService(), protectorId, credential, userId, progressCallback);
            response = authResult.gkResponse;
            response = reportResultToSoftwareRateLimiter(authResult.gkResponse, lskfId, credential);

            if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
                if ((flags & VERIFY_FLAG_WRITE_REPAIR_MODE_PW) != 0) {
@@ -2452,6 +2506,35 @@ public class LockSettingsService extends ILockSettings.Stub {
        return response;
    }

    /**
     * Reports the result of the real credential check to the software rate-limiter, if enabled.
     * Returns either the same {@link VerifyCredentialResponse}, or a modified {@link
     * VerifyCredentialResponse} with a larger timeout.
     */
    private VerifyCredentialResponse reportResultToSoftwareRateLimiter(
            VerifyCredentialResponse response,
            LskfIdentifier lskfId,
            LockscreenCredential credential) {
        if (android.security.Flags.softwareRatelimiter()) {
            if (response.isMatched()) {
                mSoftwareRateLimiter.reportSuccess(lskfId);
            } else {
                // TODO(b/395976735): don't count transient failures
                Duration swTimeout = mSoftwareRateLimiter.reportWrongGuess(lskfId, credential);

                // The software rate-limiter may use longer delays than the hardware one. While the
                // long-term solution is to update the hardware rate-limiter to match, for now this
                // case needs to be handled by reporting the maximum of the two delays so that the
                // lock screen doesn't allow another attempt until both rate-limiters allow it.
                Duration hwTimeout = response.getTimeoutAsDuration();
                if (swTimeout.compareTo(hwTimeout) > 0) {
                    response = VerifyCredentialResponse.fromTimeout(swTimeout);
                }
            }
        }
        return response;
    }

    private void notifyLockSettingsStateListeners(boolean success, int userId) {
        for (LockSettingsStateListener listener : mLockSettingsStateListeners) {
            if (success) {
@@ -2608,6 +2691,10 @@ public class LockSettingsService extends ILockSettings.Stub {
        mSpManager.removeUser(getGateKeeperService(), userId);
        mStrongAuth.removeUser(userId);

        if (android.security.Flags.softwareRatelimiter()) {
            mSoftwareRateLimiter.clearUserState(userId);
        }

        AndroidKeyStoreMaintenance.onUserRemoved(userId);
        mUnifiedProfilePasswordCache.removePassword(userId);

@@ -3130,6 +3217,9 @@ public class LockSettingsService extends ILockSettings.Stub {
                entry.getValue().zeroize();
            }
        }
        if (android.security.Flags.softwareRatelimiter()) {
            mSoftwareRateLimiter.clearLskfState(new LskfIdentifier(userId, oldProtectorId));
        }
        mSpManager.destroyLskfBasedProtector(oldProtectorId, userId);
        Slogf.i(TAG, "Successfully changed lockscreen credential of user %d", userId);
        return newProtectorId;
@@ -3508,6 +3598,14 @@ public class LockSettingsService extends ILockSettings.Stub {
        pw.println();
        pw.decreaseIndent();

        if (android.security.Flags.softwareRatelimiter()) {
            pw.println("SoftwareRateLimiter:");
            pw.increaseIndent();
            mSoftwareRateLimiter.dump(pw);
            pw.println();
            pw.decreaseIndent();
        }

        pw.println("PasswordHandleCount: " + mGatekeeperPasswords.size());
        synchronized (mUserCreationAndRemovalLock) {
            pw.println("ThirdPartyAppsStarted: " + mThirdPartyAppsStarted);
Loading