Merge "bypass anti-malware verification" into rvc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
a93686c403
@@ -3157,6 +3157,23 @@ public abstract class PackageManager {
|
||||
public static final String EXTRA_VERIFICATION_LONG_VERSION_CODE =
|
||||
"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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user