DivestOS/Patches/LineageOS-20.0/android_frameworks_base/0029-Strict_Package_Checks-...

199 lines
8.8 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Dmitry Muhomor <muhomor.dmitry@gmail.com>
Date: Tue, 31 Jan 2023 17:55:11 +0200
Subject: [PATCH] perform additional boot-time checks on system package updates
[tad@spotco.us]: disable verity checks
---
.../server/pm/InstallPackageHelper.java | 7 +
.../android/server/pm/PackageVerityExt.java | 162 ++++++++++++++++++
2 files changed, 169 insertions(+)
create mode 100644 services/core/java/com/android/server/pm/PackageVerityExt.java
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 0ec70238ff64..adf3e172d192 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -3827,6 +3827,13 @@ final class InstallPackageHelper {
@Nullable UserHandle user) throws PackageManagerException {
final boolean scanSystemPartition =
(parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0;
+ if ((scanFlags & SCAN_BOOTING) != 0) {
+ if (scanSystemPartition) {
+ PackageVerityExt.addSystemPackage(parsedPackage);
+ } else {
+ PackageVerityExt.checkSystemPackageUpdate(parsedPackage);
+ }
+ }
final ScanRequest initialScanRequest = prepareInitialScanRequest(parsedPackage, parseFlags,
scanFlags, user, null);
final PackageSetting installedPkgSetting = initialScanRequest.mPkgSetting;
diff --git a/services/core/java/com/android/server/pm/PackageVerityExt.java b/services/core/java/com/android/server/pm/PackageVerityExt.java
new file mode 100644
index 000000000000..cd0e213b3f4d
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PackageVerityExt.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2022 GrapheneOS
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package com.android.server.pm;
+
+import android.annotation.Nullable;
+import android.content.pm.SigningDetails;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
+import android.os.Build;
+import android.os.SystemProperties;
+import android.util.ArrayMap;
+import android.util.Slog;
+
+import com.android.internal.security.VerityUtils;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
+
+import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_SIGNATURE;
+import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
+import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
+
+// Performs additional checks on system package updates
+public class PackageVerityExt {
+ private static final String TAG = PackageVerityExt.class.getSimpleName();
+
+ // Parsed packages from immutable partitions. Static shared libraries are handled separately
+ // due to a different policy that OS uses for their replacement
+ private static final ArrayMap<String, AndroidPackage> packages = new ArrayMap<>();
+ private static final ArrayMap<String, AndroidPackage> staticSharedLibraries = new ArrayMap<>();
+
+ // Called when PackageManager scans a package from immutable system image partition during OS boot.
+ // All packages from immutable partitions are scanned before any packages from mutable partitions.
+ public static void addSystemPackage(AndroidPackage pkg) {
+ if (pkg.isStaticSharedLibrary()) {
+ String name = pkg.getStaticSharedLibName();
+ AndroidPackage prev;
+ synchronized (staticSharedLibraries) {
+ prev = staticSharedLibraries.put(name, pkg);
+ }
+ if (prev != null) {
+ Slog.w(TAG, "duplicate static shared lib " + name
+ + ": prev " + prev.getPath() + " -> new " + pkg.getPath());
+ }
+ } else {
+ String name = pkg.getManifestPackageName();
+ AndroidPackage prev;
+ synchronized (packages) {
+ prev = packages.put(name, pkg);
+ }
+ if (prev != null) {
+ Slog.w(TAG, "duplicate system package " + name + ": prev " + prev.getPath() +
+ " -> new " + pkg.getPath());
+ }
+ }
+ }
+
+ // If pkg is a system package update, returns its matching system image package
+ @Nullable public static AndroidPackage getSystemPackage(AndroidPackage pkg) {
+ if (pkg.isStaticSharedLibrary()) {
+ String name = pkg.getStaticSharedLibName();
+ synchronized (staticSharedLibraries) {
+ return staticSharedLibraries.get(name);
+ }
+ } else {
+ String name = pkg.getManifestPackageName();
+ synchronized (packages) {
+ return packages.get(name);
+ }
+ }
+ }
+
+ // Called when PackageManager scans a package from mutable partition (ie /data) during OS boot.
+ // PackageManagerException thrown from here will prevent this package from replacing its system
+ // image version.
+ public static void checkSystemPackageUpdate(AndroidPackage maybeSystemPackageUpdate) throws PackageManagerException {
+ final AndroidPackage systemPkg = getSystemPackage(maybeSystemPackageUpdate);
+
+ if (systemPkg == null) {
+ // not a system package update
+ return;
+ }
+
+ final AndroidPackage systemPkgUpdate = maybeSystemPackageUpdate;
+
+ Slog.d(TAG, "Performing verification of system package update "
+ + systemPkgUpdate.getManifestPackageName());
+
+ if (systemPkg.getLongVersionCode() >= systemPkgUpdate.getLongVersionCode()) {
+ throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
+ "versionCode of system image package (" + systemPkg.getLongVersionCode()
+ + ") is >= versionCode of system package update ("
+ + systemPkgUpdate.getLongVersionCode() + ")");
+ }
+
+ boolean checkFsVerity = false;
+ if (Build.IS_DEBUGGABLE) {
+ if (SystemProperties.getBoolean("persist.disable_boot_time_fsverity_check", false)) {
+ checkFsVerity = false;
+ }
+ }
+
+ if (checkFsVerity) {
+ checkFsVerity(systemPkgUpdate);
+ }
+
+ final SigningDetails updatePkgSigningDetails = parseSigningDetails(systemPkgUpdate,
+ // verify APK against its signature
+ false);
+
+ final SigningDetails systemPkgSigningDetails = parseSigningDetails(systemPkg,
+ // skip signature verification, system image APKs are protected by verified boot
+ true);
+
+ final boolean valid = updatePkgSigningDetails.checkCapability(systemPkgSigningDetails,
+ SigningDetails.CertCapabilities.INSTALLED_DATA)
+ || systemPkgSigningDetails.checkCapability(updatePkgSigningDetails,
+ SigningDetails.CertCapabilities.ROLLBACK);
+
+ if (!valid) {
+ String msg = "System package update " + systemPkgUpdate.getManifestPackageName()
+ + " signature doesn't match the signature of system image package";
+ throw new PackageManagerException(INSTALL_FAILED_BAD_SIGNATURE, msg);
+ }
+ }
+
+ public static void checkFsVerity(AndroidPackage pkg) throws PackageManagerException {
+ final String baseApkPath = pkg.getBaseApkPath();
+ if (!VerityUtils.hasFsverity(baseApkPath)) {
+ throw new PackageManagerException(INSTALL_FAILED_BAD_SIGNATURE,
+ "Base APK doesn't have fs-verity: " + baseApkPath);
+ }
+
+ for (String path : pkg.getSplitCodePaths()) {
+ if (!VerityUtils.hasFsverity(path)) {
+ throw new PackageManagerException(INSTALL_FAILED_BAD_SIGNATURE,
+ "APK split doesn't have fs-verity: " + path);
+ }
+ }
+ }
+
+ private static SigningDetails parseSigningDetails(AndroidPackage pkg, boolean skipVerify) throws PackageManagerException {
+ final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+ final ParseResult<SigningDetails> result = ParsingPackageUtils.getSigningDetails(
+ input, pkg, skipVerify);
+
+ if (result.isError()) {
+ throw new PackageManagerException(
+ result.getErrorCode(), result.getErrorMessage(), result.getException());
+ }
+
+ final SigningDetails sd = result.getResult();
+ if (sd == null) {
+ throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+ "Null signing details of package " + pkg.getManifestPackageName());
+ }
+
+ return sd;
+ }
+}