FileDocCategorySizeDatePackage
PolicyFile.javaAPI DocJava SE 6 API45104Tue Jun 10 00:23:22 BST 2008com.sun.security.auth

PolicyFile.java

/*
 * @(#)PolicyFile.java	1.37 06/03/24
 *
 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package com.sun.security.auth;

import java.io.*;
import java.lang.RuntimePermission;
import java.lang.reflect.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;

import java.security.AccessController;
import java.security.CodeSource;
import java.security.Identity;
import java.security.IdentityScope;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.Permission;
import java.security.Permissions;
import java.security.PermissionCollection;
import java.security.Principal;
import java.security.UnresolvedPermission;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;

import javax.security.auth.Subject;
import javax.security.auth.PrivateCredentialPermission;

import sun.security.util.PropertyExpander;

/**
 * This class represents a default implementation for
 * <code>javax.security.auth.Policy</code>.
 *
 * <p> 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 <code>Policy</code> object is:
 *
 * <ol>
 * <li>
 *   Loop through the <code>java.security.Security</code> properties,
 *   <i>auth.policy.url.1</i>, <i>auth.policy.url.2</i>, ...,
 *   <i>auth.policy.url.X</i>".  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 <code>URL</code> pointing to a
 *   policy file to be loaded.  Read in and load each policy.
 *   
 * <li>
 *   The <code>java.lang.System</code> property <i>java.security.auth.policy</i>
 *   may also be set to a <code>URL</code> 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,
 *   <i>policy.allowSystemProperty</i> is set to <i>true</i>),
 *   also load that policy.
 *
 * <li>
 *   If the <i>java.security.auth.policy</i> property is defined using
 *   "==" (rather than "="), then ignore all other specified
 *   policies and only load this policy.
 * </ol>
 * 
 * Each policy file consists of one or more grant entries, each of
 * which consists of a number of permission entries.
 *
 * <pre>
 *   grant signedBy "<b>alias</b>", codeBase "<b>URL</b>",
 *         principal <b>principalClass</b> "<b>principalName</b>",
 *         principal <b>principalClass</b> "<b>principalName</b>",
 *         ... {
 *
 *     permission <b>Type</b> "<b>name</b> "<b>action</b>", 
 *         signedBy "<b>alias</b>";
 *     permission <b>Type</b> "<b>name</b> "<b>action</b>",
 *         signedBy "<b>alias</b>";
 *     ....
 *   };
 * </pre>
 * 
 * All non-bold items above must appear as is (although case 
 * doesn't matter and some are optional, as noted below).
 * Italicized items represent variable values.
 *
 * <p> A grant entry must begin with the word <code>grant</code>.
 * The <code>signedBy</code> and <code>codeBase</code>
 * 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
 * <code>principal</code> name/value pair is not optional.
 * This <code>Policy</code> implementation only permits
 * Principal-based grant entries.  Note that the <i>principalClass</i>
 * may be set to the wildcard value, *, which allows it to match
 * any <code>Principal</code> class.  In addition, the <i>principalName</i>
 * may also be set to the wildcard value, *, allowing it to match
 * any <code>Principal</code> name.  When setting the <i>principalName</i>
 * to the *, do not surround the * with quotes.
 *
 * <p> A permission entry must begin with the word <code>permission</code>. 
 * The word <code><i>Type</i></code> in the template above is
 * a specific permission type, such as <code>java.io.FilePermission</code>
 * or <code>java.lang.RuntimePermission</code>.
 * 
 * <p> The "<i>action</i>" is required for
 * many permission types, such as <code>java.io.FilePermission</code>
 * (where it specifies what type of file access that is permitted).
 * It is not required for categories such as 
 * <code>java.lang.RuntimePermission</code>
 * where it is not necessary - you either have the 
 * permission specified by the <code>"<i>name</i>"</code> 
 * value following the type name or you don't.
 * 
 * <p> The <code>signedBy</code> 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:
 * 
 * <pre>
 *   grant principal foo.com.Principal "Duke" {
 *     permission Foo "foobar", signedBy "FooSoft";
 *   }
 * </pre>
 * 
 * <p> Then this permission of type <i>Foo</i> is granted if the 
 * <code>Foo.class</code> permission has been signed by the 
 * "FooSoft" alias, or if <code>Foo.class</code> is a 
 * system class (i.e., is found on the CLASSPATH).
 * 
 * <p> Items that appear in an entry must appear in the specified order
 * (<code>permission</code>, <i>Type</i>, "<i>name</i>", and 
 * "<i>action</i>"). An entry is terminated with a semicolon.
 * 
 * <p> Case is unimportant for the identifiers (<code>permission</code>, 
 * <code>signedBy</code>, <code>codeBase</code>, etc.) but is 
 * significant for the <i>Type</i>
 * or for any string that is passed in as a value. <p>
 * 
 * <p> An example of two entries in a policy configuration file is
 * <pre>
 *   // 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";
 * </pre> 
 *
 * <p> This <code>Policy</code> implementation supports
 * special handling for PrivateCredentialPermissions.
 * If a grant entry is configured with a
 * <code>PrivateCredentialPermission</code>,
 * and the "Principal Class/Principal Name" for that
 * <code>PrivateCredentialPermission</code> is "self",
 * then the entry grants the specified <code>Subject</code> permission to
 * access its own private Credential.  For example,
 * the following grants the <code>Subject</code> "Duke"
 * access to its own a.b.Credential.
 *
 * <pre>
 *   grant principal foo.com.Principal "Duke" {
 *	permission javax.security.auth.PrivateCredentialPermission
 *		"a.b.Credential self",
 *		"read";
 *    };
 * </pre>
 *
 * The following grants the <code>Subject</code> "Duke"
 * access to all of its own private Credentials:
 *
 * <pre>
 *   grant principal foo.com.Principal "Duke" {
 *	permission javax.security.auth.PrivateCredentialPermission
 *		"* self",
 *		"read";
 *    };
 * </pre>
 *
 * The following grants all Subjects authenticated as a
 * <code>SolarisPrincipal</code> (regardless of their respective names)
 * permission to access their own private Credentials:
 *
 * <pre>
 *   grant principal com.sun.security.auth.SolarisPrincipal * {
 *	permission javax.security.auth.PrivateCredentialPermission
 *		"* self",
 *		"read";
 *    };
 * </pre>
 * 
 * The following grants all Subjects permission to access their own
 * private Credentials:
 *
 * <pre>
 *   grant principal * * {
 *	permission javax.security.auth.PrivateCredentialPermission
 *		"* self",
 *		"read";
 *    };
 * </pre>
 
 * @deprecated As of JDK 1.4, replaced by 
 *             <code>sun.security.provider.PolicyFile</code>.
 *             This class is entirely deprecated.
 *
 * @version 1.22, 01/25/00
 * @see java.security.CodeSource
 * @see java.security.Permissions
 * @see java.security.ProtectionDomain 
 */
@Deprecated
public class PolicyFile extends javax.security.auth.Policy {

    static final java.util.ResourceBundle rb =
          (java.util.ResourceBundle)java.security.AccessController.doPrivileged
          (new java.security.PrivilegedAction() {
              public Object run() {
                  return (java.util.ResourceBundle.getBundle
                                ("sun.security.util.AuthResources"));
              }
	   });
    // needs to be package private

    private static final sun.security.util.Debug debug =
	sun.security.util.Debug.getInstance("policy", "\t[Auth Policy]");

    private static final String AUTH_POLICY = "java.security.auth.policy";
    private static final String SECURITY_MANAGER = "java.security.manager";
    private static final String AUTH_POLICY_URL = "auth.policy.url.";

    private Vector policyEntries;
    private Hashtable aliasMapping;

    private boolean initialized = false;

    private boolean expandProperties = true;
    private boolean ignoreIdentityScope = false;

    // for use with the reflection API

    private static final Class[] PARAMS = { String.class, String.class};

    /** 
     * Initializes the Policy object and reads the default policy 
     * configuration file(s) into the Policy object.
     */
    public PolicyFile() {
	// initialize Policy if either the AUTH_POLICY or
	// SECURITY_MANAGER properties are set
	String prop = System.getProperty(AUTH_POLICY);

	if (prop == null) {
	    prop = System.getProperty(SECURITY_MANAGER);
	}
 	if (prop != null)
 	    init();
    }

    private synchronized void init() {

	if (initialized)
	    return;

	policyEntries = new Vector();
	aliasMapping = new Hashtable(11);
	
	initPolicyFile();
	initialized = true;
    }

    /**
     * Refreshes the policy object by re-reading all the policy files.
     *
     * <p>
     *
     * @exception SecurityException if the caller doesn't have permission
     *		to refresh the <code>Policy</code>.
     */
    public synchronized void refresh()
    {

	java.lang.SecurityManager sm = System.getSecurityManager();
	if (sm != null) {
	    sm.checkPermission(new javax.security.auth.AuthPermission
				("refreshPolicy"));
	}

	// XXX
	//
	// 1)	if code instantiates PolicyFile directly, then it will need
	// 	all the permissions required for the PolicyFile initialization
	// 2)	if code calls Policy.getPolicy, then it simply needs
	//	AuthPermission(getPolicy), and the javax.security.auth.Policy
	//	implementation instantiates PolicyFile in a doPrivileged block
	// 3)	if after instantiating a Policy (either via #1 or #2),
	//	code calls refresh, it simply needs
	//	AuthPermission(refreshPolicy).  then PolicyFile wraps
	//	the refresh in a doPrivileged block.
	initialized = false;
	java.security.AccessController.doPrivileged
	    (new java.security.PrivilegedAction() {
	    public Object run() {
		init();
		return null;
	    }
	});
    }

    private KeyStore initKeyStore(URL policyUrl, String keyStoreName,
				  String keyStoreType) {
	if (keyStoreName != null) {
	    try {
		/*
		 * location of keystore is specified as absolute URL in policy
		 * file, or is relative to URL of policy file
		 */
		URL keyStoreUrl = null;
		try {
		    keyStoreUrl = new URL(keyStoreName);
		    // absolute URL
		} catch (java.net.MalformedURLException e) {
		    // relative URL
		    keyStoreUrl = new URL(policyUrl, keyStoreName);
		}

		if (debug != null) {
		    debug.println("reading keystore"+keyStoreUrl);
		}

		InputStream inStream = 
		    new BufferedInputStream(getInputStream(keyStoreUrl));

		KeyStore ks;
		if (keyStoreType != null)
		    ks = KeyStore.getInstance(keyStoreType);
		else
		    ks = KeyStore.getInstance(KeyStore.getDefaultType());
		ks.load(inStream, null);
		inStream.close();
		return ks;
	    } catch (Exception e) {
		// ignore, treat it like we have no keystore
		if (debug != null) {
		    e.printStackTrace();
		}
		return null;
	    }	    
	}
	return null;
    }

    private void initPolicyFile() {

	String prop = Security.getProperty("policy.expandProperties");

	if (prop != null) expandProperties = prop.equalsIgnoreCase("true");

	String iscp = Security.getProperty("policy.ignoreIdentityScope");

	if (iscp != null) ignoreIdentityScope = iscp.equalsIgnoreCase("true");

	String allowSys  = Security.getProperty("policy.allowSystemProperty");

	if ((allowSys!=null) && allowSys.equalsIgnoreCase("true")) {

	    String extra_policy = System.getProperty(AUTH_POLICY);
	    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 =
			    new URL("file:" + policyFile.getCanonicalPath());
		    } else {
			policyURL = new URL(extra_policy);
		    }
		    if (debug != null)
			debug.println("reading "+policyURL);
		    init(policyURL);
		} catch (Exception e) {
		    // ignore. 
		    if (debug != null) {
			debug.println("caught exception: "+e);
		    }

		}
		if (overrideAll) {
		    if (debug != null) {
			debug.println("overriding other policies!");
		    }
		    return;
		}
	    }
	}

	int n = 1;
	boolean loaded_one = false;
	String policy_url;

	while ((policy_url = Security.getProperty(AUTH_POLICY_URL+n)) != null) {
	    try {
		policy_url = PropertyExpander.expand(policy_url).replace
						(File.separatorChar, '/');
		if (debug != null)
		    debug.println("reading "+policy_url);
		init(new URL(policy_url));
		loaded_one = true;
	    } catch (Exception e) {
		if (debug != null) {
		    debug.println("error reading policy "+e);
		    e.printStackTrace();
		}
		// ignore that policy
	    }
	    n++;
	}

	if (loaded_one == false) {
	    // do not load a static policy
	}
    }

    /** the scope to check */
    private static IdentityScope scope = null;

    /**
     * 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) {
	// XXX	JAAS has no way to access the SUN package.
	//	we'll add this back in when JAAS goes into core.
	return false;
    }

    /** 
     * Reads a policy configuration into the Policy object using a
     * Reader object.
     * 
     * @param policyFile the policy Reader object.
     */
    private void init(URL policy) {
	PolicyParser pp = new PolicyParser(expandProperties);
	try {
	    InputStreamReader isr
		= new InputStreamReader(getInputStream(policy)); 
	    pp.read(isr);
	    isr.close();
	    KeyStore keyStore = initKeyStore(policy, pp.getKeyStoreUrl(),
					     pp.getKeyStoreType());
	    Enumeration enum_ = pp.grantElements();
	    while (enum_.hasMoreElements()) {
		PolicyParser.GrantEntry ge =
		    (PolicyParser.GrantEntry) enum_.nextElement();
		addGrantEntry(ge, keyStore);
	    }
	} catch (PolicyParser.ParsingException pe) {
	    System.err.println(AUTH_POLICY +
				rb.getString(": error parsing ") + policy);
	    System.err.println(AUTH_POLICY +
				rb.getString(": ") +
				pe.getMessage());
	    if (debug != null) 
		pe.printStackTrace();

	} catch (Exception e) {
	    if (debug != null) {
		debug.println("error parsing "+policy);
		debug.println(e.toString());
		e.printStackTrace();
	    }
	}
    }

    /*
     * Fast path reading from file urls in order to avoid calling
     * FileURLConnection.connect() which can be quite slow the first time
     * it is called. We really should clean up FileURLConnection so that
     * this is not a problem but in the meantime this fix helps reduce
     * start up time noticeably for the new launcher. -- DAC
     */
    private InputStream getInputStream(URL url) throws IOException {
	if ("file".equals(url.getProtocol())) {
	    String path = url.getFile().replace('/', File.separatorChar);
	    return new FileInputStream(path);
	} else {
	    return url.openStream();
	}
    }

    /**
     * Given a PermissionEntry, create a codeSource.
     *
     * @return null if signedBy alias is not recognized
     */
    CodeSource getCodeSource(PolicyParser.GrantEntry ge, KeyStore keyStore) 
	throws java.net.MalformedURLException
    {
	Certificate[] certs = null;
	if (ge.signedBy != null) {
	    certs = getCertificates(keyStore, ge.signedBy);
	    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.");
		}
		return null;
	    }
	}
	
	URL location;

	if (ge.codeBase != null)
	    location = new URL(ge.codeBase);
	else
	    location = null;

	if (ge.principals == null || ge.principals.size() == 0) {
	    return (canonicalizeCodebase
			(new CodeSource(location, certs),
			false));
	} else {
	    return (canonicalizeCodebase
		(new SubjectCodeSource(null, ge.principals, location, certs),
		false));
	}
    }

    /**
     * Add one policy entry to the vector. 
     */
    private void addGrantEntry(PolicyParser.GrantEntry ge,
			       KeyStore keyStore) {

	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 =
			(PolicyParser.PrincipalEntry)li.next();
		    debug.println("  " + pppe.principalClass +
					" " + pppe.principalName);
		}
	    }
	    debug.println();
	}

	try {
	    CodeSource codesource = getCodeSource(ge, keyStore);
	    // skip if signedBy alias was unknown...
	    if (codesource == null) return;

	    PolicyEntry entry = new PolicyEntry(codesource);
	    Enumeration enum_ = ge.permissionElements();
	    while (enum_.hasMoreElements()) {
		PolicyParser.PermissionEntry pe =
		    (PolicyParser.PermissionEntry) enum_.nextElement();
		try { 
		    // XXX special case PrivateCredentialPermission-SELF
		    Permission perm;
		    if (pe.permission.equals
			("javax.security.auth.PrivateCredentialPermission") &&
			pe.name.endsWith(" self")) {
			perm = getInstance(pe.permission,
					 pe.name + " \"self\"",
					 pe.action);
		    } 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);
		    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) {
		    System.err.println
			(AUTH_POLICY +
			rb.getString(": error adding Permission ") +
			pe.permission +
			rb.getString(" ") +
			ite.getTargetException());
		} catch (Exception e) {
		    System.err.println
			(AUTH_POLICY +
			rb.getString(": error adding Permission ") +
			pe.permission +
			rb.getString(" ") +
			e);
		}
	    }
	    policyEntries.addElement(entry);
	} catch (Exception e) {
	    System.err.println
		(AUTH_POLICY +
		rb.getString(": error adding Entry ") +
		ge +
		rb.getString(" ") +
		e);
	}

	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 <code>Class.forName</code> 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.
     *
     * @exception  ClassNotFoundException  if the particular Permission
     *             class could not be found.
     *
     * @exception  IllegalAccessException  if the class or initializer is
     *               not accessible.
     *
     * @exception  InstantiationException  if getInstance tries to
     *               instantiate an abstract class or an interface, or if the
     *               instantiation fails for some other reason.
     *
     * @exception  NoSuchMethodException if the (String, String) constructor
     *               is not found.
     *
     * @exception  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);
	Constructor c = pc.getConstructor(PARAMS);
	return (Permission) c.newInstance(new Object[] { name, actions });
    }

    /**
     * Fetch all certs associated with this alias. 
     */
    Certificate[] getCertificates(
				    KeyStore keyStore, String aliases) {

	Vector 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
	    cert = (Certificate) 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) {
		    aliasMapping.put(alias, cert);
		    aliasMapping.put(cert, alias);
		} 
	    }

	    if (cert != null) {
		if (vcerts == null)
		    vcerts = new Vector();
		vcerts.addElement(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.copyInto(certs);
	    return certs;
	} else {
	    return null;
	}
    }

    /**
     * Enumerate all the entries in the global policy object. 
     * This method is used by policy admin tools.   The tools
     * should use the Enumeration methods on the returned object
     * to fetch the elements sequentially. 
     */
    private final synchronized Enumeration elements(){
	return policyEntries.elements();
    }

    /**
     * Examines this <code>Policy</code> and returns the Permissions granted
     * to the specified <code>Subject</code> and <code>CodeSource</code>.
     *
     * <p> Permissions for a particular <i>grant</i> entry are returned
     * if the <code>CodeSource</code> constructed using the codebase and
     * signedby values specified in the entry <code>implies</code>
     * the <code>CodeSource</code> provided to this method, and if the
     * <code>Subject</code> provided to this method contains all of the
     * Principals specified in the entry.
     *
     * <p> The <code>Subject</code> provided to this method contains all
     * of the Principals specified in the entry if, for each
     * <code>Principal</code>, "P1", specified in the <i>grant</i> entry
     * one of the following two conditions is met:
     *
     * <p>
     * <ol>
     * <li> the <code>Subject</code> has a
     *      <code>Principal</code>, "P2", where
     *      <code>P2.getClass().getName()</code> equals the
     *      P1's class name, and where
     *      <code>P2.getName()</code> equals the P1's name.
     *
     * <li> P1 implements
     *      <code>com.sun.security.auth.PrincipalComparator</code>,
     *      and <code>P1.implies</code> the provided <code>Subject</code>.
     * </ol>
     *
     * <p> Note that this <code>Policy</code> implementation has
     * special handling for PrivateCredentialPermissions.
     * When this method encounters a <code>PrivateCredentialPermission</code>
     * which specifies "self" as the <code>Principal</code> class and name,
     * it does not add that <code>Permission</code> to the returned
     * <code>PermissionCollection</code>.  Instead, it builds
     * a new <code>PrivateCredentialPermission</code>
     * for each <code>Principal</code> associated with the provided
     * <code>Subject</code>.  Each new <code>PrivateCredentialPermission</code>
     * contains the same Credential class as specified in the
     * originally granted permission, as well as the Class and name
     * for the respective <code>Principal</code>.
     *
     * <p>
     *
     * @param subject the Permissions granted to this <code>Subject</code>
     *		and the additionally provided <code>CodeSource</code>
     *		are returned. <p>
     *
     * @param codesource the Permissions granted to this <code>CodeSource</code>
     *		and the additionally provided <code>Subject</code>
     *		are returned.
     *
     * @return the Permissions granted to the provided <code>Subject</code>
     *		<code>CodeSource</code>.
     */
    public PermissionCollection getPermissions(final Subject subject,
					final CodeSource codesource) {

	// XXX	when JAAS goes into the JDK core,
	//	we can remove this method and simply
	//	rely on the getPermissions variant that takes a codesource,
	//	which no one can use at this point in time.
	//	at that time, we can also make SubjectCodeSource a public
	//	class.

	// XXX
	//
	// 1)	if code instantiates PolicyFile directly, then it will need
	// 	all the permissions required for the PolicyFile initialization
	// 2)	if code calls Policy.getPolicy, then it simply needs
	//	AuthPermission(getPolicy), and the javax.security.auth.Policy
	//	implementation instantiates PolicyFile in a doPrivileged block
	// 3)	if after instantiating a Policy (either via #1 or #2),
	//	code calls getPermissions, PolicyFile wraps the call
	//	in a doPrivileged block.
	return (PermissionCollection)java.security.AccessController.doPrivileged
	    (new java.security.PrivilegedAction() {
	    public Object run() {
		SubjectCodeSource scs = new SubjectCodeSource
		    (subject, 
		    null,
		    codesource == null ? null : codesource.getLocation(),
		    codesource == null ? null : codesource.getCertificates());
 		if (initialized)
 		    return getPermissions(new Permissions(), scs);
 		else
		    return new PolicyPermissions(PolicyFile.this, scs);
	    }
	});
    }

    /**
     * Examines the global policy for the specified CodeSource, and
     * creates a PermissionCollection object with
     * the set of permissions for that principal's protection domain.
     *
     * @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.  
     */
    PermissionCollection getPermissions(CodeSource codesource) {

 	if (initialized)
 	    return getPermissions(new Permissions(), codesource);
 	else
 	    return new PolicyPermissions(this, codesource);
    }

    /**
     * Examines the global policy for the specified CodeSource, and
     * creates a PermissionCollection object with
     * the set of permissions for that principal's protection domain.
     *
     * @param permissions the permissions to populate
     * @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.  
     */
    Permissions getPermissions(final Permissions perms,
			       final CodeSource cs)
    {
	if (!initialized) {
	    init();
	}

	final CodeSource codesource[] = {null};

	codesource[0] = canonicalizeCodebase(cs, true);

	if (debug != null) {
	    debug.println("evaluate("+codesource[0]+")\n");
	}
	    
	// needs to be in a begin/endPrivileged block because
	// codesource.implies calls URL.equals which does an
	// InetAddress lookup

	for (int i = 0; i < policyEntries.size(); i++) {

	   PolicyEntry entry = (PolicyEntry)policyEntries.elementAt(i);

	   if (debug != null) {
		debug.println("PolicyFile CodeSource implies: " +
			entry.codesource.toString() + "\n\n" +
			"\t" + codesource[0].toString() + "\n\n");
	   }

	   if (entry.codesource.implies(codesource[0])) {
	       for (int j = 0; j < entry.permissions.size(); j++) {
		    Permission p = 
		       (Permission) entry.permissions.elementAt(j);
		    if (debug != null) {
		       debug.println("  granting " + p);
		    }
		    if (!addSelfPermissions(p, entry.codesource,
					codesource[0], perms)) {
			// we could check for duplicates
			// before adding new permissions,
			// but the SubjectDomainCombiner
			// already checks for duplicates later
			perms.add(p);
		    }
		}	
	    }
	}

	// now see if any of the keys are trusted ids.

	if (!ignoreIdentityScope) {
	    Certificate certs[] = codesource[0].getCertificates();
	    if (certs != null) {
		for (int k=0; k < certs.length; k++) {
		    if ((aliasMapping.get(certs[k]) == null) &&
			checkForTrustedIdentity(certs[k])) {
			// 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(new java.security.AllPermission());
		    }
		}
	    }
	}
	return perms;
    }

    /**
     * Returns true if 'Self' permissions were added to the provided
     * 'perms', and false otherwise.
     *
     * <p>
     *
     * @param p check to see if this Permission is a "SELF"
     *			PrivateCredentialPermission. <p>
     *
     * @param entryCs the codesource for the Policy entry.
     *
     * @param accCs the codesource for from the current AccessControlContext.
     *
     * @param perms the PermissionCollection where the individual
     *			PrivateCredentialPermissions will be added.
     */
    private boolean addSelfPermissions(final Permission p,
				CodeSource entryCs,
				CodeSource accCs,
				Permissions perms) {

	if (!(p instanceof PrivateCredentialPermission))
	    return false;

	if (!(entryCs instanceof SubjectCodeSource))
	    return false;


	PrivateCredentialPermission pcp = (PrivateCredentialPermission)p;
	SubjectCodeSource scs = (SubjectCodeSource)entryCs;

	// see if it is a SELF permission
	String[][] pPrincipals = pcp.getPrincipals();
	if (pPrincipals.length <= 0 ||
	    !pPrincipals[0][0].equalsIgnoreCase("self") ||
	    !pPrincipals[0][1].equalsIgnoreCase("self")) {

	    // regular PrivateCredentialPermission
	    return false;
	} else {

	    // granted a SELF permission - create a
	    // PrivateCredentialPermission for each
	    // of the Policy entry's CodeSource Principals

	    if (scs.getPrincipals() == null) {
		// XXX SubjectCodeSource has no Subject???
		return true;
	    }

	    ListIterator pli = scs.getPrincipals().listIterator();
	    while (pli.hasNext()) {

		PolicyParser.PrincipalEntry principal =
				(PolicyParser.PrincipalEntry)pli.next();

		// XXX
		//	if the Policy entry's Principal does not contain a
		//		WILDCARD for the Principal name, then a
		//		new PrivateCredentialPermission is created
		//		for the Principal listed in the Policy entry.
		//	if the Policy entry's Principal contains a WILDCARD
		//		for the Principal name, then a new
		//		PrivateCredentialPermission is created
		//		for each Principal associated with the Subject
		//		in the current ACC.

		String[][] principalInfo = getPrincipalInfo
						(principal, accCs);

		for (int i = 0; i < principalInfo.length; i++) {

		    // here's the new PrivateCredentialPermission

		    PrivateCredentialPermission newPcp = 
			new PrivateCredentialPermission
				(pcp.getCredentialClass() +
					" " +
					principalInfo[i][0] +
					" " +
					"\"" + principalInfo[i][1] + "\"",
				"read");

		    if (debug != null) {
			debug.println("adding SELF permission: " +
					newPcp.toString());
		    }

		    perms.add(newPcp);
		}
	    }
	}
	return true;
    }

    /**
     * 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 principal,
		final CodeSource accCs) {

	// 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 (!principal.principalClass.equals
		(PolicyParser.PrincipalEntry.WILDCARD_CLASS) &&
	    !principal.principalName.equals
		(PolicyParser.PrincipalEntry.WILDCARD_NAME)) {

	    // build a PrivateCredentialPermission for the principal
	    // from the Policy entry
	    String[][] info = new String[1][2];
	    info[0][0] = principal.principalClass;
	    info[0][1] = principal.principalName;
	    return info;

	} else if (!principal.principalClass.equals
		(PolicyParser.PrincipalEntry.WILDCARD_CLASS) &&
	    principal.principalName.equals
		(PolicyParser.PrincipalEntry.WILDCARD_NAME)) {

	    // build a PrivateCredentialPermission for all
	    // the Subject's principals that are instances of principalClass
	    
	    // the accCs is guaranteed to be a SubjectCodeSource
	    // because the earlier CodeSource.implies succeeded
	    SubjectCodeSource scs = (SubjectCodeSource)accCs;

	    Set principalSet = null;
	    try {
		Class pClass = Class.forName(principal.principalClass, false,
				ClassLoader.getSystemClassLoader());
		principalSet = scs.getSubject().getPrincipals(pClass);
	    } catch (Exception e) {
		if (debug != null) {
		    debug.println("problem finding Principal Class " +
				"when expanding SELF permission: " +
				e.toString());
		}
	    }

	    if (principalSet == null) {
		// error
		return new String[0][0];
	    }

	    String[][] info = new String[principalSet.size()][2];
	    java.util.Iterator pIterator = principalSet.iterator();

	    int i = 0;
	    while (pIterator.hasNext()) {
		Principal p = (Principal)pIterator.next();
		info[i][0] = p.getClass().getName();
		info[i][1] = p.getName();
		i++;
	    }
	    return info;

	} else {

	    // build a PrivateCredentialPermission for every
	    // one of the current Subject's principals

	    // the accCs is guaranteed to be a SubjectCodeSource
	    // because the earlier CodeSource.implies succeeded
	    SubjectCodeSource scs = (SubjectCodeSource)accCs;
	    Set principalSet = scs.getSubject().getPrincipals();

	    String[][] info = new String[principalSet.size()][2];
	    java.util.Iterator pIterator = principalSet.iterator();

	    int i = 0;
	    while (pIterator.hasNext()) {
		Principal p = (Principal)pIterator.next();
		info[i][0] = p.getClass().getName();
		info[i][1] = p.getName();
		i++;
	    }
	    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 verify
     * signed 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).
     */
    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) {
	CodeSource canonCs = cs;
	if (cs.getLocation() != null &&
	    cs.getLocation().getProtocol().equalsIgnoreCase("file")) {
	    try {
		String path = cs.getLocation().getFile().replace
							('/',
							File.separatorChar);
		URL csUrl = null;
		if (path.endsWith("*")) {
		    // remove trailing '*' because it causes canonicalization
		    // to fail on win32
		    path = path.substring(0, path.length()-1);
		    boolean appendFileSep = false;
		    if (path.endsWith(File.separator))
			appendFileSep = true;
		    if (path.equals("")) {
			path = System.getProperty("user.dir");
		    }
		    File f = new File(path);
		    path = f.getCanonicalPath();
		    StringBuffer sb = new StringBuffer(path);
		    // reappend '*' to canonicalized filename (note that
		    // canonicalization may have removed trailing file
		    // separator, so we have to check for that, too)
		    if (!path.endsWith(File.separator) &&
			(appendFileSep || f.isDirectory()))
			sb.append(File.separatorChar);
		    sb.append('*');
		    path = sb.toString();
		} else {
		    path = new File(path).getCanonicalPath();
		}
		csUrl = new File(path).toURL();

		if (cs instanceof SubjectCodeSource) {
		    SubjectCodeSource scs = (SubjectCodeSource)cs;
		    if (extractSignerCerts) {
			canonCs = new SubjectCodeSource
						(scs.getSubject(),
						scs.getPrincipals(),
						csUrl,
						getSignerCertificates(scs));
		    } else {
			canonCs = new SubjectCodeSource
						(scs.getSubject(),
						scs.getPrincipals(),
						csUrl,
						scs.getCertificates());
		    }
		} else {
		    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) {
		    if (!(cs instanceof SubjectCodeSource)) {
			canonCs = new CodeSource(cs.getLocation(),
						getSignerCertificates(cs));
		    } else {
			SubjectCodeSource scs = (SubjectCodeSource)cs;
			canonCs = new SubjectCodeSource(scs.getSubject(),
						scs.getPrincipals(),
						scs.getLocation(),
						getSignerCertificates(scs));
		    }
		}
	    }
	} else {
	    if (extractSignerCerts) {
		if (!(cs instanceof SubjectCodeSource)) {
		    canonCs = new CodeSource(cs.getLocation(),
					getSignerCertificates(cs));
		} else {
		    SubjectCodeSource scs = (SubjectCodeSource)cs;
		    canonCs = new SubjectCodeSource(scs.getSubject(),
					scs.getPrincipals(),
					scs.getLocation(),
					getSignerCertificates(scs));
		}
	    }
	}
	return canonCs;
    }

    /**
     * Each entry in the policy configuration file is represented by a
     * PolicyEntry object.  <p>
     *
     * 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. <p>
     * 
     * The Permission contains the (Type, Name, Action) triplet. <p>
     * 
     * 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.
     * 
     * <p>
     * For example, the entry
     * <pre>
     * 		permission java.io.File "/tmp", "read,write",
     *		signedBy "Duke";	
     * </pre>
     * is represented internally 
     * <pre>
     * 
     * 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);
     * </pre>
     * 
     * @author Marianne Mueller
     * @author Roland Schemers
     * @version 1.6, 03/04/97
     * @see java.security.CodeSource
     * @see java.security.Policy
     * @see java.security.Permissions
     * @see java.security.ProtectionDomain
     */

    private static class PolicyEntry {

	CodeSource codesource;
	Vector permissions;

	/**
	 * 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)
	{
	    this.codesource = cs;
	    this.permissions = new Vector();
	}

	/**
	 * add a Permission object to this entry.
	 */
	void add(Permission p) {
	    permissions.addElement(p);
	}

	/**
	 * Return the CodeSource for this policy entry
	 */
	CodeSource getCodeSource() {
	    return this.codesource;
	}

	public String toString(){
	    StringBuffer sb = new StringBuffer();
	    sb.append(rb.getString("("));
	    sb.append(getCodeSource());
	    sb.append("\n");
	    for (int j = 0; j < permissions.size(); j++) {
		Permission p = (Permission) permissions.elementAt(j);
		sb.append(rb.getString(" "));
		sb.append(rb.getString(" "));
		sb.append(p);
		sb.append(rb.getString("\n"));
	    }
	    sb.append(rb.getString(")"));
	    sb.append(rb.getString("\n"));
	    return sb.toString();
	}

    }
}

class PolicyPermissions extends PermissionCollection {

    private static final long serialVersionUID = -1954188373270545523L;

    private CodeSource codesource;
    private Permissions perms;
    private PolicyFile policy;
    private boolean notInit; // have we pulled in the policy permissions yet?
    private Vector additionalPerms;

    PolicyPermissions(PolicyFile policy,
		      CodeSource codesource)
    {
	this.codesource = codesource;
	this.policy = policy;
	this.perms = null;
	this.notInit = true;
	this.additionalPerms = null;
    }

    public void add(Permission permission) {
	if (isReadOnly())
	    throw new SecurityException
	    (PolicyFile.rb.getString
	    ("attempt to add a Permission to a readonly PermissionCollection"));

	if (perms == null) {
	    if (additionalPerms == null)
		additionalPerms = new Vector();
	    additionalPerms.add(permission);
	} else {
	    perms.add(permission);
	}
    }

    private synchronized void init() {
	if (notInit) {
	    if (perms == null)
		perms = new Permissions();

	    if (additionalPerms != null) {
		Enumeration e = additionalPerms.elements();
		while (e.hasMoreElements()) {
		    perms.add((Permission)e.nextElement());
		}
		additionalPerms = null;
	    }
	    policy.getPermissions(perms,codesource);
	    notInit=false;
	}
    }

    public boolean implies(Permission permission) {
	if (notInit)
	    init();
	return perms.implies(permission);
    }

    public Enumeration elements() {
	if (notInit)
	    init();
	return perms.elements();
    }

    public String toString() {
	if (notInit)
	    init();
	return perms.toString();
    }
}