• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 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.appsecurity.cts;
18 
19 import static android.appsecurity.cts.Utils.waitForBootCompleted;
20 
21 import static org.junit.Assert.assertNotNull;
22 import static org.junit.Assert.assertTrue;
23 
24 import android.platform.test.annotations.AppModeFull;
25 import android.platform.test.annotations.AppModeInstant;
26 
27 import com.android.ddmlib.Log;
28 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
29 
30 import org.junit.Before;
31 import org.junit.Test;
32 import org.junit.runner.RunWith;
33 
34 /**
35  * Set of tests that verify various security checks involving multiple apps are
36  * properly enforced.
37  */
38 @RunWith(DeviceJUnit4ClassRunner.class)
39 public class AppSecurityTests extends BaseAppSecurityTest {
40 
41     // testAppUpgradeDifferentCerts constants
42     private static final String SIMPLE_APP_APK = "CtsSimpleAppInstall.apk";
43     private static final String SIMPLE_APP_PKG = "com.android.cts.simpleappinstall";
44     private static final String SIMPLE_APP_DIFF_CERT_APK = "CtsSimpleAppInstallDiffCert.apk";
45 
46     // testAppFailAccessPrivateData constants
47     private static final String APP_WITH_DATA_APK = "CtsAppWithData.apk";
48     private static final String APP_WITH_DATA_PKG = "com.android.cts.appwithdata";
49     private static final String APP_WITH_DATA_CLASS =
50             "com.android.cts.appwithdata.CreatePrivateDataTest";
51     private static final String APP_WITH_DATA_CREATE_METHOD =
52             "testCreatePrivateData";
53     private static final String APP_WITH_DATA_CHECK_NOEXIST_METHOD =
54             "testEnsurePrivateDataNotExist";
55     private static final String APP_ACCESS_DATA_APK = "CtsAppAccessData.apk";
56     private static final String APP_ACCESS_DATA_PKG = "com.android.cts.appaccessdata";
57 
58     // testInstrumentationDiffCert constants
59     private static final String TARGET_INSTRUMENT_APK = "CtsTargetInstrumentationApp.apk";
60     private static final String TARGET_INSTRUMENT_PKG = "com.android.cts.targetinstrumentationapp";
61     private static final String INSTRUMENT_DIFF_CERT_APK = "CtsInstrumentationAppDiffCert.apk";
62     private static final String INSTRUMENT_DIFF_CERT_PKG =
63             "com.android.cts.instrumentationdiffcertapp";
64     private static final String INSTRUMENT_DIFF_CERT_CLASS =
65             "com.android.cts.instrumentationdiffcertapp.InstrumentationFailToRunTest";
66 
67     // testPermissionDiffCert constants
68     private static final String DECLARE_PERMISSION_APK = "CtsPermissionDeclareApp.apk";
69     private static final String DECLARE_PERMISSION_PKG = "com.android.cts.permissiondeclareapp";
70     private static final String DECLARE_PERMISSION_COMPAT_APK = "CtsPermissionDeclareAppCompat.apk";
71     private static final String DECLARE_PERMISSION_COMPAT_PKG = "com.android.cts.permissiondeclareappcompat";
72 
73     private static final String PERMISSION_DIFF_CERT_APK = "CtsUsePermissionDiffCert.apk";
74     private static final String PERMISSION_DIFF_CERT_PKG =
75         "com.android.cts.usespermissiondiffcertapp";
76 
77     private static final String DUPLICATE_DECLARE_PERMISSION_APK =
78             "CtsDuplicatePermissionDeclareApp.apk";
79     private static final String DUPLICATE_DECLARE_PERMISSION_PKG =
80             "com.android.cts.duplicatepermissiondeclareapp";
81 
82     private static final String LOG_TAG = "AppSecurityTests";
83 
84     @Before
setUp()85     public void setUp() throws Exception {
86         Utils.prepareSingleUser(getDevice());
87         assertNotNull(getBuild());
88     }
89 
90     /**
91      * Test that an app update cannot be installed over an existing app if it has a different
92      * certificate.
93      */
94     @Test
95     @AppModeFull(reason = "'full' portion of the hostside test")
testAppUpgradeDifferentCerts_full()96     public void testAppUpgradeDifferentCerts_full() throws Exception {
97         testAppUpgradeDifferentCerts(false);
98     }
99     @Test
100     @AppModeInstant(reason = "'instant' portion of the hostside test")
testAppUpgradeDifferentCerts_instant()101     public void testAppUpgradeDifferentCerts_instant() throws Exception {
102         testAppUpgradeDifferentCerts(true);
103     }
testAppUpgradeDifferentCerts(boolean instant)104     private void testAppUpgradeDifferentCerts(boolean instant) throws Exception {
105         Log.i(LOG_TAG, "installing app upgrade with different certs");
106         try {
107             getDevice().uninstallPackage(SIMPLE_APP_PKG);
108             getDevice().uninstallPackage(SIMPLE_APP_DIFF_CERT_APK);
109 
110             new InstallMultiple(instant).addApk(SIMPLE_APP_APK).run();
111             new InstallMultiple(instant).addApk(SIMPLE_APP_DIFF_CERT_APK)
112                     .runExpectingFailure("INSTALL_FAILED_UPDATE_INCOMPATIBLE");
113         } finally {
114             getDevice().uninstallPackage(SIMPLE_APP_PKG);
115             getDevice().uninstallPackage(SIMPLE_APP_DIFF_CERT_APK);
116         }
117     }
118 
119     /**
120      * Test that an app cannot access another app's private data.
121      */
122     @Test
123     @AppModeFull(reason = "'full' portion of the hostside test")
testAppFailAccessPrivateData_full()124     public void testAppFailAccessPrivateData_full() throws Exception {
125         testAppFailAccessPrivateData(false);
126     }
127     @Test
128     @AppModeInstant(reason = "'instant' portion of the hostside test")
testAppFailAccessPrivateData_instant()129     public void testAppFailAccessPrivateData_instant() throws Exception {
130         testAppFailAccessPrivateData(true);
131     }
testAppFailAccessPrivateData(boolean instant)132     private void testAppFailAccessPrivateData(boolean instant)
133             throws Exception {
134         Log.i(LOG_TAG, "installing app that attempts to access another app's private data");
135         try {
136             getDevice().uninstallPackage(APP_WITH_DATA_PKG);
137             getDevice().uninstallPackage(APP_ACCESS_DATA_PKG);
138 
139             new InstallMultiple().addApk(APP_WITH_DATA_APK).run();
140             runDeviceTests(APP_WITH_DATA_PKG, APP_WITH_DATA_CLASS, APP_WITH_DATA_CREATE_METHOD);
141 
142             new InstallMultiple(instant).addApk(APP_ACCESS_DATA_APK).run();
143             runDeviceTests(APP_ACCESS_DATA_PKG, null, null, instant);
144         } finally {
145             getDevice().uninstallPackage(APP_WITH_DATA_PKG);
146             getDevice().uninstallPackage(APP_ACCESS_DATA_PKG);
147         }
148     }
149 
150     /**
151      * Test that uninstall of an app removes its private data.
152      */
153     @Test
154     @AppModeFull(reason = "'full' portion of the hostside test")
testUninstallRemovesData_full()155     public void testUninstallRemovesData_full() throws Exception {
156         testUninstallRemovesData(false);
157     }
158     @Test
159     @AppModeInstant(reason = "'instant' portion of the hostside test")
testUninstallRemovesData_instant()160     public void testUninstallRemovesData_instant() throws Exception {
161         testUninstallRemovesData(true);
162     }
testUninstallRemovesData(boolean instant)163     private void testUninstallRemovesData(boolean instant) throws Exception {
164         Log.i(LOG_TAG, "Uninstalling app, verifying data is removed.");
165         try {
166             getDevice().uninstallPackage(APP_WITH_DATA_PKG);
167 
168             new InstallMultiple(instant).addApk(APP_WITH_DATA_APK).run();
169             runDeviceTests(
170                     APP_WITH_DATA_PKG, APP_WITH_DATA_CLASS, APP_WITH_DATA_CREATE_METHOD);
171 
172             getDevice().uninstallPackage(APP_WITH_DATA_PKG);
173 
174             new InstallMultiple(instant).addApk(APP_WITH_DATA_APK).run();
175             runDeviceTests(
176                     APP_WITH_DATA_PKG, APP_WITH_DATA_CLASS, APP_WITH_DATA_CHECK_NOEXIST_METHOD);
177         } finally {
178             getDevice().uninstallPackage(APP_WITH_DATA_PKG);
179         }
180     }
181 
182     /**
183      * Test that an app cannot instrument another app that is signed with different certificate.
184      */
185     @Test
186     @AppModeFull(reason = "'full' portion of the hostside test")
testInstrumentationDiffCert_full()187     public void testInstrumentationDiffCert_full() throws Exception {
188         testInstrumentationDiffCert(false, false);
189     }
190     @Test
191     @AppModeInstant(reason = "'instant' portion of the hostside test")
testInstrumentationDiffCert_instant()192     public void testInstrumentationDiffCert_instant() throws Exception {
193         testInstrumentationDiffCert(false, true);
194         testInstrumentationDiffCert(true, false);
195         testInstrumentationDiffCert(true, true);
196     }
testInstrumentationDiffCert(boolean targetInstant, boolean instrumentInstant)197     private void testInstrumentationDiffCert(boolean targetInstant, boolean instrumentInstant)
198             throws Exception {
199         Log.i(LOG_TAG, "installing app that attempts to instrument another app");
200         try {
201             // cleanup test app that might be installed from previous partial test run
202             getDevice().uninstallPackage(TARGET_INSTRUMENT_PKG);
203             getDevice().uninstallPackage(INSTRUMENT_DIFF_CERT_PKG);
204 
205             new InstallMultiple(targetInstant).addApk(TARGET_INSTRUMENT_APK).run();
206             new InstallMultiple(instrumentInstant).addApk(INSTRUMENT_DIFF_CERT_APK).run();
207 
208             // if we've installed either the instrumentation or target as an instant application,
209             // starting an instrumentation will just fail instead of throwing a security exception
210             // because neither the target nor instrumentation packages can see one another
211             final String methodName = (targetInstant|instrumentInstant)
212                     ? "testInstrumentationNotAllowed_fail"
213                     : "testInstrumentationNotAllowed_exception";
214             runDeviceTests(INSTRUMENT_DIFF_CERT_PKG, INSTRUMENT_DIFF_CERT_CLASS, methodName);
215         } finally {
216             getDevice().uninstallPackage(TARGET_INSTRUMENT_PKG);
217             getDevice().uninstallPackage(INSTRUMENT_DIFF_CERT_PKG);
218         }
219     }
220 
221     /**
222      * Test that an app cannot use a signature-enforced permission if it is signed with a different
223      * certificate than the app that declared the permission.
224      */
225     @Test
226     @AppModeFull(reason = "Only the platform can define permissions obtainable by instant applications")
testPermissionDiffCert()227     public void testPermissionDiffCert() throws Exception {
228         Log.i(LOG_TAG, "installing app that attempts to use permission of another app");
229         try {
230             // cleanup test app that might be installed from previous partial test run
231             getDevice().uninstallPackage(DECLARE_PERMISSION_PKG);
232             getDevice().uninstallPackage(DECLARE_PERMISSION_COMPAT_PKG);
233             getDevice().uninstallPackage(PERMISSION_DIFF_CERT_PKG);
234 
235             new InstallMultiple().addApk(DECLARE_PERMISSION_APK).run();
236             new InstallMultiple().addApk(DECLARE_PERMISSION_COMPAT_APK).run();
237 
238             new InstallMultiple().addApk(PERMISSION_DIFF_CERT_APK).run();
239 
240             // Enable alert window permission so it can start activity in background
241             enableAlertWindowAppOp(DECLARE_PERMISSION_PKG);
242 
243             runDeviceTests(PERMISSION_DIFF_CERT_PKG, null);
244         } finally {
245             getDevice().uninstallPackage(DECLARE_PERMISSION_PKG);
246             getDevice().uninstallPackage(DECLARE_PERMISSION_COMPAT_PKG);
247             getDevice().uninstallPackage(PERMISSION_DIFF_CERT_PKG);
248         }
249     }
250 
251     /**
252      * Test what happens if an app tried to take a permission away from another
253      */
254     @Test
rebootWithDuplicatePermission()255     public void rebootWithDuplicatePermission() throws Exception {
256         try {
257             new InstallMultiple(false).addApk(DECLARE_PERMISSION_APK).run();
258             new InstallMultiple(false).addApk(DUPLICATE_DECLARE_PERMISSION_APK).run();
259 
260             // Enable alert window permission so it can start activity in background
261             enableAlertWindowAppOp(DECLARE_PERMISSION_PKG);
262 
263             runDeviceTests(DUPLICATE_DECLARE_PERMISSION_PKG, null);
264 
265             // make sure behavior is preserved after reboot
266             getDevice().reboot();
267             waitForBootCompleted(getDevice());
268             runDeviceTests(DUPLICATE_DECLARE_PERMISSION_PKG, null);
269         } finally {
270             getDevice().uninstallPackage(DECLARE_PERMISSION_PKG);
271             getDevice().uninstallPackage(DUPLICATE_DECLARE_PERMISSION_PKG);
272         }
273     }
274 
275     /**
276      * Tests that an arbitrary file cannot be installed using the 'cmd' command.
277      */
278     @Test
279     @AppModeFull(reason = "'full' portion of the hostside test")
testAdbInstallFile_full()280     public void testAdbInstallFile_full() throws Exception {
281         testAdbInstallFile(false);
282     }
283     @Test
284     @AppModeInstant(reason = "'instant' portion of the hostside test")
testAdbInstallFile_instant()285     public void testAdbInstallFile_instant() throws Exception {
286         testAdbInstallFile(true);
287     }
testAdbInstallFile(boolean instant)288     private void testAdbInstallFile(boolean instant) throws Exception {
289         String output = getDevice().executeShellCommand(
290                 "cmd package install"
291                         + (instant ? " --instant" : " --full")
292                         + " -S 1024 /data/local/tmp/foo.apk");
293         assertTrue("Error text", output.contains("Error"));
294     }
295 
enableAlertWindowAppOp(String pkgName)296     private void enableAlertWindowAppOp(String pkgName) throws Exception {
297         getDevice().executeShellCommand(
298                 "appops set " + pkgName + " android:system_alert_window allow");
299         String result = "No operations.";
300         while (result.contains("No operations")) {
301             result = getDevice().executeShellCommand(
302                     "appops get " + pkgName + " android:system_alert_window");
303         }
304     }
305 }
306