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

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

Check cross-user interactions for permissions and app-ops operations

1.
We want to be quite permissive here as without being able to check
permissions or appops nothing else works. Hence allow cross-user
interactions if any cross-user permission is granted.

2.
Also we need to prevent infinite recursion as we are checking permission
and appops inside of permission and app-op checks.

2.
Clear Binder.callingUid when checking permission inside system server

Makeing the binder call "checkPermission" usually sets
Binder.callingUid to the calling processes UID. Hence clearing the
calling UID is superflous. If the call is inside the system server
though "checkPermission" is not a binder all, it is only a method call.
Hence Binder.callingUid might still be set to the app that called the
system server. This can lead to problems as not every app can check the
same permission the system server can check.

E.g. the system server can check permission accross user boundaries,
most regular apps can't

Test: atest CtsPermissionHostTestCases CtsAppOpHostTestCases // execute the new paths for both full users and profiles
      atest ManagedProfileTest#testCameraPolicy              // a previous version of the patch caused a regression in this test
      atest AccountManagerXUserTest                          // a previous version of the patch caused a regression in this test
      Accessed clipboard from chrome in work profile
Fixes: 153996875
Change-Id: I2a8a84f574fbf07ab88ed991445830fa85aa4450
parent 5f9b30a1
Loading
Loading
Loading
Loading
+23 −4
Original line number Diff line number Diff line
@@ -46,20 +46,39 @@ public abstract class ActivityManagerInternal {


    // Access modes for handleIncomingUser.
    /**
     * Allows access to a caller with {@link android.Manifest.permission#INTERACT_ACROSS_USERS} or
     * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}.
     */
    public static final int ALLOW_NON_FULL = 0;
    /**
     * Allows access to a caller with {@link android.Manifest.permission#INTERACT_ACROSS_USERS}
     * if in the same profile group.
     * or {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} if in the same profile
     * group.
     * Otherwise, {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} is required.
     */
    public static final int ALLOW_NON_FULL_IN_PROFILE = 1;
    public static final int ALLOW_NON_FULL_IN_PROFILE_OR_FULL = 1;
    /**
     * Allows access to a caller with {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}
     * only.
     */
    public static final int ALLOW_FULL_ONLY = 2;
    /**
     * Allows access to a caller with {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES}
     * or {@link android.Manifest.permission#INTERACT_ACROSS_USERS} if in the same profile group.
     * or {@link android.Manifest.permission#INTERACT_ACROSS_USERS} or
     * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} if in the same profile group.
     * Otherwise, {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} is required.
     */
    public static final int ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE = 3;
    public static final int ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_FULL = 3;
    /**
     * Requires {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES},
     * {@link android.Manifest.permission#INTERACT_ACROSS_USERS}, or
     * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} if in same profile group,
     * otherwise {@link android.Manifest.permission#INTERACT_ACROSS_USERS} or
     * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}. (so this is an extension
     * to {@link #ALLOW_NON_FULL})
     */
    public static final int ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_NON_FULL = 4;

    /**
     * Verify that calling app has access to the given provider.
+65 −19
Original line number Diff line number Diff line
@@ -6741,10 +6741,14 @@ public class AppOpsManager {
     */
    @RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
    public void setUidMode(int code, int uid, @Mode int mode) {
        // Clear calling UID to handle calls from inside the system server. See #noteOpNoThrow
        long token = Binder.clearCallingIdentity();
        try {
            mService.setUidMode(code, uid, mode);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

@@ -6762,11 +6766,7 @@ public class AppOpsManager {
    @TestApi
    @RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
    public void setUidMode(@NonNull String appOp, int uid, @Mode int mode) {
        try {
            mService.setUidMode(AppOpsManager.strOpToOp(appOp), uid, mode);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
        setUidMode(AppOpsManager.strOpToOp(appOp), uid, mode);
    }

    /** @hide */
@@ -6795,10 +6795,14 @@ public class AppOpsManager {
    @TestApi
    @RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
    public void setMode(int code, int uid, String packageName, @Mode int mode) {
        // Clear calling UID to handle calls from inside the system server. See #noteOpNoThrow
        long token = Binder.clearCallingIdentity();
        try {
            mService.setMode(code, uid, packageName, mode);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

@@ -6818,11 +6822,7 @@ public class AppOpsManager {
    @RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
    public void setMode(@NonNull String op, int uid, @Nullable String packageName,
            @Mode int mode) {
        try {
            mService.setMode(strOpToOp(op), uid, packageName, mode);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
        setMode(strOpToOp(op), uid, packageName, mode);
    }

    /**
@@ -7298,10 +7298,14 @@ public class AppOpsManager {
     * @hide
     */
    public int unsafeCheckOpRawNoThrow(int op, int uid, @NonNull String packageName) {
        // Clear calling UID to handle calls from inside the system server. See #noteOpNoThrow
        long token = Binder.clearCallingIdentity();
        try {
            return mService.checkOperationRaw(op, uid, packageName);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

@@ -7473,8 +7477,20 @@ public class AppOpsManager {
                }
            }

            int mode = mService.noteOperation(op, uid, packageName, attributionTag,
            int mode;
            // Making the binder call "noteOperation" usually sets Binder.callingUid to the calling
            // processes UID. Hence clearing the calling UID is superfluous.
            // If the call is inside the system server though "noteOperation" is not a binder all,
            // it is only a method call. Hence Binder.callingUid might still be set to the app that
            // called the system server. This can lead to problems as not every app can see the
            // same appops the system server can see.
            long token = Binder.clearCallingIdentity();
            try {
                mode = mService.noteOperation(op, uid, packageName, attributionTag,
                        collectionMode == COLLECT_ASYNC, message, shouldCollectMessage);
            } finally {
                Binder.restoreCallingIdentity(token);
            }

            if (mode == MODE_ALLOWED) {
                if (collectionMode == COLLECT_SELF) {
@@ -7637,10 +7653,17 @@ public class AppOpsManager {
                }
            }

            int mode = mService.noteProxyOperation(op, proxiedUid, proxiedPackageName,
            int mode;
            // Clear calling UID to handle calls from inside the system server. See #noteOpNoThrow
            long token = Binder.clearCallingIdentity();
            try {
                mode = mService.noteProxyOperation(op, proxiedUid, proxiedPackageName,
                        proxiedAttributionTag, myUid, mContext.getOpPackageName(),
                        mContext.getAttributionTag(), collectionMode == COLLECT_ASYNC, message,
                        shouldCollectMessage);
            } finally {
                Binder.restoreCallingIdentity(token);
            }

            if (mode == MODE_ALLOWED) {
                if (collectionMode == COLLECT_SELF) {
@@ -7690,6 +7713,8 @@ public class AppOpsManager {
     */
    @UnsupportedAppUsage
    public int checkOp(int op, int uid, String packageName) {
        // Clear calling UID to handle calls from inside the system server. See #noteOpNoThrow
        long token = Binder.clearCallingIdentity();
        try {
            int mode = mService.checkOperation(op, uid, packageName);
            if (mode == MODE_ERRORED) {
@@ -7698,6 +7723,8 @@ public class AppOpsManager {
            return mode;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

@@ -7708,11 +7735,15 @@ public class AppOpsManager {
     */
    @UnsupportedAppUsage
    public int checkOpNoThrow(int op, int uid, String packageName) {
        // Clear calling UID to handle calls from inside the system server. See #noteOpNoThrow
        long token = Binder.clearCallingIdentity();
        try {
            int mode = mService.checkOperation(op, uid, packageName);
            return mode == AppOpsManager.MODE_FOREGROUND ? AppOpsManager.MODE_ALLOWED : mode;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

@@ -7964,9 +7995,16 @@ public class AppOpsManager {
                }
            }

            int mode = mService.startOperation(getClientId(), op, uid, packageName,
                    attributionTag, startIfModeDefault, collectionMode == COLLECT_ASYNC, message,
                    shouldCollectMessage);
            int mode;
            // Clear calling UID to handle calls from inside the system server. See #noteOpNoThrow
            long token = Binder.clearCallingIdentity();
            try {
                mode = mService.startOperation(getClientId(), op, uid, packageName,
                        attributionTag, startIfModeDefault, collectionMode == COLLECT_ASYNC,
                        message, shouldCollectMessage);
            } finally {
                Binder.restoreCallingIdentity(token);
            }

            if (mode == MODE_ALLOWED) {
                if (collectionMode == COLLECT_SELF) {
@@ -8029,10 +8067,14 @@ public class AppOpsManager {
     */
    public void finishOp(int op, int uid, @NonNull String packageName,
            @Nullable String attributionTag) {
        // Clear calling UID to handle calls from inside the system server. See #noteOpNoThrow
        long token = Binder.clearCallingIdentity();
        try {
            mService.finishOperation(getClientId(), op, uid, packageName, attributionTag);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

@@ -8624,10 +8666,14 @@ public class AppOpsManager {
    // TODO: Uncomment below annotation once b/73559440 is fixed
    // @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true)
    public boolean isOperationActive(int code, int uid, String packageName) {
        // Clear calling UID to handle calls from inside the system server. See #noteOpNoThrow
        long token = Binder.clearCallingIdentity();
        try {
            return mService.isOperationActive(code, uid, packageName);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

+15 −0
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import android.content.Context;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.permission.SplitPermissionInfoParcelable;
import android.os.Binder;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -543,10 +544,15 @@ public final class PermissionManager {
                    + permission);
            return PackageManager.PERMISSION_DENIED;
        }
        // Clear Binder.callingUid in case this is called inside the system server. See
        // more extensive comment in checkPackageNamePermissionUncached
        long token = Binder.clearCallingIdentity();
        try {
            return am.checkPermission(permission, pid, uid);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

@@ -679,11 +685,20 @@ public final class PermissionManager {
    /* @hide */
    private static int checkPackageNamePermissionUncached(
            String permName, String pkgName, @UserIdInt int userId) {
        // Makeing the binder call "checkPermission" usually sets Binder.callingUid to the calling
        // processes UID. Hence clearing the calling UID is superflous.
        // If the call is inside the system server though "checkPermission" is not a binder all, it
        // is only a method call. Hence Binder.callingUid might still be set to the app that called
        // the system server. This can lead to problems as not every app can check the same
        // permissions the system server can check.
        long token = Binder.clearCallingIdentity();
        try {
            return ActivityThread.getPermissionManager().checkPermission(
                    permName, pkgName, userId);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

+3 −3
Original line number Diff line number Diff line
@@ -2609,12 +2609,12 @@ public final class ActiveServices {

    private int getAllowMode(Intent service, @Nullable String callingPackage) {
        if (callingPackage == null || service.getComponent() == null) {
            return ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE;
            return ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE_OR_FULL;
        }
        if (callingPackage.equals(service.getComponent().getPackageName())) {
            return ActivityManagerInternal.ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE;
            return ActivityManagerInternal.ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_FULL;
        } else {
            return ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE;
            return ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE_OR_FULL;
        }
    }

+14 −8
Original line number Diff line number Diff line
@@ -23,10 +23,11 @@ import static android.app.ActivityManager.USER_OP_ERROR_IS_SYSTEM;
import static android.app.ActivityManager.USER_OP_ERROR_RELATED_USERS_CANNOT_STOP;
import static android.app.ActivityManager.USER_OP_IS_CURRENT;
import static android.app.ActivityManager.USER_OP_SUCCESS;
import static android.app.ActivityManagerInternal.ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE;
import static android.app.ActivityManagerInternal.ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_FULL;
import static android.app.ActivityManagerInternal.ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_NON_FULL;
import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE_OR_FULL;
import static android.os.Process.SHELL_UID;
import static android.os.Process.SYSTEM_UID;

@@ -1911,11 +1912,12 @@ class UserController implements Handler.Callback {
                    callingUid, -1, true) != PackageManager.PERMISSION_GRANTED) {
                // If the caller does not have either permission, they are always doomed.
                allow = false;
            } else if (allowMode == ALLOW_NON_FULL) {
            } else if (allowMode == ALLOW_NON_FULL
                    || allowMode == ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_NON_FULL) {
                // We are blanket allowing non-full access, you lucky caller!
                allow = true;
            } else if (allowMode == ALLOW_NON_FULL_IN_PROFILE
                        || allowMode == ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE) {
            } else if (allowMode == ALLOW_NON_FULL_IN_PROFILE_OR_FULL
                        || allowMode == ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_FULL) {
                // We may or may not allow this depending on whether the two users are
                // in the same profile.
                allow = isSameProfileGroup;
@@ -1942,12 +1944,15 @@ class UserController implements Handler.Callback {
                    builder.append("; this requires ");
                    builder.append(INTERACT_ACROSS_USERS_FULL);
                    if (allowMode != ALLOW_FULL_ONLY) {
                        if (allowMode == ALLOW_NON_FULL || isSameProfileGroup) {
                        if (allowMode == ALLOW_NON_FULL
                                || allowMode == ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_NON_FULL
                                || isSameProfileGroup) {
                            builder.append(" or ");
                            builder.append(INTERACT_ACROSS_USERS);
                        }
                        if (isSameProfileGroup
                                && allowMode == ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE) {
                                && (allowMode == ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_FULL
                                || allowMode == ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_NON_FULL)) {
                            builder.append(" or ");
                            builder.append(INTERACT_ACROSS_PROFILES);
                        }
@@ -1974,7 +1979,8 @@ class UserController implements Handler.Callback {
    private boolean canInteractWithAcrossProfilesPermission(
            int allowMode, boolean isSameProfileGroup, int callingPid, int callingUid,
            String callingPackage) {
        if (allowMode != ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE) {
        if (allowMode != ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_FULL
                && allowMode != ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_NON_FULL) {
            return false;
        }
        if (!isSameProfileGroup) {
Loading