Check foreground and appOps for BiometricPrompt#authenticate
Bug: 158481661
Test: Callers with incorrect opPackageName are no longer able to
request auth
Test: Callers not in foreground are no longer able to request auth
Test: atest AuthServiceTest
Change-Id: Ic26e47c11395a5fded1d2ab3e75466fdbd6c2f1b
Merged-In: Ic26e47c11395a5fded1d2ab3e75466fdbd6c2f1b
This commit is contained in:
@@ -28,6 +28,7 @@ import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRIN
|
||||
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_IRIS;
|
||||
import static android.hardware.biometrics.BiometricManager.Authenticators;
|
||||
|
||||
import android.app.AppOpsManager;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.hardware.biometrics.BiometricPrompt;
|
||||
@@ -128,6 +129,11 @@ public class AuthService extends SystemService {
|
||||
return IIrisService.Stub.asInterface(
|
||||
ServiceManager.getService(Context.IRIS_SERVICE));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public AppOpsManager getAppOps(Context context) {
|
||||
return context.getSystemService(AppOpsManager.class);
|
||||
}
|
||||
}
|
||||
|
||||
private final class AuthServiceImpl extends IAuthService.Stub {
|
||||
@@ -138,6 +144,8 @@ public class AuthService extends SystemService {
|
||||
|
||||
// Only allow internal clients to authenticate with a different userId.
|
||||
final int callingUserId = UserHandle.getCallingUserId();
|
||||
final int callingUid = Binder.getCallingUid();
|
||||
final int callingPid = Binder.getCallingPid();
|
||||
if (userId == callingUserId) {
|
||||
checkPermission();
|
||||
} else {
|
||||
@@ -146,6 +154,16 @@ public class AuthService extends SystemService {
|
||||
checkInternalPermission();
|
||||
}
|
||||
|
||||
if (!checkAppOps(callingUid, opPackageName, "authenticate()")) {
|
||||
Slog.e(TAG, "Denied by app ops: " + opPackageName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Utils.isForeground(callingUid, callingPid)) {
|
||||
Slog.e(TAG, "Caller is not foreground: " + opPackageName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (token == null || receiver == null || opPackageName == null || bundle == null) {
|
||||
Slog.e(TAG, "Unable to authenticate, one or more null arguments");
|
||||
return;
|
||||
@@ -163,8 +181,6 @@ public class AuthService extends SystemService {
|
||||
checkInternalPermission();
|
||||
}
|
||||
|
||||
final int callingUid = Binder.getCallingUid();
|
||||
final int callingPid = Binder.getCallingPid();
|
||||
final long identity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mBiometricService.authenticate(
|
||||
@@ -392,4 +408,9 @@ public class AuthService extends SystemService {
|
||||
"Must have USE_BIOMETRIC permission");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkAppOps(int uid, String opPackageName, String reason) {
|
||||
return mInjector.getAppOps(getContext()).noteOp(AppOpsManager.OP_USE_BIOMETRIC, uid,
|
||||
opPackageName, null /* attributionTag */, reason) == AppOpsManager.MODE_ALLOWED;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1019,7 +1019,7 @@ public abstract class BiometricServiceBase extends SystemService
|
||||
return false;
|
||||
}
|
||||
|
||||
if (requireForeground && !(isForegroundActivity(uid, pid) || isCurrentClient(
|
||||
if (requireForeground && !(Utils.isForeground(uid, pid) || isCurrentClient(
|
||||
opPackageName))) {
|
||||
Slog.w(getTag(), "Rejecting " + opPackageName + "; not in foreground");
|
||||
return false;
|
||||
@@ -1042,29 +1042,6 @@ public abstract class BiometricServiceBase extends SystemService
|
||||
return mKeyguardPackage.equals(clientPackage);
|
||||
}
|
||||
|
||||
private boolean isForegroundActivity(int uid, int pid) {
|
||||
try {
|
||||
final List<ActivityManager.RunningAppProcessInfo> procs =
|
||||
ActivityManager.getService().getRunningAppProcesses();
|
||||
if (procs == null) {
|
||||
Slog.e(getTag(), "Processes null, defaulting to true");
|
||||
return true;
|
||||
}
|
||||
|
||||
int N = procs.size();
|
||||
for (int i = 0; i < N; i++) {
|
||||
ActivityManager.RunningAppProcessInfo proc = procs.get(i);
|
||||
if (proc.pid == pid && proc.uid == uid
|
||||
&& proc.importance <= IMPORTANCE_FOREGROUND_SERVICE) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Slog.w(getTag(), "am.getRunningAppProcesses() failed");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the HAL to switch states to the new task. If there's already a current task,
|
||||
* it calls cancel() and sets mPendingClient to begin when the current task finishes
|
||||
|
||||
@@ -16,8 +16,10 @@
|
||||
|
||||
package com.android.server.biometrics;
|
||||
|
||||
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
|
||||
import static android.hardware.biometrics.BiometricManager.Authenticators;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import android.hardware.biometrics.BiometricConstants;
|
||||
import android.hardware.biometrics.BiometricManager;
|
||||
@@ -25,11 +27,16 @@ import android.hardware.biometrics.BiometricPrompt;
|
||||
import android.hardware.biometrics.BiometricPrompt.AuthenticationResultType;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.util.Slog;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class Utils {
|
||||
private static final String TAG = "BiometricUtils";
|
||||
|
||||
public static boolean isDebugEnabled(Context context, int targetUserId) {
|
||||
if (targetUserId == UserHandle.USER_NULL) {
|
||||
return false;
|
||||
@@ -256,4 +263,26 @@ public class Utils {
|
||||
throw new IllegalArgumentException("Unsupported dismissal reason: " + reason);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isForeground(int callingUid, int callingPid) {
|
||||
try {
|
||||
final List<ActivityManager.RunningAppProcessInfo> procs =
|
||||
ActivityManager.getService().getRunningAppProcesses();
|
||||
if (procs == null) {
|
||||
Slog.e(TAG, "No running app processes found, defaulting to true");
|
||||
return true;
|
||||
}
|
||||
|
||||
for (int i = 0; i < procs.size(); i++) {
|
||||
ActivityManager.RunningAppProcessInfo proc = procs.get(i);
|
||||
if (proc.pid == callingPid && proc.uid == callingUid
|
||||
&& proc.importance <= IMPORTANCE_FOREGROUND_SERVICE) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Slog.w(TAG, "am.getRunningAppProcesses() failed");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.AppOpsManager;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
|
||||
@@ -72,6 +73,8 @@ public class AuthServiceTest {
|
||||
IIrisService mIrisService;
|
||||
@Mock
|
||||
IFaceService mFaceService;
|
||||
@Mock
|
||||
AppOpsManager mAppOpsManager;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
@@ -90,6 +93,7 @@ public class AuthServiceTest {
|
||||
when(mInjector.getFingerprintService()).thenReturn(mFingerprintService);
|
||||
when(mInjector.getFaceService()).thenReturn(mFaceService);
|
||||
when(mInjector.getIrisService()).thenReturn(mIrisService);
|
||||
when(mInjector.getAppOps(any())).thenReturn(mAppOpsManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -137,7 +141,9 @@ public class AuthServiceTest {
|
||||
|
||||
// TODO(b/141025588): Check that an exception is thrown when the userId != callingUserId
|
||||
@Test
|
||||
public void testAuthenticate_callsBiometricServiceAuthenticate() throws Exception {
|
||||
public void testAuthenticate_appOpsOk_callsBiometricServiceAuthenticate() throws Exception {
|
||||
when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_USE_BIOMETRIC), anyInt(), any(), any(),
|
||||
any())).thenReturn(AppOpsManager.MODE_ALLOWED);
|
||||
mAuthService = new AuthService(mContext, mInjector);
|
||||
mAuthService.onStart();
|
||||
|
||||
@@ -166,6 +172,38 @@ public class AuthServiceTest {
|
||||
eq(UserHandle.getCallingUserId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAuthenticate_appOpsDenied_doesNotCallBiometricService() throws Exception {
|
||||
when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_USE_BIOMETRIC), anyInt(), any(), any(),
|
||||
any())).thenReturn(AppOpsManager.MODE_ERRORED);
|
||||
mAuthService = new AuthService(mContext, mInjector);
|
||||
mAuthService.onStart();
|
||||
|
||||
final Binder token = new Binder();
|
||||
final Bundle bundle = new Bundle();
|
||||
final long sessionId = 0;
|
||||
final int userId = 0;
|
||||
|
||||
mAuthService.mImpl.authenticate(
|
||||
token,
|
||||
sessionId,
|
||||
userId,
|
||||
mReceiver,
|
||||
TEST_OP_PACKAGE_NAME,
|
||||
bundle);
|
||||
waitForIdle();
|
||||
verify(mBiometricService, never()).authenticate(
|
||||
eq(token),
|
||||
eq(sessionId),
|
||||
eq(userId),
|
||||
eq(mReceiver),
|
||||
eq(TEST_OP_PACKAGE_NAME),
|
||||
eq(bundle),
|
||||
eq(Binder.getCallingUid()),
|
||||
eq(Binder.getCallingPid()),
|
||||
eq(UserHandle.getCallingUserId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCanAuthenticate_callsBiometricServiceCanAuthenticate() throws Exception {
|
||||
mAuthService = new AuthService(mContext, mInjector);
|
||||
|
||||
Reference in New Issue
Block a user