Overlayable actor enforcement
Validates that the caller of an OverlayManager API that mutates state
is actually allowed to act on the target as defined in the target's
overlayable tag.
<overlayable name="MyResources" actor="namespace/name">
An actor is valid if any of the following is true:
- is root/system
- is the target overlay package
- has the CHANGE_OVERLAY_PACKAGES permission and an actor is not defined
- is the same package name as the sole resolved Activity for the actor specified
in the overlayable definition, with only pre-installed, namespaced actors
currently supported
Bug: 119442583
Bug: 135052950
Test: atest SystemConfigNamedActorTest
Test: atest com.android.server.om
Change-Id: If56b9e8366852eaef84f6bb25c3e6871eaa3f219
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 5f3e503..63de61c 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -39,10 +39,12 @@
import android.content.IntentFilter;
import android.content.om.IOverlayManager;
import android.content.om.OverlayInfo;
+import android.content.om.OverlayableInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManagerInternal;
import android.content.pm.UserInfo;
+import android.content.res.ApkAssets;
import android.net.Uri;
import android.os.Binder;
import android.os.Environment;
@@ -63,6 +65,7 @@
import com.android.server.FgThread;
import com.android.server.IoThread;
import com.android.server.LocalServices;
+import com.android.server.SystemConfig;
import com.android.server.SystemService;
import com.android.server.pm.UserManagerService;
@@ -229,6 +232,8 @@
private final OverlayManagerServiceImpl mImpl;
+ private final OverlayActorEnforcer mActorEnforcer;
+
private final AtomicBoolean mPersistSettingsScheduled = new AtomicBoolean(false);
public OverlayManagerService(@NonNull final Context context) {
@@ -237,12 +242,13 @@
traceBegin(TRACE_TAG_RRO, "OMS#OverlayManagerService");
mSettingsFile = new AtomicFile(
new File(Environment.getDataSystemDirectory(), "overlays.xml"), "overlays");
- mPackageManager = new PackageManagerHelper();
+ mPackageManager = new PackageManagerHelper(context);
mUserManager = UserManagerService.getInstance();
IdmapManager im = new IdmapManager(mPackageManager);
mSettings = new OverlayManagerSettings();
mImpl = new OverlayManagerServiceImpl(mPackageManager, im, mSettings,
getDefaultOverlayPackages(), new OverlayChangeListener());
+ mActorEnforcer = new OverlayActorEnforcer(mPackageManager);
final IntentFilter packageFilter = new IntentFilter();
packageFilter.addAction(ACTION_PACKAGE_ADDED);
@@ -581,7 +587,7 @@
int userId) throws RemoteException {
try {
traceBegin(TRACE_TAG_RRO, "OMS#setEnabled " + packageName + " " + enable);
- enforceChangeOverlayPackagesPermission("setEnabled");
+ enforceActor(packageName, "setEnabled", userId);
userId = handleIncomingUser(userId, "setEnabled");
if (packageName == null) {
return false;
@@ -605,7 +611,7 @@
int userId) throws RemoteException {
try {
traceBegin(TRACE_TAG_RRO, "OMS#setEnabledExclusive " + packageName + " " + enable);
- enforceChangeOverlayPackagesPermission("setEnabledExclusive");
+ enforceActor(packageName, "setEnabledExclusive", userId);
userId = handleIncomingUser(userId, "setEnabledExclusive");
if (packageName == null || !enable) {
return false;
@@ -630,7 +636,7 @@
throws RemoteException {
try {
traceBegin(TRACE_TAG_RRO, "OMS#setEnabledExclusiveInCategory " + packageName);
- enforceChangeOverlayPackagesPermission("setEnabledExclusiveInCategory");
+ enforceActor(packageName, "setEnabledExclusiveInCategory", userId);
userId = handleIncomingUser(userId, "setEnabledExclusiveInCategory");
if (packageName == null) {
return false;
@@ -656,7 +662,7 @@
try {
traceBegin(TRACE_TAG_RRO, "OMS#setPriority " + packageName + " "
+ parentPackageName);
- enforceChangeOverlayPackagesPermission("setPriority");
+ enforceActor(packageName, "setPriority", userId);
userId = handleIncomingUser(userId, "setPriority");
if (packageName == null || parentPackageName == null) {
return false;
@@ -680,7 +686,7 @@
throws RemoteException {
try {
traceBegin(TRACE_TAG_RRO, "OMS#setHighestPriority " + packageName);
- enforceChangeOverlayPackagesPermission("setHighestPriority");
+ enforceActor(packageName, "setHighestPriority", userId);
userId = handleIncomingUser(userId, "setHighestPriority");
if (packageName == null) {
return false;
@@ -704,7 +710,7 @@
throws RemoteException {
try {
traceBegin(TRACE_TAG_RRO, "OMS#setLowestPriority " + packageName);
- enforceChangeOverlayPackagesPermission("setLowestPriority");
+ enforceActor(packageName, "setLowestPriority", userId);
userId = handleIncomingUser(userId, "setLowestPriority");
if (packageName == null) {
return false;
@@ -750,7 +756,7 @@
return;
}
- enforceChangeOverlayPackagesPermission("invalidateCachesForOverlay");
+ enforceActor(packageName, "invalidateCachesForOverlay", userId);
userId = handleIncomingUser(userId, "invalidateCachesForOverlay");
final long ident = Binder.clearCallingIdentity();
try {
@@ -861,18 +867,6 @@
}
/**
- * Enforce that the caller holds the CHANGE_OVERLAY_PACKAGES permission (or is
- * system or root).
- *
- * @param message used as message if SecurityException is thrown
- * @throws SecurityException if the permission check fails
- */
- private void enforceChangeOverlayPackagesPermission(@NonNull final String message) {
- getContext().enforceCallingOrSelfPermission(
- android.Manifest.permission.CHANGE_OVERLAY_PACKAGES, message);
- }
-
- /**
* Enforce that the caller holds the DUMP permission (or is system or root).
*
* @param message used as message if SecurityException is thrown
@@ -881,6 +875,13 @@
private void enforceDumpPermission(@NonNull final String message) {
getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, message);
}
+
+ private void enforceActor(String packageName, String methodName, int userId)
+ throws SecurityException {
+ OverlayInfo overlayInfo = mImpl.getOverlayInfo(packageName, userId);
+ int callingUid = Binder.getCallingUid();
+ mActorEnforcer.enforceActor(overlayInfo, methodName, callingUid, userId);
+ }
};
private final class OverlayChangeListener
@@ -1035,9 +1036,16 @@
}
}
- private static final class PackageManagerHelper implements
- OverlayManagerServiceImpl.PackageManagerHelper {
+ /**
+ * Delegate for {@link android.content.pm.PackageManager} and {@link PackageManagerInternal}
+ * functionality, separated for easy testing.
+ *
+ * @hide
+ */
+ public static final class PackageManagerHelper implements
+ OverlayManagerServiceImpl.PackageManagerHelper, OverlayActorEnforcer.VerifyCallback {
+ private final Context mContext;
private final IPackageManager mPackageManager;
private final PackageManagerInternal mPackageManagerInternal;
@@ -1048,11 +1056,14 @@
// behind until all pending intents have been processed.
private final SparseArray<HashMap<String, PackageInfo>> mCache = new SparseArray<>();
- PackageManagerHelper() {
+ PackageManagerHelper(Context context) {
+ mContext = context;
mPackageManager = getPackageManager();
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
}
+ // TODO(b/143096091): Remove PackageInfo cache so that PackageManager is always queried
+ // to enforce visibility/other permission checks
public PackageInfo getPackageInfo(@NonNull final String packageName, final int userId,
final boolean useCache) {
if (useCache) {
@@ -1075,7 +1086,19 @@
@Override
public PackageInfo getPackageInfo(@NonNull final String packageName, final int userId) {
- return getPackageInfo(packageName, userId, true);
+ // TODO(b/143096091): Remove clearing calling ID
+ long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ return getPackageInfo(packageName, userId, true);
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ @NonNull
+ @Override
+ public Map<String, ? extends Map<String, String>> getNamedActors() {
+ return SystemConfig.getInstance().getNamedActors();
}
@Override
@@ -1097,6 +1120,70 @@
return mPackageManagerInternal.getOverlayPackages(userId);
}
+ @Nullable
+ @Override
+ public OverlayableInfo getOverlayableForTarget(@NonNull String packageName,
+ @Nullable String targetOverlayableName, int userId)
+ throws IOException {
+ // TODO(b/143096091): Remove clearing calling ID
+ long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ PackageInfo packageInfo = getPackageInfo(packageName, userId);
+ if (packageInfo == null) {
+ throw new IOException("Unable to get target package");
+ }
+
+ String baseCodePath = packageInfo.applicationInfo.getBaseCodePath();
+
+ ApkAssets apkAssets = null;
+ try {
+ apkAssets = ApkAssets.loadFromPath(baseCodePath);
+ return apkAssets.getOverlayableInfo(targetOverlayableName);
+ } finally {
+ if (apkAssets != null) {
+ try {
+ apkAssets.close();
+ } catch (Throwable ignored) {
+ }
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ @Override
+ public boolean doesTargetDefineOverlayable(String targetPackageName, int userId)
+ throws RemoteException, IOException {
+ // TODO(b/143096091): Remove clearing calling ID
+ long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ PackageInfo packageInfo = mPackageManager.getPackageInfo(targetPackageName, 0,
+ userId);
+ String baseCodePath = packageInfo.applicationInfo.getBaseCodePath();
+
+ ApkAssets apkAssets = null;
+ try {
+ apkAssets = ApkAssets.loadFromPath(baseCodePath);
+ return apkAssets.definesOverlayable();
+ } finally {
+ if (apkAssets != null) {
+ try {
+ apkAssets.close();
+ } catch (Throwable ignored) {
+ }
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ @Override
+ public void enforcePermission(String permission, String message) throws SecurityException {
+ mContext.enforceCallingOrSelfPermission(permission, message);
+ }
+
public PackageInfo getCachedPackageInfo(@NonNull final String packageName,
final int userId) {
final HashMap<String, PackageInfo> map = mCache.get(userId);
@@ -1128,6 +1215,22 @@
mCache.delete(userId);
}
+ @Nullable
+ @Override
+ public String[] getPackagesForUid(int uid) {
+ // TODO(b/143096091): Remove clearing calling ID
+ long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ try {
+ return mPackageManager.getPackagesForUid(uid);
+ } catch (RemoteException ignored) {
+ return null;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
+
private static final String TAB1 = " ";
private static final String TAB2 = TAB1 + TAB1;