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