1 /*
2  * Copyright (C) 2014 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 android.security.cts;
18 
19 import android.platform.test.annotations.RestrictedBuildTest;
20 
21 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
22 import com.android.compatibility.common.util.PropertyUtil;
23 import com.android.tradefed.build.IBuildInfo;
24 import com.android.tradefed.device.CollectingOutputReceiver;
25 import com.android.tradefed.device.DeviceNotAvailableException;
26 import com.android.tradefed.device.ITestDevice;
27 import com.android.tradefed.testtype.DeviceTestCase;
28 import com.android.tradefed.testtype.IBuildReceiver;
29 import com.android.tradefed.testtype.IDeviceTest;
30 
31 import com.android.compatibility.common.util.CddTest;
32 
33 import java.io.BufferedReader;
34 import java.io.ByteArrayOutputStream;
35 import java.io.File;
36 import java.io.FileReader;
37 import java.io.FileInputStream;
38 import java.io.FileOutputStream;
39 import java.io.IOException;
40 import java.io.InputStream;
41 import java.io.InputStreamReader;
42 import java.lang.String;
43 import java.util.ArrayList;
44 import java.util.Arrays;
45 import java.util.Collections;
46 import java.util.HashMap;
47 import java.util.HashSet;
48 import java.util.List;
49 import java.util.Map;
50 import java.util.regex.Matcher;
51 import java.util.regex.Pattern;
52 import java.util.Scanner;
53 import java.util.Set;
54 
55 import javax.xml.parsers.DocumentBuilder;
56 import javax.xml.parsers.DocumentBuilderFactory;
57 import org.w3c.dom.Document;
58 import org.w3c.dom.Element;
59 
60 /**
61  * Host-side SELinux tests.
62  *
63  * These tests analyze the policy file in use on the subject device directly or
64  * run as the shell user to evaluate aspects of the state of SELinux on the test
65  * device which otherwise would not be available to a normal apk.
66  */
67 public class SELinuxHostTest extends DeviceTestCase implements IBuildReceiver, IDeviceTest {
68 
69     private static final Map<ITestDevice, File> cachedDevicePolicyFiles = new HashMap<>(1);
70     private static final Map<ITestDevice, File> cachedDevicePlatFcFiles = new HashMap<>(1);
71     private static final Map<ITestDevice, File> cachedDeviceNonplatFcFiles = new HashMap<>(1);
72     private static final Map<ITestDevice, File> cachedDeviceVendorManifest = new HashMap<>(1);
73     private static final Map<ITestDevice, File> cachedDeviceSystemPolicy = new HashMap<>(1);
74 
75     private File sepolicyAnalyze;
76     private File checkSeapp;
77     private File checkFc;
78     private File aospSeappFile;
79     private File aospFcFile;
80     private File aospPcFile;
81     private File aospSvcFile;
82     private File devicePolicyFile;
83     private File devicePlatSeappFile;
84     private File deviceNonplatSeappFile;
85     private File devicePlatFcFile;
86     private File deviceNonplatFcFile;
87     private File devicePcFile;
88     private File deviceSvcFile;
89     private File seappNeverAllowFile;
90     private File libsepolwrap;
91     private File libcpp;
92     private File sepolicyTests;
93 
94     private IBuildInfo mBuild;
95 
96     /**
97      * A reference to the device under test.
98      */
99     private ITestDevice mDevice;
100 
101     /**
102      * {@inheritDoc}
103      */
104     @Override
setBuild(IBuildInfo build)105     public void setBuild(IBuildInfo build) {
106         mBuild = build;
107     }
108 
109     /**
110      * {@inheritDoc}
111      */
112     @Override
setDevice(ITestDevice device)113     public void setDevice(ITestDevice device) {
114         super.setDevice(device);
115         mDevice = device;
116     }
117 
copyResourceToTempFile(String resName)118     public static File copyResourceToTempFile(String resName) throws IOException {
119         InputStream is = SELinuxHostTest.class.getResourceAsStream(resName);
120         File tempFile = File.createTempFile("SELinuxHostTest", ".tmp");
121         FileOutputStream os = new FileOutputStream(tempFile);
122         byte[] buf = new byte[1024];
123         int len;
124 
125         while ((len = is.read(buf)) != -1) {
126             os.write(buf, 0, len);
127         }
128         os.flush();
129         os.close();
130         tempFile.deleteOnExit();
131         return tempFile;
132     }
133 
appendTo(String dest, String src)134     private static void appendTo(String dest, String src) throws IOException {
135         try (FileInputStream is = new FileInputStream(new File(src));
136              FileOutputStream os = new FileOutputStream(new File(dest))) {
137             byte[] buf = new byte[1024];
138             int len;
139 
140             while ((len = is.read(buf)) != -1) {
141                 os.write(buf, 0, len);
142             }
143         }
144     }
145 
146     @Override
setUp()147     protected void setUp() throws Exception {
148         super.setUp();
149         CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mBuild);
150         sepolicyAnalyze = buildHelper.getTestFile("sepolicy-analyze");
151         sepolicyAnalyze.setExecutable(true);
152 
153         devicePolicyFile = getDevicePolicyFile(mDevice);
154         if (isSepolicySplit(mDevice)) {
155             devicePlatFcFile = getDeviceFile(mDevice, cachedDevicePlatFcFiles,
156                     "/system/etc/selinux/plat_file_contexts", "plat_file_contexts");
157             if (mDevice.doesFileExist("/vendor/etc/selinux/nonplat_file_contexts")){
158                 // Old nonplat_* naming can be present if a framework-only OTA was done.
159                 deviceNonplatFcFile = getDeviceFile(mDevice, cachedDeviceNonplatFcFiles,
160                         "/vendor/etc/selinux/nonplat_file_contexts", "nonplat_file_contexts");
161             } else {
162                 deviceNonplatFcFile = getDeviceFile(mDevice, cachedDeviceNonplatFcFiles,
163                         "/vendor/etc/selinux/vendor_file_contexts", "vendor_file_contexts");
164             }
165         } else {
166             devicePlatFcFile = getDeviceFile(mDevice, cachedDevicePlatFcFiles,
167                     "/plat_file_contexts", "plat_file_contexts");
168             deviceNonplatFcFile = getDeviceFile(mDevice, cachedDeviceNonplatFcFiles,
169                     "/vendor_file_contexts", "vendor_file_contexts");
170         }
171     }
172 
173     /*
174      * IMPLEMENTATION DETAILS: We cache some host-side policy files on per-device basis (in case
175      * CTS supports running against multiple devices at the same time). HashMap is used instead
176      * of WeakHashMap because in the grand scheme of things, keeping ITestDevice and
177      * corresponding File objects from being garbage-collected is not a big deal in CTS. If this
178      * becomes a big deal, this can be switched to WeakHashMap.
179      */
getDeviceFile(ITestDevice device, Map<ITestDevice, File> cache, String deviceFilePath, String tmpFileName)180     private static File getDeviceFile(ITestDevice device,
181             Map<ITestDevice, File> cache, String deviceFilePath,
182             String tmpFileName) throws Exception {
183         if (!device.doesFileExist(deviceFilePath)){
184             throw new Exception();
185         }
186         File file;
187         synchronized (cache) {
188             file = cache.get(device);
189         }
190         if (file != null) {
191             return file;
192         }
193         file = File.createTempFile(tmpFileName, ".tmp");
194         file.deleteOnExit();
195         device.pullFile(deviceFilePath, file);
196         synchronized (cache) {
197             cache.put(device, file);
198         }
199         return file;
200     }
201 
buildSystemPolicy(ITestDevice device, Map<ITestDevice, File> cache, String tmpFileName)202     private static File buildSystemPolicy(ITestDevice device, Map<ITestDevice, File> cache,
203             String tmpFileName) throws Exception {
204         File builtPolicyFile;
205         synchronized (cache) {
206             builtPolicyFile = cache.get(device);
207         }
208         if (builtPolicyFile != null) {
209             return builtPolicyFile;
210         }
211 
212 
213         builtPolicyFile = File.createTempFile(tmpFileName, ".tmp");
214         builtPolicyFile.deleteOnExit();
215 
216         File secilc = copyResourceToTempFile("/secilc");
217         secilc.setExecutable(true);
218 
219         File systemSepolicyCilFile = File.createTempFile("plat_sepolicy", ".cil");
220         systemSepolicyCilFile.deleteOnExit();
221 
222         assertTrue(device.pullFile("/system/etc/selinux/plat_sepolicy.cil", systemSepolicyCilFile));
223 
224         ProcessBuilder pb = new ProcessBuilder(
225             secilc.getAbsolutePath(),
226             "-m", "-M", "true", "-c", "30",
227             "-o", builtPolicyFile.getAbsolutePath(),
228             systemSepolicyCilFile.getAbsolutePath());
229         pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
230         pb.redirectErrorStream(true);
231         Process p = pb.start();
232         p.waitFor();
233         BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream()));
234         String line;
235         StringBuilder errorString = new StringBuilder();
236         while ((line = result.readLine()) != null) {
237             errorString.append(line);
238             errorString.append("\n");
239         }
240         assertTrue(errorString.toString(), errorString.length() == 0);
241 
242         synchronized (cache) {
243             cache.put(device, builtPolicyFile);
244         }
245         return builtPolicyFile;
246     }
247 
248     // NOTE: cts/tools/selinux depends on this method. Rename/change with caution.
249     /**
250      * Returns the host-side file containing the SELinux policy of the device under test.
251      */
getDevicePolicyFile(ITestDevice device)252     public static File getDevicePolicyFile(ITestDevice device) throws Exception {
253         return getDeviceFile(device, cachedDevicePolicyFiles, "/sys/fs/selinux/policy", "sepolicy");
254     }
255 
256     // NOTE: cts/tools/selinux depends on this method. Rename/change with caution.
257     /**
258      * Returns the host-side file containing the system SELinux policy of the device under test.
259      */
getDeviceSystemPolicyFile(ITestDevice device)260     public static File getDeviceSystemPolicyFile(ITestDevice device) throws Exception {
261         return buildSystemPolicy(device, cachedDeviceSystemPolicy, "system_sepolicy");
262     }
263 
264     // NOTE: cts/tools/selinux depends on this method. Rename/change with caution.
265     /**
266      * Returns the major number of sepolicy version of device's vendor implementation.
267      * TODO(b/37999212): Use VINTF object API instead of parsing vendor manifest.
268      */
getVendorSepolicyVersion(ITestDevice device)269     public static int getVendorSepolicyVersion(ITestDevice device) throws Exception {
270         String deviceManifestPath =
271                 (device.doesFileExist("/vendor/etc/vintf/manifest.xml")) ?
272                 "/vendor/etc/vintf/manifest.xml" :
273                 "/vendor/manifest.xml";
274         File vendorManifestFile = getDeviceFile(device, cachedDeviceVendorManifest,
275                 deviceManifestPath, "manifest.xml");
276 
277         DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
278         DocumentBuilder db = dbf.newDocumentBuilder();
279         Document doc = db.parse(vendorManifestFile);
280         Element root = doc.getDocumentElement();
281         Element sepolicy = (Element) root.getElementsByTagName("sepolicy").item(0);
282         Element version = (Element) sepolicy.getElementsByTagName("version").item(0);
283         String sepolicyVersion = version.getTextContent().split("\\.")[0];
284         return Integer.parseInt(sepolicyVersion);
285     }
286 
287     /**
288      * Tests that the kernel is enforcing selinux policy globally.
289      *
290      * @throws Exception
291      */
292     @CddTest(requirement="9.7")
testGlobalEnforcing()293     public void testGlobalEnforcing() throws Exception {
294         CollectingOutputReceiver out = new CollectingOutputReceiver();
295         mDevice.executeShellCommand("cat /sys/fs/selinux/enforce", out);
296         assertEquals("SELinux policy is not being enforced!", "1", out.getOutput());
297     }
298 
299     /**
300      * Tests that all domains in the running policy file are in enforcing mode
301      *
302      * @throws Exception
303      */
304     @CddTest(requirement="9.7")
305     @RestrictedBuildTest
testAllDomainsEnforcing()306     public void testAllDomainsEnforcing() throws Exception {
307 
308         /* run sepolicy-analyze permissive check on policy file */
309         ProcessBuilder pb = new ProcessBuilder(sepolicyAnalyze.getAbsolutePath(),
310                 devicePolicyFile.getAbsolutePath(), "permissive");
311         pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
312         pb.redirectErrorStream(true);
313         Process p = pb.start();
314         p.waitFor();
315         BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream()));
316         String line;
317         StringBuilder errorString = new StringBuilder();
318         while ((line = result.readLine()) != null) {
319             errorString.append(line);
320             errorString.append("\n");
321         }
322         assertTrue("The following SELinux domains were found to be in permissive mode:\n"
323                    + errorString, errorString.length() == 0);
324     }
325 
326     /**
327      * Asserts that specified type is not associated with the specified
328      * attribute.
329      *
330      * @param attribute
331      *  The attribute name.
332      * @param type
333      *  The type name.
334      */
assertNotInAttribute(String attribute, String badtype)335     private void assertNotInAttribute(String attribute, String badtype) throws Exception {
336         Set<String> actualTypes = sepolicyAnalyzeGetTypesAssociatedWithAttribute(attribute);
337         if (actualTypes.contains(badtype)) {
338             fail("Attribute " + attribute + " includes " + badtype);
339         }
340     }
341 
readFully(InputStream in)342     private static final byte[] readFully(InputStream in) throws IOException {
343         ByteArrayOutputStream result = new ByteArrayOutputStream();
344         byte[] buf = new byte[65536];
345         int chunkSize;
346         while ((chunkSize = in.read(buf)) != -1) {
347             result.write(buf, 0, chunkSize);
348         }
349         return result.toByteArray();
350     }
351 
352     /**
353      * Runs sepolicy-analyze against the device's SELinux policy and returns the set of types
354      * associated with the provided attribute.
355      */
sepolicyAnalyzeGetTypesAssociatedWithAttribute( String attribute)356     private Set<String> sepolicyAnalyzeGetTypesAssociatedWithAttribute(
357             String attribute) throws Exception {
358         ProcessBuilder pb =
359                 new ProcessBuilder(
360                         sepolicyAnalyze.getAbsolutePath(),
361                         devicePolicyFile.getAbsolutePath(),
362                         "attribute",
363                         attribute);
364         pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
365         pb.redirectErrorStream(true);
366         Process p = pb.start();
367         int errorCode = p.waitFor();
368         if (errorCode != 0) {
369             fail("sepolicy-analyze attribute " + attribute + " failed with error code " + errorCode
370                     + ": " + new String(readFully(p.getInputStream())));
371         }
372         try (BufferedReader in =
373                 new BufferedReader(new InputStreamReader(p.getInputStream()))) {
374             Set<String> types = new HashSet<>();
375             String type;
376             while ((type = in.readLine()) != null) {
377                 types.add(type.trim());
378             }
379             return types;
380         }
381     }
382 
383     // NOTE: cts/tools/selinux depends on this method. Rename/change with caution.
384     /**
385      * Returns {@code true} if this device is required to be a full Treble device.
386      */
isFullTrebleDevice(ITestDevice device)387     public static boolean isFullTrebleDevice(ITestDevice device)
388             throws DeviceNotAvailableException {
389         return PropertyUtil.getFirstApiLevel(device) > 26;
390     }
391 
isFullTrebleDevice()392     private boolean isFullTrebleDevice() throws DeviceNotAvailableException {
393         return isFullTrebleDevice(mDevice);
394     }
395 
396     // NOTE: cts/tools/selinux depends on this method. Rename/change with caution.
397     /**
398      * Returns {@code true} if this device is required to enforce compatible property.
399      */
isCompatiblePropertyEnforcedDevice(ITestDevice device)400     public static boolean isCompatiblePropertyEnforcedDevice(ITestDevice device)
401             throws DeviceNotAvailableException {
402         return PropertyUtil.getFirstApiLevel(device) > 27;
403     }
404 
405     // NOTE: cts/tools/selinux depends on this method. Rename/change with caution.
406     /**
407      * Returns {@code true} if this device has sepolicy split across different paritions.
408      * This is possible even for devices launched at api level higher than 26.
409      */
isSepolicySplit(ITestDevice device)410     public static boolean isSepolicySplit(ITestDevice device)
411             throws DeviceNotAvailableException {
412         return device.doesFileExist("/system/etc/selinux/plat_file_contexts");
413     }
414 
415     /**
416      * Asserts that no vendor domains are exempted from the prohibition on Binder use.
417      *
418      * <p>NOTE: binder_in_vendor_violators attribute is only there to help bring up Treble devices.
419      * It offers a convenient way to temporarily bypass the prohibition on Binder use in vendor
420      * domains. This attribute must not be used on production Treble devices.
421      */
testNoExemptionsForBinderInVendorBan()422     public void testNoExemptionsForBinderInVendorBan() throws Exception {
423         if (!isFullTrebleDevice()) {
424             return;
425         }
426 
427         Set<String> types =
428             sepolicyAnalyzeGetTypesAssociatedWithAttribute("binder_in_vendor_violators");
429         if (!types.isEmpty()) {
430             List<String> sortedTypes = new ArrayList<>(types);
431             Collections.sort(sortedTypes);
432             fail("Policy exempts vendor domains from ban on Binder: " + sortedTypes);
433         }
434     }
435 
436     /**
437      * Asserts that no HAL server domains are exempted from the prohibition of socket use with the
438      * only exceptions for the automotive device type.
439      */
testNoExemptionsForSocketsUseWithinHalServer()440     public void testNoExemptionsForSocketsUseWithinHalServer() throws Exception {
441         if (!isFullTrebleDevice()) {
442             return;
443         }
444 
445         if (getDevice().hasFeature("android.hardware.type.automotive")) {
446             return;
447         }
448 
449         Set<String> types = sepolicyAnalyzeGetTypesAssociatedWithAttribute(
450                 "hal_automotive_socket_exemption");
451         if (!types.isEmpty()) {
452             List<String> sortedTypes = new ArrayList<>(types);
453             Collections.sort(sortedTypes);
454             fail("Policy exempts domains from ban on socket usage from HAL servers: "
455                     + sortedTypes);
456         }
457     }
458 
459     /**
460      * Asserts that no domains are exempted from the prohibition on initiating socket communications
461      * between core and vendor domains.
462      *
463      * <p>NOTE: socket_between_core_and_vendor_violators attribute is only there to help bring up
464      * Treble devices. It offers a convenient way to temporarily bypass the prohibition on
465      * initiating socket communications between core and vendor domains. This attribute must not be
466      * used on production Treble devices.
467      */
testNoExemptionsForSocketsBetweenCoreAndVendorBan()468     public void testNoExemptionsForSocketsBetweenCoreAndVendorBan() throws Exception {
469         if (!isFullTrebleDevice()) {
470             return;
471         }
472 
473         Set<String> types =
474                 sepolicyAnalyzeGetTypesAssociatedWithAttribute(
475                         "socket_between_core_and_vendor_violators");
476         if (!types.isEmpty()) {
477             List<String> sortedTypes = new ArrayList<>(types);
478             Collections.sort(sortedTypes);
479             fail("Policy exempts domains from ban on socket communications between core and"
480                     + " vendor: " + sortedTypes);
481         }
482     }
483 
484     /**
485      * Asserts that no vendor domains are exempted from the prohibition on directly
486      * executing binaries from /system.
487      * */
testNoExemptionsForVendorExecutingCore()488     public void testNoExemptionsForVendorExecutingCore() throws Exception {
489         if (!isFullTrebleDevice()) {
490             return;
491         }
492 
493         Set<String> types =
494                 sepolicyAnalyzeGetTypesAssociatedWithAttribute(
495                         "vendor_executes_system_violators");
496         if (!types.isEmpty()) {
497             List<String> sortedTypes = new ArrayList<>(types);
498             Collections.sort(sortedTypes);
499             fail("Policy exempts vendor domains from ban on executing files in /system: "
500                     + sortedTypes);
501         }
502     }
503 
504     /**
505      * Tests that mlstrustedsubject does not include untrusted_app
506      * and that mlstrustedobject does not include app_data_file.
507      * This helps prevent circumventing the per-user isolation of
508      * normal apps via levelFrom=user.
509      *
510      * @throws Exception
511      */
512     @CddTest(requirement="9.7")
testMLSAttributes()513     public void testMLSAttributes() throws Exception {
514         assertNotInAttribute("mlstrustedsubject", "untrusted_app");
515         assertNotInAttribute("mlstrustedobject", "app_data_file");
516     }
517 
518     /**
519      * Tests that the seapp_contexts file on the device is valid.
520      *
521      * @throws Exception
522      */
523     @CddTest(requirement="9.7")
testValidSeappContexts()524     public void testValidSeappContexts() throws Exception {
525 
526         /* obtain seapp_contexts file from running device */
527         devicePlatSeappFile = File.createTempFile("plat_seapp_contexts", ".tmp");
528         devicePlatSeappFile.deleteOnExit();
529         deviceNonplatSeappFile = File.createTempFile("nonplat_seapp_contexts", ".tmp");
530         deviceNonplatSeappFile.deleteOnExit();
531         if (mDevice.pullFile("/system/etc/selinux/plat_seapp_contexts", devicePlatSeappFile)) {
532             mDevice.pullFile("/vendor/etc/selinux/nonplat_seapp_contexts", deviceNonplatSeappFile);
533         }else {
534             mDevice.pullFile("/plat_seapp_contexts", devicePlatSeappFile);
535             mDevice.pullFile("/nonplat_seapp_contexts", deviceNonplatSeappFile);
536 	}
537 
538         /* retrieve the checkseapp executable from jar */
539         checkSeapp = copyResourceToTempFile("/checkseapp");
540         checkSeapp.setExecutable(true);
541 
542         /* retrieve the AOSP seapp_neverallows file from jar */
543         seappNeverAllowFile = copyResourceToTempFile("/plat_seapp_neverallows");
544 
545         /* run checkseapp on seapp_contexts */
546         ProcessBuilder pb = new ProcessBuilder(checkSeapp.getAbsolutePath(),
547                 "-p", devicePolicyFile.getAbsolutePath(),
548                 seappNeverAllowFile.getAbsolutePath(),
549                 devicePlatSeappFile.getAbsolutePath(),
550                 deviceNonplatSeappFile.getAbsolutePath());
551         pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
552         pb.redirectErrorStream(true);
553         Process p = pb.start();
554         p.waitFor();
555         BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream()));
556         String line;
557         StringBuilder errorString = new StringBuilder();
558         while ((line = result.readLine()) != null) {
559             errorString.append(line);
560             errorString.append("\n");
561         }
562         assertTrue("The seapp_contexts file was invalid:\n"
563                    + errorString, errorString.length() == 0);
564     }
565 
566     /**
567      * Asserts that the actual file contents starts with the expected file
568      * contents.
569      *
570      * @param expectedFile
571      *  The file with the expected contents.
572      * @param actualFile
573      *  The actual file being checked.
574      */
assertFileStartsWith(File expectedFile, File actualFile)575     private void assertFileStartsWith(File expectedFile, File actualFile) throws Exception {
576         BufferedReader expectedReader = new BufferedReader(new FileReader(expectedFile.getAbsolutePath()));
577         BufferedReader actualReader = new BufferedReader(new FileReader(actualFile.getAbsolutePath()));
578         String expectedLine, actualLine;
579         while ((expectedLine = expectedReader.readLine()) != null) {
580             actualLine = actualReader.readLine();
581             assertEquals("Lines do not match:", expectedLine, actualLine);
582         }
583     }
584 
585     /**
586      * Tests that the seapp_contexts file on the device contains
587      * the standard AOSP entries.
588      *
589      * @throws Exception
590      */
591     @CddTest(requirement="9.7")
testAospSeappContexts()592     public void testAospSeappContexts() throws Exception {
593 
594         /* obtain seapp_contexts file from running device */
595         devicePlatSeappFile = File.createTempFile("seapp_contexts", ".tmp");
596         devicePlatSeappFile.deleteOnExit();
597         if (!mDevice.pullFile("/system/etc/selinux/plat_seapp_contexts", devicePlatSeappFile)) {
598             mDevice.pullFile("/plat_seapp_contexts", devicePlatSeappFile);
599         }
600         /* retrieve the AOSP seapp_contexts file from jar */
601         aospSeappFile = copyResourceToTempFile("/plat_seapp_contexts");
602 
603         assertFileStartsWith(aospSeappFile, devicePlatSeappFile);
604     }
605 
606     /**
607      * Tests that the file_contexts.bin file on the device contains
608      * the standard AOSP entries.
609      *
610      * @throws Exception
611      */
612     @CddTest(requirement="9.7")
testAospFileContexts()613     public void testAospFileContexts() throws Exception {
614 
615         /* retrieve the checkfc executable from jar */
616         checkFc = copyResourceToTempFile("/checkfc");
617         checkFc.setExecutable(true);
618 
619         /* retrieve the AOSP file_contexts file from jar */
620         aospFcFile = copyResourceToTempFile("/plat_file_contexts");
621 
622         /* run checkfc -c plat_file_contexts file_contexts.bin */
623         ProcessBuilder pb = new ProcessBuilder(checkFc.getAbsolutePath(),
624                 "-c", aospFcFile.getAbsolutePath(),
625                 devicePlatFcFile.getAbsolutePath());
626         pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
627         pb.redirectErrorStream(true);
628         Process p = pb.start();
629         p.waitFor();
630         BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream()));
631         String line = result.readLine();
632         assertTrue("The file_contexts file did not include the AOSP entries:\n"
633                    + line + "\n",
634                    line.equals("equal") || line.equals("subset"));
635     }
636 
637     /**
638      * Tests that the property_contexts file on the device contains
639      * the standard AOSP entries.
640      *
641      * @throws Exception
642      */
643     @CddTest(requirement="9.7")
testAospPropertyContexts()644     public void testAospPropertyContexts() throws Exception {
645 
646         /* obtain property_contexts file from running device */
647         devicePcFile = File.createTempFile("plat_property_contexts", ".tmp");
648         devicePcFile.deleteOnExit();
649         // plat_property_contexts may be either in /system/etc/sepolicy or in /
650         if (!mDevice.pullFile("/system/etc/selinux/plat_property_contexts", devicePcFile)) {
651             mDevice.pullFile("/plat_property_contexts", devicePcFile);
652         }
653 
654         // Retrieve the AOSP property_contexts file from JAR.
655         // The location of this file in the JAR has nothing to do with the location of this file on
656         // Android devices. See build script of this CTS module.
657         aospPcFile = copyResourceToTempFile("/plat_property_contexts");
658 
659         assertFileStartsWith(aospPcFile, devicePcFile);
660     }
661 
662     /**
663      * Tests that the service_contexts file on the device contains
664      * the standard AOSP entries.
665      *
666      * @throws Exception
667      */
668     @CddTest(requirement="9.7")
testAospServiceContexts()669     public void testAospServiceContexts() throws Exception {
670 
671         /* obtain service_contexts file from running device */
672         deviceSvcFile = File.createTempFile("service_contexts", ".tmp");
673         deviceSvcFile.deleteOnExit();
674         if (!mDevice.pullFile("/system/etc/selinux/plat_service_contexts", deviceSvcFile)) {
675             mDevice.pullFile("/plat_service_contexts", deviceSvcFile);
676         }
677 
678         /* retrieve the AOSP service_contexts file from jar */
679         aospSvcFile = copyResourceToTempFile("/plat_service_contexts");
680 
681         assertFileStartsWith(aospSvcFile, deviceSvcFile);
682     }
683 
684     /**
685      * Tests that the file_contexts file(s) on the device is valid.
686      *
687      * @throws Exception
688      */
689     @CddTest(requirement="9.7")
testValidFileContexts()690     public void testValidFileContexts() throws Exception {
691 
692         /* retrieve the checkfc executable from jar */
693         checkFc = copyResourceToTempFile("/checkfc");
694         checkFc.setExecutable(true);
695 
696         /* combine plat and nonplat policies for testing */
697         File combinedFcFile = File.createTempFile("combined_file_context", ".tmp");
698         combinedFcFile.deleteOnExit();
699         appendTo(combinedFcFile.getAbsolutePath(), devicePlatFcFile.getAbsolutePath());
700         appendTo(combinedFcFile.getAbsolutePath(), deviceNonplatFcFile.getAbsolutePath());
701 
702         /* run checkfc sepolicy file_contexts */
703         ProcessBuilder pb = new ProcessBuilder(checkFc.getAbsolutePath(),
704                 devicePolicyFile.getAbsolutePath(),
705                 combinedFcFile.getAbsolutePath());
706         pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
707         pb.redirectErrorStream(true);
708         Process p = pb.start();
709         p.waitFor();
710         BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream()));
711         String line;
712         StringBuilder errorString = new StringBuilder();
713         while ((line = result.readLine()) != null) {
714             errorString.append(line);
715             errorString.append("\n");
716         }
717         assertTrue("file_contexts was invalid:\n"
718                    + errorString, errorString.length() == 0);
719     }
720 
721     /**
722      * Tests that the property_contexts file on the device is valid.
723      *
724      * @throws Exception
725      */
726     @CddTest(requirement="9.7")
testValidPropertyContexts()727     public void testValidPropertyContexts() throws Exception {
728 
729         /* retrieve the checkfc executable from jar */
730         File propertyInfoChecker = copyResourceToTempFile("/property_info_checker");
731         propertyInfoChecker.setExecutable(true);
732 
733         /* obtain property_contexts file from running device */
734         devicePcFile = File.createTempFile("property_contexts", ".tmp");
735         devicePcFile.deleteOnExit();
736         // plat_property_contexts may be either in /system/etc/sepolicy or in /
737         if (!mDevice.pullFile("/system/etc/selinux/plat_property_contexts", devicePcFile)) {
738             mDevice.pullFile("/plat_property_contexts", devicePcFile);
739         }
740 
741         /* run property_info_checker on property_contexts */
742         ProcessBuilder pb = new ProcessBuilder(propertyInfoChecker.getAbsolutePath(),
743                 devicePolicyFile.getAbsolutePath(),
744                 devicePcFile.getAbsolutePath());
745         pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
746         pb.redirectErrorStream(true);
747         Process p = pb.start();
748         p.waitFor();
749         BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream()));
750         String line;
751         StringBuilder errorString = new StringBuilder();
752         while ((line = result.readLine()) != null) {
753             errorString.append(line);
754             errorString.append("\n");
755         }
756         assertTrue("The property_contexts file was invalid:\n"
757                    + errorString, errorString.length() == 0);
758     }
759 
760     /**
761      * Tests that the service_contexts file on the device is valid.
762      *
763      * @throws Exception
764      */
765     @CddTest(requirement="9.7")
testValidServiceContexts()766     public void testValidServiceContexts() throws Exception {
767 
768         /* retrieve the checkfc executable from jar */
769         checkFc = copyResourceToTempFile("/checkfc");
770         checkFc.setExecutable(true);
771 
772         /* obtain service_contexts file from running device */
773         deviceSvcFile = File.createTempFile("service_contexts", ".tmp");
774         deviceSvcFile.deleteOnExit();
775         mDevice.pullFile("/service_contexts", deviceSvcFile);
776 
777         /* run checkfc -s on service_contexts */
778         ProcessBuilder pb = new ProcessBuilder(checkFc.getAbsolutePath(),
779                 "-s", devicePolicyFile.getAbsolutePath(),
780                 deviceSvcFile.getAbsolutePath());
781         pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
782         pb.redirectErrorStream(true);
783         Process p = pb.start();
784         p.waitFor();
785         BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream()));
786         String line;
787         StringBuilder errorString = new StringBuilder();
788         while ((line = result.readLine()) != null) {
789             errorString.append(line);
790             errorString.append("\n");
791         }
792         assertTrue("The service_contexts file was invalid:\n"
793                    + errorString, errorString.length() == 0);
794     }
795 
isMac()796     public static boolean isMac() {
797         String os = System.getProperty("os.name").toLowerCase();
798         return (os.startsWith("mac") || os.startsWith("darwin"));
799     }
800 
setupLibraries()801     private void setupLibraries() throws Exception {
802         // The host side binary tests are host OS specific. Use Linux
803         // libraries on Linux and Mac libraries on Mac.
804         if (isMac()) {
805             libsepolwrap = copyResourceToTempFile("/libsepolwrap.dylib");
806             libcpp = copyResourceToTempFile("/libc++.dylib");
807             libcpp.renameTo(new File(System.getProperty("java.io.tmpdir") + "/libc++.dylib"));
808         } else {
809             libsepolwrap = copyResourceToTempFile("/libsepolwrap.so");
810             libcpp = copyResourceToTempFile("/libc++.so");
811             libcpp.renameTo(new File(System.getProperty("java.io.tmpdir") + "/libc++.so"));
812         }
813         libsepolwrap.deleteOnExit();
814         libcpp.deleteOnExit();
815     }
816 
assertSepolicyTests(String test, String testExecutable)817     private void assertSepolicyTests(String test, String testExecutable) throws Exception {
818         setupLibraries();
819         sepolicyTests = copyResourceToTempFile(testExecutable);
820         sepolicyTests.setExecutable(true);
821         ProcessBuilder pb = new ProcessBuilder(
822                 sepolicyTests.getAbsolutePath(),
823                 "-l", libsepolwrap.getAbsolutePath(),
824                 "-f", devicePlatFcFile.getAbsolutePath(),
825                 "-f", deviceNonplatFcFile.getAbsolutePath(),
826                 "-p", devicePolicyFile.getAbsolutePath(),
827                 "--test", test);
828         Map<String, String> env = pb.environment();
829         if (isMac()) {
830             env.put("DYLD_LIBRARY_PATH", System.getProperty("java.io.tmpdir"));
831         } else {
832             env.put("LD_LIBRARY_PATH", System.getProperty("java.io.tmpdir"));
833         }
834         pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
835         pb.redirectErrorStream(true);
836         Process p = pb.start();
837         p.waitFor();
838         BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream()));
839         String line;
840         StringBuilder errorString = new StringBuilder();
841         while ((line = result.readLine()) != null) {
842             errorString.append(line);
843             errorString.append("\n");
844         }
845         assertTrue(errorString.toString(), errorString.length() == 0);
846     }
847 
848     /**
849      * Tests that all types on /data have the data_file_type attribute.
850      *
851      * @throws Exception
852      */
testDataTypeViolators()853     public void testDataTypeViolators() throws Exception {
854         assertSepolicyTests("TestDataTypeViolations", "/sepolicy_tests");
855     }
856 
857     /**
858      * Tests that all types in /proc have the proc_type attribute.
859      *
860      * @throws Exception
861      */
testProcTypeViolators()862     public void testProcTypeViolators() throws Exception {
863         assertSepolicyTests("TestProcTypeViolations", "/sepolicy_tests");
864     }
865 
866     /**
867      * Tests that all types in /sys have the sysfs_type attribute.
868      *
869      * @throws Exception
870      */
testSysfsTypeViolators()871     public void testSysfsTypeViolators() throws Exception {
872         assertSepolicyTests("TestSysfsTypeViolations", "/sepolicy_tests");
873     }
874 
875     /**
876      * Tests that all types on /vendor have the vendor_file_type attribute.
877      *
878      * @throws Exception
879      */
testVendorTypeViolators()880     public void testVendorTypeViolators() throws Exception {
881         assertSepolicyTests("TestVendorTypeViolations", "/sepolicy_tests");
882     }
883 
884     /**
885      * Tests that all domains with entrypoints on /system have the coredomain
886      * attribute, and that all domains with entrypoints on /vendor do not have the
887      * coredomain attribute.
888      *
889      * @throws Exception
890      */
testCoredomainViolators()891     public void testCoredomainViolators() throws Exception {
892         assertSepolicyTests("CoredomainViolations", "/treble_sepolicy_tests");
893     }
894 
895    /**
896      * Tests that the policy defines no booleans (runtime conditional policy).
897      *
898      * @throws Exception
899      */
900     @CddTest(requirement="9.7")
testNoBooleans()901     public void testNoBooleans() throws Exception {
902 
903         /* run sepolicy-analyze booleans check on policy file */
904         ProcessBuilder pb = new ProcessBuilder(sepolicyAnalyze.getAbsolutePath(),
905                 devicePolicyFile.getAbsolutePath(), "booleans");
906         pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
907         pb.redirectErrorStream(true);
908         Process p = pb.start();
909         p.waitFor();
910         BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream()));
911         String line;
912         StringBuilder errorString = new StringBuilder();
913         while ((line = result.readLine()) != null) {
914             errorString.append(line);
915             errorString.append("\n");
916         }
917         assertTrue("The policy contained booleans:\n"
918                    + errorString, errorString.length() == 0);
919     }
920 
921     /**
922      * Tests that important domain labels are being appropriately applied.
923      */
924 
925     /**
926      * Asserts that no processes are running in a domain.
927      *
928      * @param domain
929      *  The domain or SELinux context to check.
930      */
assertDomainEmpty(String domain)931     private void assertDomainEmpty(String domain) throws DeviceNotAvailableException {
932         List<ProcessDetails> procs = ProcessDetails.getProcMap(mDevice).get(domain);
933         String msg = "Expected no processes in SELinux domain \"" + domain + "\""
934             + " Found: \"" + procs + "\"";
935         assertNull(msg, procs);
936     }
937 
938     /**
939      * Asserts that a domain exists and that only one, well defined, process is
940      * running in that domain.
941      *
942      * @param domain
943      *  The domain or SELinux context to check.
944      * @param executable
945      *  The path of the executable or application package name.
946      */
assertDomainOne(String domain, String executable)947     private void assertDomainOne(String domain, String executable) throws DeviceNotAvailableException {
948         List<ProcessDetails> procs = ProcessDetails.getProcMap(mDevice).get(domain);
949         List<ProcessDetails> exeProcs = ProcessDetails.getExeMap(mDevice).get(executable);
950         String msg = "Expected 1 process in SELinux domain \"" + domain + "\""
951             + " Found \"" + procs + "\"";
952         assertNotNull(msg, procs);
953         assertEquals(msg, 1, procs.size());
954 
955         msg = "Expected executable \"" + executable + "\" in SELinux domain \"" + domain + "\""
956             + "Found: \"" + procs + "\"";
957         assertEquals(msg, executable, procs.get(0).procTitle);
958 
959         msg = "Expected 1 process with executable \"" + executable + "\""
960             + " Found \"" + procs + "\"";
961         assertNotNull(msg, exeProcs);
962         assertEquals(msg, 1, exeProcs.size());
963 
964         msg = "Expected executable \"" + executable + "\" in SELinux domain \"" + domain + "\""
965             + "Found: \"" + procs + "\"";
966         assertEquals(msg, domain, exeProcs.get(0).label);
967     }
968 
969     /**
970      * Asserts that a domain may exist. If a domain exists, the cardinality of
971      * the domain is verified to be 1 and that the correct process is running in
972      * that domain.
973      *
974      * @param domain
975      *  The domain or SELinux context to check.
976      * @param executable
977      *  The path of the executable or application package name.
978      */
assertDomainZeroOrOne(String domain, String executable)979     private void assertDomainZeroOrOne(String domain, String executable)
980         throws DeviceNotAvailableException {
981         List<ProcessDetails> procs = ProcessDetails.getProcMap(mDevice).get(domain);
982         List<ProcessDetails> exeProcs = ProcessDetails.getExeMap(mDevice).get(executable);
983 
984         if (procs != null) {
985             String msg = "Expected 1 process in SELinux domain \"" + domain + "\""
986             + " Found: \"" + procs + "\"";
987             assertEquals(msg, 1, procs.size());
988 
989             msg = "Expected executable \"" + executable + "\" in SELinux domain \"" + domain + "\""
990                 + "Found: \"" + procs.get(0) + "\"";
991             assertEquals(msg, executable, procs.get(0).procTitle);
992         }
993 
994         if (exeProcs != null) {
995             String msg = "Expected 1 process with executable \"" + executable + "\""
996             + " Found: \"" + procs + "\"";
997             assertEquals(msg, 1, exeProcs.size());
998 
999             msg = "Expected executable \"" + executable + "\" in SELinux domain \"" + domain + "\""
1000                 + "Found: \"" + procs.get(0) + "\"";
1001             assertEquals(msg, domain, exeProcs.get(0).label);
1002         }
1003     }
1004 
1005     /**
1006      * Asserts that a domain must exist, and that the cardinality is greater
1007      * than or equal to 1.
1008      *
1009      * @param domain
1010      *  The domain or SELinux context to check.
1011      * @param executables
1012      *  The path of the allowed executables or application package names.
1013      */
assertDomainN(String domain, String... executables)1014     private void assertDomainN(String domain, String... executables)
1015         throws DeviceNotAvailableException {
1016         List<ProcessDetails> procs = ProcessDetails.getProcMap(mDevice).get(domain);
1017         String msg = "Expected 1 or more processes in SELinux domain but found none.";
1018         assertNotNull(msg, procs);
1019 
1020         Set<String> execList = new HashSet<String>(Arrays.asList(executables));
1021 
1022         for (ProcessDetails p : procs) {
1023             msg = "Expected one of \"" + execList + "\" in SELinux domain \"" + domain + "\""
1024                 + " Found: \"" + p + "\"";
1025             assertTrue(msg, execList.contains(p.procTitle));
1026         }
1027 
1028         for (String exe : executables) {
1029             List<ProcessDetails> exeProcs = ProcessDetails.getExeMap(mDevice).get(exe);
1030 
1031             if (exeProcs != null) {
1032                 for (ProcessDetails p : exeProcs) {
1033                     msg = "Expected executable \"" + exe + "\" in SELinux domain \""
1034                         + domain + "\"" + " Found: \"" + p + "\"";
1035                     assertEquals(msg, domain, p.label);
1036                 }
1037             }
1038         }
1039     }
1040 
1041     /**
1042      * Asserts that a domain, if it exists, is only running the listed executables.
1043      *
1044      * @param domain
1045      *  The domain or SELinux context to check.
1046      * @param executables
1047      *  The path of the allowed executables or application package names.
1048      */
assertDomainHasExecutable(String domain, String... executables)1049     private void assertDomainHasExecutable(String domain, String... executables)
1050         throws DeviceNotAvailableException {
1051         List<ProcessDetails> procs = ProcessDetails.getProcMap(mDevice).get(domain);
1052 
1053         if (procs != null) {
1054             Set<String> execList = new HashSet<String>(Arrays.asList(executables));
1055 
1056             for (ProcessDetails p : procs) {
1057                 String msg = "Expected one of \"" + execList + "\" in SELinux domain \""
1058                     + domain + "\"" + " Found: \"" + p + "\"";
1059                 assertTrue(msg, execList.contains(p.procTitle));
1060             }
1061         }
1062     }
1063 
1064     /**
1065      * Asserts that an executable exists and is only running in the listed domains.
1066      *
1067      * @param executable
1068      *  The path of the executable to check.
1069      * @param domains
1070      *  The list of allowed domains.
1071      */
assertExecutableHasDomain(String executable, String... domains)1072     private void assertExecutableHasDomain(String executable, String... domains)
1073         throws DeviceNotAvailableException {
1074         List<ProcessDetails> exeProcs = ProcessDetails.getExeMap(mDevice).get(executable);
1075         Set<String> domainList = new HashSet<String>(Arrays.asList(domains));
1076 
1077         String msg = "Expected 1 or more processes for executable \"" + executable + "\".";
1078         assertNotNull(msg, exeProcs);
1079 
1080         for (ProcessDetails p : exeProcs) {
1081             msg = "Expected one of  \"" + domainList + "\" for executable \"" + executable
1082                     + "\"" + " Found: \"" + p.label + "\"";
1083             assertTrue(msg, domainList.contains(p.label));
1084         }
1085     }
1086 
1087     /* Init is always there */
1088     @CddTest(requirement="9.7")
testInitDomain()1089     public void testInitDomain() throws DeviceNotAvailableException {
1090         assertDomainHasExecutable("u:r:init:s0", "/init");
1091         assertDomainHasExecutable("u:r:vendor_init:s0", "/init");
1092         assertExecutableHasDomain("/init", "u:r:init:s0", "u:r:vendor_init:s0");
1093     }
1094 
1095     /* Ueventd is always there */
1096     @CddTest(requirement="9.7")
testUeventdDomain()1097     public void testUeventdDomain() throws DeviceNotAvailableException {
1098         assertDomainOne("u:r:ueventd:s0", "/sbin/ueventd");
1099     }
1100 
1101     /* healthd may or may not exist */
1102     @CddTest(requirement="9.7")
testHealthdDomain()1103     public void testHealthdDomain() throws DeviceNotAvailableException {
1104         assertDomainZeroOrOne("u:r:healthd:s0", "/system/bin/healthd");
1105     }
1106 
1107     /* Servicemanager is always there */
1108     @CddTest(requirement="9.7")
testServicemanagerDomain()1109     public void testServicemanagerDomain() throws DeviceNotAvailableException {
1110         assertDomainOne("u:r:servicemanager:s0", "/system/bin/servicemanager");
1111     }
1112 
1113     /* Vold is always there */
1114     @CddTest(requirement="9.7")
testVoldDomain()1115     public void testVoldDomain() throws DeviceNotAvailableException {
1116         assertDomainOne("u:r:vold:s0", "/system/bin/vold");
1117     }
1118 
1119     /* netd is always there */
1120     @CddTest(requirement="9.7")
testNetdDomain()1121     public void testNetdDomain() throws DeviceNotAvailableException {
1122         assertDomainN("u:r:netd:s0", "/system/bin/netd", "/system/bin/iptables-restore", "/system/bin/ip6tables-restore");
1123     }
1124 
1125     /* Surface flinger is always there */
1126     @CddTest(requirement="9.7")
testSurfaceflingerDomain()1127     public void testSurfaceflingerDomain() throws DeviceNotAvailableException {
1128         assertDomainOne("u:r:surfaceflinger:s0", "/system/bin/surfaceflinger");
1129     }
1130 
1131     /* Zygote is always running */
1132     @CddTest(requirement="9.7")
testZygoteDomain()1133     public void testZygoteDomain() throws DeviceNotAvailableException {
1134         assertDomainN("u:r:zygote:s0", "zygote", "zygote64");
1135     }
1136 
1137     /* Checks drmserver for devices that require it */
1138     @CddTest(requirement="9.7")
testDrmServerDomain()1139     public void testDrmServerDomain() throws DeviceNotAvailableException {
1140         assertDomainZeroOrOne("u:r:drmserver:s0", "/system/bin/drmserver");
1141     }
1142 
1143     /* Installd is always running */
1144     @CddTest(requirement="9.7")
testInstalldDomain()1145     public void testInstalldDomain() throws DeviceNotAvailableException {
1146         assertDomainOne("u:r:installd:s0", "/system/bin/installd");
1147     }
1148 
1149     /* keystore is always running */
1150     @CddTest(requirement="9.7")
testKeystoreDomain()1151     public void testKeystoreDomain() throws DeviceNotAvailableException {
1152         assertDomainOne("u:r:keystore:s0", "/system/bin/keystore");
1153     }
1154 
1155     /* System server better be running :-P */
1156     @CddTest(requirement="9.7")
testSystemServerDomain()1157     public void testSystemServerDomain() throws DeviceNotAvailableException {
1158         assertDomainOne("u:r:system_server:s0", "system_server");
1159     }
1160 
1161     /* Watchdogd may or may not be there */
1162     @CddTest(requirement="9.7")
testWatchdogdDomain()1163     public void testWatchdogdDomain() throws DeviceNotAvailableException {
1164         assertDomainZeroOrOne("u:r:watchdogd:s0", "/sbin/watchdogd");
1165     }
1166 
1167     /* logd may or may not be there */
1168     @CddTest(requirement="9.7")
testLogdDomain()1169     public void testLogdDomain() throws DeviceNotAvailableException {
1170         assertDomainZeroOrOne("u:r:logd:s0", "/system/bin/logd");
1171     }
1172 
1173     /* lmkd may or may not be there */
1174     @CddTest(requirement="9.7")
testLmkdDomain()1175     public void testLmkdDomain() throws DeviceNotAvailableException {
1176         assertDomainZeroOrOne("u:r:lmkd:s0", "/system/bin/lmkd");
1177     }
1178 
1179     /* Wifi may be off so cardinality of 0 or 1 is ok */
1180     @CddTest(requirement="9.7")
testWpaDomain()1181     public void testWpaDomain() throws DeviceNotAvailableException {
1182         assertDomainZeroOrOne("u:r:wpa:s0", "/system/bin/wpa_supplicant");
1183     }
1184 
1185     /*
1186      * Nothing should be running in this domain, cardinality test is all thats
1187      * needed
1188      */
1189     @CddTest(requirement="9.7")
testInitShellDomain()1190     public void testInitShellDomain() throws DeviceNotAvailableException {
1191         assertDomainEmpty("u:r:init_shell:s0");
1192     }
1193 
1194     /*
1195      * Nothing should be running in this domain, cardinality test is all thats
1196      * needed
1197      */
1198     @CddTest(requirement="9.7")
testRecoveryDomain()1199     public void testRecoveryDomain() throws DeviceNotAvailableException {
1200         assertDomainEmpty("u:r:recovery:s0");
1201     }
1202 
1203     /*
1204      * Nothing should be running in this domain, cardinality test is all thats
1205      * needed
1206      */
1207     @CddTest(requirement="9.7")
1208     @RestrictedBuildTest
testSuDomain()1209     public void testSuDomain() throws DeviceNotAvailableException {
1210         assertDomainEmpty("u:r:su:s0");
1211     }
1212 
1213     /*
1214      * All kthreads should be in kernel context.
1215      */
1216     @CddTest(requirement="9.7")
testKernelDomain()1217     public void testKernelDomain() throws DeviceNotAvailableException {
1218         String domain = "u:r:kernel:s0";
1219         List<ProcessDetails> procs = ProcessDetails.getProcMap(mDevice).get(domain);
1220         if (procs != null) {
1221             for (ProcessDetails p : procs) {
1222                 assertTrue("Non Kernel thread \"" + p + "\" found!", p.isKernel());
1223             }
1224         }
1225     }
1226 
1227     private static class ProcessDetails {
1228         public String label;
1229         public String user;
1230         public int pid;
1231         public int ppid;
1232         public String procTitle;
1233 
1234         private static HashMap<String, ArrayList<ProcessDetails>> procMap;
1235         private static HashMap<String, ArrayList<ProcessDetails>> exeMap;
1236         private static int kernelParentThreadpid = -1;
1237 
ProcessDetails(String label, String user, int pid, int ppid, String procTitle)1238         ProcessDetails(String label, String user, int pid, int ppid, String procTitle) {
1239             this.label = label;
1240             this.user = user;
1241             this.pid = pid;
1242             this.ppid = ppid;
1243             this.procTitle = procTitle;
1244         }
1245 
1246         @Override
toString()1247         public String toString() {
1248             return "label: " + label
1249                     + " user: " + user
1250                     + " pid: " + pid
1251                     + " ppid: " + ppid
1252                     + " cmd: " + procTitle;
1253         }
1254 
1255 
createProcMap(ITestDevice tDevice)1256         private static void createProcMap(ITestDevice tDevice) throws DeviceNotAvailableException {
1257 
1258             /* take the output of a ps -Z to do our analysis */
1259             CollectingOutputReceiver psOut = new CollectingOutputReceiver();
1260             // TODO: remove "toybox" below and just run "ps"
1261             tDevice.executeShellCommand("toybox ps -A -o label,user,pid,ppid,cmdline", psOut);
1262             String psOutString = psOut.getOutput();
1263             Pattern p = Pattern.compile(
1264                     "^([\\w_:]+)\\s+([\\w_]+)\\s+(\\d+)\\s+(\\d+)\\s+(\\p{Graph}+)(\\s\\p{Graph}+)*\\s*$",
1265                     Pattern.MULTILINE);
1266             Matcher m = p.matcher(psOutString);
1267             procMap = new HashMap<String, ArrayList<ProcessDetails>>();
1268             exeMap = new HashMap<String, ArrayList<ProcessDetails>>();
1269             while(m.find()) {
1270                 String domainLabel = m.group(1);
1271                 String user = m.group(2);
1272                 int pid = Integer.parseInt(m.group(3));
1273                 int ppid = Integer.parseInt(m.group(4));
1274                 String procTitle = m.group(5);
1275                 ProcessDetails proc = new ProcessDetails(domainLabel, user, pid, ppid, procTitle);
1276                 if (procMap.get(domainLabel) == null) {
1277                     procMap.put(domainLabel, new ArrayList<ProcessDetails>());
1278                 }
1279                 procMap.get(domainLabel).add(proc);
1280                 if (procTitle.equals("[kthreadd]") && ppid == 0) {
1281                     kernelParentThreadpid = pid;
1282                 }
1283                 if (exeMap.get(procTitle) == null) {
1284                     exeMap.put(procTitle, new ArrayList<ProcessDetails>());
1285                 }
1286                 exeMap.get(procTitle).add(proc);
1287             }
1288         }
1289 
getProcMap(ITestDevice tDevice)1290         public static HashMap<String, ArrayList<ProcessDetails>> getProcMap(ITestDevice tDevice)
1291                 throws DeviceNotAvailableException{
1292             if (procMap == null) {
1293                 createProcMap(tDevice);
1294             }
1295             return procMap;
1296         }
1297 
getExeMap(ITestDevice tDevice)1298         public static HashMap<String, ArrayList<ProcessDetails>> getExeMap(ITestDevice tDevice)
1299                 throws DeviceNotAvailableException{
1300             if (exeMap == null) {
1301                 createProcMap(tDevice);
1302             }
1303             return exeMap;
1304         }
1305 
isKernel()1306         public boolean isKernel() {
1307             return (pid == kernelParentThreadpid || ppid == kernelParentThreadpid);
1308         }
1309     }
1310 }
1311