summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Jackal Guo <jackalguo@google.com> 2022-06-27 14:38:20 +0800
committer Jackal Guo <jackalguo@google.com> 2022-07-11 09:38:32 +0800
commitc2f85365b67e2c7e18709e398b916f938c8d2c62 (patch)
tree91c91fcc1ba0147279e22a8e0ee0b4c967c38b03
parenta274071090babd3ae0b296669fd860d54c769c7f (diff)
Add the check for authority access on related APIs.
Several sync-related APIs in ContentService leave the possibility that malicious code could do a side channel attack. Apply the app visibility check to mitigate this. Bug: 207133734 Bug: 207670653 Bug: 207671082 Bug: 208257015 Bug: 208257145 Bug: 208258815 Bug: 208258924 Test: atest CtsContentTestCases Test: atest CtsProviderTestCases Test: atest CtsSyncManagerTestsCases Test: atest FrameworksCoreTests:ContentResolverTest Test: atest FrameworksCoreTests:ManagedUserContentResolverTest Test: atest FrameworksCoreTests:SecondaryUserContentResolverTest Test: manually using the PoC in the buganizer to ensure the symptom no longer exists. Change-Id: I56f40560b7d7546e50107b76a4800f0716dfe40f
-rw-r--r--services/core/java/com/android/server/content/ContentService.java105
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;