diff options
| -rw-r--r-- | services/core/java/com/android/server/content/ContentService.java | 105 |
1 files changed, 94 insertions, 11 deletions
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java index 75b4eb4de5d6..781920c8d4c0 100644 --- a/services/core/java/com/android/server/content/ContentService.java +++ b/services/core/java/com/android/server/content/ContentService.java @@ -119,6 +119,13 @@ public final class ContentService extends IContentService.Stub { @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.S_V2) public static final long ACCOUNT_ACCESS_CHECK_CHANGE_ID = 201794303L; + /** + * Enables checking for authority access for the calling uid on all sync-related APIs. + */ + @ChangeId + @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.TIRAMISU) + public static final long AUTHORITY_ACCESS_CHECK_CHANGE_ID = 207133734L; + public static class Lifecycle extends SystemService { private ContentService mService; @@ -608,6 +615,9 @@ public final class ContentService extends IContentService.Stub { if (!hasAccountAccess(true, account, callingUid)) { return; } + if (!hasAuthorityAccess(authority, callingUid, userId)) { + return; + } validateExtras(callingUid, extras); final int syncExemption = getSyncExemptionAndCleanUpExtrasForCaller(callingUid, extras); @@ -661,6 +671,9 @@ public final class ContentService extends IContentService.Stub { if (!hasAccountAccess(true, request.getAccount(), callingUid)) { return; } + if (!hasAuthorityAccess(request.getProvider(), callingUid, userId)) { + return; + } final Bundle extras = request.getBundle(); validateExtras(callingUid, extras); @@ -863,7 +876,12 @@ 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())) { + + final int callingUid = Binder.getCallingUid(); + if (!hasAccountAccess(true, account, callingUid)) { + return false; + } + if (!hasAuthorityAccess(providerName, callingUid, userId)) { return false; } @@ -897,6 +915,9 @@ public final class ContentService extends IContentService.Stub { if (!hasAccountAccess(true, account, callingUid)) { return; } + if (!hasAuthorityAccess(providerName, callingUid, userId)) { + return; + } final int syncExemptionFlag = getSyncExemptionForCaller(callingUid); @@ -924,16 +945,19 @@ public final class ContentService extends IContentService.Stub { "no permission to write the sync settings"); final int callingUid = Binder.getCallingUid(); + final int userId = UserHandle.getCallingUserId(); if (!hasAccountAccess(true, account, callingUid)) { return; } + if (!hasAuthorityAccess(authority, callingUid, userId)) { + return; + } validateExtras(callingUid, extras); pollFrequency = clampPeriod(pollFrequency); long defaultFlex = SyncStorageEngine.calculateDefaultFlexTime(pollFrequency); - int userId = UserHandle.getCallingUserId(); final long identityToken = clearCallingIdentity(); try { SyncStorageEngine.EndPoint info = @@ -958,13 +982,16 @@ public final class ContentService extends IContentService.Stub { "no permission to write the sync settings"); final int callingUid = Binder.getCallingUid(); + final int userId = UserHandle.getCallingUserId(); if (!hasAccountAccess(true, account, callingUid)) { return; } + if (!hasAuthorityAccess(authority, callingUid, userId)) { + return; + } validateExtras(callingUid, extras); - int userId = UserHandle.getCallingUserId(); final long identityToken = clearCallingIdentity(); try { getSyncManager().removePeriodicSync( @@ -986,11 +1013,16 @@ 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())) { + + final int callingUid = Binder.getCallingUid(); + final int userId = UserHandle.getCallingUserId(); + if (!hasAccountAccess(true, account, callingUid)) { return new ArrayList<>(); // return a new empty list for consistent behavior } + if (!hasAuthorityAccess(providerName, callingUid, userId)) { + return new ArrayList<>(); + } - int userId = UserHandle.getCallingUserId(); final long identityToken = clearCallingIdentity(); try { return getSyncManager().getPeriodicSyncs( @@ -1015,9 +1047,14 @@ 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())) { + + final int callingUid = Binder.getCallingUid(); + if (!hasAccountAccess(true, account, callingUid)) { return SyncStorageEngine.AuthorityInfo.NOT_SYNCABLE; // to keep behavior consistent } + if (!hasAuthorityAccess(providerName, callingUid, userId)) { + return SyncStorageEngine.AuthorityInfo.NOT_SYNCABLE; + } final long identityToken = clearCallingIdentity(); try { @@ -1052,6 +1089,9 @@ public final class ContentService extends IContentService.Stub { if (!hasAccountAccess(true, account, callingUid)) { return; } + if (!hasAuthorityAccess(providerName, callingUid, userId)) { + return; + } final long identityToken = clearCallingIdentity(); try { @@ -1114,11 +1154,16 @@ 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())) { + + final int callingUid = Binder.getCallingUid(); + final int userId = UserHandle.getCallingUserId(); + if (!hasAccountAccess(true, account, callingUid)) { + return false; + } + if (!hasAuthorityAccess(authority, callingUid, userId)) { return false; } - int userId = UserHandle.getCallingUserId(); final long identityToken = clearCallingIdentity(); try { return getSyncManager().getSyncStorageEngine().isSyncActive( @@ -1147,13 +1192,17 @@ public final class ContentService extends IContentService.Stub { final boolean canAccessAccounts = mContext.checkCallingOrSelfPermission(Manifest.permission.GET_ACCOUNTS) == PackageManager.PERMISSION_GRANTED; + final List<SyncInfo> results; + final int callingUid = Binder.getCallingUid(); final long identityToken = clearCallingIdentity(); try { - return getSyncManager().getSyncStorageEngine() + results = getSyncManager().getSyncStorageEngine() .getCurrentSyncsCopy(userId, canAccessAccounts); } finally { restoreCallingIdentity(identityToken); } + results.removeIf(i -> !hasAuthorityAccess(i.authority, callingUid, userId)); + return results; } @Override @@ -1177,7 +1226,12 @@ 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())) { + + final int callingUid = Binder.getCallingUid(); + if (!hasAccountAccess(true, account, callingUid)) { + return null; + } + if (!hasAuthorityAccess(authority, callingUid, userId)) { return null; } @@ -1207,7 +1261,12 @@ 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())) { + + final int callingUid = Binder.getCallingUid(); + if (!hasAccountAccess(true, account, callingUid)) { + return false; + } + if (!hasAuthorityAccess(authority, callingUid, userId)) { return false; } @@ -1441,6 +1500,30 @@ public final class ContentService extends IContentService.Stub { } } + /** + * Checks to see if the given authority is accessible by the caller. + * + * @param authority the authority to be accessed + * @param uid the uid trying to access the authority + * @param userId the user id for which to access the authority + * @return {@code true} if the authority is accessible by the caller, {@code false} otherwise + */ + private boolean hasAuthorityAccess(@Nullable String authority, int uid, @UserIdInt int userId) { + if (TextUtils.isEmpty(authority)) { + return true; + } + if (!CompatChanges.isChangeEnabled(AUTHORITY_ACCESS_CHECK_CHANGE_ID, uid)) { + return true; + } + // Since #getSyncAdapterPackagesForAuthorityAsUser would filter out the packages + // that aren't visible to the callers, using this to check if the given authority + // is accessible by the callers. + final String[] syncAdapterPackages = + getSyncAdapterPackagesForAuthorityAsUser(authority, userId); + return !ArrayUtils.isEmpty(syncAdapterPackages); + } + + private static int normalizeSyncable(int syncable) { if (syncable > 0) { return SyncStorageEngine.AuthorityInfo.SYNCABLE; |