diff options
| -rw-r--r-- | services/core/java/com/android/server/integrity/IntegrityFileManager.java | 218 | ||||
| -rw-r--r-- | services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java | 243 |
2 files changed, 0 insertions, 461 deletions
diff --git a/services/core/java/com/android/server/integrity/IntegrityFileManager.java b/services/core/java/com/android/server/integrity/IntegrityFileManager.java deleted file mode 100644 index 7f0231e72373..000000000000 --- a/services/core/java/com/android/server/integrity/IntegrityFileManager.java +++ /dev/null @@ -1,218 +0,0 @@ -/* - * 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; - -import android.annotation.Nullable; -import android.content.integrity.AppInstallMetadata; -import android.content.integrity.Rule; -import android.os.Environment; -import android.util.Slog; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.server.integrity.model.RuleMetadata; -import com.android.server.integrity.parser.RandomAccessObject; -import com.android.server.integrity.parser.RuleBinaryParser; -import com.android.server.integrity.parser.RuleIndexRange; -import com.android.server.integrity.parser.RuleIndexingController; -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.serializer.RuleMetadataSerializer; -import com.android.server.integrity.serializer.RuleSerializeException; -import com.android.server.integrity.serializer.RuleSerializer; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.Collections; -import java.util.List; -import java.util.Optional; - -/** Abstraction over the underlying storage of rules and other metadata. */ -public class IntegrityFileManager { - private static final String TAG = "IntegrityFileManager"; - - private static final String METADATA_FILE = "metadata"; - private static final String RULES_FILE = "rules"; - private static final String INDEXING_FILE = "indexing"; - private static final Object RULES_LOCK = new Object(); - - private static IntegrityFileManager sInstance = null; - - private final RuleParser mRuleParser; - private final RuleSerializer mRuleSerializer; - - private final File mDataDir; - // mRulesDir contains data of the actual rules currently stored. - private final File mRulesDir; - // mStagingDir is used to store the temporary rules / metadata during updating, since we want to - // update rules atomically. - private final File mStagingDir; - - @Nullable private RuleMetadata mRuleMetadataCache; - @Nullable private RuleIndexingController mRuleIndexingController; - - /** Get the singleton instance of this class. */ - public static synchronized IntegrityFileManager getInstance() { - if (sInstance == null) { - sInstance = new IntegrityFileManager(); - } - return sInstance; - } - - private IntegrityFileManager() { - this( - new RuleBinaryParser(), - new RuleBinarySerializer(), - Environment.getDataSystemDirectory()); - } - - @VisibleForTesting - IntegrityFileManager(RuleParser ruleParser, RuleSerializer ruleSerializer, File dataDir) { - mRuleParser = ruleParser; - mRuleSerializer = ruleSerializer; - mDataDir = dataDir; - - mRulesDir = new File(dataDir, "integrity_rules"); - mStagingDir = new File(dataDir, "integrity_staging"); - - if (!mStagingDir.mkdirs() || !mRulesDir.mkdirs()) { - Slog.e(TAG, "Error creating staging and rules directory"); - // TODO: maybe throw an exception? - } - - File metadataFile = new File(mRulesDir, METADATA_FILE); - if (metadataFile.exists()) { - try (FileInputStream inputStream = new FileInputStream(metadataFile)) { - mRuleMetadataCache = RuleMetadataParser.parse(inputStream); - } catch (Exception e) { - Slog.e(TAG, "Error reading metadata file.", e); - } - } - - updateRuleIndexingController(); - } - - /** - * Returns if the rules have been initialized. - * - * <p>Used to fail early if there are no rules (so we don't need to parse the apk at all). - */ - public boolean initialized() { - return new File(mRulesDir, RULES_FILE).exists() - && new File(mRulesDir, METADATA_FILE).exists() - && new File(mRulesDir, INDEXING_FILE).exists(); - } - - /** Write rules to persistent storage. */ - public void writeRules(String version, String ruleProvider, List<Rule> rules) - throws IOException, RuleSerializeException { - try { - writeMetadata(mStagingDir, ruleProvider, version); - } catch (IOException e) { - Slog.e(TAG, "Error writing metadata.", e); - // We don't consider this fatal so we continue execution. - } - - try (FileOutputStream ruleFileOutputStream = - new FileOutputStream(new File(mStagingDir, RULES_FILE)); - FileOutputStream indexingFileOutputStream = - new FileOutputStream(new File(mStagingDir, INDEXING_FILE))) { - mRuleSerializer.serialize( - rules, Optional.empty(), ruleFileOutputStream, indexingFileOutputStream); - } - - switchStagingRulesDir(); - - // Update object holding the indexing information. - updateRuleIndexingController(); - } - - /** - * Read rules from persistent storage. - * - * @param appInstallMetadata information about the install used to select rules to read. If - * null, all rules will be read. - */ - public List<Rule> readRules(@Nullable AppInstallMetadata appInstallMetadata) - throws IOException, RuleParseException { - synchronized (RULES_LOCK) { - // Try to identify indexes from the index file. - List<RuleIndexRange> ruleReadingIndexes = Collections.emptyList(); - if (appInstallMetadata != null) { - try { - ruleReadingIndexes = - mRuleIndexingController.identifyRulesToEvaluate(appInstallMetadata); - } catch (Exception e) { - Slog.w(TAG, "Error identifying the rule indexes. Trying unindexed.", e); - } - } - - // Read the rules based on the index information when available. - File ruleFile = new File(mRulesDir, RULES_FILE); - List<Rule> rules = - mRuleParser.parse(RandomAccessObject.ofFile(ruleFile), ruleReadingIndexes); - return rules; - } - } - - /** Read the metadata of the current rules in storage. */ - @Nullable - public RuleMetadata readMetadata() { - return mRuleMetadataCache; - } - - private void switchStagingRulesDir() throws IOException { - synchronized (RULES_LOCK) { - File tmpDir = new File(mDataDir, "temp"); - - if (!(mRulesDir.renameTo(tmpDir) - && mStagingDir.renameTo(mRulesDir) - && tmpDir.renameTo(mStagingDir))) { - throw new IOException("Error switching staging/rules directory"); - } - - for (File file : mStagingDir.listFiles()) { - file.delete(); - } - } - } - - private void updateRuleIndexingController() { - File ruleIndexingFile = new File(mRulesDir, INDEXING_FILE); - if (ruleIndexingFile.exists()) { - try (FileInputStream inputStream = new FileInputStream(ruleIndexingFile)) { - mRuleIndexingController = new RuleIndexingController(inputStream); - } catch (Exception e) { - Slog.e(TAG, "Error parsing the rule indexing file.", e); - } - } - } - - private void writeMetadata(File directory, String ruleProvider, String version) - throws IOException { - mRuleMetadataCache = new RuleMetadata(ruleProvider, version); - - File metadataFile = new File(directory, METADATA_FILE); - - try (FileOutputStream outputStream = new FileOutputStream(metadataFile)) { - RuleMetadataSerializer.serialize(mRuleMetadataCache, outputStream); - } - } -} diff --git a/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java b/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java deleted file mode 100644 index 096c80b13b89..000000000000 --- a/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java +++ /dev/null @@ -1,243 +0,0 @@ -/* - * 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 com.android.server.integrity; - -import static com.android.server.integrity.model.IndexingFileConstants.INDEXING_BLOCK_SIZE; - -import static com.google.common.truth.Truth.assertThat; - -import android.content.integrity.AppInstallMetadata; -import android.content.integrity.AtomicFormula; -import android.content.integrity.AtomicFormula.LongAtomicFormula; -import android.content.integrity.AtomicFormula.StringAtomicFormula; -import android.content.integrity.CompoundFormula; -import android.content.integrity.Rule; -import android.util.Slog; - -import com.android.server.integrity.parser.RuleBinaryParser; -import com.android.server.integrity.serializer.RuleBinarySerializer; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -import java.io.File; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -/** Unit test for {@link IntegrityFileManager} */ -@RunWith(JUnit4.class) -public class IntegrityFileManagerTest { - private static final String TAG = "IntegrityFileManagerTest"; - - private static final String VERSION = "version"; - private static final String RULE_PROVIDER = "rule_provider"; - - private File mTmpDir; - - // under test - private IntegrityFileManager mIntegrityFileManager; - - @Before - public void setUp() throws Exception { - mTmpDir = Files.createTempDirectory("IntegrityFileManagerTest").toFile(); - Slog.i(TAG, "Using temp directory " + mTmpDir); - - // Use Xml Parser/Serializer to help with debugging since we can just print the file. - mIntegrityFileManager = - new IntegrityFileManager( - new RuleBinaryParser(), new RuleBinarySerializer(), mTmpDir); - Files.walk(mTmpDir.toPath()) - .forEach( - path -> { - Slog.i(TAG, "before " + path); - }); - } - - @After - public void tearDown() throws Exception { - Files.walk(mTmpDir.toPath()) - .forEach( - path -> { - Slog.i(TAG, "after " + path); - }); - // Sorting paths in reverse order guarantees that we delete inside files before deleting - // directory. - Files.walk(mTmpDir.toPath()) - .sorted(Comparator.reverseOrder()) - .map(Path::toFile) - .forEach(File::delete); - } - - @Test - public void testGetMetadata() throws Exception { - assertThat(mIntegrityFileManager.readMetadata()).isNull(); - mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, Collections.EMPTY_LIST); - - assertThat(mIntegrityFileManager.readMetadata()).isNotNull(); - assertThat(mIntegrityFileManager.readMetadata().getVersion()).isEqualTo(VERSION); - assertThat(mIntegrityFileManager.readMetadata().getRuleProvider()).isEqualTo(RULE_PROVIDER); - } - - @Test - public void testIsInitialized() throws Exception { - assertThat(mIntegrityFileManager.initialized()).isFalse(); - mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, Collections.EMPTY_LIST); - assertThat(mIntegrityFileManager.initialized()).isTrue(); - } - - @Test - public void testGetRules() throws Exception { - String packageName = "package"; - String packageCert = "cert"; - int version = 123; - Rule packageNameRule = getPackageNameIndexedRule(packageName); - Rule packageCertRule = getAppCertificateIndexedRule(packageCert); - Rule versionCodeRule = - new Rule( - new LongAtomicFormula( - AtomicFormula.VERSION_CODE, AtomicFormula.EQ, version), - Rule.DENY); - Rule randomRule = - new Rule( - new CompoundFormula( - CompoundFormula.OR, - Arrays.asList( - new StringAtomicFormula( - AtomicFormula.PACKAGE_NAME, - "abc", - /* isHashedValue= */ false), - new LongAtomicFormula( - AtomicFormula.VERSION_CODE, - AtomicFormula.EQ, - version))), - Rule.DENY); - - List<Rule> rules = - Arrays.asList(packageNameRule, packageCertRule, versionCodeRule, randomRule); - mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, rules); - - AppInstallMetadata appInstallMetadata = - new AppInstallMetadata.Builder() - .setPackageName(packageName) - .setAppCertificates(Collections.singletonList(packageCert)) - .setAppCertificateLineage(Collections.singletonList(packageCert)) - .setVersionCode(version) - .setInstallerName("abc") - .setInstallerCertificates(Collections.singletonList("abc")) - .setIsPreInstalled(true) - .build(); - List<Rule> rulesFetched = mIntegrityFileManager.readRules(appInstallMetadata); - - assertThat(rulesFetched) - .containsExactly(packageNameRule, packageCertRule, versionCodeRule, randomRule); - } - - @Test - public void testGetRules_indexedForManyRules() throws Exception { - String packageName = "package"; - String installerName = "installer"; - String appCertificate = "cert"; - - // Create a rule set with 2500 package name indexed, 2500 app certificate indexed and - // 500 unindexed rules. - List<Rule> rules = new ArrayList<>(); - int unindexedRuleCount = 70; - - for (int i = 0; i < 2500; i++) { - rules.add(getPackageNameIndexedRule(String.format("%s%04d", packageName, i))); - rules.add(getAppCertificateIndexedRule(String.format("%s%04d", appCertificate, i))); - } - - for (int i = 0; i < unindexedRuleCount; i++) { - rules.add(getInstallerCertificateRule(String.format("%s%04d", installerName, i))); - } - - // Write the rules and get them indexed. - mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, rules); - - // Read the rules for a specific rule. - String installedPackageName = String.format("%s%04d", packageName, 264); - String installedAppCertificate = String.format("%s%04d", appCertificate, 1264); - AppInstallMetadata appInstallMetadata = - new AppInstallMetadata.Builder() - .setPackageName(installedPackageName) - .setAppCertificates(Collections.singletonList(installedAppCertificate)) - .setAppCertificateLineage( - Collections.singletonList(installedAppCertificate)) - .setVersionCode(250) - .setInstallerName("abc") - .setInstallerCertificates(Collections.singletonList("abc")) - .setIsPreInstalled(true) - .build(); - List<Rule> rulesFetched = mIntegrityFileManager.readRules(appInstallMetadata); - - // Verify that we do not load all the rules and we have the necessary rules to evaluate. - assertThat(rulesFetched.size()) - .isEqualTo(INDEXING_BLOCK_SIZE * 2 + unindexedRuleCount); - assertThat(rulesFetched) - .containsAtLeast( - getPackageNameIndexedRule(installedPackageName), - getAppCertificateIndexedRule(installedAppCertificate)); - } - - private Rule getPackageNameIndexedRule(String packageName) { - return new Rule( - new StringAtomicFormula( - AtomicFormula.PACKAGE_NAME, packageName, /* isHashedValue= */false), - Rule.DENY); - } - - private Rule getAppCertificateIndexedRule(String appCertificate) { - return new Rule( - new StringAtomicFormula( - AtomicFormula.APP_CERTIFICATE, - appCertificate, /* isHashedValue= */ false), - Rule.DENY); - } - - private Rule getInstallerCertificateRule(String installerCert) { - return new Rule( - new StringAtomicFormula( - AtomicFormula.INSTALLER_NAME, installerCert, /* isHashedValue= */false), - Rule.DENY); - } - - @Test - public void testStagingDirectoryCleared() throws Exception { - // We must push rules two times to ensure that staging directory is empty because we cleared - // it, rather than because original rules directory is empty. - mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, Collections.EMPTY_LIST); - mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, Collections.EMPTY_LIST); - - assertStagingDirectoryCleared(); - } - - private void assertStagingDirectoryCleared() { - File stagingDir = new File(mTmpDir, "integrity_staging"); - assertThat(stagingDir.exists()).isTrue(); - assertThat(stagingDir.isDirectory()).isTrue(); - assertThat(stagingDir.listFiles()).isEmpty(); - } -} |