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