/* * @(#)Policy.java 1.94 04/06/28 * * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ package java.security; import java.io.*; import java.lang.RuntimePermission; import java.net.MalformedURLException; import java.net.URL; import java.util.Enumeration; import java.util.Hashtable; import java.util.Vector; import java.util.StringTokenizer; import java.util.PropertyPermission; import java.lang.reflect.*; import java.util.WeakHashMap; import sun.security.util.Debug; import sun.security.util.SecurityConstants; /** * This is an abstract class for representing the system security * policy for a Java application environment (specifying * which permissions are available for code from various sources). * That is, the security policy is represented by a Policy subclass * providing an implementation of the abstract methods * in this Policy class. * *

There is only one Policy object in effect at any given time. * *

The source location for the policy information utilized by the * Policy object is up to the Policy implementation. * The policy configuration may be stored, for example, as a * flat ASCII file, as a serialized binary file of * the Policy class, or as a database. * *

The currently-installed Policy object can be obtained by * calling the getPolicy method, and it can be * changed by a call to the setPolicy method (by * code with permission to reset the Policy). * *

The refresh method causes the policy * object to refresh/reload its current configuration. * *

This is implementation-dependent. For example, if the policy * object stores its policy in configuration files, calling * refresh will cause it to re-read the configuration * policy files. The refreshed policy may not have an effect on classes * in a particular ProtectionDomain. This is dependent on the Policy * provider's implementation of the * {@link #implies(ProtectionDomain,Permission) implies} * method and the PermissionCollection caching strategy. * *

The default Policy implementation can be changed by setting the * value of the "policy.provider" security property (in the Java * security properties file) to the fully qualified name of * the desired Policy implementation class. * The Java security properties file is located in the file named * <JAVA_HOME>/lib/security/java.security, where <JAVA_HOME> * refers to the directory where the JDK was installed. * * @author Roland Schemers * @author Gary Ellison * @version 1.94, 06/28/04 * @see java.security.CodeSource * @see java.security.PermissionCollection * @see java.security.SecureClassLoader */ public abstract class Policy { /** the system-wide policy. */ private static Policy policy; // package private for AccessControlContext private static final Debug debug = Debug.getInstance("policy"); // Cache mapping ProtectionDomain to PermissionCollection private WeakHashMap pdMapping; /** package private for AccessControlContext */ static boolean isSet() { return policy != null; } /** * Returns the installed Policy object. This value should not be cached, * as it may be changed by a call to setPolicy. * This method first calls * SecurityManager.checkPermission with a * SecurityPermission("getPolicy") permission * to ensure it's ok to get the Policy object.. * * @return the installed Policy. * * @throws SecurityException * if a security manager exists and its * checkPermission method doesn't allow * getting the Policy object. * * @see SecurityManager#checkPermission(Permission) * @see #setPolicy(java.security.Policy) */ public static Policy getPolicy() { SecurityManager sm = System.getSecurityManager(); if (sm != null) sm.checkPermission(SecurityConstants.GET_POLICY_PERMISSION); return getPolicyNoCheck(); } /** * Returns the installed Policy object, skipping the security check. * Used by SecureClassLoader and getPolicy. * * @return the installed Policy. * */ static synchronized Policy getPolicyNoCheck() { if (policy == null) { String policy_class = null; policy_class = (String)AccessController.doPrivileged( new PrivilegedAction() { public Object run() { return Security.getProperty("policy.provider"); } }); if (policy_class == null) { policy_class = "sun.security.provider.PolicyFile"; } try { policy = (Policy) Class.forName(policy_class).newInstance(); } catch (Exception e) { /* * The policy_class seems to be an extension * so we have to bootstrap loading it via a policy * provider that is on the bootclasspath * If it loads then shift gears to using the configured * provider. */ // install the bootstrap provider to avoid recursion policy = new sun.security.provider.PolicyFile(); final String pc = policy_class; Policy p = (Policy) AccessController.doPrivileged(new PrivilegedAction() { public Object run() { try { ClassLoader cl = ClassLoader.getSystemClassLoader(); // we want the extension loader ClassLoader extcl = null; while (cl != null) { extcl = cl; cl = cl.getParent(); } return (extcl != null? Class.forName (pc, true, extcl).newInstance(): null); } catch (Exception e) { return null; } } }); /* * if it loaded install it as the policy provider. Otherwise * continue to use the system default implementation */ if (p != null) policy = p; if (p == null && debug != null) { debug.println("policy provider " + policy_class + " not available;using " + "sun.security.provider.PolicyFile"); e.printStackTrace(); } } } return policy; } /** * Sets the system-wide Policy object. This method first calls * SecurityManager.checkPermission with a * SecurityPermission("setPolicy") * permission to ensure it's ok to set the Policy. * * @param p the new system Policy object. * * @throws SecurityException * if a security manager exists and its * checkPermission method doesn't allow * setting the Policy. * * @see SecurityManager#checkPermission(Permission) * @see #getPolicy() * */ public static void setPolicy(Policy p) { SecurityManager sm = System.getSecurityManager(); if (sm != null) sm.checkPermission( new SecurityPermission("setPolicy")); if (p != null) { initPolicy(p); } synchronized (Policy.class) { Policy.policy = p; } } /** * Initialize superclass state such that a legacy provider can * handle queries for itself. * * @since 1.4 */ private static void initPolicy (final Policy p) { /* * A policy provider not on the bootclasspath could trigger * security checks fulfilling a call to either Policy.implies * or Policy.getPermissions. If this does occur the provider * must be able to answer for it's own ProtectionDomain * without triggering additional security checks, otherwise * the policy implementation will end up in an infinite * recursion. * * To mitigate this, the provider can collect it's own * ProtectionDomain and associate a PermissionCollection while * it is being installed. The currently installed policy * provider (if there is one) will handle calls to * Policy.implies or Policy.getPermissions during this * process. * * This Policy superclass caches away the ProtectionDomain and * statically binds permissions so that legacy Policy * implementations will continue to function. */ ProtectionDomain policyDomain = (ProtectionDomain) AccessController.doPrivileged(new PrivilegedAction() { public Object run() { return p.getClass().getProtectionDomain(); } }); /* * Collect the permissions granted to this protection domain * so that the provider can be security checked while processing * calls to Policy.implies or Policy.getPermissions. */ PermissionCollection policyPerms = null; synchronized (p) { if (p.pdMapping == null) { p.pdMapping = new WeakHashMap(); } } if (policyDomain.getCodeSource() != null) { if (Policy.isSet()) { policyPerms = policy.getPermissions(policyDomain); } if (policyPerms == null) { // assume it has all policyPerms = new Permissions(); policyPerms.add(SecurityConstants.ALL_PERMISSION); } synchronized (p.pdMapping) { // cache of pd to permissions p.pdMapping.put(policyDomain, policyPerms); } } return; } /** * Evaluates the global policy and returns a * PermissionCollection object specifying the set of * permissions allowed for code from the specified * code source. * * @param codesource the CodeSource associated with the caller. * This encapsulates the original location of the code (where the code * came from) and the public key(s) of its signer. * * @return the set of permissions allowed for code from codesource * according to the policy.The returned set of permissions must be * a new mutable instance and it must support heterogeneous * Permission types. * */ public abstract PermissionCollection getPermissions(CodeSource codesource); /** * Evaluates the global policy and returns a * PermissionCollection object specifying the set of * permissions allowed given the characteristics of the * protection domain. * * @param domain the ProtectionDomain associated with the caller. * * @return the set of permissions allowed for the domain * according to the policy.The returned set of permissions must be * a new mutable instance and it must support heterogeneous * Permission types. * * @see java.security.ProtectionDomain * @see java.security.SecureClassLoader * @since 1.4 */ public PermissionCollection getPermissions(ProtectionDomain domain) { PermissionCollection pc = null; if (domain == null) return new Permissions(); if (pdMapping == null) { initPolicy(this); } synchronized (pdMapping) { pc = (PermissionCollection)pdMapping.get(domain); } if (pc != null) { Permissions perms = new Permissions(); synchronized (pc) { for (Enumeration e = pc.elements() ; e.hasMoreElements() ;) { perms.add((Permission)e.nextElement()); } } return perms; } pc = getPermissions(domain.getCodeSource()); if (pc == null) { pc = new Permissions(); } addStaticPerms(pc, domain.getPermissions()); return pc; } /** * add static permissions to provided permission collection */ private void addStaticPerms(PermissionCollection perms, PermissionCollection statics) { if (statics != null) { synchronized (statics) { Enumeration e = statics.elements(); while (e.hasMoreElements()) { perms.add((Permission)e.nextElement()); } } } } /** * Evaluates the global policy for the permissions granted to * the ProtectionDomain and tests whether the permission is * granted. * * @param domain the ProtectionDomain to test * @param permission the Permission object to be tested for implication. * * @return true if "permission" is a proper subset of a permission * granted to this ProtectionDomain. * * @see java.security.ProtectionDomain * @since 1.4 */ public boolean implies(ProtectionDomain domain, Permission permission) { PermissionCollection pc; if (pdMapping == null) { initPolicy(this); } synchronized (pdMapping) { pc = (PermissionCollection)pdMapping.get(domain); } if (pc != null) { return pc.implies(permission); } pc = getPermissions(domain); if (pc == null) { return false; } synchronized (pdMapping) { // cache it pdMapping.put(domain, pc); } return pc.implies(permission); } /** * Refreshes/reloads the policy configuration. The behavior of this method * depends on the implementation. For example, calling refresh * on a file-based policy will cause the file to be re-read. * */ public abstract void refresh(); }