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
This commit is contained in:
Kevin Chyn
2020-07-15 14:31:13 -07:00
parent 1f9cce1b37
commit 613ee92b15
4 changed files with 88 additions and 27 deletions

View File

@@ -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.IAuthService;
@@ -127,6 +128,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 {
@@ -137,6 +143,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 {
@@ -145,6 +153,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 || promptInfo == null) {
Slog.e(TAG, "Unable to authenticate, one or more null arguments");
return;
@@ -155,8 +173,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(
@@ -370,4 +386,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;
}
}

View File

@@ -17,6 +17,7 @@
package com.android.server.biometrics;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
import static android.hardware.biometrics.BiometricManager.Authenticators;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
@@ -60,6 +61,8 @@ import com.android.internal.R;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.biometrics.sensors.ClientMonitor;
import java.util.List;
public class Utils {
private static final String TAG = "BiometricUtils";
@@ -419,4 +422,26 @@ public class Utils {
Slog.d(TAG, "isEncrypted: " + isEncrypted + " isLockdown: " + isLockDown);
return isEncrypted || isLockDown;
}
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;
}
}

View File

@@ -415,7 +415,7 @@ public class FingerprintService extends SystemService {
Slog.w(TAG, "Rejecting " + opPackageName + "; permission denied");
return false;
}
if (requireForeground && !(isForegroundActivity(uid, pid))) {
if (requireForeground && !Utils.isForeground(uid, pid)) {
Slog.w(TAG, "Rejecting " + opPackageName + "; not in foreground");
return false;
}
@@ -434,28 +434,5 @@ public class FingerprintService extends SystemService {
return appOpsOk;
}
private boolean isForegroundActivity(int uid, int pid) {
try {
final List<ActivityManager.RunningAppProcessInfo> procs =
ActivityManager.getService().getRunningAppProcesses();
if (procs == null) {
Slog.e(TAG, "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(TAG, "am.getRunningAppProcesses() failed");
}
return false;
}
private native NativeHandle convertSurfaceToNativeHandle(Surface surface);
}

View File

@@ -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;
@@ -75,6 +76,8 @@ public class AuthServiceTest {
IIrisService mIrisService;
@Mock
IFaceService mFaceService;
@Mock
AppOpsManager mAppOpsManager;
@Before
public void setUp() {
@@ -93,6 +96,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
@@ -140,7 +144,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();
@@ -169,6 +175,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 PromptInfo promptInfo = new PromptInfo();
final long sessionId = 0;
final int userId = 0;
mAuthService.mImpl.authenticate(
token,
sessionId,
userId,
mReceiver,
TEST_OP_PACKAGE_NAME,
promptInfo);
waitForIdle();
verify(mBiometricService, never()).authenticate(
eq(token),
eq(sessionId),
eq(userId),
eq(mReceiver),
eq(TEST_OP_PACKAGE_NAME),
eq(promptInfo),
eq(Binder.getCallingUid()),
eq(Binder.getCallingPid()),
eq(UserHandle.getCallingUserId()));
}
@Test
public void testCanAuthenticate_callsBiometricServiceCanAuthenticate() throws Exception {
mAuthService = new AuthService(mContext, mInjector);