DO NOT MERGE Check fingerprint client against top activity in auth callback
Due to a race condition with activity task stack broadcasts, it's currently possible for fingerprint authentication to succeed for a non-top activity. This means, for example, that a malicious overlay could be drawn in order to mislead the user about what they are authenticating for. This commit addresses the issue by adding a check to the fingerprint authentication client interface that ensures the authenticating activity is on top at the time of authentication. Otherwise, the pending authentication will fail, as if an incorrect biometric been presented. Test: Follow steps from b/159249069: 1. Install com.pro100svitlo.fingerprintauthdemo from the Play store. 2. Install the PoC attack app from b/159249069. 3. Start the PoC attack app and press the "Launch PoC attack" button. 4. Use fingerprint to authenticate while the overlay is showing. Before: Authentication succeeds, and a new activity is launched. After: Authentication fails, and no new activity is launched. Bug: 159249069 Change-Id: Ic482cbfdd810e9c8b60a5093fd8d615ee320a6a6 Merged-In: I0707c3f55eaf2a69c6625a3ceb3b5626b3676b26 Merged-In: If5cdf8ffaf3aa7d8a1ac81272e3bfb2cc7cdddf1 Merged-In: Iee6af379515385777984da55048c1efd9339ed88 Merged-In: I9b242a9fee0acbfb430875061e2d809c00fe4b97 Merged-In: I1241a12eafa0bdbac59a8ddd4cf6a0637d467b19 Merged-In: Ie5a0f8c3e9b92d348a78678a6ed192d440c45ffc Merged-In: I289d67e5c7055ed60f7a96725c523d07cd047b23
This commit is contained in:
@@ -16,18 +16,29 @@
|
|||||||
|
|
||||||
package com.android.server.fingerprint;
|
package com.android.server.fingerprint;
|
||||||
|
|
||||||
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
|
import static android.Manifest.permission.USE_FINGERPRINT;
|
||||||
import com.android.internal.logging.MetricsLogger;
|
|
||||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
|
||||||
|
|
||||||
|
import android.app.ActivityManager;
|
||||||
|
import android.app.IActivityManager;
|
||||||
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
|
||||||
import android.hardware.fingerprint.Fingerprint;
|
import android.hardware.fingerprint.Fingerprint;
|
||||||
import android.hardware.fingerprint.FingerprintManager;
|
import android.hardware.fingerprint.FingerprintManager;
|
||||||
import android.hardware.fingerprint.IFingerprintServiceReceiver;
|
import android.hardware.fingerprint.IFingerprintServiceReceiver;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
|
import android.util.EventLog;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
|
|
||||||
|
import com.android.internal.R;
|
||||||
|
import com.android.internal.logging.MetricsLogger;
|
||||||
|
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class to keep track of the authentication state for a given client.
|
* A class to keep track of the authentication state for a given client.
|
||||||
*/
|
*/
|
||||||
@@ -53,6 +64,56 @@ public abstract class AuthenticationClient extends ClientMonitor {
|
|||||||
boolean result = false;
|
boolean result = false;
|
||||||
boolean authenticated = fingerId != 0;
|
boolean authenticated = fingerId != 0;
|
||||||
|
|
||||||
|
// Ensure authentication only succeeds if the client activity is on top or is keyguard.
|
||||||
|
boolean isBackgroundAuth = false;
|
||||||
|
if (authenticated && !isKeyguard(getContext(), getOwnerString())) {
|
||||||
|
final ActivityManager activityManager =
|
||||||
|
(ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE);
|
||||||
|
final IActivityManager activityManagerService = activityManager != null
|
||||||
|
? activityManager.getService()
|
||||||
|
: null;
|
||||||
|
if (activityManagerService == null) {
|
||||||
|
Slog.e(TAG, "Unable to get activity manager service");
|
||||||
|
isBackgroundAuth = true;
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
final List<ActivityManager.RunningTaskInfo> tasks =
|
||||||
|
activityManagerService.getTasks(1, 0 /* flags */);
|
||||||
|
if (tasks == null || tasks.isEmpty()) {
|
||||||
|
Slog.e(TAG, "No running tasks reported");
|
||||||
|
isBackgroundAuth = true;
|
||||||
|
} else {
|
||||||
|
final ComponentName topActivity = tasks.get(0).topActivity;
|
||||||
|
if (topActivity == null) {
|
||||||
|
Slog.e(TAG, "Unable to get top activity");
|
||||||
|
isBackgroundAuth = true;
|
||||||
|
} else {
|
||||||
|
final String topPackage = topActivity.getPackageName();
|
||||||
|
if (!topPackage.contentEquals(getOwnerString())) {
|
||||||
|
Slog.e(TAG, "Background authentication detected, top: " + topPackage
|
||||||
|
+ ", client: " + this);
|
||||||
|
isBackgroundAuth = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Slog.e(TAG, "Unable to get running tasks", e);
|
||||||
|
isBackgroundAuth = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fail authentication if we can't confirm the client activity is on top.
|
||||||
|
if (isBackgroundAuth) {
|
||||||
|
Slog.e(TAG, "Failing possible background authentication");
|
||||||
|
authenticated = false;
|
||||||
|
|
||||||
|
// SafetyNet logging for exploitation attempts of b/159249069.
|
||||||
|
final ApplicationInfo appInfo = getContext().getApplicationInfo();
|
||||||
|
EventLog.writeEvent(0x534e4554, "159249069", appInfo != null ? appInfo.uid : -1,
|
||||||
|
"Attempted background authentication");
|
||||||
|
}
|
||||||
|
|
||||||
IFingerprintServiceReceiver receiver = getReceiver();
|
IFingerprintServiceReceiver receiver = getReceiver();
|
||||||
if (receiver != null) {
|
if (receiver != null) {
|
||||||
try {
|
try {
|
||||||
@@ -61,6 +122,14 @@ public abstract class AuthenticationClient extends ClientMonitor {
|
|||||||
if (!authenticated) {
|
if (!authenticated) {
|
||||||
receiver.onAuthenticationFailed(getHalDeviceId());
|
receiver.onAuthenticationFailed(getHalDeviceId());
|
||||||
} else {
|
} else {
|
||||||
|
// SafetyNet logging for b/159249069 if constraint is violated.
|
||||||
|
if (isBackgroundAuth) {
|
||||||
|
final ApplicationInfo appInfo = getContext().getApplicationInfo();
|
||||||
|
EventLog.writeEvent(0x534e4554, "159249069",
|
||||||
|
appInfo != null ? appInfo.uid : -1,
|
||||||
|
"Successful background authentication! Receiver notified");
|
||||||
|
}
|
||||||
|
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.v(TAG, "onAuthenticated(owner=" + getOwnerString()
|
Slog.v(TAG, "onAuthenticated(owner=" + getOwnerString()
|
||||||
+ ", id=" + fingerId + ", gp=" + groupId + ")");
|
+ ", id=" + fingerId + ", gp=" + groupId + ")");
|
||||||
@@ -98,6 +167,14 @@ public abstract class AuthenticationClient extends ClientMonitor {
|
|||||||
}
|
}
|
||||||
result |= lockoutMode != LOCKOUT_NONE; // in a lockout mode
|
result |= lockoutMode != LOCKOUT_NONE; // in a lockout mode
|
||||||
} else {
|
} else {
|
||||||
|
// SafetyNet logging for b/159249069 if constraint is violated.
|
||||||
|
if (isBackgroundAuth) {
|
||||||
|
final ApplicationInfo appInfo = getContext().getApplicationInfo();
|
||||||
|
EventLog.writeEvent(0x534e4554, "159249069",
|
||||||
|
appInfo != null ? appInfo.uid : -1,
|
||||||
|
"Successful background authentication! Lockout reset");
|
||||||
|
}
|
||||||
|
|
||||||
if (receiver != null) {
|
if (receiver != null) {
|
||||||
FingerprintUtils.vibrateFingerprintSuccess(getContext());
|
FingerprintUtils.vibrateFingerprintSuccess(getContext());
|
||||||
}
|
}
|
||||||
@@ -107,6 +184,17 @@ public abstract class AuthenticationClient extends ClientMonitor {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean isKeyguard(Context context, String clientPackage) {
|
||||||
|
final boolean hasPermission = context.checkCallingOrSelfPermission(USE_FINGERPRINT)
|
||||||
|
== PackageManager.PERMISSION_GRANTED;
|
||||||
|
|
||||||
|
final ComponentName keyguardComponent = ComponentName.unflattenFromString(
|
||||||
|
context.getResources().getString(R.string.config_keyguardComponent));
|
||||||
|
final String keyguardPackage = keyguardComponent != null
|
||||||
|
? keyguardComponent.getPackageName() : null;
|
||||||
|
return hasPermission && keyguardPackage != null && keyguardPackage.equals(clientPackage);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start authentication
|
* Start authentication
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user