summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Jeff Sharkey <jsharkey@android.com> 2016-12-02 18:19:51 +0000
committer android-build-merger <android-build-merger@google.com> 2016-12-02 18:19:51 +0000
commit792d49dfb50587dbda95f5f681d80501519631cd (patch)
tree952c952caf9c81359994ad899f89076571ec6d22
parent8bd3aa70ad3397708eab406418de8d003748213d (diff)
parent91add43ae7d31d9f2ccd9a2e7a692289ea4d258f (diff)
DO NOT MERGE. Check provider access for content changes.
am: 91add43ae7 Change-Id: I158a5dab0643fb5d2c07393f0df030e93b3c006a
-rw-r--r--core/java/android/app/ActivityManagerInternal.java5
-rwxr-xr-xservices/core/java/com/android/server/am/ActivityManagerService.java43
-rw-r--r--services/core/java/com/android/server/content/ContentService.java66
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.