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

Commit 9edef25e authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

Detailed ContentProvider permissions checks.

The new MediaProvider design has an internal dynamic security model
based on the value stored in OWNER_PACKAGE_NAME, so the OS always
needs to consult the provider when resolving Uri permission grants.

Blocking calls from the system process like this are typically
discouraged, but this is the best we can do with the limited time
left, and there is existing precident with getType().

For now, use "forceUriPermissions" as a proxy for determining when
we need to consult the provider directly.

Bug: 115619667
Test: atest --test-mapping packages/providers/MediaProvider
Test: atest android.appsecurity.cts.ExternalStorageHostTest
Change-Id: I1d54feeec93fbb4cf5ff55240ef4eae3a35ed068
parent 26f2c379
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.content.pm.ActivityInfo;
import android.content.pm.ActivityPresentationInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.UserInfo;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.os.TransactionTooLargeException;
@@ -52,6 +53,12 @@ public abstract class ActivityManagerInternal {
     */
    public abstract String checkContentProviderAccess(String authority, int userId);

    /**
     * Verify that calling UID has access to the given provider.
     */
    public abstract int checkContentProviderUriPermission(Uri uri, int userId,
            int callingUid, int modeFlags);

    // Called by the power manager.
    public abstract void onWakefulnessChanged(int wakefulness);

+3 −0
Original line number Diff line number Diff line
@@ -56,6 +56,9 @@ public interface ContentInterface {
    public boolean refresh(@NonNull Uri uri, @Nullable Bundle args,
            @Nullable CancellationSignal cancellationSignal) throws RemoteException;

    public int checkUriPermission(@NonNull Uri uri, int uid, @Intent.AccessUriMode int modeFlags)
            throws RemoteException;

    public @Nullable Uri insert(@NonNull Uri uri, @Nullable ContentValues initialValues)
            throws RemoteException;

+23 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.app.AppOpsManager;
import android.content.pm.PackageManager;
import android.content.pm.PathPermission;
import android.content.pm.ProviderInfo;
import android.content.res.AssetFileDescriptor;
@@ -582,6 +583,22 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
            }
        }

        @Override
        public int checkUriPermission(String callingPkg, Uri uri, int uid, int modeFlags) {
            uri = validateIncomingUri(uri);
            uri = maybeGetUriWithoutUserId(uri);
            Trace.traceBegin(TRACE_TAG_DATABASE, "checkUriPermission");
            final String original = setCallingPackage(callingPkg);
            try {
                return mInterface.checkUriPermission(uri, uid, modeFlags);
            } catch (RemoteException e) {
                throw e.rethrowAsRuntimeException();
            } finally {
                setCallingPackage(original);
                Trace.traceEnd(TRACE_TAG_DATABASE);
            }
        }

        private void enforceFilePermission(String callingPkg, Uri uri, String mode,
                IBinder callerToken) throws FileNotFoundException, SecurityException {
            if (mode != null && mode.indexOf('w') != -1) {
@@ -1416,6 +1433,12 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
        return false;
    }

    /** {@hide} */
    @Override
    public int checkUriPermission(@NonNull Uri uri, int uid, @Intent.AccessUriMode int modeFlags) {
        return PackageManager.PERMISSION_DENIED;
    }

    /**
     * @hide
     * Implementation when a caller has performed an insert on the content
+19 −0
Original line number Diff line number Diff line
@@ -307,6 +307,25 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable {
        }
    }

    /** {@hide} */
    @Override
    public int checkUriPermission(@NonNull Uri uri, int uid, @Intent.AccessUriMode int modeFlags)
            throws RemoteException {
        Preconditions.checkNotNull(uri, "uri");

        beforeRemote();
        try {
            return mContentProvider.checkUriPermission(mPackageName, uri, uid, modeFlags);
        } catch (DeadObjectException e) {
            if (!mStable) {
                mContentResolver.unstableProviderDied(mContentProvider);
            }
            throw e;
        } finally {
            afterRemote();
        }
    }

    /** See {@link ContentProvider#insert ContentProvider.insert} */
    @Override
    public @Nullable Uri insert(@NonNull Uri url, @Nullable ContentValues initialValues)
+36 −0
Original line number Diff line number Diff line
@@ -363,6 +363,19 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
                    reply.writeInt(out ? 0 : -1);
                    return true;
                }

                case CHECK_URI_PERMISSION_TRANSACTION: {
                    data.enforceInterface(IContentProvider.descriptor);
                    String callingPkg = data.readString();
                    Uri uri = Uri.CREATOR.createFromParcel(data);
                    int uid = data.readInt();
                    int modeFlags = data.readInt();

                    int out = checkUriPermission(callingPkg, uri, uid, modeFlags);
                    reply.writeNoException();
                    reply.writeInt(out);
                    return true;
                }
            }
        } catch (Exception e) {
            DatabaseUtils.writeExceptionToParcel(reply, e);
@@ -800,6 +813,29 @@ final class ContentProviderProxy implements IContentProvider
        }
    }

    @Override
    public int checkUriPermission(String callingPkg, Uri url, int uid, int modeFlags)
            throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        try {
            data.writeInterfaceToken(IContentProvider.descriptor);

            data.writeString(callingPkg);
            url.writeToParcel(data, 0);
            data.writeInt(uid);
            data.writeInt(modeFlags);

            mRemote.transact(IContentProvider.CHECK_URI_PERMISSION_TRANSACTION, data, reply, 0);

            DatabaseUtils.readExceptionFromParcel(reply);
            return reply.readInt();
        } finally {
            data.recycle();
            reply.recycle();
        }
    }

    @UnsupportedAppUsage
    private IBinder mRemote;
}
Loading