1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.integrity; 18 19 import static com.android.server.integrity.model.IndexingFileConstants.INDEXING_BLOCK_SIZE; 20 21 import static com.google.common.truth.Truth.assertThat; 22 23 import android.content.integrity.AppInstallMetadata; 24 import android.content.integrity.AtomicFormula; 25 import android.content.integrity.AtomicFormula.LongAtomicFormula; 26 import android.content.integrity.AtomicFormula.StringAtomicFormula; 27 import android.content.integrity.CompoundFormula; 28 import android.content.integrity.Rule; 29 import android.util.Slog; 30 31 import com.android.server.integrity.parser.RuleBinaryParser; 32 import com.android.server.integrity.serializer.RuleBinarySerializer; 33 34 import org.junit.After; 35 import org.junit.Before; 36 import org.junit.Test; 37 import org.junit.runner.RunWith; 38 import org.junit.runners.JUnit4; 39 40 import java.io.File; 41 import java.nio.file.Files; 42 import java.nio.file.Path; 43 import java.util.ArrayList; 44 import java.util.Arrays; 45 import java.util.Collections; 46 import java.util.Comparator; 47 import java.util.List; 48 49 /** Unit test for {@link IntegrityFileManager} */ 50 @RunWith(JUnit4.class) 51 public class IntegrityFileManagerTest { 52 private static final String TAG = "IntegrityFileManagerTest"; 53 54 private static final String VERSION = "version"; 55 private static final String RULE_PROVIDER = "rule_provider"; 56 57 private File mTmpDir; 58 59 // under test 60 private IntegrityFileManager mIntegrityFileManager; 61 62 @Before setUp()63 public void setUp() throws Exception { 64 mTmpDir = Files.createTempDirectory("IntegrityFileManagerTest").toFile(); 65 Slog.i(TAG, "Using temp directory " + mTmpDir); 66 67 // Use Xml Parser/Serializer to help with debugging since we can just print the file. 68 mIntegrityFileManager = 69 new IntegrityFileManager( 70 new RuleBinaryParser(), new RuleBinarySerializer(), mTmpDir); 71 Files.walk(mTmpDir.toPath()) 72 .forEach( 73 path -> { 74 Slog.i(TAG, "before " + path); 75 }); 76 } 77 78 @After tearDown()79 public void tearDown() throws Exception { 80 Files.walk(mTmpDir.toPath()) 81 .forEach( 82 path -> { 83 Slog.i(TAG, "after " + path); 84 }); 85 // Sorting paths in reverse order guarantees that we delete inside files before deleting 86 // directory. 87 Files.walk(mTmpDir.toPath()) 88 .sorted(Comparator.reverseOrder()) 89 .map(Path::toFile) 90 .forEach(File::delete); 91 } 92 93 @Test testGetMetadata()94 public void testGetMetadata() throws Exception { 95 assertThat(mIntegrityFileManager.readMetadata()).isNull(); 96 mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, Collections.EMPTY_LIST); 97 98 assertThat(mIntegrityFileManager.readMetadata()).isNotNull(); 99 assertThat(mIntegrityFileManager.readMetadata().getVersion()).isEqualTo(VERSION); 100 assertThat(mIntegrityFileManager.readMetadata().getRuleProvider()).isEqualTo(RULE_PROVIDER); 101 } 102 103 @Test testIsInitialized()104 public void testIsInitialized() throws Exception { 105 assertThat(mIntegrityFileManager.initialized()).isFalse(); 106 mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, Collections.EMPTY_LIST); 107 assertThat(mIntegrityFileManager.initialized()).isTrue(); 108 } 109 110 @Test testGetRules()111 public void testGetRules() throws Exception { 112 String packageName = "package"; 113 String packageCert = "cert"; 114 int version = 123; 115 Rule packageNameRule = getPackageNameIndexedRule(packageName); 116 Rule packageCertRule = getAppCertificateIndexedRule(packageCert); 117 Rule versionCodeRule = 118 new Rule( 119 new LongAtomicFormula( 120 AtomicFormula.VERSION_CODE, AtomicFormula.EQ, version), 121 Rule.DENY); 122 Rule randomRule = 123 new Rule( 124 new CompoundFormula( 125 CompoundFormula.OR, 126 Arrays.asList( 127 new StringAtomicFormula( 128 AtomicFormula.PACKAGE_NAME, 129 "abc", 130 /* isHashedValue= */ false), 131 new LongAtomicFormula( 132 AtomicFormula.VERSION_CODE, 133 AtomicFormula.EQ, 134 version))), 135 Rule.DENY); 136 137 List<Rule> rules = 138 Arrays.asList(packageNameRule, packageCertRule, versionCodeRule, randomRule); 139 mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, rules); 140 141 AppInstallMetadata appInstallMetadata = 142 new AppInstallMetadata.Builder() 143 .setPackageName(packageName) 144 .setAppCertificates(Collections.singletonList(packageCert)) 145 .setAppCertificateLineage(Collections.singletonList(packageCert)) 146 .setVersionCode(version) 147 .setInstallerName("abc") 148 .setInstallerCertificates(Collections.singletonList("abc")) 149 .setIsPreInstalled(true) 150 .build(); 151 List<Rule> rulesFetched = mIntegrityFileManager.readRules(appInstallMetadata); 152 153 assertThat(rulesFetched) 154 .containsExactly(packageNameRule, packageCertRule, versionCodeRule, randomRule); 155 } 156 157 @Test testGetRules_indexedForManyRules()158 public void testGetRules_indexedForManyRules() throws Exception { 159 String packageName = "package"; 160 String installerName = "installer"; 161 String appCertificate = "cert"; 162 163 // Create a rule set with 2500 package name indexed, 2500 app certificate indexed and 164 // 500 unindexed rules. 165 List<Rule> rules = new ArrayList<>(); 166 int unindexedRuleCount = 70; 167 168 for (int i = 0; i < 2500; i++) { 169 rules.add(getPackageNameIndexedRule(String.format("%s%04d", packageName, i))); 170 rules.add(getAppCertificateIndexedRule(String.format("%s%04d", appCertificate, i))); 171 } 172 173 for (int i = 0; i < unindexedRuleCount; i++) { 174 rules.add(getInstallerCertificateRule(String.format("%s%04d", installerName, i))); 175 } 176 177 // Write the rules and get them indexed. 178 mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, rules); 179 180 // Read the rules for a specific rule. 181 String installedPackageName = String.format("%s%04d", packageName, 264); 182 String installedAppCertificate = String.format("%s%04d", appCertificate, 1264); 183 AppInstallMetadata appInstallMetadata = 184 new AppInstallMetadata.Builder() 185 .setPackageName(installedPackageName) 186 .setAppCertificates(Collections.singletonList(installedAppCertificate)) 187 .setAppCertificateLineage( 188 Collections.singletonList(installedAppCertificate)) 189 .setVersionCode(250) 190 .setInstallerName("abc") 191 .setInstallerCertificates(Collections.singletonList("abc")) 192 .setIsPreInstalled(true) 193 .build(); 194 List<Rule> rulesFetched = mIntegrityFileManager.readRules(appInstallMetadata); 195 196 // Verify that we do not load all the rules and we have the necessary rules to evaluate. 197 assertThat(rulesFetched.size()) 198 .isEqualTo(INDEXING_BLOCK_SIZE * 2 + unindexedRuleCount); 199 assertThat(rulesFetched) 200 .containsAtLeast( 201 getPackageNameIndexedRule(installedPackageName), 202 getAppCertificateIndexedRule(installedAppCertificate)); 203 } 204 getPackageNameIndexedRule(String packageName)205 private Rule getPackageNameIndexedRule(String packageName) { 206 return new Rule( 207 new StringAtomicFormula( 208 AtomicFormula.PACKAGE_NAME, packageName, /* isHashedValue= */false), 209 Rule.DENY); 210 } 211 getAppCertificateIndexedRule(String appCertificate)212 private Rule getAppCertificateIndexedRule(String appCertificate) { 213 return new Rule( 214 new StringAtomicFormula( 215 AtomicFormula.APP_CERTIFICATE, 216 appCertificate, /* isHashedValue= */ false), 217 Rule.DENY); 218 } 219 getInstallerCertificateRule(String installerCert)220 private Rule getInstallerCertificateRule(String installerCert) { 221 return new Rule( 222 new StringAtomicFormula( 223 AtomicFormula.INSTALLER_NAME, installerCert, /* isHashedValue= */false), 224 Rule.DENY); 225 } 226 227 @Test testStagingDirectoryCleared()228 public void testStagingDirectoryCleared() throws Exception { 229 // We must push rules two times to ensure that staging directory is empty because we cleared 230 // it, rather than because original rules directory is empty. 231 mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, Collections.EMPTY_LIST); 232 mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, Collections.EMPTY_LIST); 233 234 assertStagingDirectoryCleared(); 235 } 236 assertStagingDirectoryCleared()237 private void assertStagingDirectoryCleared() { 238 File stagingDir = new File(mTmpDir, "integrity_staging"); 239 assertThat(stagingDir.exists()).isTrue(); 240 assertThat(stagingDir.isDirectory()).isTrue(); 241 assertThat(stagingDir.listFiles()).isEmpty(); 242 } 243 } 244