Enforce package visibility to the callbacks of the sync status

App can read installed sync content providers without requiring
any permission via the api ContentResolver#requestSync and
ContentResolver#addStatusChangeListener.

This cl enforces package visibility filter to the api
ContentResolver#addStatusChangeListener. Application without the
package visibility tag of the target applications in its manifest
cannot receive sync status callbacks of the target applications.

Bug: 190726121
Test: atest AppEnumerationTests
Test: atest CtsSyncManagerTestsCases
Test: atest ContentResolverSyncTestCaseatest
Test: atest SyncManagerTest
Test  atest SyncOperationTest
Test: atest SyncStorageEngineTest
Test: atest DeferSyncTest
Change-Id: I58d0baf5baefd942b454f06e1fa9d3b34d532a77
This commit is contained in:
Rhed Jao
2021-07-12 11:08:47 +08:00
parent b1a430939d
commit 650cb7a9da
5 changed files with 104 additions and 27 deletions

View File

@@ -3192,6 +3192,21 @@ public abstract class ContentResolver implements ContentInterface {
}
}
/**
* Returns the package name of the syncadapter that matches a given account type, authority
* and user.
* @hide
*/
@Nullable
public static String getSyncAdapterPackageAsUser(@NonNull String accountType,
@NonNull String authority, @UserIdInt int userId) {
try {
return getContentService().getSyncAdapterPackageAsUser(accountType, authority, userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Check if the provider should be synced when a network tickle is received
* <p>This method requires the caller to hold the permission

View File

@@ -150,6 +150,7 @@ interface IContentService {
SyncAdapterType[] getSyncAdapterTypesAsUser(int userId);
String[] getSyncAdapterPackagesForAuthorityAsUser(String authority, int userId);
String getSyncAdapterPackageAsUser(String accountType, String authority, int userId);
/**
* Returns true if there is currently a operation for the given account/authority or service

View File

@@ -23,6 +23,7 @@ import android.accounts.Account;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
@@ -822,6 +823,21 @@ public final class ContentService extends IContentService.Stub {
}
}
@Override
public String getSyncAdapterPackageAsUser(@NonNull String accountType,
@NonNull String authority, @UserIdInt int userId) {
enforceCrossUserPermission(userId,
"no permission to read sync settings for user " + userId);
final int callingUid = Binder.getCallingUid();
final long identityToken = clearCallingIdentity();
try {
return getSyncManager().getSyncAdapterPackageAsUser(accountType, authority,
callingUid, userId);
} finally {
restoreCallingIdentity(identityToken);
}
}
@Override
public boolean getSyncAutomatically(Account account, String providerName) {
return getSyncAutomaticallyAsUser(account, providerName, UserHandle.getCallingUserId());
@@ -1205,7 +1221,7 @@ public final class ContentService extends IContentService.Stub {
SyncManager syncManager = getSyncManager();
if (syncManager != null && callback != null) {
syncManager.getSyncStorageEngine().addStatusChangeListener(
mask, UserHandle.getUserId(callingUid), callback);
mask, callingUid, callback);
}
} finally {
restoreCallingIdentity(identityToken);

View File

@@ -1246,6 +1246,26 @@ public class SyncManager {
return filteredResult.toArray(new String[] {});
}
public String getSyncAdapterPackageAsUser(String accountType, String authority,
int callingUid, int userId) {
if (accountType == null || authority == null) {
return null;
}
final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
mSyncAdapters.getServiceInfo(
SyncAdapterType.newKey(authority, accountType),
userId);
if (syncAdapterInfo == null) {
return null;
}
final String packageName = syncAdapterInfo.type.getPackageName();
if (TextUtils.isEmpty(packageName) || mPackageManagerInternal.filterAppAccess(
packageName, callingUid, userId)) {
return null;
}
return packageName;
}
private void sendSyncFinishedOrCanceledMessage(ActiveSyncContext syncContext,
SyncResult syncResult) {
if (Log.isLoggable(TAG, Log.VERBOSE)) Slog.v(TAG, "sending MESSAGE_SYNC_FINISHED");
@@ -3403,7 +3423,7 @@ public class SyncManager {
scheduleSyncOperationH(op);
mSyncStorageEngine.reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS,
target.userId);
op.owningPackage, target.userId);
}
/**
@@ -3922,7 +3942,7 @@ public class SyncManager {
syncOperation.toEventLog(SyncStorageEngine.EVENT_STOP));
mSyncStorageEngine.stopSyncEvent(rowId, elapsedTime,
resultMessage, downstreamActivity, upstreamActivity,
syncOperation.target.userId);
syncOperation.owningPackage, syncOperation.target.userId);
}
}

View File

@@ -33,6 +33,7 @@ import android.content.SyncInfo;
import android.content.SyncRequest;
import android.content.SyncStatusInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
@@ -59,6 +60,7 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.IntPair;
import com.android.server.LocalServices;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -166,6 +168,8 @@ public class SyncStorageEngine {
private static HashMap<String, String> sAuthorityRenames;
private static PeriodicSyncAddedListener mPeriodicSyncAddedListener;
private final PackageManagerInternal mPackageManagerInternal;
private volatile boolean mIsClockValid;
static {
@@ -525,6 +529,8 @@ public class SyncStorageEngine {
mDefaultMasterSyncAutomatically = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_syncstorageengine_masterSyncAutomatically);
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
File systemDir = new File(dataDir, "system");
mSyncDir = new File(systemDir, SYNC_DIR_NAME);
mSyncDir.mkdirs();
@@ -609,9 +615,9 @@ public class SyncStorageEngine {
return mSyncRandomOffset;
}
public void addStatusChangeListener(int mask, int userId, ISyncStatusObserver callback) {
public void addStatusChangeListener(int mask, int callingUid, ISyncStatusObserver callback) {
synchronized (mAuthorities) {
final long cookie = IntPair.of(userId, mask);
final long cookie = IntPair.of(callingUid, mask);
mChangeListeners.register(callback, cookie);
}
}
@@ -644,16 +650,32 @@ public class SyncStorageEngine {
}
}
void reportChange(int which, int callingUserId) {
void reportChange(int which, EndPoint target) {
final String syncAdapterPackageName;
if (target.account == null || target.provider == null) {
syncAdapterPackageName = null;
} else {
syncAdapterPackageName = ContentResolver.getSyncAdapterPackageAsUser(
target.account.type, target.provider, target.userId);
}
reportChange(which, syncAdapterPackageName, target.userId);
}
void reportChange(int which, String callingPackageName, int callingUserId) {
ArrayList<ISyncStatusObserver> reports = null;
synchronized (mAuthorities) {
int i = mChangeListeners.beginBroadcast();
while (i > 0) {
i--;
final long cookie = (long) mChangeListeners.getBroadcastCookie(i);
final int userId = IntPair.first(cookie);
final int registerUid = IntPair.first(cookie);
final int registerUserId = UserHandle.getUserId(registerUid);
final int mask = IntPair.second(cookie);
if ((which & mask) == 0 || callingUserId != userId) {
if ((which & mask) == 0 || callingUserId != registerUserId) {
continue;
}
if (callingPackageName != null && mPackageManagerInternal.filterAppAccess(
callingPackageName, registerUid, callingUserId)) {
continue;
}
if (reports == null) {
@@ -716,12 +738,12 @@ public class SyncStorageEngine {
" cuid=", callingUid,
" cpid=", callingPid
);
final AuthorityInfo authority;
synchronized (mAuthorities) {
AuthorityInfo authority =
getOrCreateAuthorityLocked(
new EndPoint(account, providerName, userId),
-1 /* ident */,
false);
authority = getOrCreateAuthorityLocked(
new EndPoint(account, providerName, userId),
-1 /* ident */,
false);
if (authority.enabled == sync) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Slog.d(TAG, "setSyncAutomatically: already set to " + sync + ", doing nothing");
@@ -743,7 +765,7 @@ public class SyncStorageEngine {
new Bundle(),
syncExemptionFlag, callingUid, callingPid);
}
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, userId);
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, authority.target);
queueBackup();
}
@@ -811,7 +833,7 @@ public class SyncStorageEngine {
requestSync(aInfo, SyncOperation.REASON_IS_SYNCABLE, new Bundle(),
ContentResolver.SYNC_EXEMPTION_NONE, callingUid, callingPid);
}
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, target.userId);
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, target);
}
public Pair<Long, Long> getBackoff(EndPoint info) {
@@ -857,7 +879,7 @@ public class SyncStorageEngine {
}
}
if (changed) {
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, info.userId);
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, info);
}
}
@@ -919,7 +941,8 @@ public class SyncStorageEngine {
}
for (int i = changedUserIds.size() - 1; i > 0; i--) {
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, changedUserIds.valueAt(i));
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS,
null /* callingPackageName */, changedUserIds.valueAt(i));
}
}
@@ -945,7 +968,7 @@ public class SyncStorageEngine {
}
authority.delayUntil = delayUntil;
}
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, info.userId);
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, info);
}
/**
@@ -988,7 +1011,8 @@ public class SyncStorageEngine {
new Bundle(),
syncExemptionFlag, callingUid, callingPid);
}
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, userId);
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS,
null /* callingPackageName */, userId);
mContext.sendBroadcast(ContentResolver.ACTION_SYNC_CONN_STATUS_CHANGED);
queueBackup();
}
@@ -1039,7 +1063,7 @@ public class SyncStorageEngine {
SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
status.pending = pendingValue;
}
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING, info.userId);
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING, info);
}
/**
@@ -1129,7 +1153,7 @@ public class SyncStorageEngine {
activeSyncContext.mStartTime);
getCurrentSyncs(authorityInfo.target.userId).add(syncInfo);
}
reportActiveChange(activeSyncContext.mSyncOperation.target.userId);
reportActiveChange(activeSyncContext.mSyncOperation.target);
return syncInfo;
}
@@ -1146,14 +1170,14 @@ public class SyncStorageEngine {
getCurrentSyncs(userId).remove(syncInfo);
}
reportActiveChange(userId);
reportActiveChange(new EndPoint(syncInfo.account, syncInfo.authority, userId));
}
/**
* To allow others to send active change reports, to poke clients.
*/
public void reportActiveChange(int userId) {
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE, userId);
public void reportActiveChange(EndPoint target) {
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE, target);
}
/**
@@ -1188,12 +1212,13 @@ public class SyncStorageEngine {
if (Log.isLoggable(TAG, Log.VERBOSE)) Slog.v(TAG, "returning historyId " + id);
}
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS, op.target.userId);
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS, op.owningPackage, op.target.userId);
return id;
}
public void stopSyncEvent(long historyId, long elapsedTime, String resultMessage,
long downstreamActivity, long upstreamActivity, int userId) {
long downstreamActivity, long upstreamActivity, String opPackageName,
int userId) {
synchronized (mAuthorities) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Slog.v(TAG, "stopSyncEvent: historyId=" + historyId);
@@ -1333,7 +1358,7 @@ public class SyncStorageEngine {
}
}
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS, userId);
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS, opPackageName, userId);
}
/**