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

Commit 9720fec5 authored by Varun Shah's avatar Varun Shah Committed by Android (Google) Code Review
Browse files

Merge "Add a check for account access on all sync-related APIs." into tm-dev

parents aab0963f e63ad8e6
Loading
Loading
Loading
Loading
+86 −4
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import static android.content.PermissionChecker.PERMISSION_GRANTED;

import android.Manifest;
import android.accounts.Account;
import android.accounts.AccountManagerInternal;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -28,7 +29,10 @@ import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.compat.CompatChanges;
import android.app.job.JobInfo;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -106,6 +110,13 @@ public final class ContentService extends IContentService.Stub {
     */
    private static final long BACKGROUND_OBSERVER_DELAY = 10 * DateUtils.SECOND_IN_MILLIS;

    /**
     * Enables checking for account access for the calling uid on all sync-related APIs.
     */
    @ChangeId
    @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.S_V2)
    public static final long ACCOUNT_ACCESS_CHECK_CHANGE_ID = 201794303L;

    public static class Lifecycle extends SystemService {
        private ContentService mService;

@@ -157,6 +168,8 @@ public final class ContentService extends IContentService.Stub {
    private SyncManager mSyncManager = null;
    private final Object mSyncManagerLock = new Object();

    private final AccountManagerInternal mAccountManagerInternal;

    private static final BinderDeathDispatcher<IContentObserver> sObserverDeathDispatcher =
            new BinderDeathDispatcher<>();

@@ -317,6 +330,8 @@ public final class ContentService extends IContentService.Stub {
        localeFilter.addAction(Intent.ACTION_LOCALE_CHANGED);
        mContext.registerReceiverAsUser(mCacheReceiver, UserHandle.ALL,
                localeFilter, null, null);

        mAccountManagerInternal = LocalServices.getService(AccountManagerInternal.class);
    }

    void onBootPhase(int phase) {
@@ -593,6 +608,10 @@ public final class ContentService extends IContentService.Stub {
        final int callingUid = Binder.getCallingUid();
        final int callingPid = Binder.getCallingPid();

        if (!hasAccountAccess(true, account, callingUid)) {
            return;
        }

        validateExtras(callingUid, extras);
        final int syncExemption = getSyncExemptionAndCleanUpExtrasForCaller(callingUid, extras);

@@ -642,11 +661,14 @@ public final class ContentService extends IContentService.Stub {
    @Override
    public void syncAsUser(SyncRequest request, int userId, String callingPackage) {
        enforceCrossUserPermission(userId, "no permission to request sync as user: " + userId);

        final int callingUid = Binder.getCallingUid();
        final int callingPid = Binder.getCallingPid();
        if (!hasAccountAccess(true, request.getAccount(), callingUid)) {
            return;
        }

        final Bundle extras = request.getBundle();

        validateExtras(callingUid, extras);
        final int syncExemption = getSyncExemptionAndCleanUpExtrasForCaller(callingUid, extras);

@@ -853,6 +875,9 @@ public final class ContentService extends IContentService.Stub {
                "no permission to read the sync settings for user " + userId);
        mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
                "no permission to read the sync settings");
        if (!hasAccountAccess(true, account, Binder.getCallingUid())) {
            return false;
        }

        final long identityToken = clearCallingIdentity();
        try {
@@ -882,8 +907,13 @@ public final class ContentService extends IContentService.Stub {
                "no permission to write the sync settings");
        enforceCrossUserPermission(userId,
                "no permission to modify the sync settings for user " + userId);

        final int callingUid = Binder.getCallingUid();
        final int callingPid = Binder.getCallingPid();
        if (!hasAccountAccess(true, account, callingUid)) {
            return;
        }

        final int syncExemptionFlag = getSyncExemptionForCaller(callingUid);

        final long identityToken = clearCallingIdentity();
@@ -912,7 +942,11 @@ public final class ContentService extends IContentService.Stub {
        mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
                "no permission to write the sync settings");

        validateExtras(Binder.getCallingUid(), extras);
        final int callingUid = Binder.getCallingUid();
        if (!hasAccountAccess(true, account, callingUid)) {
            return;
        }
        validateExtras(callingUid, extras);

        int userId = UserHandle.getCallingUserId();

@@ -942,9 +976,11 @@ public final class ContentService extends IContentService.Stub {
        mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
                "no permission to write the sync settings");

        validateExtras(Binder.getCallingUid(), extras);

        final int callingUid = Binder.getCallingUid();
        if (!hasAccountAccess(true, account, callingUid)) {
            return;
        }
        validateExtras(callingUid, extras);

        int userId = UserHandle.getCallingUserId();
        final long identityToken = clearCallingIdentity();
@@ -969,6 +1005,9 @@ public final class ContentService extends IContentService.Stub {
        }
        mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
                "no permission to read the sync settings");
        if (!hasAccountAccess(true, account, Binder.getCallingUid())) {
            return new ArrayList<>(); // return a new empty list for consistent behavior
        }

        int userId = UserHandle.getCallingUserId();
        final long identityToken = clearCallingIdentity();
@@ -995,6 +1034,9 @@ public final class ContentService extends IContentService.Stub {
                "no permission to read the sync settings for user " + userId);
        mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
                "no permission to read the sync settings");
        if (!hasAccountAccess(true, account, Binder.getCallingUid())) {
            return SyncStorageEngine.AuthorityInfo.NOT_SYNCABLE; // to keep behavior consistent
        }

        final long identityToken = clearCallingIdentity();
        try {
@@ -1031,6 +1073,9 @@ public final class ContentService extends IContentService.Stub {
        syncable = normalizeSyncable(syncable);
        final int callingUid = Binder.getCallingUid();
        final int callingPid = Binder.getCallingPid();
        if (!hasAccountAccess(true, account, callingUid)) {
            return;
        }

        final long identityToken = clearCallingIdentity();
        try {
@@ -1103,6 +1148,10 @@ public final class ContentService extends IContentService.Stub {
    public boolean isSyncActive(Account account, String authority, ComponentName cname) {
        mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
                "no permission to read the sync stats");
        if (!hasAccountAccess(true, account, Binder.getCallingUid())) {
            return false;
        }

        int userId = UserHandle.getCallingUserId();
        final long identityToken = clearCallingIdentity();
        try {
@@ -1165,6 +1214,9 @@ public final class ContentService extends IContentService.Stub {
                "no permission to read the sync stats for user " + userId);
        mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
                "no permission to read the sync stats");
        if (!hasAccountAccess(true, account, Binder.getCallingUid())) {
            return null;
        }

        final long identityToken = clearCallingIdentity();
        try {
@@ -1196,6 +1248,10 @@ public final class ContentService extends IContentService.Stub {
                "no permission to read the sync stats");
        enforceCrossUserPermission(userId,
                "no permission to retrieve the sync settings for user " + userId);
        if (!hasAccountAccess(true, account, Binder.getCallingUid())) {
            return false;
        }

        final long identityToken = clearCallingIdentity();
        SyncManager syncManager = getSyncManager();
        if (syncManager == null) return false;
@@ -1405,6 +1461,32 @@ public final class ContentService extends IContentService.Stub {
                Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
    }

    /**
     * Checks to see if the given account is accessible by the provided uid.
     *
     * @param checkCompatFlag whether to check if the ACCOUNT_ACCESS_CHECK_CHANGE_ID flag is enabled
     * @param account the account trying to be accessed
     * @param uid the uid trying to access the account
     * @return {@code true} if the account is accessible by the given uid, {@code false} otherwise
     */
    private boolean hasAccountAccess(boolean checkCompatFlag, Account account, int uid) {
        if (account == null) {
            // If the account is null, it means to check for all accounts hence skip the check here.
            return true;
        }
        if (checkCompatFlag
                && !CompatChanges.isChangeEnabled(ACCOUNT_ACCESS_CHECK_CHANGE_ID, uid)) {
            return true;
        }

        final long identityToken = clearCallingIdentity();
        try {
            return mAccountManagerInternal.hasAccountAccess(account, uid);
        } finally {
            restoreCallingIdentity(identityToken);
        }
    }

    private static int normalizeSyncable(int syncable) {
        if (syncable > 0) {
            return SyncStorageEngine.AuthorityInfo.SYNCABLE;