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

Commit c6f4ebf7 authored by Rubin Xu's avatar Rubin Xu
Browse files

Force garbage collection after credential verification

This is to sanitize memory containing sensitive user
lockscreen credentials. Most of LockSettingsService
already sanitizes credentials when needed, but there
is one copy of the credential unmarshalled from the
binder transaction and passed into LSS as argument
which is not easily sanitiziable manually except
by forcing a garbage collection.

Bug: 144537463
Test: atest com.android.server.locksettings
Test: go through Settings password change flow,
      then take a heapdump of system_server and
      verifies no password shard exists in the dump.
Change-Id: I3b0a2dab5766c40bc3ba9b38311c039337c408d3
parent e658c76b
Loading
Loading
Loading
Loading
+54 −22
Original line number Original line Diff line number Diff line
@@ -1616,6 +1616,7 @@ public class LockSettingsService extends ILockSettings.Stub {
        synchronized (mSeparateChallengeLock) {
        synchronized (mSeparateChallengeLock) {
            if (!setLockCredentialInternal(credential, savedCredential,
            if (!setLockCredentialInternal(credential, savedCredential,
                    userId, /* isLockTiedToParent= */ false)) {
                    userId, /* isLockTiedToParent= */ false)) {
                scheduleGc();
                return false;
                return false;
            }
            }
            setSeparateProfileChallengeEnabledLocked(userId, true, /* unused */ null);
            setSeparateProfileChallengeEnabledLocked(userId, true, /* unused */ null);
@@ -1626,6 +1627,7 @@ public class LockSettingsService extends ILockSettings.Stub {
            setDeviceUnlockedForUser(userId);
            setDeviceUnlockedForUser(userId);
        }
        }
        notifySeparateProfileChallengeChanged(userId);
        notifySeparateProfileChallengeChanged(userId);
        scheduleGc();
        return true;
        return true;
    }
    }


@@ -1965,7 +1967,11 @@ public class LockSettingsService extends ILockSettings.Stub {
    public VerifyCredentialResponse checkCredential(LockscreenCredential credential, int userId,
    public VerifyCredentialResponse checkCredential(LockscreenCredential credential, int userId,
            ICheckCredentialProgressCallback progressCallback) {
            ICheckCredentialProgressCallback progressCallback) {
        checkPasswordReadPermission(userId);
        checkPasswordReadPermission(userId);
        try {
            return doVerifyCredential(credential, CHALLENGE_NONE, 0, userId, progressCallback);
            return doVerifyCredential(credential, CHALLENGE_NONE, 0, userId, progressCallback);
        } finally {
            scheduleGc();
        }
    }
    }


    @Override
    @Override
@@ -1978,8 +1984,12 @@ public class LockSettingsService extends ILockSettings.Stub {
            challengeType = CHALLENGE_NONE;
            challengeType = CHALLENGE_NONE;


        }
        }
        try {
            return doVerifyCredential(credential, challengeType, challenge, userId,
            return doVerifyCredential(credential, challengeType, challenge, userId,
                    null /* progressCallback */);
                    null /* progressCallback */);
        } finally {
            scheduleGc();
        }
    }
    }


    private VerifyCredentialResponse doVerifyCredential(LockscreenCredential credential,
    private VerifyCredentialResponse doVerifyCredential(LockscreenCredential credential,
@@ -2070,6 +2080,8 @@ public class LockSettingsService extends ILockSettings.Stub {
                | BadPaddingException | CertificateException | IOException e) {
                | BadPaddingException | CertificateException | IOException e) {
            Slog.e(TAG, "Failed to decrypt child profile key", e);
            Slog.e(TAG, "Failed to decrypt child profile key", e);
            throw new IllegalStateException("Unable to get tied profile token");
            throw new IllegalStateException("Unable to get tied profile token");
        } finally {
            scheduleGc();
        }
        }
    }
    }


@@ -2983,6 +2995,7 @@ public class LockSettingsService extends ILockSettings.Stub {
    @Override
    @Override
    public byte[] getHashFactor(LockscreenCredential currentCredential, int userId) {
    public byte[] getHashFactor(LockscreenCredential currentCredential, int userId) {
        checkPasswordReadPermission(userId);
        checkPasswordReadPermission(userId);
        try {
            if (isManagedProfileWithUnifiedLock(userId)) {
            if (isManagedProfileWithUnifiedLock(userId)) {
                try {
                try {
                    currentCredential = getDecryptedPasswordForTiedProfile(userId);
                    currentCredential = getDecryptedPasswordForTiedProfile(userId);
@@ -3005,6 +3018,9 @@ public class LockSettingsService extends ILockSettings.Stub {
                }
                }
                return auth.authToken.derivePasswordHashFactor();
                return auth.authToken.derivePasswordHashFactor();
            }
            }
        } finally {
            scheduleGc();
        }
    }
    }


    private long addEscrowToken(byte[] token, int userId, EscrowTokenStateChangeCallback callback) {
    private long addEscrowToken(byte[] token, int userId, EscrowTokenStateChangeCallback callback) {
@@ -3287,6 +3303,22 @@ public class LockSettingsService extends ILockSettings.Stub {
        }
        }
    }
    }


    /**
     * 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() {
        mHandler.postDelayed(() -> {
            System.gc();
            System.runFinalization();
            System.gc();
        }, 2000);
    }

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