8005117: Eliminate dependency from ConfigSpiFile to com.sun.security.auth.login.ConfigFile
Reviewed-by: alanb, mchung, weijun
--- a/jdk/src/share/classes/com/sun/security/auth/login/ConfigFile.java Mon Dec 17 08:30:06 2012 -0500
+++ b/jdk/src/share/classes/com/sun/security/auth/login/ConfigFile.java Wed Dec 26 10:07:00 2012 -0500
@@ -25,41 +25,39 @@
package com.sun.security.auth.login;
-import javax.security.auth.AuthPermission;
import javax.security.auth.login.AppConfigurationEntry;
-import java.io.*;
-import java.util.*;
+import javax.security.auth.login.Configuration;
import java.net.URI;
-import java.net.URL;
-import java.net.MalformedURLException;
-import java.text.MessageFormat;
-import sun.security.util.Debug;
-import sun.security.util.ResourcesMgr;
-import sun.security.util.PropertyExpander;
+
+// NOTE: As of JDK 8, this class instantiates
+// sun.security.provider.ConfigSpiFile and forwards all methods to that
+// implementation. All implementation fixes and enhancements should be made to
+// sun.security.provider.ConfigSpiFile and not this class.
+// See JDK-8005117 for more information.
/**
* This class represents a default implementation for
- * <code>javax.security.auth.login.Configuration</code>.
+ * {@code javax.security.auth.login.Configuration}.
*
* <p> 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 <code>Configuration</code> object is:
+ * information into this {@code Configuration} object is:
*
* <ol>
* <li>
* Loop through the security properties,
* <i>login.config.url.1</i>, <i>login.config.url.2</i>, ...,
* <i>login.config.url.X</i>.
- * Each property value specifies a <code>URL</code> pointing to a
+ * Each property value specifies a {@code URL} pointing to a
* login configuration file to be loaded. Read in and load
* each configuration.
*
* <li>
- * The <code>java.lang.System</code> property
+ * The {@code java.lang.System} property
* <i>java.security.auth.login.config</i>
- * may also be set to a <code>URL</code> pointing to another
+ * may also be set to a {@code 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
@@ -80,593 +78,63 @@
*
* <p> The configuration syntax supported by this implementation
* is exactly that syntax specified in the
- * <code>javax.security.auth.login.Configuration</code> class.
+ * {@code javax.security.auth.login.Configuration} class.
*
* @see javax.security.auth.login.LoginContext
* @see java.security.Security security properties
*/
-public class ConfigFile extends javax.security.auth.login.Configuration {
+public class ConfigFile extends Configuration {
- private StreamTokenizer st;
- private int lookahead;
- private int linenum;
- private HashMap<String, LinkedList<AppConfigurationEntry>> configuration;
- private boolean expandProp = true;
- private URL url;
-
- private static Debug debugConfig = Debug.getInstance("configfile");
- private static Debug debugParser = Debug.getInstance("configparser");
+ private sun.security.provider.ConfigSpiFile configFile;
/**
- * Create a new <code>Configuration</code> object.
+ * Create a new {@code Configuration} object.
+ *
+ * @throws SecurityException if the {@code Configuration} can not be
+ * initialized
*/
public ConfigFile() {
- try {
- init(url);
- } catch (IOException ioe) {
- throw (SecurityException)
- new SecurityException(ioe.getMessage()).initCause(ioe);
- }
- }
-
- /**
- * Create a new <code>Configuration</code> object from the specified URI.
- *
- * @param uri Create a new Configuration object from this URI.
- */
- public ConfigFile(URI uri) {
- // only load config from the specified URI
- try {
- url = uri.toURL();
- init(url);
- } catch (MalformedURLException mue) {
- throw (SecurityException)
- new SecurityException(mue.getMessage()).initCause(mue);
- } catch (IOException ioe) {
- throw (SecurityException)
- new SecurityException(ioe.getMessage()).initCause(ioe);
- }
+ configFile = new sun.security.provider.ConfigSpiFile();
}
/**
- * Read and initialize the entire login Configuration.
- *
- * <p>
+ * Create a new {@code Configuration} object from the specified {@code URI}.
*
- * @exception IOException if the Configuration can not be initialized. <p>
- * @exception SecurityException if the caller does not have permission
- * to initialize the Configuration.
+ * @param uri the {@code URI}
+ * @throws SecurityException if the {@code Configuration} can not be
+ * initialized
+ * @throws NullPointerException if {@code uri} is null
*/
- private void init(URL url) throws IOException {
-
- boolean initialized = false;
- FileReader fr = null;
- String sep = File.separator;
-
- if ("false".equals(System.getProperty("policy.expandProperties"))) {
- expandProp = false;
- }
-
- // new configuration
- HashMap<String, LinkedList<AppConfigurationEntry>> newConfig =
- new HashMap<>();
-
- if (url != null) {
-
- /**
- * If the caller specified a URI via Configuration.getInstance,
- * we only read from that URI
- */
- if (debugConfig != null) {
- debugConfig.println("reading " + url);
- }
- init(url, newConfig);
- configuration = newConfig;
- return;
- }
-
- /**
- * Caller did not specify URI via Configuration.getInstance.
- * Read from URLs listed in the java.security properties file.
- */
-
- 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 = configFile.toURI().toURL();
- } 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 (debugConfig != null) {
- debugConfig.println("reading "+configURL);
- }
- init(configURL, newConfig);
- initialized = true;
- if (overrideAll) {
- if (debugConfig != null) {
- debugConfig.println("overriding other policies!");
- }
- configuration = newConfig;
- return;
- }
- }
- }
-
- 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 (debugConfig != null) {
- debugConfig.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 (debugConfig != null) {
- debugConfig.println("\tReading Policy " +
- "from ~/.java.login.config");
- }
- config_url = System.getProperty("user.home");
- String userConfigFile = config_url +
- File.separatorChar + ".java.login.config";
-
- // No longer throws an exception when there's no config file
- // at all. Returns an empty Configuration instead.
- if (new File(userConfigFile).exists()) {
- init(new File(userConfigFile).toURI().toURL(),
- newConfig);
- }
- }
-
- configuration = newConfig;
- }
-
- private void init(URL config,
- HashMap<String, LinkedList<AppConfigurationEntry>> newConfig)
- throws IOException {
-
- InputStreamReader isr = null;
- try {
- isr = new InputStreamReader(getInputStream(config), "UTF-8");
- readConfig(isr, newConfig);
- } catch (FileNotFoundException fnfe) {
- if (debugConfig != null) {
- debugConfig.println(fnfe.toString());
- }
- throw new IOException(ResourcesMgr.getString
- ("Configuration.Error.No.such.file.or.directory",
- "sun.security.util.AuthResources"));
- } finally {
- if (isr != null) {
- isr.close();
- }
- }
- }
-
- /**
- * Retrieve an entry from the Configuration using an application name
- * as an index.
- *
- * <p>
- *
- * @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<AppConfigurationEntry> list = null;
- synchronized (configuration) {
- list = configuration.get(applicationName);
- }
-
- if (list == null || list.size() == 0)
- return null;
-
- AppConfigurationEntry[] entries =
- new AppConfigurationEntry[list.size()];
- Iterator<AppConfigurationEntry> iterator = list.iterator();
- for (int i = 0; iterator.hasNext(); i++) {
- AppConfigurationEntry e = iterator.next();
- entries[i] = new AppConfigurationEntry(e.getLoginModuleName(),
- e.getControlFlag(),
- e.getOptions());
- }
- return entries;
+ public ConfigFile(URI uri) {
+ configFile = new sun.security.provider.ConfigSpiFile(uri);
}
/**
- * Refresh and reload the Configuration by re-reading all of the
- * login configurations.
+ * Retrieve an entry from the {@code Configuration} using an application
+ * name as an index.
*
- * <p>
- *
- * @exception SecurityException if the caller does not have permission
- * to refresh the Configuration.
+ * @param applicationName the name used to index the {@code Configuration}
+ * @return an array of {@code AppConfigurationEntry} which correspond to
+ * the stacked configuration of {@code LoginModule}s for this
+ * application, or null if this application has no configured
+ * {@code LoginModule}s.
*/
- 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<Void>() {
- public Void run() {
- try {
- init(url);
- } catch (java.io.IOException ioe) {
- throw (SecurityException) new SecurityException
- (ioe.getLocalizedMessage()).initCause(ioe);
- }
- return null;
- }
- });
- }
-
- private void readConfig(Reader reader,
- HashMap<String, LinkedList<AppConfigurationEntry>> 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) {
- parseLoginEntry(newConfig);
- }
- }
-
- private void parseLoginEntry(
- HashMap<String, LinkedList<AppConfigurationEntry>> newConfig)
- throws IOException {
-
- String appName;
- String moduleClass;
- String sflag;
- AppConfigurationEntry.LoginModuleControlFlag controlFlag;
- LinkedList<AppConfigurationEntry> configEntries = new LinkedList<>();
-
- // application name
- appName = st.sval;
- lookahead = nextToken();
-
- if (debugParser != null) {
- debugParser.println("\tReading next config entry: " + appName);
- }
-
- match("{");
+ @Override
+ public AppConfigurationEntry[] getAppConfigurationEntry
+ (String applicationName) {
- // get the modules
- while (peek("}") == false) {
- // 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.Invalid.control.flag.flag",
- "sun.security.util.AuthResources"));
- Object[] source = {sflag};
- throw new IOException(form.format(source));
- }
-
- // get the args
- HashMap<String, String> 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 (debugParser != null) {
- debugParser.println("\t\t" + moduleClass + ", " + sflag);
- java.util.Iterator<String> i = options.keySet().iterator();
- while (i.hasNext()) {
- key = i.next();
- debugParser.println("\t\t\t" +
- key +
- "=" +
- options.get(key));
- }
- }
- 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.Can.not.specify.multiple.entries.for.appName",
- "sun.security.util.AuthResources"));
- Object[] source = {appName};
- throw new IOException(form.format(source));
- }
- newConfig.put(appName, configEntries);
+ return configFile.engineGetAppConfigurationEntry(applicationName);
}
- private String match(String expect) throws IOException {
-
- String value = null;
-
- switch(lookahead) {
- case StreamTokenizer.TT_EOF:
-
- MessageFormat form1 = new MessageFormat(ResourcesMgr.getString
- ("Configuration.Error.expected.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.Line.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.Line.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.Line.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.Line.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.Line.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.Line.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
+ /**
+ * Refresh and reload the {@code Configuration} by re-reading all of the
+ * login configurations.
+ *
+ * @throws SecurityException if the caller does not have permission
+ * to refresh the {@code Configuration}
*/
- private InputStream getInputStream(URL url) throws IOException {
- if ("file".equalsIgnoreCase(url.getProtocol())) {
- // Compatibility notes:
- //
- // Code changed from
- // String path = url.getFile().replace('/', File.separatorChar);
- // return new FileInputStream(path);
- //
- // The original implementation would search for "/tmp/a%20b"
- // when url is "file:///tmp/a%20b". This is incorrect. The
- // current codes fix this bug and searches for "/tmp/a b".
- // For compatibility reasons, when the file "/tmp/a b" does
- // not exist, the file named "/tmp/a%20b" will be tried.
- //
- // This also means that if both file exists, the behavior of
- // this method is changed, and the current codes choose the
- // correct one.
- try {
- return url.openStream();
- } catch (Exception e) {
- String file = url.getPath();
- if (url.getHost().length() > 0) { // For Windows UNC
- file = "//" + url.getHost() + file;
- }
- if (debugConfig != null) {
- debugConfig.println("cannot read " + url +
- ", try " + file);
- }
- return new FileInputStream(file);
- }
- } 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.Line.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;
- }
+ @Override
+ public synchronized void refresh() {
+ configFile.engineRefresh();
}
}
--- a/jdk/src/share/classes/sun/security/provider/ConfigSpiFile.java Mon Dec 17 08:30:06 2012 -0500
+++ b/jdk/src/share/classes/sun/security/provider/ConfigSpiFile.java Wed Dec 26 10:07:00 2012 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2012, 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
@@ -25,81 +25,668 @@
package sun.security.provider;
+import java.io.*;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.security.Security;
import java.security.URIParameter;
-
+import java.text.MessageFormat;
+import java.util.*;
+import javax.security.auth.AuthPermission;
+import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.Configuration;
import javax.security.auth.login.ConfigurationSpi;
-import javax.security.auth.login.AppConfigurationEntry;
-
-import com.sun.security.auth.login.ConfigFile;
+import sun.security.util.Debug;
+import sun.security.util.PropertyExpander;
+import sun.security.util.ResourcesMgr;
/**
- * This class wraps the ConfigFile subclass implementation of Configuration
- * inside a ConfigurationSpi implementation that is available from the
- * SUN provider via the Configuration.getInstance calls.
+ * This class represents a default implementation for
+ * {@code javax.security.auth.login.Configuration}.
+ *
+ * <p> 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 {@code Configuration} object is:
+ *
+ * <ol>
+ * <li>
+ * Loop through the security properties,
+ * <i>login.config.url.1</i>, <i>login.config.url.2</i>, ...,
+ * <i>login.config.url.X</i>.
+ * Each property value specifies a <code>URL</code> pointing to a
+ * login configuration file to be loaded. Read in and load
+ * each configuration.
*
+ * <li>
+ * The {@code java.lang.System} property
+ * <i>java.security.auth.login.config</i>
+ * may also be set to a {@code 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,
+ * <i>policy.allowSystemProperty</i> is set to <i>true</i>),
+ * also load that login configuration.
+ *
+ * <li>
+ * If the <i>java.security.auth.login.config</i> property is defined using
+ * "==" (rather than "="), then ignore all other specified
+ * login configurations and only load this configuration.
+ *
+ * <li>
+ * If no system or security properties were set, try to read from the file,
+ * ${user.home}/.java.login.config, where ${user.home} is the value
+ * represented by the "user.home" System property.
+ * </ol>
+ *
+ * <p> The configuration syntax supported by this implementation
+ * is exactly that syntax specified in the
+ * {@code javax.security.auth.login.Configuration} class.
+ *
+ * @see javax.security.auth.login.LoginContext
+ * @see java.security.Security security properties
*/
public final class ConfigSpiFile extends ConfigurationSpi {
- private ConfigFile cf;
+ private URL url;
+ private boolean expandProp = true;
+ private Map<String, List<AppConfigurationEntry>> configuration;
+ private int linenum;
+ private StreamTokenizer st;
+ private int lookahead;
+
+ private static Debug debugConfig = Debug.getInstance("configfile");
+ private static Debug debugParser = Debug.getInstance("configparser");
+
+ /**
+ * Create a new {@code Configuration} object.
+ *
+ * @throws SecurityException if the {@code Configuration} can not be
+ * initialized
+ */
+ public ConfigSpiFile() {
+ try {
+ init();
+ } catch (IOException ioe) {
+ throw new SecurityException(ioe);
+ }
+ }
+
+ /**
+ * Create a new {@code Configuration} object from the specified {@code URI}.
+ *
+ * @param uri the {@code URI}
+ * @throws SecurityException if the {@code Configuration} can not be
+ * initialized
+ * @throws NullPointerException if {@code uri} is null
+ */
+ public ConfigSpiFile(URI uri) {
+ // only load config from the specified URI
+ try {
+ url = uri.toURL();
+ init();
+ } catch (IOException ioe) {
+ throw new SecurityException(ioe);
+ }
+ }
public ConfigSpiFile(final Configuration.Parameters params)
- throws java.io.IOException {
+ throws IOException {
// call in a doPrivileged
//
// we have already passed the Configuration.getInstance
// security check. also this class is not freely accessible
// (it is in the "sun" package).
- //
- // we can not put doPrivileged calls into
- // ConfigFile because it is a public com.sun class
try {
- AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
+ public Void run() throws IOException {
+ if (params == null) {
+ init();
+ } else {
+ if (!(params instanceof URIParameter)) {
+ throw new IllegalArgumentException
+ ("Unrecognized parameter: " + params);
+ }
+ URIParameter uriParam = (URIParameter)params;
+ url = uriParam.getURI().toURL();
+ init();
+ }
+ return null;
+ }
+ });
+ } catch (PrivilegedActionException pae) {
+ throw (IOException)pae.getException();
+ }
+
+ // if init() throws some other RuntimeException,
+ // let it percolate up naturally.
+ }
+
+ /**
+ * Read and initialize the entire login Configuration from the configured
+ * URL.
+ *
+ * @throws IOException if the Configuration can not be initialized
+ * @throws SecurityException if the caller does not have permission
+ * to initialize the Configuration
+ */
+ private void init() throws IOException {
+
+ boolean initialized = false;
+
+ // For policy.expandProperties, check if either a security or system
+ // property is set to false (old code erroneously checked the system
+ // prop so we must check both to preserve compatibility).
+ String expand = Security.getProperty("policy.expandProperties");
+ if (expand == null) {
+ expand = System.getProperty("policy.expandProperties");
+ }
+ if ("false".equals(expand)) {
+ expandProp = false;
+ }
+
+ // new configuration
+ Map<String, List<AppConfigurationEntry>> newConfig = new HashMap<>();
+
+ if (url != null) {
+ /**
+ * If the caller specified a URI via Configuration.getInstance,
+ * we only read from that URI
+ */
+ if (debugConfig != null) {
+ debugConfig.println("reading " + url);
+ }
+ init(url, newConfig);
+ configuration = newConfig;
+ return;
+ }
+
+ /**
+ * Caller did not specify URI via Configuration.getInstance.
+ * Read from URLs listed in the java.security properties file.
+ */
+ String allowSys = 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 (MalformedURLException mue) {
+ File configFile = new File(extra_config);
+ if (configFile.exists()) {
+ configURL = configFile.toURI().toURL();
+ } 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 (debugConfig != null) {
+ debugConfig.println("reading "+configURL);
+ }
+ init(configURL, newConfig);
+ initialized = true;
+ if (overrideAll) {
+ if (debugConfig != null) {
+ debugConfig.println("overriding other policies!");
+ }
+ configuration = newConfig;
+ return;
+ }
+ }
+ }
+
+ int n = 1;
+ String config_url;
+ while ((config_url = Security.getProperty
+ ("login.config.url."+n)) != null) {
+ try {
+ config_url = PropertyExpander.expand
+ (config_url).replace(File.separatorChar, '/');
+ if (debugConfig != null) {
+ debugConfig.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 (debugConfig != null) {
+ debugConfig.println("\tReading Policy " +
+ "from ~/.java.login.config");
+ }
+ config_url = System.getProperty("user.home");
+ String userConfigFile = config_url +
+ File.separatorChar + ".java.login.config";
+
+ // No longer throws an exception when there's no config file
+ // at all. Returns an empty Configuration instead.
+ if (new File(userConfigFile).exists()) {
+ init(new File(userConfigFile).toURI().toURL(),
+ newConfig);
+ }
+ }
+
+ configuration = newConfig;
+ }
+
+ private void init(URL config,
+ Map<String, List<AppConfigurationEntry>> newConfig)
+ throws IOException {
+
+ try (InputStreamReader isr
+ = new InputStreamReader(getInputStream(config), "UTF-8")) {
+ readConfig(isr, newConfig);
+ } catch (FileNotFoundException fnfe) {
+ if (debugConfig != null) {
+ debugConfig.println(fnfe.toString());
+ }
+ throw new IOException(ResourcesMgr.getString
+ ("Configuration.Error.No.such.file.or.directory",
+ "sun.security.util.AuthResources"));
+ }
+ }
+
+ /**
+ * 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.
+ */
+ @Override
+ public AppConfigurationEntry[] engineGetAppConfigurationEntry
+ (String applicationName) {
+
+ List<AppConfigurationEntry> list = null;
+ synchronized (configuration) {
+ list = configuration.get(applicationName);
+ }
+
+ if (list == null || list.size() == 0)
+ return null;
+
+ AppConfigurationEntry[] entries =
+ new AppConfigurationEntry[list.size()];
+ Iterator<AppConfigurationEntry> iterator = list.iterator();
+ for (int i = 0; iterator.hasNext(); i++) {
+ AppConfigurationEntry e = 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.
+ *
+ * @throws SecurityException if the caller does not have permission
+ * to refresh the Configuration.
+ */
+ @Override
+ public synchronized void engineRefresh() {
+
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ sm.checkPermission(new AuthPermission("refreshLoginConfiguration"));
+
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
- if (params == null) {
- cf = new ConfigFile();
- } else {
- if (!(params instanceof URIParameter)) {
- throw new IllegalArgumentException
- ("Unrecognized parameter: " + params);
- }
- URIParameter uriParam = (URIParameter)params;
-
- cf = new ConfigFile(uriParam.getURI());
+ try {
+ init();
+ } catch (IOException ioe) {
+ throw new SecurityException(ioe.getLocalizedMessage(), ioe);
}
return null;
}
- });
- } catch (SecurityException se) {
+ });
+ }
+
+ private void readConfig(Reader reader,
+ Map<String, List<AppConfigurationEntry>> newConfig)
+ throws IOException {
+
+ 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) {
+ parseLoginEntry(newConfig);
+ }
+ }
+
+ private void parseLoginEntry(
+ Map<String, List<AppConfigurationEntry>> newConfig)
+ throws IOException {
+
+ List<AppConfigurationEntry> configEntries = new LinkedList<>();
+
+ // application name
+ String appName = st.sval;
+ lookahead = nextToken();
+
+ if (debugParser != null) {
+ debugParser.println("\tReading next config entry: " + appName);
+ }
+
+ match("{");
- // if ConfigFile threw a standalone SecurityException
- // (no cause), re-throw it.
- //
- // ConfigFile chains checked IOExceptions to SecurityException.
+ // get the modules
+ while (peek("}") == false) {
+ // get the module class name
+ String moduleClass = match("module class name");
- Throwable cause = se.getCause();
- if (cause != null && cause instanceof java.io.IOException) {
- throw (java.io.IOException)cause;
+ // controlFlag (required, optional, etc)
+ AppConfigurationEntry.LoginModuleControlFlag controlFlag;
+ String sflag = match("controlFlag").toUpperCase();
+ switch (sflag) {
+ case "REQUIRED":
+ controlFlag =
+ AppConfigurationEntry.LoginModuleControlFlag.REQUIRED;
+ break;
+ case "REQUISITE":
+ controlFlag =
+ AppConfigurationEntry.LoginModuleControlFlag.REQUISITE;
+ break;
+ case "SUFFICIENT":
+ controlFlag =
+ AppConfigurationEntry.LoginModuleControlFlag.SUFFICIENT;
+ break;
+ case "OPTIONAL":
+ controlFlag =
+ AppConfigurationEntry.LoginModuleControlFlag.OPTIONAL;
+ break;
+ default:
+ MessageFormat form = new MessageFormat(
+ ResourcesMgr.getString
+ ("Configuration.Error.Invalid.control.flag.flag",
+ "sun.security.util.AuthResources"));
+ Object[] source = {sflag};
+ throw new IOException(form.format(source));
+ }
+
+ // get the args
+ Map<String, String> options = new HashMap<>();
+ while (peek(";") == false) {
+ String key = match("option key");
+ match("=");
+ try {
+ options.put(key, expand(match("option value")));
+ } catch (PropertyExpander.ExpandException peee) {
+ throw new IOException(peee.getLocalizedMessage());
+ }
}
- // unrecognized cause
- throw se;
+ lookahead = nextToken();
+
+ // create the new element
+ if (debugParser != null) {
+ debugParser.println("\t\t" + moduleClass + ", " + sflag);
+ for (String key : options.keySet()) {
+ debugParser.println("\t\t\t" + key +
+ "=" + options.get(key));
+ }
+ }
+ configEntries.add(new AppConfigurationEntry(moduleClass,
+ controlFlag, options));
}
- // if ConfigFile throws some other RuntimeException,
- // let it percolate up naturally.
+ match("}");
+ match(";");
+
+ // add this configuration entry
+ if (newConfig.containsKey(appName)) {
+ MessageFormat form = new MessageFormat(ResourcesMgr.getString
+ ("Configuration.Error.Can.not.specify.multiple.entries.for.appName",
+ "sun.security.util.AuthResources"));
+ Object[] source = {appName};
+ throw new IOException(form.format(source));
+ }
+ newConfig.put(appName, configEntries);
}
- protected AppConfigurationEntry[] engineGetAppConfigurationEntry
- (String name) {
- return cf.getAppConfigurationEntry(name);
+ private String match(String expect) throws IOException {
+
+ String value = null;
+
+ switch(lookahead) {
+ case StreamTokenizer.TT_EOF:
+
+ MessageFormat form1 = new MessageFormat(ResourcesMgr.getString
+ ("Configuration.Error.expected.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.Line.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.Line.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.Line.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.Line.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.Line.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.Line.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;
}
- protected void engineRefresh() {
- cf.refresh();
+ 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;
+ }
+
+ private InputStream getInputStream(URL url) throws IOException {
+ if ("file".equalsIgnoreCase(url.getProtocol())) {
+ // Compatibility notes:
+ //
+ // Code changed from
+ // String path = url.getFile().replace('/', File.separatorChar);
+ // return new FileInputStream(path);
+ //
+ // The original implementation would search for "/tmp/a%20b"
+ // when url is "file:///tmp/a%20b". This is incorrect. The
+ // current codes fix this bug and searches for "/tmp/a b".
+ // For compatibility reasons, when the file "/tmp/a b" does
+ // not exist, the file named "/tmp/a%20b" will be tried.
+ //
+ // This also means that if both file exists, the behavior of
+ // this method is changed, and the current codes choose the
+ // correct one.
+ try {
+ return url.openStream();
+ } catch (Exception e) {
+ String file = url.getPath();
+ if (url.getHost().length() > 0) { // For Windows UNC
+ file = "//" + url.getHost() + file;
+ }
+ if (debugConfig != null) {
+ debugConfig.println("cannot read " + url +
+ ", try " + file);
+ }
+ return new FileInputStream(file);
+ }
+ } else {
+ return url.openStream();
+ }
+ }
+
+ private String expand(String value)
+ throws PropertyExpander.ExpandException, IOException {
+
+ if (value.isEmpty()) {
+ return value;
+ }
+
+ if (expandProp) {
+
+ String s = PropertyExpander.expand(value);
+
+ if (s == null || s.length() == 0) {
+ MessageFormat form = new MessageFormat(ResourcesMgr.getString
+ ("Configuration.Error.Line.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;
+ }
}
}