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

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

Clean up exception usage in LockSettingsService (part 1)

RemoteException are thrown when someone invokes a binder call
but the binder calls fails due to technical reasons (the remote
process died; data sent through binder is too big etc). Right now
in LockSettingsService most RemoteExceptions are just blindly
bubbled up through the call chain. This has the issue that it
will be silently ignored if this is part of some app calling into
LSS via binder, because RemoteException cannot be propagated
across binder at the moment. To fix this, we look at each place
within LSS where RemoteException is thrown and take the following
action:

* If the RemoteException is not critical, just swallow it in place
  and emit a log.
* If it's critical (LSS cannot carry on with the failed invocation):
  ** Rethrow as IllegalStateException (which is binder-compatible)
     so it fails fast.
  ** Unless the surronding method has appropriate error return semantics
     which can be adopted instead. In this case, swallow the exception,
     return error code, and emit log.

Bug: 128831839
Test: atest frameworks/base/services/tests/servicestests/src/com/android/server/locksettings

Change-Id: I4e19b8e63a86e5477933bf21c718771e9018e718
parent deadb7bf
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -31,6 +31,8 @@ public final class GateKeeperResponse implements Parcelable {
    public static final int RESPONSE_OK = 0;
    public static final int RESPONSE_RETRY = 1;

    public static final GateKeeperResponse ERROR = createGenericResponse(RESPONSE_ERROR);

    private final int mResponseCode;

    private int mTimeout;
+135 −139

File changed.

Preview size limit exceeded, changes collapsed.

+140 −86
Original line number Diff line number Diff line
@@ -349,11 +349,9 @@ public class SyntheticPasswordManager {
     * a default all-zero key is used. If the value is not specified, a fresh random secret is
     * generated as the value.
     *
     * @return the value stored in the weaver slot
     * @throws RemoteException
     * @return the value stored in the weaver slot, or null if the operation fails
     */
    private byte[] weaverEnroll(int slot, byte[] key, @Nullable byte[] value)
            throws RemoteException {
    private byte[] weaverEnroll(int slot, byte[] key, @Nullable byte[] value) {
        if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) {
            throw new RuntimeException("Invalid slot for weaver");
        }
@@ -365,11 +363,16 @@ public class SyntheticPasswordManager {
        if (value == null) {
            value = secureRandom(mWeaverConfig.valueSize);
        }
        try {
            int writeStatus = mWeaver.write(slot, toByteArrayList(key), toByteArrayList(value));
            if (writeStatus != WeaverStatus.OK) {
                Log.e(TAG, "weaver write failed, slot: " + slot + " status: " + writeStatus);
                return null;
            }
        } catch (RemoteException e) {
            Log.e(TAG, "weaver write failed", e);
            return null;
        }
        return value;
    }

@@ -377,9 +380,8 @@ public class SyntheticPasswordManager {
     * Verify the supplied key against a weaver slot, returning a response indicating whether
     * the verification is successful, throttled or failed. If successful, the bound secret
     * is also returned.
     * @throws RemoteException
     */
    private VerifyCredentialResponse weaverVerify(int slot, byte[] key) throws RemoteException {
    private VerifyCredentialResponse weaverVerify(int slot, byte[] key) {
        if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) {
            throw new RuntimeException("Invalid slot for weaver");
        }
@@ -389,7 +391,9 @@ public class SyntheticPasswordManager {
            throw new RuntimeException("Invalid key size for weaver");
        }
        final VerifyCredentialResponse[] response = new VerifyCredentialResponse[1];
        mWeaver.read(slot, toByteArrayList(key), (int status, WeaverReadResponse readResponse) -> {
        try {
            mWeaver.read(slot, toByteArrayList(key),
                    (int status, WeaverReadResponse readResponse) -> {
                    switch (status) {
                        case WeaverReadStatus.OK:
                            response[0] = new VerifyCredentialResponse(
@@ -405,7 +409,8 @@ public class SyntheticPasswordManager {
                                Log.e(TAG, "weaver read failed (INCORRECT_KEY), slot: " + slot);
                            } else {
                                response[0] = new VerifyCredentialResponse(readResponse.timeout);
                        Log.e(TAG, "weaver read failed (INCORRECT_KEY/THROTTLE), slot: " + slot);
                                Log.e(TAG, "weaver read failed (INCORRECT_KEY/THROTTLE), slot: "
                                        + slot);
                            }
                            break;
                        case WeaverReadStatus.FAILED:
@@ -418,6 +423,10 @@ public class SyntheticPasswordManager {
                            break;
                    }
                });
        } catch (RemoteException e) {
            response[0] = VerifyCredentialResponse.ERROR;
            Log.e(TAG, "weaver read failed, slot: " + slot, e);
        }
        return response[0];
    }

@@ -460,12 +469,15 @@ public class SyntheticPasswordManager {
     *
     */
    public AuthenticationToken newSyntheticPasswordAndSid(IGateKeeperService gatekeeper,
            byte[] hash, byte[] credential, int userId) throws RemoteException {
            byte[] hash, byte[] credential, int userId) {
        AuthenticationToken result = AuthenticationToken.create();
        GateKeeperResponse response;
        if (hash != null) {
            response = gatekeeper.enroll(userId, hash, credential,
                    result.deriveGkPassword());
            try {
                response = gatekeeper.enroll(userId, hash, credential, result.deriveGkPassword());
            } catch (RemoteException e) {
                throw new IllegalStateException("Failed to enroll credential duing SP init", e);
            }
            if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
                Log.w(TAG, "Fail to migrate SID, assuming no SID, user " + userId);
                clearSidForUser(userId);
@@ -484,9 +496,13 @@ public class SyntheticPasswordManager {
     * Used when adding password to previously-unsecured devices.
     */
    public void newSidForUser(IGateKeeperService gatekeeper, AuthenticationToken authToken,
            int userId) throws RemoteException {
        GateKeeperResponse response = gatekeeper.enroll(userId, null, null,
                authToken.deriveGkPassword());
            int userId) {
        GateKeeperResponse response;
        try {
            response = gatekeeper.enroll(userId, null, null, authToken.deriveGkPassword());
        } catch (RemoteException e) {
            throw new IllegalStateException("Failed to create new SID for user", e);
        }
        if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
            Log.e(TAG, "Fail to create new SID for user " + userId);
            return;
@@ -565,12 +581,8 @@ public class SyntheticPasswordManager {
            Set<Integer> usedSlots = getUsedWeaverSlots();
            if (!usedSlots.contains(slot)) {
                Log.i(TAG, "Destroy weaver slot " + slot + " for user " + userId);
                try {
                weaverEnroll(slot, null, null);
                mPasswordSlotManager.markSlotDeleted(slot);
                } catch (RemoteException e) {
                    Log.w(TAG, "Failed to destroy slot", e);
                }
            } else {
                Log.w(TAG, "Skip destroying reused weaver slot " + slot + " for user " + userId);
            }
@@ -622,11 +634,12 @@ public class SyntheticPasswordManager {
     *
     * @see #newSidForUser
     * @see #clearSidForUser
     * @return a new password handle for the wrapped SP blob
     * @throw IllegalStateException if creation fails.
     */
    public long createPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper,
            byte[] credential, int credentialType, AuthenticationToken authToken,
            int requestedQuality, int userId)
                    throws RemoteException {
            int requestedQuality, int userId) {
        if (credential == null || credentialType == LockPatternUtils.CREDENTIAL_TYPE_NONE) {
            credentialType = LockPatternUtils.CREDENTIAL_TYPE_NONE;
            credential = DEFAULT_PASSWORD;
@@ -642,10 +655,11 @@ public class SyntheticPasswordManager {
            // Weaver based user password
            int weaverSlot = getNextAvailableWeaverSlot();
            Log.i(TAG, "Weaver enroll password to slot " + weaverSlot + " for user " + userId);
            byte[] weaverSecret = weaverEnroll(weaverSlot, passwordTokenToWeaverKey(pwdToken), null);
            byte[] weaverSecret = weaverEnroll(weaverSlot, passwordTokenToWeaverKey(pwdToken),
                    null);
            if (weaverSecret == null) {
                Log.e(TAG, "Fail to enroll user password under weaver " + userId);
                return DEFAULT_HANDLE;
                throw new IllegalStateException(
                        "Fail to enroll user password under weaver " + userId);
            }
            saveWeaverSlot(weaverSlot, handle, userId);
            mPasswordSlotManager.markSlotInUse(weaverSlot);
@@ -657,13 +671,22 @@ public class SyntheticPasswordManager {
        } else {
            // In case GK enrollment leaves persistent state around (in RPMB), this will nuke them
            // to prevent them from accumulating and causing problems.
            try {
                gatekeeper.clearSecureUserId(fakeUid(userId));
            } catch (RemoteException ignore) {
                Log.w(TAG, "Failed to clear SID from gatekeeper");
            }
            // GateKeeper based user password
            GateKeeperResponse response = gatekeeper.enroll(fakeUid(userId), null, null,
            GateKeeperResponse response;
            try {
                response = gatekeeper.enroll(fakeUid(userId), null, null,
                        passwordTokenToGkInput(pwdToken));
            } catch (RemoteException e) {
                throw new IllegalStateException("Failed to enroll password for new SP blob", e);
            }
            if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
                Log.e(TAG, "Fail to enroll user password when creating SP for user " + userId);
                return DEFAULT_HANDLE;
                throw new IllegalStateException(
                        "Fail to enroll user password when creating SP for user " + userId);
            }
            pwd.passwordHandle = response.getPayload();
            sid = sidFromPasswordHandle(pwd.passwordHandle);
@@ -680,14 +703,20 @@ public class SyntheticPasswordManager {

    public VerifyCredentialResponse verifyFrpCredential(IGateKeeperService gatekeeper,
            byte[] userCredential, int credentialType,
            ICheckCredentialProgressCallback progressCallback) throws RemoteException {
            ICheckCredentialProgressCallback progressCallback) {
        PersistentData persistentData = mStorage.readPersistentDataBlock();
        if (persistentData.type == PersistentData.TYPE_SP) {
            PasswordData pwd = PasswordData.fromBytes(persistentData.payload);
            byte[] pwdToken = computePasswordToken(userCredential, pwd);

            GateKeeperResponse response = gatekeeper.verifyChallenge(fakeUid(persistentData.userId),
            GateKeeperResponse response;
            try {
                response = gatekeeper.verifyChallenge(fakeUid(persistentData.userId),
                        0 /* challenge */, pwd.passwordHandle, passwordTokenToGkInput(pwdToken));
            } catch (RemoteException e) {
                Log.e(TAG, "FRP verifyChallenge failed", e);
                return VerifyCredentialResponse.ERROR;
            }
            return VerifyCredentialResponse.fromGateKeeperResponse(response);
        } else if (persistentData.type == PersistentData.TYPE_SP_WEAVER) {
            PasswordData pwd = PasswordData.fromBytes(persistentData.payload);
@@ -805,11 +834,9 @@ public class SyntheticPasswordManager {
        }
        if (isWeaverAvailable()) {
            int slot = getNextAvailableWeaverSlot();
            try {
            Log.i(TAG, "Weaver enroll token to slot " + slot + " for user " + userId);
                weaverEnroll(slot, null, tokenData.weaverSecret);
            } catch (RemoteException e) {
                Log.e(TAG, "Failed to enroll weaver secret when activating token", e);
            if (weaverEnroll(slot, null, tokenData.weaverSecret) == null) {
                Log.e(TAG, "Failed to enroll weaver secret when activating token");
                return false;
            }
            saveWeaverSlot(slot, handle, userId);
@@ -859,7 +886,7 @@ public class SyntheticPasswordManager {
     */
    public AuthenticationResult unwrapPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper,
            long handle, byte[] credential, int userId,
            ICheckCredentialProgressCallback progressCallback) throws RemoteException {
            ICheckCredentialProgressCallback progressCallback) {
        if (credential == null) {
            credential = DEFAULT_PASSWORD;
        }
@@ -886,14 +913,28 @@ public class SyntheticPasswordManager {
            applicationId = transformUnderWeaverSecret(pwdToken, result.gkResponse.getPayload());
        } else {
            byte[] gkPwdToken = passwordTokenToGkInput(pwdToken);
            GateKeeperResponse response = gatekeeper.verifyChallenge(fakeUid(userId), 0L,
            GateKeeperResponse response;
            try {
                response = gatekeeper.verifyChallenge(fakeUid(userId), 0L,
                        pwd.passwordHandle, gkPwdToken);
            } catch (RemoteException e) {
                Log.e(TAG, "gatekeeper verify failed", e);
                result.gkResponse = VerifyCredentialResponse.ERROR;
                return result;
            }
            int responseCode = response.getResponseCode();
            if (responseCode == GateKeeperResponse.RESPONSE_OK) {
                result.gkResponse = VerifyCredentialResponse.OK;
                if (response.getShouldReEnroll()) {
                    GateKeeperResponse reenrollResponse = gatekeeper.enroll(fakeUid(userId),
                    GateKeeperResponse reenrollResponse;
                    try {
                        reenrollResponse = gatekeeper.enroll(fakeUid(userId),
                                pwd.passwordHandle, gkPwdToken, gkPwdToken);
                    } catch (RemoteException e) {
                        Log.w(TAG, "Fail to invoke gatekeeper.enroll", e);
                        reenrollResponse = GateKeeperResponse.ERROR;
                        // continue the flow anyway
                    }
                    if (reenrollResponse.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
                        pwd.passwordHandle = reenrollResponse.getPayload();
                        saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId);
@@ -922,7 +963,11 @@ public class SyntheticPasswordManager {
        // Supplied credential passes first stage weaver/gatekeeper check so it should be correct.
        // Notify the callback so the keyguard UI can proceed immediately.
        if (progressCallback != null) {
            try {
                progressCallback.onCredentialVerified();
            } catch (RemoteException e) {
                Log.w(TAG, "progressCallback throws exception", e);
            }
        }
        result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_PASSWORD_BASED,
                applicationId, sid, userId);
@@ -938,8 +983,7 @@ public class SyntheticPasswordManager {
     * verification to referesh the SID & Auth token maintained by the system.
     */
    public @NonNull AuthenticationResult unwrapTokenBasedSyntheticPassword(
            IGateKeeperService gatekeeper, long handle, byte[] token, int userId)
                    throws RemoteException {
            IGateKeeperService gatekeeper, long handle, byte[] token, int userId) {
        AuthenticationResult result = new AuthenticationResult();
        byte[] secdiscardable = loadSecdiscardable(handle, userId);
        int slotId = loadWeaverSlot(handle, userId);
@@ -1028,38 +1072,48 @@ public class SyntheticPasswordManager {
     * decrypt SP.
     */
    public @Nullable VerifyCredentialResponse verifyChallenge(IGateKeeperService gatekeeper,
            @NonNull AuthenticationToken auth, long challenge, int userId) throws RemoteException {
            @NonNull AuthenticationToken auth, long challenge, int userId) {
        byte[] spHandle = loadSyntheticPasswordHandle(userId);
        if (spHandle == null) {
            // There is no password handle associated with the given user, i.e. the user is not
            // secured by lockscreen and has no SID, so just return here;
            return null;
        }
        VerifyCredentialResponse result;
        GateKeeperResponse response = gatekeeper.verifyChallenge(userId, challenge,
        GateKeeperResponse response;
        try {
            response = gatekeeper.verifyChallenge(userId, challenge,
                    spHandle, auth.deriveGkPassword());
        } catch (RemoteException e) {
            Log.e(TAG, "Fail to verify with gatekeeper " + userId, e);
            return VerifyCredentialResponse.ERROR;
        }
        int responseCode = response.getResponseCode();
        if (responseCode == GateKeeperResponse.RESPONSE_OK) {
            result = new VerifyCredentialResponse(response.getPayload());
            VerifyCredentialResponse result = new VerifyCredentialResponse(response.getPayload());
            if (response.getShouldReEnroll()) {
                response = gatekeeper.enroll(userId, spHandle,
                        spHandle, auth.deriveGkPassword());
                try {
                    response = gatekeeper.enroll(userId, spHandle, spHandle,
                            auth.deriveGkPassword());
                } catch (RemoteException e) {
                    Log.e(TAG, "Failed to invoke gatekeeper.enroll", e);
                    response = GateKeeperResponse.ERROR;
                }
                if (response.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
                    spHandle = response.getPayload();
                    saveSyntheticPasswordHandle(spHandle, userId);
                    // Call self again to re-verify with updated handle
                    return verifyChallenge(gatekeeper, auth, challenge, userId);
                } else {
                    // Fall through, return result from the previous verification attempt.
                    Log.w(TAG, "Fail to re-enroll SP handle for user " + userId);
                    // Fall through, return existing handle
                }
            }
            return result;
        } else if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
            result = new VerifyCredentialResponse(response.getTimeout());
            return new VerifyCredentialResponse(response.getTimeout());
        } else {
            result = VerifyCredentialResponse.ERROR;
            return VerifyCredentialResponse.ERROR;
        }
        return result;
    }

    public boolean existsHandle(long handle, int userId) {