From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Oli Lan Date: Fri, 26 Aug 2022 18:29:16 +0100 Subject: [PATCH] Prevent exfiltration of system files via avatar picker. This adds mitigations to prevent system files being exfiltrated via the settings content provider when a content URI is provided as a chosen user image. The mitigations are: 1) Copy the image to a new URI rather than the existing takePictureUri prior to cropping. 2) Only allow a system handler to respond to the CROP intent. This is a fixed version of ag/17003629, to address b/239513606. Bug: 187702830 Test: build and check functionality Merged-In: I15e15ad88b768a5b679de32c5429d921d850a3cb Change-Id: I98eea867f926c508456ec9bc654e24eeeffa0e54 (cherry picked from commit f70e351d1a3bc7765da1fa8f9e0bb52d425b27e4) Merged-In: I98eea867f926c508456ec9bc654e24eeeffa0e54 --- .../users/EditUserPhotoController.java | 43 +++++++++++++------ 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/src/com/android/settings/users/EditUserPhotoController.java b/src/com/android/settings/users/EditUserPhotoController.java index 0f67b181de..a874d6a0e5 100644 --- a/src/com/android/settings/users/EditUserPhotoController.java +++ b/src/com/android/settings/users/EditUserPhotoController.java @@ -22,7 +22,9 @@ import android.content.ClipData; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; @@ -75,6 +77,7 @@ public class EditUserPhotoController { private static final int REQUEST_CODE_TAKE_PHOTO = 1002; private static final int REQUEST_CODE_CROP_PHOTO = 1003; + private static final String PRE_CROP_PICTURE_FILE_NAME = "PreCropEditUserPhoto.jpg"; private static final String CROP_PICTURE_FILE_NAME = "CropEditUserPhoto.jpg"; private static final String TAKE_PICTURE_FILE_NAME = "TakeEditUserPhoto2.jpg"; private static final String NEW_USER_PHOTO_FILE_NAME = "NewUserPhoto.png"; @@ -85,6 +88,7 @@ public class EditUserPhotoController { private final Fragment mFragment; private final ImageView mImageView; + private final Uri mPreCropPictureUri; private final Uri mCropPictureUri; private final Uri mTakePictureUri; @@ -96,6 +100,8 @@ public class EditUserPhotoController { mContext = view.getContext(); mFragment = fragment; mImageView = view; + + mPreCropPictureUri = createTempImageUri(mContext, PRE_CROP_PICTURE_FILE_NAME, !waiting); mCropPictureUri = createTempImageUri(mContext, CROP_PICTURE_FILE_NAME, !waiting); mTakePictureUri = createTempImageUri(mContext, TAKE_PICTURE_FILE_NAME, !waiting); mPhotoSize = getPhotoSize(mContext); @@ -130,7 +136,7 @@ public class EditUserPhotoController { case REQUEST_CODE_TAKE_PHOTO: case REQUEST_CODE_CHOOSE_PHOTO: if (mTakePictureUri.equals(pictureUri)) { - cropPhoto(); + cropPhoto(pictureUri); } else { copyAndCropPhoto(pictureUri); } @@ -239,7 +245,7 @@ public class EditUserPhotoController { protected Void doInBackground(Void... params) { final ContentResolver cr = mContext.getContentResolver(); try (InputStream in = cr.openInputStream(pictureUri); - OutputStream out = cr.openOutputStream(mTakePictureUri)) { + OutputStream out = cr.openOutputStream(mPreCropPictureUri)) { Streams.copy(in, out); } catch (IOException e) { Log.w(TAG, "Failed to copy photo", e); @@ -250,27 +256,38 @@ public class EditUserPhotoController { @Override protected void onPostExecute(Void result) { if (!mFragment.isAdded()) return; - cropPhoto(); + cropPhoto(mPreCropPictureUri); } }.execute(); } - private void cropPhoto() { + private void cropPhoto(final Uri pictureUri) { // TODO: Use a public intent, when there is one. Intent intent = new Intent("com.android.camera.action.CROP"); - intent.setDataAndType(mTakePictureUri, "image/*"); + intent.setDataAndType(pictureUri, "image/*"); appendOutputExtra(intent, mCropPictureUri); appendCropExtras(intent); - if (intent.resolveActivity(mContext.getPackageManager()) != null) { - try { - StrictMode.disableDeathOnFileUriExposure(); - mFragment.startActivityForResult(intent, REQUEST_CODE_CROP_PHOTO); - } finally { - StrictMode.enableDeathOnFileUriExposure(); + try { + StrictMode.disableDeathOnFileUriExposure(); + if (startSystemActivityForResult(intent, REQUEST_CODE_CROP_PHOTO)) { + return; } - } else { - onPhotoCropped(mTakePictureUri, false); + } finally { + StrictMode.enableDeathOnFileUriExposure(); + } + onPhotoCropped(mTakePictureUri, false); + } + + private boolean startSystemActivityForResult(Intent intent, int code) { + List resolveInfos = mContext.getPackageManager() + .queryIntentActivities(intent, PackageManager.MATCH_SYSTEM_ONLY); + if (resolveInfos.isEmpty()) { + Log.w(TAG, "No system package activity could be found for code " + code); + return false; } + intent.setPackage(resolveInfos.get(0).activityInfo.packageName); + mFragment.startActivityForResult(intent, code); + return true; } private void appendOutputExtra(Intent intent, Uri pictureUri) {