Skip to content
Snippets Groups Projects
Commit 339d2005 authored by Billy Huang's avatar Billy Huang Committed by Android Build Coastguard Worker
Browse files

RESTRICT AUTOMERGE backport "opp: validate that content uri belongs to current user"

Bug: 296915500
Flag: EXEMPT trivial fix with complete testing coverage
Test: atest GoogleBluetoothInstrumentationTests:BluetoothOppSendFileInfoTest
Ignore-AOSP-First: fix for undisclosed vulnerability
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:2c5add83a18d87ea4a46bc8ab7f675e32c8d6a56)
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:0de38e22f1771fde73340d7b209342751616fa0b)
Merged-In: I0b6423025c95c13eeea3cbf584212913b5fbf307
Change-Id: I0b6423025c95c13eeea3cbf584212913b5fbf307
parent d860a3f7
No related branches found
No related tags found
No related merge requests found
......@@ -32,6 +32,8 @@
package com.android.bluetooth.opp;
import static android.os.UserHandle.myUserId;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
......@@ -39,6 +41,7 @@ import android.database.Cursor;
import android.database.sqlite.SQLiteException;
import android.net.Uri;
import android.provider.OpenableColumns;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
......@@ -49,6 +52,7 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Objects;
/**
* This class stores information about a single sending file It will only be
......@@ -117,6 +121,11 @@ public class BluetoothOppSendFileInfo {
return SEND_FILE_INFO_ERROR;
}
if (isContentUriForOtherUser(uri)) {
Log.e(TAG, "Uri: " + uri + " is invalid for user " + myUserId());
return SEND_FILE_INFO_ERROR;
}
contentType = contentResolver.getType(uri);
Cursor metadataCursor;
try {
......@@ -253,6 +262,12 @@ public class BluetoothOppSendFileInfo {
return new BluetoothOppSendFileInfo(fileName, contentType, length, is, 0);
}
private static boolean isContentUriForOtherUser(Uri uri) {
String uriUserId = uri.getUserInfo();
return !TextUtils.isEmpty(uriUserId)
&& !Objects.equals(uriUserId, String.valueOf(myUserId()));
}
private static long getStreamSize(FileInputStream is) throws IOException {
long length = 0;
byte[] unused = new byte[4096];
......
......@@ -17,6 +17,8 @@
package com.android.bluetooth.opp;
import static android.os.UserHandle.myUserId;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
......@@ -31,7 +33,6 @@ import android.content.res.AssetFileDescriptor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.provider.OpenableColumns;
import android.util.Log;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
......@@ -120,6 +121,110 @@ public class BluetoothOppSendFileInfoTest {
assertThat(info).isEqualTo(BluetoothOppSendFileInfo.SEND_FILE_INFO_ERROR);
}
@Test
public void generateFileInfo_withContentUriForOtherUser_returnsSendFileInfoError()
throws Exception {
String type = "image/jpeg";
Uri uri = buildContentUriWithEncodedAuthority((myUserId() + 1) + "@media");
long fileLength = 1000;
String fileName = "pic.jpg";
FileInputStream fs = mock(FileInputStream.class);
AssetFileDescriptor fd = mock(AssetFileDescriptor.class);
doReturn(fileLength).when(fd).getLength();
doReturn(fs).when(fd).createInputStream();
doReturn(fd).when(mCallProxy).contentResolverOpenAssetFileDescriptor(any(), eq(uri), any());
mCursor =
new MatrixCursor(new String[] {OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE});
mCursor.addRow(new Object[] {fileName, fileLength});
doReturn(mCursor)
.when(mCallProxy)
.contentResolverQuery(any(), eq(uri), any(), any(), any(), any());
BluetoothOppSendFileInfo info =
BluetoothOppSendFileInfo.generateFileInfo(mContext, uri, type, true);
assertThat(info).isEqualTo(BluetoothOppSendFileInfo.SEND_FILE_INFO_ERROR);
}
@Test
public void generateFileInfo_withContentUriForImplicitUser_returnsInfoWithCorrectLength()
throws Exception {
String type = "image/jpeg";
Uri uri = buildContentUriWithEncodedAuthority("media");
long fileLength = 1000;
String fileName = "pic.jpg";
FileInputStream fs = mock(FileInputStream.class);
AssetFileDescriptor fd = mock(AssetFileDescriptor.class);
doReturn(fileLength).when(fd).getLength();
doReturn(fs).when(fd).createInputStream();
doReturn(fd).when(mCallProxy).contentResolverOpenAssetFileDescriptor(any(), eq(uri), any());
mCursor =
new MatrixCursor(new String[] {OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE});
mCursor.addRow(new Object[] {fileName, fileLength});
doReturn(mCursor)
.when(mCallProxy)
.contentResolverQuery(any(), eq(uri), any(), any(), any(), any());
BluetoothOppSendFileInfo info =
BluetoothOppSendFileInfo.generateFileInfo(mContext, uri, type, true);
assertThat(info.mInputStream).isEqualTo(fs);
assertThat(info.mFileName).isEqualTo(fileName);
assertThat(info.mLength).isEqualTo(fileLength);
assertThat(info.mStatus).isEqualTo(0);
}
@Test
public void generateFileInfo_withContentUriForSameUser_returnsInfoWithCorrectLength()
throws Exception {
String type = "image/jpeg";
Uri uri = buildContentUriWithEncodedAuthority(myUserId() + "@media");
long fileLength = 1000;
String fileName = "pic.jpg";
FileInputStream fs = mock(FileInputStream.class);
AssetFileDescriptor fd = mock(AssetFileDescriptor.class);
doReturn(fileLength).when(fd).getLength();
doReturn(fs).when(fd).createInputStream();
doReturn(fd).when(mCallProxy).contentResolverOpenAssetFileDescriptor(any(), eq(uri), any());
mCursor =
new MatrixCursor(new String[] {OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE});
mCursor.addRow(new Object[] {fileName, fileLength});
doReturn(mCursor)
.when(mCallProxy)
.contentResolverQuery(any(), eq(uri), any(), any(), any(), any());
BluetoothOppSendFileInfo info =
BluetoothOppSendFileInfo.generateFileInfo(mContext, uri, type, true);
assertThat(info.mInputStream).isEqualTo(fs);
assertThat(info.mFileName).isEqualTo(fileName);
assertThat(info.mLength).isEqualTo(fileLength);
assertThat(info.mStatus).isEqualTo(0);
}
private static Uri buildContentUriWithEncodedAuthority(String authority) {
return new Uri.Builder()
.scheme("content")
.encodedAuthority(authority)
.path("external/images/media/1")
.build();
}
@Test
public void generateFileInfo_withoutPermissionForAccessingUri_returnsSendFileInfoError() {
String type = "text/plain";
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment