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