diff options
| author | 2016-12-02 18:19:51 +0000 | |
|---|---|---|
| committer | 2016-12-02 18:19:51 +0000 | |
| commit | 792d49dfb50587dbda95f5f681d80501519631cd (patch) | |
| tree | 952c952caf9c81359994ad899f89076571ec6d22 | |
| parent | 8bd3aa70ad3397708eab406418de8d003748213d (diff) | |
| parent | 91add43ae7d31d9f2ccd9a2e7a692289ea4d258f (diff) | |
DO NOT MERGE. Check provider access for content changes.
am: 91add43ae7
Change-Id: I158a5dab0643fb5d2c07393f0df030e93b3c006a
3 files changed, 91 insertions, 23 deletions
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index d56dc1e07f52..44b564cb885c 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -22,6 +22,11 @@ package android.app; * @hide Only for use within the system server. */ public abstract class ActivityManagerInternal { + /** + * Verify that calling app has access to the given provider. + */ + public abstract String checkContentProviderAccess(String authority, int userId); + // Called by the power manager. public abstract void onWakefulnessChanged(int wakefulness); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 69b4ecd33815..5805fb356b83 100755 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -8999,6 +8999,44 @@ public final class ActivityManagerService extends ActivityManagerNative } /** + * Check if the calling UID has a possible chance at accessing the provider + * at the given authority and user. + */ + public String checkContentProviderAccess(String authority, int userId) { + if (userId == UserHandle.USER_ALL) { + mContext.enforceCallingOrSelfPermission( + Manifest.permission.INTERACT_ACROSS_USERS_FULL, TAG); + userId = UserHandle.getCallingUserId(); + } + + ProviderInfo cpi = null; + try { + cpi = AppGlobals.getPackageManager().resolveContentProvider(authority, + STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId); + } catch (RemoteException ignored) { + } + if (cpi == null) { + // TODO: make this an outright failure in a future platform release; + // until then anonymous content notifications are unprotected + //return "Failed to find provider " + authority + " for user " + userId; + return null; + } + + ProcessRecord r = null; + synchronized (mPidsSelfLocked) { + r = mPidsSelfLocked.get(Binder.getCallingPid()); + } + if (r == null) { + return "Failed to find PID " + Binder.getCallingPid(); + } + + synchronized (this) { + return checkContentProviderPermissionLocked(cpi, r, userId, true); + } + } + + + /** * Check if {@link ProcessRecord} has a possible chance at accessing the * given {@link ProviderInfo}. Final permission checking is always done * in {@link ContentProvider}. @@ -19661,6 +19699,11 @@ public final class ActivityManagerService extends ActivityManagerNative private final class LocalService extends ActivityManagerInternal { @Override + public String checkContentProviderAccess(String authority, int userId) { + return ActivityManagerService.this.checkContentProviderAccess(authority, userId); + } + + @Override public void onWakefulnessChanged(int wakefulness) { ActivityManagerService.this.onWakefulnessChanged(wakefulness); } diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java index 22141dcb282d..d07d76a77d42 100644 --- a/services/core/java/com/android/server/content/ContentService.java +++ b/services/core/java/com/android/server/content/ContentService.java @@ -19,11 +19,14 @@ package com.android.server.content; import android.Manifest; import android.accounts.Account; import android.app.ActivityManager; +import android.app.ActivityManagerInternal; +import android.app.ActivityManagerNative; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.IContentService; import android.content.ISyncStatusObserver; +import android.content.Intent; import android.content.PeriodicSync; import android.content.pm.PackageManager; import android.content.SyncAdapterType; @@ -46,6 +49,8 @@ import android.util.Log; import android.util.Slog; import android.util.SparseIntArray; +import com.android.server.LocalServices; + import java.io.FileDescriptor; import java.io.PrintWriter; import java.security.InvalidParameterException; @@ -172,16 +177,17 @@ public final class ContentService extends IContentService.Stub { throw new IllegalArgumentException("You must pass a valid uri and observer"); } - enforceCrossUserPermission(userHandle, - "no permission to observe other users' provider view"); + final int uid = Binder.getCallingUid(); + final int pid = Binder.getCallingPid(); - if (userHandle < 0) { - if (userHandle == UserHandle.USER_CURRENT) { - userHandle = ActivityManager.getCurrentUser(); - } else if (userHandle != UserHandle.USER_ALL) { - throw new InvalidParameterException("Bad user handle for registerContentObserver: " - + userHandle); - } + userHandle = handleIncomingUser(uri, pid, uid, + Intent.FLAG_GRANT_READ_URI_PERMISSION, userHandle); + + final String msg = LocalServices.getService(ActivityManagerInternal.class) + .checkContentProviderAccess(uri.getAuthority(), userHandle); + if (msg != null) { + Log.w(TAG, "Ignoring content changes for " + uri + " from " + uid + ": " + msg); + return; } synchronized (mRootNode) { @@ -224,24 +230,20 @@ public final class ContentService extends IContentService.Stub { + " from observer " + observer + ", syncToNetwork " + syncToNetwork); } - // Notify for any user other than the caller's own requires permission. + final int uid = Binder.getCallingUid(); + final int pid = Binder.getCallingPid(); final int callingUserHandle = UserHandle.getCallingUserId(); - if (userHandle != callingUserHandle) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS, - "no permission to notify other users"); - } - // We passed the permission check; resolve pseudouser targets as appropriate - if (userHandle < 0) { - if (userHandle == UserHandle.USER_CURRENT) { - userHandle = ActivityManager.getCurrentUser(); - } else if (userHandle != UserHandle.USER_ALL) { - throw new InvalidParameterException("Bad user handle for notifyChange: " - + userHandle); - } + userHandle = handleIncomingUser(uri, pid, uid, + Intent.FLAG_GRANT_WRITE_URI_PERMISSION, userHandle); + + final String msg = LocalServices.getService(ActivityManagerInternal.class) + .checkContentProviderAccess(uri.getAuthority(), userHandle); + if (msg != null) { + Log.w(TAG, "Ignoring notify for " + uri + " from " + uid + ": " + msg); + return; } - final int uid = Binder.getCallingUid(); // This makes it so that future permission checks will be in the context of this // process rather than the caller's process. We will restore this before returning. long identityToken = clearCallingIdentity(); @@ -881,6 +883,24 @@ public final class ContentService extends IContentService.Stub { return service; } + private int handleIncomingUser(Uri uri, int pid, int uid, int modeFlags, int userId) { + if (userId == UserHandle.USER_CURRENT) { + userId = ActivityManager.getCurrentUser(); + } + + if (userId == UserHandle.USER_ALL) { + mContext.enforceCallingOrSelfPermission( + Manifest.permission.INTERACT_ACROSS_USERS_FULL, TAG); + } else if (userId < 0) { + throw new IllegalArgumentException("Invalid user: " + userId); + } else if (userId != UserHandle.getCallingUserId()) { + mContext.enforceCallingOrSelfPermission( + Manifest.permission.INTERACT_ACROSS_USERS_FULL, TAG); + } + + return userId; + } + /** * Checks if the request is from the system or an app that has INTERACT_ACROSS_USERS_FULL * permission, if the userHandle is not for the caller. |