1 /* 2 * Copyright (C) 2012 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.os.IBinder; 20 import android.os.DeadObjectException; 21 import android.os.TransactionTooLargeException; 22 import android.platform.test.annotations.SecurityTest; 23 import android.test.AndroidTestCase; 24 import android.util.Log; 25 26 import java.io.BufferedReader; 27 import java.io.File; 28 import java.io.FileInputStream; 29 import java.io.FileOutputStream; 30 import java.io.InputStreamReader; 31 import java.lang.reflect.InvocationTargetException; 32 import java.util.ArrayList; 33 34 /** 35 * Verifies that permissions are enforced on various system services. 36 */ 37 @SecurityTest 38 public class ServicePermissionsTest extends AndroidTestCase { 39 40 private static final String TAG = "ServicePermissionsTest"; 41 42 private File mTempFile; 43 44 @Override 45 protected void setUp() throws Exception { 46 super.setUp(); 47 mTempFile = new File(getContext().getCacheDir(), "CTS_DUMP"); 48 } 49 50 @Override 51 protected void tearDown() throws Exception { 52 try { 53 mTempFile.delete(); 54 } finally { 55 super.tearDown(); 56 } 57 } 58 59 /** 60 * Test that {@link IBinder#dump(java.io.FileDescriptor, String[])} on all 61 * registered system services checks if caller holds 62 * {@link android.Manifest.permission#DUMP} permission. 63 */ 64 public void testDumpProtected() throws Exception { 65 66 String[] services = null; 67 try { 68 services = (String[]) Class.forName("android.os.ServiceManager") 69 .getDeclaredMethod("listServices").invoke(null); 70 } catch (ClassCastException e) { 71 } catch (ClassNotFoundException e) { 72 } catch (NoSuchMethodException e) { 73 } catch (InvocationTargetException e) { 74 } catch (IllegalAccessException e) { 75 } 76 77 if ((services == null) || (services.length == 0)) { 78 Log.w(TAG, "No registered services, that's odd"); 79 return; 80 } 81 82 for (String service : services) { 83 mTempFile.delete(); 84 85 IBinder serviceBinder = null; 86 try { 87 serviceBinder = (IBinder) Class.forName("android.os.ServiceManager") 88 .getDeclaredMethod("getService", String.class).invoke(null, service); 89 } catch (ClassCastException e) { 90 } catch (ClassNotFoundException e) { 91 } catch (NoSuchMethodException e) { 92 } catch (InvocationTargetException e) { 93 } catch (IllegalAccessException e) { 94 } 95 96 if (serviceBinder == null) { 97 Log.w(TAG, "Missing service " + service); 98 continue; 99 } 100 101 Log.d(TAG, "Dumping service " + service); 102 final FileOutputStream out = new FileOutputStream(mTempFile); 103 try { 104 serviceBinder.dump(out.getFD(), new String[0]); 105 } catch (SecurityException e) { 106 String msg = e.getMessage(); 107 if ((msg == null) || msg.contains("android.permission.DUMP")) { 108 // Service correctly checked for DUMP permission, yay 109 } else { 110 // Service is throwing about something else; they're 111 // probably not checking for DUMP. 112 throw e; 113 } 114 } catch (TransactionTooLargeException | DeadObjectException e) { 115 // SELinux likely prevented the dump - assume safe 116 continue; 117 } finally { 118 out.close(); 119 } 120 121 // Verify that dump produced minimal output 122 final BufferedReader reader = new BufferedReader( 123 new InputStreamReader(new FileInputStream(mTempFile))); 124 final ArrayList<String> lines = new ArrayList<String>(); 125 try { 126 String line; 127 while ((line = reader.readLine()) != null) { 128 lines.add(line); 129 Log.v(TAG, "--> " + line); 130 } 131 } finally { 132 reader.close(); 133 } 134 135 if (lines.size() > 1) { 136 fail("dump() for " + service + " produced several lines of output; this " 137 + "may be leaking sensitive data. At most, services should emit a " 138 + "single line when the caller doesn't have DUMP permission."); 139 } 140 141 if (lines.size() == 1) { 142 String message = lines.get(0); 143 if (!message.contains("Permission Denial") && 144 !message.contains("android.permission.DUMP")) { 145 fail("dump() for " + service + " produced a single line which didn't " 146 + "reference a permission; it may be leaking sensitive data."); 147 } 148 } 149 } 150 } 151 } 152