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

Commit 278310e3 authored by Victor Hsieh's avatar Victor Hsieh Committed by Android (Google) Code Review
Browse files

Merge "Ensure signature for allowlisted system app update on boot" into main

parents 925853a9 a8f6d75c
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -60,6 +60,12 @@ prebuilt_etc {
    src: "preinstalled-packages-asl-files.xml",
}

prebuilt_etc {
    name: "preinstalled-packages-strict-signature.xml",
    sub_dir: "sysconfig",
    src: "preinstalled-packages-strict-signature.xml",
}

// Privapp permission whitelist files

prebuilt_etc {
+27 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
  ~ Copyright (C) 2023 The Android Open Source Project
  ~
  ~ Licensed under the Apache License, Version 2.0 (the "License");
  ~ you may not use this file except in compliance with the License.
  ~ You may obtain a copy of the License at
  ~
  ~      http://www.apache.org/licenses/LICENSE-2.0
  ~
  ~ Unless required by applicable law or agreed to in writing, software
  ~ distributed under the License is distributed on an "AS IS" BASIS,
  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  ~ See the License for the specific language governing permissions and
  ~ limitations under the License.
  -->

<!--
This XML file declares which preinstalled apps, after updated, need to have strict signature check
in boot time and avoid the cached results. This is to ensure the updated version still verifies
against the preinstalled version.

Example usage:
    <require-strict-signature package="com.foo.bar"/>
-->

<config></config>
+19 −0
Original line number Diff line number Diff line
@@ -340,6 +340,10 @@ public class SystemConfig {
    // A map of preloaded package names and the path to its app metadata file path.
    private final ArrayMap<String, String> mAppMetadataFilePaths = new ArrayMap<>();

    // A set of pre-installed package names that requires strict signature verification once
    // updated to avoid cached/potentially tampered results.
    private final Set<String> mPreinstallPackagesWithStrictSignatureCheck = new ArraySet<>();

    /**
     * Map of system pre-defined, uniquely named actors; keys are namespace,
     * value maps actor name to package name.
@@ -542,6 +546,10 @@ public class SystemConfig {
        return mAppMetadataFilePaths;
    }

    public Set<String> getPreinstallPackagesWithStrictSignatureCheck() {
        return mPreinstallPackagesWithStrictSignatureCheck;
    }

    /**
     * Only use for testing. Do NOT use in production code.
     * @param readPermissions false to create an empty SystemConfig; true to read the permissions.
@@ -1485,6 +1493,17 @@ public class SystemConfig {
                            mAppMetadataFilePaths.put(packageName, path);
                        }
                    } break;
                    case "require-strict-signature": {
                        if (android.security.Flags.extendVbChainToUpdatedApk()) {
                            String packageName = parser.getAttributeValue(null, "package");
                            if (TextUtils.isEmpty(packageName)) {
                                Slog.w(TAG, "<" + name + "> without valid package in " + permFile
                                        + " at " + parser.getPositionDescription());
                            } else {
                                mPreinstallPackagesWithStrictSignatureCheck.add(packageName);
                            }
                        }
                    } break;
                    default: {
                        Slog.w(TAG, "Tag " + name + " is unknown in "
                                + permFile + " at " + parser.getPositionDescription());
+31 −24
Original line number Diff line number Diff line
@@ -3728,7 +3728,7 @@ final class InstallPackageHelper {
        final ScanResult scanResult = scanResultPair.first;
        boolean shouldHideSystemApp = scanResultPair.second;
        final InstallRequest installRequest = new InstallRequest(
                parsedPackage, parseFlags, scanFlags, user, scanResult);
                parsedPackage, parseFlags, scanFlags, user, scanResult, disabledPkgSetting);

        String existingApexModuleName = null;
        synchronized (mPm.mLock) {
@@ -3962,6 +3962,7 @@ final class InstallPackageHelper {
        final String disabledPkgName = pkgAlreadyExists
                ? pkgSetting.getPackageName() : parsedPackage.getPackageName();
        final boolean isSystemPkgUpdated;
        final PackageSetting disabledPkgSetting;
        final boolean isUpgrade;
        synchronized (mPm.mLock) {
            isUpgrade = mPm.isDeviceUpgrading();
@@ -3975,8 +3976,7 @@ final class InstallPackageHelper {
                        + "and install it as non-updated system app.");
                mPm.mSettings.removeDisabledSystemPackageLPw(disabledPkgName);
            }
            final PackageSetting disabledPkgSetting =
                    mPm.mSettings.getDisabledSystemPkgLPr(disabledPkgName);
            disabledPkgSetting = mPm.mSettings.getDisabledSystemPkgLPr(disabledPkgName);
            isSystemPkgUpdated = disabledPkgSetting != null;

            if (DEBUG_INSTALL && isSystemPkgUpdated) {
@@ -4048,6 +4048,23 @@ final class InstallPackageHelper {
        // equal to the version on the /data partition. Throw an exception and use
        // the application already installed on the /data partition.
        if (scanSystemPartition && isSystemPkgUpdated && !isSystemPkgBetter) {
            // For some updated system packages, during addForInit we want to ensure the
            // PackageSetting has the correct SigningDetails compares to the original version on
            // the system partition. For the check to happen later during the /data scan, update
            // the disabled package setting per the original APK on a system partition so that it
            // can be trusted during reconcile.
            if (needSignatureMatchToSystem(parsedPackage.getPackageName())) {
                final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
                final ParseResult<SigningDetails> result =
                        ParsingPackageUtils.getSigningDetails(input, parsedPackage,
                                false /*skipVerify*/);
                if (result.isError()) {
                    throw new PrepareFailure("Failed collect during scanSystemPackageLI",
                            result.getException());
                }
                disabledPkgSetting.setSigningDetails(result.getResult());
            }

            // In the case of a skipped package, commitReconciledScanResultLocked is not called to
            // add the object to the "live" data structures, so this is the final mutation step
            // for the package. Which means it needs to be finalized here to cache derived fields.
@@ -4065,19 +4082,16 @@ final class InstallPackageHelper {
        // Verify certificates against what was last scanned. Force re-collecting certificate in two
        // special cases:
        // 1) when scanning system, force re-collect only if system is upgrading.
        // 2) when scanning /data, force re-collect only if the app is privileged (updated from
        // preinstall, or treated as privileged, e.g. due to shared user ID).
        // 2) when scanning /data, force re-collect only if the package name is allowlisted.
        final boolean forceCollect = scanSystemPartition ? isUpgrade
                : PackageManagerServiceUtils.isApkVerificationForced(pkgSetting);
                : pkgAlreadyExists && needSignatureMatchToSystem(pkgSetting.getPackageName());
        if (DEBUG_VERIFY && forceCollect) {
            Slog.d(TAG, "Force collect certificate of " + parsedPackage.getPackageName());
        }

        // Full APK verification can be skipped during certificate collection, only if the file is
        // in verified partition, or can be verified on access (when apk verity is enabled). In both
        // cases, only data in Signing Block is verified instead of the whole file.
        final boolean skipVerify = scanSystemPartition
                || (forceCollect && canSkipForcedPackageVerification(parsedPackage));
        // APK verification can be skipped during certificate collection, only if the file is in a
        // verified partition.
        final boolean skipVerify = scanSystemPartition;
        ScanPackageUtils.collectCertificatesLI(pkgSetting, parsedPackage,
                mPm.getSettingsVersionForPackage(parsedPackage), forceCollect, skipVerify,
                mPm.isPreNMR1Upgrade());
@@ -4196,22 +4210,15 @@ final class InstallPackageHelper {
    }

    /**
     * Returns if forced apk verification can be skipped for the whole package, including splits.
     * Returns whether the package needs a signature verification against the pre-installed version
     * at boot.
     */
    private boolean canSkipForcedPackageVerification(AndroidPackage pkg) {
        if (!VerityUtils.hasFsverity(pkg.getBaseApkPath())) {
            return false;
        }
        // TODO: Allow base and splits to be verified individually.
        String[] splitCodePaths = pkg.getSplitCodePaths();
        if (!ArrayUtils.isEmpty(splitCodePaths)) {
            for (int i = 0; i < splitCodePaths.length; i++) {
                if (!VerityUtils.hasFsverity(splitCodePaths[i])) {
    private boolean needSignatureMatchToSystem(String packageName) {
        if (!android.security.Flags.extendVbChainToUpdatedApk()) {
            return false;
        }
            }
        }
        return true;
        return mPm.mInjector.getSystemConfig().getPreinstallPackagesWithStrictSignatureCheck()
            .contains(packageName);
    }

    /**
+2 −2
Original line number Diff line number Diff line
@@ -58,7 +58,6 @@ import com.android.server.pm.pkg.parsing.ParsingPackageUtils;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;

@@ -190,7 +189,7 @@ final class InstallRequest {

    // addForInit
    InstallRequest(ParsedPackage parsedPackage, int parseFlags, int scanFlags,
            @Nullable UserHandle user, ScanResult scanResult) {
            @Nullable UserHandle user, ScanResult scanResult, PackageSetting disabledPs) {
        if (user != null) {
            mUserId = user.getIdentifier();
        } else {
@@ -206,6 +205,7 @@ final class InstallRequest {
        mPackageMetrics = null; // No logging from this code path
        mSessionId = -1;
        mRequireUserAction = USER_ACTION_UNSPECIFIED;
        mDisabledPs = disabledPs;
    }

    @Nullable
Loading