• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.content.Context;
20 import android.content.res.AssetManager;
21 import android.security.cts.SELinuxPolicyRule;
22 import android.test.AndroidTestCase;
23 
24 import junit.framework.TestCase;
25 import org.xmlpull.v1.XmlPullParserException;
26 
27 import java.io.File;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.util.ArrayList;
31 import java.util.List;
32 import java.util.Collection;
33 import java.util.HashMap;
34 import java.util.Map;
35 import java.util.HashSet;
36 
37 /**
38  * Verify that the SELinux configuration is sane.
39  */
40 public class SELinuxTest extends AndroidTestCase {
41 
42     static {
43         System.loadLibrary("ctssecurity_jni");
44     }
45 
testMyJni()46     public void testMyJni() {
47         try {
48             checkSELinuxAccess(null, null, null, null, null);
49             fail("checkSELinuxAccess should have thrown");
50         } catch (NullPointerException e) {
51             // expected
52         }
53         try {
54             checkSELinuxContext(null);
55             fail("checkSELinuxContext should have thrown");
56         } catch (NullPointerException e) {
57             // expected
58         }
59     }
60 
testCheckAccessSane()61     public void testCheckAccessSane() {
62         assertFalse(checkSELinuxAccess("a", "b", "c", "d", "e"));
63     }
64 
testCheckContextSane()65     public void testCheckContextSane() {
66         assertFalse(checkSELinuxContext("a"));
67     }
68 
testZygoteContext()69     public void testZygoteContext() {
70         assertTrue(checkSELinuxContext("u:r:zygote:s0"));
71     }
72 
testZygote()73     public void testZygote() {
74         assertFalse(checkSELinuxAccess("u:r:zygote:s0", "u:object_r:runas_exec:s0", "file", "getattr", "/system/bin/run-as"));
75         // Also check init, just as a sanity check (init is unconfined, so it should pass)
76         assertTrue(checkSELinuxAccess("u:r:init:s0", "u:object_r:runas_exec:s0", "file", "getattr", "/system/bin/run-as"));
77     }
78 
testNoBooleans()79     public void testNoBooleans() throws Exception {
80         // Intentionally not using JNI bindings to keep things simple
81         File[] files = new File("/sys/fs/selinux/booleans/").listFiles();
82         assertEquals(0, files.length);
83     }
84 
85     /**
86      * Verify all of the rules described by the selinux_policy.xml file are in effect.  Allow rules
87      * should return access granted, and Neverallow should return access denied.  All checks are run
88      * and then a list of specific failed checks is printed.
89      */
testSELinuxPolicyFile()90     public void testSELinuxPolicyFile() throws IOException, XmlPullParserException {
91         List<String> failedChecks = new ArrayList<String>();
92         Map<String, Boolean> contextsCache = new HashMap<String, Boolean>();
93         int invalidContextsCount = 0;
94         int totalChecks = 0;
95         int totalFailedChecks = 0;
96         AssetManager assets = mContext.getAssets();
97         InputStream in = assets.open("selinux_policy.xml");
98         Collection<SELinuxPolicyRule> rules = SELinuxPolicyRule.readRulesFile(in);
99         for (SELinuxPolicyRule r : rules) {
100             PolicyFileTestResult result = runRuleChecks(r, contextsCache);
101             totalChecks += result.numTotalChecks;
102             if (result.numFailedChecks != 0) {
103                 totalFailedChecks += result.numFailedChecks;
104 
105                 /* print failures to log, so as not to run OOM in the event of large policy mismatch,
106                    but record actual rule type and number */
107                 failedChecks.add("SELinux avc rule " + r.type + r.name + " failed " + result.numFailedChecks +
108                         " out of " + result.numTotalChecks + " checks.");
109                 for (String k : result.failedChecks) {
110                     System.out.println(r.type + r.name + " failed " + k);
111                 }
112             }
113         }
114         if (totalFailedChecks != 0) {
115 
116             /* print out failed rules, just the rule number and type */
117             for (String k : failedChecks) {
118                 System.out.println(k);
119             }
120             System.out.println("Failed SELinux Policy Test: " + totalFailedChecks + " failed out of " + totalChecks);
121         }
122         for (String k : contextsCache.keySet()) {
123             if (!contextsCache.get(k)) {
124                 invalidContextsCount++;
125                 System.out.println("Invalid SELinux context encountered: " + k);
126             }
127         }
128         System.out.println("SELinuxPolicy Test Encountered: " + invalidContextsCount + " missing contexts out of " + contextsCache.size());
129         assertTrue(totalFailedChecks == 0);
130     }
131 
132     /**
133      * A class for containing all of the results we care to know from checking each SELinux rule
134      */
135     private class PolicyFileTestResult {
136         private int numTotalChecks;
137         private int numFailedChecks;
138         private List<String> failedChecks = new ArrayList<String>();
139     }
140 
runRuleChecks(SELinuxPolicyRule r, Map<String, Boolean> contextsCache)141     private PolicyFileTestResult runRuleChecks(SELinuxPolicyRule r, Map<String, Boolean> contextsCache) {
142         PolicyFileTestResult result = new PolicyFileTestResult();
143 
144         /* run checks by going through every possible 4-tuple specified by rule.  Start with class
145            and perm to allow early-exit based on context. */
146         for (String c : r.obj_classes.keySet()) {
147             for (String p : r.obj_classes.get(c)) {
148                 for (String s : r.source_types) {
149 
150                     /* check source context */
151                     String source_context = createAvcContext(s, false, c, p);
152                     if (!contextsCache.containsKey(source_context)) {
153                         contextsCache.put(source_context, checkSELinuxContext(source_context));
154                     }
155                     if (!contextsCache.get(source_context)) {
156                         continue;
157                     }
158                     for (String t : r.target_types) {
159                         if (t.equals("self")) {
160                             t = s;
161                         }
162 
163                         /* check target context */
164                         String target_context = createAvcContext(t, true, c, p);
165                         if (!contextsCache.containsKey(target_context)) {
166                             contextsCache.put(target_context, checkSELinuxContext(target_context));
167                         }
168                         if (!contextsCache.get(target_context)) {
169                             continue;
170                         }
171                         boolean canAccess  = checkSELinuxAccess(source_context, target_context,
172                                 c, p, "");
173                         result.numTotalChecks++;
174                         if ((r.type.equals("allow") && !canAccess)
175                                 || (r.type.equals("neverallow") && canAccess)) {
176                             String failureNotice = s + ", " + t + ", " + c + ", " + p;
177                             result.numFailedChecks++;
178                             result.failedChecks.add(failureNotice);
179                         }
180                     }
181                 }
182             }
183         }
184         return result;
185     }
186 
187     /* createAvcContext - currently uses class type and perm to determine user, role and mls values.
188      *
189      * @param target - false if source domain, true if target.
190      */
createAvcContext(String domain, boolean target, String obj_class, String perm)191     private String createAvcContext(String domain, boolean target,
192             String obj_class, String perm) {
193         String usr = "u";
194         String role;
195 
196         /* understand role labeling better */
197         if (obj_class.equals("filesystem") && perm.equals("associate")) {
198             role = "object_r";
199         } else if(obj_class.equals("process") || obj_class.endsWith("socket")) {
200             role = "r";
201         } else if (target) {
202             role = "object_r";
203         } else {
204             role = "r";
205         }
206         return String.format("%s:%s:%s:s0", usr, role, domain);
207     }
208 
checkSELinuxAccess(String scon, String tcon, String tclass, String perm, String extra)209     private static native boolean checkSELinuxAccess(String scon, String tcon, String tclass, String perm, String extra);
210 
checkSELinuxContext(String con)211     private static native boolean checkSELinuxContext(String con);
212 }
213