summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Sharkey <jsharkey@android.com>2018-09-24 13:23:57 -0600
committerTim Schumacher <timschumi@gmx.de>2019-02-02 15:51:19 +0100
commit617b938e24f4ec1ef399f9d456d14acf450a060b (patch)
treefcda7f3d54fa355fd99dc3db04d782fe5d917f93
parentdc98477da901236db94126f15370c713b163e11e (diff)
downloadframeworks_base-617b938e24f4ec1ef399f9d456d14acf450a060b.tar.gz
frameworks_base-617b938e24f4ec1ef399f9d456d14acf450a060b.tar.bz2
frameworks_base-617b938e24f4ec1ef399f9d456d14acf450a060b.zip
RESTRICT AUTOMERGE: Recover shady content:// paths.
The path-permission element offers prefix or regex style matching of paths, but most providers internally use UriMatcher to decide what to do with an incoming Uri. This causes trouble because UriMatcher uses Uri.getPathSegments(), which quietly ignores "empty" paths. Consider this example: <path-permission android:pathPrefix="/private" ... /> uriMatcher.addURI("com.example", "/private", CODE_PRIVATE); content://com.example//private The Uri above will pass the security check, since it's not technically a prefix match. But the UriMatcher will then match it as CODE_PRIVATE, since it ignores the "//" zero-length path. Since we can't safely change the behavior of either path-permission or UriMatcher, we're left with recovering these shady paths by trimming away zero-length paths. Bug: 112555574 Test: cts-tradefed run cts -m CtsAppSecurityHostTestCases -t android.appsecurity.cts.AppSecurityTests Change-Id: Ibadbfa4fc904ec54780c8102958735b03293fb9a (cherry picked from commit a1ec7b115cc378f0547f10cf1074a5248d42d94f)
-rw-r--r--core/java/android/content/ContentProvider.java51
-rw-r--r--core/java/android/content/ContentProviderOperation.java16
2 files changed, 35 insertions, 32 deletions
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index ba9cf7c780f..fa69b1d282f 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -52,6 +52,7 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Objects;
/**
* Content providers are one of the primary building blocks of Android applications, providing
@@ -206,7 +207,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
public Cursor query(String callingPkg, Uri uri, String[] projection,
String selection, String[] selectionArgs, String sortOrder,
ICancellationSignal cancellationSignal) {
- validateIncomingUri(uri);
+ uri = validateIncomingUri(uri);
uri = getUriWithoutUserId(uri);
if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
// The caller has no access to the data, so return an empty cursor with
@@ -245,14 +246,14 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
@Override
public String getType(Uri uri) {
- validateIncomingUri(uri);
+ uri = validateIncomingUri(uri);
uri = getUriWithoutUserId(uri);
return ContentProvider.this.getType(uri);
}
@Override
public Uri insert(String callingPkg, Uri uri, ContentValues initialValues) {
- validateIncomingUri(uri);
+ uri = validateIncomingUri(uri);
int userId = getUserIdFromUri(uri);
uri = getUriWithoutUserId(uri);
if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
@@ -268,7 +269,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
@Override
public int bulkInsert(String callingPkg, Uri uri, ContentValues[] initialValues) {
- validateIncomingUri(uri);
+ uri = validateIncomingUri(uri);
uri = getUriWithoutUserId(uri);
if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
return 0;
@@ -290,11 +291,12 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
for (int i = 0; i < numOperations; i++) {
ContentProviderOperation operation = operations.get(i);
Uri uri = operation.getUri();
- validateIncomingUri(uri);
userIds[i] = getUserIdFromUri(uri);
- if (userIds[i] != UserHandle.USER_CURRENT) {
- // Removing the user id from the uri.
- operation = new ContentProviderOperation(operation, true);
+ uri = validateIncomingUri(uri);
+ uri = getUriWithoutUserId(uri);
+ // Rebuild operation if we changed the Uri above
+ if (!Objects.equals(operation.getUri(), uri)) {
+ operation = new ContentProviderOperation(operation, uri);
operations.set(i, operation);
}
if (operation.isReadOperation()) {
@@ -329,7 +331,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
@Override
public int delete(String callingPkg, Uri uri, String selection, String[] selectionArgs) {
- validateIncomingUri(uri);
+ uri = validateIncomingUri(uri);
uri = getUriWithoutUserId(uri);
if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
return 0;
@@ -345,7 +347,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
@Override
public int update(String callingPkg, Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
- validateIncomingUri(uri);
+ uri = validateIncomingUri(uri);
uri = getUriWithoutUserId(uri);
if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
return 0;
@@ -362,7 +364,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
public ParcelFileDescriptor openFile(
String callingPkg, Uri uri, String mode, ICancellationSignal cancellationSignal,
IBinder callerToken) throws FileNotFoundException {
- validateIncomingUri(uri);
+ uri = validateIncomingUri(uri);
uri = getUriWithoutUserId(uri);
enforceFilePermission(callingPkg, uri, mode, callerToken);
final String original = setCallingPackage(callingPkg);
@@ -378,7 +380,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
public AssetFileDescriptor openAssetFile(
String callingPkg, Uri uri, String mode, ICancellationSignal cancellationSignal)
throws FileNotFoundException {
- validateIncomingUri(uri);
+ uri = validateIncomingUri(uri);
uri = getUriWithoutUserId(uri);
enforceFilePermission(callingPkg, uri, mode, null);
final String original = setCallingPackage(callingPkg);
@@ -403,7 +405,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
@Override
public String[] getStreamTypes(Uri uri, String mimeTypeFilter) {
- validateIncomingUri(uri);
+ uri = validateIncomingUri(uri);
uri = getUriWithoutUserId(uri);
return ContentProvider.this.getStreamTypes(uri, mimeTypeFilter);
}
@@ -411,7 +413,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
@Override
public AssetFileDescriptor openTypedAssetFile(String callingPkg, Uri uri, String mimeType,
Bundle opts, ICancellationSignal cancellationSignal) throws FileNotFoundException {
- validateIncomingUri(uri);
+ uri = validateIncomingUri(uri);
uri = getUriWithoutUserId(uri);
enforceFilePermission(callingPkg, uri, "r", null);
final String original = setCallingPackage(callingPkg);
@@ -430,7 +432,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
@Override
public Uri canonicalize(String callingPkg, Uri uri) {
- validateIncomingUri(uri);
+ uri = validateIncomingUri(uri);
int userId = getUserIdFromUri(uri);
uri = getUriWithoutUserId(uri);
if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
@@ -446,7 +448,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
@Override
public Uri uncanonicalize(String callingPkg, Uri uri) {
- validateIncomingUri(uri);
+ uri = validateIncomingUri(uri);
int userId = getUserIdFromUri(uri);
uri = getUriWithoutUserId(uri);
if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
@@ -1732,7 +1734,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
*/
if (mContext == null) {
mContext = context;
- if (context != null) {
+ if (context != null && mTransport != null) {
mTransport.mAppOpsManager = (AppOpsManager) context.getSystemService(
Context.APP_OPS_SERVICE);
}
@@ -1841,7 +1843,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
}
/** @hide */
- private void validateIncomingUri(Uri uri) throws SecurityException {
+ public Uri validateIncomingUri(Uri uri) throws SecurityException {
String auth = uri.getAuthority();
int userId = getUserIdFromAuthority(auth, UserHandle.USER_CURRENT);
if (userId != UserHandle.USER_CURRENT && userId != mContext.getUserId()) {
@@ -1858,6 +1860,19 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
}
throw new SecurityException(message);
}
+
+ // Normalize the path by removing any empty path segments, which can be
+ // a source of security issues.
+ final String encodedPath = uri.getEncodedPath();
+ if (encodedPath != null && encodedPath.indexOf("//") != -1) {
+ final Uri normalized = uri.buildUpon()
+ .encodedPath(encodedPath.replaceAll("//+", "/")).build();
+ Log.w(TAG, "Normalized " + uri + " to " + normalized
+ + " to avoid possible security issues");
+ return normalized;
+ } else {
+ return uri;
+ }
}
/** @hide */
diff --git a/core/java/android/content/ContentProviderOperation.java b/core/java/android/content/ContentProviderOperation.java
index fd1e24a48da..1a03bd68cd0 100644
--- a/core/java/android/content/ContentProviderOperation.java
+++ b/core/java/android/content/ContentProviderOperation.java
@@ -94,13 +94,9 @@ public class ContentProviderOperation implements Parcelable {
}
/** @hide */
- public ContentProviderOperation(ContentProviderOperation cpo, boolean removeUserIdFromUri) {
+ public ContentProviderOperation(ContentProviderOperation cpo, Uri withUri) {
mType = cpo.mType;
- if (removeUserIdFromUri) {
- mUri = ContentProvider.getUriWithoutUserId(cpo.mUri);
- } else {
- mUri = cpo.mUri;
- }
+ mUri = withUri;
mValues = cpo.mValues;
mSelection = cpo.mSelection;
mSelectionArgs = cpo.mSelectionArgs;
@@ -110,14 +106,6 @@ public class ContentProviderOperation implements Parcelable {
mYieldAllowed = cpo.mYieldAllowed;
}
- /** @hide */
- public ContentProviderOperation getWithoutUserIdInUri() {
- if (ContentProvider.uriHasUserId(mUri)) {
- return new ContentProviderOperation(this, true);
- }
- return this;
- }
-
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mType);
Uri.writeToParcel(dest, mUri);