diff options
| author | 2021-07-14 17:38:45 +0100 | |
|---|---|---|
| committer | 2021-12-03 18:19:00 +0000 | |
| commit | 95ecac506dba532636f0a2f8f56b64a9914b0d6a (patch) | |
| tree | 6fccbb8f03dd8c6fefc07c00af46c82c9c70e0b2 | |
| parent | 8d221843b767e18c04c82bb972c8bbdfd459742b (diff) | |
Allow privapp permission allowlist in apex
Read priv-app permission allowlists from APEX file and warn if
they're in the /system partition instead.
Test: boots
Bug: 190375768
Change-Id: I37d6deb60f0dffb75dd634075cd95bcf7ddf9684
Merged-In: I37d6deb60f0dffb75dd634075cd95bcf7ddf9684
3 files changed, 115 insertions, 13 deletions
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java index b0cf5dcbbfd5..ae9d71610aaa 100644 --- a/core/java/com/android/server/SystemConfig.java +++ b/core/java/com/android/server/SystemConfig.java @@ -56,6 +56,7 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -95,6 +96,9 @@ public class SystemConfig { // property for runtime configuration differentiation in vendor private static final String VENDOR_SKU_PROPERTY = "ro.boot.product.vendor.sku"; + private static final ArrayMap<String, ArraySet<String>> EMPTY_PERMISSIONS = + new ArrayMap<>(); + // Group-ids that are given to all packages as read from etc/permissions/*.xml. int[] mGlobalGids = EmptyArray.INT; @@ -224,6 +228,11 @@ public class SystemConfig { final ArrayMap<String, ArraySet<String>> mSystemExtPrivAppPermissions = new ArrayMap<>(); final ArrayMap<String, ArraySet<String>> mSystemExtPrivAppDenyPermissions = new ArrayMap<>(); + final ArrayMap<String, ArrayMap<String, ArraySet<String>>> mApexPrivAppPermissions = + new ArrayMap<>(); + final ArrayMap<String, ArrayMap<String, ArraySet<String>>> mApexPrivAppDenyPermissions = + new ArrayMap<>(); + final ArrayMap<String, ArrayMap<String, Boolean>> mOemPermissions = new ArrayMap<>(); // Allowed associations between applications. If there are any entries @@ -360,6 +369,18 @@ public class SystemConfig { return mPrivAppDenyPermissions.get(packageName); } + /** Get privapp permission allowlist for an apk-in-apex. */ + public ArraySet<String> getApexPrivAppPermissions(String module, String packageName) { + return mApexPrivAppPermissions.getOrDefault(module, EMPTY_PERMISSIONS) + .get(packageName); + } + + /** Get privapp permissions denylist for an apk-in-apex. */ + public ArraySet<String> getApexPrivAppDenyPermissions(String module, String packageName) { + return mApexPrivAppDenyPermissions.getOrDefault(module, EMPTY_PERMISSIONS) + .get(packageName); + } + public ArraySet<String> getVendorPrivAppPermissions(String packageName) { return mVendorPrivAppPermissions.get(packageName); } @@ -573,8 +594,8 @@ public class SystemConfig { if (!isSystemProcess()) { return; } - // Read configuration of features and libs from apex module. - int apexPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES; + // Read configuration of features, libs and priv-app permissions from apex module. + int apexPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PRIVAPP_PERMISSIONS; // TODO: Use a solid way to filter apex module folders? for (File f: FileUtils.listFilesOrEmpty(Environment.getApexDirectory())) { if (f.isFile() || f.getPath().contains("@")) { @@ -1040,10 +1061,10 @@ public class SystemConfig { } break; case "privapp-permissions": { if (allowPrivappPermissions) { - // privapp permissions from system, vendor, product and system_ext - // partitions are stored separately. This is to prevent xml files in - // the vendor partition from granting permissions to priv apps in the - // system partition and vice versa. + // privapp permissions from system, apex, vendor, product and + // system_ext partitions are stored separately. This is to + // prevent xml files in the vendor partition from granting + // permissions to priv apps in the system partition and vice versa. boolean vendor = permFile.toPath().startsWith( Environment.getVendorDirectory().toPath() + "/") || permFile.toPath().startsWith( @@ -1052,6 +1073,8 @@ public class SystemConfig { Environment.getProductDirectory().toPath() + "/"); boolean systemExt = permFile.toPath().startsWith( Environment.getSystemExtDirectory().toPath() + "/"); + boolean apex = permFile.toPath().startsWith( + Environment.getApexDirectory().toPath() + "/"); if (vendor) { readPrivAppPermissions(parser, mVendorPrivAppPermissions, mVendorPrivAppDenyPermissions); @@ -1061,6 +1084,8 @@ public class SystemConfig { } else if (systemExt) { readPrivAppPermissions(parser, mSystemExtPrivAppPermissions, mSystemExtPrivAppDenyPermissions); + } else if (apex) { + readApexPrivAppPermissions(parser, permFile); } else { readPrivAppPermissions(parser, mPrivAppPermissions, mPrivAppDenyPermissions); @@ -1616,6 +1641,43 @@ public class SystemConfig { } } + + /** + * Returns the module name for a file in the apex module's partition. + */ + private String getApexModuleNameFromFilePath(Path path) { + final Path apexDirectoryPath = Environment.getApexDirectory().toPath(); + if (!path.startsWith(apexDirectoryPath)) { + throw new IllegalArgumentException("File " + path + " is not part of an APEX."); + } + // File must be in <apex_directory>/<module_name>/[extra_paths/]<xml_file> + if (path.getNameCount() <= (apexDirectoryPath.getNameCount() + 1)) { + throw new IllegalArgumentException("File " + path + " is in the APEX partition," + + " but not inside a module."); + } + return path.getName(apexDirectoryPath.getNameCount()).toString(); + } + + private void readApexPrivAppPermissions(XmlPullParser parser, File permFile) + throws IOException, XmlPullParserException { + final String moduleName = getApexModuleNameFromFilePath(permFile.toPath()); + final ArrayMap<String, ArraySet<String>> privAppPermissions; + if (mApexPrivAppPermissions.containsKey(moduleName)) { + privAppPermissions = mApexPrivAppPermissions.get(moduleName); + } else { + privAppPermissions = new ArrayMap<>(); + mApexPrivAppPermissions.put(moduleName, privAppPermissions); + } + final ArrayMap<String, ArraySet<String>> privAppDenyPermissions; + if (mApexPrivAppDenyPermissions.containsKey(moduleName)) { + privAppDenyPermissions = mApexPrivAppDenyPermissions.get(moduleName); + } else { + privAppDenyPermissions = new ArrayMap<>(); + mApexPrivAppDenyPermissions.put(moduleName, privAppDenyPermissions); + } + readPrivAppPermissions(parser, privAppPermissions, privAppDenyPermissions); + } + private static boolean isSystemProcess() { return Process.myUid() == Process.SYSTEM_UID; } diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 1aa80a953d6c..5b4084e9b229 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -2438,6 +2438,15 @@ class PackageManagerShellCommand extends ShellCommand { } } + private String getApexPackageNameContainingPackage(String pkg) { + ApexManager apexManager = ApexManager.getInstance(); + return apexManager.getActiveApexPackageNameContainingPackage(pkg); + } + + private boolean isApexApp(String pkg) { + return getApexPackageNameContainingPackage(pkg) != null; + } + private int runGetPrivappPermissions() { final String pkg = getNextArg(); if (pkg == null) { @@ -2453,6 +2462,9 @@ class PackageManagerShellCommand extends ShellCommand { } else if (isSystemExtApp(pkg)) { privAppPermissions = SystemConfig.getInstance() .getSystemExtPrivAppPermissions(pkg); + } else if (isApexApp(pkg)) { + privAppPermissions = SystemConfig.getInstance() + .getApexPrivAppPermissions(getApexPackageNameContainingPackage(pkg), pkg); } else { privAppPermissions = SystemConfig.getInstance().getPrivAppPermissions(pkg); } @@ -2477,6 +2489,9 @@ class PackageManagerShellCommand extends ShellCommand { } else if (isSystemExtApp(pkg)) { privAppPermissions = SystemConfig.getInstance() .getSystemExtPrivAppDenyPermissions(pkg); + } else if (isApexApp(pkg)) { + privAppPermissions = SystemConfig.getInstance() + .getApexPrivAppDenyPermissions(getApexPackageNameContainingPackage(pkg), pkg); } else { privAppPermissions = SystemConfig.getInstance().getPrivAppDenyPermissions(pkg); } diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 7b12709e4efd..f733a2e0bb3f 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -3443,10 +3443,15 @@ public class PermissionManagerService extends IPermissionManager.Stub { return true; } final String permissionName = permission.getName(); - if (isInSystemConfigPrivAppPermissions(pkg, permissionName)) { + final ApexManager apexManager = ApexManager.getInstance(); + final String containingApexPackageName = + apexManager.getActiveApexPackageNameContainingPackage(packageName); + if (isInSystemConfigPrivAppPermissions(pkg, permissionName, + containingApexPackageName)) { return true; } - if (isInSystemConfigPrivAppDenyPermissions(pkg, permissionName)) { + if (isInSystemConfigPrivAppDenyPermissions(pkg, permissionName, + containingApexPackageName)) { return false; } // Updated system apps do not need to be allowlisted @@ -3463,9 +3468,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { } // Only enforce the allowlist on boot if (!mSystemReady) { - final ApexManager apexManager = ApexManager.getInstance(); - final String containingApexPackageName = - apexManager.getActiveApexPackageNameContainingPackage(packageName); final boolean isInUpdatedApex = containingApexPackageName != null && !apexManager.isFactory(apexManager.getPackageInfo(containingApexPackageName, MATCH_ACTIVE_PACKAGE)); @@ -3489,7 +3491,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } private boolean isInSystemConfigPrivAppPermissions(@NonNull AndroidPackage pkg, - @NonNull String permission) { + @NonNull String permission, String containingApexPackageName) { final SystemConfig systemConfig = SystemConfig.getInstance(); final Set<String> permissions; if (pkg.isVendor()) { @@ -3498,6 +3500,26 @@ public class PermissionManagerService extends IPermissionManager.Stub { permissions = systemConfig.getProductPrivAppPermissions(pkg.getPackageName()); } else if (pkg.isSystemExt()) { permissions = systemConfig.getSystemExtPrivAppPermissions(pkg.getPackageName()); + } else if (containingApexPackageName != null) { + final Set<String> privAppPermissions = systemConfig.getPrivAppPermissions( + pkg.getPackageName()); + final Set<String> apexPermissions = systemConfig.getApexPrivAppPermissions( + containingApexPackageName, pkg.getPackageName()); + if (privAppPermissions != null) { + // TODO(andreionea): Remove check as soon as all apk-in-apex + // permission allowlists are migrated. + Slog.w(TAG, "Package " + pkg.getPackageName() + " is an APK in APEX," + + " but has permission allowlist on the system image. Please bundle the" + + " allowlist in the " + containingApexPackageName + " APEX instead."); + if (apexPermissions != null) { + permissions = new ArraySet<>(privAppPermissions); + permissions.addAll(apexPermissions); + } else { + permissions = privAppPermissions; + } + } else { + permissions = apexPermissions; + } } else { permissions = systemConfig.getPrivAppPermissions(pkg.getPackageName()); } @@ -3505,7 +3527,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } private boolean isInSystemConfigPrivAppDenyPermissions(@NonNull AndroidPackage pkg, - @NonNull String permission) { + @NonNull String permission, String containingApexPackageName) { final SystemConfig systemConfig = SystemConfig.getInstance(); final Set<String> permissions; if (pkg.isVendor()) { @@ -3514,6 +3536,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { permissions = systemConfig.getProductPrivAppDenyPermissions(pkg.getPackageName()); } else if (pkg.isSystemExt()) { permissions = systemConfig.getSystemExtPrivAppDenyPermissions(pkg.getPackageName()); + } else if (containingApexPackageName != null) { + permissions = systemConfig.getApexPrivAppDenyPermissions(containingApexPackageName, + pkg.getPackageName()); } else { permissions = systemConfig.getPrivAppDenyPermissions(pkg.getPackageName()); } |