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