From 51b3aaccfe7f8d7a97fb1218c40a37428f26a6a1 Mon Sep 17 00:00:00 2001 From: Todd Kennedy Date: Thu, 30 Mar 2017 17:50:42 -0700 Subject: [PATCH] Implement service filtering Instant apps can no longer see services that haven't been exposed to them via the visibleToInstantApps attribute. Change-Id: I6fd78067d1253825668d67b9e17dd3a0703f5d57 Fixes: 35871716 Test: cts-tradefed run commandAndExit cts-dev -m CtsAppSecurityHostTestCases -t android.appsecurity.cts.EphemeralTest --- .../android/content/pm/PackageManagerInternal.java | 8 +- .../java/com/android/server/am/ActiveServices.java | 13 +- .../java/com/android/server/am/ServiceRecord.java | 4 +- .../android/server/pm/PackageManagerService.java | 190 +++++++++++++++++---- .../core/java/com/android/server/pm/Settings.java | 2 +- 5 files changed, 173 insertions(+), 44 deletions(-) diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java index 16d582efb730..7bfde751e155 100644 --- a/core/java/android/content/pm/PackageManagerInternal.java +++ b/core/java/android/content/pm/PackageManagerInternal.java @@ -310,12 +310,18 @@ public abstract class PackageManagerInternal { List overlayPackageNames); /** - * Resolves an intent, allowing instant apps to be resolved. + * Resolves an activity intent, allowing instant apps to be resolved. */ public abstract ResolveInfo resolveIntent(Intent intent, String resolvedType, int flags, int userId); /** + * Resolves a service intent, allowing instant apps to be resolved. + */ + public abstract ResolveInfo resolveService(Intent intent, String resolvedType, + int flags, int userId, int callingUid); + + /** * Track the creator of a new isolated uid. * @param isolatedUid The newly created isolated uid. * @param ownerUid The uid of the app that created the isolated process. diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 8b0665c6d312..9ad945ffb43a 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -398,7 +398,7 @@ public final class ActiveServices { r.delayedStop = false; r.fgRequired = fgRequired; r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(), - service, neededGrants)); + service, neededGrants, callingUid)); final ServiceMap smap = getServiceMapLocked(r.userId); boolean addToStarting = false; @@ -1355,10 +1355,10 @@ public final class ActiveServices { if (r == null) { try { // TODO: come back and remove this assumption to triage all services - ResolveInfo rInfo = AppGlobals.getPackageManager().resolveService(service, + ResolveInfo rInfo = mAm.getPackageManagerInternalLocked().resolveService(service, resolvedType, ActivityManagerService.STOCK_PM_FLAGS | PackageManager.MATCH_DEBUG_TRIAGED_MISSING, - userId); + userId, callingUid); ServiceInfo sInfo = rInfo != null ? rInfo.serviceInfo : null; if (sInfo == null) { @@ -1927,7 +1927,7 @@ public final class ActiveServices { // be called. if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) { r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(), - null, null)); + null, null, 0)); } sendServiceArgsLocked(r, execInFg, true); @@ -1979,7 +1979,8 @@ public final class ActiveServices { mAm.grantUriPermissionUncheckedFromIntentLocked(si.neededGrants, si.getUriPermissionsLocked()); } - // TODO b/34123112; Insert ephemeral grant here + mAm.grantEphemeralAccessLocked(r.userId, si.intent, + r.appInfo.uid, UserHandle.getAppId(si.callingId)); bumpServiceExecutingLocked(r, execInFg, "start"); if (!oomAdjusted) { oomAdjusted = true; @@ -2591,7 +2592,7 @@ public final class ActiveServices { stopServiceLocked(sr); } else { sr.pendingStarts.add(new ServiceRecord.StartItem(sr, true, - sr.makeNextStartId(), baseIntent, null)); + sr.makeNextStartId(), baseIntent, null, 0)); if (sr.app != null && sr.app.thread != null) { // We always run in the foreground, since this is called as // part of the "remove task" UI operation. diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 44ebf50eb31a..b57f6c366f8f 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -124,6 +124,7 @@ final class ServiceRecord extends Binder { final ServiceRecord sr; final boolean taskRemoved; final int id; + final int callingId; final Intent intent; final ActivityManagerService.NeededUriGrants neededGrants; long deliveredTime; @@ -134,12 +135,13 @@ final class ServiceRecord extends Binder { String stringName; // caching of toString StartItem(ServiceRecord _sr, boolean _taskRemoved, int _id, Intent _intent, - ActivityManagerService.NeededUriGrants _neededGrants) { + ActivityManagerService.NeededUriGrants _neededGrants, int _callingId) { sr = _sr; taskRemoved = _taskRemoved; id = _id; intent = _intent; neededGrants = _neededGrants; + callingId = _callingId; } UriPermissionOwner getUriPermissionsLocked() { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 2115f316402f..e3b55f03ddc1 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -3036,13 +3036,14 @@ public class PackageManagerService extends IPackageManager.Stub { return null; } + final int callingUid = Binder.getCallingUid(); final int resolveFlags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE | (!Build.IS_DEBUGGABLE ? MATCH_SYSTEM_ONLY : 0); final Intent resolverIntent = new Intent(Intent.ACTION_RESOLVE_EPHEMERAL_PACKAGE); final List resolvers = queryIntentServicesInternal(resolverIntent, null, - resolveFlags, UserHandle.USER_SYSTEM); + resolveFlags, UserHandle.USER_SYSTEM, callingUid, false /*includeInstantApps*/); final int N = resolvers.size(); if (N == 0) { @@ -4021,12 +4022,12 @@ public class PackageManagerService extends IPackageManager.Stub { * action and a {@code android.intent.category.BROWSABLE} category * */ - int updateFlagsForResolve(int flags, int userId, Intent intent, boolean includeInstantApp) { + int updateFlagsForResolve(int flags, int userId, Intent intent, int callingUid, + boolean includeInstantApps) { // Safe mode means we shouldn't match any third-party components if (mSafeMode) { flags |= PackageManager.MATCH_SYSTEM_ONLY; } - final int callingUid = Binder.getCallingUid(); if (getInstantAppPackageName(callingUid) != null) { // But, ephemeral apps see both ephemeral and exposed, non-ephemeral components flags |= PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY; @@ -4038,7 +4039,7 @@ public class PackageManagerService extends IPackageManager.Stub { || callingUid == Process.SHELL_UID || callingUid == 0; final boolean allowMatchInstant = - (includeInstantApp + (includeInstantApps && Intent.ACTION_VIEW.equals(intent.getAction()) && intent.hasCategory(Intent.CATEGORY_BROWSABLE) && hasWebURI(intent)) @@ -5588,22 +5589,23 @@ public class PackageManagerService extends IPackageManager.Stub { public ResolveInfo resolveIntent(Intent intent, String resolvedType, int flags, int userId) { return resolveIntentInternal( - intent, resolvedType, flags, userId, false /*includeInstantApp*/); + intent, resolvedType, flags, userId, false /*includeInstantApps*/); } private ResolveInfo resolveIntentInternal(Intent intent, String resolvedType, - int flags, int userId, boolean includeInstantApp) { + int flags, int userId, boolean includeInstantApps) { try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveIntent"); if (!sUserManager.exists(userId)) return null; - flags = updateFlagsForResolve(flags, userId, intent, includeInstantApp); - enforceCrossUserPermission(Binder.getCallingUid(), userId, + final int callingUid = Binder.getCallingUid(); + flags = updateFlagsForResolve(flags, userId, intent, callingUid, includeInstantApps); + enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "resolve intent"); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities"); final List query = queryIntentActivitiesInternal(intent, resolvedType, - flags, userId, includeInstantApp); + flags, userId, includeInstantApps); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); final ResolveInfo bestChoice = @@ -5623,9 +5625,11 @@ public class PackageManagerService extends IPackageManager.Stub { if (!sUserManager.exists(userId)) { return null; } + final int callingUid = Binder.getCallingUid(); intent = updateIntentForResolve(intent); final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver()); - final int flags = updateFlagsForResolve(0, userId, intent, false); + final int flags = updateFlagsForResolve( + 0, userId, intent, callingUid, false /*includeInstantApps*/); final List query = queryIntentActivitiesInternal(intent, resolvedType, flags, userId); synchronized (mPackages) { @@ -5914,7 +5918,9 @@ public class PackageManagerService extends IPackageManager.Stub { List query, int priority, boolean always, boolean removeMatches, boolean debug, int userId) { if (!sUserManager.exists(userId)) return null; - flags = updateFlagsForResolve(flags, userId, intent, false); + final int callingUid = Binder.getCallingUid(); + flags = updateFlagsForResolve( + flags, userId, intent, callingUid, false /*includeInstantApps*/); intent = updateIntentForResolve(intent); // writer synchronized (mPackages) { @@ -6080,9 +6086,11 @@ public class PackageManagerService extends IPackageManager.Stub { } if (hasWebURI(intent)) { // cross-profile app linking works only towards the parent. + final int callingUid = Binder.getCallingUid(); final UserInfo parent = getProfileParent(sourceUserId); synchronized(mPackages) { - int flags = updateFlagsForResolve(0, parent.id, intent, false); + int flags = updateFlagsForResolve(0, parent.id, intent, callingUid, + false /*includeInstantApps*/); CrossProfileDomainInfo xpDomainInfo = getCrossProfileDomainPreferredLpr( intent, resolvedType, flags, sourceUserId, parent.id); return xpDomainInfo != null; @@ -6149,11 +6157,12 @@ public class PackageManagerService extends IPackageManager.Stub { } private @NonNull List queryIntentActivitiesInternal(Intent intent, - String resolvedType, int flags, int userId, boolean includeInstantApp) { + String resolvedType, int flags, int userId, boolean includeInstantApps) { if (!sUserManager.exists(userId)) return Collections.emptyList(); - final String instantAppPkgName = getInstantAppPackageName(Binder.getCallingUid()); - flags = updateFlagsForResolve(flags, userId, intent, includeInstantApp); - enforceCrossUserPermission(Binder.getCallingUid(), userId, + final int callingUid = Binder.getCallingUid(); + final String instantAppPkgName = getInstantAppPackageName(callingUid); + flags = updateFlagsForResolve(flags, userId, intent, callingUid, includeInstantApps); + enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "query intent activities"); ComponentName comp = intent.getComponent(); @@ -6783,9 +6792,11 @@ public class PackageManagerService extends IPackageManager.Stub { Intent[] specifics, String[] specificTypes, Intent intent, String resolvedType, int flags, int userId) { if (!sUserManager.exists(userId)) return Collections.emptyList(); - flags = updateFlagsForResolve(flags, userId, intent, false); - enforceCrossUserPermission(Binder.getCallingUid(), userId, - false /* requireFullPermission */, false /* checkShell */, + final int callingUid = Binder.getCallingUid(); + flags = updateFlagsForResolve(flags, userId, intent, callingUid, + false /*includeInstantApps*/); + enforceCrossUserPermission(callingUid, userId, + false /*requireFullPermission*/, false /*checkShell*/, "query intent activity options"); final String resultsAction = intent.getAction(); @@ -6963,7 +6974,9 @@ public class PackageManagerService extends IPackageManager.Stub { private @NonNull List queryIntentReceiversInternal(Intent intent, String resolvedType, int flags, int userId) { if (!sUserManager.exists(userId)) return Collections.emptyList(); - flags = updateFlagsForResolve(flags, userId, intent, false); + final int callingUid = Binder.getCallingUid(); + flags = updateFlagsForResolve(flags, userId, intent, callingUid, + false /*includeInstantApps*/); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { @@ -6999,9 +7012,17 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public ResolveInfo resolveService(Intent intent, String resolvedType, int flags, int userId) { + final int callingUid = Binder.getCallingUid(); + return resolveServiceInternal( + intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/); + } + + private ResolveInfo resolveServiceInternal(Intent intent, String resolvedType, int flags, + int userId, int callingUid, boolean includeInstantApps) { if (!sUserManager.exists(userId)) return null; - flags = updateFlagsForResolve(flags, userId, intent, false); - List query = queryIntentServicesInternal(intent, resolvedType, flags, userId); + flags = updateFlagsForResolve(flags, userId, intent, callingUid, includeInstantApps); + List query = queryIntentServicesInternal( + intent, resolvedType, flags, userId, callingUid, includeInstantApps); if (query != null) { if (query.size() >= 1) { // If there is more than one service with the same priority, @@ -7015,14 +7036,17 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public @NonNull ParceledListSlice queryIntentServices(Intent intent, String resolvedType, int flags, int userId) { - return new ParceledListSlice<>( - queryIntentServicesInternal(intent, resolvedType, flags, userId)); + final int callingUid = Binder.getCallingUid(); + return new ParceledListSlice<>(queryIntentServicesInternal( + intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/)); } private @NonNull List queryIntentServicesInternal(Intent intent, - String resolvedType, int flags, int userId) { + String resolvedType, int flags, int userId, int callingUid, + boolean includeInstantApps) { if (!sUserManager.exists(userId)) return Collections.emptyList(); - flags = updateFlagsForResolve(flags, userId, intent, false); + final String instantAppPkgName = getInstantAppPackageName(callingUid); + flags = updateFlagsForResolve(flags, userId, intent, callingUid, includeInstantApps); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { @@ -7034,9 +7058,27 @@ public class PackageManagerService extends IPackageManager.Stub { final List list = new ArrayList(1); final ServiceInfo si = getServiceInfo(comp, flags, userId); if (si != null) { - final ResolveInfo ri = new ResolveInfo(); - ri.serviceInfo = si; - list.add(ri); + // When specifying an explicit component, we prevent the service from being + // used when either 1) the service is in an instant application and the + // caller is not the same instant application or 2) the calling package is + // ephemeral and the activity is not visible to ephemeral applications. + final boolean matchVisibleToInstantAppOnly = + (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0; + final boolean isCallerInstantApp = + instantAppPkgName != null; + final boolean isTargetSameInstantApp = + comp.getPackageName().equals(instantAppPkgName); + final boolean isTargetHiddenFromInstantApp = + (si.flags & ServiceInfo.FLAG_VISIBLE_TO_EPHEMERAL) == 0; + final boolean blockResolution = + !isTargetSameInstantApp + && ((matchVisibleToInstantAppOnly && isCallerInstantApp + && isTargetHiddenFromInstantApp)); + if (!blockResolution) { + final ResolveInfo ri = new ResolveInfo(); + ri.serviceInfo = si; + list.add(ri); + } } return list; } @@ -7045,17 +7087,67 @@ public class PackageManagerService extends IPackageManager.Stub { synchronized (mPackages) { String pkgName = intent.getPackage(); if (pkgName == null) { - return mServices.queryIntent(intent, resolvedType, flags, userId); + return applyPostServiceResolutionFilter( + mServices.queryIntent(intent, resolvedType, flags, userId), + instantAppPkgName); } final PackageParser.Package pkg = mPackages.get(pkgName); if (pkg != null) { - return mServices.queryIntentForPackage(intent, resolvedType, flags, pkg.services, - userId); + return applyPostServiceResolutionFilter( + mServices.queryIntentForPackage(intent, resolvedType, flags, pkg.services, + userId), + instantAppPkgName); } return Collections.emptyList(); } } + private List applyPostServiceResolutionFilter(List resolveInfos, + String instantAppPkgName) { + // TODO: When adding on-demand split support for non-instant apps, remove this check + // and always apply post filtering + if (instantAppPkgName == null) { + return resolveInfos; + } + for (int i = resolveInfos.size() - 1; i >= 0; i--) { + final ResolveInfo info = resolveInfos.get(i); + final boolean isEphemeralApp = info.serviceInfo.applicationInfo.isInstantApp(); + // allow services that are defined in the provided package + if (isEphemeralApp && instantAppPkgName.equals(info.serviceInfo.packageName)) { + if (info.serviceInfo.splitName != null + && !ArrayUtils.contains(info.serviceInfo.applicationInfo.splitNames, + info.serviceInfo.splitName)) { + // requested service is defined in a split that hasn't been installed yet. + // add the installer to the resolve list + if (DEBUG_EPHEMERAL) { + Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list"); + } + final ResolveInfo installerInfo = new ResolveInfo(mInstantAppInstallerInfo); + installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo( + info.serviceInfo.packageName, info.serviceInfo.splitName, + info.serviceInfo.applicationInfo.versionCode); + // make sure this resolver is the default + installerInfo.isDefault = true; + installerInfo.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART + | IntentFilter.MATCH_ADJUSTMENT_NORMAL; + // add a non-generic filter + installerInfo.filter = new IntentFilter(); + // load resources from the correct package + installerInfo.resolvePackageName = info.getComponentInfo().packageName; + resolveInfos.set(i, installerInfo); + } + continue; + } + // allow services that have been explicitly exposed to ephemeral apps + if (!isEphemeralApp + && ((info.serviceInfo.flags & ActivityInfo.FLAG_VISIBLE_TO_EPHEMERAL) != 0)) { + continue; + } + resolveInfos.remove(i); + } + return resolveInfos; + } + @Override public @NonNull ParceledListSlice queryIntentContentProviders(Intent intent, String resolvedType, int flags, int userId) { @@ -7066,7 +7158,9 @@ public class PackageManagerService extends IPackageManager.Stub { private @NonNull List queryIntentContentProvidersInternal( Intent intent, String resolvedType, int flags, int userId) { if (!sUserManager.exists(userId)) return Collections.emptyList(); - flags = updateFlagsForResolve(flags, userId, intent, false); + final int callingUid = Binder.getCallingUid(); + flags = updateFlagsForResolve(flags, userId, intent, callingUid, + false /*includeInstantApps*/); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { @@ -12401,11 +12495,29 @@ public class PackageManagerService extends IPackageManager.Stub { if (ps == null) { return null; } + final PackageUserState userState = ps.readUserState(userId); ServiceInfo si = PackageParser.generateServiceInfo(service, mFlags, - ps.readUserState(userId), userId); + userState, userId); if (si == null) { return null; } + final boolean matchVisibleToInstantApp = + (mFlags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0; + final boolean isInstantApp = (mFlags & PackageManager.MATCH_INSTANT) != 0; + // throw out filters that aren't visible to ephemeral apps + if (matchVisibleToInstantApp + && !(info.isVisibleToInstantApp() || userState.instantApp)) { + return null; + } + // throw out ephemeral filters if we're not explicitly requesting them + if (!isInstantApp && userState.instantApp) { + return null; + } + // throw out instant app filters if updates are available; will trigger + // instant app resolution + if (userState.instantApp && ps.isUpdateAvailable()) { + return null; + } final ResolveInfo res = new ResolveInfo(); res.serviceInfo = si; if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) { @@ -23146,10 +23258,18 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } } + @Override public ResolveInfo resolveIntent(Intent intent, String resolvedType, int flags, int userId) { return resolveIntentInternal( - intent, resolvedType, flags, userId, true /*includeInstantApp*/); + intent, resolvedType, flags, userId, true /*includeInstantApps*/); + } + + @Override + public ResolveInfo resolveService(Intent intent, String resolvedType, + int flags, int userId, int callingUid) { + return resolveServiceInternal( + intent, resolvedType, flags, userId, callingUid, true /*includeInstantApps*/); } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 8739089552e4..6fb056a48f7e 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -3375,7 +3375,7 @@ final class Settings { private void applyDefaultPreferredActivityLPw(PackageManagerService service, Intent intent, int flags, ComponentName cn, String scheme, PatternMatcher ssp, IntentFilter.AuthorityEntry auth, PatternMatcher path, int userId) { - flags = service.updateFlagsForResolve(flags, userId, intent, false); + flags = service.updateFlagsForResolve(flags, userId, intent, Binder.getCallingUid(), false); List ri = service.mActivities.queryIntent(intent, intent.getType(), flags, 0); if (PackageManagerService.DEBUG_PREFERRED) Log.d(TAG, "Queried " + intent -- 2.11.0