OpenJDK / jdk / jdk
changeset 14923:189882d66a51
8005117: Eliminate dependency from ConfigSpiFile to com.sun.security.auth.login.ConfigFile
Reviewed-by: alanb, mchung, weijun
author | mullan |
---|---|
date | Wed, 26 Dec 2012 10:07:00 -0500 |
parents | e909ef77aa59 |
children | 43383e2d85a6 |
files | jdk/src/share/classes/com/sun/security/auth/login/ConfigFile.java jdk/src/share/classes/sun/security/provider/ConfigSpiFile.java |
diffstat | 2 files changed, 671 insertions(+), 616 deletions(-) [+] |
line wrap: on
line diff
--- 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); - } + configFile = new sun.security.provider.ConfigSpiFile(); } /** - * Create a new <code>Configuration</code> object from the specified URI. + * Create a new {@code Configuration} object from the specified {@code URI}. * - * @param uri Create a new Configuration object from this URI. + * @param uri the {@code URI} + * @throws SecurityException if the {@code Configuration} can not be + * initialized + * @throws NullPointerException if {@code uri} is null */ 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(uri); } /** - * Read and initialize the entire login Configuration. + * Retrieve an entry from the {@code Configuration} using an application + * name as an index. * - * <p> - * - * @exception IOException if the Configuration can not be initialized. <p> - * @exception SecurityException if the caller does not have permission - * to initialize 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. */ - private void init(URL url) throws IOException { + @Override + public AppConfigurationEntry[] getAppConfigurationEntry + (String applicationName) { - 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(); - } - } + return configFile.engineGetAppConfigurationEntry(applicationName); } /** - * 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; - } - - /** - * Refresh and reload the Configuration by re-reading all of the + * Refresh and reload the {@code Configuration} by re-reading all of the * login configurations. * - * <p> - * - * @exception SecurityException if the caller does not have permission - * to refresh the Configuration. + * @throws SecurityException if the caller does not have permission + * to refresh the {@code Configuration} */ + @Override 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("{"); - - // 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); - } - - 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 - */ - 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; - } + 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) { + }); + } - // if ConfigFile threw a standalone SecurityException - // (no cause), re-throw it. - // - // ConfigFile chains checked IOExceptions to SecurityException. + private void readConfig(Reader reader, + Map<String, List<AppConfigurationEntry>> newConfig) + throws IOException { - Throwable cause = se.getCause(); - if (cause != null && cause instanceof java.io.IOException) { - throw (java.io.IOException)cause; + 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("{"); + + // get the modules + while (peek("}") == false) { + // get the module class name + String moduleClass = match("module class name"); + + // 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)); } - // unrecognized cause - throw se; + // 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()); + } + } + + 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; + } } }