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