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

Commit a6a449da authored by Willie Koomson's avatar Willie Koomson
Browse files

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
parent b287108b
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -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
  }
}
+41 −0
Original line number Diff line number Diff line
@@ -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);

@@ -2566,6 +2574,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.
     *