All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.enterprise.security.provider.PolicyFile Maven / Gradle / Ivy

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package com.sun.enterprise.security.provider;

import java.io.*;
 import java.lang.reflect.*;
 import java.net.URL;
 import java.net.URI;
 import java.util.*;
 import java.util.Enumeration;
 import java.util.List;
 import java.util.StringTokenizer;
 import java.util.ArrayList;
 import java.util.ListIterator;
 import java.util.WeakHashMap;
 import java.text.MessageFormat;
 import com.sun.security.auth.PrincipalComparator;
 import java.security.*;
 import java.security.cert.Certificate;
 import java.security.cert.X509Certificate;
 import javax.security.auth.Subject;
 import javax.security.auth.x500.X500Principal;
 import java.io.FilePermission;
 import java.net.SocketPermission;
 import java.net.NetPermission;
 import java.util.PropertyPermission;
 import java.util.concurrent.atomic.AtomicReference;
 import java.awt.AWTPermission;
 /*
 import javax.security.auth.AuthPermission;
 import javax.security.auth.kerberos.ServicePermission;
 import javax.security.auth.kerberos.DelegationPermission;
 import java.io.SerializablePermission;
 import java.util.logging.LoggingPermission;
 import java.sql.SQLPermission;
 import java.lang.reflect.ReflectPermission;
 import javax.sound.sampled.AudioPermission;
 import javax.net.ssl.SSLPermission;
 */
 import sun.security.provider.SystemIdentity;
 import sun.security.provider.SystemSigner;
 import sun.security.util.PropertyExpander;
 import sun.security.util.Debug;
 import sun.security.util.ResourcesMgr;
 import sun.security.util.SecurityConstants;
 import sun.net.www.ParseUtil;

/**
* This class represents a default implementation for
 * java.security.Policy.
 *
 * Note:
 * For backward compatibility with JAAS 1.0 it loads
 * both java.auth.policy and java.policy. However it
 * is recommended that java.auth.policy be not used
 * and the java.policy contain all grant entries including
 * that contain principal-based entries.
 *
 *
 * 

This object stores the policy for entire Java runtime, * and is the amalgamation of multiple static policy * configurations that resides in files. * The algorithm for locating the policy file(s) and reading their * information into this Policy object is: * *

    *
  1. * Loop through the java.security.Security properties, * policy.url.1, policy.url.2, ..., * policy.url.X" and * auth.policy.url.1, auth.policy.url.2, ..., * auth.policy.url.X". These properties are set * in the Java security properties file, which is located in the file named * <JAVA_HOME>/lib/security/java.security. * <JAVA_HOME> refers to the value of the java.home system property, * and specifies the directory where the JRE is installed. * Each property value specifies a URL pointing to a * policy file to be loaded. Read in and load each policy. * * auth.policy.url is supported only for backward compatibility. * *
  2. * The java.lang.System property java.security.policy * may also be set to a URL pointing to another policy file * (which is the case when a user uses the -D switch at runtime). * If this property is defined, and its use is allowed by the * security property file (the Security property, * policy.allowSystemProperty is set to true), * also load that policy. * *
  3. * The java.lang.System property * java.security.auth.policy may also be set to a * URL pointing to another policy file * (which is the case when a user uses the -D switch at runtime). * If this property is defined, and its use is allowed by the * security property file (the Security property, * policy.allowSystemProperty is set to true), * also load that policy. * * java.security.auth.policy is supported only for backward * compatibility. * * If the java.security.policy or * java.security.auth.policy property is defined using * "==" (rather than "="), then ignore all other specified * policies and only load this policy. *
* * Each policy file consists of one or more grant entries, each of * which consists of a number of permission entries. * *
 *   grant signedBy "alias", codeBase "URL",
 *         principal principalClass "principalName",
 *         principal principalClass "principalName",
 *         ... {
 *
 *     permission Type "name "action",
 *         signedBy "alias";
 *     permission Type "name "action",
 *         signedBy "alias";
 *     ....
 *   };
 * 
* * All non-bold items above must appear as is (although case * doesn't matter and some are optional, as noted below). * principal entries are optional and need not be present. * Italicized items represent variable values. * *

A grant entry must begin with the word grant. * The signedBy,codeBase and principal * name/value pairs are optional. * If they are not present, then any signer (including unsigned code) * will match, and any codeBase will match. * Note that the principalClass * may be set to the wildcard value, *, which allows it to match * any Principal class. In addition, the principalName * may also be set to the wildcard value, *, allowing it to match * any Principal name. When setting the principalName * to the *, do not surround the * with quotes. * *

A permission entry must begin with the word permission. * The word Type in the template above is * a specific permission type, such as java.io.FilePermission * or java.lang.RuntimePermission. * *

The "action" is required for * many permission types, such as java.io.FilePermission * (where it specifies what type of file access that is permitted). * It is not required for categories such as * java.lang.RuntimePermission * where it is not necessary - you either have the * permission specified by the "name" * value following the type name or you don't. * *

The signedBy name/value pair for a permission entry * is optional. If present, it indicates a signed permission. That is, * the permission class itself must be signed by the given alias in * order for it to be granted. For example, * suppose you have the following grant entry: * *

 *   grant principal foo.com.Principal "Duke" {
 *     permission Foo "foobar", signedBy "FooSoft";
 *   }
 * 
* *

Then this permission of type Foo is granted if the * Foo.class permission has been signed by the * "FooSoft" alias, or if XXX Foo.class is a * system class (i.e., is found on the CLASSPATH). * * *

Items that appear in an entry must appear in the specified order * (permission, Type, "name", and * "action"). An entry is terminated with a semicolon. * *

Case is unimportant for the identifiers (permission, * signedBy, codeBase, etc.) but is * significant for the Type * or for any string that is passed in as a value.

* *

An example of two entries in a policy configuration file is *

 *   // if the code is comes from "foo.com" and is running as "Duke",
 *   // grant it read/write to all files in /tmp.
 *
 *   grant codeBase "foo.com", principal foo.com.Principal "Duke" {
 *              permission java.io.FilePermission "/tmp/*", "read,write";
 *   };
 *
 *   // grant any code running as "Duke" permission to read
 *   // the "java.vendor" Property.
 *
 *   grant principal foo.com.Principal "Duke" {
 *         permission java.util.PropertyPermission "java.vendor";
 *
 *
 * 
* This Policy implementation supports special handling of any * permission that contains the string, "${{self}}", as part of * its target name. When such a permission is evaluated * (such as during a security check), ${{self}} is replaced * with one or more Principal class/name pairs. The exact * replacement performed depends upon the contents of the * grant clause to which the permission belongs. *

* * If the grant clause does not contain any principal information, * the permission will be ignored (permissions containing * ${{self}} in their target names are only valid in the context * of a principal-based grant clause). For example, BarPermission * will always be ignored in the following grant clause: * *

 *    grant codebase "www.foo.com", signedby "duke" {
 *      permission BarPermission "... ${{self}} ...";
 *    };
 *
* * If the grant clause contains principal information, ${{self}} * will be replaced with that same principal information. * For example, ${{self}} in BarPermission will be replaced by * javax.security.auth.x500.X500Principal "cn=Duke" * in the following grant clause: * *
 *    grant principal javax.security.auth.x500.X500Principal "cn=Duke" {
 *      permission BarPermission "... ${{self}} ...";
 *    };
 *  
* * If there is a comma-separated list of principals in the grant * clause, then ${{self}} will be replaced by the same * comma-separated list or principals. * In the case where both the principal class and name are * wildcarded in the grant clause, ${{self}} is replaced * with all the principals associated with the Subject * in the current AccessControlContext. * * *

For PrivateCredentialPermissions, you can also use "self" * instead of "${{self}}". However the use of "self" is * deprecated in favour of "${{self}}". * * @version %I%, %G% * @see java.security.CodeSource * @see java.security.Permissions * @see java.security.ProtectionDomain */ public class PolicyFile extends java.security.Policy { private static final Debug debug = Debug.getInstance("policy"); private static final String NONE = "NONE"; private static final String P11KEYSTORE = "PKCS11"; private static final String SELF = "${{self}}"; private static final String X500PRINCIPAL = "javax.security.auth.x500.X500Principal"; private static final String POLICY = "java.security.policy"; private static final String SECURITY_MANAGER = "java.security.manager"; private static final String POLICY_URL = "policy.url."; private static final String AUTH_POLICY = "java.security.auth.policy"; private static final String AUTH_POLICY_URL = "auth.policy.url."; private static final int DEFAULT_CACHE_SIZE = 1; /** * the scope to check */ private static IdentityScope scope = null; // contains the policy grant entries, PD cache, and alias mapping private AtomicReference policyInfo = new AtomicReference(); private boolean constructed = false; private boolean expandProperties = true; private boolean ignoreIdentityScope = false; private boolean allowSystemProperties = true; private boolean notUtf8 = false; private URL url; // for use with the reflection API private static final Class[] PARAMS0 = {}; private static final Class[] PARAMS1 = {String.class}; private static final Class[] PARAMS2 = {String.class, String.class}; /** * Initializes the Policy object and reads the default policy * configuration file(s) into the Policy object. */ public PolicyFile() { init((URL) null); } /** * Initializes the Policy object and reads the default policy * from the specified URL only. */ public PolicyFile(URL url) { this.url = url; init(url); } /** * Initializes the Policy object and reads the default policy * configuration file(s) into the Policy object. *

* The algorithm for locating the policy file(s) and reading their * information into the Policy object is: *

      *   loop through the Security Properties named "policy.url.1",
      *  ""policy.url.2", "auth.policy.url.1",  "auth.policy.url.2" etc, until
      *   you don't find one. Each of these specify a policy file.
      * 

* if none of these could be loaded, use a builtin static policy * equivalent to the default lib/security/java.policy file. *

* if the system property "java.policy" or "java.auth.policy" is defined * (which is the * case when the user uses the -D switch at runtime), and * its use is allowed by the security property file, * also load it. *

*

* Each policy file consists of one or more grant entries, each of * which consists of a number of permission entries. *

      *   grant signedBy "alias", codeBase "URL" {
      *     permission Type "name", "action",
      *         signedBy "alias";
      *     ....
      *     permission Type "name", "action",
      *         signedBy "alias";
      *   };
      * 

*

*

* All non-italicized items above must appear as is (although case * doesn't matter and some are optional, as noted below). * Italicized items represent variable values. *

*

A grant entry must begin with the word grant. * The signedBy and codeBase name/value * pairs are optional. * If they are not present, then any signer (including unsigned code) * will match, and any codeBase will match. *

*

A permission entry must begin with the word permission. * The word Type in the template above would actually * be a specific permission type, such as * java.io.FilePermission or * java.lang.RuntimePermission. *

*

The "action" is required for * many permission types, such as java.io.FilePermission * (where it specifies what type of file access is permitted). * It is not required for categories such as * java.lang.RuntimePermission * where it is not necessary - you either have the * permission specified by the "name" * value following the type name or you don't. *

*

The signedBy name/value pair for a permission entry * is optional. If present, it indicates a signed permission. That is, * the permission class itself must be signed by the given alias in * order for it to be granted. For example, * suppose you have the following grant entry: *

*

      *   grant {
      *     permission Foo "foobar", signedBy "FooSoft";
      *   }
      * 
*

*

Then this permission of type Foo is granted if the * Foo.class permission has been signed by the * "FooSoft" alias, or if Foo.class is a * system class (i.e., is found on the CLASSPATH). *

*

Items that appear in an entry must appear in the specified order * (permission, Type, "name", and * "action"). An entry is terminated with a semicolon. *

*

Case is unimportant for the identifiers (permission, * signedBy, codeBase, etc.) but is * significant for the Type * or for any string that is passed in as a value.

*

*

An example of two entries in a policy configuration file is *

      *   //  if the code is signed by "Duke", grant it read/write to all
      *   // files in /tmp.
      * 

* grant signedBy "Duke" { * permission java.io.FilePermission "/tmp/*", "read,write"; * }; *

* // grant everyone the following permission *

* grant { * permission java.util.PropertyPermission "java.vendor"; * }; *

*/ private void init(URL url) { // Properties are set once for each init(); ignore changes between // between diff invocations of initPolicyFile(policy, url, info). String numCacheStr = AccessController.doPrivileged(new PrivilegedAction() { public String run() { expandProperties = "true".equalsIgnoreCase (Security.getProperty("policy.expandProperties")); ignoreIdentityScope = "true".equalsIgnoreCase (Security.getProperty("policy.ignoreIdentityScope")); allowSystemProperties = "true".equalsIgnoreCase (Security.getProperty("policy.allowSystemProperty")); notUtf8 = "false".equalsIgnoreCase (System.getProperty("sun.security.policy.utf8")); return System.getProperty("sun.security.policy.numcaches"); } }); int numCaches; if (numCacheStr != null) { try { numCaches = Integer.parseInt(numCacheStr); } catch (NumberFormatException e) { numCaches = DEFAULT_CACHE_SIZE; } } else { numCaches = DEFAULT_CACHE_SIZE; } // System.out.println("number caches=" + numCaches); PolicyInfo newInfo = new PolicyInfo(numCaches); initPolicyFile(newInfo, url); policyInfo.set(newInfo); } private void initPolicyFile(final PolicyInfo newInfo, final URL url) { if (url != null) { /** * If the caller specified a URL via Policy.getInstance, * we only read from that URL */ if (debug != null) { debug.println("reading " + url); } AccessController.doPrivileged(new PrivilegedAction() { public Void run() { if (init(url, newInfo) == false) { // use static policy if all else fails initStaticPolicy(newInfo); } return null; } }); } else { /** * Caller did not specify URL via Policy.getInstance. * Read from URLs listed in the java.security properties file. * * We call initPolicyFile with POLICY , POLICY_URL and then * call it with AUTH_POLICY and AUTH_POLICY_URL * So first we will process the JAVA standard policy * and then process the JAVA AUTH Policy. * This is for backward compatibility as well as to handle * cases where the user has a single unified policyfile * with both java policy entries and auth entries */ boolean loaded_one = initPolicyFile(POLICY, POLICY_URL, newInfo); // To maintain strict backward compatibility // we load the static policy only if POLICY load failed if (!loaded_one) { // use static policy if all else fails initStaticPolicy(newInfo); } initPolicyFile(AUTH_POLICY, AUTH_POLICY_URL, newInfo); } } private boolean initPolicyFile(final String propname, final String urlname, final PolicyInfo newInfo) { Boolean loadedPolicy = AccessController.doPrivileged(new PrivilegedAction() { public Boolean run() { boolean loaded_policy = false; if (allowSystemProperties) { String extra_policy = System.getProperty(propname); if (extra_policy != null) { boolean overrideAll = false; if (extra_policy.startsWith("=")) { overrideAll = true; extra_policy = extra_policy.substring(1); } try { extra_policy = PropertyExpander.expand(extra_policy); URL policyURL; File policyFile = new File(extra_policy); if (policyFile.exists()) { policyURL = ParseUtil.fileToEncodedURL (new File(policyFile.getCanonicalPath())); } else { policyURL = new URL(extra_policy); } if (debug != null) debug.println("reading " + policyURL); if (init(policyURL, newInfo)) loaded_policy = true; } catch (Exception e) { // ignore. if (debug != null) { debug.println("caught exception: " + e); } } if (overrideAll) { if (debug != null) { debug.println("overriding other policies!"); } return loaded_policy; } } } int n = 1; String policy_uri; while ((policy_uri = Security.getProperty(urlname + n)) != null) { try { URL policy_url = null; String expanded_uri = PropertyExpander.expand (policy_uri).replace(File.separatorChar, '/'); if (policy_uri.startsWith("file:${java.home}/") || policy_uri.startsWith("file:${user.home}/")) { // this special case accommodates // the situation java.home/user.home // expand to a single slash, resulting in // a file://foo URI policy_url = new File (expanded_uri.substring(5)).toURI().toURL(); } else { policy_url = new URI(expanded_uri).toURL(); } if (debug != null) debug.println("reading " + policy_url); if (init(policy_url, newInfo)) loaded_policy = true; } catch (Exception e) { if (debug != null) { debug.println("error reading policy " + e); e.printStackTrace(); } // ignore that policy } n++; } return loaded_policy; } }); return loadedPolicy; } /** * Reads a policy configuration into the Policy object using a * Reader object. */ private boolean init(URL policy, PolicyInfo newInfo) { boolean success = false; PolicyParser pp = new PolicyParser(expandProperties); InputStreamReader isr = null; try { // read in policy using UTF-8 by default // // check non-standard system property to see if // the default encoding should be used instead if (notUtf8) { isr = new InputStreamReader (PolicyUtil.getInputStream(policy)); } else { isr = new InputStreamReader (PolicyUtil.getInputStream(policy), "UTF-8"); } pp.read(isr); KeyStore keyStore = null; try { keyStore = PolicyUtil.getKeyStore (policy, pp.getKeyStoreUrl(), pp.getKeyStoreType(), pp.getKeyStoreProvider(), pp.getStorePassURL(), debug); } catch (Exception e) { // ignore, treat it like we have no keystore if (debug != null) { e.printStackTrace(); } } Enumeration enum_ = pp.grantElements(); while (enum_.hasMoreElements()) { PolicyParser.GrantEntry ge = enum_.nextElement(); addGrantEntry(ge, keyStore, newInfo); } } catch (PolicyParser.ParsingException pe) { MessageFormat form = new MessageFormat(ResourcesMgr.getString (POLICY + ": error parsing policy:\n\tmessage")); Object[] source = {policy, pe.getLocalizedMessage()}; System.err.println(form.format(source)); if (debug != null) pe.printStackTrace(); } catch (Exception e) { if (debug != null) { debug.println("error parsing " + policy); debug.println(e.toString()); e.printStackTrace(); } } finally { if (isr != null) { try { isr.close(); success = true; } catch (IOException e) { // ignore the exception } } else { success = true; } } return success; } private void initStaticPolicy(final PolicyInfo newInfo) { AccessController.doPrivileged(new PrivilegedAction() { public Void run() { PolicyEntry pe = new PolicyEntry(new CodeSource(null, (Certificate[]) null)); pe.add(SecurityConstants.LOCAL_LISTEN_PERMISSION); pe.add(new PropertyPermission("java.version", SecurityConstants.PROPERTY_READ_ACTION)); pe.add(new PropertyPermission("java.vendor", SecurityConstants.PROPERTY_READ_ACTION)); pe.add(new PropertyPermission("java.vendor.url", SecurityConstants.PROPERTY_READ_ACTION)); pe.add(new PropertyPermission("java.class.version", SecurityConstants.PROPERTY_READ_ACTION)); pe.add(new PropertyPermission("os.name", SecurityConstants.PROPERTY_READ_ACTION)); pe.add(new PropertyPermission("os.version", SecurityConstants.PROPERTY_READ_ACTION)); pe.add(new PropertyPermission("os.arch", SecurityConstants.PROPERTY_READ_ACTION)); pe.add(new PropertyPermission("file.separator", SecurityConstants.PROPERTY_READ_ACTION)); pe.add(new PropertyPermission("path.separator", SecurityConstants.PROPERTY_READ_ACTION)); pe.add(new PropertyPermission("line.separator", SecurityConstants.PROPERTY_READ_ACTION)); pe.add(new PropertyPermission ("java.specification.version", SecurityConstants.PROPERTY_READ_ACTION)); pe.add(new PropertyPermission ("java.specification.vendor", SecurityConstants.PROPERTY_READ_ACTION)); pe.add(new PropertyPermission ("java.specification.name", SecurityConstants.PROPERTY_READ_ACTION)); pe.add(new PropertyPermission ("java.vm.specification.version", SecurityConstants.PROPERTY_READ_ACTION)); pe.add(new PropertyPermission ("java.vm.specification.vendor", SecurityConstants.PROPERTY_READ_ACTION)); pe.add(new PropertyPermission ("java.vm.specification.name", SecurityConstants.PROPERTY_READ_ACTION)); pe.add(new PropertyPermission("java.vm.version", SecurityConstants.PROPERTY_READ_ACTION)); pe.add(new PropertyPermission("java.vm.vendor", SecurityConstants.PROPERTY_READ_ACTION)); pe.add(new PropertyPermission("java.vm.name", SecurityConstants.PROPERTY_READ_ACTION)); // No need to sync because noone has access to newInfo yet newInfo.policyEntries.add(pe); // Add AllPermissions for standard extensions String[] extCodebases = PolicyParser.parseExtDirs( PolicyParser.EXTDIRS_EXPANSION, 0); if (extCodebases != null && extCodebases.length > 0) { for (int i = 0; i < extCodebases.length; i++) { try { pe = new PolicyEntry(canonicalizeCodebase( new CodeSource(new URL(extCodebases[i]), (Certificate[]) null), false)); pe.add(SecurityConstants.ALL_PERMISSION); // No need to sync because noone has access to // newInfo yet newInfo.policyEntries.add(pe); } catch (Exception e) { // this is probably bad (though not dangerous). // What should we do? } } } return null; } }); } /** * Given a GrantEntry, create a codeSource. * * @return null if signedBy alias is not recognized */ private CodeSource getCodeSource(PolicyParser.GrantEntry ge, KeyStore keyStore, PolicyInfo newInfo) throws java.net.MalformedURLException { Certificate[] certs = null; if (ge.signedBy != null) { certs = getCertificates(keyStore, ge.signedBy, newInfo); if (certs == null) { // we don't have a key for this alias, // just return if (debug != null) { debug.println(" -- No certs for alias '" + ge.signedBy + "' - ignoring entry"); } return null; } } URL location; if (ge.codeBase != null) location = new URL(ge.codeBase); else location = null; return (canonicalizeCodebase(new CodeSource(location, certs), false)); } /** * Add one policy entry to the list. */ private void addGrantEntry(PolicyParser.GrantEntry ge, KeyStore keyStore, PolicyInfo newInfo) { if (debug != null) { debug.println("Adding policy entry: "); debug.println(" signedBy " + ge.signedBy); debug.println(" codeBase " + ge.codeBase); if (ge.principals != null && ge.principals.size() > 0) { ListIterator li = ge.principals.listIterator(); while (li.hasNext()) { PolicyParser.PrincipalEntry pppe = li.next(); debug.println(" " + pppe.toString()); } } } try { CodeSource codesource = getCodeSource(ge, keyStore, newInfo); // skip if signedBy alias was unknown... if (codesource == null) return; // perform keystore alias principal replacement. // for example, if alias resolves to X509 certificate, // replace principal with: // -- skip if alias is unknown if (replacePrincipals(ge.principals, keyStore) == false) return; PolicyEntry entry = new PolicyEntry(codesource, ge.principals); Enumeration enum_ = ge.permissionElements(); while (enum_.hasMoreElements()) { PolicyParser.PermissionEntry pe = enum_.nextElement(); try { // perform ${{ ... }} expansions within permission name expandPermissionName(pe, keyStore); // XXX special case PrivateCredentialPermission-SELF Permission perm; if (pe.permission.equals ("javax.security.auth.PrivateCredentialPermission") && pe.name.endsWith(" self")) { pe.name = pe.name.substring(0, pe.name.indexOf("self")) + SELF; } // check for self if (pe.name != null && pe.name.indexOf(SELF) != -1) { // Create a "SelfPermission" , it could be an // an unresolved permission which will be resolved // when implies is called // Add it to entry Certificate certs[]; if (pe.signedBy != null) { certs = getCertificates(keyStore, pe.signedBy, newInfo); } else { certs = null; } perm = new SelfPermission(pe.permission, pe.name, pe.action, certs); } else { perm = getInstance(pe.permission, pe.name, pe.action); } entry.add(perm); if (debug != null) { debug.println(" " + perm); } } catch (ClassNotFoundException cnfe) { Certificate certs[]; if (pe.signedBy != null) { certs = getCertificates(keyStore, pe.signedBy, newInfo); } else { certs = null; } // only add if we had no signer or we had a // a signer and found the keys for it. if (certs != null || pe.signedBy == null) { Permission perm = new UnresolvedPermission( pe.permission, pe.name, pe.action, certs); entry.add(perm); if (debug != null) { debug.println(" " + perm); } } } catch (java.lang.reflect.InvocationTargetException ite) { MessageFormat form = new MessageFormat (ResourcesMgr.getString (POLICY + ": error adding Permission, perm:\n\tmessage")); Object[] source = {pe.permission, ite.getTargetException().toString()}; System.err.println(form.format(source)); } catch (Exception e) { MessageFormat form = new MessageFormat (ResourcesMgr.getString (POLICY + ": error adding Permission, perm:\n\tmessage")); Object[] source = {pe.permission, e.toString()}; System.err.println(form.format(source)); } } // No need to sync because noone has access to newInfo yet newInfo.policyEntries.add(entry); } catch (Exception e) { MessageFormat form = new MessageFormat(ResourcesMgr.getString (POLICY + ": error adding Entry:\n\tmessage")); Object[] source = {e.toString()}; System.err.println(form.format(source)); } if (debug != null) debug.println(); } /** * Returns a new Permission object of the given Type. The Permission is * created by getting the * Class object using the Class.forName method, and using * the reflection API to invoke the (String name, String actions) * constructor on the * object. * * @param type the type of Permission being created. * @param name the name of the Permission being created. * @param actions the actions of the Permission being created. * @throws ClassNotFoundException if the particular Permission * class could not be found. * @throws IllegalAccessException if the class or initializer is * not accessible. * @throws InstantiationException if getInstance tries to * instantiate an abstract class or an interface, or if the * instantiation fails for some other reason. * @throws NoSuchMethodException if the (String, String) constructor * is not found. * @throws InvocationTargetException if the underlying Permission * constructor throws an exception. */ private static final Permission getInstance(String type, String name, String actions) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { //XXX we might want to keep a hash of created factories... Class pc = Class.forName(type); Permission answer = getKnownInstance(pc, name, actions); if (answer != null) { return answer; } if (name == null && actions == null) { try { Constructor c = pc.getConstructor(PARAMS0); return (Permission) c.newInstance(new Object[]{}); } catch (NoSuchMethodException ne) { try { Constructor c = pc.getConstructor(PARAMS1); return (Permission) c.newInstance( new Object[]{name}); } catch (NoSuchMethodException ne1) { Constructor c = pc.getConstructor(PARAMS2); return (Permission) c.newInstance( new Object[]{name, actions}); } } } else { if (name != null && actions == null) { try { Constructor c = pc.getConstructor(PARAMS1); return (Permission) c.newInstance(new Object[]{name}); } catch (NoSuchMethodException ne) { Constructor c = pc.getConstructor(PARAMS2); return (Permission) c.newInstance( new Object[]{name, actions}); } } else { Constructor c = pc.getConstructor(PARAMS2); return (Permission) c.newInstance( new Object[]{name, actions}); } } } /** * Creates one of the well-known permissions directly instead of * via reflection. Keep list short to not penalize non-JDK-defined * permissions. */ private static final Permission getKnownInstance(Class claz, String name, String actions) { // XXX shorten list to most popular ones? if (claz.equals(FilePermission.class)) { return new FilePermission(name, actions); } else if (claz.equals(SocketPermission.class)) { return new SocketPermission(name, actions); } else if (claz.equals(RuntimePermission.class)) { return new RuntimePermission(name, actions); } else if (claz.equals(PropertyPermission.class)) { return new PropertyPermission(name, actions); } else if (claz.equals(NetPermission.class)) { return new NetPermission(name, actions); } else if (claz.equals(AllPermission.class)) { return SecurityConstants.ALL_PERMISSION; } else if (claz.equals(AWTPermission.class)) { return new AWTPermission(name, actions); /* } else if (claz.equals(ReflectPermission.class)) { return new ReflectPermission(name, actions); } else if (claz.equals(SecurityPermission.class)) { return new SecurityPermission(name, actions); } else if (claz.equals(PrivateCredentialPermission.class)) { return new PrivateCredentialPermission(name, actions); } else if (claz.equals(AuthPermission.class)) { return new AuthPermission(name, actions); } else if (claz.equals(ServicePermission.class)) { return new ServicePermission(name, actions); } else if (claz.equals(DelegationPermission.class)) { return new DelegationPermission(name, actions); } else if (claz.equals(SerializablePermission.class)) { return new SerializablePermission(name, actions); } else if (claz.equals(AudioPermission.class)) { return new AudioPermission(name, actions); } else if (claz.equals(SSLPermission.class)) { return new SSLPermission(name, actions); } else if (claz.equals(LoggingPermission.class)) { return new LoggingPermission(name, actions); } else if (claz.equals(SQLPermission.class)) { return new SQLPermission(name, actions); */ } else { return null; } } /** * Fetch all certs associated with this alias. */ private Certificate[] getCertificates (KeyStore keyStore, String aliases, PolicyInfo newInfo) { List vcerts = null; StringTokenizer st = new StringTokenizer(aliases, ","); int n = 0; while (st.hasMoreTokens()) { String alias = st.nextToken().trim(); n++; Certificate cert = null; // See if this alias's cert has already been cached synchronized (newInfo.aliasMapping) { cert = (Certificate) newInfo.aliasMapping.get(alias); if (cert == null && keyStore != null) { try { cert = keyStore.getCertificate(alias); } catch (KeyStoreException kse) { // never happens, because keystore has already been loaded // when we call this } if (cert != null) { newInfo.aliasMapping.put(alias, cert); newInfo.aliasMapping.put(cert, alias); } } } if (cert != null) { if (vcerts == null) vcerts = new ArrayList(); vcerts.add(cert); } } // make sure n == vcerts.size, since we are doing a logical *and* if (vcerts != null && n == vcerts.size()) { Certificate[] certs = new Certificate[vcerts.size()]; vcerts.toArray(certs); return certs; } else { return null; } } /** * Refreshes the policy object by re-reading all the policy files. */ public void refresh() { init(url); } /** * Evaluates the the global policy for the permissions granted to * the ProtectionDomain and tests whether the permission is * granted. * * @param pd the ProtectionDomain to test * @param p 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 */ public boolean implies(ProtectionDomain pd, Permission p) { PolicyInfo pi = policyInfo.get(); Map pdMap = pi.getPdMapping(); PermissionCollection pc = pdMap.get(pd); if (pc != null) { return pc.implies(p); } pc = getPermissions(pd); if (pc == null) { return false; } // cache mapping of protection domain to its PermissionCollection pdMap.put(pd, pc); return pc.implies(p); } /** * Examines this Policy and returns the permissions granted * to the specified ProtectionDomain. This includes * the permissions currently associated with the domain as well * as the policy permissions granted to the domain's * CodeSource, ClassLoader, and Principals. *

*

Note that this Policy implementation has * special handling for PrivateCredentialPermissions. * When this method encounters a PrivateCredentialPermission * which specifies "self" as the Principal class and name, * it does not add that Permission to the returned * PermissionCollection. Instead, it builds * a new PrivateCredentialPermission * for each Principal associated with the provided * Subject. Each new PrivateCredentialPermission * contains the same Credential class as specified in the * originally granted permission, as well as the Class and name * for the respective Principal. *

*

* * @param domain the Permissions granted to this * ProtectionDomain are returned. * @return the Permissions granted to the provided * ProtectionDomain. */ public PermissionCollection getPermissions(ProtectionDomain domain) { Permissions perms = new Permissions(); if (domain == null) return perms; // first get policy perms getPermissions(perms, domain); // add static perms // - adding static perms after policy perms is necessary // to avoid a regression for 4301064 PermissionCollection pc = domain.getPermissions(); if (pc != null) { synchronized (pc) { Enumeration e = pc.elements(); while (e.hasMoreElements()) { perms.add(e.nextElement()); } } } return perms; } /** * Examines this Policy and creates a PermissionCollection object with * the set of permissions for the specified CodeSource. * * @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 according to the policy. */ public PermissionCollection getPermissions(CodeSource codesource) { return getPermissions(new Permissions(), codesource); } /** * Examines the global policy and returns the provided Permissions * object with additional permissions granted to the specified * ProtectionDomain. * * @param perms the Permissions to populate * @param pd the ProtectionDomain associated with the caller. * @return the set of Permissions according to the policy. */ private PermissionCollection getPermissions(Permissions perms, ProtectionDomain pd) { if (debug != null) { debug.println("getPermissions:\n\t" + printPD(pd)); } final CodeSource cs = pd.getCodeSource(); if (cs == null) return perms; CodeSource canonCodeSource = AccessController.doPrivileged( new java.security.PrivilegedAction() { public CodeSource run() { return canonicalizeCodebase(cs, true); } }); return getPermissions(perms, canonCodeSource, pd.getPrincipals()); } /** * Examines the global policy and returns the provided Permissions * object with additional permissions granted to the specified * CodeSource. * * @param perms the permissions to populate * @param cs 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 according to the policy. */ private PermissionCollection getPermissions(Permissions perms, final CodeSource cs) { CodeSource canonCodeSource = AccessController.doPrivileged( new java.security.PrivilegedAction() { public CodeSource run() { return canonicalizeCodebase(cs, true); } }); return getPermissions(perms, canonCodeSource, null); } private Permissions getPermissions(Permissions perms, final CodeSource cs, Principal[] principals) { PolicyInfo pi = policyInfo.get(); for (PolicyEntry entry : pi.policyEntries) { addPermissions(perms, cs, principals, entry); } // Go through policyEntries gotten from identity db; sync required // because checkForTrustedIdentity (below) might update list synchronized (pi.identityPolicyEntries) { for (PolicyEntry entry : pi.identityPolicyEntries) { addPermissions(perms, cs, principals, entry); } } // now see if any of the keys are trusted ids. if (!ignoreIdentityScope) { Certificate certs[] = cs.getCertificates(); if (certs != null) { for (int k = 0; k < certs.length; k++) { Object idMap = pi.aliasMapping.get(certs[k]); if (idMap == null && checkForTrustedIdentity(certs[k], pi)) { // checkForTrustedIdentity added it // to the policy for us. next time // around we'll find it. This time // around we need to add it. perms.add(SecurityConstants.ALL_PERMISSION); } } } } return perms; } private void addPermissions(Permissions perms, final CodeSource cs, Principal[] principals, final PolicyEntry entry) { if (debug != null) { debug.println("evaluate codesources:\n" + "\tPolicy CodeSource: " + entry.getCodeSource() + "\n" + "\tActive CodeSource: " + cs); } // check to see if the CodeSource implies Boolean imp = AccessController.doPrivileged (new PrivilegedAction() { public Boolean run() { return entry.getCodeSource().implies(cs); } }); if (!imp) { if (debug != null) { debug.println("evaluation (codesource) failed"); } // CodeSource does not imply - return and try next policy entry return; } // check to see if the Principals imply List entryPs = entry.getPrincipals(); if (debug != null) { ArrayList accPs = new ArrayList(); if (principals != null) { for (int i = 0; i < principals.length; i++) { accPs.add(new PolicyParser.PrincipalEntry (principals[i].getClass().getName(), principals[i].getName())); } } debug.println("evaluate principals:\n" + "\tPolicy Principals: " + entryPs + "\n" + "\tActive Principals: " + accPs); } if (entryPs == null || entryPs.size() == 0) { // policy entry has no principals - // add perms regardless of principals in current ACC addPerms(perms, principals, entry); if (debug != null) { debug.println("evaluation (codesource/principals) passed"); } return; } else if (principals == null || principals.length == 0) { // current thread has no principals but this policy entry // has principals - perms are not added if (debug != null) { debug.println("evaluation (principals) failed"); } return; } // current thread has principals and this policy entry // has principals. see if policy entry principals match // principals in current ACC for (int i = 0; i < entryPs.size(); i++) { PolicyParser.PrincipalEntry pppe = entryPs.get(i); // see if principal entry is a PrincipalComparator try { Class pClass = Class.forName (pppe.principalClass, true, Thread.currentThread().getContextClassLoader()); if (!PrincipalComparator.class.isAssignableFrom(pClass)) { // common case - dealing with regular Principal class. // see if policy entry principal is in current ACC if (!checkEntryPs(principals, pppe)) { if (debug != null) { debug.println("evaluation (principals) failed"); } // policy entry principal not in current ACC - // immediately return and go to next policy entry return; } } else { // dealing with a PrincipalComparator Constructor c = pClass.getConstructor(PARAMS1); PrincipalComparator pc = (PrincipalComparator) c.newInstance (new Object[]{pppe.principalName}); if (debug != null) { debug.println("found PrincipalComparator " + pc.getClass().getName()); } // check if the PrincipalComparator // implies the current thread's principals Set pSet = new HashSet(principals.length); for (int j = 0; j < principals.length; j++) { pSet.add(principals[j]); } Subject subject = new Subject(true, pSet, Collections.EMPTY_SET, Collections.EMPTY_SET); if (!pc.implies(subject)) { if (debug != null) { debug.println ("evaluation (principal comparator) failed"); } // policy principal does not imply the current Subject - // immediately return and go to next policy entry return; } } } catch (Exception e) { // fall back to regular principal comparison. // see if policy entry principal is in current ACC if (debug != null) { e.printStackTrace(); } if (!checkEntryPs(principals, pppe)) { if (debug != null) { debug.println("evaluation (principals) failed"); } // policy entry principal not in current ACC - // immediately return and go to next policy entry return; } } // either the principal information matched, // or the PrincipalComparator.implies succeeded. // continue loop and test the next policy principal } // all policy entry principals were found in the current ACC - // grant the policy permissions if (debug != null) { debug.println("evaluation (codesource/principals) passed"); } addPerms(perms, principals, entry); } private void addPerms(Permissions perms, Principal[] accPs, PolicyEntry entry) { for (int i = 0; i < entry.permissions.size(); i++) { Permission p = (Permission) entry.permissions.get(i); if (debug != null) { debug.println(" granting " + p); } if (p instanceof SelfPermission) { // handle "SELF" permissions expandSelf((SelfPermission) p, entry.getPrincipals(), accPs, perms); } else { perms.add(p); } } } /** * This method returns, true, if the principal in the policy entry, * pppe, is part of the current thread's principal array, pList. * This method also returns, true, if the policy entry's principal * is appropriately wildcarded. *

* Note that the provided pppe argument may have * wildcards (*) for both the Principal class and name. * * @param pList an array of principals from the current thread's * AccessControlContext. * @param pppe a Principal specified in a policy grant entry. * @return true if the current thread's pList "contains" the * principal in the policy entry, pppe. This method * also returns true if the policy entry's principal * appropriately wildcarded. */ private boolean checkEntryPs(Principal[] pList, PolicyParser.PrincipalEntry pppe) { for (int i = 0; i < pList.length; i++) { if (pppe.principalClass.equals (PolicyParser.PrincipalEntry.WILDCARD_CLASS) || pppe.principalClass.equals (pList[i].getClass().getName())) { if (pppe.principalName.equals (PolicyParser.PrincipalEntry.WILDCARD_NAME) || pppe.principalName.equals (pList[i].getName())) { return true; } } } return false; } /** *

* * @param sp the SelfPermission that needs to be expanded

* @param entryPs list of principals for the Policy entry. * @param pdp Principal array from the current ProtectionDomain. * @param perms the PermissionCollection where the individual * Permissions will be added after expansion. */ private void expandSelf(SelfPermission sp, List entryPs, Principal[] pdp, Permissions perms) { if (entryPs == null || entryPs.size() == 0) { // No principals in the grant to substitute if (debug != null) { debug.println("Ignoring permission " + sp.getSelfType() + " with target name (" + sp.getSelfName() + "). " + "No Principal(s) specified " + "in the grant clause. " + "SELF-based target names are " + "only valid in the context " + "of a Principal-based grant entry." ); } return; } int startIndex = 0; int v; StringBuilder sb = new StringBuilder(); while ((v = sp.getSelfName().indexOf(SELF, startIndex)) != -1) { // add non-SELF string sb.append(sp.getSelfName().substring(startIndex, v)); // expand SELF ListIterator pli = entryPs.listIterator(); while (pli.hasNext()) { PolicyParser.PrincipalEntry pppe = pli.next(); String[][] principalInfo = getPrincipalInfo(pppe, pdp); for (int i = 0; i < principalInfo.length; i++) { if (i != 0) { sb.append(", "); } sb.append(principalInfo[i][0] + " " + "\"" + principalInfo[i][1] + "\""); } if (pli.hasNext()) { sb.append(", "); } } startIndex = v + SELF.length(); } // add remaining string (might be the entire string) sb.append(sp.getSelfName().substring(startIndex)); if (debug != null) { debug.println(" expanded:\n\t" + sp.getSelfName() + "\n into:\n\t" + sb.toString()); } try { // first try to instantiate the permission perms.add(getInstance(sp.getSelfType(), sb.toString(), sp.getSelfActions())); } catch (ClassNotFoundException cnfe) { // ok, the permission is not in the bootclasspath. // before we add an UnresolvedPermission, check to see // whether this perm already belongs to the collection. // if so, use that perm's ClassLoader to create a new // one. Class pc = null; synchronized (perms) { Enumeration e = perms.elements(); while (e.hasMoreElements()) { Permission pElement = e.nextElement(); if (pElement.getClass().getName().equals(sp.getSelfType())) { pc = pElement.getClass(); break; } } } if (pc == null) { // create an UnresolvedPermission perms.add(new UnresolvedPermission(sp.getSelfType(), sb.toString(), sp.getSelfActions(), sp.getCerts())); } else { try { // we found an instantiated permission. // use its class loader to instantiate a new permission. Constructor c; // name parameter can not be null if (sp.getSelfActions() == null) { try { c = pc.getConstructor(PARAMS1); perms.add((Permission) c.newInstance (new Object[]{sb.toString()})); } catch (NoSuchMethodException ne) { c = pc.getConstructor(PARAMS2); perms.add((Permission) c.newInstance (new Object[]{sb.toString(), sp.getSelfActions()})); } } else { c = pc.getConstructor(PARAMS2); perms.add((Permission) c.newInstance (new Object[]{sb.toString(), sp.getSelfActions()})); } } catch (Exception nme) { if (debug != null) { debug.println("self entry expansion " + " instantiation failed: " + nme.toString()); } } } } catch (Exception e) { if (debug != null) { debug.println(e.toString()); } } } /** * return the principal class/name pair in the 2D array. * array[x][y]: x corresponds to the array length. * if (y == 0), it's the principal class. * if (y == 1), it's the principal name. */ private String[][] getPrincipalInfo (PolicyParser.PrincipalEntry pe, Principal[] pdp) { // there are 3 possibilities: // 1) the entry's Principal class and name are not wildcarded // 2) the entry's Principal name is wildcarded only // 3) the entry's Principal class and name are wildcarded if (!pe.principalClass.equals (PolicyParser.PrincipalEntry.WILDCARD_CLASS) && !pe.principalName.equals (PolicyParser.PrincipalEntry.WILDCARD_NAME)) { // build an info array for the principal // from the Policy entry String[][] info = new String[1][2]; info[0][0] = pe.principalClass; info[0][1] = pe.principalName; return info; } else if (!pe.principalClass.equals (PolicyParser.PrincipalEntry.WILDCARD_CLASS) && pe.principalName.equals (PolicyParser.PrincipalEntry.WILDCARD_NAME)) { // build an info array for every principal // in the current domain which has a principal class // that is equal to policy entry principal class name List plist = new ArrayList(); for (int i = 0; i < pdp.length; i++) { if (pe.principalClass.equals(pdp[i].getClass().getName())) plist.add(pdp[i]); } String[][] info = new String[plist.size()][2]; int i = 0; java.util.Iterator pIterator = plist.iterator(); while (pIterator.hasNext()) { Principal p = pIterator.next(); info[i][0] = p.getClass().getName(); info[i][1] = p.getName(); i++; } return info; } else { // build an info array for every // one of the current Domain's principals String[][] info = new String[pdp.length][2]; for (int i = 0; i < pdp.length; i++) { info[i][0] = pdp[i].getClass().getName(); info[i][1] = pdp[i].getName(); } return info; } } /* * Returns the signer certificates from the list of certificates * associated with the given code source. * * The signer certificates are those certificates that were used * to verifysigned code originating from the codesource location. * * This method assumes that in the given code source, each signer * certificate is followed by its supporting certificate chain * (which may be empty), and that the signer certificate and its * supporting certificate chain are ordered bottom-to-top * (i.e., with the signer certificate first and the (root) certificate * authority last). */ protected Certificate[] getSignerCertificates(CodeSource cs) { Certificate[] certs = null; if ((certs = cs.getCertificates()) == null) return null; for (int i = 0; i < certs.length; i++) { if (!(certs[i] instanceof X509Certificate)) return cs.getCertificates(); } // Do we have to do anything? int i = 0; int count = 0; while (i < certs.length) { count++; while (((i + 1) < certs.length) && ((X509Certificate) certs[i]).getIssuerDN().equals( ((X509Certificate) certs[i + 1]).getSubjectDN())) { i++; } i++; } if (count == certs.length) // Done return certs; ArrayList userCertList = new ArrayList(); i = 0; while (i < certs.length) { userCertList.add(certs[i]); while (((i + 1) < certs.length) && ((X509Certificate) certs[i]).getIssuerDN().equals( ((X509Certificate) certs[i + 1]).getSubjectDN())) { i++; } i++; } Certificate[] userCerts = new Certificate[userCertList.size()]; userCertList.toArray(userCerts); return userCerts; } private CodeSource canonicalizeCodebase(CodeSource cs, boolean extractSignerCerts) { String path = null; CodeSource canonCs = cs; URL u = cs.getLocation(); if (u != null && u.getProtocol().equals("file")) { boolean isLocalFile = false; String host = u.getHost(); isLocalFile = (host == null || host.equals("") || host.equals("~") || host.equalsIgnoreCase("localhost")); if (isLocalFile) { path = u.getFile().replace('/', File.separatorChar); path = ParseUtil.decode(path); } } if (path != null) { try { URL csUrl = null; path = canonPath(path); csUrl = ParseUtil.fileToEncodedURL(new File(path)); if (extractSignerCerts) { canonCs = new CodeSource(csUrl, getSignerCertificates(cs)); } else { canonCs = new CodeSource(csUrl, cs.getCertificates()); } } catch (IOException ioe) { // leave codesource as it is, unless we have to extract its // signer certificates if (extractSignerCerts) { canonCs = new CodeSource(cs.getLocation(), getSignerCertificates(cs)); } } } else { if (extractSignerCerts) { canonCs = new CodeSource(cs.getLocation(), getSignerCertificates(cs)); } } return canonCs; } // public for java.io.FilePermission public static String canonPath(String path) throws IOException { if (path.endsWith("*")) { path = path.substring(0, path.length() - 1) + "-"; path = new File(path).getCanonicalPath(); return path.substring(0, path.length() - 1) + "*"; } else { return new File(path).getCanonicalPath(); } } private String printPD(ProtectionDomain pd) { Principal[] principals = pd.getPrincipals(); String pals = ""; 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(", "); else palBuf.append(")"); } pals = palBuf.toString(); } return "PD CodeSource: " + pd.getCodeSource() + "\n\t" + "PD ClassLoader: " + pd.getClassLoader() + "\n\t" + "PD Principals: " + pals; } /** * return true if no replacement was performed, * or if replacement succeeded. */ private boolean replacePrincipals( List principals, KeyStore keystore) { if (principals == null || principals.size() == 0 || keystore == null) return true; ListIterator i = principals.listIterator(); while (i.hasNext()) { PolicyParser.PrincipalEntry pppe = i.next(); if (pppe.principalClass.equals(PolicyParser.REPLACE_NAME)) { // perform replacement // (only X509 replacement is possible now) String name; if ((name = getDN(pppe.principalName, keystore)) == null) { return false; } if (debug != null) { debug.println(" Replacing \"" + pppe.principalName + "\" with " + X500PRINCIPAL + "/\"" + name + "\""); } pppe.principalClass = X500PRINCIPAL; pppe.principalName = name; } } // return true if no replacement was performed, // or if replacement succeeded return true; } private void expandPermissionName(PolicyParser.PermissionEntry pe, KeyStore keystore) throws Exception { // short cut the common case if (pe.name == null || pe.name.indexOf("${{", 0) == -1) { return; } int startIndex = 0; int b, e; StringBuilder sb = new StringBuilder(); while ((b = pe.name.indexOf("${{", startIndex)) != -1) { e = pe.name.indexOf("}}", b); if (e < 1) { break; } sb.append(pe.name.substring(startIndex, b)); // get the value in ${{...}} String value = pe.name.substring(b + 3, e); // parse up to the first ':' int colonIndex; String prefix = value; String suffix; if ((colonIndex = value.indexOf(":")) != -1) { prefix = value.substring(0, colonIndex); } // handle different prefix possibilities if (prefix.equalsIgnoreCase("self")) { // do nothing - handled later sb.append(pe.name.substring(b, e + 2)); startIndex = e + 2; continue; } else if (prefix.equalsIgnoreCase("alias")) { // get the suffix and perform keystore alias replacement if (colonIndex == -1) { MessageFormat form = new MessageFormat (ResourcesMgr.getString ("alias name not provided (pe.name)")); Object[] source = {pe.name}; throw new Exception(form.format(source)); } suffix = value.substring(colonIndex + 1); if ((suffix = getDN(suffix, keystore)) == null) { MessageFormat form = new MessageFormat (ResourcesMgr.getString ("unable to perform substitution on alias, suffix")); Object[] source = {value.substring(colonIndex + 1)}; throw new Exception(form.format(source)); } sb.append(X500PRINCIPAL + " \"" + suffix + "\""); startIndex = e + 2; } else { MessageFormat form = new MessageFormat (ResourcesMgr.getString ("substitution value, prefix, unsupported")); Object[] source = {prefix}; throw new Exception(form.format(source)); } } // copy the rest of the value sb.append(pe.name.substring(startIndex)); // replace the name with expanded value if (debug != null) { debug.println(" Permission name expanded from:\n\t" + pe.name + "\nto\n\t" + sb.toString()); } pe.name = sb.toString(); } private String getDN(String alias, KeyStore keystore) { Certificate cert = null; try { cert = keystore.getCertificate(alias); } catch (Exception e) { if (debug != null) { debug.println(" Error retrieving certificate for '" + alias + "': " + e.toString()); } return null; } if (cert == null || !(cert instanceof X509Certificate)) { if (debug != null) { debug.println(" -- No certificate for '" + alias + "' - ignoring entry"); } return null; } else { X509Certificate x509Cert = (X509Certificate) cert; // 4702543: X500 names with an EmailAddress // were encoded incorrectly. create new // X500Principal name with correct encoding X500Principal p = new X500Principal (x509Cert.getSubjectX500Principal().toString()); return p.getName(); } } /** * Checks public key. If it is marked as trusted in * the identity database, add it to the policy * with the AllPermission. */ private boolean checkForTrustedIdentity(final Certificate cert, PolicyInfo myInfo) { if (cert == null) return false; // see if we are ignoring the identity scope or not if (ignoreIdentityScope) return false; // try to initialize scope synchronized (PolicyFile.class) { if (scope == null) { IdentityScope is = IdentityScope.getSystemScope(); if (is instanceof sun.security.provider.IdentityDatabase) { scope = is; } else { // leave scope null } } } if (scope == null) { ignoreIdentityScope = true; return false; } // need privileged block for getIdentity in case we are trying // to get a signer final Identity id = AccessController.doPrivileged( new java.security.PrivilegedAction() { public Identity run() { return scope.getIdentity(cert.getPublicKey()); } }); if (isTrusted(id)) { if (debug != null) { debug.println("Adding policy entry for trusted Identity: "); //needed for identity toString! AccessController.doPrivileged( new java.security.PrivilegedAction() { public Void run() { debug.println(" identity = " + id); return null; } }); debug.println(""); } // add it to the policy for future reference Certificate certs[] = new Certificate[]{cert}; PolicyEntry pe = new PolicyEntry(new CodeSource(null, certs)); pe.add(SecurityConstants.ALL_PERMISSION); myInfo.identityPolicyEntries.add(pe); // add it to the mapping as well so // we don't have to go through this again myInfo.aliasMapping.put(cert, id.getName()); return true; } return false; } private static boolean isTrusted(Identity id) { if (id instanceof SystemIdentity) { SystemIdentity sysid = (SystemIdentity) id; if (sysid.isTrusted()) { return true; } } else if (id instanceof SystemSigner) { SystemSigner sysid = (SystemSigner) id; if (sysid.isTrusted()) { return true; } } return false; } /** * Each entry in the policy configuration file is represented by a * PolicyEntry object.

*

* A PolicyEntry is a (CodeSource,Permission) pair. The * CodeSource contains the (URL, PublicKey) that together identify * where the Java bytecodes come from and who (if anyone) signed * them. The URL could refer to localhost. The URL could also be * null, meaning that this policy entry is given to all comers, as * long as they match the signer field. The signer could be null, * meaning the code is not signed.

*

* The Permission contains the (Type, Name, Action) triplet.

*

* For now, the Policy object retrieves the public key from the * X.509 certificate on disk that corresponds to the signedBy * alias specified in the Policy config file. For reasons of * efficiency, the Policy object keeps a hashtable of certs already * read in. This could be replaced by a secure internal key * store. *

*

* For example, the entry *

      *          permission java.io.File "/tmp", "read,write",
      *          signedBy "Duke";
      * 
* is represented internally *
      * 

* FilePermission f = new FilePermission("/tmp", "read,write"); * PublicKey p = publickeys.get("Duke"); * URL u = InetAddress.getLocalHost(); * CodeBase c = new CodeBase( p, u ); * pe = new PolicyEntry(f, c); *

* * @author Marianne Mueller * @author Roland Schemers * @version 1.52, 10/26/00 * @see java.security.CodeSource * @see java.security.Policy * @see java.security.Permissions * @see java.security.ProtectionDomain */ private static class PolicyEntry { private final CodeSource codesource; final List permissions; private final List principals; /** * Given a Permission and a CodeSource, create a policy entry. *

* XXX Decide if/how to add validity fields and "purpose" fields to * XXX policy entries * * @param cs the CodeSource, which encapsulates the URL and the * public key * attributes from the policy config file. Validity checks * are performed on the public key before PolicyEntry is * called. */ PolicyEntry(CodeSource cs, List principals) { this.codesource = cs; this.permissions = new ArrayList(); this.principals = principals; // can be null } PolicyEntry(CodeSource cs) { this(cs, null); } List getPrincipals() { return principals; // can be null } /** * add a Permission object to this entry. * No need to sync add op because perms are added to entry only * while entry is being initialized */ void add(Permission p) { permissions.add(p); } /** * Return the CodeSource for this policy entry */ CodeSource getCodeSource() { return codesource; } public String toString() { StringBuilder sb = new StringBuilder(); sb.append(ResourcesMgr.getString("(")); sb.append(getCodeSource()); sb.append("\n"); for (int j = 0; j < permissions.size(); j++) { Permission p = permissions.get(j); sb.append(ResourcesMgr.getString(" ")); sb.append(ResourcesMgr.getString(" ")); sb.append(p); sb.append(ResourcesMgr.getString("\n")); } sb.append(ResourcesMgr.getString(")")); sb.append(ResourcesMgr.getString("\n")); return sb.toString(); } } private static class SelfPermission extends Permission { private static final long serialVersionUID = -8315562579967246806L; /** * The class name of the Permission class that will be * created when this self permission is expanded . * * @serial */ private String type; /** * The permission name. * * @serial */ private String name; /** * The actions of the permission. * * @serial */ private String actions; /** * The certs of the permission. * * @serial */ private Certificate certs[]; /** * Creates a new SelfPermission containing the permission * information needed later to expand the self * * @param type the class name of the Permission class that will be * created when this permission is expanded and if necessary resolved. * @param name the name of the permission. * @param actions the actions of the permission. * @param certs the certificates the permission's class was signed with. * This is a list of certificate chains, where each chain is composed of * a signer certificate and optionally its supporting certificate chain. * Each chain is ordered bottom-to-top (i.e., with the signer * certificate first and the (root) certificate authority last). */ public SelfPermission(String type, String name, String actions, Certificate certs[]) { super(type); if (type == null) { throw new NullPointerException (ResourcesMgr.getString("type can't be null")); } this.type = type; this.name = name; this.actions = actions; if (certs != null) { // Extract the signer certs from the list of certificates. for (int i = 0; i < certs.length; i++) { if (!(certs[i] instanceof X509Certificate)) { // there is no concept of signer certs, so we store the // entire cert array this.certs = (Certificate[]) certs.clone(); break; } } if (this.certs == null) { // Go through the list of certs and see if all the certs are // signer certs. int i = 0; int count = 0; while (i < certs.length) { count++; while (((i + 1) < certs.length) && ((X509Certificate) certs[i]).getIssuerDN().equals( ((X509Certificate) certs[i + 1]).getSubjectDN())) { i++; } i++; } if (count == certs.length) { // All the certs are signer certs, so we store the // entire array this.certs = (Certificate[]) certs.clone(); } if (this.certs == null) { // extract the signer certs ArrayList signerCerts = new ArrayList(); i = 0; while (i < certs.length) { signerCerts.add(certs[i]); while (((i + 1) < certs.length) && ((X509Certificate) certs[i]).getIssuerDN().equals( ((X509Certificate) certs[i + 1]).getSubjectDN())) { i++; } i++; } this.certs = new Certificate[signerCerts.size()]; signerCerts.toArray(this.certs); } } } } /** * This method always returns false for SelfPermission permissions. * That is, an SelfPermission never considered to * imply another permission. * * @param p the permission to check against. * @return false. */ public boolean implies(Permission p) { return false; } /** * Checks two SelfPermission objects for equality. *

* Checks that obj is an SelfPermission, and has * the same type (class) name, permission name, actions, and * certificates as this object. * * @param obj the object we are testing for equality with this object. * @return true if obj is an SelfPermission, and has the same * type (class) name, permission name, actions, and * certificates as this object. */ public boolean equals(Object obj) { if (obj == this) return true; if (!(obj instanceof SelfPermission)) return false; SelfPermission that = (SelfPermission) obj; if (!(this.type.equals(that.type) && this.name.equals(that.name) && this.actions.equals(that.actions))) return false; if (this.certs.length != that.certs.length) return false; int i, j; boolean match; for (i = 0; i < this.certs.length; i++) { match = false; for (j = 0; j < that.certs.length; j++) { if (this.certs[i].equals(that.certs[j])) { match = true; break; } } if (!match) return false; } for (i = 0; i < that.certs.length; i++) { match = false; for (j = 0; j < this.certs.length; j++) { if (that.certs[i].equals(this.certs[j])) { match = true; break; } } if (!match) return false; } return true; } /** * Returns the hash code value for this object. * * @return a hash code value for this object. */ public int hashCode() { int hash = type.hashCode(); if (name != null) hash ^= name.hashCode(); if (actions != null) hash ^= actions.hashCode(); return hash; } /** * Returns the canonical string representation of the actions, * which currently is the empty string "", since there are no actions * for an SelfPermission. That is, the actions for the * permission that will be created when this SelfPermission * is resolved may be non-null, but an SelfPermission * itself is never considered to have any actions. * * @return the empty string "". */ public String getActions() { return ""; } public String getSelfType() { return type; } public String getSelfName() { return name; } public String getSelfActions() { return actions; } public Certificate[] getCerts() { return certs; } /** * Returns a string describing this SelfPermission. The convention * is to specify the class name, the permission name, and the actions, * in the following format: '(unresolved "ClassName" "name" "actions")'. * * @return information about this SelfPermission. */ public String toString() { return "(SelfPermission " + type + " " + name + " " + actions + ")"; } } /** * holds policy information that we need to synch on */ private static class PolicyInfo { private static final boolean verbose = false; // Stores grant entries in the policy final List policyEntries; // Stores grant entries gotten from identity database // Use separate lists to avoid sync on policyEntries final List identityPolicyEntries; // Maps aliases to certs final Map aliasMapping; // Maps ProtectionDomain to PermissionCollection private final Map[] pdMapping; private java.util.Random random; PolicyInfo(int numCaches) { policyEntries = new ArrayList(); identityPolicyEntries = Collections.synchronizedList(new ArrayList(2)); aliasMapping = Collections.synchronizedMap(new HashMap(11)); pdMapping = new Map[numCaches]; for (int i = 0; i < numCaches; i++) { pdMapping[i] = Collections.synchronizedMap (new WeakHashMap()); } if (numCaches > 1) { random = new java.util.Random(); } } Map getPdMapping() { if (pdMapping.length == 1) { return pdMapping[0]; } else { int i = java.lang.Math.abs(random.nextInt() % pdMapping.length); return pdMapping[i]; } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy