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

This commit is contained in:
Varun Shah
2022-03-16 02:14:53 +00:00
committed by Android (Google) Code Review

View File

@@ -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;