/* * Copyright (c) 1999, 2006, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ 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. * * @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.security.AccessController.doPrivileged (new java.security.PrivilegedAction<java.util.ResourceBundle>() { public java.util.ResourceBundle 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<PolicyEntry> 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<PolicyEntry>(); 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<Void>() { public Void 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<PolicyParser.GrantEntry> enum_ = pp.grantElements(); while (enum_.hasMoreElements()) { PolicyParser.GrantEntry ge = 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<PolicyParser.PrincipalEntry> li = ge.principals.listIterator(); while (li.hasNext()) { PolicyParser.PrincipalEntry pppe = 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<PolicyParser.PermissionEntry> enum_ = ge.permissionElements(); while (enum_.hasMoreElements()) { PolicyParser.PermissionEntry pe = 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<Certificate> 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<Certificate>(); 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<PolicyEntry> 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 java.security.AccessController.doPrivileged (new java.security.PrivilegedAction<PermissionCollection>() { public PermissionCollection 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 = 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 = 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<PolicyParser.PrincipalEntry> pli = scs.getPrincipals().listIterator(); while (pli.hasNext()) { PolicyParser.PrincipalEntry principal = 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<Principal> 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<Principal> pIterator = principalSet.iterator(); int i = 0; while (pIterator.hasNext()) { Principal p = 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<Principal> principalSet = scs.getSubject().getPrincipals(); String[][] info = new String[principalSet.size()][2]; java.util.Iterator<Principal> pIterator = principalSet.iterator(); int i = 0; while (pIterator.hasNext()) { Principal p = 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<Certificate> userCertList = new ArrayList<Certificate>(); 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 * @see java.security.CodeSource * @see java.security.Policy * @see java.security.Permissions * @see java.security.ProtectionDomain */ private static class PolicyEntry { CodeSource codesource; Vector<Permission> 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<Permission>(); } /** * 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 = 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<Permission> 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<Permission>(); additionalPerms.add(permission); } else { perms.add(permission); } } private synchronized void init() { if (notInit) { if (perms == null) perms = new Permissions(); if (additionalPerms != null) { Enumeration<Permission> e = additionalPerms.elements(); while (e.hasMoreElements()) { perms.add(e.nextElement()); } additionalPerms = null; } policy.getPermissions(perms,codesource); notInit=false; } } public boolean implies(Permission permission) { if (notInit) init(); return perms.implies(permission); } public Enumeration<Permission> elements() { if (notInit) init(); return perms.elements(); } public String toString() { if (notInit) init(); return perms.toString(); } }