/* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.cts.usespermissiondiffcertapp; import android.content.BroadcastReceiver; import android.content.ClipData; import android.content.ComponentName; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.UriPermission; import android.database.Cursor; import android.net.Uri; import android.os.SystemClock; import android.test.AndroidTestCase; import android.util.Log; import com.android.cts.permissiondeclareapp.GrantUriPermission; import java.io.IOException; import java.util.List; /** * Tests that signature-enforced permissions cannot be accessed by apps signed * with different certs than app that declares the permission. * * Accesses app cts/tests/appsecurity-tests/test-apps/PermissionDeclareApp/... */ public class AccessPermissionWithDiffSigTest extends AndroidTestCase { private static final ComponentName GRANT_URI_PERM_COMP = new ComponentName("com.android.cts.permissiondeclareapp", "com.android.cts.permissiondeclareapp.GrantUriPermission"); private static final Uri PERM_URI = Uri.parse("content://ctspermissionwithsignature"); private static final Uri PERM_URI_GRANTING = Uri.parse("content://ctspermissionwithsignaturegranting"); private static final Uri PERM_URI_PATH = Uri.parse("content://ctspermissionwithsignaturepath"); private static final Uri PERM_URI_PATH_RESTRICTING = Uri.parse( "content://ctspermissionwithsignaturepathrestricting"); private static final Uri PRIV_URI = Uri.parse("content://ctsprivateprovider"); private static final Uri PRIV_URI_GRANTING = Uri.parse("content://ctsprivateprovidergranting"); private static final String EXPECTED_MIME_TYPE = "got/theMIME"; private static final Uri AMBIGUOUS_URI_COMPAT = Uri.parse("content://ctsambiguousprovidercompat"); private static final String EXPECTED_MIME_TYPE_AMBIGUOUS = "got/theUnspecifiedMIME"; private static final Uri AMBIGUOUS_URI = Uri.parse("content://ctsambiguousprovider"); @Override protected void tearDown() throws Exception { super.tearDown(); // Always dispose, usually to clean up from failed tests ReceiveUriActivity.finishCurInstanceSync(); } private void assertReadingContentUriNotAllowed(Uri uri, String msg) { try { getContext().getContentResolver().query(uri, null, null, null, null); fail("expected SecurityException reading " + uri + ": " + msg); } catch (SecurityException expected) { assertNotNull("security exception's error message.", expected.getMessage()); } } private void assertReadingContentUriAllowed(Uri uri) { try { getContext().getContentResolver().query(uri, null, null, null, null); } catch (SecurityException e) { fail("unexpected SecurityException reading " + uri + ": " + e.getMessage()); } } private void assertReadingClipNotAllowed(ClipData clip, String msg) { for (int i=0; i= (startTime + TIMEOUT_MS)) { throw new RuntimeException("Timeout"); } } if (!mGoodResult) { fail("Broadcast receiver did not return good result"); } if (!mSucceeded) { fail(failureMessage); } } } void assertFailure(String failureMessage) { synchronized (this) { final long startTime = SystemClock.uptimeMillis(); while (!mHaveResult) { try { wait(TIMEOUT_MS); } catch (InterruptedException e) { } if (SystemClock.uptimeMillis() >= (startTime + TIMEOUT_MS)) { throw new RuntimeException("Timeout"); } } if (!mGoodResult) { fail("Broadcast receiver did not return good result"); } if (mSucceeded) { fail(failureMessage); } } } } private void grantUriPermissionFail(Uri uri, int mode, boolean service) { Uri grantDataUri = Uri.withAppendedPath(uri, "data"); Intent grantIntent = new Intent(); grantIntent.setData(grantDataUri); grantIntent.addFlags(mode); grantIntent.setClass(getContext(), service ? ReceiveUriService.class : ReceiveUriActivity.class); Intent intent = new Intent(); intent.setComponent(GRANT_URI_PERM_COMP); intent.setAction(service ? GrantUriPermission.ACTION_START_SERVICE : GrantUriPermission.ACTION_START_ACTIVITY); intent.putExtra(GrantUriPermission.EXTRA_INTENT, grantIntent); GrantResultReceiver receiver = new GrantResultReceiver(); getContext().sendOrderedBroadcast(intent, null, receiver, null, 0, null, null); receiver.assertFailure("Able to grant URI permission to " + grantDataUri + " when should not"); grantIntent = makeClipIntent(uri, mode); grantIntent.setClass(getContext(), service ? ReceiveUriService.class : ReceiveUriActivity.class); intent = new Intent(); intent.setComponent(GRANT_URI_PERM_COMP); intent.setAction(service ? GrantUriPermission.ACTION_START_SERVICE : GrantUriPermission.ACTION_START_ACTIVITY); intent.putExtra(GrantUriPermission.EXTRA_INTENT, grantIntent); receiver = new GrantResultReceiver(); getContext().sendOrderedBroadcast(intent, null, receiver, null, 0, null, null); receiver.assertFailure("Able to grant URI permission to " + grantIntent.getClipData() + " when should not"); } private void doTestGrantUriPermissionFail(Uri uri) { grantUriPermissionFail(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION, false); grantUriPermissionFail(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, false); grantUriPermissionFail(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION, true); grantUriPermissionFail(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, true); } /** * Test that the ctspermissionwithsignature content provider can not grant * URI permissions to others. */ public void testGrantPermissionNonGrantingFail() { doTestGrantUriPermissionFail(PERM_URI); } /** * Test that the ctspermissionwithsignaturegranting content provider can not grant * URI permissions to paths outside of the grant tree */ public void testGrantPermissionOutsideGrantingFail() { doTestGrantUriPermissionFail(PERM_URI_GRANTING); doTestGrantUriPermissionFail(Uri.withAppendedPath(PERM_URI_GRANTING, "invalid")); } /** * Test that the ctsprivateprovider content provider can not grant * URI permissions to others. */ public void testGrantPrivateNonGrantingFail() { doTestGrantUriPermissionFail(PRIV_URI); } /** * Test that the ctsambiguousprovider content provider can not grant * URI permissions to others. */ public void testGrantAmbiguousNonGrantingFail() { doTestGrantUriPermissionFail(AMBIGUOUS_URI); } /** * Test that the ctsprivateprovidergranting content provider can not grant * URI permissions to paths outside of the grant tree */ public void testGrantPrivateOutsideGrantingFail() { doTestGrantUriPermissionFail(PRIV_URI_GRANTING); doTestGrantUriPermissionFail(Uri.withAppendedPath(PRIV_URI_GRANTING, "invalid")); } private void grantClipUriPermission(ClipData clip, int mode, boolean service) { Intent grantIntent = new Intent(); if (clip.getItemCount() == 1) { grantIntent.setData(clip.getItemAt(0).getUri()); } else { grantIntent.setClipData(clip); // Make this Intent unique from the one that started it. for (int i=0; i perms = getContext() .getContentResolver().getPersistedUriPermissions(); if (uri != null) { assertEquals("expected exactly one permission", 1, perms.size()); final UriPermission perm = perms.get(0); assertEquals("unexpected uri", uri, perm.getUri()); final long actual = perm.getPersistedTime(); if (before != -1) { assertTrue("found " + actual + " before " + before, actual >= before); } if (after != -1) { assertTrue("found " + actual + " after " + after, actual <= after); } final boolean expectedRead = (flags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0; final boolean expectedWrite = (flags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0; assertEquals("unexpected read status", expectedRead, perm.isReadPermission()); assertEquals("unexpected write status", expectedWrite, perm.isWritePermission()); } else { assertEquals("expected zero permissions", 0, perms.size()); } // And assert remote Intent intent = new Intent(); intent.setComponent(GRANT_URI_PERM_COMP); intent.setAction(GrantUriPermission.ACTION_VERIFY_OUTGOING_PERSISTED); intent.putExtra(GrantUriPermission.EXTRA_URI, uri); GrantResultReceiver receiver = new GrantResultReceiver(); getContext().sendOrderedBroadcast(intent, null, receiver, null, 0, null, null); receiver.assertSuccess("unexpected outgoing persisted Uri status"); } /** * Validate behavior of prefix permission grants. */ public void testGrantPrefixUriPermission() throws Exception { final Uri target = Uri.withAppendedPath(PERM_URI_GRANTING, "foo1"); final Uri targetMeow = Uri.withAppendedPath(target, "meow"); final Uri targetMeowCat = Uri.withAppendedPath(targetMeow, "cat"); final ClipData clip = makeSingleClipData(target); final ClipData clipMeow = makeSingleClipData(targetMeow); final ClipData clipMeowCat = makeSingleClipData(targetMeowCat); // Make sure we can't see the target assertReadingClipNotAllowed(clip, "reading should have failed"); assertWritingClipNotAllowed(clip, "writing should have failed"); // Give ourselves prefix read access ReceiveUriActivity.clearStarted(); grantClipUriPermission(clipMeow, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION, false); ReceiveUriActivity.waitForStart(); // Verify prefix read access assertReadingClipNotAllowed(clip, "reading should have failed"); assertReadingClipAllowed(clipMeow); assertReadingClipAllowed(clipMeowCat); assertWritingClipNotAllowed(clip, "writing should have failed"); assertWritingClipNotAllowed(clipMeow, "writing should have failed"); assertWritingClipNotAllowed(clipMeowCat, "writing should have failed"); // Now give ourselves exact write access ReceiveUriActivity.clearNewIntent(); grantClipUriPermission(clip, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, false); ReceiveUriActivity.waitForNewIntent(); // Verify we have exact write access, but not prefix write assertReadingClipNotAllowed(clip, "reading should have failed"); assertReadingClipAllowed(clipMeow); assertReadingClipAllowed(clipMeowCat); assertWritingClipAllowed(clip); assertWritingClipNotAllowed(clipMeow, "writing should have failed"); assertWritingClipNotAllowed(clipMeowCat, "writing should have failed"); ReceiveUriActivity.finishCurInstanceSync(); } public void testGrantPersistablePrefixUriPermission() { final ContentResolver resolver = getContext().getContentResolver(); final Uri target = Uri.withAppendedPath(PERM_URI_GRANTING, "foo2"); final Uri targetMeow = Uri.withAppendedPath(target, "meow"); final ClipData clip = makeSingleClipData(target); final ClipData clipMeow = makeSingleClipData(targetMeow); // Make sure we can't see the target assertReadingClipNotAllowed(clip, "reading should have failed"); // Give ourselves prefix read access ReceiveUriActivity.clearStarted(); grantClipUriPermission(clip, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION, false); ReceiveUriActivity.waitForStart(); // Verify prefix read access assertReadingClipAllowed(clip); assertReadingClipAllowed(clipMeow); // Verify we can persist direct grant long before = System.currentTimeMillis(); resolver.takePersistableUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION); long after = System.currentTimeMillis(); assertPersistedUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION, before, after); // But we can't take anywhere under the prefix try { resolver.takePersistableUriPermission(targetMeow, Intent.FLAG_GRANT_READ_URI_PERMISSION); fail("taking under prefix should have failed"); } catch (SecurityException expected) { } // Should still have access regardless of taking assertReadingClipAllowed(clip); assertReadingClipAllowed(clipMeow); // And clean up our grants resolver.releasePersistableUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION); assertNoPersistedUriPermission(); ReceiveUriActivity.finishCurInstanceSync(); } /** * Validate behavior of directly granting/revoking permission grants. */ public void testDirectGrantRevokeUriPermission() throws Exception { final ContentResolver resolver = getContext().getContentResolver(); final Uri target = Uri.withAppendedPath(PERM_URI_GRANTING, "foo3"); final Uri targetMeow = Uri.withAppendedPath(target, "meow"); final Uri targetMeowCat = Uri.withAppendedPath(targetMeow, "cat"); final ClipData clip = makeSingleClipData(target); final ClipData clipMeow = makeSingleClipData(targetMeow); final ClipData clipMeowCat = makeSingleClipData(targetMeowCat); // Make sure we can't see the target assertReadingClipNotAllowed(clipMeow, "reading should have failed"); assertWritingClipNotAllowed(clipMeow, "writing should have failed"); // Give ourselves some grants: // /meow/cat WRITE|PERSISTABLE // /meow READ|PREFIX // /meow WRITE grantClipUriPermissionViaContext(targetMeowCat, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); grantClipUriPermissionViaContext(targetMeow, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION); grantClipUriPermissionViaContext(targetMeow, Intent.FLAG_GRANT_WRITE_URI_PERMISSION); SystemClock.sleep(2000); long before = System.currentTimeMillis(); resolver.takePersistableUriPermission(targetMeowCat, Intent.FLAG_GRANT_WRITE_URI_PERMISSION); long after = System.currentTimeMillis(); assertPersistedUriPermission(targetMeowCat, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, before, after); // Verify they look good assertReadingClipNotAllowed(clip, "reading should have failed"); assertReadingClipAllowed(clipMeow); assertReadingClipAllowed(clipMeowCat); assertWritingClipNotAllowed(clip, "writing should have failed"); assertWritingClipAllowed(clipMeow); assertWritingClipAllowed(clipMeowCat); // Revoke anyone with write under meow revokeClipUriPermissionViaContext(targetMeow, Intent.FLAG_GRANT_WRITE_URI_PERMISSION); SystemClock.sleep(2000); // This should have nuked persisted permission at lower level, but it // shoulnd't have touched our prefix read. assertReadingClipNotAllowed(clip, "reading should have failed"); assertReadingClipAllowed(clipMeow); assertReadingClipAllowed(clipMeowCat); assertWritingClipNotAllowed(clip, "writing should have failed"); assertWritingClipNotAllowed(clipMeow, "writing should have failed"); assertWritingClipNotAllowed(clipMeowCat, "writing should have failed"); assertNoPersistedUriPermission(); // Revoking read at top of tree should nuke everything else revokeClipUriPermissionViaContext(target, Intent.FLAG_GRANT_READ_URI_PERMISSION); SystemClock.sleep(2000); assertReadingClipNotAllowed(clip, "reading should have failed"); assertReadingClipNotAllowed(clipMeow, "reading should have failed"); assertReadingClipNotAllowed(clipMeowCat, "reading should have failed"); assertWritingClipNotAllowed(clip, "writing should have failed"); assertWritingClipNotAllowed(clipMeow, "writing should have failed"); assertWritingClipNotAllowed(clipMeowCat, "writing should have failed"); assertNoPersistedUriPermission(); } /** * Validate behavior of a direct permission grant, where the receiver of * that permission revokes it. */ public void testDirectGrantReceiverRevokeUriPermission() throws Exception { final ContentResolver resolver = getContext().getContentResolver(); final Uri target = Uri.withAppendedPath(PERM_URI_GRANTING, "foo3"); final Uri targetMeow = Uri.withAppendedPath(target, "meow"); final Uri targetMeowCat = Uri.withAppendedPath(targetMeow, "cat"); final ClipData clip = makeSingleClipData(target); final ClipData clipMeow = makeSingleClipData(targetMeow); final ClipData clipMeowCat = makeSingleClipData(targetMeowCat); // Make sure we can't see the target assertReadingClipNotAllowed(clipMeow, "reading should have failed"); assertWritingClipNotAllowed(clipMeow, "writing should have failed"); // Give ourselves some grants: // /meow/cat WRITE|PERSISTABLE // /meow READ|PREFIX // /meow WRITE grantClipUriPermissionViaContext(targetMeowCat, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); grantClipUriPermissionViaContext(targetMeow, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION); grantClipUriPermissionViaContext(targetMeow, Intent.FLAG_GRANT_WRITE_URI_PERMISSION); SystemClock.sleep(2000); long before = System.currentTimeMillis(); resolver.takePersistableUriPermission(targetMeowCat, Intent.FLAG_GRANT_WRITE_URI_PERMISSION); long after = System.currentTimeMillis(); assertPersistedUriPermission(targetMeowCat, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, before, after); // Verify they look good assertReadingClipNotAllowed(clip, "reading should have failed"); assertReadingClipAllowed(clipMeow); assertReadingClipAllowed(clipMeowCat); assertWritingClipNotAllowed(clip, "writing should have failed"); assertWritingClipAllowed(clipMeow); assertWritingClipAllowed(clipMeowCat); // Revoke anyone with write under meow getContext().revokeUriPermission(targetMeow, Intent.FLAG_GRANT_WRITE_URI_PERMISSION); // This should have nuked persisted permission at lower level, but it // shoulnd't have touched our prefix read. assertReadingClipNotAllowed(clip, "reading should have failed"); assertReadingClipAllowed(clipMeow); assertReadingClipAllowed(clipMeowCat); assertWritingClipNotAllowed(clip, "writing should have failed"); assertWritingClipNotAllowed(clipMeow, "writing should have failed"); assertWritingClipNotAllowed(clipMeowCat, "writing should have failed"); assertNoPersistedUriPermission(); // Revoking read at top of tree should nuke everything else getContext().revokeUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION); assertReadingClipNotAllowed(clip, "reading should have failed"); assertReadingClipNotAllowed(clipMeow, "reading should have failed"); assertReadingClipNotAllowed(clipMeowCat, "reading should have failed"); assertWritingClipNotAllowed(clip, "writing should have failed"); assertWritingClipNotAllowed(clipMeow, "writing should have failed"); assertWritingClipNotAllowed(clipMeowCat, "writing should have failed"); assertNoPersistedUriPermission(); } }