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