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

Commit 3623f321 authored by Philip P. Moltmann's avatar Philip P. Moltmann
Browse files

Add optional reasons why permissions were denied

Currently only for the ContextImpl.enforcePermission code paths and only
when 'secure debug_package_permission_check' is set.

This feature is disabled on user-builds.

Test: Enabled permission checking for my app and
      - checked runtime permision denial
      - permission denial because app is instant
Bug: 111075456
Change-Id: Ib85777db69ee490608e9dac32a3b97971c0ba215
parent 085528db
Loading
Loading
Loading
Loading
+12 −8
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@ import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.WorkSource;
import android.permission.PermissionManager;
import android.util.ArrayMap;
import android.util.DisplayMetrics;
import android.util.Singleton;
@@ -3738,6 +3739,7 @@ public class ActivityManager {
        }
        // Isolated processes don't get any permissions.
        if (UserHandle.isIsolated(uid)) {
            PermissionManager.addPermissionDenialHint("uid " + uid + " is isolated");
            return PackageManager.PERMISSION_DENIED;
        }
        // If there is a uid that owns whatever is being accessed, it has
@@ -3753,24 +3755,26 @@ public class ActivityManager {
            Slog.w(TAG, "Permission denied: checkComponentPermission() owningUid=" + owningUid,
                    here);
            */
            PermissionManager.addPermissionDenialHint(
                    "Target is not exported. owningUid=" + owningUid);
            return PackageManager.PERMISSION_DENIED;
        }
        if (permission == null) {
            return PackageManager.PERMISSION_GRANTED;
        }
        try {
            return AppGlobals.getPackageManager()
                    .checkUidPermission(permission, uid);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
        return checkUidPermission(permission, uid);
    }

    /** @hide */
    public static int checkUidPermission(String permission, int uid) {
        try {
            List<String> hints = PermissionManager.getPermissionDenialHints();
            if (hints == null) {
                return AppGlobals.getPackageManager().checkUidPermission(permission, uid);
            } else {
                return AppGlobals.getPackageManager()
                    .checkUidPermission(permission, uid);
                        .checkUidPermissionWithDenialHintForwarding(permission, uid, hints);
            }
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
+45 −19
Original line number Diff line number Diff line
@@ -68,6 +68,7 @@ import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
import android.permission.PermissionManager;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
@@ -98,6 +99,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;

@@ -1828,11 +1830,17 @@ class ContextImpl extends Context {
            }
            Slog.w(TAG, "Missing ActivityManager; assuming " + uid + " does not hold "
                    + permission);
            PermissionManager.addPermissionDenialHint("Missing ActivityManager");
            return PackageManager.PERMISSION_DENIED;
        }

        try {
            List<String> hints = PermissionManager.getPermissionDenialHints();
            if (hints == null) {
                return am.checkPermission(permission, pid, uid);
            } else {
                return am.checkPermissionWithDenialHintForwarding(permission, pid, uid, hints);
            }
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
@@ -1889,43 +1897,61 @@ class ContextImpl extends Context {
            String permission, int resultOfCheck,
            boolean selfToo, int uid, String message) {
        if (resultOfCheck != PackageManager.PERMISSION_GRANTED) {
            List<String> hints = PermissionManager.getPermissionDenialHints();
            throw new SecurityException(
                    (message != null ? (message + ": ") : "") +
                    (selfToo
                     ? "Neither user " + uid + " nor current process has "
                     : "uid " + uid + " does not have ") +
                    permission +
                    ".");
                     : "uid " + uid + " does not have ")
                            + permission + "."
                            + (hints == null ? "" : " Hints: " + hints));
        }
    }

    @Override
    public void enforcePermission(
            String permission, int pid, int uid, String message) {
        List<String> prev = PermissionManager.collectPermissionDenialHints(this, uid);
        try {
            enforce(permission,
                    checkPermission(permission, pid, uid),
                    false,
                    uid,
                    message);
        } finally {
            PermissionManager.resetPermissionDenialHints(prev);
        }
    }

    @Override
    public void enforceCallingPermission(String permission, String message) {
        List<String> prev = PermissionManager.collectPermissionDenialHints(this,
                Binder.getCallingUid());
        try {
            enforce(permission,
                    checkCallingPermission(permission),
                    false,
                    Binder.getCallingUid(),
                    message);
        } finally {
            PermissionManager.resetPermissionDenialHints(prev);
        }
    }

    @Override
    public void enforceCallingOrSelfPermission(
            String permission, String message) {
        List<String> prev = PermissionManager.collectPermissionDenialHints(this,
                Binder.getCallingUid());
        try {
            enforce(permission,
                    checkCallingOrSelfPermission(permission),
                    true,
                    Binder.getCallingUid(),
                    message);
        } finally {
            PermissionManager.resetPermissionDenialHints(prev);
        }
    }

    @Override
+1 −0
Original line number Diff line number Diff line
@@ -194,6 +194,7 @@ interface IActivityManager {
    int getProcessLimit();
    @UnsupportedAppUsage
    int checkPermission(in String permission, int pid, int uid);
    int checkPermissionWithDenialHintForwarding(in String permission, int pid, int uid, inout List<String> permissionDenialHints);
    int checkUriPermission(in Uri uri, int pid, int uid, int mode, int userId,
            in IBinder callerToken);
    void grantUriPermission(in IApplicationThread caller, in String targetPkg, in Uri uri,
+1 −0
Original line number Diff line number Diff line
@@ -108,6 +108,7 @@ interface IPackageManager {
    @UnsupportedAppUsage
    int checkPermission(String permName, String pkgName, int userId);

    int checkUidPermissionWithDenialHintForwarding(String permName, int uid, inout List<String> permissionDenialHints);
    int checkUidPermission(String permName, int uid);

    @UnsupportedAppUsage
+122 −0
Original line number Diff line number Diff line
@@ -19,15 +19,22 @@ package android.permission;
import android.Manifest;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.RemoteException;
import android.provider.Settings;
import android.util.Log;

import com.android.internal.annotations.Immutable;
import com.android.internal.util.ArrayUtils;
import com.android.server.SystemConfig;

import java.util.ArrayList;
@@ -42,6 +49,8 @@ import java.util.Objects;
@SystemApi
@SystemService(Context.PERMISSION_SERVICE)
public final class PermissionManager {
    private static final String LOG_TAG = PermissionManager.class.getSimpleName();

    /**
     * {@link android.content.pm.PackageParser} needs access without having a {@link Context}.
     *
@@ -54,6 +63,119 @@ public final class PermissionManager {

    private final IPackageManager mPackageManager;

    /** Permission denials added via {@link addPermissionDenial} */
    private static final ThreadLocal<List<String>> sPermissionDenialHints = new ThreadLocal<>();

    /**
     * Report a hint that might explain why a permission check returned
     * {@link PackageManager#PERMISSION_DENIED}.
     *
     * <p>Hints are only collected if enabled via {@link collectPermissionDenialHints} or
     * when a non-null value was passed to {@link resetPermissionDenialHints}
     *
     * @param hint A description of the reason
     *
     * @hide
     */
    public static void addPermissionDenialHint(@NonNull String hint) {
        List<String> hints = sPermissionDenialHints.get();
        if (hints == null) {
            return;
        }

        hints.add(hint);
    }

    /**
     * @return hints added via {@link #addPermissionDenialHint(String)} on this thread before.
     *
     * @hide
     */
    public static @Nullable List<String> getPermissionDenialHints() {
        if (Build.IS_USER) {
            return null;
        }

        return sPermissionDenialHints.get();
    }

    /**
     * Reset the permission denial hints for this thread.
     *
     * @param initial The initial values. If not null, enabled collection on this thread.
     *
     * @return the previously collected hints
     *
     * @hide
     */
    public static @Nullable List<String> resetPermissionDenialHints(
            @Nullable List<String> initial) {
        List<String> prev = getPermissionDenialHints();
        if (initial == null) {
            sPermissionDenialHints.remove();
        } else {
            sPermissionDenialHints.set(initial);
        }
        return prev;
    }

    /**
     * Enable permission denial hint collection if package is in
     * {@link Settings.Secure.DEBUG_PACKAGE_PERMISSION_CHECK}
     *
     * @param context A context to use
     * @param uid The uid the permission check is for.
     *
     * @return the previously collected hints
     *
     * @hide
     */
    public static @Nullable List<String> collectPermissionDenialHints(@NonNull Context context,
            int uid) {
        List<String> prev = getPermissionDenialHints();

        if (Build.IS_USER) {
            return prev;
        }

        ContentResolver cr = context.getContentResolver();
        if (cr == null) {
            return prev;
        }

        String debugSetting;
        try {
            debugSetting = Settings.Secure.getString(cr,
                    Settings.Secure.DEBUG_PACKAGE_PERMISSION_CHECK);
        } catch (IllegalStateException e) {
            Log.e(LOG_TAG, "Cannot access settings", e);
            return prev;
        }
        if (debugSetting == null) {
            return prev;
        }
        String[] debugPkgs = debugSetting.split(",");

        PackageManager pm = context.getPackageManager();
        if (pm == null) {
            return prev;
        }

        String[] packages = pm.getPackagesForUid(uid);
        if (packages == null) {
            return prev;
        }

        for (String pkg : packages) {
            if (ArrayUtils.contains(debugPkgs, pkg)) {
                sPermissionDenialHints.set(new ArrayList<>(0));
                break;
            }
        }

        return prev;
    }

    /**
     * Creates a new instance.
     *
Loading