Merge "Add permission check in service"

This commit is contained in:
Becca Hughes
2023-02-06 22:47:03 +00:00
committed by Android (Google) Code Review
2 changed files with 163 additions and 107 deletions

View File

@@ -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,
@@ -286,7 +286,7 @@ public final class CredentialManager {
*/
public static boolean isServiceEnabled(Context context) {
if (context == null) {
return false;
return false;
}
CredentialManager credentialManager =
(CredentialManager) context.getSystemService(Context.CREDENTIAL_SERVICE);
@@ -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 IllegalArgumentException} if the calling Credential Provider can not handle
* one or more of the Credential Types that are sent for registration.
*
* @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 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}
* 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.
*
* @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 {

View File

@@ -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,9 +110,11 @@ 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,16 +253,18 @@ 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(
mContext,
UserHandle.getCallingUserId(),
session,
packageName,
requestOptions));
for (String packageName : activeCredentialContainers) {
providerSessions.add(
ProviderRegistryGetSession.createNewSession(
mContext,
UserHandle.getCallingUserId(),
session,
packageName,
requestOptions));
}
return providerSessions;
}
@@ -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
.getCredentialRetrievalData()
.getString(CredentialOption
.FLATTENED_REQUEST))
options.stream()
.map(
getCredentialOption ->
getCredentialOption
.getCredentialRetrievalData()
.getString(CredentialOption.FLATTENED_REQUEST))
.collect(Collectors.toSet());
// All requested credential descriptions based on the given request.
@@ -306,10 +319,14 @@ public final class CredentialManagerService
private CallingAppInfo constructCallingAppInfo(String packageName, int userId) {
final PackageInfo packageInfo;
try {
packageInfo = getContext().getPackageManager().getPackageInfoAsUser(
packageName,
PackageManager.PackageInfoFlags.of(PackageManager.GET_SIGNING_CERTIFICATES),
userId);
packageInfo =
getContext()
.getPackageManager()
.getPackageInfoAsUser(
packageName,
PackageManager.PackageInfoFlags.of(
PackageManager.GET_SIGNING_CERTIFICATES),
userId);
} catch (PackageManager.NameNotFoundException e) {
Log.i(TAG, "Issue while retrieving signatureInfo : " + e.getMessage());
return new CallingAppInfo(packageName, null);
@@ -345,37 +362,50 @@ public final class CredentialManagerService
if (isCredentialDescriptionApiEnabled()) {
List<CredentialOption> optionsThatRequireActiveCredentials =
request.getCredentialOptions().stream()
.filter(getCredentialOption ->
!TextUtils.isEmpty(getCredentialOption
.getCredentialRetrievalData().getString(
CredentialOption
.FLATTENED_REQUEST, null)))
.filter(
getCredentialOption ->
!TextUtils.isEmpty(
getCredentialOption
.getCredentialRetrievalData()
.getString(
CredentialOption
.FLATTENED_REQUEST,
null)))
.toList();
List<CredentialOption> optionsThatDoNotRequireActiveCredentials =
request.getCredentialOptions().stream()
.filter(getCredentialOption ->
TextUtils.isEmpty(getCredentialOption
.getCredentialRetrievalData().getString(
CredentialOption
.FLATTENED_REQUEST, null)))
.filter(
getCredentialOption ->
TextUtils.isEmpty(
getCredentialOption
.getCredentialRetrievalData()
.getString(
CredentialOption
.FLATTENED_REQUEST,
null)))
.toList();
List<ProviderSession> sessionsWithoutRemoteService =
initiateProviderSessionsWithActiveContainers(session,
optionsThatRequireActiveCredentials
.stream().map(getCredentialOption ->
getCredentialOption.getCredentialRetrievalData()
.getString(CredentialOption
.FLATTENED_REQUEST))
initiateProviderSessionsWithActiveContainers(
session,
optionsThatRequireActiveCredentials.stream()
.map(
getCredentialOption ->
getCredentialOption
.getCredentialRetrievalData()
.getString(
CredentialOption
.FLATTENED_REQUEST))
.collect(Collectors.toList()),
getFilteredResultFromRegistry(optionsThatRequireActiveCredentials));
List<ProviderSession> sessionsWithRemoteService = initiateProviderSessions(
session,
optionsThatDoNotRequireActiveCredentials.stream()
.map(CredentialOption::getType)
.collect(Collectors.toList()));
List<ProviderSession> sessionsWithRemoteService =
initiateProviderSessions(
session,
optionsThatDoNotRequireActiveCredentials.stream()
.map(CredentialOption::getType)
.collect(Collectors.toList()));
Set<ProviderSession> all = new LinkedHashSet<>();
all.addAll(sessionsWithRemoteService);
@@ -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,
@@ -569,39 +620,50 @@ public final class CredentialManagerService
@Override
public void registerCredentialDescription(
RegisterCredentialDescriptionRequest request, String callingPackage)
throws IllegalArgumentException , NonCredentialProviderCallerException {
throws IllegalArgumentException, NonCredentialProviderCallerException {
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(
credentialProviderInfo ->
credentialProviderInfo.getServiceInfo()
.packageName.equals(callingPackage)).toList();
List<CredentialProviderInfo> matchingService =
services.stream()
.filter(
credentialProviderInfo ->
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"
+ "of the registered types. Check your XML entry.");
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);
}