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

Commit b8c81768 authored by Jeff Sharkey's avatar Jeff Sharkey Committed by Android (Google) Code Review
Browse files

Merge "Let <path-permission> block unprotected providers."

parents b9e8d86b 110a6b62
Loading
Loading
Loading
Loading
+88 −59
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package android.content;

import static android.content.pm.PackageManager.PERMISSION_GRANTED;

import android.content.pm.PackageManager;
import android.content.pm.PathPermission;
import android.content.pm.ProviderInfo;
@@ -30,6 +32,7 @@ import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserId;
import android.util.Log;

import java.io.File;
@@ -267,41 +270,58 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
            return CancellationSignal.createTransport();
        }

        private void enforceReadPermission(Uri uri) {
        private boolean hasReadPermission(Uri uri) {
            final Context context = getContext();
            final int pid = Binder.getCallingPid();
            final int uid = Binder.getCallingUid();

            if (uid == mMyUid) {
                return;
            }
                return true;

            final Context context = getContext();
            final String rperm = getReadPermission();
            final int pid = Binder.getCallingPid();
            if (mExported && (rperm == null
                    || context.checkPermission(rperm, pid, uid)
                    == PackageManager.PERMISSION_GRANTED)) {
                return;
            } else if (mExported) {
                final String componentPerm = getReadPermission();
                if (componentPerm != null
                        && (context.checkPermission(componentPerm, pid, uid) == PERMISSION_GRANTED)) {
                    return true;
                }

            PathPermission[] pps = getPathPermissions();
                // track if unprotected read is allowed; any denied
                // <path-permission> below removes this ability
                boolean allowDefaultRead = (componentPerm == null);

                final PathPermission[] pps = getPathPermissions();
                if (pps != null) {
                    final String path = uri.getPath();
                int i = pps.length;
                while (i > 0) {
                    i--;
                    final PathPermission pp = pps[i];
                    final String pprperm = pp.getReadPermission();
                    if (pprperm != null && pp.match(path)) {
                        if (context.checkPermission(pprperm, pid, uid)
                                == PackageManager.PERMISSION_GRANTED) {
                            return;
                    for (PathPermission pp : pps) {
                        final String pathPerm = pp.getReadPermission();
                        if (pathPerm != null && pp.match(path)) {
                            if (context.checkPermission(pathPerm, pid, uid) == PERMISSION_GRANTED) {
                                return true;
                            } else {
                                // any denied <path-permission> means we lose
                                // default <provider> access.
                                allowDefaultRead = false;
                            }
                        }
                    }
                }

            if (context.checkUriPermission(uri, pid, uid,
                    Intent.FLAG_GRANT_READ_URI_PERMISSION)
                    == PackageManager.PERMISSION_GRANTED) {
                // if we passed <path-permission> checks above, and no default
                // <provider> permission, then allow access.
                if (allowDefaultRead) return true;
            }

            // last chance, check against any uri grants
            if (context.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION)
                    == PERMISSION_GRANTED) {
                return true;
            }

            return false;
        }

        private void enforceReadPermission(Uri uri) {
            if (hasReadPermission(uri)) {
                return;
            }

@@ -309,45 +329,54 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
                    + ContentProvider.this.getClass().getName()
                    + " uri " + uri + " from pid=" + Binder.getCallingPid()
                    + ", uid=" + Binder.getCallingUid()
                    + " requires " + rperm;
                    + " requires " + getReadPermission();
            throw new SecurityException(msg);
        }

        private boolean hasWritePermission(Uri uri) {
            final Context context = getContext();
            final int pid = Binder.getCallingPid();
            final int uid = Binder.getCallingUid();

            if (uid == mMyUid) {
                return true;
            }

            final Context context = getContext();
            final String wperm = getWritePermission();
            final int pid = Binder.getCallingPid();
            if (mExported && (wperm == null
                    || context.checkPermission(wperm, pid, uid)
                    == PackageManager.PERMISSION_GRANTED)) {
            } else if (mExported) {
                final String componentPerm = getWritePermission();
                if (componentPerm != null
                        && (context.checkPermission(componentPerm, pid, uid) == PERMISSION_GRANTED)) {
                    return true;
                }

            PathPermission[] pps = getPathPermissions();
                // track if unprotected write is allowed; any denied
                // <path-permission> below removes this ability
                boolean allowDefaultWrite = (componentPerm == null);

                final PathPermission[] pps = getPathPermissions();
                if (pps != null) {
                    final String path = uri.getPath();
                int i = pps.length;
                while (i > 0) {
                    i--;
                    final PathPermission pp = pps[i];
                    final String ppwperm = pp.getWritePermission();
                    if (ppwperm != null && pp.match(path)) {
                        if (context.checkPermission(ppwperm, pid, uid)
                                == PackageManager.PERMISSION_GRANTED) {
                    for (PathPermission pp : pps) {
                        final String pathPerm = pp.getWritePermission();
                        if (pathPerm != null && pp.match(path)) {
                            if (context.checkPermission(pathPerm, pid, uid) == PERMISSION_GRANTED) {
                                return true;
                            } else {
                                // any denied <path-permission> means we lose
                                // default <provider> access.
                                allowDefaultWrite = false;
                            }
                        }
                    }
                }

            if (context.checkUriPermission(uri, pid, uid,
                    Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
                    == PackageManager.PERMISSION_GRANTED) {
                // if we passed <path-permission> checks above, and no default
                // <provider> permission, then allow access.
                if (allowDefaultWrite) return true;
            }

            // last chance, check against any uri grants
            if (context.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
                    == PERMISSION_GRANTED) {
                return true;
            }

+74 −51
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@
package com.android.server.am;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import com.android.internal.R;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.ProcessStats;
@@ -55,6 +57,7 @@ import android.content.BroadcastReceiver;
import android.content.ClipData;
import android.content.ComponentCallbacks2;
import android.content.ComponentName;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
@@ -4543,76 +4546,91 @@ public final class ActivityManagerService extends ActivityManagerNative
        throw new SecurityException(msg);
    }
    private final boolean checkHoldingPermissionsLocked(IPackageManager pm,
            ProviderInfo pi, Uri uri, int uid, int modeFlags) {
        boolean readPerm = (modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0;
        boolean writePerm = (modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0;
    /**
     * Determine if UID is holding permissions required to access {@link Uri} in
     * the given {@link ProviderInfo}. Final permission checking is always done
     * in {@link ContentProvider}.
     */
    private final boolean checkHoldingPermissionsLocked(
            IPackageManager pm, ProviderInfo pi, Uri uri, int uid, int modeFlags) {
        if (DEBUG_URI_PERMISSION) Slog.v(TAG,
                "checkHoldingPermissionsLocked: uri=" + uri + " uid=" + uid);
        try {
            // Is the component private from the target uid?
            final boolean prv = !pi.exported && pi.applicationInfo.uid != uid;
            // Acceptable if the there is no read permission needed from the
            // target or the target is holding the read permission.
            if (!readPerm) {
                if ((!prv && pi.readPermission == null) ||
                        (pm.checkUidPermission(pi.readPermission, uid)
                                == PackageManager.PERMISSION_GRANTED)) {
                    readPerm = true;
                }
        if (pi.applicationInfo.uid == uid) {
            return true;
        } else if (!pi.exported) {
            return false;
        }
            // Acceptable if the there is no write permission needed from the
            // target or the target is holding the read permission.
            if (!writePerm) {
                if (!prv && (pi.writePermission == null) ||
                        (pm.checkUidPermission(pi.writePermission, uid)
                                == PackageManager.PERMISSION_GRANTED)) {
                    writePerm = true;
        boolean readMet = (modeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0;
        boolean writeMet = (modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0;
        try {
            // check if target holds top-level <provider> permissions
            if (!readMet && pi.readPermission != null
                    && (pm.checkUidPermission(pi.readPermission, uid) == PERMISSION_GRANTED)) {
                readMet = true;
            }
            if (!writeMet && pi.writePermission != null
                    && (pm.checkUidPermission(pi.writePermission, uid) == PERMISSION_GRANTED)) {
                writeMet = true;
            }
            // Acceptable if there is a path permission matching the URI that
            // the target holds the permission on.
            PathPermission[] pps = pi.pathPermissions;
            if (pps != null && (!readPerm || !writePerm)) {
            // track if unprotected read/write is allowed; any denied
            // <path-permission> below removes this ability
            boolean allowDefaultRead = pi.readPermission == null;
            boolean allowDefaultWrite = pi.writePermission == null;
            // check if target holds any <path-permission> that match uri
            final PathPermission[] pps = pi.pathPermissions;
            if (pps != null) {
                final String path = uri.getPath();
                int i = pps.length;
                while (i > 0 && (!readPerm || !writePerm)) {
                while (i > 0 && (!readMet || !writeMet)) {
                    i--;
                    PathPermission pp = pps[i];
                    if (!readPerm) {
                    if (pp.match(path)) {
                        if (!readMet) {
                            final String pprperm = pp.getReadPermission();
                            if (DEBUG_URI_PERMISSION) Slog.v(TAG, "Checking read perm for "
                                    + pprperm + " for " + pp.getPath()
                                    + ": match=" + pp.match(path)
                                    + " check=" + pm.checkUidPermission(pprperm, uid));
                        if (pprperm != null && pp.match(path) &&
                                (pm.checkUidPermission(pprperm, uid)
                                        == PackageManager.PERMISSION_GRANTED)) {
                            readPerm = true;
                            if (pprperm != null) {
                                if (pm.checkUidPermission(pprperm, uid) == PERMISSION_GRANTED) {
                                    readMet = true;
                                } else {
                                    allowDefaultRead = false;
                                }
                            }
                        }
                    if (!writePerm) {
                        if (!writeMet) {
                            final String ppwperm = pp.getWritePermission();
                            if (DEBUG_URI_PERMISSION) Slog.v(TAG, "Checking write perm "
                                    + ppwperm + " for " + pp.getPath()
                                    + ": match=" + pp.match(path)
                                    + " check=" + pm.checkUidPermission(ppwperm, uid));
                        if (ppwperm != null && pp.match(path) &&
                                (pm.checkUidPermission(ppwperm, uid)
                                        == PackageManager.PERMISSION_GRANTED)) {
                            writePerm = true;
                            if (ppwperm != null) {
                                if (pm.checkUidPermission(ppwperm, uid) == PERMISSION_GRANTED) {
                                    writeMet = true;
                                } else {
                                    allowDefaultWrite = false;
                                }
                            }
                        }
                    }
                }
            }
            // grant unprotected <provider> read/write, if not blocked by
            // <path-permission> above
            if (allowDefaultRead) readMet = true;
            if (allowDefaultWrite) writeMet = true;
        } catch (RemoteException e) {
            return false;
        }
        return readPerm && writePerm;
        return readMet && writeMet;
    }
    private final boolean checkUriPermissionLocked(Uri uri, int uid,
@@ -5819,6 +5837,11 @@ public final class ActivityManagerService extends ActivityManagerNative
        return providers;
    }
    /**
     * Check if {@link ProcessRecord} has a possible chance at accessing the
     * given {@link ProviderInfo}. Final permission checking is always done
     * in {@link ContentProvider}.
     */
    private final String checkContentProviderPermissionLocked(
            ProviderInfo cpi, ProcessRecord r) {
        final int callingPid = (r != null) ? r.pid : Binder.getCallingPid();