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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user