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

Commit 911d7f41 authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

Provide calling package to ContentProviders.

The calling package is important for ContentProviders that want to
grant Uri permissions as a side effect of operations, so offer it
through a new API.  Validates the provided package against the
calling UID before returning.

Bug: 10626527
Change-Id: I7277880eebbd48444c024bcf5f69199133cd59e4
parent a61dc8e0
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -3172,6 +3172,7 @@ package android.app {
  public class AppOpsManager {
    method public int checkOp(int, int, java.lang.String);
    method public int checkOpNoThrow(int, int, java.lang.String);
    method public void checkPackage(int, java.lang.String);
    method public void finishOp(int, int, java.lang.String);
    method public void finishOp(int);
    method public int noteOp(int, int, java.lang.String);
@@ -5624,6 +5625,7 @@ package android.content {
    method public android.os.Bundle call(java.lang.String, java.lang.String, android.os.Bundle);
    method public abstract int delete(android.net.Uri, java.lang.String, java.lang.String[]);
    method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
    method public final java.lang.String getCallingPackage();
    method public final android.content.Context getContext();
    method public final android.content.pm.PathPermission[] getPathPermissions();
    method public final java.lang.String getReadPermission();
+17 −0
Original line number Diff line number Diff line
@@ -615,6 +615,23 @@ public class AppOpsManager {
        return MODE_IGNORED;
    }

    /**
     * Do a quick check to validate if a package name belongs to a UID.
     *
     * @throws SecurityException if the package name doesn't belong to the given
     *             UID, or if ownership cannot be verified.
     */
    public void checkPackage(int uid, String packageName) {
        try {
            if (mService.checkPackage(uid, packageName) != MODE_ALLOWED) {
                throw new SecurityException(
                        "Package " + packageName + " does not belong to " + uid);
            }
        } catch (RemoteException e) {
            throw new SecurityException("Unable to verify package ownership", e);
        }
    }

    /**
     * Make note of an application performing an operation.  Note that you must pass
     * in both the uid and name of the application to be checked; this function will verify
+91 −25
Original line number Diff line number Diff line
@@ -102,6 +102,8 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
    private boolean mExported;
    private boolean mNoPerms;

    private final ThreadLocal<String> mCallingPackage = new ThreadLocal<String>();

    private Transport mTransport = new Transport();

    /**
@@ -194,8 +196,14 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
                return rejectQuery(uri, projection, selection, selectionArgs, sortOrder,
                        CancellationSignal.fromTransport(cancellationSignal));
            }
            return ContentProvider.this.query(uri, projection, selection, selectionArgs, sortOrder,
            mCallingPackage.set(callingPkg);
            try {
                return ContentProvider.this.query(
                        uri, projection, selection, selectionArgs, sortOrder,
                        CancellationSignal.fromTransport(cancellationSignal));
            } finally {
                mCallingPackage.set(null);
            }
        }

        @Override
@@ -208,7 +216,12 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
            if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
                return rejectInsert(uri, initialValues);
            }
            mCallingPackage.set(callingPkg);
            try {
                return ContentProvider.this.insert(uri, initialValues);
            } finally {
                mCallingPackage.set(null);
            }
        }

        @Override
@@ -216,7 +229,12 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
            if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
                return 0;
            }
            mCallingPackage.set(callingPkg);
            try {
                return ContentProvider.this.bulkInsert(uri, initialValues);
            } finally {
                mCallingPackage.set(null);
            }
        }

        @Override
@@ -238,7 +256,12 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
                    }
                }
            }
            mCallingPackage.set(callingPkg);
            try {
                return ContentProvider.this.applyBatch(operations);
            } finally {
                mCallingPackage.set(null);
            }
        }

        @Override
@@ -246,7 +269,12 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
            if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
                return 0;
            }
            mCallingPackage.set(callingPkg);
            try {
                return ContentProvider.this.delete(uri, selection, selectionArgs);
            } finally {
                mCallingPackage.set(null);
            }
        }

        @Override
@@ -255,7 +283,12 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
            if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
                return 0;
            }
            mCallingPackage.set(callingPkg);
            try {
                return ContentProvider.this.update(uri, values, selection, selectionArgs);
            } finally {
                mCallingPackage.set(null);
            }
        }

        @Override
@@ -263,8 +296,13 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
                String callingPkg, Uri uri, String mode, ICancellationSignal cancellationSignal)
                throws FileNotFoundException {
            enforceFilePermission(callingPkg, uri, mode);
            mCallingPackage.set(callingPkg);
            try {
                return ContentProvider.this.openFile(
                        uri, mode, CancellationSignal.fromTransport(cancellationSignal));
            } finally {
                mCallingPackage.set(null);
            }
        }

        @Override
@@ -272,13 +310,23 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
                String callingPkg, Uri uri, String mode, ICancellationSignal cancellationSignal)
                throws FileNotFoundException {
            enforceFilePermission(callingPkg, uri, mode);
            mCallingPackage.set(callingPkg);
            try {
                return ContentProvider.this.openAssetFile(
                        uri, mode, CancellationSignal.fromTransport(cancellationSignal));
            } finally {
                mCallingPackage.set(null);
            }
        }

        @Override
        public Bundle call(String callingPkg, String method, String arg, Bundle extras) {
            return ContentProvider.this.callFromPackage(callingPkg, method, arg, extras);
            mCallingPackage.set(callingPkg);
            try {
                return ContentProvider.this.call(method, arg, extras);
            } finally {
                mCallingPackage.set(null);
            }
        }

        @Override
@@ -290,8 +338,13 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
        public AssetFileDescriptor openTypedAssetFile(String callingPkg, Uri uri, String mimeType,
                Bundle opts, ICancellationSignal cancellationSignal) throws FileNotFoundException {
            enforceFilePermission(callingPkg, uri, "r");
            mCallingPackage.set(callingPkg);
            try {
                return ContentProvider.this.openTypedAssetFile(
                        uri, mimeType, opts, CancellationSignal.fromTransport(cancellationSignal));
            } finally {
                mCallingPackage.set(null);
            }
        }

        @Override
@@ -460,6 +513,28 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
        return mContext;
    }

    /**
     * Return the package name of the caller that initiated the request being
     * processed on the current thread. The returned package will have been
     * verified to belong to the calling UID. Returns {@code null} if not
     * currently processing a request.
     * <p>
     * This will always return {@code null} when processing
     * {@link #getType(Uri)} or {@link #getStreamTypes(Uri, String)} requests.
     *
     * @see Binder#getCallingUid()
     * @see Context#grantUriPermission(String, Uri, int)
     * @throws SecurityException if the calling package doesn't belong to the
     *             calling UID.
     */
    public final String getCallingPackage() {
        final String pkg = mCallingPackage.get();
        if (pkg != null) {
            mTransport.mAppOpsManager.checkPackage(Binder.getCallingUid(), pkg);
        }
        return pkg;
    }

    /**
     * Change the permission required to read data from the content
     * provider.  This is normally set for you from its manifest information
@@ -529,8 +604,6 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
    /** @hide */
    public final void setAppOps(int readOp, int writeOp) {
        if (!mNoPerms) {
            mTransport.mAppOpsManager = (AppOpsManager)mContext.getSystemService(
                    Context.APP_OPS_SERVICE);
            mTransport.mReadOp = readOp;
            mTransport.mWriteOp = writeOp;
        }
@@ -1413,6 +1486,8 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
         */
        if (mContext == null) {
            mContext = context;
            mTransport.mAppOpsManager = (AppOpsManager) mContext.getSystemService(
                    Context.APP_OPS_SERVICE);
            mMyUid = Process.myUid();
            if (info != null) {
                setReadPermission(info.readPermission);
@@ -1451,15 +1526,6 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
        return results;
    }

    /**
     * @hide
     * Front-end to {@link #call(String, String, android.os.Bundle)} that provides the name
     * of the calling package.
     */
    public Bundle callFromPackage(String callingPackag, String method, String arg, Bundle extras) {
        return call(method, arg, extras);
    }

    /**
     * Call a provider-defined method.  This can be used to implement
     * interfaces that are cheaper and/or unnatural for a table-like
+12 −8
Original line number Diff line number Diff line
@@ -34,7 +34,6 @@ import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.graphics.Point;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
@@ -42,8 +41,6 @@ import android.os.ParcelFileDescriptor.OnCloseListener;
import android.provider.DocumentsContract.Document;
import android.util.Log;

import com.android.internal.util.ArrayUtils;

import libcore.io.IoUtils;

import java.io.FileNotFoundException;
@@ -332,15 +329,22 @@ public abstract class DocumentsProvider extends ContentProvider {
        throw new UnsupportedOperationException("Update not supported");
    }

    /** {@hide} */
    /**
     * Implementation is provided by the parent class. Can be overridden to
     * provide additional functionality, but subclasses <em>must</em> always
     * call the superclass. If the superclass returns {@code null}, the subclass
     * may implement custom behavior.
     *
     * @see #openDocument(String, String, CancellationSignal)
     * @see #deleteDocument(String)
     */
    @Override
    public final Bundle callFromPackage(
            String callingPackage, String method, String arg, Bundle extras) {
    public Bundle call(String method, String arg, Bundle extras) {
        final Context context = getContext();

        if (!method.startsWith("android:")) {
            // Let non-platform methods pass through
            return super.callFromPackage(callingPackage, method, arg, extras);
            return super.call(method, arg, extras);
        }

        final String documentId = extras.getString(Document.COLUMN_DOCUMENT_ID);
@@ -368,7 +372,7 @@ public abstract class DocumentsProvider extends ContentProvider {
                if (!callerHasManage) {
                    final Uri newDocumentUri = DocumentsContract.buildDocumentUri(
                            mAuthority, newDocumentId);
                    context.grantUriPermission(callingPackage, newDocumentUri,
                    context.grantUriPermission(getCallingPackage(), newDocumentUri,
                            Intent.FLAG_GRANT_READ_URI_PERMISSION
                            | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
                            | Intent.FLAG_PERSIST_GRANT_URI_PERMISSION);
+1 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ interface IAppOpsService {
    // These first methods are also called by native code, so must
    // be kept in sync with frameworks/native/include/binder/IAppOpsService.h
    int checkOperation(int code, int uid, String packageName);
    int checkPackage(int uid, String packageName);
    int noteOperation(int code, int uid, String packageName);
    int startOperation(IBinder token, int code, int uid, String packageName);
    void finishOperation(IBinder token, int code, int uid, String packageName);
Loading