Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/security/provider/ConfigFile.java
38830 views
/*1* Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.provider;2627import java.io.*;28import java.net.MalformedURLException;29import java.net.URI;30import java.net.URL;31import java.security.AccessController;32import java.security.PrivilegedAction;33import java.security.PrivilegedActionException;34import java.security.PrivilegedExceptionAction;35import java.security.Security;36import java.security.URIParameter;37import java.text.MessageFormat;38import java.util.*;39import javax.security.auth.AuthPermission;40import javax.security.auth.login.AppConfigurationEntry;41import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag;42import javax.security.auth.login.Configuration;43import javax.security.auth.login.ConfigurationSpi;44import sun.security.util.Debug;45import sun.security.util.PropertyExpander;46import sun.security.util.ResourcesMgr;4748/**49* This class represents a default implementation for50* {@code javax.security.auth.login.Configuration}.51*52* <p> This object stores the runtime login configuration representation,53* and is the amalgamation of multiple static login configurations that54* resides in files. The algorithm for locating the login configuration55* file(s) and reading their information into this {@code Configuration}56* object is:57*58* <ol>59* <li>60* Loop through the security properties,61* <i>login.config.url.1</i>, <i>login.config.url.2</i>, ...,62* <i>login.config.url.X</i>.63* Each property value specifies a {@code URL} pointing to a64* login configuration file to be loaded. Read in and load65* each configuration.66*67* <li>68* The {@code java.lang.System} property69* <i>java.security.auth.login.config</i>70* may also be set to a {@code URL} pointing to another71* login configuration file72* (which is the case when a user uses the -D switch at runtime).73* If this property is defined, and its use is allowed by the74* security property file (the Security property,75* <i>policy.allowSystemProperty</i> is set to <i>true</i>),76* also load that login configuration.77*78* <li>79* If the <i>java.security.auth.login.config</i> property is defined using80* "==" (rather than "="), then ignore all other specified81* login configurations and only load this configuration.82*83* <li>84* If no system or security properties were set, try to read from the file,85* ${user.home}/.java.login.config, where ${user.home} is the value86* represented by the "user.home" System property.87* </ol>88*89* <p> The configuration syntax supported by this implementation90* is exactly that syntax specified in the91* {@code javax.security.auth.login.Configuration} class.92*93* @see javax.security.auth.login.LoginContext94* @see java.security.Security security properties95*/96public final class ConfigFile extends Configuration {9798private final Spi spi;99100public ConfigFile() {101spi = new Spi();102}103104@Override105public AppConfigurationEntry[] getAppConfigurationEntry(String appName) {106return spi.engineGetAppConfigurationEntry(appName);107}108109@Override110public synchronized void refresh() {111spi.engineRefresh();112}113114public final static class Spi extends ConfigurationSpi {115116private URL url;117private boolean expandProp = true;118private Map<String, List<AppConfigurationEntry>> configuration;119private int linenum;120private StreamTokenizer st;121private int lookahead;122123private static Debug debugConfig = Debug.getInstance("configfile");124private static Debug debugParser = Debug.getInstance("configparser");125126/**127* Creates a new {@code ConfigurationSpi} object.128*129* @throws SecurityException if the {@code ConfigurationSpi} can not be130* initialized131*/132public Spi() {133try {134init();135} catch (IOException ioe) {136throw new SecurityException(ioe);137}138}139140/**141* Creates a new {@code ConfigurationSpi} object from the specified142* {@code URI}.143*144* @param uri the {@code URI}145* @throws SecurityException if the {@code ConfigurationSpi} can not be146* initialized147* @throws NullPointerException if {@code uri} is null148*/149public Spi(URI uri) {150// only load config from the specified URI151try {152url = uri.toURL();153init();154} catch (IOException ioe) {155throw new SecurityException(ioe);156}157}158159public Spi(final Configuration.Parameters params) throws IOException {160161// call in a doPrivileged162//163// we have already passed the Configuration.getInstance164// security check. also this class is not freely accessible165// (it is in the "sun" package).166167try {168AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {169public Void run() throws IOException {170if (params == null) {171init();172} else {173if (!(params instanceof URIParameter)) {174throw new IllegalArgumentException175("Unrecognized parameter: " + params);176}177URIParameter uriParam = (URIParameter)params;178url = uriParam.getURI().toURL();179init();180}181return null;182}183});184} catch (PrivilegedActionException pae) {185throw (IOException)pae.getException();186}187188// if init() throws some other RuntimeException,189// let it percolate up naturally.190}191192/**193* Read and initialize the entire login Configuration from the194* configured URL.195*196* @throws IOException if the Configuration can not be initialized197* @throws SecurityException if the caller does not have permission198* to initialize the Configuration199*/200private void init() throws IOException {201202boolean initialized = false;203204// For policy.expandProperties, check if either a security or system205// property is set to false (old code erroneously checked the system206// prop so we must check both to preserve compatibility).207String expand = Security.getProperty("policy.expandProperties");208if (expand == null) {209expand = System.getProperty("policy.expandProperties");210}211if ("false".equals(expand)) {212expandProp = false;213}214215// new configuration216Map<String, List<AppConfigurationEntry>> newConfig = new HashMap<>();217218if (url != null) {219/**220* If the caller specified a URI via Configuration.getInstance,221* we only read from that URI222*/223if (debugConfig != null) {224debugConfig.println("reading " + url);225}226init(url, newConfig);227configuration = newConfig;228return;229}230231/**232* Caller did not specify URI via Configuration.getInstance.233* Read from URLs listed in the java.security properties file.234*/235String allowSys = Security.getProperty("policy.allowSystemProperty");236237if ("true".equalsIgnoreCase(allowSys)) {238String extra_config = System.getProperty239("java.security.auth.login.config");240if (extra_config != null) {241boolean overrideAll = false;242if (extra_config.startsWith("=")) {243overrideAll = true;244extra_config = extra_config.substring(1);245}246try {247extra_config = PropertyExpander.expand(extra_config);248} catch (PropertyExpander.ExpandException peee) {249throw ioException("Unable.to.properly.expand.config",250extra_config);251}252253URL configURL = null;254try {255configURL = new URL(extra_config);256} catch (MalformedURLException mue) {257File configFile = new File(extra_config);258if (configFile.exists()) {259configURL = configFile.toURI().toURL();260} else {261throw ioException(262"extra.config.No.such.file.or.directory.",263extra_config);264}265}266267if (debugConfig != null) {268debugConfig.println("reading "+configURL);269}270init(configURL, newConfig);271initialized = true;272if (overrideAll) {273if (debugConfig != null) {274debugConfig.println("overriding other policies!");275}276configuration = newConfig;277return;278}279}280}281282int n = 1;283String config_url;284while ((config_url = Security.getProperty285("login.config.url."+n)) != null) {286try {287config_url = PropertyExpander.expand288(config_url).replace(File.separatorChar, '/');289if (debugConfig != null) {290debugConfig.println("\tReading config: " + config_url);291}292init(new URL(config_url), newConfig);293initialized = true;294} catch (PropertyExpander.ExpandException peee) {295throw ioException("Unable.to.properly.expand.config",296config_url);297}298n++;299}300301if (initialized == false && n == 1 && config_url == null) {302303// get the config from the user's home directory304if (debugConfig != null) {305debugConfig.println("\tReading Policy " +306"from ~/.java.login.config");307}308config_url = System.getProperty("user.home");309String userConfigFile = config_url + File.separatorChar +310".java.login.config";311312// No longer throws an exception when there's no config file313// at all. Returns an empty Configuration instead.314if (new File(userConfigFile).exists()) {315init(new File(userConfigFile).toURI().toURL(), newConfig);316}317}318319configuration = newConfig;320}321322private void init(URL config,323Map<String, List<AppConfigurationEntry>> newConfig)324throws IOException {325326try (InputStreamReader isr327= new InputStreamReader(getInputStream(config), "UTF-8")) {328readConfig(isr, newConfig);329} catch (FileNotFoundException fnfe) {330if (debugConfig != null) {331debugConfig.println(fnfe.toString());332}333throw new IOException(ResourcesMgr.getString334("Configuration.Error.No.such.file.or.directory",335"sun.security.util.AuthResources"));336}337}338339/**340* Retrieve an entry from the Configuration using an application name341* as an index.342*343* @param applicationName the name used to index the Configuration.344* @return an array of AppConfigurationEntries which correspond to345* the stacked configuration of LoginModules for this346* application, or null if this application has no configured347* LoginModules.348*/349@Override350public AppConfigurationEntry[] engineGetAppConfigurationEntry351(String applicationName) {352353List<AppConfigurationEntry> list = null;354synchronized (configuration) {355list = configuration.get(applicationName);356}357358if (list == null || list.size() == 0) {359return null;360}361362AppConfigurationEntry[] entries =363new AppConfigurationEntry[list.size()];364Iterator<AppConfigurationEntry> iterator = list.iterator();365for (int i = 0; iterator.hasNext(); i++) {366AppConfigurationEntry e = iterator.next();367entries[i] = new AppConfigurationEntry(e.getLoginModuleName(),368e.getControlFlag(),369e.getOptions());370}371return entries;372}373374/**375* Refresh and reload the Configuration by re-reading all of the376* login configurations.377*378* @throws SecurityException if the caller does not have permission379* to refresh the Configuration.380*/381@Override382public synchronized void engineRefresh() {383384SecurityManager sm = System.getSecurityManager();385if (sm != null) {386sm.checkPermission(387new AuthPermission("refreshLoginConfiguration"));388}389390AccessController.doPrivileged(new PrivilegedAction<Void>() {391public Void run() {392try {393init();394} catch (IOException ioe) {395throw new SecurityException(ioe.getLocalizedMessage(),396ioe);397}398return null;399}400});401}402403private void readConfig(Reader reader,404Map<String, List<AppConfigurationEntry>> newConfig)405throws IOException {406407linenum = 1;408409if (!(reader instanceof BufferedReader)) {410reader = new BufferedReader(reader);411}412413st = new StreamTokenizer(reader);414st.quoteChar('"');415st.wordChars('$', '$');416st.wordChars('_', '_');417st.wordChars('-', '-');418st.wordChars('*', '*');419st.lowerCaseMode(false);420st.slashSlashComments(true);421st.slashStarComments(true);422st.eolIsSignificant(true);423424lookahead = nextToken();425while (lookahead != StreamTokenizer.TT_EOF) {426parseLoginEntry(newConfig);427}428}429430private void parseLoginEntry(431Map<String, List<AppConfigurationEntry>> newConfig)432throws IOException {433434List<AppConfigurationEntry> configEntries = new LinkedList<>();435436// application name437String appName = st.sval;438lookahead = nextToken();439440if (debugParser != null) {441debugParser.println("\tReading next config entry: " + appName);442}443444match("{");445446// get the modules447while (peek("}") == false) {448// get the module class name449String moduleClass = match("module class name");450451// controlFlag (required, optional, etc)452LoginModuleControlFlag controlFlag;453String sflag = match("controlFlag").toUpperCase(Locale.ENGLISH);454switch (sflag) {455case "REQUIRED":456controlFlag = LoginModuleControlFlag.REQUIRED;457break;458case "REQUISITE":459controlFlag = LoginModuleControlFlag.REQUISITE;460break;461case "SUFFICIENT":462controlFlag = LoginModuleControlFlag.SUFFICIENT;463break;464case "OPTIONAL":465controlFlag = LoginModuleControlFlag.OPTIONAL;466break;467default:468throw ioException(469"Configuration.Error.Invalid.control.flag.flag",470sflag);471}472473// get the args474Map<String, String> options = new HashMap<>();475while (peek(";") == false) {476String key = match("option key");477match("=");478try {479options.put(key, expand(match("option value")));480} catch (PropertyExpander.ExpandException peee) {481throw new IOException(peee.getLocalizedMessage());482}483}484485lookahead = nextToken();486487// create the new element488if (debugParser != null) {489debugParser.println("\t\t" + moduleClass + ", " + sflag);490for (String key : options.keySet()) {491debugParser.println("\t\t\t" + key +492"=" + options.get(key));493}494}495configEntries.add(new AppConfigurationEntry(moduleClass,496controlFlag,497options));498}499500match("}");501match(";");502503// add this configuration entry504if (newConfig.containsKey(appName)) {505throw ioException(506"Configuration.Error.Can.not.specify.multiple.entries.for.appName",507appName);508}509newConfig.put(appName, configEntries);510}511512private String match(String expect) throws IOException {513514String value = null;515516switch(lookahead) {517case StreamTokenizer.TT_EOF:518throw ioException(519"Configuration.Error.expected.expect.read.end.of.file.",520expect);521522case '"':523case StreamTokenizer.TT_WORD:524if (expect.equalsIgnoreCase("module class name") ||525expect.equalsIgnoreCase("controlFlag") ||526expect.equalsIgnoreCase("option key") ||527expect.equalsIgnoreCase("option value")) {528value = st.sval;529lookahead = nextToken();530} else {531throw ioException(532"Configuration.Error.Line.line.expected.expect.found.value.",533new Integer(linenum), expect, st.sval);534}535break;536537case '{':538if (expect.equalsIgnoreCase("{")) {539lookahead = nextToken();540} else {541throw ioException(542"Configuration.Error.Line.line.expected.expect.",543new Integer(linenum), expect, st.sval);544}545break;546547case ';':548if (expect.equalsIgnoreCase(";")) {549lookahead = nextToken();550} else {551throw ioException(552"Configuration.Error.Line.line.expected.expect.",553new Integer(linenum), expect, st.sval);554}555break;556557case '}':558if (expect.equalsIgnoreCase("}")) {559lookahead = nextToken();560} else {561throw ioException(562"Configuration.Error.Line.line.expected.expect.",563new Integer(linenum), expect, st.sval);564}565break;566567case '=':568if (expect.equalsIgnoreCase("=")) {569lookahead = nextToken();570} else {571throw ioException(572"Configuration.Error.Line.line.expected.expect.",573new Integer(linenum), expect, st.sval);574}575break;576577default:578throw ioException(579"Configuration.Error.Line.line.expected.expect.found.value.",580new Integer(linenum), expect, st.sval);581}582return value;583}584585private boolean peek(String expect) {586switch (lookahead) {587case ',':588return expect.equalsIgnoreCase(",");589case ';':590return expect.equalsIgnoreCase(";");591case '{':592return expect.equalsIgnoreCase("{");593case '}':594return expect.equalsIgnoreCase("}");595default:596return false;597}598}599600private int nextToken() throws IOException {601int tok;602while ((tok = st.nextToken()) == StreamTokenizer.TT_EOL) {603linenum++;604}605return tok;606}607608private InputStream getInputStream(URL url) throws IOException {609if ("file".equalsIgnoreCase(url.getProtocol())) {610// Compatibility notes:611//612// Code changed from613// String path = url.getFile().replace('/', File.separatorChar);614// return new FileInputStream(path);615//616// The original implementation would search for "/tmp/a%20b"617// when url is "file:///tmp/a%20b". This is incorrect. The618// current codes fix this bug and searches for "/tmp/a b".619// For compatibility reasons, when the file "/tmp/a b" does620// not exist, the file named "/tmp/a%20b" will be tried.621//622// This also means that if both file exists, the behavior of623// this method is changed, and the current codes choose the624// correct one.625try {626return url.openStream();627} catch (Exception e) {628String file = url.getPath();629if (url.getHost().length() > 0) { // For Windows UNC630file = "//" + url.getHost() + file;631}632if (debugConfig != null) {633debugConfig.println("cannot read " + url +634", try " + file);635}636return new FileInputStream(file);637}638} else {639return url.openStream();640}641}642643private String expand(String value)644throws PropertyExpander.ExpandException, IOException {645646if (value.isEmpty()) {647return value;648}649650if (!expandProp) {651return value;652}653String s = PropertyExpander.expand(value);654if (s == null || s.length() == 0) {655throw ioException(656"Configuration.Error.Line.line.system.property.value.expanded.to.empty.value",657new Integer(linenum), value);658}659return s;660}661662private IOException ioException(String resourceKey, Object... args) {663MessageFormat form = new MessageFormat(ResourcesMgr.getString664(resourceKey, "sun.security.util.AuthResources"));665return new IOException(form.format(args));666}667}668}669670671