Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions services/core/java/com/android/server/pm/ext/GmsCoreUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.android.server.pm.ext;

import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.ext.PackageId;
import android.util.Slog;

public class GmsCoreUtils {
private static final String TAG = "GmsCoreUtils";

public static boolean isGmsRemoteCredentialsServiceComponent(ComponentName componentName) {
// FIDO2 is from "remote devices", so it's handed by the RemoteService
return componentName != null
&& PackageId.GMS_CORE_NAME.equals(componentName.getPackageName())
&& "com.google.android.gms.auth.api.credentials.credman.service.RemoteService"
.equals(componentName.getClassName());
}

public static boolean shouldBypassRemoteEntryCredentialProviderRestrictions(
Context context, ComponentName remoteCredentialProvider, @UserIdInt int userId) {
if (!isGmsRemoteCredentialsServiceComponent(remoteCredentialProvider)) {
return false;
}

// Ensure GMS is installed for the user that the credential request is for
final ApplicationInfo gmsAppInfo;
try {
gmsAppInfo = context.getPackageManager().getApplicationInfoAsUser(
remoteCredentialProvider.getPackageName(), 0, userId);
} catch (PackageManager.NameNotFoundException e) {
Slog.w(TAG, "failed to resolve " + remoteCredentialProvider, e);
return false;
}
// getApplicationInfoAsUser is @NonNull, but just mimicking upstream code from
// ProviderSession
if (gmsAppInfo != null) {
final int packageId = gmsAppInfo.ext().getPackageId();
// ensure it's from verified GMS core
if (packageId == PackageId.GMS_CORE) {
// Note: Not checking for Manifest.permission.PROVIDE_REMOTE_CREDENTIALS
// (signature|privileged|role); it seems FIDO2 works fine without granting that
// permission
return true;
} else {
Slog.w(TAG,"bad gmsAppInfo packageId " + packageId + " for "
+ remoteCredentialProvider);
}
}

return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
import android.util.Pair;
import android.util.Slog;

import com.android.server.pm.ext.GmsCoreUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
Expand Down Expand Up @@ -94,7 +96,7 @@ public static ProviderGetSession createNewSession(
android.credentials.GetCredentialRequest filteredRequest =
filterOptions(providerInfo.getCapabilities(),
getRequestSession.mClientRequest,
providerInfo, getRequestSession.mHybridService);
providerInfo, getRequestSession.mHybridService, context, userId);
if (filteredRequest != null) {
Map<String, CredentialOption> beginGetOptionToCredentialOptionMap =
new HashMap<>();
Expand Down Expand Up @@ -130,7 +132,7 @@ public static ProviderGetSession createNewSession(
android.credentials.GetCredentialRequest filteredRequest =
filterOptions(providerInfo.getCapabilities(),
getRequestSession.mClientRequest,
providerInfo, getRequestSession.mHybridService);
providerInfo, getRequestSession.mHybridService, context, userId);
if (filteredRequest != null) {
Map<String, CredentialOption> beginGetOptionToCredentialOptionMap =
new HashMap<>();
Expand Down Expand Up @@ -181,7 +183,9 @@ private static android.credentials.GetCredentialRequest filterOptions(
List<String> providerCapabilities,
android.credentials.GetCredentialRequest clientRequest,
CredentialProviderInfo info,
String hybridService) {
String hybridService,
Context context,
@UserIdInt int userId) {
Slog.i(TAG, "Filtering request options for: " + info.getComponentName());
if (android.credentials.flags.Flags.hybridFilterOptFixEnabled()) {
ComponentName hybridComponentName = ComponentName.unflattenFromString(hybridService);
Expand All @@ -190,6 +194,13 @@ private static android.credentials.GetCredentialRequest filterOptions(
Slog.i(TAG, "Skipping filtering of options for hybrid service");
return clientRequest;
}
// Filter options are skipped on stock OS, since hybridComponentName corresponds to
// what's set in OEM config (GMS's RemoteService on stock Pixel)
if (GmsCoreUtils.shouldBypassRemoteEntryCredentialProviderRestrictions(
context, info.getComponentName(), userId)) {
Slog.i(TAG, "Skipping filtering of options for hybrid service due to GMS core");
return clientRequest;
}
Slog.w(TAG, "Could not parse hybrid service while filtering options");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import android.util.Slog;

import com.android.server.credentials.metrics.ProviderSessionMetric;
import com.android.server.pm.ext.GmsCoreUtils;

import java.util.UUID;

Expand Down Expand Up @@ -253,6 +254,15 @@ protected R getProviderResponse() {

protected boolean enforceRemoteEntryRestrictions(
@Nullable ComponentName expectedRemoteEntryProviderService) {
if (GmsCoreUtils.shouldBypassRemoteEntryCredentialProviderRestrictions(
mContext, mComponentName, mUserId)) {
// Bypassing a frameworks OEM config check and the permission grant check for
// Manifest.permission.PROVIDE_REMOTE_CREDENTIALS. GMS doesn't seem to require
// Manifest.permission.PROVIDE_REMOTE_CREDENTIALS for FIDO2 (NFC and USB) to work.
Slog.w(TAG, "Remote entry accepted from GmsCoreUtils bypass");
return true;
}

// Check if the service is the one set by the OEM. If not silently reject this entry
if (!mComponentName.equals(expectedRemoteEntryProviderService)) {
Slog.w(TAG, "Remote entry being dropped as it is not from the service "
Expand Down