Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7b1c1db8 authored by Kevin Chyn's avatar Kevin Chyn
Browse files

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
parent 4c17f946
Loading
Loading
Loading
Loading
+23 −2
Original line number Diff line number Diff line
@@ -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;
    }
}
+1 −24
Original line number Diff line number Diff line
@@ -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
+29 −0
Original line number Diff line number Diff line
@@ -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;
    }
}
+39 −1
Original line number Diff line number Diff line
@@ -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);