summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Todd Kennedy <toddke@google.com> 2017-01-13 14:15:36 -0800
committer Todd Kennedy <toddke@google.com> 2017-01-19 15:26:21 -0800
commit0e989d00ed1e95be0ccb77c29846ee0b6ac33356 (patch)
treee1a62dd3e131ecc4fa36df545c3be0cf6bf48ae3
parent4ea4f63d28efe745051f0887c8a1e70f916fe500 (diff)
Grant access to ephemeral metadata
When an ephemeral application explicitly accesses an installed application, it grants access to its package metadata. The ephemeral application effectively stays hidden if it doesn't explicitly connect to any activity, service or provider [i.e. implicit connections using an ACTION_VIEW/CATEGORY_BROWSABLE intent will not expose its metadata]. Bug: 34123112 Test: cts-tradefed run commandAndExit cts-dev -m CtsAppSecurityHostTestCases -t android.appsecurity.cts.EphemeralTest Change-Id: I7e1680902599b3ada0d4fba5998af30566017051
-rw-r--r--core/java/android/content/pm/PackageManagerInternal.java21
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java1
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java6
-rw-r--r--services/core/java/com/android/server/am/ActivityStarter.java3
-rw-r--r--services/core/java/com/android/server/pm/EphemeralApplicationRegistry.java127
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java52
6 files changed, 199 insertions, 11 deletions
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index 2590a6beda39..62f38483250a 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -223,6 +223,27 @@ public abstract class PackageManagerInternal {
int userId);
/**
+ * Grants access to the package metadata for an ephemeral application.
+ * <p>
+ * When an ephemeral application explicitly tries to interact with a full
+ * install application [via an activity, service or provider that has been
+ * exposed using the {@code visibleToInstantApp} attribute], the normal
+ * application must be able to see metadata about the connecting ephemeral
+ * app. If the ephemeral application uses an implicit intent [ie action VIEW,
+ * category BROWSABLE], it remains hidden from the launched activity.
+ * <p>
+ * If the {@code sourceUid} is not for an ephemeral app or {@code targetUid}
+ * is not for a fully installed app, this method will be a no-op.
+ *
+ * @param userId the user
+ * @param intent the intent that triggered the grant
+ * @param targetAppId The app ID of the fully installed application
+ * @param ephemeralAppId The app ID of the ephemeral application
+ */
+ public abstract void grantEphemeralAccess(int userId, Intent intent,
+ int targetAppId, int ephemeralAppId);
+
+ /**
* @return The SetupWizard package name.
*/
public abstract String getSetupWizardPackageName();
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 7661127927e1..bfed37d09f32 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1907,6 +1907,7 @@ public final class ActiveServices {
mAm.grantUriPermissionUncheckedFromIntentLocked(si.neededGrants,
si.getUriPermissionsLocked());
}
+ // TODO b/34123112; Insert ephemeral grant here
bumpServiceExecutingLocked(r, execInFg, "start");
if (!oomAdjusted) {
oomAdjusted = true;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 2a324ebafbd7..0243391d6b92 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8079,6 +8079,12 @@ public class ActivityManagerService extends IActivityManager.Stub
return pi;
}
+ void grantEphemeralAccessLocked(int userId, Intent intent,
+ int targetAppId, int ephemeralAppId) {
+ getPackageManagerInternalLocked().
+ grantEphemeralAccess(userId, intent, targetAppId, ephemeralAppId);
+ }
+
private UriPermission findUriPermissionLocked(int targetUid, GrantUri grantUri) {
final ArrayMap<GrantUri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid);
if (targetUris != null) {
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 3f71d123aff0..46e004799a63 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -1131,7 +1131,8 @@ class ActivityStarter {
mService.grantUriPermissionFromIntentLocked(mCallingUid, mStartActivity.packageName,
mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.userId);
-
+ mService.grantEphemeralAccessLocked(mStartActivity.userId, mIntent,
+ mStartActivity.appInfo.uid, UserHandle.getAppId(mCallingUid));
if (mSourceRecord != null && mSourceRecord.isRecentsActivity()) {
mStartActivity.task.setTaskToReturnTo(RECENTS_ACTIVITY_TYPE);
}
diff --git a/services/core/java/com/android/server/pm/EphemeralApplicationRegistry.java b/services/core/java/com/android/server/pm/EphemeralApplicationRegistry.java
index 1e3e0caa1ece..e8be62948915 100644
--- a/services/core/java/com/android/server/pm/EphemeralApplicationRegistry.java
+++ b/services/core/java/com/android/server/pm/EphemeralApplicationRegistry.java
@@ -17,6 +17,7 @@
package com.android.server.pm;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.EphemeralApplicationInfo;
import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
@@ -27,10 +28,13 @@ import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Binder;
import android.os.Environment;
+import android.os.UserHandle;
import android.provider.Settings;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
@@ -51,6 +55,7 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -62,7 +67,7 @@ import java.util.Set;
class EphemeralApplicationRegistry {
private static final boolean DEBUG = false;
- private static final boolean ENABLED = false;
+ private static final boolean ENABLED = true;
private static final String LOG_TAG = "EphemeralAppRegistry";
@@ -90,6 +95,16 @@ class EphemeralApplicationRegistry {
@GuardedBy("mService.mPackages")
private SparseArray<List<UninstalledEphemeralAppState>> mUninstalledEphemeralApps;
+ /**
+ * Automatic grants for access to instant app metadata.
+ * The key is the target application UID.
+ * The value is a set of instant app UIDs.
+ * UserID -> TargetAppId -> InstantAppId
+ */
+ private SparseArray<SparseArray<SparseBooleanArray>> mEphemeralGrants;
+ /** The set of all installed instant apps. UserID -> AppID */
+ private SparseArray<SparseBooleanArray> mInstalledEphemeralAppUids;
+
public EphemeralApplicationRegistry(PackageManagerService service) {
mService = service;
}
@@ -189,6 +204,9 @@ class EphemeralApplicationRegistry {
// Propagate permissions before removing any state
propagateEphemeralAppPermissionsIfNeeded(pkg, userId);
+ if (pkg.applicationInfo.isEphemeralApp()) {
+ addEphemeralAppLPw(userId, ps.appId);
+ }
// Remove the in-memory state
if (mUninstalledEphemeralApps != null) {
@@ -248,9 +266,11 @@ class EphemeralApplicationRegistry {
if (pkg.applicationInfo.isEphemeralApp()) {
// Add a record for an uninstalled ephemeral app
addUninstalledEphemeralAppLPw(pkg, userId);
+ removeEphemeralAppLPw(userId, ps.appId);
} else {
// Deleting an app prunes all ephemeral state such as cookie
deleteDir(getEphemeralApplicationDir(pkg.packageName, userId));
+ removeAppLPw(userId, ps.appId);
}
}
}
@@ -262,9 +282,114 @@ class EphemeralApplicationRegistry {
if (mUninstalledEphemeralApps != null) {
mUninstalledEphemeralApps.remove(userId);
}
+ if (mInstalledEphemeralAppUids != null) {
+ mInstalledEphemeralAppUids.remove(userId);
+ }
+ if (mEphemeralGrants != null) {
+ mEphemeralGrants.remove(userId);
+ }
deleteDir(getEphemeralApplicationsDir(userId));
}
+ public boolean isEphemeralAccessGranted(int userId, int targetAppId, int ephemeralAppId) {
+ if (mEphemeralGrants == null) {
+ return false;
+ }
+ final SparseArray<SparseBooleanArray> targetAppList = mEphemeralGrants.get(userId);
+ if (targetAppList == null) {
+ return false;
+ }
+ final SparseBooleanArray ephemeralGrantList = targetAppList.get(targetAppId);
+ if (ephemeralGrantList == null) {
+ return false;
+ }
+ return ephemeralGrantList.get(ephemeralAppId);
+ }
+
+ public void grantEphemeralAccessLPw(int userId, Intent intent,
+ int targetAppId, int ephemeralAppId) {
+ if (mInstalledEphemeralAppUids == null) {
+ return; // no ephemeral apps installed; no need to grant
+ }
+ SparseBooleanArray ephemeralAppList = mInstalledEphemeralAppUids.get(userId);
+ if (ephemeralAppList == null || !ephemeralAppList.get(ephemeralAppId)) {
+ return; // ephemeral app id isn't installed; no need to grant
+ }
+ if (ephemeralAppList.get(targetAppId)) {
+ return; // target app id is an ephemeral app; no need to grant
+ }
+ if (intent != null && Intent.ACTION_VIEW.equals(intent.getAction())) {
+ final Set<String> categories = intent.getCategories();
+ if (categories != null && categories.contains(Intent.CATEGORY_BROWSABLE)) {
+ return; // launched via VIEW/BROWSABLE intent; no need to grant
+ }
+ }
+ if (mEphemeralGrants == null) {
+ mEphemeralGrants = new SparseArray<>();
+ }
+ SparseArray<SparseBooleanArray> targetAppList = mEphemeralGrants.get(userId);
+ if (targetAppList == null) {
+ targetAppList = new SparseArray<>();
+ mEphemeralGrants.put(userId, targetAppList);
+ }
+ SparseBooleanArray ephemeralGrantList = targetAppList.get(targetAppId);
+ if (ephemeralGrantList == null) {
+ ephemeralGrantList = new SparseBooleanArray();
+ targetAppList.put(targetAppId, ephemeralGrantList);
+ }
+ ephemeralGrantList.put(ephemeralAppId, true /*granted*/);
+ }
+
+ public void addEphemeralAppLPw(int userId, int ephemeralAppId) {
+ if (mInstalledEphemeralAppUids == null) {
+ mInstalledEphemeralAppUids = new SparseArray<>();
+ }
+ SparseBooleanArray ephemeralAppList = mInstalledEphemeralAppUids.get(userId);
+ if (ephemeralAppList == null) {
+ ephemeralAppList = new SparseBooleanArray();
+ mInstalledEphemeralAppUids.put(userId, ephemeralAppList);
+ }
+ ephemeralAppList.put(ephemeralAppId, true /*installed*/);
+ }
+
+ private void removeEphemeralAppLPw(int userId, int ephemeralAppId) {
+ // remove from the installed list
+ if (mInstalledEphemeralAppUids == null) {
+ return; // no ephemeral apps on the system
+ }
+ final SparseBooleanArray ephemeralAppList = mInstalledEphemeralAppUids.get(userId);
+ if (ephemeralAppList == null) {
+ Slog.w(LOG_TAG, "Remove ephemeral not in install list");
+ return;
+ } else {
+ ephemeralAppList.delete(ephemeralAppId);
+ }
+ // remove any grants
+ if (mEphemeralGrants == null) {
+ return; // no grants on the system
+ }
+ final SparseArray<SparseBooleanArray> targetAppList = mEphemeralGrants.get(userId);
+ if (targetAppList == null) {
+ return; // no grants for this user
+ }
+ final int numApps = targetAppList.size();
+ for (int i = targetAppList.size() - 1; i >= 0; --i) {
+ targetAppList.valueAt(i).delete(ephemeralAppId);
+ }
+ }
+
+ private void removeAppLPw(int userId, int targetAppId) {
+ // remove from the installed list
+ if (mEphemeralGrants == null) {
+ return; // no grants on the system
+ }
+ final SparseArray<SparseBooleanArray> targetAppList = mEphemeralGrants.get(userId);
+ if (targetAppList == null) {
+ return; // no grants for this user
+ }
+ targetAppList.delete(targetAppId);
+ }
+
private void addUninstalledEphemeralAppLPw(PackageParser.Package pkg, int userId) {
EphemeralApplicationInfo uninstalledApp = createEphemeralAppInfoForPackage(pkg, userId);
if (uninstalledApp == null) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 666988963171..ccc6a9520b3f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2224,6 +2224,7 @@ public class PackageManagerService extends IPackageManager.Stub {
Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
mDefaultPermissionPolicy = new DefaultPermissionGrantPolicy(this);
+ mEphemeralApplicationRegistry = new EphemeralApplicationRegistry(this);
File dataDir = Environment.getDataDirectory();
mAppInstallDir = new File(dataDir, "app");
@@ -2806,8 +2807,6 @@ public class PackageManagerService extends IPackageManager.Stub {
setUpEphemeralInstallerActivityLP(mEphemeralInstallerComponent);
}
- mEphemeralApplicationRegistry = new EphemeralApplicationRegistry(this);
-
// Read and update the usage of dex files.
// Do this at the end of PM init so that all the packages have their
// data directory reconciled.
@@ -3257,6 +3256,31 @@ public class PackageManagerService extends IPackageManager.Stub {
if (p == null) {
return null;
}
+ // Filter out ephemeral app metadata:
+ // * The system/shell/root can see metadata for any app
+ // * An installed app can see metadata for 1) other installed apps
+ // and 2) ephemeral apps that have explicitly interacted with it
+ // * Ephemeral apps can only see their own metadata
+ final int callingAppId = UserHandle.getAppId(Binder.getCallingUid());
+ if (callingAppId != Process.SYSTEM_UID
+ && callingAppId != Process.SHELL_UID
+ && callingAppId != Process.ROOT_UID) {
+ final String ephemeralPackageName = getEphemeralPackageName(Binder.getCallingUid());
+ if (ephemeralPackageName != null) {
+ // ephemeral apps can only get information on themselves
+ if (!ephemeralPackageName.equals(p.packageName)) {
+ return null;
+ }
+ } else {
+ if (p.applicationInfo.isEphemeralApp()) {
+ // only get access to the ephemeral app if we've been granted access
+ if (!mEphemeralApplicationRegistry.isEphemeralAccessGranted(
+ userId, callingAppId, ps.appId)) {
+ return null;
+ }
+ }
+ }
+ }
final PermissionsState permissionsState = ps.getPermissionsState();
@@ -3336,22 +3360,19 @@ public class PackageManagerService extends IPackageManager.Stub {
// reader
synchronized (mPackages) {
- // Normalize package name to hanlde renamed packages
+ // Normalize package name to handle renamed packages
packageName = normalizePackageNameLPr(packageName);
final boolean matchFactoryOnly = (flags & MATCH_FACTORY_ONLY) != 0;
- PackageParser.Package p = null;
if (matchFactoryOnly) {
final PackageSetting ps = mSettings.getDisabledSystemPkgLPr(packageName);
if (ps != null) {
return generatePackageInfo(ps, flags, userId);
}
}
- if (p == null) {
- p = mPackages.get(packageName);
- if (matchFactoryOnly && p != null && !isSystemApp(p)) {
- return null;
- }
+ PackageParser.Package p = mPackages.get(packageName);
+ if (matchFactoryOnly && p != null && !isSystemApp(p)) {
+ return null;
}
if (DEBUG_PACKAGE_INFO)
Log.v(TAG, "getPackageInfo " + packageName + ": " + p);
@@ -8839,6 +8860,10 @@ public class PackageManagerService extends IPackageManager.Stub {
// Modify state for the given package setting
commitPackageSettings(pkg, pkgSetting, user, scanFlags,
(policyFlags & PackageParser.PARSE_CHATTY) != 0 /*chatty*/);
+ if (isEphemeral(pkg)) {
+ final int userId = user == null ? 0 : user.getIdentifier();
+ mEphemeralApplicationRegistry.addEphemeralAppLPw(userId, pkgSetting.appId);
+ }
}
return pkg;
}
@@ -21924,6 +21949,15 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
responseObj, origIntent, resolvedType, launchIntent, callingPackage, userId);
}
+ @Override
+ public void grantEphemeralAccess(int userId, Intent intent,
+ int targetAppId, int ephemeralAppId) {
+ synchronized (mPackages) {
+ mEphemeralApplicationRegistry.grantEphemeralAccessLPw(userId, intent,
+ targetAppId, ephemeralAppId);
+ }
+ }
+
public String getSetupWizardPackageName() {
return mSetupWizardPackage;
}