summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/content/pm/PackageManager.java17
-rw-r--r--core/java/android/os/incremental/V4Signature.java28
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java54
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceUtils.java75
4 files changed, 155 insertions, 19 deletions
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 9ca2db970eb7..d36d583559a0 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3158,6 +3158,23 @@ public abstract class PackageManager {
"android.content.pm.extra.VERIFICATION_LONG_VERSION_CODE";
/**
+ * Extra field name for the Merkle tree root hash of a package.
+ * <p>Passed to a package verifier both prior to verification and as a result
+ * of verification.
+ * <p>The value of the extra is a specially formatted list:
+ * {@code filename1:HASH_1;filename2:HASH_2;...;filenameN:HASH_N}
+ * <p>The extra must include an entry for every APK within an installation. If
+ * a hash is not physically present, a hash value of {@code 0} will be used.
+ * <p>The root hash is generated using SHA-256, no salt with a 4096 byte block
+ * size. See the description of the
+ * <a href="https://www.kernel.org/doc/html/latest/filesystems/fsverity.html#merkle-tree">fs-verity merkle-tree</a>
+ * for more details.
+ * @hide
+ */
+ public static final String EXTRA_VERIFICATION_ROOT_HASH =
+ "android.content.pm.extra.EXTRA_VERIFICATION_ROOT_HASH";
+
+ /**
* Extra field name for the ID of a intent filter pending verification.
* Passed to an intent filter verifier and is used to call back to
* {@link #verifyIntentFilter}
diff --git a/core/java/android/os/incremental/V4Signature.java b/core/java/android/os/incremental/V4Signature.java
index 5cc73caa4f60..d35ce5b2c3f8 100644
--- a/core/java/android/os/incremental/V4Signature.java
+++ b/core/java/android/os/incremental/V4Signature.java
@@ -16,6 +16,8 @@
package android.os.incremental;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.ParcelFileDescriptor;
import java.io.ByteArrayInputStream;
@@ -45,8 +47,8 @@ public class V4Signature {
public static class HashingInfo {
public final int hashAlgorithm; // only 1 == SHA256 supported
public final byte log2BlockSize; // only 12 (block size 4096) supported now
- public final byte[] salt; // used exactly as in fs-verity, 32 bytes max
- public final byte[] rawRootHash; // salted digest of the first Merkle tree page
+ @Nullable public final byte[] salt; // used exactly as in fs-verity, 32 bytes max
+ @Nullable public final byte[] rawRootHash; // salted digest of the first Merkle tree page
HashingInfo(int hashAlgorithm, byte log2BlockSize, byte[] salt, byte[] rawRootHash) {
this.hashAlgorithm = hashAlgorithm;
@@ -58,7 +60,8 @@ public class V4Signature {
/**
* Constructs HashingInfo from byte array.
*/
- public static HashingInfo fromByteArray(byte[] bytes) throws IOException {
+ @NonNull
+ public static HashingInfo fromByteArray(@NonNull byte[] bytes) throws IOException {
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
final int hashAlgorithm = buffer.getInt();
final byte log2BlockSize = buffer.get();
@@ -106,8 +109,18 @@ public class V4Signature {
}
public final int version; // Always 2 for now.
- public final byte[] hashingInfo;
- public final byte[] signingInfo; // Passed as-is to the kernel. Can be retrieved later.
+ /**
+ * Raw byte array containing the IncFS hashing data.
+ * @see HashingInfo#fromByteArray(byte[])
+ */
+ @Nullable public final byte[] hashingInfo;
+
+ /**
+ * Raw byte array containing the V4 signature data.
+ * <p>Passed as-is to the kernel. Can be retrieved later.
+ * @see SigningInfo#fromByteArray(byte[])
+ */
+ @Nullable public final byte[] signingInfo;
/**
* Construct a V4Signature from .idsig file.
@@ -121,7 +134,8 @@ public class V4Signature {
/**
* Construct a V4Signature from a byte array.
*/
- public static V4Signature readFrom(byte[] bytes) throws IOException {
+ @NonNull
+ public static V4Signature readFrom(@NonNull byte[] bytes) throws IOException {
try (InputStream stream = new ByteArrayInputStream(bytes)) {
return readFrom(stream);
}
@@ -169,7 +183,7 @@ public class V4Signature {
return this.version == SUPPORTED_VERSION;
}
- private V4Signature(int version, byte[] hashingInfo, byte[] signingInfo) {
+ private V4Signature(int version, @Nullable byte[] hashingInfo, @Nullable byte[] signingInfo) {
this.version = version;
this.hashingInfo = hashingInfo;
this.signingInfo = signingInfo;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 59ac603875e2..12ee2f55e87f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -94,6 +94,7 @@ import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.content.pm.PackageManager.RESTRICTION_NONE;
import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN;
+import static android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4;
import static android.content.pm.PackageParser.isApkFile;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.os.incremental.IncrementalManager.isIncrementalPath;
@@ -1820,10 +1821,12 @@ public class PackageManagerService extends IPackageManager.Stub
state.setVerifierResponse(Binder.getCallingUid(),
PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT);
broadcastPackageVerified(verificationId, originUri,
- PackageManager.VERIFICATION_ALLOW, user);
+ PackageManager.VERIFICATION_ALLOW, null, args.mDataLoaderType,
+ user);
} else {
broadcastPackageVerified(verificationId, originUri,
- PackageManager.VERIFICATION_REJECT, user);
+ PackageManager.VERIFICATION_REJECT, null, args.mDataLoaderType,
+ user);
params.setReturnCode(
PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE);
state.setVerifierResponse(Binder.getCallingUid(),
@@ -1899,7 +1902,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (state.isInstallAllowed()) {
broadcastPackageVerified(verificationId, originUri,
- response.code, args.getUser());
+ response.code, null, args.mDataLoaderType, args.getUser());
} else {
params.setReturnCode(
PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE);
@@ -13575,12 +13578,17 @@ public class PackageManagerService extends IPackageManager.Stub
}
private void broadcastPackageVerified(int verificationId, Uri packageUri,
- int verificationCode, UserHandle user) {
+ int verificationCode, @Nullable String rootHashString, int dataLoaderType,
+ UserHandle user) {
final Intent intent = new Intent(Intent.ACTION_PACKAGE_VERIFIED);
intent.setDataAndType(packageUri, PACKAGE_MIME_TYPE);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.putExtra(PackageManager.EXTRA_VERIFICATION_ID, verificationId);
intent.putExtra(PackageManager.EXTRA_VERIFICATION_RESULT, verificationCode);
+ if (rootHashString != null) {
+ intent.putExtra(PackageManager.EXTRA_VERIFICATION_ROOT_HASH, rootHashString);
+ }
+ intent.putExtra(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType);
mContext.sendBroadcastAsUser(intent, user,
android.Manifest.permission.PACKAGE_VERIFICATION_AGENT);
@@ -14952,8 +14960,17 @@ public class PackageManagerService extends IPackageManager.Stub
verificationState.setRequiredVerifierUid(requiredUid);
final int installerUid =
verificationInfo == null ? -1 : verificationInfo.installerUid;
- if (!origin.existing && isVerificationEnabled(pkgLite, verifierUser.getIdentifier(),
- installFlags, installerUid)) {
+ final boolean isVerificationEnabled = isVerificationEnabled(
+ pkgLite, verifierUser.getIdentifier(), installFlags, installerUid);
+ final boolean isV4Signed =
+ (mArgs.signingDetails.signatureSchemeVersion == SIGNING_BLOCK_V4);
+ final boolean isIncrementalInstall =
+ (mArgs.mDataLoaderType == DataLoaderType.INCREMENTAL);
+ // NOTE: We purposefully skip verification for only incremental installs when there's
+ // a v4 signature block. Otherwise, proceed with verification as usual.
+ if (!origin.existing
+ && isVerificationEnabled
+ && (!isIncrementalInstall || !isV4Signed)) {
final Intent verification = new Intent(
Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
verification.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
@@ -16569,7 +16586,29 @@ public class PackageManagerService extends IPackageManager.Stub
}
executePostCommitSteps(commitRequest);
} finally {
- if (!success) {
+ if (success) {
+ for (InstallRequest request : requests) {
+ final InstallArgs args = request.args;
+ if (args.mDataLoaderType != DataLoaderType.INCREMENTAL) {
+ continue;
+ }
+ if (args.signingDetails.signatureSchemeVersion != SIGNING_BLOCK_V4) {
+ continue;
+ }
+ // For incremental installs, we bypass the verifier prior to install. Now
+ // that we know the package is valid, send a notice to the verifier with
+ // the root hash of the base.apk.
+ final String baseCodePath = request.installResult.pkg.getBaseCodePath();
+ final String[] splitCodePaths = request.installResult.pkg.getSplitCodePaths();
+ final Uri originUri = Uri.fromFile(args.origin.resolvedFile);
+ final int verificationId = mPendingVerificationToken++;
+ final String rootHashString = PackageManagerServiceUtils
+ .buildVerificationRootHashString(baseCodePath, splitCodePaths);
+ broadcastPackageVerified(verificationId, originUri,
+ PackageManager.VERIFICATION_ALLOW, rootHashString,
+ args.mDataLoaderType, args.getUser());
+ }
+ } else {
for (ScanResult result : preparedScans.values()) {
if (createdAppId.getOrDefault(result.request.parsedPackage.getPackageName(),
false)) {
@@ -16911,7 +16950,6 @@ public class PackageManagerService extends IPackageManager.Stub
if (args.signingDetails != PackageParser.SigningDetails.UNKNOWN) {
parsedPackage.setSigningDetails(args.signingDetails);
} else {
- // TODO(b/136132412): skip for Incremental installation
parsedPackage.setSigningDetails(
ParsingPackageUtils.collectCertificates(parsedPackage, false /* skipVerify */));
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 91afd846a9c3..5c175a6ef847 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -32,7 +32,6 @@ import android.annotation.Nullable;
import android.app.AppGlobals;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
@@ -40,7 +39,6 @@ import android.content.pm.PackageParser;
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.ResolveInfo;
import android.content.pm.Signature;
-import android.content.pm.parsing.ParsingPackageUtils;
import android.os.Build;
import android.os.Debug;
import android.os.Environment;
@@ -50,6 +48,9 @@ import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManagerInternal;
+import android.os.incremental.IncrementalManager;
+import android.os.incremental.V4Signature;
+import android.os.incremental.V4Signature.HashingInfo;
import android.service.pm.PackageServiceDumpProto;
import android.system.ErrnoException;
import android.system.Os;
@@ -62,6 +63,7 @@ import com.android.internal.content.NativeLibraryHelper;
import com.android.internal.content.PackageHelper;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
+import com.android.internal.util.HexDump;
import com.android.server.EventLogTags;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.PackageDexUsage;
@@ -94,8 +96,6 @@ import java.util.Collections;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
-import java.util.Map;
-import java.util.Set;
import java.util.function.Predicate;
import java.util.zip.GZIPInputStream;
@@ -943,4 +943,71 @@ public class PackageManagerServiceUtils {
Os.chmod(currentDir.getAbsolutePath(), mode);
}
}
+
+ /**
+ * Returns a string that's compatible with the verification root hash extra.
+ * @see PackageManager#EXTRA_VERIFICATION_ROOT_HASH
+ */
+ @NonNull
+ public static String buildVerificationRootHashString(@NonNull String baseFilename,
+ @Nullable String[] splitFilenameArray) {
+ final StringBuilder sb = new StringBuilder();
+ final String baseFilePath =
+ baseFilename.substring(baseFilename.lastIndexOf(File.separator) + 1);
+ sb.append(baseFilePath).append(":");
+ final byte[] baseRootHash = getRootHash(baseFilename);
+ if (baseRootHash == null) {
+ sb.append("0");
+ } else {
+ sb.append(HexDump.toHexString(baseRootHash));
+ }
+ if (splitFilenameArray == null || splitFilenameArray.length == 0) {
+ return sb.toString();
+ }
+
+ for (int i = splitFilenameArray.length - 1; i >= 0; i--) {
+ final String splitFilename = splitFilenameArray[i];
+ final String splitFilePath =
+ splitFilename.substring(splitFilename.lastIndexOf(File.separator) + 1);
+ final byte[] splitRootHash = getRootHash(splitFilename);
+ sb.append(";").append(splitFilePath).append(":");
+ if (splitRootHash == null) {
+ sb.append("0");
+ } else {
+ sb.append(HexDump.toHexString(splitRootHash));
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Returns the root has for the given file.
+ * <p>Otherwise, returns {@code null} if the root hash could not be found or calculated.
+ * <p>NOTE: This currently only works on files stored on the incremental file system. The
+ * eventual goal is that this hash [among others] can be retrieved for any file.
+ */
+ @Nullable
+ private static byte[] getRootHash(String filename) {
+ try {
+ final byte[] baseFileSignature =
+ IncrementalManager.unsafeGetFileSignature(filename);
+ if (baseFileSignature == null) {
+ throw new IOException("File signature not present");
+ }
+ final V4Signature signature =
+ V4Signature.readFrom(baseFileSignature);
+ if (signature.hashingInfo == null) {
+ throw new IOException("Hashing info not present");
+ }
+ final HashingInfo hashInfo =
+ HashingInfo.fromByteArray(signature.hashingInfo);
+ if (ArrayUtils.isEmpty(hashInfo.rawRootHash)) {
+ throw new IOException("Root has not present");
+ }
+ return hashInfo.rawRootHash;
+ } catch (IOException ignore) {
+ Slog.e(TAG, "ERROR: could not load root hash from incremental install");
+ }
+ return null;
+ }
}