Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 701cdb6c authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Automerger Merge Worker
Browse files

Merge "bypass anti-malware verification" into rvc-dev am: a93686c4 am:...

Merge "bypass anti-malware verification" into rvc-dev am: a93686c4 am: ba4ec979 am: e3666d6e am: be4b8542

Change-Id: I3a85f17ee3da0ed11b053d012a8cdd54629f6df0
parents 5018962a be4b8542
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -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
+21 −7
Original line number Diff line number Diff line
@@ -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;
+46 −8
Original line number Diff line number Diff line
@@ -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);
@@ -13589,12 +13592,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);
@@ -14966,8 +14974,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);
@@ -16584,7 +16601,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)) {
@@ -16926,7 +16965,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 */));
            }
+71 −4
Original line number Diff line number Diff line
@@ -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;
    }
}