diff options
| -rw-r--r-- | services/core/java/com/android/server/content/ContentService.java | 90 |
1 files changed, 86 insertions, 4 deletions
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java index fadcce9ee9b3..954b9307a3ef 100644 --- a/services/core/java/com/android/server/content/ContentService.java +++ b/services/core/java/com/android/server/content/ContentService.java @@ -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; |