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
This commit is contained in:
Andrei Onea
2021-07-14 17:38:45 +01:00
parent 8d221843b7
commit 95ecac506d
3 changed files with 115 additions and 13 deletions

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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());
}