From 76a0bb89209c5f97c5193e13c09239d3e5bfcb37 Mon Sep 17 00:00:00 2001 From: Song Pan Date: Thu, 6 Feb 2020 13:48:04 +0000 Subject: [PATCH] Two API changes: 1. Add a new formula type "INSTALLER_ALLOWD_BY_MANIFEST" that evaluates to true when installer is specified in the manifest. This CL only adds this class without actually removing the part where we propagate allowed installers. That will be changed in a new CL. 2. Change the AppIntegrityComponent API so that it is type-safe. Test: atest frameworks/base/services/tests/servicestests/src/com/android/server/integrity Test: atest frameworks/base/core/tests/coretests/src/android/content/integrity Bug: 148780440, 147835536 Change-Id: Icfb996b2f6de241d9790a423dd01992edaf35117 --- api/system-current.txt | 26 ++- api/test-current.txt | 26 ++- .../content/integrity/AppInstallMetadata.java | 28 +++ .../content/integrity/AtomicFormula.java | 9 +- .../InstallerAllowedByManifestFormula.java | 63 ++++++ .../content/integrity/IntegrityFormula.java | 211 +++++++----------- ...InstallerAllowedByManifestFormulaTest.java | 105 +++++++++ .../integrity/IntegrityFormulaTest.java | 77 ++----- .../integrity/model/ComponentBitSize.java | 3 +- .../integrity/parser/RuleBinaryParser.java | 4 + .../serializer/RuleBinarySerializer.java | 4 + .../RuleIndexingDetailsIdentifier.java | 1 + .../AppIntegrityManagerServiceImplTest.java | 8 +- .../parser/RuleBinaryParserTest.java | 2 +- .../RuleIndexingDetailsIdentifierTest.java | 3 +- 15 files changed, 351 insertions(+), 219 deletions(-) create mode 100644 core/java/android/content/integrity/InstallerAllowedByManifestFormula.java create mode 100644 core/tests/coretests/src/android/content/integrity/InstallerAllowedByManifestFormulaTest.java diff --git a/api/system-current.txt b/api/system-current.txt index 24936d5784f8f..dad724e74fd90 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -1922,18 +1922,22 @@ package android.content.integrity { public abstract class IntegrityFormula { method @NonNull public static android.content.integrity.IntegrityFormula all(@NonNull android.content.integrity.IntegrityFormula...); method @NonNull public static android.content.integrity.IntegrityFormula any(@NonNull android.content.integrity.IntegrityFormula...); - method @NonNull public android.content.integrity.IntegrityFormula equalTo(@NonNull String); - method @NonNull public android.content.integrity.IntegrityFormula equalTo(boolean); - method @NonNull public android.content.integrity.IntegrityFormula equalTo(long); - method @NonNull public android.content.integrity.IntegrityFormula greaterThan(long); - method @NonNull public android.content.integrity.IntegrityFormula greaterThanOrEquals(long); method @NonNull public static android.content.integrity.IntegrityFormula not(@NonNull android.content.integrity.IntegrityFormula); - field @NonNull public static final android.content.integrity.IntegrityFormula APP_CERTIFICATE; - field @NonNull public static final android.content.integrity.IntegrityFormula INSTALLER_CERTIFICATE; - field @NonNull public static final android.content.integrity.IntegrityFormula INSTALLER_NAME; - field @NonNull public static final android.content.integrity.IntegrityFormula PACKAGE_NAME; - field @NonNull public static final android.content.integrity.IntegrityFormula PRE_INSTALLED; - field @NonNull public static final android.content.integrity.IntegrityFormula VERSION_CODE; + } + + public static final class IntegrityFormula.Application { + method @NonNull public static android.content.integrity.IntegrityFormula certificatesContain(@NonNull String); + method @NonNull public static android.content.integrity.IntegrityFormula isPreInstalled(); + method @NonNull public static android.content.integrity.IntegrityFormula packageNameEquals(@NonNull String); + method @NonNull public static android.content.integrity.IntegrityFormula versionCodeEquals(@NonNull long); + method @NonNull public static android.content.integrity.IntegrityFormula versionCodeGreaterThan(@NonNull long); + method @NonNull public static android.content.integrity.IntegrityFormula versionCodeGreaterThanOrEqualTo(@NonNull long); + } + + public static final class IntegrityFormula.Installer { + method @NonNull public static android.content.integrity.IntegrityFormula certificatesContain(@NonNull String); + method @NonNull public static android.content.integrity.IntegrityFormula notAllowedByManifest(); + method @NonNull public static android.content.integrity.IntegrityFormula packageNameEquals(@NonNull String); } public final class Rule implements android.os.Parcelable { diff --git a/api/test-current.txt b/api/test-current.txt index e352cb65156ea..78a0b0a2046c9 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -812,18 +812,22 @@ package android.content.integrity { public abstract class IntegrityFormula { method @NonNull public static android.content.integrity.IntegrityFormula all(@NonNull android.content.integrity.IntegrityFormula...); method @NonNull public static android.content.integrity.IntegrityFormula any(@NonNull android.content.integrity.IntegrityFormula...); - method @NonNull public android.content.integrity.IntegrityFormula equalTo(@NonNull String); - method @NonNull public android.content.integrity.IntegrityFormula equalTo(boolean); - method @NonNull public android.content.integrity.IntegrityFormula equalTo(long); - method @NonNull public android.content.integrity.IntegrityFormula greaterThan(long); - method @NonNull public android.content.integrity.IntegrityFormula greaterThanOrEquals(long); method @NonNull public static android.content.integrity.IntegrityFormula not(@NonNull android.content.integrity.IntegrityFormula); - field @NonNull public static final android.content.integrity.IntegrityFormula APP_CERTIFICATE; - field @NonNull public static final android.content.integrity.IntegrityFormula INSTALLER_CERTIFICATE; - field @NonNull public static final android.content.integrity.IntegrityFormula INSTALLER_NAME; - field @NonNull public static final android.content.integrity.IntegrityFormula PACKAGE_NAME; - field @NonNull public static final android.content.integrity.IntegrityFormula PRE_INSTALLED; - field @NonNull public static final android.content.integrity.IntegrityFormula VERSION_CODE; + } + + public static final class IntegrityFormula.Application { + method @NonNull public static android.content.integrity.IntegrityFormula certificatesContain(@NonNull String); + method @NonNull public static android.content.integrity.IntegrityFormula isPreInstalled(); + method @NonNull public static android.content.integrity.IntegrityFormula packageNameEquals(@NonNull String); + method @NonNull public static android.content.integrity.IntegrityFormula versionCodeEquals(@NonNull long); + method @NonNull public static android.content.integrity.IntegrityFormula versionCodeGreaterThan(@NonNull long); + method @NonNull public static android.content.integrity.IntegrityFormula versionCodeGreaterThanOrEqualTo(@NonNull long); + } + + public static final class IntegrityFormula.Installer { + method @NonNull public static android.content.integrity.IntegrityFormula certificatesContain(@NonNull String); + method @NonNull public static android.content.integrity.IntegrityFormula notAllowedByManifest(); + method @NonNull public static android.content.integrity.IntegrityFormula packageNameEquals(@NonNull String); } public final class Rule implements android.os.Parcelable { diff --git a/core/java/android/content/integrity/AppInstallMetadata.java b/core/java/android/content/integrity/AppInstallMetadata.java index cd5117be61233..4be7e6df46e0a 100644 --- a/core/java/android/content/integrity/AppInstallMetadata.java +++ b/core/java/android/content/integrity/AppInstallMetadata.java @@ -18,7 +18,9 @@ package android.content.integrity; import android.annotation.NonNull; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; /** @@ -40,6 +42,7 @@ public final class AppInstallMetadata { private final List mInstallerCertificates; private final long mVersionCode; private final boolean mIsPreInstalled; + private final Map mAllowedInstallersAndCertificates; private AppInstallMetadata(Builder builder) { this.mPackageName = builder.mPackageName; @@ -48,6 +51,7 @@ public final class AppInstallMetadata { this.mInstallerCertificates = builder.mInstallerCertificates; this.mVersionCode = builder.mVersionCode; this.mIsPreInstalled = builder.mIsPreInstalled; + this.mAllowedInstallersAndCertificates = builder.mAllowedInstallersAndCertificates; } @NonNull @@ -80,6 +84,13 @@ public final class AppInstallMetadata { return mIsPreInstalled; } + /** + * Get the allowed installers and their corresponding cert. + */ + public Map getAllowedInstallersAndCertificates() { + return mAllowedInstallersAndCertificates; + } + @Override public String toString() { return String.format( @@ -101,6 +112,23 @@ public final class AppInstallMetadata { private List mInstallerCertificates; private long mVersionCode; private boolean mIsPreInstalled; + private Map mAllowedInstallersAndCertificates; + + public Builder() { + mAllowedInstallersAndCertificates = new HashMap<>(); + } + + /** + * Add allowed installers and cert. + * + * @see AppInstallMetadata#getAllowedInstallersAndCertificates() + */ + @NonNull + public Builder setAllowedInstallersAndCert( + @NonNull Map allowedInstallersAndCertificates) { + this.mAllowedInstallersAndCertificates = allowedInstallersAndCertificates; + return this; + } /** * Set package name of the app to be installed. diff --git a/core/java/android/content/integrity/AtomicFormula.java b/core/java/android/content/integrity/AtomicFormula.java index 42459779e212d..d911eabc3b839 100644 --- a/core/java/android/content/integrity/AtomicFormula.java +++ b/core/java/android/content/integrity/AtomicFormula.java @@ -55,14 +55,12 @@ public abstract class AtomicFormula extends IntegrityFormula { PRE_INSTALLED, }) @Retention(RetentionPolicy.SOURCE) - public @interface Key { - } + public @interface Key {} /** @hide */ @IntDef(value = {EQ, GT, GTE}) @Retention(RetentionPolicy.SOURCE) - public @interface Operator { - } + public @interface Operator {} /** * Package name of the app. @@ -354,7 +352,8 @@ public abstract class AtomicFormula extends IntegrityFormula { "Key %s cannot be used with StringAtomicFormula", keyToString(key))); mValue = hashValue(key, value); mIsHashedValue = - key == APP_CERTIFICATE || key == INSTALLER_CERTIFICATE + key == APP_CERTIFICATE + || key == INSTALLER_CERTIFICATE ? true : !mValue.equals(value); } diff --git a/core/java/android/content/integrity/InstallerAllowedByManifestFormula.java b/core/java/android/content/integrity/InstallerAllowedByManifestFormula.java new file mode 100644 index 0000000000000..475f019e7b261 --- /dev/null +++ b/core/java/android/content/integrity/InstallerAllowedByManifestFormula.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2020 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 android.content.integrity; + +import java.util.Map; + +/** + * An atomic formula that evaluates to true if the installer of the current install is specified in + * the "allowed installer" field in the android manifest. Note that an empty "allowed installer" by + * default means containing all possible installers. + * + * @hide + */ +public class InstallerAllowedByManifestFormula extends IntegrityFormula { + + @Override + public int getTag() { + return IntegrityFormula.INSTALLER_ALLOWED_BY_MANIFEST_FORMULA_TAG; + } + + @Override + public boolean matches(AppInstallMetadata appInstallMetadata) { + Map allowedInstallersAndCertificates = + appInstallMetadata.getAllowedInstallersAndCertificates(); + return allowedInstallersAndCertificates.isEmpty() + || installerInAllowedInstallersFromManifest( + appInstallMetadata, allowedInstallersAndCertificates); + } + + @Override + public boolean isAppCertificateFormula() { + return false; + } + + @Override + public boolean isInstallerFormula() { + return true; + } + + private static boolean installerInAllowedInstallersFromManifest( + AppInstallMetadata appInstallMetadata, + Map allowedInstallersAndCertificates) { + return allowedInstallersAndCertificates.containsKey(appInstallMetadata.getInstallerName()) + && appInstallMetadata.getInstallerCertificates() + .contains( + allowedInstallersAndCertificates + .get(appInstallMetadata.getInstallerName())); + } +} diff --git a/core/java/android/content/integrity/IntegrityFormula.java b/core/java/android/content/integrity/IntegrityFormula.java index a2d937e4df317..ac4c9071f7553 100644 --- a/core/java/android/content/integrity/IntegrityFormula.java +++ b/core/java/android/content/integrity/IntegrityFormula.java @@ -42,66 +42,88 @@ import java.util.Arrays; @VisibleForTesting public abstract class IntegrityFormula { - /** - * A static formula base for package name formulas. - * - * This formulation is incomplete and should always be used with {@code equals} formulation. - * Evaluates to false when used directly and cannot be written as a parcel. - */ - @NonNull - public static final IntegrityFormula PACKAGE_NAME = - new StringAtomicFormula(AtomicFormula.PACKAGE_NAME); + /** Factory class for creating integrity formulas based on the app being installed. */ + public static final class Application { + /** Returns an integrity formula that checks the equality to a package name. */ + @NonNull + public static IntegrityFormula packageNameEquals(@NonNull String packageName) { + return new StringAtomicFormula(AtomicFormula.PACKAGE_NAME, packageName); + } - /** - * A static formula base for app certificate formulas. - * - * This formulation is incomplete and should always be used with {@code equals} formulation. - * Evaluates to false when used directly and cannot be written as a parcel. - */ - @NonNull - public static final IntegrityFormula APP_CERTIFICATE = - new StringAtomicFormula(AtomicFormula.APP_CERTIFICATE); + /** + * Returns an integrity formula that checks if the app certificates contain {@code + * appCertificate}. + */ + @NonNull + public static IntegrityFormula certificatesContain(@NonNull String appCertificate) { + return new StringAtomicFormula(AtomicFormula.APP_CERTIFICATE, appCertificate); + } - /** - * A static formula base for installer name formulas. - * - * This formulation is incomplete and should always be used with {@code equals} formulation. - * Evaluates to false when used directly and cannot be written as a parcel. - */ - @NonNull - public static final IntegrityFormula INSTALLER_NAME = - new StringAtomicFormula(AtomicFormula.INSTALLER_NAME); + /** Returns an integrity formula that checks the equality to a version code. */ + @NonNull + public static IntegrityFormula versionCodeEquals(@NonNull long versionCode) { + return new LongAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.EQ, versionCode); + } - /** - * A static formula base for installer certificate formulas. - * - * This formulation is incomplete and should always be used with {@code equals} formulation. - * Evaluates to false when used directly and cannot be written as a parcel. - */ - @NonNull - public static final IntegrityFormula INSTALLER_CERTIFICATE = - new StringAtomicFormula(AtomicFormula.INSTALLER_CERTIFICATE); + /** + * Returns an integrity formula that checks the app's version code is greater than the + * provided value. + */ + @NonNull + public static IntegrityFormula versionCodeGreaterThan(@NonNull long versionCode) { + return new LongAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.GT, versionCode); + } - /** - * A static formula base for version code name formulas. - * - * This formulation is incomplete and should always be used with {@code equals}, - * {@code greaterThan} and {@code greaterThanEquals} formulation. Evaluates to false when used - * directly and cannot be written as a parcel. - */ - @NonNull - public static final IntegrityFormula VERSION_CODE = - new LongAtomicFormula(AtomicFormula.VERSION_CODE); + /** + * Returns an integrity formula that checks the app's version code is greater than or equal + * to the provided value. + */ + @NonNull + public static IntegrityFormula versionCodeGreaterThanOrEqualTo(@NonNull long versionCode) { + return new LongAtomicFormula( + AtomicFormula.VERSION_CODE, AtomicFormula.GTE, versionCode); + } - /** - * A static formula base for pre-installed status formulas. - * - * This formulation is incomplete and should always be used with {@code equals} formulation. - * Evaluates to false when used directly and cannot be written as a parcel. - */ - @NonNull - public static final IntegrityFormula PRE_INSTALLED = - new BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED); + /** Returns an integrity formula that is valid when app is pre-installed. */ + @NonNull + public static IntegrityFormula isPreInstalled() { + return new BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true); + } + + private Application() { + } + } + + /** Factory class for creating integrity formulas based on installer. */ + public static final class Installer { + /** Returns an integrity formula that checks the equality to an installer name. */ + @NonNull + public static IntegrityFormula packageNameEquals(@NonNull String installerName) { + return new StringAtomicFormula(AtomicFormula.INSTALLER_NAME, installerName); + } + + /** + * An static formula that evaluates to true if the installer is NOT allowed according to the + * "allowed installer" field in the android manifest. + */ + @NonNull + public static IntegrityFormula notAllowedByManifest() { + return not(new InstallerAllowedByManifestFormula()); + } + + /** + * Returns an integrity formula that checks if the installer certificates contain {@code + * installerCertificate}. + */ + @NonNull + public static IntegrityFormula certificatesContain(@NonNull String installerCertificate) { + return new StringAtomicFormula(AtomicFormula.INSTALLER_CERTIFICATE, + installerCertificate); + } + + private Installer() { + } + } /** @hide */ @IntDef( @@ -109,10 +131,12 @@ public abstract class IntegrityFormula { COMPOUND_FORMULA_TAG, STRING_ATOMIC_FORMULA_TAG, LONG_ATOMIC_FORMULA_TAG, - BOOLEAN_ATOMIC_FORMULA_TAG + BOOLEAN_ATOMIC_FORMULA_TAG, + INSTALLER_ALLOWED_BY_MANIFEST_FORMULA_TAG }) @Retention(RetentionPolicy.SOURCE) - @interface Tag {} + @interface Tag { + } /** @hide */ public static final int COMPOUND_FORMULA_TAG = 0; @@ -122,6 +146,8 @@ public abstract class IntegrityFormula { public static final int LONG_ATOMIC_FORMULA_TAG = 2; /** @hide */ public static final int BOOLEAN_ATOMIC_FORMULA_TAG = 3; + /** @hide */ + public static final int INSTALLER_ALLOWED_BY_MANIFEST_FORMULA_TAG = 4; /** * Returns the tag that identifies the current class. @@ -135,14 +161,14 @@ public abstract class IntegrityFormula { * * @hide */ - public abstract @Tag boolean matches(AppInstallMetadata appInstallMetadata); + public abstract boolean matches(AppInstallMetadata appInstallMetadata); /** * Returns true when the formula (or one of its atomic formulas) has app certificate as key. * * @hide */ - public abstract @Tag boolean isAppCertificateFormula(); + public abstract boolean isAppCertificateFormula(); /** * Returns true when the formula (or one of its atomic formulas) has installer package name @@ -150,7 +176,7 @@ public abstract class IntegrityFormula { * * @hide */ - public abstract @Tag boolean isInstallerFormula(); + public abstract boolean isInstallerFormula(); /** * Write an {@link IntegrityFormula} to {@link android.os.Parcel}. @@ -159,7 +185,6 @@ public abstract class IntegrityFormula { * {@link Parcelable}. * * @throws IllegalArgumentException if {@link IntegrityFormula} is not a recognized subclass - * * @hide */ public static void writeToParcel( @@ -194,70 +219,6 @@ public abstract class IntegrityFormula { } } - /** - * Returns an integrity formula that evaluates to true when value of the key matches to the - * provided string value. - * - *

The value will be hashed with SHA256 and the hex digest will be computed; for - * all cases except when the key is PACKAGE_NAME or INSTALLER_NAME and the value is less than - * 32 characters. - * - *

Throws an {@link IllegalArgumentException} if the key is not string typed. - */ - @NonNull - public IntegrityFormula equalTo(@NonNull String value) { - AtomicFormula baseFormula = (AtomicFormula) this; - return new AtomicFormula.StringAtomicFormula(baseFormula.getKey(), value); - } - - /** - * Returns an integrity formula that evaluates to true when the boolean value of the key matches - * the provided boolean value. It can only be used with the boolean comparison keys. - * - *

Throws an {@link IllegalArgumentException} if the key is not boolean typed. - */ - @NonNull - public IntegrityFormula equalTo(boolean value) { - AtomicFormula baseFormula = (AtomicFormula) this; - return new AtomicFormula.BooleanAtomicFormula(baseFormula.getKey(), value); - } - - /** - * Returns a formula that evaluates to true when the value of the key in the package being - * installed is equal to {@code value}. - * - *

Throws an {@link IllegalArgumentException} if the key is not long typed. - */ - @NonNull - public IntegrityFormula equalTo(long value) { - AtomicFormula baseFormula = (AtomicFormula) this; - return new AtomicFormula.LongAtomicFormula(baseFormula.getKey(), AtomicFormula.EQ, value); - } - - /** - * Returns a formula that evaluates to true when the value of the key in the package being - * installed is greater than {@code value}. - * - *

Throws an {@link IllegalArgumentException} if the key is not long typed. - */ - @NonNull - public IntegrityFormula greaterThan(long value) { - AtomicFormula baseFormula = (AtomicFormula) this; - return new AtomicFormula.LongAtomicFormula(baseFormula.getKey(), AtomicFormula.GT, value); - } - - /** - * Returns a formula that evaluates to true when the value of the key in the package being - * installed is greater than or equals to the {@code value}. - * - *

Throws an {@link IllegalArgumentException} if the key is not long typed. - */ - @NonNull - public IntegrityFormula greaterThanOrEquals(long value) { - AtomicFormula baseFormula = (AtomicFormula) this; - return new AtomicFormula.LongAtomicFormula(baseFormula.getKey(), AtomicFormula.GTE, value); - } - /** * Returns a formula that evaluates to true when any formula in {@code formulae} evaluates to * true. diff --git a/core/tests/coretests/src/android/content/integrity/InstallerAllowedByManifestFormulaTest.java b/core/tests/coretests/src/android/content/integrity/InstallerAllowedByManifestFormulaTest.java new file mode 100644 index 0000000000000..c897ace0e0b52 --- /dev/null +++ b/core/tests/coretests/src/android/content/integrity/InstallerAllowedByManifestFormulaTest.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2020 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 android.content.integrity; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.collect.ImmutableMap; + +import org.testng.annotations.Test; + +import java.util.Arrays; +import java.util.Collections; + +public class InstallerAllowedByManifestFormulaTest { + + private static final InstallerAllowedByManifestFormula + FORMULA = new InstallerAllowedByManifestFormula(); + + @Test + public void testFormulaMatches_installerAndCertBothInManifest() { + AppInstallMetadata appInstallMetadata = getAppInstallMetadataBuilder() + .setInstallerName("installer1") + .setInstallerCertificates(Arrays.asList("installer_cert1", "random_cert")) + .setAllowedInstallersAndCert(ImmutableMap.of( + "installer1", "installer_cert1", + "installer2", "installer_cert2" + )).build(); + + assertThat(FORMULA.matches(appInstallMetadata)).isTrue(); + } + + @Test + public void testFormulaMatches_installerAndCertDoesNotMatchInManifest() { + AppInstallMetadata appInstallMetadata = getAppInstallMetadataBuilder() + .setInstallerName("installer1") + .setInstallerCertificates(Arrays.asList("installer_cert1", "random_cert")) + .setAllowedInstallersAndCert(ImmutableMap.of( + "installer1", "installer_cert2", + "installer2", "installer_cert1" + )).build(); + + assertThat(FORMULA.matches(appInstallMetadata)).isFalse(); + } + + @Test + public void testFormulaMatches_installerNotInManifest() { + AppInstallMetadata appInstallMetadata = getAppInstallMetadataBuilder() + .setInstallerName("installer3") + .setInstallerCertificates(Arrays.asList("installer_cert1", "random_cert")) + .setAllowedInstallersAndCert(ImmutableMap.of( + "installer1", "installer_cert2", + "installer2", "installer_cert1" + )).build(); + + assertThat(FORMULA.matches(appInstallMetadata)).isFalse(); + } + + @Test + public void testFormulaMatches_certificateNotInManifest() { + AppInstallMetadata appInstallMetadata = getAppInstallMetadataBuilder() + .setInstallerName("installer1") + .setInstallerCertificates(Arrays.asList("installer_cert3", "random_cert")) + .setAllowedInstallersAndCert(ImmutableMap.of( + "installer1", "installer_cert2", + "installer2", "installer_cert1" + )).build(); + + assertThat(FORMULA.matches(appInstallMetadata)).isFalse(); + } + + @Test + public void testFormulaMatches_emptyManifest() { + AppInstallMetadata appInstallMetadata = getAppInstallMetadataBuilder() + .setInstallerName("installer1") + .setInstallerCertificates(Arrays.asList("installer_cert3", "random_cert")) + .setAllowedInstallersAndCert(ImmutableMap.of()).build(); + + assertThat(FORMULA.matches(appInstallMetadata)).isTrue(); + } + + /** Returns a builder with all fields filled with some dummy data. */ + private AppInstallMetadata.Builder getAppInstallMetadataBuilder() { + return new AppInstallMetadata.Builder() + .setPackageName("abc") + .setAppCertificates(Collections.emptyList()) + .setInstallerCertificates(Collections.emptyList()) + .setInstallerName("abc") + .setVersionCode(-1) + .setIsPreInstalled(true); + } +} diff --git a/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java b/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java index 75ef1f22b8194..62c9c98f4e1d3 100644 --- a/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java +++ b/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java @@ -20,8 +20,6 @@ import static android.content.integrity.IntegrityFormula.COMPOUND_FORMULA_TAG; import static com.google.common.truth.Truth.assertThat; -import static org.testng.Assert.assertThrows; - import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -32,8 +30,7 @@ public class IntegrityFormulaTest { @Test public void createEqualsFormula_packageName() { String packageName = "com.test.app"; - IntegrityFormula formula = - IntegrityFormula.PACKAGE_NAME.equalTo(packageName); + IntegrityFormula formula = IntegrityFormula.Application.packageNameEquals(packageName); AtomicFormula.StringAtomicFormula stringAtomicFormula = (AtomicFormula.StringAtomicFormula) formula; @@ -46,8 +43,7 @@ public class IntegrityFormulaTest { @Test public void createEqualsFormula_appCertificate() { String appCertificate = "com.test.app"; - IntegrityFormula formula = - IntegrityFormula.APP_CERTIFICATE.equalTo(appCertificate); + IntegrityFormula formula = IntegrityFormula.Application.certificatesContain(appCertificate); AtomicFormula.StringAtomicFormula stringAtomicFormula = (AtomicFormula.StringAtomicFormula) formula; @@ -60,8 +56,7 @@ public class IntegrityFormulaTest { @Test public void createEqualsFormula_installerName() { String installerName = "com.test.app"; - IntegrityFormula formula = - IntegrityFormula.INSTALLER_NAME.equalTo(installerName); + IntegrityFormula formula = IntegrityFormula.Installer.packageNameEquals(installerName); AtomicFormula.StringAtomicFormula stringAtomicFormula = (AtomicFormula.StringAtomicFormula) formula; @@ -75,7 +70,7 @@ public class IntegrityFormulaTest { public void createEqualsFormula_installerCertificate() { String installerCertificate = "com.test.app"; IntegrityFormula formula = - IntegrityFormula.INSTALLER_CERTIFICATE.equalTo(installerCertificate); + IntegrityFormula.Installer.certificatesContain(installerCertificate); AtomicFormula.StringAtomicFormula stringAtomicFormula = (AtomicFormula.StringAtomicFormula) formula; @@ -88,8 +83,7 @@ public class IntegrityFormulaTest { @Test public void createEqualsFormula_versionCode() { int versionCode = 12; - IntegrityFormula formula = - IntegrityFormula.VERSION_CODE.equalTo(versionCode); + IntegrityFormula formula = IntegrityFormula.Application.versionCodeEquals(versionCode); AtomicFormula.LongAtomicFormula stringAtomicFormula = (AtomicFormula.LongAtomicFormula) formula; @@ -99,25 +93,10 @@ public class IntegrityFormulaTest { assertThat(stringAtomicFormula.getOperator()).isEqualTo(AtomicFormula.EQ); } - @Test - public void createEqualsFormula_invalidKeyTypeForStringParameter() { - assertThrows( - IllegalArgumentException.class, - () -> IntegrityFormula.PRE_INSTALLED.equalTo("wrongString")); - } - - @Test - public void createEqualsFormula_invalidKeyTypeForLongParameter() { - assertThrows( - IllegalArgumentException.class, - () -> IntegrityFormula.PACKAGE_NAME.equalTo(12)); - } - @Test public void createGreaterThanFormula_versionCode() { int versionCode = 12; - IntegrityFormula formula = - IntegrityFormula.VERSION_CODE.greaterThan(versionCode); + IntegrityFormula formula = IntegrityFormula.Application.versionCodeGreaterThan(versionCode); AtomicFormula.LongAtomicFormula stringAtomicFormula = (AtomicFormula.LongAtomicFormula) formula; @@ -127,18 +106,11 @@ public class IntegrityFormulaTest { assertThat(stringAtomicFormula.getOperator()).isEqualTo(AtomicFormula.GT); } - @Test - public void createGreaterThanFormula_invalidKeyTypeForLongParameter() { - assertThrows( - IllegalArgumentException.class, - () -> IntegrityFormula.PACKAGE_NAME.greaterThan(12)); - } - @Test public void createGreaterThanOrEqualsToFormula_versionCode() { int versionCode = 12; - IntegrityFormula formula = - IntegrityFormula.VERSION_CODE.greaterThanOrEquals(versionCode); + IntegrityFormula formula = IntegrityFormula.Application.versionCodeGreaterThanOrEqualTo( + versionCode); AtomicFormula.LongAtomicFormula stringAtomicFormula = (AtomicFormula.LongAtomicFormula) formula; @@ -148,16 +120,9 @@ public class IntegrityFormulaTest { assertThat(stringAtomicFormula.getOperator()).isEqualTo(AtomicFormula.GTE); } - @Test - public void createGreaterThanOrEqualsToFormula_invalidKeyTypeForLongParameter() { - assertThrows( - IllegalArgumentException.class, - () -> IntegrityFormula.PACKAGE_NAME.greaterThanOrEquals(12)); - } - @Test public void createIsTrueFormula_preInstalled() { - IntegrityFormula formula = IntegrityFormula.PRE_INSTALLED.equalTo(true); + IntegrityFormula formula = IntegrityFormula.Application.isPreInstalled(); AtomicFormula.BooleanAtomicFormula stringAtomicFormula = (AtomicFormula.BooleanAtomicFormula) formula; @@ -166,21 +131,13 @@ public class IntegrityFormulaTest { assertThat(stringAtomicFormula.getValue()).isTrue(); } - @Test - public void createIsTrueFormula_invalidKeyTypeForBoolParameter() { - assertThrows( - IllegalArgumentException.class, - () -> IntegrityFormula.PACKAGE_NAME.equalTo(true)); - } - @Test public void createAllFormula() { String packageName = "com.test.package"; String certificateName = "certificate"; - IntegrityFormula formula1 = - IntegrityFormula.PACKAGE_NAME.equalTo(packageName); - IntegrityFormula formula2 = - IntegrityFormula.APP_CERTIFICATE.equalTo(certificateName); + IntegrityFormula formula1 = IntegrityFormula.Application.packageNameEquals(packageName); + IntegrityFormula formula2 = IntegrityFormula.Application.certificatesContain( + certificateName); IntegrityFormula compoundFormula = IntegrityFormula.all(formula1, formula2); @@ -191,10 +148,9 @@ public class IntegrityFormulaTest { public void createAnyFormula() { String packageName = "com.test.package"; String certificateName = "certificate"; - IntegrityFormula formula1 = - IntegrityFormula.PACKAGE_NAME.equalTo(packageName); - IntegrityFormula formula2 = - IntegrityFormula.APP_CERTIFICATE.equalTo(certificateName); + IntegrityFormula formula1 = IntegrityFormula.Application.packageNameEquals(packageName); + IntegrityFormula formula2 = IntegrityFormula.Application.certificatesContain( + certificateName); IntegrityFormula compoundFormula = IntegrityFormula.any(formula1, formula2); @@ -206,8 +162,7 @@ public class IntegrityFormulaTest { String packageName = "com.test.package"; IntegrityFormula compoundFormula = - IntegrityFormula.not( - IntegrityFormula.PACKAGE_NAME.equalTo(packageName)); + IntegrityFormula.not(IntegrityFormula.Application.packageNameEquals(packageName)); assertThat(compoundFormula.getTag()).isEqualTo(COMPOUND_FORMULA_TAG); } diff --git a/services/core/java/com/android/server/integrity/model/ComponentBitSize.java b/services/core/java/com/android/server/integrity/model/ComponentBitSize.java index c3899638f40ff..94e6708c3038a 100644 --- a/services/core/java/com/android/server/integrity/model/ComponentBitSize.java +++ b/services/core/java/com/android/server/integrity/model/ComponentBitSize.java @@ -29,13 +29,14 @@ public final class ComponentBitSize { public static final int KEY_BITS = 4; public static final int OPERATOR_BITS = 3; public static final int CONNECTOR_BITS = 2; - public static final int SEPARATOR_BITS = 2; + public static final int SEPARATOR_BITS = 3; public static final int VALUE_SIZE_BITS = 8; public static final int IS_HASHED_BITS = 1; public static final int ATOMIC_FORMULA_START = 0; public static final int COMPOUND_FORMULA_START = 1; public static final int COMPOUND_FORMULA_END = 2; + public static final int INSTALLER_ALLOWED_BY_MANIFEST_START = 3; public static final int DEFAULT_FORMAT_VERSION = 1; public static final int SIGNAL_BIT = 1; diff --git a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java index 4b8efafcb6b0f..11e8d91dde124 100644 --- a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java +++ b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java @@ -23,6 +23,7 @@ import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMU import static com.android.server.integrity.model.ComponentBitSize.CONNECTOR_BITS; import static com.android.server.integrity.model.ComponentBitSize.EFFECT_BITS; import static com.android.server.integrity.model.ComponentBitSize.FORMAT_VERSION_BITS; +import static com.android.server.integrity.model.ComponentBitSize.INSTALLER_ALLOWED_BY_MANIFEST_START; import static com.android.server.integrity.model.ComponentBitSize.IS_HASHED_BITS; import static com.android.server.integrity.model.ComponentBitSize.KEY_BITS; import static com.android.server.integrity.model.ComponentBitSize.OPERATOR_BITS; @@ -35,6 +36,7 @@ import static com.android.server.integrity.parser.BinaryFileOperations.getString import android.content.integrity.AtomicFormula; import android.content.integrity.CompoundFormula; +import android.content.integrity.InstallerAllowedByManifestFormula; import android.content.integrity.IntegrityFormula; import android.content.integrity.Rule; @@ -140,6 +142,8 @@ public class RuleBinaryParser implements RuleParser { return parseCompoundFormula(bitInputStream); case COMPOUND_FORMULA_END: return null; + case INSTALLER_ALLOWED_BY_MANIFEST_START: + return new InstallerAllowedByManifestFormula(); default: throw new IllegalArgumentException( String.format("Unknown formula separator: %s", separator)); diff --git a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java index 00e054596cd70..8ba5870aef0f3 100644 --- a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java +++ b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java @@ -23,6 +23,7 @@ import static com.android.server.integrity.model.ComponentBitSize.CONNECTOR_BITS import static com.android.server.integrity.model.ComponentBitSize.DEFAULT_FORMAT_VERSION; import static com.android.server.integrity.model.ComponentBitSize.EFFECT_BITS; import static com.android.server.integrity.model.ComponentBitSize.FORMAT_VERSION_BITS; +import static com.android.server.integrity.model.ComponentBitSize.INSTALLER_ALLOWED_BY_MANIFEST_START; import static com.android.server.integrity.model.ComponentBitSize.KEY_BITS; import static com.android.server.integrity.model.ComponentBitSize.OPERATOR_BITS; import static com.android.server.integrity.model.ComponentBitSize.SEPARATOR_BITS; @@ -36,6 +37,7 @@ import static com.android.server.integrity.serializer.RuleIndexingDetails.PACKAG import android.content.integrity.AtomicFormula; import android.content.integrity.CompoundFormula; +import android.content.integrity.InstallerAllowedByManifestFormula; import android.content.integrity.IntegrityFormula; import android.content.integrity.IntegrityUtils; import android.content.integrity.Rule; @@ -202,6 +204,8 @@ public class RuleBinarySerializer implements RuleSerializer { serializeAtomicFormula((AtomicFormula) formula, bitOutputStream); } else if (formula instanceof CompoundFormula) { serializeCompoundFormula((CompoundFormula) formula, bitOutputStream); + } else if (formula instanceof InstallerAllowedByManifestFormula) { + bitOutputStream.setNext(SEPARATOR_BITS, INSTALLER_ALLOWED_BY_MANIFEST_START); } else { throw new IllegalArgumentException( String.format("Invalid formula type: %s", formula.getClass())); diff --git a/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java b/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java index 6f7d172aabcc8..e7235591fb9b9 100644 --- a/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java +++ b/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java @@ -84,6 +84,7 @@ class RuleIndexingDetailsIdentifier { return getIndexingDetailsForStringAtomicFormula( (AtomicFormula.StringAtomicFormula) formula); case IntegrityFormula.LONG_ATOMIC_FORMULA_TAG: + case IntegrityFormula.INSTALLER_ALLOWED_BY_MANIFEST_FORMULA_TAG: case IntegrityFormula.BOOLEAN_ATOMIC_FORMULA_TAG: // Package name and app certificate related formulas are string atomic formulas. return new RuleIndexingDetails(NOT_INDEXED); diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java index d40130a62fd9f..8dae48cafd7b2 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java @@ -211,7 +211,8 @@ public class AppIntegrityManagerServiceImplTest { IntentSender mockReceiver = mock(IntentSender.class); List rules = Arrays.asList( - new Rule(IntegrityFormula.PACKAGE_NAME.equalTo(PACKAGE_NAME), Rule.DENY)); + new Rule(IntegrityFormula.Application.packageNameEquals(PACKAGE_NAME), + Rule.DENY)); mService.updateRuleSet(VERSION, new ParceledListSlice<>(rules), mockReceiver); runJobInHandler(); @@ -230,7 +231,8 @@ public class AppIntegrityManagerServiceImplTest { IntentSender mockReceiver = mock(IntentSender.class); List rules = Arrays.asList( - new Rule(IntegrityFormula.PACKAGE_NAME.equalTo(PACKAGE_NAME), Rule.DENY)); + new Rule(IntegrityFormula.Application.packageNameEquals(PACKAGE_NAME), + Rule.DENY)); mService.updateRuleSet(VERSION, new ParceledListSlice<>(rules), mockReceiver); runJobInHandler(); @@ -390,7 +392,7 @@ public class AppIntegrityManagerServiceImplTest { public void getCurrentRules() throws Exception { whitelistUsAsRuleProvider(); makeUsSystemApp(); - Rule rule = new Rule(IntegrityFormula.PACKAGE_NAME.equalTo("package"), Rule.DENY); + Rule rule = new Rule(IntegrityFormula.Application.packageNameEquals("package"), Rule.DENY); when(mIntegrityFileManager.readRules(any())).thenReturn(Arrays.asList(rule)); assertThat(mService.getCurrentRules().getList()).containsExactly(rule); diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java index 38cf562f8c5b4..3dc26afdb9af3 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java @@ -58,7 +58,7 @@ public class RuleBinaryParserTest { getBits(COMPOUND_FORMULA_END, SEPARATOR_BITS); private static final String ATOMIC_FORMULA_START_BITS = getBits(ATOMIC_FORMULA_START, SEPARATOR_BITS); - private static final int INVALID_FORMULA_SEPARATOR_VALUE = 3; + private static final int INVALID_FORMULA_SEPARATOR_VALUE = (1 << SEPARATOR_BITS) - 1; private static final String INVALID_FORMULA_SEPARATOR_BITS = getBits(INVALID_FORMULA_SEPARATOR_VALUE, SEPARATOR_BITS); diff --git a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java index 913aff7daaa96..ea9e6ff867289 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java @@ -112,6 +112,7 @@ public class RuleIndexingDetailsIdentifierTest { ATOMIC_FORMULA_WITH_VERSION_CODE, ATOMIC_FORMULA_WITH_ISPREINSTALLED)), Rule.DENY); + public static final int INVALID_FORMULA_TAG = -1; @Test public void getIndexType_nullRule() { @@ -290,7 +291,7 @@ public class RuleIndexingDetailsIdentifierTest { return new AtomicFormula(0) { @Override public int getTag() { - return 4; + return INVALID_FORMULA_TAG; } @Override