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 com.android.cts.usespermissiondiffcertapp;
18 
19 import static com.android.cts.usespermissiondiffcertapp.Asserts.assertContentUriAllowed;
20 import static com.android.cts.usespermissiondiffcertapp.Asserts.assertContentUriNotAllowed;
21 import static com.android.cts.usespermissiondiffcertapp.Asserts.assertReadingContentUriAllowed;
22 import static com.android.cts.usespermissiondiffcertapp.Asserts.assertReadingContentUriNotAllowed;
23 import static com.android.cts.usespermissiondiffcertapp.Asserts.assertWritingContentUriAllowed;
24 import static com.android.cts.usespermissiondiffcertapp.Asserts.assertWritingContentUriNotAllowed;
25 
26 import static junit.framework.Assert.assertEquals;
27 
28 import android.content.Context;
29 import android.content.Intent;
30 import android.net.Uri;
31 import android.provider.CalendarContract;
32 import android.provider.ContactsContract;
33 
34 import androidx.test.InstrumentationRegistry;
35 import androidx.test.runner.AndroidJUnit4;
36 
37 import org.junit.Test;
38 import org.junit.runner.RunWith;
39 
40 /**
41  * Tests that signature-enforced permissions cannot be accessed by apps signed
42  * with different certs than app that declares the permission.
43  *
44  * Accesses app cts/tests/appsecurity-tests/test-apps/PermissionDeclareApp/...
45  */
46 @RunWith(AndroidJUnit4.class)
47 public class AccessPermissionWithDiffSigTest {
getContext()48     private static Context getContext() {
49         return InstrumentationRegistry.getTargetContext();
50     }
51 
52     static final Uri PERM_URI = Uri.parse("content://ctspermissionwithsignature");
53     static final Uri PERM_URI_GRANTING = Uri.parse("content://ctspermissionwithsignaturegranting");
54     static final Uri PERM_URI_PATH = Uri.parse("content://ctspermissionwithsignaturepath");
55     static final Uri PERM_URI_PATH_RESTRICTING = Uri.parse(
56             "content://ctspermissionwithsignaturepathrestricting");
57     static final Uri PRIV_URI = Uri.parse("content://ctsprivateprovider");
58     static final Uri PRIV_URI_GRANTING = Uri.parse("content://ctsprivateprovidergranting");
59     static final String EXPECTED_MIME_TYPE = "got/theMIME";
60 
61     static final Uri AMBIGUOUS_URI_COMPAT = Uri.parse("content://ctsambiguousprovidercompat");
62     static final String EXPECTED_MIME_TYPE_AMBIGUOUS = "got/theUnspecifiedMIME";
63     static final Uri AMBIGUOUS_URI = Uri.parse("content://ctsambiguousprovider");
64 
65     static final Uri[] GRANTABLE = new Uri[] {
66             Uri.withAppendedPath(PERM_URI_GRANTING, "foo"),
67             Uri.withAppendedPath(PERM_URI_PATH, "foo"),
68             Uri.withAppendedPath(PRIV_URI_GRANTING, "foo"),
69     };
70 
71     static final Uri[] NOT_GRANTABLE = new Uri[] {
72             Uri.withAppendedPath(PERM_URI, "bar"),
73             Uri.withAppendedPath(PERM_URI_GRANTING, "bar"),
74             Uri.withAppendedPath(PERM_URI_PATH, "bar"),
75             Uri.withAppendedPath(PRIV_URI, "bar"),
76             Uri.withAppendedPath(PRIV_URI_GRANTING, "bar"),
77             Uri.withAppendedPath(AMBIGUOUS_URI, "bar"),
78             CalendarContract.CONTENT_URI,
79             ContactsContract.AUTHORITY_URI,
80     };
81 
82     static final int[] GRANTABLE_MODES = new int[] {
83             Intent.FLAG_GRANT_READ_URI_PERMISSION,
84             Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
85     };
86 
87     static final int[] NOT_GRANTABLE_MODES = new int[] {
88             Intent.FLAG_GRANT_READ_URI_PERMISSION,
89             Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
90             Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION,
91             Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION,
92             Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION,
93             Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION,
94     };
95 
96     /**
97      * Test that the ctspermissionwithsignature content provider cannot be read,
98      * since this app lacks the required certs
99      */
100     @Test
testReadProviderWithDiff()101     public void testReadProviderWithDiff() {
102         assertReadingContentUriNotAllowed(PERM_URI, null);
103     }
104 
105     /**
106      * Test that the ctspermissionwithsignature content provider cannot be written,
107      * since this app lacks the required certs
108      */
109     @Test
testWriteProviderWithDiff()110     public void testWriteProviderWithDiff() {
111         assertWritingContentUriNotAllowed(PERM_URI, null);
112     }
113 
114     /**
115      * Test that the ctsprivateprovider content provider cannot be read,
116      * since it is not exported from its app.
117      */
118     @Test
testReadProviderWhenPrivate()119     public void testReadProviderWhenPrivate() {
120         assertReadingContentUriNotAllowed(PRIV_URI, "shouldn't read private provider");
121     }
122 
123     /**
124      * Test that the ctsambiguousprovider content provider cannot be read,
125      * since it doesn't have an "exported=" line.
126      */
127     @Test
testReadProviderWhenAmbiguous()128     public void testReadProviderWhenAmbiguous() {
129         assertReadingContentUriNotAllowed(AMBIGUOUS_URI, "shouldn't read ambiguous provider");
130     }
131 
132     /**
133      * Old App Compatibility Test
134      *
135      * Test that the ctsambiguousprovidercompat content provider can be read for older
136      * API versions, because it didn't specify either exported=true or exported=false.
137      */
138     @Test
testReadProviderWhenAmbiguousCompat()139     public void testReadProviderWhenAmbiguousCompat() {
140         assertReadingContentUriAllowed(AMBIGUOUS_URI_COMPAT);
141     }
142 
143     /**
144      * Old App Compatibility Test
145      *
146      * Test that the ctsambiguousprovidercompat content provider can be written for older
147      * API versions, because it didn't specify either exported=true or exported=false.
148      */
149     @Test
testWriteProviderWhenAmbiguousCompat()150     public void testWriteProviderWhenAmbiguousCompat() {
151         assertWritingContentUriAllowed(AMBIGUOUS_URI_COMPAT);
152     }
153 
154     /**
155      * Test that the ctsprivateprovider content provider cannot be written,
156      * since it is not exported from its app.
157      */
158     @Test
testWriteProviderWhenPrivate()159     public void testWriteProviderWhenPrivate() {
160         assertWritingContentUriNotAllowed(PRIV_URI, "shouldn't write private provider");
161     }
162 
163     /**
164      * Test that the ctsambiguousprovider content provider cannot be written,
165      * since it doesn't have an exported= line.
166      */
167     @Test
testWriteProviderWhenAmbiguous()168     public void testWriteProviderWhenAmbiguous() {
169         assertWritingContentUriNotAllowed(AMBIGUOUS_URI, "shouldn't write ambiguous provider");
170     }
171 
172     /**
173      * Verify that we can access paths outside the {@code path-permission}
174      * protections, which should only rely on {@code provider} permissions.
175      */
176     @Test
testRestrictingProviderNoMatchingPath()177     public void testRestrictingProviderNoMatchingPath() {
178         assertReadingContentUriAllowed(PERM_URI_PATH_RESTRICTING);
179         assertWritingContentUriAllowed(PERM_URI_PATH_RESTRICTING);
180 
181         // allowed by no top-level permission
182         final Uri test = PERM_URI_PATH_RESTRICTING.buildUpon().appendPath("fo").build();
183         assertReadingContentUriAllowed(test);
184         assertWritingContentUriAllowed(test);
185     }
186 
187     /**
188      * Verify that paths under {@code path-permission} restriction aren't
189      * allowed, even though the {@code provider} requires no permissions.
190      */
191     @Test
testRestrictingProviderMatchingPathDenied()192     public void testRestrictingProviderMatchingPathDenied() {
193         // rejected by "foo" prefix
194         final Uri test1 = PERM_URI_PATH_RESTRICTING.buildUpon().appendPath("foo").build();
195         assertReadingContentUriNotAllowed(test1, null);
196         assertWritingContentUriNotAllowed(test1, null);
197 
198         // rejected by "foo" prefix
199         final Uri test2 = PERM_URI_PATH_RESTRICTING.buildUpon()
200                 .appendPath("foo").appendPath("ba").build();
201         assertReadingContentUriNotAllowed(test2, null);
202         assertWritingContentUriNotAllowed(test2, null);
203     }
204 
205     /**
206      * Test that shady {@link Uri} are blocked by {@code path-permission}.
207      */
208     @Test
testRestrictingProviderMatchingShadyPaths()209     public void testRestrictingProviderMatchingShadyPaths() {
210         assertContentUriAllowed(
211                 Uri.parse("content://ctspermissionwithsignaturepathrestricting/"));
212         assertContentUriAllowed(
213                 Uri.parse("content://ctspermissionwithsignaturepathrestricting//"));
214         assertContentUriAllowed(
215                 Uri.parse("content://ctspermissionwithsignaturepathrestricting///"));
216         assertContentUriNotAllowed(
217                 Uri.parse("content://ctspermissionwithsignaturepathrestricting/foo"), null);
218         assertContentUriNotAllowed(
219                 Uri.parse("content://ctspermissionwithsignaturepathrestricting//foo"), null);
220         assertContentUriNotAllowed(
221                 Uri.parse("content://ctspermissionwithsignaturepathrestricting///foo"), null);
222         assertContentUriNotAllowed(
223                 Uri.parse("content://ctspermissionwithsignaturepathrestricting/foo//baz"), null);
224     }
225 
226     /**
227      * Verify that at least one {@code path-permission} rule will grant access,
228      * even if the caller doesn't hold another matching {@code path-permission}.
229      */
230     @Test
testRestrictingProviderMultipleMatchingPath()231     public void testRestrictingProviderMultipleMatchingPath() {
232         // allowed by narrow "foo/bar" prefix
233         final Uri test1 = PERM_URI_PATH_RESTRICTING.buildUpon()
234                 .appendPath("foo").appendPath("bar").build();
235         assertReadingContentUriAllowed(test1);
236         assertWritingContentUriAllowed(test1);
237 
238         // allowed by narrow "foo/bar" prefix
239         final Uri test2 = PERM_URI_PATH_RESTRICTING.buildUpon()
240                 .appendPath("foo").appendPath("bar2").build();
241         assertReadingContentUriAllowed(test2);
242         assertWritingContentUriAllowed(test2);
243     }
244 
245     @Test
testGetMimeTypePermission()246     public void testGetMimeTypePermission() {
247         // Precondition: no current access.
248         assertReadingContentUriNotAllowed(PERM_URI, "shouldn't read when starting test");
249         assertWritingContentUriNotAllowed(PERM_URI, "shouldn't write when starting test");
250 
251         // All apps should be able to get MIME type regardless of permission.
252         assertEquals(getContext().getContentResolver().getType(PERM_URI), EXPECTED_MIME_TYPE);
253     }
254 
255     @Test
testGetMimeTypePrivate()256     public void testGetMimeTypePrivate() {
257         // Precondition: no current access.
258         assertReadingContentUriNotAllowed(PRIV_URI, "shouldn't read when starting test");
259         assertWritingContentUriNotAllowed(PRIV_URI, "shouldn't write when starting test");
260 
261         // All apps should be able to get MIME type even if provider is private.
262         assertEquals(getContext().getContentResolver().getType(PRIV_URI), EXPECTED_MIME_TYPE);
263     }
264 
265     @Test
testGetMimeTypeAmbiguous()266     public void testGetMimeTypeAmbiguous() {
267         // Precondition: no current access.
268         assertReadingContentUriNotAllowed(AMBIGUOUS_URI, "shouldn't read when starting test");
269         assertWritingContentUriNotAllowed(AMBIGUOUS_URI, "shouldn't write when starting test");
270 
271         // All apps should be able to get MIME type even if provider is private.
272         assertEquals(getContext().getContentResolver().getType(AMBIGUOUS_URI), EXPECTED_MIME_TYPE);
273     }
274 
275     /**
276      * Old App Compatibility Test
277      *
278      * We should be able to access the mime type of a content provider of an older
279      * application, even if that application didn't explicitly declare either
280      * exported=true or exported=false
281      */
282     @Test
testGetMimeTypeAmbiguousCompat()283     public void testGetMimeTypeAmbiguousCompat() {
284         // All apps should be able to get MIME type even if provider is private.
285         assertEquals(EXPECTED_MIME_TYPE_AMBIGUOUS,
286                 getContext().getContentResolver().getType(AMBIGUOUS_URI_COMPAT));
287     }
288 }
289