199 lines
8.8 KiB
Diff
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 259701166147..6044961e8639 100644
|
|
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
|
|
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
|
|
@@ -3824,6 +3824,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;
|
|
+ }
|
|
+}
|