From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Tim Yu Date: Tue, 20 Jun 2023 21:24:36 +0000 Subject: [PATCH] Verify URI Permissions in Autofill RemoteViews Check permissions of URI inside of FillResponse's RemoteViews. If the current user does not have the required permissions to view the URI, the RemoteView is dropped from displaying. This fixes a security spill in which a user can view content of another user through a malicious Autofill provider. Bug: 283137865 Fixes: b/283264674 b/281666022 b/281665050 b/281848557 b/281533566 b/281534749 b/283101289 Test: Verified by POC app attached in bugs Test: atest CtsAutoFillServiceTestCases (added new tests) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:26beceb9a252a50374d056b162fa7e8ea55051b3) Merged-In: I6f4d2a35e89bbed7bd9e07bf5cd3e2d68b20af9a Change-Id: I6f4d2a35e89bbed7bd9e07bf5cd3e2d68b20af9a --- .../com/android/server/autofill/Helper.java | 43 +++++++++++++++++++ .../android/server/autofill/ui/FillUi.java | 11 +++-- .../android/server/autofill/ui/SaveUi.java | 2 +- 3 files changed, 52 insertions(+), 4 deletions(-) diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java index f14c8f1aa7f6..a50d87ac81e4 100644 --- a/services/autofill/java/com/android/server/autofill/Helper.java +++ b/services/autofill/java/com/android/server/autofill/Helper.java @@ -18,6 +18,8 @@ package com.android.server.autofill; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.app.ActivityManager; import android.app.assist.AssistStructure; import android.app.assist.AssistStructure.ViewNode; import android.content.ComponentName; @@ -29,13 +31,16 @@ import android.util.Slog; import android.view.WindowManager; import android.view.autofill.AutofillId; import android.view.autofill.AutofillValue; +import android.widget.RemoteViews; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.ArrayUtils; import java.io.PrintWriter; +import java.util.Arrays; import java.util.ArrayList; import java.util.LinkedList; +import java.util.concurrent.atomic.AtomicBoolean; public final class Helper { @@ -79,6 +84,44 @@ public final class Helper { throw new UnsupportedOperationException("contains static members only"); } + private static boolean checkRemoteViewUriPermissions( + @UserIdInt int userId, @NonNull RemoteViews rView) { + final AtomicBoolean permissionsOk = new AtomicBoolean(true); + + rView.visitUris(uri -> { + int uriOwnerId = android.content.ContentProvider.getUserIdFromUri(uri); + boolean allowed = uriOwnerId == userId; + permissionsOk.set(allowed && permissionsOk.get()); + }); + + return permissionsOk.get(); + } + + /** + * Checks the URI permissions of the remote view, + * to see if the current userId is able to access it. + * + * Returns the RemoteView that is passed if user is able, null otherwise. + * + * TODO: instead of returning a null remoteview when + * the current userId cannot access an URI, + * return a new RemoteView with the URI removed. + */ + public static @Nullable RemoteViews sanitizeRemoteView(RemoteViews rView) { + if (rView == null) return null; + + int userId = ActivityManager.getCurrentUser(); + + boolean ok = checkRemoteViewUriPermissions(userId, rView); + if (!ok) { + Slog.w(TAG, + "sanitizeRemoteView() user: " + userId + + " tried accessing resource that does not belong to them"); + } + return (ok ? rView : null); + } + + @Nullable static AutofillId[] toArray(@Nullable ArraySet set) { if (set == null) return null; diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java index 8119054f4196..cacfcdff686f 100644 --- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java @@ -137,8 +137,9 @@ final class FillUi { mContext = new ContextThemeWrapper(context, THEME_ID); final LayoutInflater inflater = LayoutInflater.from(mContext); - final RemoteViews headerPresentation = response.getHeader(); - final RemoteViews footerPresentation = response.getFooter(); + final RemoteViews headerPresentation = Helper.sanitizeRemoteView(response.getHeader()); + final RemoteViews footerPresentation = Helper.sanitizeRemoteView(response.getFooter()); + final ViewGroup decor; if (mFullScreen) { decor = (ViewGroup) inflater.inflate(R.layout.autofill_dataset_picker_fullscreen, null); @@ -219,6 +220,9 @@ final class FillUi { ViewGroup container = decor.findViewById(R.id.autofill_dataset_picker); final View content; try { + if (Helper.sanitizeRemoteView(response.getPresentation()) == null) { + throw new RuntimeException("Permission error accessing RemoteView"); + } response.getPresentation().setApplyTheme(THEME_ID); content = response.getPresentation().apply(mContext, decor, interceptionHandler); container.addView(content); @@ -296,7 +300,8 @@ final class FillUi { final Dataset dataset = response.getDatasets().get(i); final int index = dataset.getFieldIds().indexOf(focusedViewId); if (index >= 0) { - final RemoteViews presentation = dataset.getFieldPresentation(index); + final RemoteViews presentation = Helper.sanitizeRemoteView( + dataset.getFieldPresentation(index)); if (presentation == null) { Slog.w(TAG, "not displaying UI on field " + focusedViewId + " because " + "service didn't provide a presentation for it on " + dataset); diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java index 58823036212d..695171e82773 100644 --- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java @@ -269,7 +269,7 @@ final class SaveUi { final int type = info.getType(); writeLog(MetricsEvent.AUTOFILL_SAVE_CUSTOM_DESCRIPTION, type); - final RemoteViews template = customDescription.getPresentation(); + final RemoteViews template = Helper.sanitizeRemoteView(customDescription.getPresentation()); if (template == null) { Slog.w(TAG, "No remote view on custom description"); return false;