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

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

Merge changes I92dcb4d9,I40e06d47 into main

* changes:
  Remove explicit garbage collection from LockSettingsService
  Zeroize LockscreenCredential objects received by LockSettingsService
parents fcab278f ffbd7fad
Loading
Loading
Loading
Loading
+26 −10
Original line number Diff line number Diff line
@@ -82,6 +82,9 @@ public class LockscreenCredential implements Parcelable, AutoCloseable {
    // generates and manages transparently to the user. Implies mType == CREDENTIAL_TYPE_PASSWORD.
    private final boolean mIsUnifiedProfilePassword;

    // Whether the credential was unmarshalled from a Parcel, rather than constructed directly.
    private final boolean mIsFromParcel;

    /**
     * Private constructor, use static builder methods instead.
     *
@@ -93,7 +96,8 @@ public class LockscreenCredential implements Parcelable, AutoCloseable {
            int type,
            byte[] credential,
            boolean hasInvalidChars,
            boolean isUnifiedProfilePassword) {
            boolean isUnifiedProfilePassword,
            boolean isFromParcel) {
        Objects.requireNonNull(credential);
        if (type == CREDENTIAL_TYPE_NONE) {
            Preconditions.checkArgument(credential.length == 0);
@@ -115,6 +119,7 @@ public class LockscreenCredential implements Parcelable, AutoCloseable {
        mCredential = credential;
        mHasInvalidChars = hasInvalidChars;
        mIsUnifiedProfilePassword = isUnifiedProfilePassword;
        mIsFromParcel = isFromParcel;
    }

    private LockscreenCredential(int type, CharSequence credential) {
@@ -122,14 +127,15 @@ public class LockscreenCredential implements Parcelable, AutoCloseable {
                type,
                charsToBytesTruncating(credential),
                hasInvalidChars(credential),
                /* isUnifiedProfilePassword= */ false);
                /* isUnifiedProfilePassword= */ false,
                /* isFromParcel= */ false);
    }

    /**
     * Creates a LockscreenCredential object representing a none credential.
     */
    public static LockscreenCredential createNone() {
        return new LockscreenCredential(CREDENTIAL_TYPE_NONE, new byte[0], false, false);
        return new LockscreenCredential(CREDENTIAL_TYPE_NONE, new byte[0], false, false, false);
    }

    /**
@@ -140,7 +146,8 @@ public class LockscreenCredential implements Parcelable, AutoCloseable {
                CREDENTIAL_TYPE_PATTERN,
                LockPatternUtils.patternToByteArray(pattern),
                /* hasInvalidChars= */ false,
                /* isUnifiedProfilePassword= */ false);
                /* isUnifiedProfilePassword= */ false,
                /* isFromParcel= */ false);
    }

    /**
@@ -161,7 +168,8 @@ public class LockscreenCredential implements Parcelable, AutoCloseable {
                CREDENTIAL_TYPE_PASSWORD,
                copyOfArrayNonMovable(password),
                /* hasInvalidChars= */ false,
                /* isUnifiedProfilePassword= */ true);
                /* isUnifiedProfilePassword= */ true,
                /* isFromParcel= */ false);
    }

    /**
@@ -271,12 +279,12 @@ public class LockscreenCredential implements Parcelable, AutoCloseable {
                mType,
                mCredential != null ? copyOfArrayNonMovable(mCredential) : null,
                mHasInvalidChars,
                mIsUnifiedProfilePassword);
                mIsUnifiedProfilePassword,
                /* Any duplicate copy is not from a Parcel, so set isFromParcel=false */
                /* isFromParcel= */ false);
    }

    /**
     * Zeroize the credential bytes.
     */
    /** Zeroizes the credential bytes. */
    public void zeroize() {
        if (mCredential != null) {
            ArrayUtils.zeroize(mCredential);
@@ -284,6 +292,13 @@ public class LockscreenCredential implements Parcelable, AutoCloseable {
        }
    }

    /** Zeroizes the given LockscreenCredential if it is non-null and was created from a Parcel. */
    public static void zeroizeIfFromParcel(@Nullable LockscreenCredential credential) {
        if (credential != null && credential.mIsFromParcel) {
            credential.zeroize();
        }
    }

    /**
     * Copies the given array into a new non-movable array.
     */
@@ -412,7 +427,8 @@ public class LockscreenCredential implements Parcelable, AutoCloseable {
                            source.readInt(),
                            source.createByteArray(),
                            source.readBoolean(),
                            source.readBoolean());
                            source.readBoolean(),
                            /* isFromParcel= */ true);
                }

                @Override
+41 −48
Original line number Diff line number Diff line
@@ -333,8 +333,6 @@ public class LockSettingsService extends ILockSettings.Stub {
    private final CopyOnWriteArrayList<LockSettingsStateListener> mLockSettingsStateListeners =
            new CopyOnWriteArrayList<>();

    private final Object mGcWorkToken = new Object();

    // This class manages life cycle events for encrypted users on File Based Encryption (FBE)
    // devices. The most basic of these is to show/hide notifications about missing features until
    // the user unlocks the account and credential-encrypted storage is available.
@@ -1328,6 +1326,7 @@ public class LockSettingsService extends ILockSettings.Stub {
    public void setSeparateProfileChallengeEnabled(int userId, boolean enabled,
            LockscreenCredential profileUserPassword) {
        checkWritePermission();
        try {
            if (!mHasSecureLockScreen
                    && profileUserPassword != null
                    && profileUserPassword.getType() != CREDENTIAL_TYPE_NONE) {
@@ -1335,10 +1334,17 @@ public class LockSettingsService extends ILockSettings.Stub {
                        "This operation requires secure lock screen feature.");
            }
            synchronized (mSeparateChallengeLock) {
            setSeparateProfileChallengeEnabledLocked(userId, enabled, profileUserPassword != null
                    ? profileUserPassword : LockscreenCredential.createNone());
                setSeparateProfileChallengeEnabledLocked(
                        userId,
                        enabled,
                        profileUserPassword != null
                                ? profileUserPassword
                                : LockscreenCredential.createNone());
            }
            notifySeparateProfileChallengeChanged(userId);
        } finally {
            LockscreenCredential.zeroizeIfFromParcel(profileUserPassword);
        }
    }

    @GuardedBy("mSeparateChallengeLock")
@@ -1841,10 +1847,11 @@ public class LockSettingsService extends ILockSettings.Stub {
                                + "ACCESS_KEYGUARD_SECURE_STORAGE");
            }
        }
        credential.validateBasicRequirements();

        final long identity = Binder.clearCallingIdentity();
        try {
            credential.validateBasicRequirements();

            enforceFrpNotActive();
            // When changing credential for profiles with unified challenge, some callers
            // will pass in empty credential while others will pass in the credential of
@@ -1869,7 +1876,6 @@ public class LockSettingsService extends ILockSettings.Stub {
            synchronized (mSeparateChallengeLock) {
                if (!setLockCredentialInternal(credential, savedCredential,
                        userId, /* isLockTiedToParent= */ false)) {
                    scheduleGc();
                    return false;
                }
                setSeparateProfileChallengeEnabledLocked(userId, true, /* unused */ null);
@@ -1881,10 +1887,11 @@ public class LockSettingsService extends ILockSettings.Stub {
            }
            notifySeparateProfileChallengeChanged(userId);
            onPostPasswordChanged(credential, userId);
            scheduleGc();
            return true;
        } finally {
            Binder.restoreCallingIdentity(identity);
            LockscreenCredential.zeroizeIfFromParcel(credential);
            LockscreenCredential.zeroizeIfFromParcel(savedCredential);
        }
    }

@@ -2348,7 +2355,7 @@ public class LockSettingsService extends ILockSettings.Stub {
            return doVerifyCredential(credential, userId, progressCallback, 0 /* flags */);
        } finally {
            Binder.restoreCallingIdentity(identity);
            scheduleGc();
            LockscreenCredential.zeroizeIfFromParcel(credential);
        }
    }

@@ -2367,7 +2374,7 @@ public class LockSettingsService extends ILockSettings.Stub {
            return doVerifyCredential(credential, userId, null /* progressCallback */, flags);
        } finally {
            Binder.restoreCallingIdentity(identity);
            scheduleGc();
            LockscreenCredential.zeroizeIfFromParcel(credential);
        }
    }

@@ -2540,10 +2547,8 @@ public class LockSettingsService extends ILockSettings.Stub {
        }
    }

    @Override
    public VerifyCredentialResponse verifyTiedProfileChallenge(LockscreenCredential credential,
            int userId, @LockPatternUtils.VerifyFlag int flags) {
        checkPasswordReadPermission();
    private VerifyCredentialResponse doVerifyTiedProfileChallenge(
            LockscreenCredential credential, int userId, @LockPatternUtils.VerifyFlag int flags) {
        Slogf.i(TAG, "Verifying tied profile challenge for user %d", userId);

        if (!isProfileWithUnifiedLock(userId)) {
@@ -2571,8 +2576,17 @@ public class LockSettingsService extends ILockSettings.Stub {
                | BadPaddingException | CertificateException | IOException e) {
            Slog.e(TAG, "Failed to decrypt child profile key", e);
            throw new IllegalStateException("Unable to get tied profile token");
        }
    }

    @Override
    public VerifyCredentialResponse verifyTiedProfileChallenge(
            LockscreenCredential credential, int userId, @LockPatternUtils.VerifyFlag int flags) {
        checkPasswordReadPermission();
        try {
            return doVerifyTiedProfileChallenge(credential, userId, flags);
        } finally {
            scheduleGc();
            LockscreenCredential.zeroizeIfFromParcel(credential);
        }
    }

@@ -3333,14 +3347,17 @@ public class LockSettingsService extends ILockSettings.Stub {
            if (profilePassword != null) {
                profilePassword.zeroize();
            }
            scheduleGc();
        }
    }

    @Override
    public byte[] getHashFactor(LockscreenCredential currentCredential, int userId) {
        checkPasswordReadPermission();
        try {
            return getHashFactorInternal(currentCredential, userId);
        } finally {
            LockscreenCredential.zeroizeIfFromParcel(currentCredential);
        }
    }

    private long addEscrowToken(@NonNull byte[] token, @TokenType int type, int userId,
@@ -3691,30 +3708,6 @@ public class LockSettingsService extends ILockSettings.Stub {
        mSpManager.destroyEscrowData(userId);
    }

    /**
     * Schedules garbage collection to sanitize lockscreen credential remnants in memory.
     *
     * One source of leftover lockscreen credentials is the unmarshalled binder method arguments.
     * Since this method will be called within the binder implementation method, a small delay is
     * added before the GC operation to allow the enclosing binder proxy code to complete and
     * release references to the argument.
     */
    private void scheduleGc() {
        // Cancel any existing GC request first, so that GC requests don't pile up if lockscreen
        // credential operations are happening very quickly, e.g. as sometimes happens during tests.
        //
        // This delays the already-requested GC, but that is fine in practice where lockscreen
        // operations don't happen very quickly.  And the precise time that the sanitization happens
        // isn't very important; doing it within a minute can be fine, for example.
        mHandler.removeCallbacksAndMessages(mGcWorkToken);

        mHandler.postDelayed(() -> {
            System.gc();
            System.runFinalization();
            System.gc();
        }, mGcWorkToken, 2000);
    }

    private class DeviceProvisionedObserver extends ContentObserver {
        private final Uri mDeviceProvisionedUri = Settings.Global.getUriFor(
                Settings.Global.DEVICE_PROVISIONED);