Do not verify APKs out of updated APEXs

For APK-in-APEX the system is not aware of the pre-installed APKs. Hence
the currently installed APK always shows up as "factory-installed APK".

For factory installed APKs we enforce that all privilidged perissions
are manually whitelisted.

If an APK-in-APEX gets updated the app might request additional
privilidged permissions. We now explicity allow this.

This change leaves all apex specific logic inside of ApexMgr. E.g.
- how to figure out if a pkg is a apk-in-apex
- how apexPkgs are marked as factory/updated

Fixes: 148853593
Test: Built a new APK-In-APEX that contained new priv permission,
      installed it and rebooted -> no crash, but permission was not granted
Change-Id: I254e5189909efdafe3dc0ca8a5ab5a520457c585
This commit is contained in:
Philip P. Moltmann
2020-03-10 14:46:27 -07:00
parent 9541b09160
commit bd2780151a
2 changed files with 81 additions and 18 deletions

View File

@@ -62,6 +62,7 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;
@@ -74,7 +75,7 @@ public abstract class ApexManager {
private static final String TAG = "ApexManager";
static final int MATCH_ACTIVE_PACKAGE = 1 << 0;
public static final int MATCH_ACTIVE_PACKAGE = 1 << 0;
static final int MATCH_FACTORY_PACKAGE = 1 << 1;
private static final Singleton<ApexManager> sApexManagerSingleton =
@@ -163,7 +164,7 @@ public abstract class ApexManager {
* is not found.
*/
@Nullable
abstract PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags);
public abstract PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags);
/**
* Retrieves information about all active APEX packages.
@@ -197,6 +198,27 @@ public abstract class ApexManager {
*/
abstract boolean isApexPackage(String packageName);
/**
* Whether the APEX package is pre-installed or not.
*
* @param packageInfo the package to check
* @return {@code true} if this package is pre-installed, {@code false} otherwise.
*/
public static boolean isFactory(@NonNull PackageInfo packageInfo) {
return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
}
/**
* Returns the active apex package's name that contains the (apk) package.
*
* @param containedPackage The (apk) package that might be in a apex
* @return the apex package's name of {@code null} if the {@code containedPackage} is not inside
* any apex.
*/
@Nullable
public abstract String getActiveApexPackageNameContainingPackage(
@NonNull AndroidPackage containedPackage);
/**
* Retrieves information about an apexd staged session i.e. the internal state used by apexd to
* track the different states of a session.
@@ -351,7 +373,7 @@ public abstract class ApexManager {
* difference between {@code packageName} and {@code apexModuleName}.
*/
@GuardedBy("mLock")
private Map<String, List<String>> mApksInApex = new ArrayMap<>();
private ArrayMap<String, List<String>> mApksInApex = new ArrayMap<>();
@GuardedBy("mLock")
private List<PackageInfo> mAllPackagesCache;
@@ -366,7 +388,7 @@ public abstract class ApexManager {
* the apk container to {@code apexModuleName} of the apex-payload inside.
*/
@GuardedBy("mLock")
private Map<String, String> mPackageNameToApexModuleName;
private ArrayMap<String, String> mPackageNameToApexModuleName;
ApexManagerImpl(IApexService apexService) {
mApexService = apexService;
@@ -382,16 +404,6 @@ public abstract class ApexManager {
return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0;
}
/**
* Whether the APEX package is pre-installed or not.
*
* @param packageInfo the package to check
* @return {@code true} if this package is pre-installed, {@code false} otherwise.
*/
private static boolean isFactory(PackageInfo packageInfo) {
return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
}
@Override
public List<ActiveApexInfo> getActiveApexInfos() {
final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",
@@ -503,7 +515,9 @@ public abstract class ApexManager {
}
@Override
@Nullable PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags) {
@Nullable
public PackageInfo getPackageInfo(String packageName,
@PackageInfoFlags int flags) {
Preconditions.checkState(mAllPackagesCache != null,
"APEX packages have not been scanned");
boolean matchActive = (flags & MATCH_ACTIVE_PACKAGE) != 0;
@@ -563,6 +577,36 @@ public abstract class ApexManager {
return false;
}
@Override
@Nullable
public String getActiveApexPackageNameContainingPackage(
@NonNull AndroidPackage containedPackage) {
Preconditions.checkState(mPackageNameToApexModuleName != null,
"APEX packages have not been scanned");
Objects.requireNonNull(containedPackage);
synchronized (mLock) {
int numApksInApex = mApksInApex.size();
for (int apkInApexNum = 0; apkInApexNum < numApksInApex; apkInApexNum++) {
if (mApksInApex.valueAt(apkInApexNum).contains(
containedPackage.getPackageName())) {
String apexModuleName = mApksInApex.keyAt(apkInApexNum);
int numApexPkgs = mPackageNameToApexModuleName.size();
for (int apexPkgNum = 0; apexPkgNum < numApexPkgs; apexPkgNum++) {
if (mPackageNameToApexModuleName.valueAt(apexPkgNum).equals(
apexModuleName)) {
return mPackageNameToApexModuleName.keyAt(apexPkgNum);
}
}
}
}
}
return null;
}
@Override
@Nullable ApexSessionInfo getStagedSessionInfo(int sessionId) {
try {
@@ -893,7 +937,7 @@ public abstract class ApexManager {
}
@Override
PackageInfo getPackageInfo(String packageName, int flags) {
public PackageInfo getPackageInfo(String packageName, int flags) {
return null;
}
@@ -917,6 +961,15 @@ public abstract class ApexManager {
return false;
}
@Override
@Nullable
public String getActiveApexPackageNameContainingPackage(
@NonNull AndroidPackage containedPackage) {
Objects.requireNonNull(containedPackage);
return null;
}
@Override
ApexSessionInfo getStagedSessionInfo(int sessionId) {
throw new UnsupportedOperationException();

View File

@@ -40,6 +40,7 @@ import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.permission.PermissionManager.KILL_APP_REASON_GIDS_CHANGED;
import static android.permission.PermissionManager.KILL_APP_REASON_PERMISSIONS_REVOKED;
import static com.android.server.pm.ApexManager.MATCH_ACTIVE_PACKAGE;
import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING;
import static com.android.server.pm.PackageManagerService.DEBUG_PERMISSIONS;
@@ -128,6 +129,7 @@ import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.SystemConfig;
import com.android.server.Watchdog;
import com.android.server.pm.ApexManager;
import com.android.server.pm.PackageManagerServiceUtils;
import com.android.server.pm.PackageSetting;
import com.android.server.pm.SharedUserSetting;
@@ -3321,8 +3323,16 @@ public class PermissionManagerService extends IPermissionManager.Stub {
if (!privappPermissionsDisable && privilegedPermission && pkg.isPrivileged()
&& !platformPackage && platformPermission) {
if (!hasPrivappWhitelistEntry(perm, pkg)) {
// Only report violations for apps on system image
if (!mSystemReady && !pkgSetting.getPkgState().isUpdatedSystemApp()) {
ApexManager apexMgr = ApexManager.getInstance();
String apexContainingPkg = apexMgr.getActiveApexPackageNameContainingPackage(pkg);
// Only enforce whitelist this on boot
if (!mSystemReady
// Updated system apps do not need to be whitelisted
&& !pkgSetting.getPkgState().isUpdatedSystemApp()
// Apps that are in updated apexs' do not need to be whitelisted
&& (apexContainingPkg == null || apexMgr.isFactory(
apexMgr.getPackageInfo(apexContainingPkg, MATCH_ACTIVE_PACKAGE)))) {
// it's only a reportable violation if the permission isn't explicitly denied
ArraySet<String> deniedPermissions = null;
if (pkg.isVendor()) {