1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.locksettings;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.content.Context;
22 import android.hardware.biometrics.BiometricManager;
23 import android.hardware.face.FaceManager;
24 import android.hardware.face.FaceSensorPropertiesInternal;
25 import android.hardware.fingerprint.FingerprintManager;
26 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
27 import android.os.Handler;
28 import android.os.IBinder;
29 import android.os.ServiceManager;
30 import android.service.gatekeeper.IGateKeeperService;
31 import android.util.ArraySet;
32 import android.util.Slog;
33 
34 import com.android.internal.widget.VerifyCredentialResponse;
35 import com.android.server.biometrics.BiometricHandlerProvider;
36 
37 import java.util.ArrayList;
38 import java.util.List;
39 import java.util.Set;
40 
41 /**
42  * Class that handles biometric-related work in the {@link LockSettingsService} area, for example
43  * resetLockout.
44  */
45 @SuppressWarnings("deprecation")
46 public class BiometricDeferredQueue {
47     private static final String TAG = "BiometricDeferredQueue";
48 
49     @NonNull private final SyntheticPasswordManager mSpManager;
50     @NonNull private final Handler mHandler;
51     @Nullable private FingerprintManager mFingerprintManager;
52     @Nullable private FaceManager mFaceManager;
53     @Nullable private BiometricManager mBiometricManager;
54 
55     // Entries added by LockSettingsService once a user's synthetic password is known. At this point
56     // things are still keyed by userId.
57     @NonNull private final ArrayList<UserAuthInfo> mPendingResetLockoutsForFingerprint;
58     @NonNull private final ArrayList<UserAuthInfo> mPendingResetLockoutsForFace;
59     @NonNull private final ArrayList<UserAuthInfo> mPendingResetLockouts;
60 
61     /**
62      * Authentication info for a successful user unlock via Synthetic Password. This can be used to
63      * perform multiple operations (e.g. resetLockout for multiple HALs/Sensors) by sending the
64      * Gatekeeper Password to Gatekeer multiple times, each with a sensor-specific challenge.
65      */
66     private static class UserAuthInfo {
67         final int userId;
68         @NonNull final byte[] gatekeeperPassword;
69 
UserAuthInfo(int userId, @NonNull byte[] gatekeeperPassword)70         UserAuthInfo(int userId, @NonNull byte[] gatekeeperPassword) {
71             this.userId = userId;
72             this.gatekeeperPassword = gatekeeperPassword;
73         }
74     }
75 
76     /**
77      * Per-authentication callback.
78      */
79     private static class FaceResetLockoutTask implements FaceManager.GenerateChallengeCallback {
80         interface FinishCallback {
onFinished()81             void onFinished();
82         }
83 
84         @NonNull FinishCallback finishCallback;
85         @NonNull FaceManager faceManager;
86         @NonNull SyntheticPasswordManager spManager;
87         @NonNull Set<Integer> sensorIds; // IDs of sensors waiting for challenge
88         @NonNull List<UserAuthInfo> pendingResetLockuts;
89 
FaceResetLockoutTask( @onNull FinishCallback finishCallback, @NonNull FaceManager faceManager, @NonNull SyntheticPasswordManager spManager, @NonNull Set<Integer> sensorIds, @NonNull List<UserAuthInfo> pendingResetLockouts)90         FaceResetLockoutTask(
91                 @NonNull FinishCallback finishCallback,
92                 @NonNull FaceManager faceManager,
93                 @NonNull SyntheticPasswordManager spManager,
94                 @NonNull Set<Integer> sensorIds,
95                 @NonNull List<UserAuthInfo> pendingResetLockouts) {
96             this.finishCallback = finishCallback;
97             this.faceManager = faceManager;
98             this.spManager = spManager;
99             this.sensorIds = sensorIds;
100             this.pendingResetLockuts = pendingResetLockouts;
101         }
102 
103         @Override
onGenerateChallengeResult(int sensorId, int userId, long challenge)104         public void onGenerateChallengeResult(int sensorId, int userId, long challenge) {
105             if (!sensorIds.contains(sensorId)) {
106                 Slog.e(TAG, "Unknown sensorId received: " + sensorId);
107                 return;
108             }
109 
110             // Challenge received for a sensor. For each sensor, reset lockout for all users.
111             for (UserAuthInfo userAuthInfo : pendingResetLockuts) {
112                 Slog.d(TAG, "Resetting face lockout for sensor: " + sensorId
113                         + ", user: " + userAuthInfo.userId);
114                 final byte[] hat = requestHatFromGatekeeperPassword(spManager, userAuthInfo,
115                         challenge);
116                 if (hat != null) {
117                     faceManager.resetLockout(sensorId, userAuthInfo.userId, hat);
118                 }
119             }
120 
121             sensorIds.remove(sensorId);
122             faceManager.revokeChallenge(sensorId, userId, challenge);
123 
124             if (sensorIds.isEmpty()) {
125                 Slog.d(TAG, "Done requesting resetLockout for all face sensors");
126                 finishCallback.onFinished();
127             }
128         }
129     }
130 
131     @Nullable private FaceResetLockoutTask mFaceResetLockoutTask;
132     private final FaceResetLockoutTask.FinishCallback mFaceFinishCallback = () -> {
133         mFaceResetLockoutTask = null;
134     };
135 
BiometricDeferredQueue(@onNull SyntheticPasswordManager spManager)136     BiometricDeferredQueue(@NonNull SyntheticPasswordManager spManager) {
137         mSpManager = spManager;
138 
139         //Using a higher priority thread to avoid any delays and interruption of clients
140         mHandler = BiometricHandlerProvider.getInstance().getBiometricCallbackHandler();
141         mPendingResetLockoutsForFingerprint = new ArrayList<>();
142         mPendingResetLockoutsForFace = new ArrayList<>();
143         mPendingResetLockouts = new ArrayList<>();
144     }
145 
systemReady(@ullable FingerprintManager fingerprintManager, @Nullable FaceManager faceManager, @Nullable BiometricManager biometricManager)146     public void systemReady(@Nullable FingerprintManager fingerprintManager,
147             @Nullable FaceManager faceManager, @Nullable BiometricManager biometricManager) {
148         mFingerprintManager = fingerprintManager;
149         mFaceManager = faceManager;
150         mBiometricManager = biometricManager;
151     }
152 
153     /**
154      * Adds a request for resetLockout on all biometric sensors for the user specified. The queue
155      * owner must invoke {@link #processPendingLockoutResets()} at some point to kick off the
156      * operations.
157      *
158      * Note that this should only ever be invoked for successful authentications, otherwise it will
159      * consume a Gatekeeper authentication attempt and potentially wipe the user/device.
160      *
161      * @param userId             The user that the operation will apply for.
162      * @param gatekeeperPassword The Gatekeeper Password
163      */
addPendingLockoutResetForUser(int userId, @NonNull byte[] gatekeeperPassword)164     void addPendingLockoutResetForUser(int userId, @NonNull byte[] gatekeeperPassword) {
165         mHandler.post(() -> {
166             if (mFaceManager != null && mFaceManager.hasEnrolledTemplates(userId)) {
167                 Slog.d(TAG, "Face addPendingLockoutResetForUser: " + userId);
168                 mPendingResetLockoutsForFace.add(new UserAuthInfo(userId, gatekeeperPassword));
169             }
170 
171             if (mFingerprintManager != null
172                     && mFingerprintManager.hasEnrolledFingerprints(userId)) {
173                 Slog.d(TAG, "Fingerprint addPendingLockoutResetForUser: " + userId);
174                 mPendingResetLockoutsForFingerprint.add(new UserAuthInfo(userId,
175                         gatekeeperPassword));
176             }
177 
178             if (mBiometricManager != null) {
179                 Slog.d(TAG, "Fingerprint addPendingLockoutResetForUser: " + userId);
180                 mPendingResetLockouts.add(new UserAuthInfo(userId,
181                         gatekeeperPassword));
182             }
183         });
184     }
185 
processPendingLockoutResets()186     void processPendingLockoutResets() {
187         mHandler.post(() -> {
188             if (!mPendingResetLockoutsForFace.isEmpty()) {
189                 Slog.d(TAG, "Processing pending resetLockout for face");
190                 processPendingLockoutsForFace(new ArrayList<>(mPendingResetLockoutsForFace));
191                 mPendingResetLockoutsForFace.clear();
192             }
193 
194             if (!mPendingResetLockoutsForFingerprint.isEmpty()) {
195                 Slog.d(TAG, "Processing pending resetLockout for fingerprint");
196                 processPendingLockoutsForFingerprint(
197                         new ArrayList<>(mPendingResetLockoutsForFingerprint));
198                 mPendingResetLockoutsForFingerprint.clear();
199             }
200 
201             if (!mPendingResetLockouts.isEmpty()) {
202                 Slog.d(TAG, "Processing pending resetLockouts(Generic)");
203                 processPendingLockoutsGeneric(
204                         new ArrayList<>(mPendingResetLockouts));
205                 mPendingResetLockouts.clear();
206             }
207 
208         });
209     }
210 
processPendingLockoutsForFingerprint(List<UserAuthInfo> pendingResetLockouts)211     private void processPendingLockoutsForFingerprint(List<UserAuthInfo> pendingResetLockouts) {
212         if (mFingerprintManager != null) {
213             final List<FingerprintSensorPropertiesInternal> fingerprintSensorProperties =
214                     mFingerprintManager.getSensorPropertiesInternal();
215             for (FingerprintSensorPropertiesInternal prop : fingerprintSensorProperties) {
216                 if (!prop.resetLockoutRequiresHardwareAuthToken) {
217                     for (UserAuthInfo user : pendingResetLockouts) {
218                         mFingerprintManager.resetLockout(prop.sensorId, user.userId,
219                                 null /* hardwareAuthToken */);
220                     }
221                 } else if (!prop.resetLockoutRequiresChallenge) {
222                     for (UserAuthInfo user : pendingResetLockouts) {
223                         Slog.d(TAG, "Resetting fingerprint lockout for sensor: " + prop.sensorId
224                                 + ", user: " + user.userId);
225                         final byte[] hat = requestHatFromGatekeeperPassword(mSpManager, user,
226                                 0 /* challenge */);
227                         if (hat != null) {
228                             mFingerprintManager.resetLockout(prop.sensorId, user.userId, hat);
229                         }
230                     }
231                 } else {
232                     Slog.w(TAG, "No fingerprint HAL interface requires HAT with challenge"
233                             + ", sensorId: " + prop.sensorId);
234                 }
235             }
236         }
237     }
238 
processPendingLockoutsForFace(List<UserAuthInfo> pendingResetLockouts)239     private void processPendingLockoutsForFace(List<UserAuthInfo> pendingResetLockouts) {
240         if (mFaceManager != null) {
241             if (mFaceResetLockoutTask != null) {
242                 // This code will need to be updated if this problem ever occurs.
243                 Slog.w(TAG,
244                         "mFaceGenerateChallengeCallback not null, previous operation may be stuck");
245             }
246             final List<FaceSensorPropertiesInternal> faceSensorProperties =
247                     mFaceManager.getSensorPropertiesInternal();
248             final Set<Integer> sensorIds = new ArraySet<>();
249             for (FaceSensorPropertiesInternal prop : faceSensorProperties) {
250                 sensorIds.add(prop.sensorId);
251             }
252 
253             mFaceResetLockoutTask = new FaceResetLockoutTask(mFaceFinishCallback, mFaceManager,
254                     mSpManager, sensorIds, pendingResetLockouts);
255             for (final FaceSensorPropertiesInternal prop : faceSensorProperties) {
256                 if (prop.resetLockoutRequiresHardwareAuthToken) {
257                     for (UserAuthInfo user : pendingResetLockouts) {
258                         if (prop.resetLockoutRequiresChallenge) {
259                             Slog.d(TAG, "Generating challenge for sensor: " + prop.sensorId
260                                     + ", user: " + user.userId);
261                             mFaceManager.generateChallenge(prop.sensorId, user.userId,
262                                     mFaceResetLockoutTask);
263                         } else {
264                             Slog.d(TAG, "Resetting face lockout for sensor: " + prop.sensorId
265                                     + ", user: " + user.userId);
266                             final byte[] hat = requestHatFromGatekeeperPassword(mSpManager, user,
267                                     0 /* challenge */);
268                             if (hat != null) {
269                                 mFaceManager.resetLockout(prop.sensorId, user.userId, hat);
270                             }
271                         }
272                     }
273                 } else {
274                     Slog.w(TAG, "Lockout is below the HAL for all face authentication interfaces"
275                             + ", sensorId: " + prop.sensorId);
276                 }
277             }
278         }
279     }
280 
processPendingLockoutsGeneric(List<UserAuthInfo> pendingResetLockouts)281     private void processPendingLockoutsGeneric(List<UserAuthInfo> pendingResetLockouts) {
282         for (UserAuthInfo user : pendingResetLockouts) {
283             Slog.d(TAG, "Resetting biometric lockout for user: " + user.userId);
284             final byte[] hat = requestHatFromGatekeeperPassword(mSpManager, user,
285                     0 /* challenge */);
286             if (hat != null) {
287                 mBiometricManager.resetLockout(user.userId, hat);
288             }
289         }
290     }
291 
292     @Nullable
requestHatFromGatekeeperPassword( @onNull SyntheticPasswordManager spManager, @NonNull UserAuthInfo userAuthInfo, long challenge)293     private static byte[] requestHatFromGatekeeperPassword(
294             @NonNull SyntheticPasswordManager spManager,
295             @NonNull UserAuthInfo userAuthInfo, long challenge) {
296         final VerifyCredentialResponse response = spManager.verifyChallengeInternal(
297                 getGatekeeperService(), userAuthInfo.gatekeeperPassword, challenge,
298                 userAuthInfo.userId);
299         if (response == null) {
300             Slog.wtf(TAG, "VerifyChallenge failed, null response");
301             return null;
302         }
303         if (response.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
304             Slog.wtf(TAG, "VerifyChallenge failed, response: "
305                     + response.getResponseCode());
306             return null;
307         }
308         if (response.getGatekeeperHAT() == null) {
309             Slog.e(TAG, "Null HAT received from spManager");
310         }
311 
312         return response.getGatekeeperHAT();
313     }
314 
315     @Nullable
getGatekeeperService()316     private static synchronized IGateKeeperService getGatekeeperService() {
317         final IBinder service = ServiceManager.waitForService(Context.GATEKEEPER_SERVICE);
318         if (service == null) {
319             Slog.e(TAG, "Unable to acquire GateKeeperService");
320             return null;
321         }
322         return IGateKeeperService.Stub.asInterface(service);
323     }
324 }
325