• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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 package android.security.cts;
17 
18 import static org.junit.Assert.assertTrue;
19 
20 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
21 import com.android.tradefed.device.ITestDevice;
22 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
23 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
24 
25 import org.junit.Before;
26 import org.junit.Test;
27 import org.junit.runner.RunWith;
28 
29 import java.io.BufferedReader;
30 import java.io.File;
31 import java.io.FileOutputStream;
32 import java.io.FileReader;
33 import java.io.IOException;
34 import java.io.InputStream;
35 import java.io.InputStreamReader;
36 import java.util.Arrays;
37 import java.util.List;
38 import java.util.Objects;
39 import java.util.stream.Stream;
40 
41 @RunWith(DeviceJUnit4ClassRunner.class)
42 public class SeamendcHostTest extends BaseHostJUnit4Test {
43 
44     private ITestDevice mDevice;
45 
46     // executable binaries
47     private File seamendc;
48     private File secilc;
49     private File searchpolicy;
50     private File libsepolwrap;
51 
52     // CIL policies
53     private File mPlatPolicyCil;
54     private File mPlatCompatCil;
55     private File mSystemExtPolicyCil;
56     private File mSystemExtMappingCil;
57     private File mSystemExtCompatCil;
58     private File mProductPolicyCil;
59     private File mProductMappingCil;
60     private File mVendorPolicyCil;
61     private File mPlatPubVersionedCil;
62     private File mOdmPolicyCil;
63     private File mApexSepolicyCil;
64     private File mApexSepolicyDecompiledCil;
65 
66     @Before
setUp()67     public void setUp() throws Exception {
68         mDevice = getDevice();
69 
70         seamendc = copyResToTempFile("/seamendc");
71         seamendc.setExecutable(true);
72         secilc = copyResToTempFile("/secilc");
73         secilc.setExecutable(true);
74         searchpolicy = copyResToTempFile("/searchpolicy");
75         searchpolicy.setExecutable(true);
76         libsepolwrap = new CompatibilityBuildHelper(getBuild()).getTestFile("libsepolwrap.so");
77         libsepolwrap.deleteOnExit();
78 
79         // Pull CIL files for policy compilation, using selinux.cpp as reference
80         // https://cs.android.com/android/platform/superproject/+/master:system/core/init/selinux.cpp;l=378-453;drc=2d579af880afae96239c80765b11c7dbc2c1b264
81         mPlatPolicyCil = getPlatPolicyFromDevice();
82         String vendorMappingVersion =
83                 readFirstLine("/vendor/etc/selinux/", "plat_sepolicy_vers.txt");
84         mPlatCompatCil =
85                 getDeviceFile("/system/etc/selinux/mapping/", vendorMappingVersion + ".cil");
86         mSystemExtPolicyCil = getDeviceFile("/system_ext/etc/selinux/", "system_ext_sepolicy.cil");
87         mSystemExtMappingCil =
88                 getDeviceFile("/system_ext/etc/selinux/mapping/", vendorMappingVersion + ".cil");
89         mSystemExtCompatCil =
90                 getDeviceFile(
91                         "/system_ext/etc/selinux/mapping/", vendorMappingVersion + ".compat.cil");
92         mProductPolicyCil = getDeviceFile("/product/etc/selinux/", "product_sepolicy.cil");
93         mProductMappingCil =
94                 getDeviceFile("/product/etc/selinux/mapping", vendorMappingVersion + ".cil");
95         mVendorPolicyCil = getDeviceFile("/vendor/etc/selinux/", "vendor_sepolicy.cil");
96         mPlatPubVersionedCil = getDeviceFile("/vendor/etc/selinux/", "plat_pub_versioned.cil");
97         mOdmPolicyCil = getDeviceFile("/odm/etc/selinux/", "odm_sepolicy.cil");
98         mApexSepolicyCil = copyResToTempFile("/apex_sepolicy.cil");
99 
100         mApexSepolicyDecompiledCil = copyResToTempFile("/apex_sepolicy.decompiled.cil");
101     }
102 
103     /**
104      * Verifies that the files necessary for policy compilation exist.
105      *
106      * @throws Exception
107      */
108     @Test
testRequiredDeviceFilesArePresent()109     public void testRequiredDeviceFilesArePresent() throws Exception {
110         assertTrue(mPlatPolicyCil.getName() + " is missing", mPlatPolicyCil != null);
111         assertTrue(mPlatCompatCil.getName() + " is missing", mPlatCompatCil != null);
112         assertTrue(mVendorPolicyCil.getName() + " is missing", mVendorPolicyCil != null);
113         assertTrue(mPlatPubVersionedCil.getName() + " is missing", mPlatPubVersionedCil != null);
114     }
115 
getPlatPolicyFromDevice()116     private File getPlatPolicyFromDevice() throws Exception {
117         File policyFile = getDeviceFile("/system_ext/etc/selinux/", "userdebug_plat_sepolicy.cil");
118         if (policyFile == null) {
119             policyFile = getDeviceFile("/debug_ramdisk/", "userdebug_plat_sepolicy.cil");
120         }
121         if (policyFile == null) {
122             policyFile = getDeviceFile("/system/etc/selinux/", "plat_sepolicy.cil");
123         }
124         return policyFile;
125     }
126 
readFirstLine(String filePath, String fileName)127     private String readFirstLine(String filePath, String fileName) throws Exception {
128         try (BufferedReader brTest =
129                 new BufferedReader(new FileReader(getDeviceFile(filePath, fileName)))) {
130             return brTest.readLine();
131         }
132     }
133 
getDeviceFile(String filePath, String fileName)134     private File getDeviceFile(String filePath, String fileName) throws Exception {
135         String deviceFile = filePath + fileName;
136         File file = File.createTempFile(fileName, ".tmp");
137         file.deleteOnExit();
138         return mDevice.pullFile(deviceFile, file) ? file : null;
139     }
140 
copyResToTempFile(String resName)141     private static File copyResToTempFile(String resName) throws IOException {
142         InputStream is = SeamendcHostTest.class.getResourceAsStream(resName);
143         File tempFile = File.createTempFile(resName, ".tmp");
144         FileOutputStream os = new FileOutputStream(tempFile);
145         byte[] buf = new byte[1024];
146         int len;
147         while ((len = is.read(buf)) != -1) {
148             os.write(buf, 0, len);
149         }
150         os.flush();
151         os.close();
152         tempFile.deleteOnExit();
153         return tempFile;
154     }
155 
runProcess(String... args)156     private static String runProcess(String... args) throws Exception {
157         ProcessBuilder pb = new ProcessBuilder(args);
158         pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
159         pb.redirectErrorStream(true);
160         Process p = pb.start();
161         BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream()));
162         String line;
163         StringBuilder errorString = new StringBuilder();
164         while ((line = result.readLine()) != null) {
165             errorString.append(line);
166             errorString.append("\n");
167         }
168         p.waitFor();
169         return errorString.toString();
170     }
171 
searchpolicySource(File policy, String name)172     private String searchpolicySource(File policy, String name) throws Exception {
173         return runProcess(
174                 searchpolicy.getAbsolutePath(),
175                 "--allow",
176                 "-s",
177                 name,
178                 "--libpath",
179                 libsepolwrap.getAbsolutePath(),
180                 policy.getAbsolutePath());
181     }
182 
searchpolicyTarget(File policy, String name)183     private String searchpolicyTarget(File policy, String name) throws Exception {
184         return runProcess(
185                 searchpolicy.getAbsolutePath(),
186                 "--allow",
187                 "-t",
188                 name,
189                 "--libpath",
190                 libsepolwrap.getAbsolutePath(),
191                 policy.getAbsolutePath());
192     }
193 
194     /** Uses searchpolicy to verify the two policies wrt the provided source type. */
assertSource(File left, File right, String type, boolean equal)195     private void assertSource(File left, File right, String type, boolean equal) throws Exception {
196         String diff = diff(searchpolicySource(left, type), searchpolicySource(right, type));
197         if (equal) {
198             assertTrue("Policy sources are not equal:\n" + diff, diff.length() == 0);
199         } else {
200             assertTrue("Policy sources should be different.", diff.length() != 0);
201         }
202     }
203 
assertSourceEqual(File left, File right, String type)204     private void assertSourceEqual(File left, File right, String type) throws Exception {
205         assertSource(left, right, type, /* equal= */ true);
206     }
207 
assertSourceNotEqual(File left, File right, String type)208     private void assertSourceNotEqual(File left, File right, String type) throws Exception {
209         assertSource(left, right, type, /* equal= */ false);
210     }
211 
212     /** Uses searchpolicy to verify the two policies wrt the provided target type. */
assertTarget(File left, File right, String type, boolean equal)213     private void assertTarget(File left, File right, String type, boolean equal) throws Exception {
214         String diff = diff(searchpolicyTarget(left, type), searchpolicyTarget(right, type));
215         if (equal) {
216             assertTrue("Policies are not equal:\n" + diff, diff.length() == 0);
217         } else {
218             assertTrue("Policies should be different.", diff.length() != 0);
219         }
220     }
221 
assertTargetEqual(File left, File right, String type)222     private void assertTargetEqual(File left, File right, String type) throws Exception {
223         assertTarget(left, right, type, /* equal= */ true);
224     }
225 
assertTargetNotEqual(File left, File right, String type)226     private void assertTargetNotEqual(File left, File right, String type) throws Exception {
227         assertTarget(left, right, type, /* equal= */ false);
228     }
229 
runSeamendc(File basePolicy, File... cilFiles)230     private File runSeamendc(File basePolicy, File... cilFiles) throws Exception {
231         File output = File.createTempFile("seamendc-out", ".binary");
232         output.deleteOnExit();
233         String errorString =
234                 runProcess(
235                         Stream.concat(
236                                         Stream.of(
237                                                 seamendc.getAbsolutePath(),
238                                                 "-b",
239                                                 basePolicy.getAbsolutePath(),
240                                                 "-o",
241                                                 output.getAbsolutePath()),
242                                         Stream.of(cilFiles).map(File::getAbsolutePath))
243                                 .toArray(String[]::new));
244         assertTrue(errorString, errorString.length() == 0);
245         return output;
246     }
247 
runSecilc(File... cilFiles)248     private File runSecilc(File... cilFiles) throws Exception {
249         File fileContexts = File.createTempFile("file_contexts", ".txt");
250         fileContexts.deleteOnExit();
251         File output = File.createTempFile("secilc-out", ".binary");
252         output.deleteOnExit();
253         String errorString =
254                 runProcess(
255                         Stream.concat(
256                                         Stream.of(
257                                                 secilc.getAbsolutePath(),
258                                                 "-m",
259                                                 "-M",
260                                                 "true",
261                                                 "-G",
262                                                 "-N",
263                                                 "-c",
264                                                 "30",
265                                                 "-f",
266                                                 fileContexts.getAbsolutePath(),
267                                                 "-o",
268                                                 output.getAbsolutePath()),
269                                         Stream.of(cilFiles)
270                                                 .filter(Objects::nonNull)
271                                                 .map(File::getAbsolutePath))
272                                 .toArray(String[]::new));
273         assertTrue(errorString, errorString.length() == 0);
274         return output;
275     }
276 
diff(String left, String right)277     private static String diff(String left, String right) {
278         List<String> leftLines = Arrays.asList(left.split("\\r?\\n"));
279         List<String> rightLines = Arrays.asList(right.split("\\r?\\n"));
280 
281         // Generate diff information.
282         List<String> unifiedDiff =
283                 difflib.DiffUtils.generateUnifiedDiff(
284                         "original",
285                         "diff",
286                         leftLines,
287                         difflib.DiffUtils.diff(leftLines, rightLines),
288                         /* contextSize= */ 0);
289         StringBuilder stringBuilder = new StringBuilder();
290         for (String delta : unifiedDiff) {
291             stringBuilder.append(delta);
292             stringBuilder.append("\n");
293         }
294 
295         return stringBuilder.toString();
296     }
297 
298     /**
299      * Verifies the output of seamendc against the binary policy obtained by secilc-compiling the
300      * CIL policies on the device. The binary policies must be the same.
301      *
302      * @throws Exception
303      */
304     @Test
testSeamendcAgainstSecilc()305     public void testSeamendcAgainstSecilc() throws Exception {
306         File secilcOutWithApex =
307                 runSecilc(
308                         mPlatPolicyCil,
309                         mPlatCompatCil,
310                         mSystemExtPolicyCil,
311                         mSystemExtMappingCil,
312                         mSystemExtCompatCil,
313                         mProductPolicyCil,
314                         mProductMappingCil,
315                         mVendorPolicyCil,
316                         mPlatPubVersionedCil,
317                         mOdmPolicyCil,
318                         mApexSepolicyCil);
319         File secilcOutWithoutApex =
320                 runSecilc(
321                         mPlatPolicyCil,
322                         mPlatCompatCil,
323                         mSystemExtPolicyCil,
324                         mSystemExtMappingCil,
325                         mSystemExtCompatCil,
326                         mProductPolicyCil,
327                         mProductMappingCil,
328                         mVendorPolicyCil,
329                         mPlatPubVersionedCil,
330                         mOdmPolicyCil);
331         File seamendcOutWithApex = runSeamendc(secilcOutWithoutApex, mApexSepolicyDecompiledCil);
332 
333         // system/sepolicy/com.android.sepolicy/33/shell.te
334         assertSourceNotEqual(secilcOutWithoutApex, seamendcOutWithApex, "shell");
335         assertTargetEqual(secilcOutWithoutApex, seamendcOutWithApex, "shell");
336         assertSourceEqual(secilcOutWithApex, seamendcOutWithApex, "shell");
337         assertTargetEqual(secilcOutWithApex, seamendcOutWithApex, "shell");
338     }
339 }
340