Merge "bypass anti-malware verification" into rvc-dev

This commit is contained in:
TreeHugger Robot
2020-04-15 20:13:43 +00:00
committed by Android (Google) Code Review
4 changed files with 155 additions and 19 deletions

View File

@@ -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

View File

@@ -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;

View File

@@ -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 */));
}

View File

@@ -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;
}
}