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.biometrics.sensors; 18 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.app.ActivityTaskManager; 23 import android.app.TaskStackListener; 24 import android.content.Context; 25 import android.content.pm.ApplicationInfo; 26 import android.hardware.biometrics.BiometricAuthenticator; 27 import android.hardware.biometrics.BiometricConstants; 28 import android.hardware.biometrics.BiometricManager; 29 import android.hardware.biometrics.BiometricOverlayConstants; 30 import android.os.IBinder; 31 import android.os.RemoteException; 32 import android.os.SystemClock; 33 import android.security.KeyStore; 34 import android.util.EventLog; 35 import android.util.Slog; 36 37 import com.android.server.biometrics.BiometricsProto; 38 import com.android.server.biometrics.Utils; 39 import com.android.server.biometrics.log.BiometricContext; 40 import com.android.server.biometrics.log.BiometricLogger; 41 42 import java.util.ArrayList; 43 import java.util.function.Supplier; 44 45 /** 46 * A class to keep track of the authentication state for a given client. 47 */ 48 public abstract class AuthenticationClient<T> extends AcquisitionClient<T> 49 implements AuthenticationConsumer { 50 51 private static final String TAG = "Biometrics/AuthenticationClient"; 52 53 // New, has not started yet 54 public static final int STATE_NEW = 0; 55 // Framework/HAL have started this operation 56 public static final int STATE_STARTED = 1; 57 // Operation is started, but requires some user action to start (such as finger lift & re-touch) 58 public static final int STATE_STARTED_PAUSED = 2; 59 // Same as above, except auth was attempted (rejected, timed out, etc). 60 public static final int STATE_STARTED_PAUSED_ATTEMPTED = 3; 61 // Done, errored, canceled, etc. HAL/framework are not running this sensor anymore. 62 public static final int STATE_STOPPED = 4; 63 64 @IntDef({STATE_NEW, 65 STATE_STARTED, 66 STATE_STARTED_PAUSED, 67 STATE_STARTED_PAUSED_ATTEMPTED, 68 STATE_STOPPED}) 69 @interface State {} 70 71 private final boolean mIsStrongBiometric; 72 private final boolean mRequireConfirmation; 73 private final ActivityTaskManager mActivityTaskManager; 74 private final BiometricManager mBiometricManager; 75 @Nullable private final TaskStackListener mTaskStackListener; 76 private final LockoutTracker mLockoutTracker; 77 private final boolean mIsRestricted; 78 private final boolean mAllowBackgroundAuthentication; 79 private final boolean mIsKeyguardBypassEnabled; 80 81 protected final long mOperationId; 82 83 private long mStartTimeMs; 84 85 private boolean mAuthAttempted; 86 private boolean mAuthSuccess = false; 87 88 // TODO: This is currently hard to maintain, as each AuthenticationClient subclass must update 89 // the state. We should think of a way to improve this in the future. 90 protected @State int mState = STATE_NEW; 91 AuthenticationClient(@onNull Context context, @NonNull Supplier<T> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId, boolean restricted, @NonNull String owner, int cookie, boolean requireConfirmation, int sensorId, @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext, boolean isStrongBiometric, @Nullable TaskStackListener taskStackListener, @NonNull LockoutTracker lockoutTracker, boolean allowBackgroundAuthentication, boolean shouldVibrate, boolean isKeyguardBypassEnabled)92 public AuthenticationClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon, 93 @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, 94 int targetUserId, long operationId, boolean restricted, @NonNull String owner, 95 int cookie, boolean requireConfirmation, int sensorId, 96 @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext, 97 boolean isStrongBiometric, @Nullable TaskStackListener taskStackListener, 98 @NonNull LockoutTracker lockoutTracker, boolean allowBackgroundAuthentication, 99 boolean shouldVibrate, boolean isKeyguardBypassEnabled) { 100 super(context, lazyDaemon, token, listener, targetUserId, owner, cookie, sensorId, 101 shouldVibrate, biometricLogger, biometricContext); 102 mIsStrongBiometric = isStrongBiometric; 103 mOperationId = operationId; 104 mRequireConfirmation = requireConfirmation; 105 mActivityTaskManager = getActivityTaskManager(); 106 mBiometricManager = context.getSystemService(BiometricManager.class); 107 mTaskStackListener = taskStackListener; 108 mLockoutTracker = lockoutTracker; 109 mIsRestricted = restricted; 110 mAllowBackgroundAuthentication = allowBackgroundAuthentication; 111 mIsKeyguardBypassEnabled = isKeyguardBypassEnabled; 112 } 113 handleFailedAttempt(int userId)114 public @LockoutTracker.LockoutMode int handleFailedAttempt(int userId) { 115 final @LockoutTracker.LockoutMode int lockoutMode = 116 mLockoutTracker.getLockoutModeForUser(userId); 117 final PerformanceTracker performanceTracker = 118 PerformanceTracker.getInstanceForSensorId(getSensorId()); 119 120 if (lockoutMode == LockoutTracker.LOCKOUT_PERMANENT) { 121 performanceTracker.incrementPermanentLockoutForUser(userId); 122 } else if (lockoutMode == LockoutTracker.LOCKOUT_TIMED) { 123 performanceTracker.incrementTimedLockoutForUser(userId); 124 } 125 126 return lockoutMode; 127 } 128 getStartTimeMs()129 protected long getStartTimeMs() { 130 return mStartTimeMs; 131 } 132 getActivityTaskManager()133 protected ActivityTaskManager getActivityTaskManager() { 134 return ActivityTaskManager.getInstance(); 135 } 136 137 @Override binderDied()138 public void binderDied() { 139 final boolean clearListener = !isBiometricPrompt(); 140 binderDiedInternal(clearListener); 141 } 142 isBiometricPrompt()143 public boolean isBiometricPrompt() { 144 return getCookie() != 0; 145 } 146 getOperationId()147 public long getOperationId() { 148 return mOperationId; 149 } 150 isRestricted()151 public boolean isRestricted() { 152 return mIsRestricted; 153 } 154 isKeyguard()155 public boolean isKeyguard() { 156 return Utils.isKeyguard(getContext(), getOwnerString()); 157 } 158 isSettings()159 private boolean isSettings() { 160 return Utils.isSettings(getContext(), getOwnerString()); 161 } 162 163 @Override isCryptoOperation()164 protected boolean isCryptoOperation() { 165 return mOperationId != 0; 166 } 167 168 @Override onAuthenticated(BiometricAuthenticator.Identifier identifier, boolean authenticated, ArrayList<Byte> hardwareAuthToken)169 public void onAuthenticated(BiometricAuthenticator.Identifier identifier, 170 boolean authenticated, ArrayList<Byte> hardwareAuthToken) { 171 getLogger().logOnAuthenticated(getContext(), getOperationContext(), 172 authenticated, mRequireConfirmation, getTargetUserId(), isBiometricPrompt()); 173 174 final ClientMonitorCallbackConverter listener = getListener(); 175 176 if (DEBUG) Slog.v(TAG, "onAuthenticated(" + authenticated + ")" 177 + ", ID:" + identifier.getBiometricId() 178 + ", Owner: " + getOwnerString() 179 + ", isBP: " + isBiometricPrompt() 180 + ", listener: " + listener 181 + ", requireConfirmation: " + mRequireConfirmation 182 + ", user: " + getTargetUserId() 183 + ", clientMonitor: " + toString()); 184 185 final PerformanceTracker pm = PerformanceTracker.getInstanceForSensorId(getSensorId()); 186 if (isCryptoOperation()) { 187 pm.incrementCryptoAuthForUser(getTargetUserId(), authenticated); 188 } else { 189 pm.incrementAuthForUser(getTargetUserId(), authenticated); 190 } 191 192 if (mAllowBackgroundAuthentication) { 193 Slog.w(TAG, "Allowing background authentication," 194 + " this is allowed only for platform or test invocations"); 195 } 196 197 // Ensure authentication only succeeds if the client activity is on top. 198 boolean isBackgroundAuth = false; 199 if (!mAllowBackgroundAuthentication && authenticated 200 && !Utils.isKeyguard(getContext(), getOwnerString()) 201 && !Utils.isSystem(getContext(), getOwnerString())) { 202 isBackgroundAuth = Utils.isBackground(getOwnerString()); 203 } 204 205 // Fail authentication if we can't confirm the client activity is on top. 206 if (isBackgroundAuth) { 207 Slog.e(TAG, "Failing possible background authentication"); 208 authenticated = false; 209 210 // SafetyNet logging for exploitation attempts of b/159249069. 211 final ApplicationInfo appInfo = getContext().getApplicationInfo(); 212 EventLog.writeEvent(0x534e4554, "159249069", appInfo != null ? appInfo.uid : -1, 213 "Attempted background authentication"); 214 } 215 216 if (authenticated) { 217 // SafetyNet logging for b/159249069 if constraint is violated. 218 if (isBackgroundAuth) { 219 final ApplicationInfo appInfo = getContext().getApplicationInfo(); 220 EventLog.writeEvent(0x534e4554, "159249069", appInfo != null ? appInfo.uid : -1, 221 "Successful background authentication!"); 222 } 223 224 mAuthSuccess = true; 225 markAlreadyDone(); 226 227 if (mTaskStackListener != null) { 228 mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener); 229 } 230 231 final byte[] byteToken = new byte[hardwareAuthToken.size()]; 232 for (int i = 0; i < hardwareAuthToken.size(); i++) { 233 byteToken[i] = hardwareAuthToken.get(i); 234 } 235 236 if (mIsStrongBiometric) { 237 mBiometricManager.resetLockoutTimeBound(getToken(), 238 getContext().getOpPackageName(), 239 getSensorId(), getTargetUserId(), byteToken); 240 } 241 242 final CoexCoordinator coordinator = CoexCoordinator.getInstance(); 243 coordinator.onAuthenticationSucceeded(SystemClock.uptimeMillis(), this, 244 new CoexCoordinator.Callback() { 245 @Override 246 public void sendAuthenticationResult(boolean addAuthTokenIfStrong) { 247 if (addAuthTokenIfStrong && mIsStrongBiometric) { 248 final int result = KeyStore.getInstance().addAuthToken(byteToken); 249 Slog.d(TAG, "addAuthToken: " + result); 250 } else { 251 Slog.d(TAG, "Skipping addAuthToken"); 252 } 253 254 if (listener != null) { 255 try { 256 // Explicitly have if/else here to make it super obvious in case the 257 // code is touched in the future. 258 if (!mIsRestricted) { 259 listener.onAuthenticationSucceeded(getSensorId(), 260 identifier, 261 byteToken, 262 getTargetUserId(), 263 mIsStrongBiometric); 264 } else { 265 listener.onAuthenticationSucceeded(getSensorId(), 266 null /* identifier */, 267 byteToken, 268 getTargetUserId(), 269 mIsStrongBiometric); 270 } 271 } catch (RemoteException e) { 272 Slog.e(TAG, "Unable to notify listener", e); 273 } 274 } else { 275 Slog.w(TAG, "Client not listening"); 276 } 277 } 278 279 @Override 280 public void sendHapticFeedback() { 281 if (listener != null && mShouldVibrate) { 282 vibrateSuccess(); 283 } 284 } 285 286 @Override 287 public void handleLifecycleAfterAuth() { 288 AuthenticationClient.this.handleLifecycleAfterAuth(true /* authenticated */); 289 } 290 291 @Override 292 public void sendAuthenticationCanceled() { 293 sendCancelOnly(listener); 294 } 295 }); 296 } else { // not authenticated 297 if (isBackgroundAuth) { 298 Slog.e(TAG, "cancelling due to background auth"); 299 cancel(); 300 } else { 301 // Allow system-defined limit of number of attempts before giving up 302 final @LockoutTracker.LockoutMode int lockoutMode = 303 handleFailedAttempt(getTargetUserId()); 304 if (lockoutMode != LockoutTracker.LOCKOUT_NONE) { 305 markAlreadyDone(); 306 } 307 308 final CoexCoordinator coordinator = CoexCoordinator.getInstance(); 309 coordinator.onAuthenticationRejected(SystemClock.uptimeMillis(), this, lockoutMode, 310 new CoexCoordinator.Callback() { 311 @Override 312 public void sendAuthenticationResult(boolean addAuthTokenIfStrong) { 313 if (listener != null) { 314 try { 315 listener.onAuthenticationFailed(getSensorId()); 316 } catch (RemoteException e) { 317 Slog.e(TAG, "Unable to notify listener", e); 318 } 319 } 320 } 321 322 @Override 323 public void sendHapticFeedback() { 324 if (listener != null && mShouldVibrate) { 325 vibrateError(); 326 } 327 } 328 329 @Override 330 public void handleLifecycleAfterAuth() { 331 AuthenticationClient.this.handleLifecycleAfterAuth(false /* authenticated */); 332 } 333 334 @Override 335 public void sendAuthenticationCanceled() { 336 sendCancelOnly(listener); 337 } 338 }); 339 } 340 } 341 } 342 343 /** 344 * Only call this method on interfaces where lockout does not come from onError, I.E. the 345 * old HIDL implementation. 346 */ onLockoutTimed(long durationMillis)347 protected void onLockoutTimed(long durationMillis) { 348 final ClientMonitorCallbackConverter listener = getListener(); 349 final CoexCoordinator coordinator = CoexCoordinator.getInstance(); 350 coordinator.onAuthenticationError(this, BiometricConstants.BIOMETRIC_ERROR_LOCKOUT, 351 new CoexCoordinator.ErrorCallback() { 352 @Override 353 public void sendHapticFeedback() { 354 if (listener != null && mShouldVibrate) { 355 vibrateError(); 356 } 357 } 358 }); 359 } 360 361 /** 362 * Only call this method on interfaces where lockout does not come from onError, I.E. the 363 * old HIDL implementation. 364 */ onLockoutPermanent()365 protected void onLockoutPermanent() { 366 final ClientMonitorCallbackConverter listener = getListener(); 367 final CoexCoordinator coordinator = CoexCoordinator.getInstance(); 368 coordinator.onAuthenticationError(this, 369 BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT, 370 new CoexCoordinator.ErrorCallback() { 371 @Override 372 public void sendHapticFeedback() { 373 if (listener != null && mShouldVibrate) { 374 vibrateError(); 375 } 376 } 377 }); 378 } 379 sendCancelOnly(@ullable ClientMonitorCallbackConverter listener)380 private void sendCancelOnly(@Nullable ClientMonitorCallbackConverter listener) { 381 if (listener == null) { 382 Slog.e(TAG, "Unable to sendAuthenticationCanceled, listener null"); 383 return; 384 } 385 try { 386 listener.onError(getSensorId(), 387 getCookie(), 388 BiometricConstants.BIOMETRIC_ERROR_CANCELED, 389 0 /* vendorCode */); 390 } catch (RemoteException e) { 391 Slog.e(TAG, "Remote exception", e); 392 } 393 } 394 395 @Override onAcquired(int acquiredInfo, int vendorCode)396 public void onAcquired(int acquiredInfo, int vendorCode) { 397 super.onAcquired(acquiredInfo, vendorCode); 398 399 final @LockoutTracker.LockoutMode int lockoutMode = 400 mLockoutTracker.getLockoutModeForUser(getTargetUserId()); 401 if (lockoutMode == LockoutTracker.LOCKOUT_NONE) { 402 PerformanceTracker pt = PerformanceTracker.getInstanceForSensorId(getSensorId()); 403 pt.incrementAcquireForUser(getTargetUserId(), isCryptoOperation()); 404 } 405 } 406 407 @Override onError(@iometricConstants.Errors int errorCode, int vendorCode)408 public void onError(@BiometricConstants.Errors int errorCode, int vendorCode) { 409 super.onError(errorCode, vendorCode); 410 mState = STATE_STOPPED; 411 412 CoexCoordinator.getInstance().onAuthenticationError(this, errorCode, this::vibrateError); 413 } 414 415 /** 416 * Start authentication 417 */ 418 @Override start(@onNull ClientMonitorCallback callback)419 public void start(@NonNull ClientMonitorCallback callback) { 420 super.start(callback); 421 422 final @LockoutTracker.LockoutMode int lockoutMode = 423 mLockoutTracker.getLockoutModeForUser(getTargetUserId()); 424 if (lockoutMode != LockoutTracker.LOCKOUT_NONE) { 425 Slog.v(TAG, "In lockout mode(" + lockoutMode + ") ; disallowing authentication"); 426 int errorCode = lockoutMode == LockoutTracker.LOCKOUT_TIMED 427 ? BiometricConstants.BIOMETRIC_ERROR_LOCKOUT 428 : BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT; 429 onError(errorCode, 0 /* vendorCode */); 430 return; 431 } 432 433 if (mTaskStackListener != null) { 434 mActivityTaskManager.registerTaskStackListener(mTaskStackListener); 435 } 436 437 Slog.d(TAG, "Requesting auth for " + getOwnerString()); 438 439 mStartTimeMs = System.currentTimeMillis(); 440 mAuthAttempted = true; 441 startHalOperation(); 442 } 443 444 @Override cancel()445 public void cancel() { 446 super.cancel(); 447 if (mTaskStackListener != null) { 448 mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener); 449 } 450 } 451 452 /** 453 * Handles lifecycle, e.g. {@link BiometricScheduler}, 454 * {@link com.android.server.biometrics.sensors.BaseClientMonitor.Callback} after authentication 455 * results are known. Note that this happens asynchronously from (but shortly after) 456 * {@link #onAuthenticated(BiometricAuthenticator.Identifier, boolean, ArrayList)} and allows 457 * {@link CoexCoordinator} a chance to invoke/delay this event. 458 * @param authenticated 459 */ handleLifecycleAfterAuth(boolean authenticated)460 protected abstract void handleLifecycleAfterAuth(boolean authenticated); 461 462 /** 463 * @return true if a user was detected (i.e. face was found, fingerprint sensor was touched. 464 * etc) 465 */ wasUserDetected()466 public abstract boolean wasUserDetected(); 467 getState()468 public @State int getState() { 469 return mState; 470 } 471 472 /** 473 * @return true if the client supports bypass (e.g. passive auth such as face), and if it's 474 * enabled by the user. 475 */ isKeyguardBypassEnabled()476 public boolean isKeyguardBypassEnabled() { 477 return mIsKeyguardBypassEnabled; 478 } 479 480 @Override getProtoEnum()481 public int getProtoEnum() { 482 return BiometricsProto.CM_AUTHENTICATE; 483 } 484 485 @Override interruptsPrecedingClients()486 public boolean interruptsPrecedingClients() { 487 return true; 488 } 489 wasAuthAttempted()490 public boolean wasAuthAttempted() { 491 return mAuthAttempted; 492 } 493 494 /** If an auth attempt completed successfully. */ wasAuthSuccessful()495 public boolean wasAuthSuccessful() { 496 return mAuthSuccess; 497 } 498 getShowOverlayReason()499 protected int getShowOverlayReason() { 500 if (isKeyguard()) { 501 return BiometricOverlayConstants.REASON_AUTH_KEYGUARD; 502 } else if (isBiometricPrompt()) { 503 // BP reason always takes precedent over settings, since callers from within 504 // settings can always invoke BP. 505 return BiometricOverlayConstants.REASON_AUTH_BP; 506 } else if (isSettings()) { 507 // This is pretty much only for FingerprintManager#authenticate usage from 508 // FingerprintSettings. 509 return BiometricOverlayConstants.REASON_AUTH_SETTINGS; 510 } else { 511 return BiometricOverlayConstants.REASON_AUTH_OTHER; 512 } 513 } 514 } 515