Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d1a55f05 authored by Oli Lan's avatar Oli Lan Committed by Android Build Coastguard Worker
Browse files

Prevent exfiltration of system files via user image settings.

This is a backport of ag/17005706.

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.

Bug: 187702830
Test: build and check functionality
Change-Id: Ia6314b6810afb5efa0329f3eeaee9ccfff791966
Merged-In: I15e15ad88b768a5b679de32c5429d921d850a3cb
(cherry picked from commit 8950a900)
(cherry picked from commit 3c63dd20ebdbead1ea92d16ee94a9397238895d5)
Merged-In: Ia6314b6810afb5efa0329f3eeaee9ccfff791966
parent fe8fe00d
Loading
Loading
Loading
Loading
+30 −13
Original line number Original line Diff line number Diff line
@@ -21,6 +21,8 @@ import android.content.ClipData;
import android.content.ContentResolver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Context;
import android.content.Intent;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Bitmap.Config;
@@ -77,6 +79,7 @@ public class EditUserPhotoController {
    private static final int REQUEST_CODE_TAKE_PHOTO   = 1002;
    private static final int REQUEST_CODE_TAKE_PHOTO   = 1002;
    private static final int REQUEST_CODE_CROP_PHOTO   = 1003;
    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 CROP_PICTURE_FILE_NAME = "CropEditUserPhoto.jpg";
    private static final String TAKE_PICTURE_FILE_NAME = "TakeEditUserPhoto2.jpg";
    private static final String TAKE_PICTURE_FILE_NAME = "TakeEditUserPhoto2.jpg";
    private static final String NEW_USER_PHOTO_FILE_NAME = "NewUserPhoto.png";
    private static final String NEW_USER_PHOTO_FILE_NAME = "NewUserPhoto.png";
@@ -87,6 +90,7 @@ public class EditUserPhotoController {
    private final Fragment mFragment;
    private final Fragment mFragment;
    private final ImageView mImageView;
    private final ImageView mImageView;


    private final Uri mPreCropPictureUri;
    private final Uri mCropPictureUri;
    private final Uri mCropPictureUri;
    private final Uri mTakePictureUri;
    private final Uri mTakePictureUri;


@@ -98,6 +102,8 @@ public class EditUserPhotoController {
        mContext = view.getContext();
        mContext = view.getContext();
        mFragment = fragment;
        mFragment = fragment;
        mImageView = view;
        mImageView = view;

        mPreCropPictureUri = createTempImageUri(mContext, PRE_CROP_PICTURE_FILE_NAME, !waiting);
        mCropPictureUri = createTempImageUri(mContext, CROP_PICTURE_FILE_NAME, !waiting);
        mCropPictureUri = createTempImageUri(mContext, CROP_PICTURE_FILE_NAME, !waiting);
        mTakePictureUri = createTempImageUri(mContext, TAKE_PICTURE_FILE_NAME, !waiting);
        mTakePictureUri = createTempImageUri(mContext, TAKE_PICTURE_FILE_NAME, !waiting);
        mPhotoSize = getPhotoSize(mContext);
        mPhotoSize = getPhotoSize(mContext);
@@ -132,7 +138,7 @@ public class EditUserPhotoController {
            case REQUEST_CODE_TAKE_PHOTO:
            case REQUEST_CODE_TAKE_PHOTO:
            case REQUEST_CODE_CHOOSE_PHOTO:
            case REQUEST_CODE_CHOOSE_PHOTO:
                if (mTakePictureUri.equals(pictureUri)) {
                if (mTakePictureUri.equals(pictureUri)) {
                    cropPhoto();
                    cropPhoto(pictureUri);
                } else {
                } else {
                    copyAndCropPhoto(pictureUri);
                    copyAndCropPhoto(pictureUri);
                }
                }
@@ -228,7 +234,7 @@ public class EditUserPhotoController {
            protected Void doInBackground(Void... params) {
            protected Void doInBackground(Void... params) {
                final ContentResolver cr = mContext.getContentResolver();
                final ContentResolver cr = mContext.getContentResolver();
                try (InputStream in = cr.openInputStream(pictureUri);
                try (InputStream in = cr.openInputStream(pictureUri);
                        OutputStream out = cr.openOutputStream(mTakePictureUri)) {
                        OutputStream out = cr.openOutputStream(mPreCropPictureUri)) {
                    Streams.copy(in, out);
                    Streams.copy(in, out);
                } catch (IOException e) {
                } catch (IOException e) {
                    Log.w(TAG, "Failed to copy photo", e);
                    Log.w(TAG, "Failed to copy photo", e);
@@ -239,27 +245,38 @@ public class EditUserPhotoController {
            @Override
            @Override
            protected void onPostExecute(Void result) {
            protected void onPostExecute(Void result) {
                if (!mFragment.isAdded()) return;
                if (!mFragment.isAdded()) return;
                cropPhoto();
                cropPhoto(mPreCropPictureUri);
            }
            }
        }.execute();
        }.execute();
    }
    }


    private void cropPhoto() {
    private void cropPhoto(final Uri pictureUri) {
        // TODO: Use a public intent, when there is one.
        // TODO: Use a public intent, when there is one.
        Intent intent = new Intent("com.android.camera.action.CROP");
        Intent intent = new Intent("com.android.camera.action.CROP");
        intent.setDataAndType(mTakePictureUri, "image/*");
        intent.setDataAndType(pictureUri, "image/*");
        appendOutputExtra(intent, mCropPictureUri);
        appendOutputExtra(intent, mCropPictureUri);
        appendCropExtras(intent);
        appendCropExtras(intent);
        if (intent.resolveActivity(mContext.getPackageManager()) != null) {
        try {
        try {
            StrictMode.disableDeathOnFileUriExposure();
            StrictMode.disableDeathOnFileUriExposure();
                mFragment.startActivityForResult(intent, REQUEST_CODE_CROP_PHOTO);
            if (startSystemActivityForResult(intent, REQUEST_CODE_CROP_PHOTO)) {
                return;
            }
        } finally {
        } finally {
            StrictMode.enableDeathOnFileUriExposure();
            StrictMode.enableDeathOnFileUriExposure();
        }
        }
        } else {
        onPhotoCropped(mTakePictureUri, false);
        onPhotoCropped(mTakePictureUri, false);
    }
    }

    private boolean startSystemActivityForResult(Intent intent, int code) {
        ActivityInfo info = intent.resolveActivityInfo(mContext.getPackageManager(),
                PackageManager.MATCH_SYSTEM_ONLY);
        if (info == null) {
            Log.w(TAG, "No system package activity could be found for code " + code);
            return false;
        }
        intent.setPackage(info.packageName);
        mFragment.startActivityForResult(intent, code);
        return true;
    }
    }


    private void appendOutputExtra(Intent intent, Uri pictureUri) {
    private void appendOutputExtra(Intent intent, Uri pictureUri) {