/*
* @(#)ConfigFile.java 1.18 03/12/19
*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package com.sun.security.auth.login;
import javax.security.auth.AuthPermission;
import javax.security.auth.login.AppConfigurationEntry;
import java.io.*;
import java.util.*;
import java.net.URL;
import java.text.MessageFormat;
import sun.security.util.ResourcesMgr;
import sun.security.util.PropertyExpander;
/**
* This class represents a default implementation for
* javax.security.auth.login.Configuration
.
*
*
This object stores the runtime login configuration representation,
* and is the amalgamation of multiple static login
* configurations that resides in files.
* The algorithm for locating the login configuration file(s) and reading their
* information into this Configuration
object is:
*
*
java.security.Security
properties,
* login.config.url.1, login.config.url.2, ...,
* login.config.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, where <JAVA_HOME>
* refers to the directory where the JDK was installed.
* Each property value specifies a URL
pointing to a
* login configuration file to be loaded. Read in and load
* each configuration.
*
* java.lang.System
property
* java.security.auth.login.config
* may also be set to a URL
pointing to another
* login configuration 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 login configuration.
*
* The configuration syntax supported by this implementation
* is exactly that syntax specified in the
* javax.security.auth.login.Configuration
class.
*
* @version 1.6, 01/14/00
* @see javax.security.auth.login.LoginContext
*/
public class ConfigFile extends javax.security.auth.login.Configuration {
private StreamTokenizer st;
private int lookahead;
private int linenum;
private HashMap configuration;
private boolean expandProp = true;
private boolean testing = false;
/**
* Create a new Configuration
object.
*/
public ConfigFile() {
String expandProperties = (String)
java.security.AccessController.doPrivileged
(new java.security.PrivilegedAction() {
public Object run() {
return System.getProperty("policy.expandProperties");
}
});
if ("false".equals(expandProperties))
expandProp = false;
try {
init();
} catch (IOException ioe) {
throw (SecurityException)
new SecurityException(ioe.getMessage()).initCause(ioe);
}
}
/**
* Read and initialize the entire login Configuration.
*
*
* * @exception IOException if the Configuration can not be initialized.
* @exception SecurityException if the caller does not have permission * to initialize the Configuration. */ private void init() throws IOException { boolean initialized = false; FileReader fr = null; String sep = File.separator; // new configuration HashMap newConfig = new HashMap(); String allowSys = java.security.Security.getProperty ("policy.allowSystemProperty"); if ("true".equalsIgnoreCase(allowSys)) { String extra_config = System.getProperty ("java.security.auth.login.config"); if (extra_config != null) { boolean overrideAll = false; if (extra_config.startsWith("=")) { overrideAll = true; extra_config = extra_config.substring(1); } try { extra_config = PropertyExpander.expand(extra_config); } catch (PropertyExpander.ExpandException peee) { MessageFormat form = new MessageFormat (ResourcesMgr.getString ("Unable to properly expand config", "sun.security.util.AuthResources")); Object[] source = {extra_config}; throw new IOException(form.format(source)); } URL configURL = null; try { configURL = new URL(extra_config); } catch (java.net.MalformedURLException mue) { File configFile = new File(extra_config); if (configFile.exists()) { configURL = new URL("file:" + configFile.getCanonicalPath()); } else { MessageFormat form = new MessageFormat (ResourcesMgr.getString ("extra_config (No such file or directory)", "sun.security.util.AuthResources")); Object[] source = {extra_config}; throw new IOException(form.format(source)); } } if (testing) System.out.println("reading "+configURL); init(configURL, newConfig); initialized = true; if (overrideAll) { if (testing) System.out.println("overriding other policies!"); } } } int n = 1; String config_url; while ((config_url = java.security.Security.getProperty ("login.config.url."+n)) != null) { try { config_url = PropertyExpander.expand (config_url).replace(File.separatorChar, '/'); if (testing) System.out.println("\tReading config: " + config_url); init(new URL(config_url), newConfig); initialized = true; } catch (PropertyExpander.ExpandException peee) { MessageFormat form = new MessageFormat (ResourcesMgr.getString ("Unable to properly expand config", "sun.security.util.AuthResources")); Object[] source = {config_url}; throw new IOException(form.format(source)); } n++; } if (initialized == false && n == 1 && config_url == null) { // get the config from the user's home directory if (testing) System.out.println("\tReading Policy " + "from ~/.java.login.config"); config_url = System.getProperty("user.home"); try { init(new URL("file:" + config_url + File.separatorChar + ".java.login.config"), newConfig); } catch (IOException ioe) { throw new IOException(ResourcesMgr.getString ("Unable to locate a login configuration", "sun.security.util.AuthResources")); } } configuration = newConfig; } private void init(URL config, HashMap newConfig) throws IOException { InputStreamReader isr = new InputStreamReader(getInputStream(config), "UTF-8"); readConfig(isr, newConfig); isr.close(); } /** * Retrieve an entry from the Configuration using an application name * as an index. * *
* * @param applicationName the name used to index the Configuration. * @return an array of AppConfigurationEntries which correspond to * the stacked configuration of LoginModules for this * application, or null if this application has no configured * LoginModules. */ public AppConfigurationEntry[] getAppConfigurationEntry (String applicationName) { LinkedList list = null; synchronized (configuration) { list = (LinkedList)configuration.get(applicationName); } if (list == null || list.size() == 0) return null; AppConfigurationEntry[] entries = new AppConfigurationEntry[list.size()]; Iterator iterator = list.iterator(); for (int i = 0; iterator.hasNext(); i++) { AppConfigurationEntry e = (AppConfigurationEntry)iterator.next(); entries[i] = new AppConfigurationEntry(e.getLoginModuleName(), e.getControlFlag(), e.getOptions()); } return entries; } /** * Refresh and reload the Configuration by re-reading all of the * login configurations. * *
* * @exception SecurityException if the caller does not have permission * to refresh the Configuration. */ public synchronized void refresh() { java.lang.SecurityManager sm = System.getSecurityManager(); if (sm != null) sm.checkPermission(new AuthPermission("refreshLoginConfiguration")); java.security.AccessController.doPrivileged (new java.security.PrivilegedAction() { public Object run() { try { init(); } catch (java.io.IOException ioe) { throw new SecurityException(ioe.getLocalizedMessage()); } return null; } }); } private void readConfig(Reader reader, HashMap newConfig) throws IOException { int linenum = 1; if (!(reader instanceof BufferedReader)) reader = new BufferedReader(reader); st = new StreamTokenizer(reader); st.quoteChar('"'); st.wordChars('$', '$'); st.wordChars('_', '_'); st.wordChars('-', '-'); st.lowerCaseMode(false); st.slashSlashComments(true); st.slashStarComments(true); st.eolIsSignificant(true); lookahead = nextToken(); while (lookahead != StreamTokenizer.TT_EOF) { if (testing) System.out.print("\tReading next config entry: "); parseLoginEntry(newConfig); } } private void parseLoginEntry(HashMap newConfig) throws IOException { String appName; String moduleClass; String sflag; AppConfigurationEntry.LoginModuleControlFlag controlFlag; LinkedList configEntries = new LinkedList(); // application name appName = st.sval; lookahead = nextToken(); if (testing) System.out.println("appName = " + appName); match("{"); // get the modules while (peek("}") == false) { HashSet argSet = new HashSet(); // get the module class name moduleClass = match("module class name"); // controlFlag (required, optional, etc) sflag = match("controlFlag"); if (sflag.equalsIgnoreCase("REQUIRED")) controlFlag = AppConfigurationEntry.LoginModuleControlFlag.REQUIRED; else if (sflag.equalsIgnoreCase("REQUISITE")) controlFlag = AppConfigurationEntry.LoginModuleControlFlag.REQUISITE; else if (sflag.equalsIgnoreCase("SUFFICIENT")) controlFlag = AppConfigurationEntry.LoginModuleControlFlag.SUFFICIENT; else if (sflag.equalsIgnoreCase("OPTIONAL")) controlFlag = AppConfigurationEntry.LoginModuleControlFlag.OPTIONAL; else { MessageFormat form = new MessageFormat(ResourcesMgr.getString ("Configuration Error:\n\tInvalid control flag, flag", "sun.security.util.AuthResources")); Object[] source = {sflag}; throw new IOException(form.format(source)); } // get the args HashMap options = new HashMap(); String key; String value; while (peek(";") == false) { key = match("option key"); match("="); try { value = expand(match("option value")); } catch (PropertyExpander.ExpandException peee) { throw new IOException(peee.getLocalizedMessage()); } options.put(key, value); } lookahead = nextToken(); // create the new element if (testing) { System.out.print("\t\t" + moduleClass + ", " + sflag); java.util.Iterator i = options.keySet().iterator(); while (i.hasNext()) { key = (String)i.next(); System.out.print(", " + key + "=" + (String)options.get(key)); } System.out.println(""); } AppConfigurationEntry entry = new AppConfigurationEntry (moduleClass, controlFlag, options); configEntries.add(entry); } match("}"); match(";"); // add this configuration entry if (newConfig.containsKey(appName)) { MessageFormat form = new MessageFormat(ResourcesMgr.getString ("Configuration Error:\n\t" + "Can not specify multiple entries for appName", "sun.security.util.AuthResources")); Object[] source = {appName}; throw new IOException(form.format(source)); } newConfig.put(appName, configEntries); if (testing) System.out.println("\t\t***Added entry for " + appName + " to overall configuration***"); } private String match(String expect) throws IOException { String value = null; switch(lookahead) { case StreamTokenizer.TT_EOF: MessageFormat form1 = new MessageFormat(ResourcesMgr.getString ("Configuration Error:\n\texpected [expect], " + "read [end of file]", "sun.security.util.AuthResources")); Object[] source1 = {expect}; throw new IOException(form1.format(source1)); case '"': case StreamTokenizer.TT_WORD: if (expect.equalsIgnoreCase("module class name") || expect.equalsIgnoreCase("controlFlag") || expect.equalsIgnoreCase("option key") || expect.equalsIgnoreCase("option value")) { value = st.sval; lookahead = nextToken(); } else { MessageFormat form = new MessageFormat(ResourcesMgr.getString ("Configuration Error:\n\tLine line: " + "expected [expect], found [value]", "sun.security.util.AuthResources")); Object[] source = {new Integer(linenum), expect, st.sval}; throw new IOException(form.format(source)); } break; case '{': if (expect.equalsIgnoreCase("{")) { lookahead = nextToken(); } else { MessageFormat form = new MessageFormat(ResourcesMgr.getString ("Configuration Error:\n\tLine line: expected [expect]", "sun.security.util.AuthResources")); Object[] source = {new Integer(linenum), expect, st.sval}; throw new IOException(form.format(source)); } break; case ';': if (expect.equalsIgnoreCase(";")) { lookahead = nextToken(); } else { MessageFormat form = new MessageFormat(ResourcesMgr.getString ("Configuration Error:\n\tLine line: expected [expect]", "sun.security.util.AuthResources")); Object[] source = {new Integer(linenum), expect, st.sval}; throw new IOException(form.format(source)); } break; case '}': if (expect.equalsIgnoreCase("}")) { lookahead = nextToken(); } else { MessageFormat form = new MessageFormat(ResourcesMgr.getString ("Configuration Error:\n\tLine line: expected [expect]", "sun.security.util.AuthResources")); Object[] source = {new Integer(linenum), expect, st.sval}; throw new IOException(form.format(source)); } break; case '=': if (expect.equalsIgnoreCase("=")) { lookahead = nextToken(); } else { MessageFormat form = new MessageFormat(ResourcesMgr.getString ("Configuration Error:\n\tLine line: expected [expect]", "sun.security.util.AuthResources")); Object[] source = {new Integer(linenum), expect, st.sval}; throw new IOException(form.format(source)); } break; default: MessageFormat form = new MessageFormat(ResourcesMgr.getString ("Configuration Error:\n\tLine line: " + "expected [expect], found [value]", "sun.security.util.AuthResources")); Object[] source = {new Integer(linenum), expect, st.sval}; throw new IOException(form.format(source)); } return value; } private boolean peek(String expect) { boolean found = false; switch (lookahead) { case ',': if (expect.equalsIgnoreCase(",")) found = true; break; case ';': if (expect.equalsIgnoreCase(";")) found = true; break; case '{': if (expect.equalsIgnoreCase("{")) found = true; break; case '}': if (expect.equalsIgnoreCase("}")) found = true; break; default: } return found; } private int nextToken() throws IOException { int tok; while ((tok = st.nextToken()) == StreamTokenizer.TT_EOL) { linenum++; } return tok; } /* * 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(); } } private String expand(String value) throws PropertyExpander.ExpandException, IOException { if ("".equals(value)) { return value; } if (expandProp) { String s = PropertyExpander.expand(value); if (s == null || s.length() == 0) { MessageFormat form = new MessageFormat(ResourcesMgr.getString ("Configuration Error:\n\tLine line: " + "system property [value] expanded to empty value", "sun.security.util.AuthResources")); Object[] source = {new Integer(linenum), value}; throw new IOException(form.format(source)); } return s; } else { return value; } } }