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

Commit fe9a53bc authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

Consistent dump() permission checking.

This change introduces new methods on DumpUtils that can check if the
caller has DUMP and/or PACKAGE_USAGE_STATS access.  It then moves all
existing dump() methods to use these checks so that we emit
consistent error messages.

Test: cts-tradefed run commandAndExit cts-dev -m CtsSecurityTestCases -t android.security.cts.ServicePermissionsTest
Bug: 32806790
Change-Id: Iaff6b9506818ee082b1e169c89ebe1001b3bfeca
parent 21a5edcc
Loading
Loading
Loading
Loading
+94 −0
Original line number Diff line number Diff line
@@ -16,7 +16,12 @@

package com.android.internal.util;

import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.Handler;
import android.util.Slog;

import java.io.PrintWriter;
import java.io.StringWriter;
@@ -25,6 +30,9 @@ import java.io.StringWriter;
 * Helper functions for dumping the state of system services.
 */
public final class DumpUtils {
    private static final String TAG = "DumpUtils";
    private static final boolean DEBUG = true;

    private DumpUtils() {
    }

@@ -55,4 +63,90 @@ public final class DumpUtils {
    public interface Dump {
        void dump(PrintWriter pw, String prefix);
    }

    private static void logMessage(PrintWriter pw, String msg) {
        if (DEBUG) Slog.v(TAG, msg);
        pw.println(msg);
    }

    /**
     * Verify that caller holds {@link android.Manifest.permission#DUMP}.
     *
     * @return true if access should be granted.
     * @hide
     */
    public static boolean checkDumpPermission(Context context, String tag, PrintWriter pw) {
        if (context.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
                != PackageManager.PERMISSION_GRANTED) {
            logMessage(pw, "Permission Denial: can't dump " + tag + " from from pid="
                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
                    + " due to missing android.permission.DUMP permission");
            return false;
        } else {
            return true;
        }
    }

    /**
     * Verify that caller holds
     * {@link android.Manifest.permission#PACKAGE_USAGE_STATS} and that they
     * have {@link AppOpsManager#OP_GET_USAGE_STATS} access.
     *
     * @return true if access should be granted.
     * @hide
     */
    public static boolean checkUsageStatsPermission(Context context, String tag, PrintWriter pw) {
        // System internals always get access
        final int uid = Binder.getCallingUid();
        switch (uid) {
            case android.os.Process.ROOT_UID:
            case android.os.Process.SYSTEM_UID:
            case android.os.Process.SHELL_UID:
                return true;
        }

        // Caller always needs to hold permission
        if (context.checkCallingOrSelfPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)
                != PackageManager.PERMISSION_GRANTED) {
            logMessage(pw, "Permission Denial: can't dump " + tag + " from from pid="
                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
                    + " due to missing android.permission.PACKAGE_USAGE_STATS permission");
            return false;
        }

        // And finally, caller needs to have appops access; this is totally
        // hacky, but it's the easiest way to wire this up without retrofitting
        // Binder.dump() to pass through package names.
        final AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
        final String[] pkgs = context.getPackageManager().getPackagesForUid(uid);
        if (pkgs != null) {
            for (String pkg : pkgs) {
                if (appOps.checkOpNoThrow(AppOpsManager.OP_GET_USAGE_STATS, uid,
                        pkg) == AppOpsManager.MODE_ALLOWED) {
                    appOps.noteOp(AppOpsManager.OP_GET_USAGE_STATS, uid, pkg);
                    if (DEBUG) Slog.v(TAG, "Found package " + pkg + " with "
                                + "android:get_usage_stats access");
                    return true;
                }
            }
        }

        logMessage(pw, "Permission Denial: can't dump " + tag + " from from pid="
                + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
                + " due to android:get_usage_stats app-op not allowed");
        return false;
    }

    /**
     * Verify that caller holds both {@link android.Manifest.permission#DUMP}
     * and {@link android.Manifest.permission#PACKAGE_USAGE_STATS}, and that
     * they have {@link AppOpsManager#OP_GET_USAGE_STATS} access.
     *
     * @return true if access should be granted.
     * @hide
     */
    public static boolean checkDumpAndUsageStatsPermission(Context context, String tag,
            PrintWriter pw) {
        return checkDumpPermission(context, tag, pw) && checkUsageStatsPermission(context, tag, pw);
    }
}
+3 −2
Original line number Diff line number Diff line
@@ -104,6 +104,7 @@ import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IntPair;
import com.android.server.LocalServices;
import com.android.server.policy.AccessibilityShortcutController;
@@ -2360,7 +2361,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {

    @Override
    public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
        mSecurityPolicy.enforceCallingPermission(Manifest.permission.DUMP, FUNCTION_DUMP);
        if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return;
        synchronized (mLock) {
            pw.println("ACCESSIBILITY MANAGER (dumpsys accessibility)");
            pw.println();
@@ -3658,7 +3659,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {

        @Override
        public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
            mSecurityPolicy.enforceCallingPermission(Manifest.permission.DUMP, FUNCTION_DUMP);
            if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return;
            synchronized (mLock) {
                pw.append("Service[label=" + mAccessibilityServiceInfo.getResolveInfo()
                        .loadLabel(mContext.getPackageManager()));
+2 −4
Original line number Diff line number Diff line
@@ -97,6 +97,7 @@ import com.android.internal.appwidget.IAppWidgetHost;
import com.android.internal.appwidget.IAppWidgetService;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.widget.IRemoteViewsAdapterConnection;
import com.android.internal.widget.IRemoteViewsFactory;
@@ -714,10 +715,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku

    @Override
    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP,
                                                "Permission Denial: can't dump from from pid="
                                                + Binder.getCallingPid()
                                                + ", uid=" + Binder.getCallingUid());
        if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;

        synchronized (mLock) {
            if (args.length > 0 && "--proto".equals(args[0])) {
+2 −7
Original line number Diff line number Diff line
@@ -60,6 +60,7 @@ import android.view.autofill.IAutoFillManagerClient;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.IResultReceiver;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
import com.android.server.FgThread;
import com.android.server.LocalServices;
@@ -418,13 +419,7 @@ public final class AutofillManagerService extends SystemService {

        @Override
        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
            if (mContext.checkCallingPermission(
                    Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) {
                pw.println("Permission Denial: can't dump autofill from from pid="
                        + Binder.getCallingPid()
                        + ", uid=" + Binder.getCallingUid());
                return;
            }
            if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
            synchronized (mLock) {
                pw.print("Disabled users: "); pw.println(mDisabledUsers);
                final int size = mServicesCache.size();
+2 −1
Original line number Diff line number Diff line
@@ -117,6 +117,7 @@ import android.util.StringBuilderPrinter;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.backup.IBackupTransport;
import com.android.internal.backup.IObbBackupService;
import com.android.internal.util.DumpUtils;
import com.android.server.AppWidgetBackupBridge;
import com.android.server.EventLogTags;
import com.android.server.SystemConfig;
@@ -11138,7 +11139,7 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
    }
    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
        if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
        long identityToken = Binder.clearCallingIdentity();
        try {
Loading