358 lines
12 KiB
Java
358 lines
12 KiB
Java
/*
|
|
* Copyright (C) 2006 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.server.am;
|
|
|
|
import android.content.Intent;
|
|
import android.net.Uri;
|
|
import android.os.UserHandle;
|
|
import android.util.Log;
|
|
|
|
import com.google.android.collect.Sets;
|
|
|
|
import java.io.PrintWriter;
|
|
import java.util.Comparator;
|
|
import java.util.HashSet;
|
|
|
|
/**
|
|
* Description of a permission granted to an app to access a particular URI.
|
|
*
|
|
* CTS tests for this functionality can be run with "runtest cts-appsecurity".
|
|
*
|
|
* Test cases are at cts/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/
|
|
* src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
|
|
*/
|
|
final class UriPermission {
|
|
private static final String TAG = "UriPermission";
|
|
|
|
public static final int STRENGTH_NONE = 0;
|
|
public static final int STRENGTH_OWNED = 1;
|
|
public static final int STRENGTH_GLOBAL = 2;
|
|
public static final int STRENGTH_PERSISTABLE = 3;
|
|
|
|
final int userHandle;
|
|
final String sourcePkg;
|
|
final String targetPkg;
|
|
|
|
/** Cached UID of {@link #targetPkg}; should not be persisted */
|
|
final int targetUid;
|
|
|
|
final Uri uri;
|
|
|
|
/**
|
|
* Allowed modes. All permission enforcement should use this field. Must
|
|
* always be a combination of {@link #ownedModeFlags},
|
|
* {@link #globalModeFlags}, {@link #persistableModeFlags}, and
|
|
* {@link #persistedModeFlags}. Mutations <em>must</em> only be performed by
|
|
* the owning class.
|
|
*/
|
|
int modeFlags = 0;
|
|
|
|
/** Allowed modes with explicit owner. */
|
|
int ownedModeFlags = 0;
|
|
/** Allowed modes without explicit owner. */
|
|
int globalModeFlags = 0;
|
|
/** Allowed modes that have been offered for possible persisting. */
|
|
int persistableModeFlags = 0;
|
|
/** Allowed modes that should be persisted across device boots. */
|
|
int persistedModeFlags = 0;
|
|
|
|
/**
|
|
* Timestamp when {@link #persistedModeFlags} was first defined in
|
|
* {@link System#currentTimeMillis()} time base.
|
|
*/
|
|
long persistedCreateTime = INVALID_TIME;
|
|
|
|
private static final long INVALID_TIME = Long.MIN_VALUE;
|
|
|
|
private HashSet<UriPermissionOwner> mReadOwners;
|
|
private HashSet<UriPermissionOwner> mWriteOwners;
|
|
|
|
private String stringName;
|
|
|
|
UriPermission(String sourcePkg, String targetPkg, int targetUid, Uri uri) {
|
|
this.userHandle = UserHandle.getUserId(targetUid);
|
|
this.sourcePkg = sourcePkg;
|
|
this.targetPkg = targetPkg;
|
|
this.targetUid = targetUid;
|
|
this.uri = uri;
|
|
}
|
|
|
|
private void updateModeFlags() {
|
|
modeFlags = ownedModeFlags | globalModeFlags | persistableModeFlags | persistedModeFlags;
|
|
}
|
|
|
|
/**
|
|
* Initialize persisted modes as read from file. This doesn't issue any
|
|
* global or owner grants.
|
|
*/
|
|
void initPersistedModes(int modeFlags, long createdTime) {
|
|
persistableModeFlags = modeFlags;
|
|
persistedModeFlags = modeFlags;
|
|
persistedCreateTime = createdTime;
|
|
|
|
updateModeFlags();
|
|
}
|
|
|
|
void grantModes(int modeFlags, boolean persistable, UriPermissionOwner owner) {
|
|
if (persistable) {
|
|
persistableModeFlags |= modeFlags;
|
|
}
|
|
|
|
if (owner == null) {
|
|
globalModeFlags |= modeFlags;
|
|
} else {
|
|
if ((modeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
|
|
addReadOwner(owner);
|
|
}
|
|
if ((modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
|
|
addWriteOwner(owner);
|
|
}
|
|
}
|
|
|
|
updateModeFlags();
|
|
}
|
|
|
|
/**
|
|
* @return if mode changes should trigger persisting.
|
|
*/
|
|
boolean takePersistableModes(int modeFlags) {
|
|
if ((modeFlags & persistableModeFlags) != modeFlags) {
|
|
throw new SecurityException("Requested flags 0x"
|
|
+ Integer.toHexString(modeFlags) + ", but only 0x"
|
|
+ Integer.toHexString(persistableModeFlags) + " are allowed");
|
|
}
|
|
|
|
final int before = persistedModeFlags;
|
|
persistedModeFlags |= (persistableModeFlags & modeFlags);
|
|
|
|
if (persistedModeFlags != 0) {
|
|
persistedCreateTime = System.currentTimeMillis();
|
|
}
|
|
|
|
updateModeFlags();
|
|
return persistedModeFlags != before;
|
|
}
|
|
|
|
boolean releasePersistableModes(int modeFlags) {
|
|
final int before = persistedModeFlags;
|
|
|
|
persistableModeFlags &= ~modeFlags;
|
|
persistedModeFlags &= ~modeFlags;
|
|
|
|
if (persistedModeFlags == 0) {
|
|
persistedCreateTime = INVALID_TIME;
|
|
}
|
|
|
|
updateModeFlags();
|
|
return persistedModeFlags != before;
|
|
}
|
|
|
|
/**
|
|
* @return if mode changes should trigger persisting.
|
|
*/
|
|
boolean clearModes(int modeFlags, boolean persistable) {
|
|
final int before = persistedModeFlags;
|
|
|
|
if ((modeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
|
|
if (persistable) {
|
|
persistableModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
|
|
persistedModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
|
|
}
|
|
globalModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
|
|
if (mReadOwners != null) {
|
|
ownedModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
|
|
for (UriPermissionOwner r : mReadOwners) {
|
|
r.removeReadPermission(this);
|
|
}
|
|
mReadOwners = null;
|
|
}
|
|
}
|
|
if ((modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
|
|
if (persistable) {
|
|
persistableModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
|
|
persistedModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
|
|
}
|
|
globalModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
|
|
if (mWriteOwners != null) {
|
|
ownedModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
|
|
for (UriPermissionOwner r : mWriteOwners) {
|
|
r.removeWritePermission(this);
|
|
}
|
|
mWriteOwners = null;
|
|
}
|
|
}
|
|
|
|
if (persistedModeFlags == 0) {
|
|
persistedCreateTime = INVALID_TIME;
|
|
}
|
|
|
|
updateModeFlags();
|
|
return persistedModeFlags != before;
|
|
}
|
|
|
|
/**
|
|
* Return strength of this permission grant for the given flags.
|
|
*/
|
|
public int getStrength(int modeFlags) {
|
|
if ((persistableModeFlags & modeFlags) == modeFlags) {
|
|
return STRENGTH_PERSISTABLE;
|
|
} else if ((globalModeFlags & modeFlags) == modeFlags) {
|
|
return STRENGTH_GLOBAL;
|
|
} else if ((ownedModeFlags & modeFlags) == modeFlags) {
|
|
return STRENGTH_OWNED;
|
|
} else {
|
|
return STRENGTH_NONE;
|
|
}
|
|
}
|
|
|
|
private void addReadOwner(UriPermissionOwner owner) {
|
|
if (mReadOwners == null) {
|
|
mReadOwners = Sets.newHashSet();
|
|
ownedModeFlags |= Intent.FLAG_GRANT_READ_URI_PERMISSION;
|
|
updateModeFlags();
|
|
}
|
|
if (mReadOwners.add(owner)) {
|
|
owner.addReadPermission(this);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Remove given read owner, updating {@Link #modeFlags} as needed.
|
|
*/
|
|
void removeReadOwner(UriPermissionOwner owner) {
|
|
if (!mReadOwners.remove(owner)) {
|
|
Log.wtf(TAG, "Unknown read owner " + owner + " in " + this);
|
|
}
|
|
if (mReadOwners.size() == 0) {
|
|
mReadOwners = null;
|
|
ownedModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
|
|
updateModeFlags();
|
|
}
|
|
}
|
|
|
|
private void addWriteOwner(UriPermissionOwner owner) {
|
|
if (mWriteOwners == null) {
|
|
mWriteOwners = Sets.newHashSet();
|
|
ownedModeFlags |= Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
|
|
updateModeFlags();
|
|
}
|
|
if (mWriteOwners.add(owner)) {
|
|
owner.addWritePermission(this);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Remove given write owner, updating {@Link #modeFlags} as needed.
|
|
*/
|
|
void removeWriteOwner(UriPermissionOwner owner) {
|
|
if (!mWriteOwners.remove(owner)) {
|
|
Log.wtf(TAG, "Unknown write owner " + owner + " in " + this);
|
|
}
|
|
if (mWriteOwners.size() == 0) {
|
|
mWriteOwners = null;
|
|
ownedModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
|
|
updateModeFlags();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
if (stringName != null) {
|
|
return stringName;
|
|
}
|
|
StringBuilder sb = new StringBuilder(128);
|
|
sb.append("UriPermission{");
|
|
sb.append(Integer.toHexString(System.identityHashCode(this)));
|
|
sb.append(' ');
|
|
sb.append(uri);
|
|
sb.append('}');
|
|
return stringName = sb.toString();
|
|
}
|
|
|
|
void dump(PrintWriter pw, String prefix) {
|
|
pw.print(prefix);
|
|
pw.print("userHandle=" + userHandle);
|
|
pw.print(" sourcePkg=" + sourcePkg);
|
|
pw.println(" targetPkg=" + targetPkg);
|
|
|
|
pw.print(prefix);
|
|
pw.print("mode=0x" + Integer.toHexString(modeFlags));
|
|
pw.print(" owned=0x" + Integer.toHexString(ownedModeFlags));
|
|
pw.print(" global=0x" + Integer.toHexString(globalModeFlags));
|
|
pw.print(" persistable=0x" + Integer.toHexString(persistableModeFlags));
|
|
pw.print(" persisted=0x" + Integer.toHexString(persistedModeFlags));
|
|
if (persistedCreateTime != INVALID_TIME) {
|
|
pw.print(" persistedCreate=" + persistedCreateTime);
|
|
}
|
|
pw.println();
|
|
|
|
if (mReadOwners != null) {
|
|
pw.print(prefix);
|
|
pw.println("readOwners:");
|
|
for (UriPermissionOwner owner : mReadOwners) {
|
|
pw.print(prefix);
|
|
pw.println(" * " + owner);
|
|
}
|
|
}
|
|
if (mWriteOwners != null) {
|
|
pw.print(prefix);
|
|
pw.println("writeOwners:");
|
|
for (UriPermissionOwner owner : mReadOwners) {
|
|
pw.print(prefix);
|
|
pw.println(" * " + owner);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static class PersistedTimeComparator implements Comparator<UriPermission> {
|
|
@Override
|
|
public int compare(UriPermission lhs, UriPermission rhs) {
|
|
return Long.compare(lhs.persistedCreateTime, rhs.persistedCreateTime);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Snapshot of {@link UriPermission} with frozen
|
|
* {@link UriPermission#persistedModeFlags} state.
|
|
*/
|
|
public static class Snapshot {
|
|
final int userHandle;
|
|
final String sourcePkg;
|
|
final String targetPkg;
|
|
final Uri uri;
|
|
final int persistedModeFlags;
|
|
final long persistedCreateTime;
|
|
|
|
private Snapshot(UriPermission perm) {
|
|
this.userHandle = perm.userHandle;
|
|
this.sourcePkg = perm.sourcePkg;
|
|
this.targetPkg = perm.targetPkg;
|
|
this.uri = perm.uri;
|
|
this.persistedModeFlags = perm.persistedModeFlags;
|
|
this.persistedCreateTime = perm.persistedCreateTime;
|
|
}
|
|
}
|
|
|
|
public Snapshot snapshot() {
|
|
return new Snapshot(this);
|
|
}
|
|
|
|
public android.content.UriPermission buildPersistedPublicApiObject() {
|
|
return new android.content.UriPermission(uri, persistedModeFlags, persistedCreateTime);
|
|
}
|
|
}
|