1 package android.security; 2 3 import android.system.Os; 4 import android.test.AndroidTestCase; 5 import java.io.BufferedReader; 6 import java.io.FileReader; 7 import java.io.IOException; 8 import java.util.Scanner; 9 import java.io.File; 10 import java.io.IOException; 11 import java.net.NetworkInterface; 12 import java.util.Collections; 13 import java.util.regex.Pattern; 14 import java.util.regex.Matcher; 15 16 abstract class SELinuxTargetSdkTestBase extends AndroidTestCase 17 { 18 static { 19 System.loadLibrary("ctsselinux_jni"); 20 } 21 getFile(String filename)22 protected static String getFile(String filename) throws IOException { 23 BufferedReader in = null; 24 try { 25 in = new BufferedReader(new FileReader(filename)); 26 return in.readLine().trim(); 27 } finally { 28 if (in != null) { 29 in.close(); 30 } 31 } 32 } 33 getProperty(String property)34 protected static String getProperty(String property) 35 throws IOException { 36 Process process = new ProcessBuilder("getprop", property).start(); 37 Scanner scanner = null; 38 String line = ""; 39 try { 40 scanner = new Scanner(process.getInputStream()); 41 line = scanner.nextLine(); 42 } finally { 43 if (scanner != null) { 44 scanner.close(); 45 } 46 } 47 return line; 48 } 49 50 /** 51 * Verify that net.dns properties may not be read 52 */ noDns()53 protected static void noDns() throws IOException { 54 String[] dnsProps = {"net.dns1", "net.dns2", "net.dns3", "net.dns4"}; 55 for(int i = 0; i < dnsProps.length; i++) { 56 String dns = getProperty(dnsProps[i]); 57 assertEquals("DNS properties may not be readable by apps past " + 58 "targetSdkVersion 26", "", dns); 59 } 60 } 61 checkNetlinkRouteGetlink(boolean expectAllowed)62 protected static void checkNetlinkRouteGetlink(boolean expectAllowed) throws IOException { 63 if (!expectAllowed) { 64 assertEquals( 65 "RTM_GETLINK is not allowed on a netlink route sockets. Verify that the" 66 + " following patch has been applied to your kernel: " 67 + "https://android-review.googlesource.com/q/I7b44ce60ad98f858c412722d41b9842f8577151f", 68 13, 69 checkNetlinkRouteGetlink()); 70 } else { 71 assertEquals( 72 "RTM_GETLINK should be allowed netlink route sockets for apps with " 73 + "targetSdkVersion <= Q", 74 -1, 75 checkNetlinkRouteGetlink()); 76 } 77 } 78 checkNetlinkRouteBind(boolean expectAllowed)79 protected static void checkNetlinkRouteBind(boolean expectAllowed) throws IOException { 80 if (!expectAllowed) { 81 assertEquals( 82 "Bind() is not allowed on a netlink route sockets", 83 13, 84 checkNetlinkRouteBind()); 85 } else { 86 assertEquals( 87 "Bind() should succeed for netlink route sockets for apps with " 88 + "targetSdkVersion <= Q", 89 -1, 90 checkNetlinkRouteBind()); 91 } 92 } 93 94 /** 95 * Check expectations of being able to read/execute dex2oat. 96 */ checkDex2oatAccess(boolean expectedAllowed)97 protected static void checkDex2oatAccess(boolean expectedAllowed) throws Exception { 98 // Check the dex2oat binary in its current and legacy locations. 99 String[] locations = {"/apex/com.android.art/bin", 100 "/apex/com.android.runtime/bin", 101 "/system/bin"}; 102 for (String loc : locations) { 103 File dex2oatBinary = new File(loc + "/dex2oat"); 104 if (dex2oatBinary.exists()) { 105 checkDex2oatBinaryAccess(dex2oatBinary, expectedAllowed); 106 } 107 } 108 } 109 checkDex2oatBinaryAccess(File dex2oatBinary, boolean expectedAllowed)110 private static void checkDex2oatBinaryAccess(File dex2oatBinary, boolean expectedAllowed) 111 throws Exception { 112 // Check permissions. 113 assertEquals(expectedAllowed, dex2oatBinary.canRead()); 114 assertEquals(expectedAllowed, dex2oatBinary.canExecute()); 115 116 // Try to execute dex2oat. 117 try { 118 Runtime rt = Runtime.getRuntime(); 119 Process p = rt.exec(dex2oatBinary.getAbsolutePath()); 120 p.waitFor(); 121 assertEquals(expectedAllowed, true); 122 } catch (IOException ex) { 123 assertEquals(expectedAllowed, false); 124 assertEquals(ex.getMessage(), 125 "Cannot run program \"" + dex2oatBinary.getAbsolutePath() + 126 "\": error=13, Permission denied"); 127 } 128 } 129 130 /** 131 * Verify that selinux context is the expected domain based on 132 * targetSdkVersion, 133 */ appDomainContext(String contextRegex, String errorMsg)134 protected void appDomainContext(String contextRegex, String errorMsg) throws IOException { 135 Pattern p = Pattern.compile(contextRegex); 136 Matcher m = p.matcher(getFile("/proc/self/attr/current")); 137 String context = getFile("/proc/self/attr/current"); 138 String msg = errorMsg + context; 139 assertTrue(msg, m.matches()); 140 } 141 142 /** 143 * Verify that selinux context is the expected type based on 144 * targetSdkVersion, 145 */ appDataContext(String contextRegex, String errorMsg)146 protected void appDataContext(String contextRegex, String errorMsg) throws Exception { 147 Pattern p = Pattern.compile(contextRegex); 148 File appDataDir = getContext().getFilesDir(); 149 Matcher m = p.matcher(getFileContext(appDataDir.getAbsolutePath())); 150 String context = getFileContext(appDataDir.getAbsolutePath()); 151 String msg = errorMsg + context; 152 assertTrue(msg, m.matches()); 153 } 154 canExecuteFromHomeDir()155 protected boolean canExecuteFromHomeDir() throws Exception { 156 File appDataDir = getContext().getFilesDir(); 157 File temp = File.createTempFile("badbin", "exe", appDataDir); 158 temp.deleteOnExit(); 159 String path = temp.getPath(); 160 Os.chmod(path, 0700); 161 try { 162 Process process = new ProcessBuilder(path).start(); 163 } catch (IOException e) { 164 return !e.toString().contains("Permission denied"); 165 } finally { 166 temp.delete(); 167 } 168 return true; 169 } 170 171 /** 172 * Verify that apps having targetSdkVersion <= 29 are able to see MAC 173 * addresses of ethernet devices. 174 * The counterpart of this test (testing for targetSdkVersion > 29) is 175 * {@link libcore.java.net.NetworkInterfaceTest#testGetHardwareAddress_returnsNull()}. 176 */ checkNetworkInterface_returnsHardwareAddresses()177 protected static void checkNetworkInterface_returnsHardwareAddresses() throws Exception { 178 assertNotNull(NetworkInterface.getNetworkInterfaces()); 179 for (NetworkInterface nif : Collections.list(NetworkInterface.getNetworkInterfaces())) { 180 if (isEthernet(nif.getName())) { 181 assertEquals(6, nif.getHardwareAddress().length); 182 } 183 } 184 } 185 186 /** 187 * Checks whether a network interface is an ethernet interface. 188 */ 189 private static Pattern ethernetNamePattern = Pattern.compile("^(eth|wlan)[0-9]+$"); isEthernet(String ifName)190 private static boolean isEthernet(String ifName) throws Exception { 191 return ethernetNamePattern.matcher(ifName).matches(); 192 } 193 checkNetlinkRouteGetlink()194 private static final native int checkNetlinkRouteGetlink(); checkNetlinkRouteBind()195 private static final native int checkNetlinkRouteBind(); getFileContext(String path)196 private static final native String getFileContext(String path); 197 } 198