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