diff options
9 files changed, 165 insertions, 22 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index c7b921c8f6d5..7c9e30f10f11 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -10379,6 +10379,7 @@ package android.content { method @CheckResult(suggest="#enforceCallingPermission(String,String)") public abstract int checkCallingPermission(@NonNull String); method @CheckResult(suggest="#enforceCallingUriPermission(Uri,int,String)") public abstract int checkCallingUriPermission(android.net.Uri, int); method @NonNull public int[] checkCallingUriPermissions(@NonNull java.util.List<android.net.Uri>, int); + method @FlaggedApi("android.security.content_uri_permission_apis") public int checkContentUriPermissionFull(@NonNull android.net.Uri, int, int, int); method @CheckResult(suggest="#enforcePermission(String,int,int,String)") public abstract int checkPermission(@NonNull String, int, int); method public abstract int checkSelfPermission(@NonNull String); method @CheckResult(suggest="#enforceUriPermission(Uri,int,int,String)") public abstract int checkUriPermission(android.net.Uri, int, int, int); diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index edeec77d48fe..ed00d9c1ddde 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -2409,6 +2409,17 @@ class ContextImpl extends Context { } } + @Override + public int checkContentUriPermissionFull(Uri uri, int pid, int uid, int modeFlags) { + try { + return ActivityManager.getService().checkContentUriPermissionFull( + ContentProvider.getUriWithoutUserId(uri), pid, uid, modeFlags, + resolveUserId(uri)); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + @NonNull @Override public int[] checkUriPermissions(@NonNull List<Uri> uris, int pid, int uid, diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 260e9859c72d..c636f8a64dfb 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -274,6 +274,7 @@ interface IActivityManager { int getProcessLimit(); int checkUriPermission(in Uri uri, int pid, int uid, int mode, int userId, in IBinder callerToken); + int checkContentUriPermissionFull(in Uri uri, int pid, int uid, int mode, int userId); int[] checkUriPermissions(in List<Uri> uris, int pid, int uid, int mode, int userId, in IBinder callerToken); void grantUriPermission(in IApplicationThread caller, in String targetPkg, in Uri uri, diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index fa76e3976a58..c69790d71910 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -6734,7 +6734,7 @@ public abstract class Context { @Intent.AccessUriMode int modeFlags); /** - * Determine whether a particular process and user ID has been granted + * Determine whether a particular process and uid has been granted * permission to access a specific URI. This only checks for permissions * that have been explicitly granted -- if the given process/uid has * more general access to the URI's content provider then this check will @@ -6758,7 +6758,38 @@ public abstract class Context { @Intent.AccessUriMode int modeFlags); /** - * Determine whether a particular process and user ID has been granted + * Determine whether a particular process and uid has been granted + * permission to access a specific content URI. + * + * <p>Unlike {@link #checkUriPermission(Uri, int, int, int)}, this method + * checks for general access to the URI's content provider, as well as + * explicitly granted permissions.</p> + * + * <p>Note, this check will throw an {@link IllegalArgumentException} + * for non-content URIs.</p> + * + * @param uri The content uri that is being checked. + * @param pid (Optional) The process ID being checked against. If the + * pid is unknown, pass -1. + * @param uid The UID being checked against. A uid of 0 is the root + * user, which will pass every permission check. + * @param modeFlags The access modes to check. + * + * @return {@link PackageManager#PERMISSION_GRANTED} if the given + * pid/uid is allowed to access that uri, or + * {@link PackageManager#PERMISSION_DENIED} if it is not. + * + * @see #checkUriPermission(Uri, int, int, int) + */ + @FlaggedApi(android.security.Flags.FLAG_CONTENT_URI_PERMISSION_APIS) + @PackageManager.PermissionResult + public int checkContentUriPermissionFull(@NonNull Uri uri, int pid, int uid, + @Intent.AccessUriMode int modeFlags) { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** + * Determine whether a particular process and uid has been granted * permission to access a list of URIs. This only checks for permissions * that have been explicitly granted -- if the given process/uid has * more general access to the URI's content provider then this check will @@ -6794,7 +6825,7 @@ public abstract class Context { @Intent.AccessUriMode int modeFlags, IBinder callerToken); /** - * Determine whether the calling process and user ID has been + * Determine whether the calling process and uid has been * granted permission to access a specific URI. This is basically * the same as calling {@link #checkUriPermission(Uri, int, int, * int)} with the pid and uid returned by {@link @@ -6817,7 +6848,7 @@ public abstract class Context { public abstract int checkCallingUriPermission(Uri uri, @Intent.AccessUriMode int modeFlags); /** - * Determine whether the calling process and user ID has been + * Determine whether the calling process and uid has been * granted permission to access a list of URIs. This is basically * the same as calling {@link #checkUriPermissions(List, int, int, int)} * with the pid and uid returned by {@link @@ -6911,7 +6942,7 @@ public abstract class Context { @Intent.AccessUriMode int modeFlags); /** - * If a particular process and user ID has not been granted + * If a particular process and uid has not been granted * permission to access a specific URI, throw {@link * SecurityException}. This only checks for permissions that have * been explicitly granted -- if the given process/uid has more @@ -6931,7 +6962,7 @@ public abstract class Context { Uri uri, int pid, int uid, @Intent.AccessUriMode int modeFlags, String message); /** - * If the calling process and user ID has not been granted + * If the calling process and uid has not been granted * permission to access a specific URI, throw {@link * SecurityException}. This is basically the same as calling * {@link #enforceUriPermission(Uri, int, int, int, String)} with diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index 0a8029c44d73..e0cf0a5f8178 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -17,6 +17,7 @@ package android.content; import android.annotation.CallbackExecutor; +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -1003,6 +1004,12 @@ public class ContextWrapper extends Context { return mBase.checkUriPermission(uri, pid, uid, modeFlags); } + @FlaggedApi(android.security.Flags.FLAG_CONTENT_URI_PERMISSION_APIS) + @Override + public int checkContentUriPermissionFull(@NonNull Uri uri, int pid, int uid, int modeFlags) { + return mBase.checkContentUriPermissionFull(uri, pid, uid, modeFlags); + } + @NonNull @Override public int[] checkUriPermissions(@NonNull List<Uri> uris, int pid, int uid, diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index fddb5707b78e..fb2eb986ca5b 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -6525,7 +6525,24 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public int checkUriPermission(Uri uri, int pid, int uid, final int modeFlags, int userId, IBinder callerToken) { - enforceNotIsolatedCaller("checkUriPermission"); + return checkUriPermission(uri, pid, uid, modeFlags, userId, + /* isFullAccessForContentUri */ false, "checkUriPermission"); + } + + /** + * @param uri This uri must NOT contain an embedded userId. + * @param userId The userId in which the uri is to be resolved. + */ + @Override + public int checkContentUriPermissionFull(Uri uri, int pid, int uid, + final int modeFlags, int userId) { + return checkUriPermission(uri, pid, uid, modeFlags, userId, + /* isFullAccessForContentUri */ true, "checkContentUriPermissionFull"); + } + + private int checkUriPermission(Uri uri, int pid, int uid, + final int modeFlags, int userId, boolean isFullAccessForContentUri, String methodName) { + enforceNotIsolatedCaller(methodName); // Our own process gets to do everything. if (pid == MY_PID) { @@ -6536,8 +6553,10 @@ public class ActivityManagerService extends IActivityManager.Stub return PackageManager.PERMISSION_DENIED; } } - return mUgmInternal.checkUriPermission(new GrantUri(userId, uri, modeFlags), uid, modeFlags) - ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED; + boolean granted = mUgmInternal.checkUriPermission(new GrantUri(userId, uri, modeFlags), uid, + modeFlags, isFullAccessForContentUri); + + return granted ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED; } @Override diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerInternal.java b/services/core/java/com/android/server/uri/UriGrantsManagerInternal.java index e54b40eb334c..03c75e018dab 100644 --- a/services/core/java/com/android/server/uri/UriGrantsManagerInternal.java +++ b/services/core/java/com/android/server/uri/UriGrantsManagerInternal.java @@ -37,7 +37,17 @@ public interface UriGrantsManagerInternal { void revokeUriPermission(String targetPackage, int callingUid, GrantUri grantUri, final int modeFlags); - boolean checkUriPermission(GrantUri grantUri, int uid, final int modeFlags); + /** + * Check if the uid has permission to the URI in grantUri. + * + * @param isFullAccessForContentUri If true, the URI has to be a content URI + * and the method will consider full access. + * Otherwise, the method will only consider + * URI grants. + */ + boolean checkUriPermission(GrantUri grantUri, int uid, int modeFlags, + boolean isFullAccessForContentUri); + int checkGrantUriPermission( int callingUid, String targetPkg, Uri uri, int modeFlags, int userId); diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java index e501b9dc9959..ce2cbed0c9a9 100644 --- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java +++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java @@ -25,6 +25,7 @@ import static android.content.Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION; import static android.content.Intent.FLAG_GRANT_PREFIX_URI_PERMISSION; import static android.content.pm.PackageManager.MATCH_ANY_USER; import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING; +import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AUTO; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES; @@ -1103,7 +1104,8 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub implements */ private int checkGrantUriPermissionUnlocked(int callingUid, String targetPkg, GrantUri grantUri, int modeFlags, int lastTargetUid) { - if (!Intent.isAccessUriMode(modeFlags)) { + if (!isContentUriWithAccessModeFlags(grantUri, modeFlags, + /* logAction */ "grant URI permission")) { return -1; } @@ -1111,12 +1113,6 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub implements if (DEBUG) Slog.v(TAG, "Checking grant " + targetPkg + " permission to " + grantUri); } - // If this is not a content: uri, we can't do anything with it. - if (!ContentResolver.SCHEME_CONTENT.equals(grantUri.uri.getScheme())) { - if (DEBUG) Slog.v(TAG, "Can't grant URI permission for non-content URI: " + grantUri); - return -1; - } - // Bail early if system is trying to hand out permissions directly; it // must always grant permissions on behalf of someone explicit. final int callingAppId = UserHandle.getAppId(callingUid); @@ -1137,7 +1133,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub implements final String authority = grantUri.uri.getAuthority(); final ProviderInfo pi = getProviderInfo(authority, grantUri.sourceUserId, - MATCH_DEBUG_TRIAGED_MISSING, callingUid); + MATCH_DIRECT_BOOT_AUTO, callingUid); if (pi == null) { Slog.w(TAG, "No content provider found for permission check: " + grantUri.uri.toSafeString()); @@ -1285,6 +1281,65 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub implements return targetUid; } + private boolean isContentUriWithAccessModeFlags(GrantUri grantUri, int modeFlags, + String logAction) { + if (!Intent.isAccessUriMode(modeFlags)) { + if (DEBUG) Slog.v(TAG, "Mode flags are not access URI mode flags: " + modeFlags); + return false; + } + + if (!ContentResolver.SCHEME_CONTENT.equals(grantUri.uri.getScheme())) { + if (DEBUG) { + Slog.v(TAG, "Can't " + logAction + " on non-content URI: " + grantUri); + } + return false; + } + + return true; + } + + /** Check if the uid has permission to the content URI in grantUri. */ + private boolean checkContentUriPermissionFullUnlocked(GrantUri grantUri, int uid, + int modeFlags) { + if (uid < 0) { + throw new IllegalArgumentException("Uid must be positive for the content URI " + + "permission check of " + grantUri.uri.toSafeString()); + } + + if (!isContentUriWithAccessModeFlags(grantUri, modeFlags, + /* logAction */ "check content URI permission")) { + throw new IllegalArgumentException("The URI must be a content URI and the mode " + + "flags must be at least read and/or write for the content URI permission " + + "check of " + grantUri.uri.toSafeString()); + } + + final int appId = UserHandle.getAppId(uid); + if ((appId == SYSTEM_UID) || (appId == ROOT_UID)) { + return true; + } + + // Retrieve the URI's content provider + final String authority = grantUri.uri.getAuthority(); + ProviderInfo pi = getProviderInfo(authority, grantUri.sourceUserId, MATCH_DIRECT_BOOT_AUTO, + uid); + + if (pi == null) { + Slog.w(TAG, "No content provider found for content URI permission check: " + + grantUri.uri.toSafeString()); + return false; + } + + // Check if it has general permission to the URI's content provider + if (checkHoldingPermissionsUnlocked(pi, grantUri, uid, modeFlags)) { + return true; + } + + // Check if it has explicitly granted permissions to the URI + synchronized (mLock) { + return checkUriPermissionLocked(grantUri, uid, modeFlags); + } + } + /** * @param userId The userId in which the uri is to be resolved. */ @@ -1482,7 +1537,12 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub implements } @Override - public boolean checkUriPermission(GrantUri grantUri, int uid, int modeFlags) { + public boolean checkUriPermission(GrantUri grantUri, int uid, int modeFlags, + boolean isFullAccessForContentUri) { + if (isFullAccessForContentUri) { + return UriGrantsManagerService.this.checkContentUriPermissionFullUnlocked(grantUri, + uid, modeFlags); + } synchronized (mLock) { return UriGrantsManagerService.this.checkUriPermissionLocked(grantUri, uid, modeFlags); diff --git a/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java index 769ec5fac023..321858685e38 100644 --- a/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java @@ -345,15 +345,18 @@ public class UriGrantsManagerServiceTest { intent, UID_PRIMARY_CAMERA, PKG_SOCIAL, USER_PRIMARY), service); // Verify that everything is good with the world - assertTrue(mService.checkUriPermission(expectedGrant, UID_PRIMARY_SOCIAL, FLAG_READ)); + assertTrue(mService.checkUriPermission(expectedGrant, UID_PRIMARY_SOCIAL, FLAG_READ, + /* isFullAccessForContentUri */ false)); // Finish activity; service should hold permission activity.removeUriPermissions(); - assertTrue(mService.checkUriPermission(expectedGrant, UID_PRIMARY_SOCIAL, FLAG_READ)); + assertTrue(mService.checkUriPermission(expectedGrant, UID_PRIMARY_SOCIAL, FLAG_READ, + /* isFullAccessForContentUri */ false)); // And finishing service should wrap things up service.removeUriPermissions(); - assertFalse(mService.checkUriPermission(expectedGrant, UID_PRIMARY_SOCIAL, FLAG_READ)); + assertFalse(mService.checkUriPermission(expectedGrant, UID_PRIMARY_SOCIAL, FLAG_READ, + /* isFullAccessForContentUri */ false)); } @Test |