summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Willie Koomson <wvk@google.com> 2024-10-29 21:55:42 +0000
committer Willie Koomson <wvk@google.com> 2024-11-12 22:12:09 +0000
commita6a449da07ee1bb28cb76d472b0fd33b3fa1fa17 (patch)
tree37932fbcd18acca1eb9699f405abc2bd677be243
parentb287108b9522991fc166f7bb33b156a13215ec21 (diff)
Check that widget provider can access RemoteViews URIs
This change responds to a security issue with privileged hosts with INTERACT_ACROSS_USERS permissions displaying URIs from non-privileged providers that the provider should not have access to. Bug: 369137473 Test: Use playground app from bug Test: atest "CtsAppWidgetTestCases:AppWidgetTest#testCheckRemoteViewsUri" Flag: android.appwidget.flags.check_remote_views_uri_permission Change-Id: I9c3f3b0799d2ebdd7a63a54bd2818a6bf37b6e60
-rw-r--r--core/java/android/appwidget/flags.aconfig10
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java41
2 files changed, 51 insertions, 0 deletions
diff --git a/core/java/android/appwidget/flags.aconfig b/core/java/android/appwidget/flags.aconfig
index ce515761551c..fb33348d9c26 100644
--- a/core/java/android/appwidget/flags.aconfig
+++ b/core/java/android/appwidget/flags.aconfig
@@ -92,3 +92,13 @@ flag {
is_exported: true
is_fixed_read_only: true
}
+
+flag {
+ name: "check_remote_views_uri_permission"
+ namespace: "app_widgets"
+ description: "Check that the widget provider has permissions to access any URIs within its RemoteViews"
+ bug: "369137473"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 0bd879b7568d..4d2f087497f9 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -16,6 +16,7 @@
package com.android.server.appwidget;
+import static android.appwidget.flags.Flags.checkRemoteViewsUriPermission;
import static android.appwidget.flags.Flags.remoteAdapterConversion;
import static android.appwidget.flags.Flags.remoteViewsProto;
import static android.appwidget.flags.Flags.removeAppWidgetServiceIoFromCriticalPath;
@@ -62,6 +63,7 @@ import android.appwidget.AppWidgetProviderInfo;
import android.appwidget.PendingHostUpdate;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.Intent.FilterComparison;
@@ -150,6 +152,8 @@ import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.WidgetBackupProvider;
+import com.android.server.uri.GrantUri;
+import com.android.server.uri.UriGrantsManagerInternal;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -2547,6 +2551,10 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
// Make sure the package runs under the caller uid.
mSecurityPolicy.enforceCallFromPackage(callingPackage);
+ // Make sure RemoteViews do not contain URIs that the caller cannot access.
+ if (checkRemoteViewsUriPermission()) {
+ checkRemoteViewsUris(views);
+ }
synchronized (mLock) {
ensureGroupStateLoadedLocked(userId);
@@ -2567,6 +2575,39 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
}
/**
+ * Checks that all of the Uris in the given RemoteViews are accessible to the caller.
+ */
+ private void checkRemoteViewsUris(RemoteViews views) {
+ UriGrantsManagerInternal uriGrantsManager = LocalServices.getService(
+ UriGrantsManagerInternal.class);
+ int callingUid = Binder.getCallingUid();
+ int callingUser = UserHandle.getCallingUserId();
+ views.visitUris(uri -> {
+ switch (uri.getScheme()) {
+ // Check that content:// URIs are accessible to the caller.
+ case ContentResolver.SCHEME_CONTENT:
+ boolean canAccessUri = uriGrantsManager.checkUriPermission(
+ GrantUri.resolve(callingUser, uri,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION), callingUid,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION,
+ /* isFullAccessForContentUri= */ true);
+ if (!canAccessUri) {
+ throw new SecurityException(
+ "Provider uid " + callingUid + " cannot access URI " + uri);
+ }
+ break;
+ // android.resource:// URIs are always allowed.
+ case ContentResolver.SCHEME_ANDROID_RESOURCE:
+ break;
+ // file:// and any other schemes are disallowed.
+ case ContentResolver.SCHEME_FILE:
+ default:
+ throw new SecurityException("Disallowed URI " + uri + " in RemoteViews.");
+ }
+ });
+ }
+
+ /**
* Increment the counter of widget ids and return the new id.
*
* Typically called by {@link #allocateAppWidgetId} when a instance of widget is created,