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

Commit 3ff38ea8 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Remove isAppSourceCertificateTrusted implementation" into main

parents 63bb6314 a93f7da8
Loading
Loading
Loading
Loading
+1 −6
Original line number Diff line number Diff line
@@ -170,11 +170,6 @@ public final class FileIntegrityManager {
    @Deprecated
    public boolean isAppSourceCertificateTrusted(@NonNull X509Certificate certificate)
            throws CertificateEncodingException {
        try {
            return mService.isAppSourceCertificateTrusted(
                    certificate.getEncoded(), mContext.getOpPackageName());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
        return false;
    }
}
+0 −1
Original line number Diff line number Diff line
@@ -25,7 +25,6 @@ import android.os.IInstalld;
 */
interface IFileIntegrityService {
    boolean isApkVeritySupported();
    boolean isAppSourceCertificateTrusted(in byte[] certificateBytes, in String packageName);

    IInstalld.IFsveritySetupAuthToken createAuthToken(in ParcelFileDescriptor authFd);

+0 −224
Original line number Diff line number Diff line
@@ -17,47 +17,24 @@
package com.android.server.security;

import android.annotation.EnforcePermission;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.os.Binder;
import android.os.Build;
import android.os.Environment;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.PermissionEnforcer;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.UserHandle;
import android.os.storage.StorageManagerInternal;
import android.security.IFileIntegrityService;
import android.util.Slog;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.security.VerityUtils;
import com.android.server.LocalServices;
import com.android.server.SystemService;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Objects;

/**
@@ -67,15 +44,6 @@ import java.util.Objects;
public class FileIntegrityService extends SystemService {
    private static final String TAG = "FileIntegrityService";

    /** The maximum size of signature file.  This is just to avoid potential abuse. */
    private static final int MAX_SIGNATURE_FILE_SIZE_BYTES = 8192;

    private static CertificateFactory sCertFactory;

    @GuardedBy("mTrustedCertificates")
    private final ArrayList<X509Certificate> mTrustedCertificates =
            new ArrayList<X509Certificate>();

    /** Gets the instance of the service */
    public static FileIntegrityService getService() {
        return LocalServices.getService(FileIntegrityService.class);
@@ -91,43 +59,6 @@ public class FileIntegrityService extends SystemService {
            return VerityUtils.isFsVeritySupported();
        }

        @Override
        public boolean isAppSourceCertificateTrusted(@Nullable byte[] certificateBytes,
                @NonNull String packageName) {
            checkCallerPermission(packageName);

            if (android.security.Flags.deprecateFsvSig()) {
                // When deprecated, stop telling the caller that any app source certificate is
                // trusted on the current device. This behavior is also consistent with devices
                // without this feature support.
                return false;
            }

            try {
                if (!VerityUtils.isFsVeritySupported()) {
                    return false;
                }
                if (certificateBytes == null) {
                    Slog.w(TAG, "Received a null certificate");
                    return false;
                }
                synchronized (mTrustedCertificates) {
                    return mTrustedCertificates.contains(toCertificate(certificateBytes));
                }
            } catch (CertificateException e) {
                Slog.e(TAG, "Failed to convert the certificate: " + e);
                return false;
            }
        }

        @Override
        public void onShellCommand(FileDescriptor in, FileDescriptor out,
                FileDescriptor err, String[] args, ShellCallback callback,
                ResultReceiver resultReceiver) {
            new FileIntegrityServiceShellCommand()
                    .exec(this, in, out, err, args, callback, resultReceiver);
        }

        private void checkCallerPackageName(String packageName) {
            final int callingUid = Binder.getCallingUid();
            final int callingUserId = UserHandle.getUserId(callingUid);
@@ -195,12 +126,6 @@ public class FileIntegrityService extends SystemService {
    public FileIntegrityService(final Context context) {
        super(context);
        mService = new BinderService(context);
        try {
            sCertFactory = CertificateFactory.getInstance("X.509");
        } catch (CertificateException e) {
            Slog.wtf(TAG, "Cannot get an instance of X.509 certificate factory");
        }

        LocalServices.addService(FileIntegrityService.class, this);
    }

@@ -215,155 +140,6 @@ public class FileIntegrityService extends SystemService {

    @Override
    public void onStart() {
        loadAllCertificates();
        publishBinderService(Context.FILE_INTEGRITY_SERVICE, mService);
    }

    /**
     * Returns whether the signature over the file's fs-verity digest can be verified by one of the
     * known certiticates.
     */
    public boolean verifyPkcs7DetachedSignature(String signaturePath, String filePath)
            throws IOException {
        if (Files.size(Paths.get(signaturePath)) > MAX_SIGNATURE_FILE_SIZE_BYTES) {
            throw new SecurityException("Signature file is unexpectedly large: "
                    + signaturePath);
        }
        byte[] signatureBytes = Files.readAllBytes(Paths.get(signaturePath));
        byte[] digest = VerityUtils.getFsverityDigest(filePath);
        synchronized (mTrustedCertificates) {
            for (var cert : mTrustedCertificates) {
                try {
                    byte[] derEncoded = cert.getEncoded();
                    if (VerityUtils.verifyPkcs7DetachedSignature(signatureBytes, digest,
                            new ByteArrayInputStream(derEncoded))) {
                        return true;
                    }
                } catch (CertificateEncodingException e) {
                    Slog.w(TAG, "Ignoring ill-formed certificate: " + e);
                }
            }
        }
        return false;
    }

    private void loadAllCertificates() {
        // Load certificates trusted by the device manufacturer.
        final String relativeDir = "etc/security/fsverity";
        loadCertificatesFromDirectory(Environment.getRootDirectory().toPath()
                .resolve(relativeDir));
        loadCertificatesFromDirectory(Environment.getProductDirectory().toPath()
                .resolve(relativeDir));
    }

    private void loadCertificatesFromDirectory(Path path) {
        try {
            File[] files = path.toFile().listFiles();
            if (files == null) {
                return;
            }

            for (File cert : files) {
                byte[] certificateBytes = Files.readAllBytes(cert.toPath());
                collectCertificate(certificateBytes);
            }
        } catch (IOException e) {
            Slog.wtf(TAG, "Failed to load fs-verity certificate from " + path, e);
        }
    }

    /**
     * Tries to convert {@code bytes} into an X.509 certificate and store in memory.
     * Errors need to be surpressed in order fo the next certificates to still be collected.
     */
    private void collectCertificate(@NonNull byte[] bytes) {
        try {
            synchronized (mTrustedCertificates) {
                mTrustedCertificates.add(toCertificate(bytes));
            }
        } catch (CertificateException e) {
            Slog.e(TAG, "Invalid certificate, ignored: " + e);
        }
    }

    /**
     * Converts byte array into one X.509 certificate. If multiple certificate is defined, ignore
     * the rest. The rational is to make it harder to smuggle.
     */
    @NonNull
    private static X509Certificate toCertificate(@NonNull byte[] bytes)
            throws CertificateException {
        Certificate certificate = sCertFactory.generateCertificate(new ByteArrayInputStream(bytes));
        if (!(certificate instanceof X509Certificate)) {
            throw new CertificateException("Expected to contain an X.509 certificate");
        }
        return (X509Certificate) certificate;
    }


    private class FileIntegrityServiceShellCommand extends ShellCommand {
        @Override
        public int onCommand(String cmd) {
            if (!Build.IS_DEBUGGABLE) {
                return -1;
            }
            if (cmd == null) {
                return handleDefaultCommands(cmd);
            }
            final PrintWriter pw = getOutPrintWriter();
            switch (cmd) {
                case "append-cert":
                    String nextArg = getNextArg();
                    if (nextArg == null) {
                        pw.println("Invalid argument");
                        pw.println("");
                        onHelp();
                        return -1;
                    }
                    ParcelFileDescriptor pfd = openFileForSystem(nextArg, "r");
                    if (pfd == null) {
                        pw.println("Cannot open the file");
                        return -1;
                    }
                    InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
                    try {
                        collectCertificate(is.readAllBytes());
                    } catch (IOException e) {
                        pw.println("Failed to add certificate: " + e);
                        return -1;
                    }
                    pw.println("Certificate is added successfully");
                    return 0;

                case "remove-last-cert":
                    synchronized (mTrustedCertificates) {
                        if (mTrustedCertificates.size() == 0) {
                            pw.println("Certificate list is already empty");
                            return -1;
                        }
                        mTrustedCertificates.remove(mTrustedCertificates.size() - 1);
                    }
                    pw.println("Certificate is removed successfully");
                    return 0;
                default:
                    pw.println("Unknown action");
                    pw.println("");
                    onHelp();
            }
            return -1;
        }

        @Override
        public void onHelp() {
            final PrintWriter pw = getOutPrintWriter();
            pw.println("File integrity service commands:");
            pw.println("  help");
            pw.println("    Print this help text.");
            pw.println("  append-cert path/to/cert.der");
            pw.println("    Add the DER-encoded certificate (only in debug builds)");
            pw.println("  remove-last-cert");
            pw.println("    Remove the last certificate in the key list (only in debug builds)");
            pw.println("");
        }
    }
}