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

Commit 7297be28 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Implement allowed installers in manfiest rules."

parents 7bc569e7 e24b80aa
Loading
Loading
Loading
Loading
+10 −18
Original line number Diff line number Diff line
@@ -225,11 +225,13 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
            builder.setIsPreInstalled(isSystemApp(packageName));

            AppInstallMetadata appInstallMetadata = builder.build();
            Map<String, String> allowedInstallers = getAllowedInstallers(packageInfo);

            Slog.i(TAG, "To be verified: " + appInstallMetadata);
            Slog.i(
                    TAG,
                    "To be verified: " + appInstallMetadata + " installers " + allowedInstallers);
            IntegrityCheckResult result =
                    mEvaluationEngine.evaluate(
                            appInstallMetadata, getAllowedInstallers(packageInfo));
                    mEvaluationEngine.evaluate(appInstallMetadata, allowedInstallers);
            Slog.i(
                    TAG,
                    "Integrity check result: "
@@ -371,7 +373,7 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
                    String[] packageAndCert =
                            packageCertPair.split(INSTALLER_PACKAGE_CERT_DELIMITER);
                    if (packageAndCert.length == 2) {
                        String packageName = packageAndCert[0];
                        String packageName = getPackageNameNormalized(packageAndCert[0]);
                        String cert = packageAndCert[1];
                        packageCertMap.put(packageName, cert);
                    }
@@ -379,21 +381,9 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
            }
        }

        Slog.i("DEBUG", "allowed installers map " + packageCertMap);
        return packageCertMap;
    }

    private boolean getPreInstalled(String packageName) {
        try {
            PackageInfo existingPackageInfo =
                    mContext.getPackageManager().getPackageInfo(packageName, 0);
            return existingPackageInfo.applicationInfo != null
                    && existingPackageInfo.applicationInfo.isSystemApp();
        } catch (PackageManager.NameNotFoundException e) {
            return false;
        }
    }

    private static Signature getSignature(@NonNull PackageInfo packageInfo) {
        if (packageInfo.signatures == null || packageInfo.signatures.length < 1) {
            throw new IllegalArgumentException("Package signature not found in " + packageInfo);
@@ -463,7 +453,8 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
        PackageInfo basePackageInfo =
                mContext.getPackageManager()
                        .getPackageArchiveInfo(
                                baseFile.getAbsolutePath(), PackageManager.GET_SIGNATURES);
                                baseFile.getAbsolutePath(),
                                PackageManager.GET_SIGNATURES | PackageManager.GET_META_DATA);

        if (basePackageInfo == null) {
            for (File apkFile : multiApkDirectory.listFiles()) {
@@ -477,7 +468,8 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
                        mContext.getPackageManager()
                                .getPackageArchiveInfo(
                                        apkFile.getAbsolutePath(),
                                        PackageManager.GET_SIGNING_CERTIFICATES);
                                        PackageManager.GET_SIGNING_CERTIFICATES
                                                | PackageManager.GET_META_DATA);
                if (basePackageInfo != null) {
                    Slog.i(TAG, "Found package info from " + apkFile);
                    break;
+4 −4
Original line number Diff line number Diff line
@@ -24,14 +24,14 @@ import android.util.Slog;

import com.android.internal.annotations.VisibleForTesting;
import com.android.server.integrity.model.RuleMetadata;
import com.android.server.integrity.parser.RuleBinaryParser;
import com.android.server.integrity.parser.RuleMetadataParser;
import com.android.server.integrity.parser.RuleParseException;
import com.android.server.integrity.parser.RuleParser;
import com.android.server.integrity.serializer.RuleBinarySerializer;
import com.android.server.integrity.parser.RuleXmlParser;
import com.android.server.integrity.serializer.RuleMetadataSerializer;
import com.android.server.integrity.serializer.RuleSerializeException;
import com.android.server.integrity.serializer.RuleSerializer;
import com.android.server.integrity.serializer.RuleXmlSerializer;

import java.io.File;
import java.io.FileInputStream;
@@ -75,8 +75,8 @@ public class IntegrityFileManager {

    private IntegrityFileManager() {
        this(
                new RuleBinaryParser(),
                new RuleBinarySerializer(),
                new RuleXmlParser(),
                new RuleXmlSerializer(),
                Environment.getDataSystemDirectory());
    }

+47 −1
Original line number Diff line number Diff line
@@ -17,15 +17,21 @@
package com.android.server.integrity.engine;

import android.content.integrity.AppInstallMetadata;
import android.content.integrity.AtomicFormula;
import android.content.integrity.CompoundFormula;
import android.content.integrity.Formula;
import android.content.integrity.Rule;
import android.util.Slog;

import com.android.internal.annotations.VisibleForTesting;
import com.android.server.integrity.IntegrityFileManager;
import com.android.server.integrity.model.IntegrityCheckResult;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * The engine used to evaluate rules against app installs.
@@ -42,7 +48,8 @@ public class RuleEvaluationEngine {

    private final IntegrityFileManager mIntegrityFileManager;

    private RuleEvaluationEngine(IntegrityFileManager integrityFileManager) {
    @VisibleForTesting
    RuleEvaluationEngine(IntegrityFileManager integrityFileManager) {
        mIntegrityFileManager = integrityFileManager;
    }

@@ -64,6 +71,7 @@ public class RuleEvaluationEngine {
    public IntegrityCheckResult evaluate(
            AppInstallMetadata appInstallMetadata, Map<String, String> allowedInstallers) {
        List<Rule> rules = loadRules(appInstallMetadata);
        allowedInstallersRule(allowedInstallers).ifPresent(rules::add);
        return RuleEvaluator.evaluateRules(rules, appInstallMetadata);
    }

@@ -75,4 +83,42 @@ public class RuleEvaluationEngine {
            return new ArrayList<>();
        }
    }

    private static Optional<Rule> allowedInstallersRule(Map<String, String> allowedInstallers) {
        if (allowedInstallers.isEmpty()) {
            return Optional.empty();
        }

        List<Formula> formulas = new ArrayList<>(allowedInstallers.size());
        allowedInstallers.forEach(
                (installer, cert) -> {
                    formulas.add(allowedInstallerFormula(installer, cert));
                });

        // We need this special case since OR-formulas require at least two operands.
        Formula allInstallersFormula =
                formulas.size() == 1
                        ? formulas.get(0)
                        : new CompoundFormula(CompoundFormula.OR, formulas);

        return Optional.of(
                new Rule(
                        new CompoundFormula(
                                CompoundFormula.NOT, Arrays.asList(allInstallersFormula)),
                        Rule.DENY));
    }

    private static Formula allowedInstallerFormula(String installer, String cert) {
        return new CompoundFormula(
                CompoundFormula.AND,
                Arrays.asList(
                        new AtomicFormula.StringAtomicFormula(
                                AtomicFormula.INSTALLER_NAME,
                                installer,
                                /* isHashedValue= */ false),
                        new AtomicFormula.StringAtomicFormula(
                                AtomicFormula.INSTALLER_CERTIFICATE,
                                cert,
                                /* isHashedValue= */ false)));
    }
}
+1 −26
Original line number Diff line number Diff line
@@ -21,9 +21,6 @@ import static android.content.integrity.Rule.FORCE_ALLOW;

import android.annotation.NonNull;
import android.content.integrity.AppInstallMetadata;
import android.content.integrity.AtomicFormula;
import android.content.integrity.CompoundFormula;
import android.content.integrity.Formula;
import android.content.integrity.Rule;
import android.util.Slog;

@@ -56,8 +53,7 @@ final class RuleEvaluator {
            List<Rule> rules, AppInstallMetadata appInstallMetadata) {
        List<Rule> matchedRules = new ArrayList<>();
        for (Rule rule : rules) {
            if (isConjunctionOfFormulas(rule.getFormula())
                    && rule.getFormula().isSatisfied(appInstallMetadata)) {
            if (rule.getFormula().isSatisfied(appInstallMetadata)) {
                matchedRules.add(rule);
            }
        }
@@ -81,25 +77,4 @@ final class RuleEvaluator {
        }
        return denied ? IntegrityCheckResult.deny(denyRule) : IntegrityCheckResult.allow();
    }

    private static boolean isConjunctionOfFormulas(Formula formula) {
        if (formula == null) {
            return false;
        }
        if (isAtomicFormula(formula)) {
            return true;
        }
        CompoundFormula compoundFormula = (CompoundFormula) formula;
        return compoundFormula.getConnector() == CompoundFormula.AND
                && compoundFormula.getFormulas().stream().allMatch(RuleEvaluator::isAtomicFormula);
    }

    private static boolean isAtomicFormula(Formula formula) {
        if (formula instanceof AtomicFormula) {
            return true;
        }
        CompoundFormula compoundFormula = (CompoundFormula) formula;
        return compoundFormula.getConnector() == CompoundFormula.NOT
                && compoundFormula.getFormulas().get(0) instanceof AtomicFormula;
    }
}
+197 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.integrity.engine;

import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;

import android.content.integrity.AppInstallMetadata;
import android.content.integrity.Rule;

import com.android.server.integrity.IntegrityFileManager;
import com.android.server.integrity.model.IntegrityCheckResult;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RunWith(JUnit4.class)
public class RuleEvaluationEngineTest {

    private static final String INSTALLER_1 = "installer1";
    private static final String INSTALLER_1_CERT = "installer1_cert";
    private static final String INSTALLER_2 = "installer2";
    private static final String INSTALLER_2_CERT = "installer2_cert";

    private static final String RANDOM_INSTALLER = "random";
    private static final String RANDOM_INSTALLER_CERT = "random_cert";

    @Mock private IntegrityFileManager mIntegrityFileManager;

    private RuleEvaluationEngine mEngine;

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);

        mEngine = new RuleEvaluationEngine(mIntegrityFileManager);

        when(mIntegrityFileManager.readRules(any())).thenReturn(new ArrayList<>());
    }

    @Test
    public void testAllowedInstallers_empty() {
        Map<String, String> allowedInstallers = Collections.emptyMap();

        assertEquals(
                IntegrityCheckResult.Effect.ALLOW,
                mEngine.evaluate(
                                getAppInstallMetadataBuilder()
                                        .setInstallerName(INSTALLER_1)
                                        .setInstallerCertificate(INSTALLER_1_CERT)
                                        .build(),
                                allowedInstallers)
                        .getEffect());
        assertEquals(
                IntegrityCheckResult.Effect.ALLOW,
                mEngine.evaluate(
                                getAppInstallMetadataBuilder()
                                        .setInstallerName(INSTALLER_2)
                                        .setInstallerCertificate(INSTALLER_2_CERT)
                                        .build(),
                                allowedInstallers)
                        .getEffect());
        assertEquals(
                IntegrityCheckResult.Effect.ALLOW,
                mEngine.evaluate(
                                getAppInstallMetadataBuilder()
                                        .setInstallerName(RANDOM_INSTALLER)
                                        .setInstallerCertificate(RANDOM_INSTALLER_CERT)
                                        .build(),
                                allowedInstallers)
                        .getEffect());
    }

    @Test
    public void testAllowedInstallers_oneElement() {
        Map<String, String> allowedInstallers =
                Collections.singletonMap(INSTALLER_1, INSTALLER_1_CERT);

        assertEquals(
                IntegrityCheckResult.Effect.ALLOW,
                mEngine.evaluate(
                                getAppInstallMetadataBuilder()
                                        .setInstallerName(INSTALLER_1)
                                        .setInstallerCertificate(INSTALLER_1_CERT)
                                        .build(),
                                allowedInstallers)
                        .getEffect());
        assertEquals(
                IntegrityCheckResult.Effect.DENY,
                mEngine.evaluate(
                                getAppInstallMetadataBuilder()
                                        .setInstallerName(RANDOM_INSTALLER)
                                        .setInstallerCertificate(INSTALLER_1_CERT)
                                        .build(),
                                allowedInstallers)
                        .getEffect());
        assertEquals(
                IntegrityCheckResult.Effect.DENY,
                mEngine.evaluate(
                                getAppInstallMetadataBuilder()
                                        .setInstallerName(INSTALLER_1)
                                        .setInstallerCertificate(RANDOM_INSTALLER_CERT)
                                        .build(),
                                allowedInstallers)
                        .getEffect());
        assertEquals(
                IntegrityCheckResult.Effect.DENY,
                mEngine.evaluate(
                                getAppInstallMetadataBuilder()
                                        .setInstallerName(RANDOM_INSTALLER)
                                        .setInstallerCertificate(RANDOM_INSTALLER_CERT)
                                        .build(),
                                allowedInstallers)
                        .getEffect());
    }

    @Test
    public void testAllowedInstallers_multipleElement() {
        List<Rule> rules = new ArrayList<>();
        Map<String, String> allowedInstallers = new HashMap<>(2);
        allowedInstallers.put(INSTALLER_1, INSTALLER_1_CERT);
        allowedInstallers.put(INSTALLER_2, INSTALLER_2_CERT);

        assertEquals(
                IntegrityCheckResult.Effect.ALLOW,
                mEngine.evaluate(
                                getAppInstallMetadataBuilder()
                                        .setInstallerName(INSTALLER_1)
                                        .setInstallerCertificate(INSTALLER_1_CERT)
                                        .build(),
                                allowedInstallers)
                        .getEffect());
        assertEquals(
                IntegrityCheckResult.Effect.ALLOW,
                mEngine.evaluate(
                                getAppInstallMetadataBuilder()
                                        .setInstallerName(INSTALLER_2)
                                        .setInstallerCertificate(INSTALLER_2_CERT)
                                        .build(),
                                allowedInstallers)
                        .getEffect());
        assertEquals(
                IntegrityCheckResult.Effect.DENY,
                mEngine.evaluate(
                                getAppInstallMetadataBuilder()
                                        .setInstallerName(INSTALLER_1)
                                        .setInstallerCertificate(INSTALLER_2_CERT)
                                        .build(),
                                allowedInstallers)
                        .getEffect());
        assertEquals(
                IntegrityCheckResult.Effect.DENY,
                mEngine.evaluate(
                                getAppInstallMetadataBuilder()
                                        .setInstallerName(INSTALLER_2)
                                        .setInstallerCertificate(INSTALLER_1_CERT)
                                        .build(),
                                allowedInstallers)
                        .getEffect());
    }

    /** Returns a builder with all fields filled with some dummy data. */
    private AppInstallMetadata.Builder getAppInstallMetadataBuilder() {
        return new AppInstallMetadata.Builder()
                .setPackageName("abc")
                .setAppCertificate("abc")
                .setInstallerCertificate("abc")
                .setInstallerName("abc")
                .setVersionCode(-1)
                .setIsPreInstalled(true);
    }
}