diff options
author | Felipe Leme <felipeal@google.com> | 2017-11-30 17:41:57 -0800 |
---|---|---|
committer | android-build-team Robot <android-build-team-robot@google.com> | 2018-07-20 00:26:33 +0000 |
commit | 3f89a390d262e4e41eeab8c5b83ac53b727e6fa6 (patch) | |
tree | 8243d4ca1a2638a29a7266b41522f2a4c48eef85 | |
parent | 1477a4f6677eeaa1993d58f296029f2df4ec7273 (diff) | |
download | base-3f89a390d262e4e41eeab8c5b83ac53b727e6fa6.tar.gz |
Make sure apps cannot forge package name on AssistStructure used for Autofill.
Test: cts-tradefed run commandAndExit cts-dev -m CtsAutoFillServiceTestCases -t android.autofillservice.cts.VirtualContainerActivityTest#testAppCannotFakePackageName
Test: cts-tradefed run commandAndExit cts-dev -m CtsAutoFillServiceTestCases
Bug: 69981710
Change-Id: Id6036cddb51dd8dd0c9128b7212d573f630d693f
Merged-In: Id6036cddb51dd8dd0c9128b7212d573f630d693f
(cherry picked from commit 23e61a9086a34405e277868474e003b37ed1b711)
7 files changed, 115 insertions, 26 deletions
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java index 9383626360dc..bf715c35d9b7 100644 --- a/core/java/android/app/assist/AssistStructure.java +++ b/core/java/android/app/assist/AssistStructure.java @@ -2058,6 +2058,16 @@ public class AssistStructure implements Parcelable { return mActivityComponent; } + /** + * Called by Autofill server when app forged a different value. + * + * @hide + */ + public void setActivityComponent(ComponentName componentName) { + ensureData(); + mActivityComponent = componentName; + } + /** @hide */ public int getFlags() { return mFlags; diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 4fb2a99af575..ba738b6dfcea 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -24,6 +24,8 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemService; +import android.app.Activity; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentSender; @@ -44,6 +46,7 @@ import android.view.View; import com.android.internal.annotations.GuardedBy; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.internal.util.Preconditions; import java.io.PrintWriter; import java.lang.annotation.Retention; @@ -390,7 +393,7 @@ public final class AutofillManager { * @hide */ public AutofillManager(Context context, IAutoFillManager service) { - mContext = context; + mContext = Preconditions.checkNotNull(context, "context cannot be null"); mService = service; } @@ -940,6 +943,13 @@ public final class AutofillManager { return mContext.getAutofillClient(); } + private ComponentName getComponentNameFromContext() { + if (mContext instanceof Activity) { + return ((Activity) mContext).getComponentName(); + } + return null; + } + /** @hide */ public void onAuthenticationResult(int authenticationId, Intent data) { if (!hasAutofillFeature()) { @@ -990,9 +1000,14 @@ public final class AutofillManager { return; } try { + final ComponentName componentName = getComponentNameFromContext(); + if (componentName == null) { + Log.w(TAG, "startSessionLocked(): context is not activity: " + mContext); + return; + } mSessionId = mService.startSession(mContext.getActivityToken(), mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(), - mCallback != null, flags, mContext.getOpPackageName()); + mCallback != null, flags, componentName); if (mSessionId != NO_SESSION) { mState = STATE_ACTIVE; } @@ -1050,9 +1065,14 @@ public final class AutofillManager { try { if (restartIfNecessary) { + final ComponentName componentName = getComponentNameFromContext(); + if (componentName == null) { + Log.w(TAG, "startSessionLocked(): context is not activity: " + mContext); + return; + } final int newId = mService.updateOrRestartSession(mContext.getActivityToken(), mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(), - mCallback != null, flags, mContext.getOpPackageName(), mSessionId, action); + mCallback != null, flags, componentName, mSessionId, action); if (newId != mSessionId) { if (sDebug) Log.d(TAG, "Session restarted: " + mSessionId + "=>" + newId); mSessionId = newId; diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl index 6bd9bec368c8..9329c4dcff6a 100644 --- a/core/java/android/view/autofill/IAutoFillManager.aidl +++ b/core/java/android/view/autofill/IAutoFillManager.aidl @@ -16,6 +16,7 @@ package android.view.autofill; +import android.content.ComponentName; import android.graphics.Rect; import android.os.Bundle; import android.os.IBinder; @@ -34,14 +35,15 @@ interface IAutoFillManager { int addClient(in IAutoFillManagerClient client, int userId); int startSession(IBinder activityToken, in IBinder appCallback, in AutofillId autoFillId, in Rect bounds, in AutofillValue value, int userId, boolean hasCallback, int flags, - String packageName); + in ComponentName componentName); FillEventHistory getFillEventHistory(); boolean restoreSession(int sessionId, in IBinder activityToken, in IBinder appCallback); void updateSession(int sessionId, in AutofillId id, in Rect bounds, in AutofillValue value, int action, int flags, int userId); int updateOrRestartSession(IBinder activityToken, in IBinder appCallback, in AutofillId autoFillId, in Rect bounds, in AutofillValue value, int userId, - boolean hasCallback, int flags, String packageName, int sessionId, int action); + boolean hasCallback, int flags, in ComponentName componentName, int sessionId, + int action); void finishSession(int sessionId, int userId); void cancelSession(int sessionId, int userId); void setAuthenticationResult(in Bundle data, int sessionId, int authenticationId, int userId); diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index a27515c0266d..3cadff242e03 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -4006,6 +4006,19 @@ message MetricsEvent { // OS: O FIELD_NOTIFICATION_GROUP_SUMMARY = 947; + // An app attempted to forge a different component name in the AssisStructure that would be + // passed to the autofill service. + // OS: O (security patch) + // Package: Real package of the app being autofilled + // Tag FIELD_AUTOFILL_SERVICE: Package of the autofill service that processed the request + // TAG FIELD_AUTOFILL_FORGED_COMPONENT_NAME: Component name being forged + AUTOFILL_FORGED_COMPONENT_ATTEMPT = 948; + + // FIELD - The component that an app tried tro forged. + // Type: string + // OS: O (security patch) + FIELD_AUTOFILL_FORGED_COMPONENT_NAME = 949; + // ---- End O Constants, all O constants go above this line ---- // OPEN: Settings > System > Languages & input > Advanced > Lift to open camera diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java index 1f4161ac54d4..ffc778a8ee40 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java @@ -533,25 +533,26 @@ public final class AutofillManagerService extends SystemService { @Override public int startSession(IBinder activityToken, IBinder appCallback, AutofillId autofillId, Rect bounds, AutofillValue value, int userId, boolean hasCallback, int flags, - String packageName) { + ComponentName componentName) { activityToken = Preconditions.checkNotNull(activityToken, "activityToken"); appCallback = Preconditions.checkNotNull(appCallback, "appCallback"); autofillId = Preconditions.checkNotNull(autofillId, "autoFillId"); - packageName = Preconditions.checkNotNull(packageName, "packageName"); + componentName = Preconditions.checkNotNull(componentName, "componentName"); + final String packageName = Preconditions.checkNotNull(componentName.getPackageName()); Preconditions.checkArgument(userId == UserHandle.getUserId(getCallingUid()), "userId"); try { mContext.getPackageManager().getPackageInfoAsUser(packageName, 0, userId); } catch (PackageManager.NameNotFoundException e) { - throw new IllegalArgumentException(packageName + " is not a valid package", e); + throw new IllegalArgumentException(componentName + " is not a valid package", e); } synchronized (mLock) { final AutofillManagerServiceImpl service = getServiceForUserLocked(userId); return service.startSessionLocked(activityToken, getCallingUid(), appCallback, - autofillId, bounds, value, hasCallback, flags, packageName); + autofillId, bounds, value, hasCallback, flags, componentName); } } @@ -603,7 +604,8 @@ public final class AutofillManagerService extends SystemService { @Override public int updateOrRestartSession(IBinder activityToken, IBinder appCallback, AutofillId autoFillId, Rect bounds, AutofillValue value, int userId, - boolean hasCallback, int flags, String packageName, int sessionId, int action) { + boolean hasCallback, int flags, ComponentName componentName, int sessionId, + int action) { boolean restart = false; synchronized (mLock) { final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); @@ -614,7 +616,7 @@ public final class AutofillManagerService extends SystemService { } if (restart) { return startSession(activityToken, appCallback, autoFillId, bounds, value, userId, - hasCallback, flags, packageName); + hasCallback, flags, componentName); } // Nothing changed... diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index 3a3b5707fc6f..a17c3ca92e5d 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -32,6 +32,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ServiceInfo; import android.graphics.Rect; import android.graphics.drawable.Drawable; @@ -43,6 +44,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.RemoteCallbackList; import android.os.RemoteException; +import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.service.autofill.AutofillService; @@ -279,7 +281,7 @@ final class AutofillManagerServiceImpl { int startSessionLocked(@NonNull IBinder activityToken, int uid, @NonNull IBinder appCallbackToken, @NonNull AutofillId autofillId, @NonNull Rect virtualBounds, @Nullable AutofillValue value, boolean hasCallback, - int flags, @NonNull String packageName) { + int flags, @NonNull ComponentName componentName) { if (!isEnabled()) { return 0; } @@ -289,7 +291,7 @@ final class AutofillManagerServiceImpl { pruneAbandonedSessionsLocked(); final Session newSession = createSessionByTokenLocked(activityToken, uid, appCallbackToken, - hasCallback, packageName); + hasCallback, componentName); if (newSession == null) { return NO_SESSION; } @@ -386,7 +388,8 @@ final class AutofillManagerServiceImpl { } private Session createSessionByTokenLocked(@NonNull IBinder activityToken, int uid, - @NonNull IBinder appCallbackToken, boolean hasCallback, @NonNull String packageName) { + @NonNull IBinder appCallbackToken, boolean hasCallback, + @NonNull ComponentName componentName) { // use random ids so that one app cannot know that another app creates sessions int sessionId; int tries = 0; @@ -400,15 +403,44 @@ final class AutofillManagerServiceImpl { sessionId = sRandom.nextInt(); } while (sessionId == NO_SESSION || mSessions.indexOfKey(sessionId) >= 0); + assertCallerLocked(componentName); + final Session newSession = new Session(this, mUi, mContext, mHandlerCaller, mUserId, mLock, sessionId, uid, activityToken, appCallbackToken, hasCallback, - mUiLatencyHistory, mInfo.getServiceInfo().getComponentName(), packageName); + mUiLatencyHistory, mInfo.getServiceInfo().getComponentName(), componentName); mSessions.put(newSession.id, newSession); return newSession; } /** + * Asserts the component is owned by the caller. + */ + private void assertCallerLocked(@NonNull ComponentName componentName) { + final PackageManager pm = mContext.getPackageManager(); + final int callingUid = Binder.getCallingUid(); + final int packageUid; + try { + packageUid = pm.getPackageUidAsUser(componentName.getPackageName(), + UserHandle.getCallingUserId()); + } catch (NameNotFoundException e) { + throw new SecurityException("Could not verify UID for " + componentName); + } + if (callingUid != packageUid) { + final String[] packages = pm.getPackagesForUid(callingUid); + final String callingPackage = packages != null ? packages[0] : "uid-" + callingUid; + Slog.w(TAG, "App (package=" + callingPackage + ", UID=" + callingUid + + ") passed component (" + componentName + ") owned by UID " + packageUid); + mMetricsLogger.write(new LogMaker(MetricsEvent.AUTOFILL_FORGED_COMPONENT_ATTEMPT) + .setPackageName(callingPackage) + .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, getServicePackageName()) + .addTaggedData(MetricsEvent.FIELD_AUTOFILL_FORGED_COMPONENT_NAME, + componentName == null ? "null" : componentName.flattenToShortString())); + throw new SecurityException("Invalid component: " + componentName); + } + } + + /** * Restores a session after an activity was temporarily destroyed. * * @param sessionId The id of the session to restore diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index de2950dd5985..1f0e51c694aa 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -126,8 +126,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") @NonNull private IBinder mActivityToken; - /** Package name of the app that is auto-filled */ - @NonNull private final String mPackageName; + /** Component that's being auto-filled */ + @NonNull private final ComponentName mComponentName; @GuardedBy("mLock") private final ArrayMap<AutofillId, ViewState> mViewStates = new ArrayMap<>(); @@ -227,6 +227,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState structure.ensureData(); // Sanitize structure before it's sent to service. + final ComponentName componentNameFromApp = structure.getActivityComponent(); + if (!mComponentName.equals(componentNameFromApp)) { + Slog.w(TAG, "Activity " + mComponentName + " forged different component on " + + "AssistStructure: " + componentNameFromApp); + structure.setActivityComponent(mComponentName); + mMetricsLogger.write(newLogMaker(MetricsEvent.AUTOFILL_FORGED_COMPONENT_ATTEMPT) + .addTaggedData(MetricsEvent.FIELD_AUTOFILL_FORGED_COMPONENT_NAME, + componentNameFromApp == null ? "null" + : componentNameFromApp.flattenToShortString())); + } structure.sanitizeForParceling(true); // Flags used to start the session. @@ -415,7 +425,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @NonNull Context context, @NonNull HandlerCaller handlerCaller, int userId, @NonNull Object lock, int sessionId, int uid, @NonNull IBinder activityToken, @NonNull IBinder client, boolean hasCallback, @NonNull LocalLog uiLatencyHistory, - @NonNull ComponentName componentName, @NonNull String packageName) { + @NonNull ComponentName serviceComponentName, @NonNull ComponentName appComponentName) { id = sessionId; this.uid = uid; mStartTime = SystemClock.elapsedRealtime(); @@ -423,11 +433,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mLock = lock; mUi = ui; mHandlerCaller = handlerCaller; - mRemoteFillService = new RemoteFillService(context, componentName, userId, this); + mRemoteFillService = new RemoteFillService(context, serviceComponentName, userId, this); mActivityToken = activityToken; mHasCallback = hasCallback; mUiLatencyHistory = uiLatencyHistory; - mPackageName = packageName; + mComponentName = appComponentName; mClient = IAutoFillManagerClient.Stub.asInterface(client); writeLog(MetricsEvent.AUTOFILL_SESSION_STARTED); @@ -1008,8 +1018,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final IAutoFillManagerClient client = getClient(); mPendingSaveUi = new PendingUi(mActivityToken, id, client); getUiForShowing().showSaveUi(mService.getServiceLabel(), mService.getServiceIcon(), - mService.getServicePackageName(), saveInfo, valueFinder, mPackageName, this, - mPendingSaveUi); + mService.getServicePackageName(), saveInfo, valueFinder, + mComponentName.getPackageName(), this, mPendingSaveUi); if (client != null) { try { client.setSaveUiState(id, true); @@ -1365,7 +1375,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } getUiForShowing().showFillUi(filledId, response, filterText, - mService.getServicePackageName(), mPackageName, this); + mService.getServicePackageName(), mComponentName.getPackageName(), this); synchronized (mLock) { if (mUiShownTime == 0) { @@ -1690,14 +1700,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @Override public String toString() { - return "Session: [id=" + id + ", pkg=" + mPackageName + "]"; + return "Session: [id=" + id + ", pkg=" + mComponentName.getPackageName() + "]"; } void dumpLocked(String prefix, PrintWriter pw) { final String prefix2 = prefix + " "; pw.print(prefix); pw.print("id: "); pw.println(id); pw.print(prefix); pw.print("uid: "); pw.println(uid); - pw.print(prefix); pw.print("mPackagename: "); pw.println(mPackageName); + pw.print(prefix); pw.print("mComponentName: "); pw.println(mComponentName); pw.print(prefix); pw.print("mActivityToken: "); pw.println(mActivityToken); pw.print(prefix); pw.print("mStartTime: "); pw.println(mStartTime); pw.print(prefix); pw.print("Time to show UI: "); @@ -1920,7 +1930,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } private LogMaker newLogMaker(int category, String servicePackageName) { - return Helper.newLogMaker(category, mPackageName, servicePackageName); + return Helper.newLogMaker(category, mComponentName.getPackageName(), servicePackageName); } private void writeLog(int category) { |