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

Commit 91add43a authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

DO NOT MERGE. Check provider access for content changes.

For an app to either send or receive content change notifications,
require that they have some level of access to the underlying
provider.

Without these checks, a malicious app could sniff sensitive user data
from the notifications of otherwise private providers.

Test: builds, boots, PoC app now fails
Bug: 32555637
Change-Id: If2dcd45cb0a9f1fb3b93e39fc7b8ae9c34c2fdef
parent 35b84533
Loading
Loading
Loading
Loading
+5 −0
Original line number Original line Diff line number Diff line
@@ -22,6 +22,11 @@ package android.app;
 * @hide Only for use within the system server.
 * @hide Only for use within the system server.
 */
 */
public abstract class ActivityManagerInternal {
public abstract class ActivityManagerInternal {
    /**
     * Verify that calling app has access to the given provider.
     */
    public abstract String checkContentProviderAccess(String authority, int userId);

    // Called by the power manager.
    // Called by the power manager.
    public abstract void onWakefulnessChanged(int wakefulness);
    public abstract void onWakefulnessChanged(int wakefulness);


+43 −0
Original line number Original line Diff line number Diff line
@@ -8991,6 +8991,44 @@ public final class ActivityManagerService extends ActivityManagerNative
        return providers;
        return providers;
    }
    }
    /**
     * Check if the calling UID has a possible chance at accessing the provider
     * at the given authority and user.
     */
    public String checkContentProviderAccess(String authority, int userId) {
        if (userId == UserHandle.USER_ALL) {
            mContext.enforceCallingOrSelfPermission(
                    Manifest.permission.INTERACT_ACROSS_USERS_FULL, TAG);
            userId = UserHandle.getCallingUserId();
        }
        ProviderInfo cpi = null;
        try {
            cpi = AppGlobals.getPackageManager().resolveContentProvider(authority,
                    STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);
        } catch (RemoteException ignored) {
        }
        if (cpi == null) {
            // TODO: make this an outright failure in a future platform release;
            // until then anonymous content notifications are unprotected
            //return "Failed to find provider " + authority + " for user " + userId;
            return null;
        }
        ProcessRecord r = null;
        synchronized (mPidsSelfLocked) {
            r = mPidsSelfLocked.get(Binder.getCallingPid());
        }
        if (r == null) {
            return "Failed to find PID " + Binder.getCallingPid();
        }
        synchronized (this) {
            return checkContentProviderPermissionLocked(cpi, r, userId, true);
        }
    }
    /**
    /**
     * Check if {@link ProcessRecord} has a possible chance at accessing the
     * Check if {@link ProcessRecord} has a possible chance at accessing the
     * given {@link ProviderInfo}. Final permission checking is always done
     * given {@link ProviderInfo}. Final permission checking is always done
@@ -19653,6 +19691,11 @@ public final class ActivityManagerService extends ActivityManagerNative
    }
    }
    private final class LocalService extends ActivityManagerInternal {
    private final class LocalService extends ActivityManagerInternal {
        @Override
        public String checkContentProviderAccess(String authority, int userId) {
            return ActivityManagerService.this.checkContentProviderAccess(authority, userId);
        }
        @Override
        @Override
        public void onWakefulnessChanged(int wakefulness) {
        public void onWakefulnessChanged(int wakefulness) {
            ActivityManagerService.this.onWakefulnessChanged(wakefulness);
            ActivityManagerService.this.onWakefulnessChanged(wakefulness);
+43 −23
Original line number Original line Diff line number Diff line
@@ -19,11 +19,14 @@ package com.android.server.content;
import android.Manifest;
import android.Manifest;
import android.accounts.Account;
import android.accounts.Account;
import android.app.ActivityManager;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.ActivityManagerNative;
import android.content.ComponentName;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Context;
import android.content.IContentService;
import android.content.IContentService;
import android.content.ISyncStatusObserver;
import android.content.ISyncStatusObserver;
import android.content.Intent;
import android.content.PeriodicSync;
import android.content.PeriodicSync;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager;
import android.content.SyncAdapterType;
import android.content.SyncAdapterType;
@@ -46,6 +49,8 @@ import android.util.Log;
import android.util.Slog;
import android.util.Slog;
import android.util.SparseIntArray;
import android.util.SparseIntArray;


import com.android.server.LocalServices;

import java.io.FileDescriptor;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.io.PrintWriter;
import java.security.InvalidParameterException;
import java.security.InvalidParameterException;
@@ -172,16 +177,17 @@ public final class ContentService extends IContentService.Stub {
            throw new IllegalArgumentException("You must pass a valid uri and observer");
            throw new IllegalArgumentException("You must pass a valid uri and observer");
        }
        }


        enforceCrossUserPermission(userHandle,
        final int uid = Binder.getCallingUid();
                "no permission to observe other users' provider view");
        final int pid = Binder.getCallingPid();


        if (userHandle < 0) {
        userHandle = handleIncomingUser(uri, pid, uid,
            if (userHandle == UserHandle.USER_CURRENT) {
                Intent.FLAG_GRANT_READ_URI_PERMISSION, userHandle);
                userHandle = ActivityManager.getCurrentUser();

            } else if (userHandle != UserHandle.USER_ALL) {
        final String msg = LocalServices.getService(ActivityManagerInternal.class)
                throw new InvalidParameterException("Bad user handle for registerContentObserver: "
                .checkContentProviderAccess(uri.getAuthority(), userHandle);
                        + userHandle);
        if (msg != null) {
            }
            Log.w(TAG, "Ignoring content changes for " + uri + " from " + uid + ": " + msg);
            return;
        }
        }


        synchronized (mRootNode) {
        synchronized (mRootNode) {
@@ -224,24 +230,20 @@ public final class ContentService extends IContentService.Stub {
                    + " from observer " + observer + ", syncToNetwork " + syncToNetwork);
                    + " from observer " + observer + ", syncToNetwork " + syncToNetwork);
        }
        }


        // Notify for any user other than the caller's own requires permission.
        final int uid = Binder.getCallingUid();
        final int pid = Binder.getCallingPid();
        final int callingUserHandle = UserHandle.getCallingUserId();
        final int callingUserHandle = UserHandle.getCallingUserId();
        if (userHandle != callingUserHandle) {
            mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS,
                    "no permission to notify other users");
        }


        // We passed the permission check; resolve pseudouser targets as appropriate
        userHandle = handleIncomingUser(uri, pid, uid,
        if (userHandle < 0) {
                Intent.FLAG_GRANT_WRITE_URI_PERMISSION, userHandle);
            if (userHandle == UserHandle.USER_CURRENT) {

                userHandle = ActivityManager.getCurrentUser();
        final String msg = LocalServices.getService(ActivityManagerInternal.class)
            } else if (userHandle != UserHandle.USER_ALL) {
                .checkContentProviderAccess(uri.getAuthority(), userHandle);
                throw new InvalidParameterException("Bad user handle for notifyChange: "
        if (msg != null) {
                        + userHandle);
            Log.w(TAG, "Ignoring notify for " + uri + " from " + uid + ": " + msg);
            }
            return;
        }
        }


        final int uid = Binder.getCallingUid();
        // This makes it so that future permission checks will be in the context of this
        // This makes it so that future permission checks will be in the context of this
        // process rather than the caller's process. We will restore this before returning.
        // process rather than the caller's process. We will restore this before returning.
        long identityToken = clearCallingIdentity();
        long identityToken = clearCallingIdentity();
@@ -881,6 +883,24 @@ public final class ContentService extends IContentService.Stub {
        return service;
        return service;
    }
    }


    private int handleIncomingUser(Uri uri, int pid, int uid, int modeFlags, int userId) {
        if (userId == UserHandle.USER_CURRENT) {
            userId = ActivityManager.getCurrentUser();
        }

        if (userId == UserHandle.USER_ALL) {
            mContext.enforceCallingOrSelfPermission(
                    Manifest.permission.INTERACT_ACROSS_USERS_FULL, TAG);
        } else if (userId < 0) {
            throw new IllegalArgumentException("Invalid user: " + userId);
        } else if (userId != UserHandle.getCallingUserId()) {
            mContext.enforceCallingOrSelfPermission(
                    Manifest.permission.INTERACT_ACROSS_USERS_FULL, TAG);
        }

        return userId;
    }

    /**
    /**
     * Checks if the request is from the system or an app that has INTERACT_ACROSS_USERS_FULL
     * Checks if the request is from the system or an app that has INTERACT_ACROSS_USERS_FULL
     * permission, if the userHandle is not for the caller.
     * permission, if the userHandle is not for the caller.