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