/*
* Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package java.security;
import java.util.
ArrayList;
import java.util.
Collections;
import java.util.
Enumeration;
import java.util.
List;
import java.util.
Map;
import java.util.
WeakHashMap;
import sun.misc.
JavaSecurityAccess;
import sun.misc.
JavaSecurityProtectionDomainAccess;
import static sun.misc.
JavaSecurityProtectionDomainAccess.
ProtectionDomainCache;
import sun.misc.
SharedSecrets;
import sun.security.util.
Debug;
import sun.security.util.
SecurityConstants;
/**
* The ProtectionDomain class encapsulates the characteristics of a domain,
* which encloses a set of classes whose instances are granted a set
* of permissions when being executed on behalf of a given set of Principals.
* <p>
* A static set of permissions can be bound to a ProtectionDomain when it is
* constructed; such permissions are granted to the domain regardless of the
* Policy in force. However, to support dynamic security policies, a
* ProtectionDomain can also be constructed such that it is dynamically
* mapped to a set of permissions by the current Policy whenever a permission
* is checked.
* <p>
*
* @author Li Gong
* @author Roland Schemers
* @author Gary Ellison
*/
public class
ProtectionDomain {
private static class
JavaSecurityAccessImpl implements
JavaSecurityAccess {
private
JavaSecurityAccessImpl() {
}
@
Override
public <T> T
doIntersectionPrivilege(
PrivilegedAction<T>
action,
final
AccessControlContext stack,
final
AccessControlContext context) {
if (
action == null) {
throw new
NullPointerException();
}
return
AccessController.
doPrivileged(
action,
getCombinedACC(
context,
stack)
);
}
@
Override
public <T> T
doIntersectionPrivilege(
PrivilegedAction<T>
action,
AccessControlContext context) {
return
doIntersectionPrivilege(
action,
AccessController.
getContext(),
context);
}
private static
AccessControlContext getCombinedACC(
AccessControlContext context,
AccessControlContext stack) {
AccessControlContext acc = new
AccessControlContext(
context,
stack.
getCombiner(), true);
return new
AccessControlContext(
stack.
getContext(),
acc).
optimize();
}
}
static {
// Set up JavaSecurityAccess in SharedSecrets
SharedSecrets.
setJavaSecurityAccess(new
JavaSecurityAccessImpl());
}
/* CodeSource */
private
CodeSource codesource ;
/* ClassLoader the protection domain was consed from */
private
ClassLoader classloader;
/* Principals running-as within this protection domain */
private
Principal[]
principals;
/* the rights this protection domain is granted */
private
PermissionCollection permissions;
/* if the permissions object has AllPermission */
private boolean
hasAllPerm = false;
/* the PermissionCollection is static (pre 1.4 constructor)
or dynamic (via a policy refresh) */
private boolean
staticPermissions;
/*
* An object used as a key when the ProtectionDomain is stored in a Map.
*/
final
Key key = new
Key();
private static final
Debug debug =
Debug.
getInstance("domain");
/**
* Creates a new ProtectionDomain with the given CodeSource and
* Permissions. If the permissions object is not null, then
* {@code setReadOnly())} will be called on the passed in
* Permissions object. The only permissions granted to this domain
* are the ones specified; the current Policy will not be consulted.
*
* @param codesource the codesource associated with this domain
* @param permissions the permissions granted to this domain
*/
public
ProtectionDomain(
CodeSource codesource,
PermissionCollection permissions) {
this.
codesource =
codesource;
if (
permissions != null) {
this.
permissions =
permissions;
this.
permissions.
setReadOnly();
if (
permissions instanceof
Permissions &&
((
Permissions)
permissions).
allPermission != null) {
hasAllPerm = true;
}
}
this.
classloader = null;
this.
principals = new
Principal[0];
staticPermissions = true;
}
/**
* Creates a new ProtectionDomain qualified by the given CodeSource,
* Permissions, ClassLoader and array of Principals. If the
* permissions object is not null, then {@code setReadOnly()}
* will be called on the passed in Permissions object.
* The permissions granted to this domain are dynamic; they include
* both the static permissions passed to this constructor, and any
* permissions granted to this domain by the current Policy at the
* time a permission is checked.
* <p>
* This constructor is typically used by
* {@link SecureClassLoader ClassLoaders}
* and {@link DomainCombiner DomainCombiners} which delegate to
* {@code Policy} to actively associate the permissions granted to
* this domain. This constructor affords the
* Policy provider the opportunity to augment the supplied
* PermissionCollection to reflect policy changes.
* <p>
*
* @param codesource the CodeSource associated with this domain
* @param permissions the permissions granted to this domain
* @param classloader the ClassLoader associated with this domain
* @param principals the array of Principals associated with this
* domain. The contents of the array are copied to protect against
* subsequent modification.
* @see Policy#refresh
* @see Policy#getPermissions(ProtectionDomain)
* @since 1.4
*/
public
ProtectionDomain(
CodeSource codesource,
PermissionCollection permissions,
ClassLoader classloader,
Principal[]
principals) {
this.
codesource =
codesource;
if (
permissions != null) {
this.
permissions =
permissions;
this.
permissions.
setReadOnly();
if (
permissions instanceof
Permissions &&
((
Permissions)
permissions).
allPermission != null) {
hasAllPerm = true;
}
}
this.
classloader =
classloader;
this.
principals = (
principals != null ?
principals.
clone():
new
Principal[0]);
staticPermissions = false;
}
/**
* Returns the CodeSource of this domain.
* @return the CodeSource of this domain which may be null.
* @since 1.2
*/
public final
CodeSource getCodeSource() {
return this.
codesource;
}
/**
* Returns the ClassLoader of this domain.
* @return the ClassLoader of this domain which may be null.
*
* @since 1.4
*/
public final
ClassLoader getClassLoader() {
return this.
classloader;
}
/**
* Returns an array of principals for this domain.
* @return a non-null array of principals for this domain.
* Returns a new array each time this method is called.
*
* @since 1.4
*/
public final
Principal[]
getPrincipals() {
return this.
principals.
clone();
}
/**
* Returns the static permissions granted to this domain.
*
* @return the static set of permissions for this domain which may be null.
* @see Policy#refresh
* @see Policy#getPermissions(ProtectionDomain)
*/
public final
PermissionCollection getPermissions() {
return
permissions;
}
/**
* Check and see if this ProtectionDomain implies the permissions
* expressed in the Permission object.
* <p>
* The set of permissions evaluated is a function of whether the
* ProtectionDomain was constructed with a static set of permissions
* or it was bound to a dynamically mapped set of permissions.
* <p>
* If the ProtectionDomain was constructed to a
* {@link #ProtectionDomain(CodeSource, PermissionCollection)
* statically bound} PermissionCollection then the permission will
* only be checked against the PermissionCollection supplied at
* construction.
* <p>
* However, if the ProtectionDomain was constructed with
* the constructor variant which supports
* {@link #ProtectionDomain(CodeSource, PermissionCollection,
* ClassLoader, java.security.Principal[]) dynamically binding}
* permissions, then the permission will be checked against the
* combination of the PermissionCollection supplied at construction and
* the current Policy binding.
* <p>
*
* @param permission the Permission object to check.
*
* @return true if "permission" is implicit to this ProtectionDomain.
*/
public boolean
implies(
Permission permission) {
if (
hasAllPerm) {
// internal permission collection already has AllPermission -
// no need to go to policy
return true;
}
if (!
staticPermissions &&
Policy.
getPolicyNoCheck().
implies(this,
permission))
return true;
if (
permissions != null)
return
permissions.
implies(
permission);
return false;
}
// called by the VM -- do not remove
boolean
impliesCreateAccessControlContext() {
return
implies(
SecurityConstants.
CREATE_ACC_PERMISSION);
}
/**
* Convert a ProtectionDomain to a String.
*/
@
Override public
String toString() {
String pals = "<no principals>";
if (
principals != null &&
principals.length > 0) {
StringBuilder palBuf = new
StringBuilder("(principals ");
for (int
i = 0;
i <
principals.length;
i++) {
palBuf.
append(
principals[
i].
getClass().
getName() +
" \"" +
principals[
i].
getName() +
"\"");
if (
i <
principals.length-1)
palBuf.
append(",\n");
else
palBuf.
append(")\n");
}
pals =
palBuf.
toString();
}
// Check if policy is set; we don't want to load
// the policy prematurely here
PermissionCollection pc =
Policy.
isSet() &&
seeAllp() ?
mergePermissions():
getPermissions();
return "ProtectionDomain "+
" "+
codesource+"\n"+
" "+
classloader+"\n"+
" "+
pals+"\n"+
" "+
pc+"\n";
}
/**
* Return true (merge policy permissions) in the following cases:
*
* . SecurityManager is null
*
* . SecurityManager is not null,
* debug is not null,
* SecurityManager impelmentation is in bootclasspath,
* Policy implementation is in bootclasspath
* (the bootclasspath restrictions avoid recursion)
*
* . SecurityManager is not null,
* debug is null,
* caller has Policy.getPolicy permission
*/
private static boolean
seeAllp() {
SecurityManager sm =
System.
getSecurityManager();
if (
sm == null) {
return true;
} else {
if (
debug != null) {
if (
sm.
getClass().
getClassLoader() == null &&
Policy.
getPolicyNoCheck().
getClass().
getClassLoader()
== null) {
return true;
}
} else {
try {
sm.
checkPermission(
SecurityConstants.
GET_POLICY_PERMISSION);
return true;
} catch (
SecurityException se) {
// fall thru and return false
}
}
}
return false;
}
private
PermissionCollection mergePermissions() {
if (
staticPermissions)
return
permissions;
PermissionCollection perms =
java.security.
AccessController.
doPrivileged
(new java.security.
PrivilegedAction<
PermissionCollection>() {
public
PermissionCollection run() {
Policy p =
Policy.
getPolicyNoCheck();
return
p.
getPermissions(
ProtectionDomain.this);
}
});
Permissions mergedPerms = new
Permissions();
int
swag = 32;
int
vcap = 8;
Enumeration<
Permission>
e;
List<
Permission>
pdVector = new
ArrayList<>(
vcap);
List<
Permission>
plVector = new
ArrayList<>(
swag);
//
// Build a vector of domain permissions for subsequent merge
if (
permissions != null) {
synchronized (
permissions) {
e =
permissions.
elements();
while (
e.
hasMoreElements()) {
pdVector.
add(
e.
nextElement());
}
}
}
//
// Build a vector of Policy permissions for subsequent merge
if (
perms != null) {
synchronized (
perms) {
e =
perms.
elements();
while (
e.
hasMoreElements()) {
plVector.
add(
e.
nextElement());
vcap++;
}
}
}
if (
perms != null &&
permissions != null) {
//
// Weed out the duplicates from the policy. Unless a refresh
// has occurred since the pd was consed this should result in
// an empty vector.
synchronized (
permissions) {
e =
permissions.
elements(); // domain vs policy
while (
e.
hasMoreElements()) {
Permission pdp =
e.
nextElement();
Class<?>
pdpClass =
pdp.
getClass();
String pdpActions =
pdp.
getActions();
String pdpName =
pdp.
getName();
for (int
i = 0;
i <
plVector.
size();
i++) {
Permission pp =
plVector.
get(
i);
if (
pdpClass.
isInstance(
pp)) {
// The equals() method on some permissions
// have some side effects so this manual
// comparison is sufficient.
if (
pdpName.
equals(
pp.
getName()) &&
pdpActions.
equals(
pp.
getActions())) {
plVector.
remove(
i);
break;
}
}
}
}
}
}
if (
perms !=null) {
// the order of adding to merged perms and permissions
// needs to preserve the bugfix 4301064
for (int
i =
plVector.
size()-1;
i >= 0;
i--) {
mergedPerms.
add(
plVector.
get(
i));
}
}
if (
permissions != null) {
for (int
i =
pdVector.
size()-1;
i >= 0;
i--) {
mergedPerms.
add(
pdVector.
get(
i));
}
}
return
mergedPerms;
}
/**
* Used for storing ProtectionDomains as keys in a Map.
*/
final class
Key {}
static {
SharedSecrets.
setJavaSecurityProtectionDomainAccess(
new
JavaSecurityProtectionDomainAccess() {
@
Override
public
ProtectionDomainCache getProtectionDomainCache() {
return new
ProtectionDomainCache() {
private final
Map<
Key,
PermissionCollection>
map =
Collections.
synchronizedMap
(new
WeakHashMap<
Key,
PermissionCollection>());
public void
put(
ProtectionDomain pd,
PermissionCollection pc) {
map.
put((
pd == null ? null :
pd.
key),
pc);
}
public
PermissionCollection get(
ProtectionDomain pd) {
return
pd == null ?
map.
get(null) :
map.
get(
pd.
key);
}
};
}
@
Override
public boolean
getStaticPermissionsField(
ProtectionDomain pd) {
return
pd.
staticPermissions;
}
});
}
}