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

Commit c6d27462 authored by Becca Hughes's avatar Becca Hughes Committed by Android (Google) Code Review
Browse files

Merge "Add permission check in service"

parents 475712da cfc0ab8d
Loading
Loading
Loading
Loading
+16 −25
Original line number Diff line number Diff line
@@ -222,7 +222,7 @@ public final class CredentialManager {
     * @param callback the callback invoked when the request succeeds or fails
     * @hide
     */
    @RequiresPermission(android.Manifest.permission.LIST_ENABLED_CREDENTIAL_PROVIDERS)
    @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
    public void listEnabledProviders(
            @Nullable CancellationSignal cancellationSignal,
            @CallbackExecutor @NonNull Executor executor,
@@ -298,8 +298,7 @@ public final class CredentialManager {

    private boolean isServiceEnabled() {
        return DeviceConfig.getBoolean(
                DeviceConfig.NAMESPACE_CREDENTIAL, DEVICE_CONFIG_ENABLE_CREDENTIAL_MANAGER,
                true);
                DeviceConfig.NAMESPACE_CREDENTIAL, DEVICE_CONFIG_ENABLE_CREDENTIAL_MANAGER, true);
    }

    /**
@@ -313,22 +312,19 @@ public final class CredentialManager {
    }

    /**
     * Registers a {@link CredentialDescription} for an actively provisioned {@link Credential}
     * a CredentialProvider has. This registry will then be used to determine where to
     * fetch the requested {@link Credential} from. Not all credential types will be supported.
     * The distinction will be made by the JetPack layer. For the types that are supported,
     * JetPack will add a new key-value pair into {@link GetCredentialRequest}. These will not
     * be persistent on the device. The Credential Providers will need to call this API again
     * upon device reboot.
     * Registers a {@link CredentialDescription} for an actively provisioned {@link Credential} a
     * CredentialProvider has. This registry will then be used to determine where to fetch the
     * requested {@link Credential} from. Not all credential types will be supported. The
     * distinction will be made by the JetPack layer. For the types that are supported, JetPack will
     * add a new key-value pair into {@link GetCredentialRequest}. These will not be persistent on
     * the device. The Credential Providers will need to call this API again upon device reboot.
     *
     * @param request the request data
     *
     * @throws {@link UnsupportedOperationException} if the feature has not been enabled.
     * @throws {@link  com.android.server.credentials.NonCredentialProviderCallerException}
     * if the calling package name is not also listed as a Credential Provider.
     * @throws {@link com.android.server.credentials.NonCredentialProviderCallerException} if the
     *     calling package name is not also listed as a Credential Provider.
     * @throws {@link IllegalArgumentException} if the calling Credential Provider can not handle
     *     one or more of the Credential Types that are sent for registration.
     *
     */
    public void registerCredentialDescription(
            @NonNull RegisterCredentialDescriptionRequest request) {
@@ -346,16 +342,12 @@ public final class CredentialManager {
        }
    }


    /**
     * Unregisters a {@link CredentialDescription} for an actively provisioned {@link Credential}
     * that has been registered previously.
     *
     *
     * @param request the request data
     *
     * @throws {@link UnsupportedOperationException} if the feature has not been enabled.
     *
     */
    public void unregisterCredentialDescription(
            @NonNull UnregisterCredentialDescriptionRequest request) {
@@ -371,7 +363,6 @@ public final class CredentialManager {
        } catch (RemoteException e) {
            e.rethrowFromSystemServer();
        }

    }

    private static class GetCredentialTransport extends IGetCredentialCallback.Stub {
+147 −82
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.credentials;

import static android.content.Context.CREDENTIAL_SERVICE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;

import android.annotation.NonNull;
import android.annotation.UserIdInt;
@@ -81,17 +82,17 @@ public final class CredentialManagerService
    private static final String TAG = "CredManSysService";
    private static final String DEVICE_CONFIG_ENABLE_CREDENTIAL_DESC_API =
            "enable_credential_description_api";
    private static final String PERMISSION_DENIED_ERROR = "permission_denied";
    private static final String PERMISSION_DENIED_WRITE_SECURE_SETTINGS_ERROR =
            "Caller is missing WRITE_SECURE_SETTINGS permission";

    private final Context mContext;

    /**
     * Cache of system service list per user id.
     */
    /** Cache of system service list per user id. */
    @GuardedBy("mLock")
    private final SparseArray<List<CredentialManagerServiceImpl>> mSystemServicesCacheList =
            new SparseArray<>();


    public CredentialManagerService(@NonNull Context context) {
        super(
                context,
@@ -109,8 +110,10 @@ public final class CredentialManagerService
        List<CredentialManagerServiceImpl> services = new ArrayList<>();
        List<CredentialProviderInfo> credentialProviderInfos =
                CredentialProviderInfo.getAvailableSystemServices(mContext, resolvedUserId);
        credentialProviderInfos.forEach(info -> {
            services.add(new CredentialManagerServiceImpl(this, mLock, resolvedUserId, info));
        credentialProviderInfos.forEach(
                info -> {
                    services.add(
                            new CredentialManagerServiceImpl(this, mLock, resolvedUserId, info));
                });
        return services;
    }
@@ -174,10 +177,9 @@ public final class CredentialManagerService
        CredentialManagerServiceImpl serviceToBeRemoved = null;
        for (CredentialManagerServiceImpl service : services) {
            if (service != null) {
                CredentialProviderInfo credentialProviderInfo =
                        service.getCredentialProviderInfo();
                ComponentName componentName = credentialProviderInfo.getServiceInfo()
                        .getComponentName();
                CredentialProviderInfo credentialProviderInfo = service.getCredentialProviderInfo();
                ComponentName componentName =
                        credentialProviderInfo.getServiceInfo().getComponentName();
                if (packageName.equals(componentName.getPackageName())) {
                    serviceToBeRemoved = service;
                    removeServiceFromMultiModeSettings(componentName.flattenToString(), userId);
@@ -193,7 +195,6 @@ public final class CredentialManagerService
        // TODO("Iterate over system services and remove if needed")
    }


    @GuardedBy("mLock")
    private List<CredentialManagerServiceImpl> getOrConstructSystemServiceListLock(
            int resolvedUserId) {
@@ -205,6 +206,16 @@ public final class CredentialManagerService
        return services;
    }

    private boolean hasWriteSecureSettingsPermission() {
        final String permission = android.Manifest.permission.WRITE_SECURE_SETTINGS;
        final boolean result =
                mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED;
        if (!result) {
            Slog.e(TAG, "Caller does not have WRITE_SECURE_SETTINGS permission.");
        }
        return result;
    }

    private void runForUser(@NonNull final Consumer<CredentialManagerServiceImpl> c) {
        final int userId = UserHandle.getCallingUserId();
        final long origId = Binder.clearCallingIdentity();
@@ -222,8 +233,7 @@ public final class CredentialManagerService
    }

    @GuardedBy("mLock")
    private List<CredentialManagerServiceImpl> getAllCredentialProviderServicesLocked(
            int userId) {
    private List<CredentialManagerServiceImpl> getAllCredentialProviderServicesLocked(int userId) {
        List<CredentialManagerServiceImpl> concatenatedServices = new ArrayList<>();
        List<CredentialManagerServiceImpl> userConfigurableServices =
                getServiceListForUserLocked(userId);
@@ -243,11 +253,13 @@ public final class CredentialManagerService
    // to be guarded by 'service.mLock', which is the same as mLock.
    private List<ProviderSession> initiateProviderSessionsWithActiveContainers(
            GetRequestSession session,
            List<String> requestOptions, Set<String> activeCredentialContainers) {
            List<String> requestOptions,
            Set<String> activeCredentialContainers) {
        List<ProviderSession> providerSessions = new ArrayList<>();
        // Invoke all services of a user to initiate a provider session
        for (String packageName : activeCredentialContainers) {
            providerSessions.add(ProviderRegistryGetSession.createNewSession(
            providerSessions.add(
                    ProviderRegistryGetSession.createNewSession(
                            mContext,
                            UserHandle.getCallingUserId(),
                            session,
@@ -260,16 +272,17 @@ public final class CredentialManagerService
    @NonNull
    private Set<String> getFilteredResultFromRegistry(List<CredentialOption> options) {
        // Session for active/provisioned credential descriptions;
        CredentialDescriptionRegistry registry = CredentialDescriptionRegistry
                .forUser(UserHandle.getCallingUserId());
        CredentialDescriptionRegistry registry =
                CredentialDescriptionRegistry.forUser(UserHandle.getCallingUserId());

        // All requested credential descriptions based on the given request.
        Set<String> requestedCredentialDescriptions =
                options.stream().map(
                        getCredentialOption -> getCredentialOption
                options.stream()
                        .map(
                                getCredentialOption ->
                                        getCredentialOption
                                                .getCredentialRetrievalData()
                                        .getString(CredentialOption
                                                .FLATTENED_REQUEST))
                                                .getString(CredentialOption.FLATTENED_REQUEST))
                        .collect(Collectors.toSet());

        // All requested credential descriptions based on the given request.
@@ -306,9 +319,13 @@ public final class CredentialManagerService
    private CallingAppInfo constructCallingAppInfo(String packageName, int userId) {
        final PackageInfo packageInfo;
        try {
            packageInfo = getContext().getPackageManager().getPackageInfoAsUser(
            packageInfo =
                    getContext()
                            .getPackageManager()
                            .getPackageInfoAsUser(
                                    packageName,
                    PackageManager.PackageInfoFlags.of(PackageManager.GET_SIGNING_CERTIFICATES),
                                    PackageManager.PackageInfoFlags.of(
                                            PackageManager.GET_SIGNING_CERTIFICATES),
                                    userId);
        } catch (PackageManager.NameNotFoundException e) {
            Log.i(TAG, "Issue while retrieving signatureInfo : " + e.getMessage());
@@ -345,33 +362,46 @@ public final class CredentialManagerService
            if (isCredentialDescriptionApiEnabled()) {
                List<CredentialOption> optionsThatRequireActiveCredentials =
                        request.getCredentialOptions().stream()
                                .filter(getCredentialOption ->
                                        !TextUtils.isEmpty(getCredentialOption
                                                .getCredentialRetrievalData().getString(
                                .filter(
                                        getCredentialOption ->
                                                !TextUtils.isEmpty(
                                                        getCredentialOption
                                                                .getCredentialRetrievalData()
                                                                .getString(
                                                                        CredentialOption
                                                        .FLATTENED_REQUEST, null)))
                                                                                .FLATTENED_REQUEST,
                                                                        null)))
                                .toList();

                List<CredentialOption> optionsThatDoNotRequireActiveCredentials =
                        request.getCredentialOptions().stream()
                                .filter(getCredentialOption ->
                                        TextUtils.isEmpty(getCredentialOption
                                                .getCredentialRetrievalData().getString(
                                .filter(
                                        getCredentialOption ->
                                                TextUtils.isEmpty(
                                                        getCredentialOption
                                                                .getCredentialRetrievalData()
                                                                .getString(
                                                                        CredentialOption
                                                                .FLATTENED_REQUEST, null)))
                                                                                .FLATTENED_REQUEST,
                                                                        null)))
                                .toList();

                List<ProviderSession> sessionsWithoutRemoteService =
                        initiateProviderSessionsWithActiveContainers(session,
                                optionsThatRequireActiveCredentials
                                        .stream().map(getCredentialOption ->
                                                getCredentialOption.getCredentialRetrievalData()
                                                        .getString(CredentialOption
                        initiateProviderSessionsWithActiveContainers(
                                session,
                                optionsThatRequireActiveCredentials.stream()
                                        .map(
                                                getCredentialOption ->
                                                        getCredentialOption
                                                                .getCredentialRetrievalData()
                                                                .getString(
                                                                        CredentialOption
                                                                                .FLATTENED_REQUEST))
                                        .collect(Collectors.toList()),
                                getFilteredResultFromRegistry(optionsThatRequireActiveCredentials));

                List<ProviderSession> sessionsWithRemoteService = initiateProviderSessions(
                List<ProviderSession> sessionsWithRemoteService =
                        initiateProviderSessions(
                                session,
                                optionsThatDoNotRequireActiveCredentials.stream()
                                        .map(CredentialOption::getType)
@@ -394,7 +424,8 @@ public final class CredentialManagerService

            if (providerSessions.isEmpty()) {
                try {
                    callback.onError(GetCredentialException.TYPE_NO_CREDENTIAL,
                    callback.onError(
                            GetCredentialException.TYPE_NO_CREDENTIAL,
                            "No credentials available on this device.");
                } catch (RemoteException e) {
                    Log.i(
@@ -437,7 +468,8 @@ public final class CredentialManagerService

            if (providerSessions.isEmpty()) {
                try {
                    callback.onError(CreateCredentialException.TYPE_NO_CREDENTIAL,
                    callback.onError(
                            CreateCredentialException.TYPE_NO_CREDENTIAL,
                            "No credentials available on this device.");
                } catch (RemoteException e) {
                    Log.i(
@@ -460,6 +492,16 @@ public final class CredentialManagerService
            Log.i(TAG, "listEnabledProviders");
            ICancellationSignal cancelTransport = CancellationSignal.createTransport();

            if (!hasWriteSecureSettingsPermission()) {
                try {
                    callback.onError(
                            PERMISSION_DENIED_ERROR, PERMISSION_DENIED_WRITE_SECURE_SETTINGS_ERROR);
                } catch (RemoteException e) {
                    Log.e(TAG, "Issue with invoking response: " + e.getMessage());
                }
                return cancelTransport;
            }

            List<String> enabledProviders = new ArrayList<>();
            runForUser(
                    (service) -> {
@@ -482,6 +524,16 @@ public final class CredentialManagerService
                List<String> providers, int userId, ISetEnabledProvidersCallback callback) {
            Log.i(TAG, "setEnabledProviders");

            if (!hasWriteSecureSettingsPermission()) {
                try {
                    callback.onError(
                            PERMISSION_DENIED_ERROR, PERMISSION_DENIED_WRITE_SECURE_SETTINGS_ERROR);
                } catch (RemoteException e) {
                    Log.e(TAG, "Issue with invoking response: " + e.getMessage());
                }
                return;
            }

            userId =
                    ActivityManager.handleIncomingUser(
                            Binder.getCallingPid(),
@@ -550,8 +602,7 @@ public final class CredentialManagerService
            if (providerSessions.isEmpty()) {
                try {
                    // TODO("Replace with properly defined error type")
                    callback.onError("UNKNOWN", "No crdentials available on this "
                            + "device");
                    callback.onError("UNKNOWN", "No crdentials available on this " + "device");
                } catch (RemoteException e) {
                    Log.i(
                            TAG,
@@ -573,35 +624,46 @@ public final class CredentialManagerService
            Log.i(TAG, "registerCredentialDescription");

            List<CredentialProviderInfo> services =
                    CredentialProviderInfo.getAvailableServices(mContext,
                            UserHandle.getCallingUserId());
                    CredentialProviderInfo.getAvailableServices(
                            mContext, UserHandle.getCallingUserId());

            List<String> providers = services.stream()
                    .map(credentialProviderInfo
                            -> credentialProviderInfo.getServiceInfo().packageName).toList();
            List<String> providers =
                    services.stream()
                            .map(
                                    credentialProviderInfo ->
                                            credentialProviderInfo.getServiceInfo().packageName)
                            .toList();

            if (!providers.contains(callingPackage)) {
                throw new NonCredentialProviderCallerException(callingPackage);
            }

            List<CredentialProviderInfo> matchingService = services.stream().filter(
            List<CredentialProviderInfo> matchingService =
                    services.stream()
                            .filter(
                                    credentialProviderInfo ->
                            credentialProviderInfo.getServiceInfo()
                                    .packageName.equals(callingPackage)).toList();
                                            credentialProviderInfo
                                                    .getServiceInfo()
                                                    .packageName
                                                    .equals(callingPackage))
                            .toList();

            CredentialProviderInfo credentialProviderInfo = matchingService.get(0);

            Set<String> supportedTypes = request.getCredentialDescriptions()
                    .stream().map(CredentialDescription::getType).filter(
                            credentialProviderInfo::hasCapability).collect(Collectors.toSet());
            Set<String> supportedTypes =
                    request.getCredentialDescriptions().stream()
                            .map(CredentialDescription::getType)
                            .filter(credentialProviderInfo::hasCapability)
                            .collect(Collectors.toSet());

            if (supportedTypes.size() != request.getCredentialDescriptions().size()) {
                throw new IllegalArgumentException("CredentialProvider does not support one or more"
                throw new IllegalArgumentException(
                        "CredentialProvider does not support one or more"
                                + "of the registered types. Check your XML entry.");
            }

            CredentialDescriptionRegistry session = CredentialDescriptionRegistry
                    .forUser(UserHandle.getCallingUserId());
            CredentialDescriptionRegistry session =
                    CredentialDescriptionRegistry.forUser(UserHandle.getCallingUserId());

            session.executeRegisterRequest(request, callingPackage);
        }
@@ -614,19 +676,22 @@ public final class CredentialManagerService
            ICancellationSignal cancelTransport = CancellationSignal.createTransport();

            List<CredentialProviderInfo> services =
                    CredentialProviderInfo.getAvailableServices(mContext,
                            UserHandle.getCallingUserId());
                    CredentialProviderInfo.getAvailableServices(
                            mContext, UserHandle.getCallingUserId());

            List<String> providers = services.stream()
                    .map(credentialProviderInfo
                            -> credentialProviderInfo.getServiceInfo().packageName).toList();
            List<String> providers =
                    services.stream()
                            .map(
                                    credentialProviderInfo ->
                                            credentialProviderInfo.getServiceInfo().packageName)
                            .toList();

            if (!providers.contains(callingPackage)) {
                throw new NonCredentialProviderCallerException(callingPackage);
            }

            CredentialDescriptionRegistry session = CredentialDescriptionRegistry
                    .forUser(UserHandle.getCallingUserId());
            CredentialDescriptionRegistry session =
                    CredentialDescriptionRegistry.forUser(UserHandle.getCallingUserId());

            session.executeUnregisterRequest(request, callingPackage);
        }