summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/content/ContentService.java90
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;