Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6af7e357 authored by Chun-Wei Wang's avatar Chun-Wei Wang Committed by Android (Google) Code Review
Browse files

Merge "Move signature checking out of ApexManager"

parents d486f80a 6b6d653c
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -499,9 +499,9 @@ public abstract class PackageManagerInternal {

    /**
     * Prunes the cache of the APKs in the given APEXes.
     * @param apexPackages The list of APEX packages that may contain APK-in-APEX.
     * @param apexPackageNames The list of APEX package names that may contain APK-in-APEX.
     */
    public abstract void pruneCachedApksInApex(@NonNull List<PackageInfo> apexPackages);
    public abstract void pruneCachedApksInApex(@NonNull List<String> apexPackageNames);

    /**
     * @return The SetupWizard package name.
+3 −32
Original line number Diff line number Diff line
@@ -49,11 +49,8 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.modules.utils.build.UnboundedSdkLevel;
import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.pkg.component.ParsedApexSystemService;
import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils;
import com.android.server.utils.TimingsTraceAndSlog;

import com.google.android.collect.Lists;
@@ -364,8 +361,7 @@ public abstract class ApexManager {
     *
     * @return {@code ApeInfo} about the newly installed APEX package.
     */
    abstract ApexInfo installPackage(File apexFile, PackageParser2 packageParser,
            ApexPackageInfo apexPackageInfo) throws PackageManagerException;
    abstract ApexInfo installPackage(File apexFile) throws PackageManagerException;

    /**
     * Get a list of apex system services implemented in an apex.
@@ -910,37 +906,13 @@ public abstract class ApexManager {
        }

        @Override
        ApexInfo installPackage(File apexFile, PackageParser2 packageParser,
                ApexPackageInfo apexPackageInfo)
        ApexInfo installPackage(File apexFile)
                throws PackageManagerException {
            try {
                final int flags = PackageManager.GET_META_DATA
                        | PackageManager.GET_SIGNING_CERTIFICATES
                        | PackageManager.GET_SIGNATURES;
                final ParsedPackage parsedPackage = packageParser.parsePackage(
                        apexFile, flags, /* useCaches= */ false);
                final PackageInfo newApexPkg = PackageInfoWithoutStateUtils.generate(parsedPackage,
                        /* apexInfo= */ null, flags);
                if (newApexPkg == null) {
                    throw new PackageManagerException(PackageManager.INSTALL_FAILED_INVALID_APK,
                            "Failed to generate package info for " + apexFile.getAbsolutePath());
                }
                final PackageInfo existingApexPkg = apexPackageInfo.getPackageInfo(
                        newApexPkg.packageName, MATCH_ACTIVE_PACKAGE);
                if (existingApexPkg == null) {
                    Slog.w(TAG, "Attempting to install new APEX package " + newApexPkg.packageName);
                    throw new PackageManagerException(PackageManager.INSTALL_FAILED_PACKAGE_CHANGED,
                            "It is forbidden to install new APEX packages");
                }
                checkApexSignature(existingApexPkg, newApexPkg);
                return waitForApexService().installAndActivatePackage(apexFile.getAbsolutePath());
            } catch (RemoteException e) {
                throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
                        "apexservice not available");
            } catch (PackageManagerException e) {
                // Catching it in order not to fall back to Exception which rethrows the
                // PackageManagerException with a common error code.
                throw e;
            } catch (Exception e) {
                // TODO(b/187864524): is INSTALL_FAILED_INTERNAL_ERROR is the right error code here?
                throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
@@ -1194,8 +1166,7 @@ public abstract class ApexManager {
        }

        @Override
        ApexInfo installPackage(File apexFile, PackageParser2 packageParser,
                ApexPackageInfo apexPackageInfo) {
        ApexInfo installPackage(File apexFile) {
            throw new UnsupportedOperationException("APEX updates are not supported");
        }

+1 −2
Original line number Diff line number Diff line
@@ -872,8 +872,7 @@ final class InstallPackageHelper {
                                + " got: " + apexes.length);
            }
            try (PackageParser2 packageParser = mPm.mInjector.getScanningPackageParser()) {
                ApexInfo apexInfo = mApexManager.installPackage(
                        apexes[0], packageParser, mPm.mApexPackageInfo);
                ApexInfo apexInfo = mApexManager.installPackage(apexes[0]);
                if (ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) {
                    ParsedPackage parsedPackage = packageParser.parsePackage(
                            new File(apexInfo.modulePath), 0, /* useCaches= */ false);
+3 −3
Original line number Diff line number Diff line
@@ -6174,7 +6174,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
        }

        @Override
        public void pruneCachedApksInApex(@NonNull List<PackageInfo> apexPackages) {
        public void pruneCachedApksInApex(@NonNull List<String> apexPackageNames) {
            if (mCacheDir == null) {
                return;
            }
@@ -6182,9 +6182,9 @@ public class PackageManagerService implements PackageSender, TestUtilityService
            final PackageCacher cacher = new PackageCacher(mCacheDir);
            synchronized (mLock) {
                final Computer snapshot = snapshot();
                for (int i = 0, size = apexPackages.size(); i < size; i++) {
                for (int i = 0, size = apexPackageNames.size(); i < size; i++) {
                    final List<String> apkNames =
                            mApexManager.getApksInApex(apexPackages.get(i).packageName);
                            mApexManager.getApksInApex(apexPackageNames.get(i));
                    for (int j = 0, apksInApex = apkNames.size(); j < apksInApex; j++) {
                        final AndroidPackage pkg = snapshot.getPackage(apkNames.get(j));
                        cacher.cleanCachedResult(new File(pkg.getPath()));
+50 −76
Original line number Diff line number Diff line
@@ -46,7 +46,6 @@ import com.android.server.LocalServices;
import com.android.server.SystemConfig;
import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils;
import com.android.server.rollback.RollbackManagerInternal;

import java.io.File;
@@ -101,10 +100,12 @@ final class PackageSessionVerifier {
                    for (PackageInstallerSession child : session.getChildSessions()) {
                        checkApexUpdateAllowed(child);
                        checkRebootlessApex(child);
                        checkApexSignature(child);
                    }
                } else {
                    checkApexUpdateAllowed(session);
                    checkRebootlessApex(session);
                    checkApexSignature(session);
                }
                verifyAPK(session, callback);
            } catch (PackageManagerException e) {
@@ -115,6 +116,47 @@ final class PackageSessionVerifier {
        });
    }

    private SigningDetails getSigningDetails(PackageInfo apexPkg) throws PackageManagerException {
        final String apexPath = apexPkg.applicationInfo.sourceDir;
        final int minSignatureScheme =
                ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
                        apexPkg.applicationInfo.targetSdkVersion);
        final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
        final ParseResult<SigningDetails> result = ApkSignatureVerifier.verify(
                input, apexPath, minSignatureScheme);
        if (result.isError()) {
            throw new PackageManagerException(PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
                    "Failed to verify APEX package " + apexPath + " : "
                            + result.getException(), result.getException());
        }
        return result.getResult();
    }

    private void checkApexSignature(PackageInstallerSession session)
            throws PackageManagerException {
        if (!session.isApexSession()) {
            return;
        }
        final String packageName = session.getPackageName();
        final PackageInfo existingApexPkg = mPm.snapshotComputer().getPackageInfo(
                session.getPackageName(), PackageManager.MATCH_APEX, UserHandle.USER_SYSTEM);
        if (existingApexPkg == null) {
            throw new PackageManagerException(PackageManager.INSTALL_FAILED_PACKAGE_CHANGED,
                    "Attempting to install new APEX package " + packageName);
        }
        final SigningDetails existingSigningDetails = getSigningDetails(existingApexPkg);
        final SigningDetails newSigningDetails = session.getSigningDetails();
        if (newSigningDetails.checkCapability(existingSigningDetails,
                SigningDetails.CertCapabilities.INSTALLED_DATA)
                || existingSigningDetails.checkCapability(newSigningDetails,
                SigningDetails.CertCapabilities.ROLLBACK)) {
            return;
        }
        throw new PackageManagerException(PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
                "APK container signature of APEX package " + packageName
                        + " is not compatible with the one currently installed on device");
    }

    /**
     * Runs verifications particular to APK. This includes APEX sessions since an APEX can also
     * be treated as APK.
@@ -283,13 +325,10 @@ final class PackageSessionVerifier {
        // APEX checks. For single-package sessions, check if they contain an APEX. For
        // multi-package sessions, find all the child sessions that contain an APEX.
        if (hasApex) {
            final List<PackageInfo> apexPackages = submitSessionToApexService(session, rollbackId);
            for (int i = 0, size = apexPackages.size(); i < size; i++) {
                validateApexSignature(apexPackages.get(i));
            }
            final List<String> apexPackageNames = submitSessionToApexService(session, rollbackId);
            final PackageManagerInternal packageManagerInternal =
                    LocalServices.getService(PackageManagerInternal.class);
            packageManagerInternal.pruneCachedApksInApex(apexPackages);
            packageManagerInternal.pruneCachedApksInApex(apexPackageNames);
        }
    }

@@ -333,62 +372,7 @@ final class PackageSessionVerifier {
        }
    }

    /**
     * Validates the signature used to sign the container of the new apex package
     *
     * @param newApexPkg The new apex package that is being installed
     */
    private void validateApexSignature(PackageInfo newApexPkg) throws PackageManagerException {
        // Get signing details of the new package
        final String apexPath = newApexPkg.applicationInfo.sourceDir;
        final String packageName = newApexPkg.packageName;
        int minSignatureScheme = ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
                newApexPkg.applicationInfo.targetSdkVersion);

        final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
        final ParseResult<SigningDetails> newResult = ApkSignatureVerifier.verify(
                input.reset(), apexPath, minSignatureScheme);
        if (newResult.isError()) {
            throw new PackageManagerException(PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
                    "Failed to parse APEX package " + apexPath + " : "
                            + newResult.getException(), newResult.getException());
        }
        final SigningDetails newSigningDetails = newResult.getResult();

        // Get signing details of the existing package
        final PackageInfo existingApexPkg = mPm.snapshotComputer().getPackageInfo(
                packageName, PackageManager.MATCH_APEX, UserHandle.USER_SYSTEM);
        if (existingApexPkg == null) {
            // This should never happen, because submitSessionToApexService ensures that no new
            // apexes were installed.
            throw new IllegalStateException("Unknown apex package " + packageName);
        }

        final ParseResult<SigningDetails> existingResult = ApkSignatureVerifier.verify(
                input.reset(), existingApexPkg.applicationInfo.sourceDir,
                SigningDetails.SignatureSchemeVersion.JAR);
        if (existingResult.isError()) {
            throw new PackageManagerException(PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
                    "Failed to parse APEX package " + existingApexPkg.applicationInfo.sourceDir
                            + " : " + existingResult.getException(), existingResult.getException());
        }
        final SigningDetails existingSigningDetails = existingResult.getResult();

        // Verify signing details for upgrade
        if (newSigningDetails.checkCapability(existingSigningDetails,
                SigningDetails.CertCapabilities.INSTALLED_DATA)
                || existingSigningDetails.checkCapability(newSigningDetails,
                SigningDetails.CertCapabilities.ROLLBACK)) {
            return;
        }

        throw new PackageManagerException(PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
                "APK-container signature of APEX package " + packageName + " with version "
                        + newApexPkg.versionCodeMajor + " and path " + apexPath + " is not"
                        + " compatible with the one currently installed on device");
    }

    private List<PackageInfo> submitSessionToApexService(StagingManager.StagedSession session,
    private List<String> submitSessionToApexService(StagingManager.StagedSession session,
            int rollbackId) throws PackageManagerException {
        final IntArray childSessionIds = new IntArray();
        if (session.isMultiPackage()) {
@@ -413,32 +397,22 @@ final class PackageSessionVerifier {
        // submitStagedSession will throw a PackageManagerException if apexd verification fails,
        // which will be propagated to populate stagedSessionErrorMessage of this session.
        final ApexInfoList apexInfoList = mApexManager.submitStagedSession(apexSessionParams);
        final List<PackageInfo> result = new ArrayList<>();
        final List<String> apexPackageNames = new ArrayList<>();
        for (ApexInfo apexInfo : apexInfoList.apexInfos) {
            final PackageInfo packageInfo;
            final int flags = PackageManager.GET_META_DATA;
            final ParsedPackage parsedPackage;
            try (PackageParser2 packageParser = mPackageParserSupplier.get()) {
                File apexFile = new File(apexInfo.modulePath);
                final ParsedPackage parsedPackage = packageParser.parsePackage(
                        apexFile, flags, false);
                packageInfo = PackageInfoWithoutStateUtils.generate(parsedPackage, apexInfo, flags);
                if (packageInfo == null) {
                    throw new PackageManagerException(
                            PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
                            "Unable to generate package info: " + apexInfo.modulePath);
                }
                parsedPackage = packageParser.parsePackage(apexFile, 0, false);
            } catch (PackageManagerException e) {
                throw new PackageManagerException(
                        PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
                        "Failed to parse APEX package " + apexInfo.modulePath + " : " + e, e);
            }
            result.add(packageInfo);
            apexPackageNames.add(packageInfo.packageName);
            apexPackageNames.add(parsedPackage.getPackageName());
        }
        Slog.d(TAG, "Session " + session.sessionId() + " has following APEX packages: "
                + apexPackageNames);
        return result;
        return apexPackageNames;
    }

    private int retrieveRollbackIdForCommitSession(int sessionId) throws PackageManagerException {
Loading