diff options
| author | 2024-10-29 21:55:42 +0000 | |
|---|---|---|
| committer | 2024-11-12 22:12:09 +0000 | |
| commit | a6a449da07ee1bb28cb76d472b0fd33b3fa1fa17 (patch) | |
| tree | 37932fbcd18acca1eb9699f405abc2bd677be243 | |
| parent | b287108b9522991fc166f7bb33b156a13215ec21 (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.aconfig | 10 | ||||
| -rw-r--r-- | services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java | 41 |
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, |