1. /*
  2. * @(#)ConfigFile.java 1.17 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package com.sun.security.auth.login;
  8. import javax.security.auth.AuthPermission;
  9. import javax.security.auth.login.AppConfigurationEntry;
  10. import java.io.*;
  11. import java.util.*;
  12. import java.net.URL;
  13. import java.text.MessageFormat;
  14. import sun.security.util.ResourcesMgr;
  15. import sun.security.util.PropertyExpander;
  16. /**
  17. * This class represents a default implementation for
  18. * <code>javax.security.auth.login.Configuration</code>.
  19. *
  20. * <p> This object stores the runtime login configuration representation,
  21. * and is the amalgamation of multiple static login
  22. * configurations that resides in files.
  23. * The algorithm for locating the login configuration file(s) and reading their
  24. * information into this <code>Configuration</code> object is:
  25. *
  26. * <ol>
  27. * <li>
  28. * Loop through the <code>java.security.Security</code> properties,
  29. * <i>login.config.url.1</i>, <i>login.config.url.2</i>, ...,
  30. * <i>login.config.url.X</i>. These properties are set
  31. * in the Java security properties file, which is located in the file named
  32. * <JAVA_HOME>/lib/security/java.security, where <JAVA_HOME>
  33. * refers to the directory where the JDK was installed.
  34. * Each property value specifies a <code>URL</code> pointing to a
  35. * login configuration file to be loaded. Read in and load
  36. * each configuration.
  37. *
  38. * <li>
  39. * The <code>java.lang.System</code> property
  40. * <i>java.security.auth.login.config</i>
  41. * may also be set to a <code>URL</code> pointing to another
  42. * login configuration file
  43. * (which is the case when a user uses the -D switch at runtime).
  44. * If this property is defined, and its use is allowed by the
  45. * security property file (the Security property,
  46. * <i>policy.allowSystemProperty</i> is set to <i>true</i>),
  47. * also load that login configuration.
  48. *
  49. * <li>
  50. * If the <i>java.security.auth.login.config</i> property is defined using
  51. * "==" (rather than "="), then ignore all other specified
  52. * login configurations and only load this configuration.
  53. *
  54. * <li>
  55. * If no system or security properties were set, try to read from the file,
  56. * ${user.home}/.java.login.config, where ${user.home} is the value
  57. * represented by the "user.home" System property.
  58. * </ol>
  59. *
  60. * <p> The configuration syntax supported by this implementation
  61. * is exactly that syntax specified in the
  62. * <code>javax.security.auth.login.Configuration</code> class.
  63. *
  64. * @version 1.6, 01/14/00
  65. * @see javax.security.auth.login.LoginContext
  66. */
  67. public class ConfigFile extends javax.security.auth.login.Configuration {
  68. private StreamTokenizer st;
  69. private int lookahead;
  70. private int linenum;
  71. private HashMap configuration;
  72. private boolean expandProp = true;
  73. private boolean testing = false;
  74. /**
  75. * Create a new <code>Configuration</code> object.
  76. */
  77. public ConfigFile() {
  78. String expandProperties = (String)
  79. java.security.AccessController.doPrivileged
  80. (new java.security.PrivilegedAction() {
  81. public Object run() {
  82. return System.getProperty("policy.expandProperties");
  83. }
  84. });
  85. if ("false".equals(expandProperties))
  86. expandProp = false;
  87. try {
  88. init();
  89. } catch (IOException ioe) {
  90. throw (SecurityException)
  91. new SecurityException(ioe.getMessage()).initCause(ioe);
  92. }
  93. }
  94. /**
  95. * Read and initialize the entire login Configuration.
  96. *
  97. * <p>
  98. *
  99. * @exception IOException if the Configuration can not be initialized. <p>
  100. * @exception SecurityException if the caller does not have permission
  101. * to initialize the Configuration.
  102. */
  103. private void init() throws IOException {
  104. boolean initialized = false;
  105. FileReader fr = null;
  106. String sep = File.separator;
  107. // new configuration
  108. HashMap newConfig = new HashMap();
  109. String allowSys = java.security.Security.getProperty
  110. ("policy.allowSystemProperty");
  111. if ("true".equalsIgnoreCase(allowSys)) {
  112. String extra_config = System.getProperty
  113. ("java.security.auth.login.config");
  114. if (extra_config != null) {
  115. boolean overrideAll = false;
  116. if (extra_config.startsWith("=")) {
  117. overrideAll = true;
  118. extra_config = extra_config.substring(1);
  119. }
  120. try {
  121. extra_config = PropertyExpander.expand(extra_config);
  122. } catch (PropertyExpander.ExpandException peee) {
  123. MessageFormat form = new MessageFormat
  124. (ResourcesMgr.getString
  125. ("Unable to properly expand config",
  126. "sun.security.util.AuthResources"));
  127. Object[] source = {extra_config};
  128. throw new IOException(form.format(source));
  129. }
  130. URL configURL = null;
  131. try {
  132. configURL = new URL(extra_config);
  133. } catch (java.net.MalformedURLException mue) {
  134. File configFile = new File(extra_config);
  135. if (configFile.exists()) {
  136. configURL = new URL("file:" +
  137. configFile.getCanonicalPath());
  138. } else {
  139. MessageFormat form = new MessageFormat
  140. (ResourcesMgr.getString
  141. ("extra_config (No such file or directory)",
  142. "sun.security.util.AuthResources"));
  143. Object[] source = {extra_config};
  144. throw new IOException(form.format(source));
  145. }
  146. }
  147. if (testing)
  148. System.out.println("reading "+configURL);
  149. init(configURL, newConfig);
  150. initialized = true;
  151. if (overrideAll) {
  152. if (testing)
  153. System.out.println("overriding other policies!");
  154. }
  155. }
  156. }
  157. int n = 1;
  158. String config_url;
  159. while ((config_url = java.security.Security.getProperty
  160. ("login.config.url."+n)) != null) {
  161. try {
  162. config_url = PropertyExpander.expand
  163. (config_url).replace(File.separatorChar, '/');
  164. if (testing)
  165. System.out.println("\tReading config: " + config_url);
  166. init(new URL(config_url), newConfig);
  167. initialized = true;
  168. } catch (PropertyExpander.ExpandException peee) {
  169. MessageFormat form = new MessageFormat
  170. (ResourcesMgr.getString
  171. ("Unable to properly expand config",
  172. "sun.security.util.AuthResources"));
  173. Object[] source = {config_url};
  174. throw new IOException(form.format(source));
  175. }
  176. n++;
  177. }
  178. if (initialized == false && n == 1 && config_url == null) {
  179. // get the config from the user's home directory
  180. if (testing)
  181. System.out.println("\tReading Policy " +
  182. "from ~/.java.login.config");
  183. config_url = System.getProperty("user.home");
  184. try {
  185. init(new URL("file:" + config_url +
  186. File.separatorChar + ".java.login.config"),
  187. newConfig);
  188. } catch (IOException ioe) {
  189. throw new IOException(ResourcesMgr.getString
  190. ("Unable to locate a login configuration",
  191. "sun.security.util.AuthResources"));
  192. }
  193. }
  194. configuration = newConfig;
  195. }
  196. private void init(URL config, HashMap newConfig) throws IOException {
  197. InputStreamReader isr
  198. = new InputStreamReader(getInputStream(config), "UTF-8");
  199. readConfig(isr, newConfig);
  200. isr.close();
  201. }
  202. /**
  203. * Retrieve an entry from the Configuration using an application name
  204. * as an index.
  205. *
  206. * <p>
  207. *
  208. * @param applicationName the name used to index the Configuration.
  209. * @return an array of AppConfigurationEntries which correspond to
  210. * the stacked configuration of LoginModules for this
  211. * application, or null if this application has no configured
  212. * LoginModules.
  213. */
  214. public AppConfigurationEntry[] getAppConfigurationEntry
  215. (String applicationName) {
  216. LinkedList list = null;
  217. synchronized (configuration) {
  218. list = (LinkedList)configuration.get(applicationName);
  219. }
  220. if (list == null || list.size() == 0)
  221. return null;
  222. AppConfigurationEntry[] entries =
  223. new AppConfigurationEntry[list.size()];
  224. Iterator iterator = list.iterator();
  225. for (int i = 0; iterator.hasNext(); i++) {
  226. AppConfigurationEntry e = (AppConfigurationEntry)iterator.next();
  227. entries[i] = new AppConfigurationEntry(e.getLoginModuleName(),
  228. e.getControlFlag(),
  229. e.getOptions());
  230. }
  231. return entries;
  232. }
  233. /**
  234. * Refresh and reload the Configuration by re-reading all of the
  235. * login configurations.
  236. *
  237. * <p>
  238. *
  239. * @exception SecurityException if the caller does not have permission
  240. * to refresh the Configuration.
  241. */
  242. public synchronized void refresh() {
  243. java.lang.SecurityManager sm = System.getSecurityManager();
  244. if (sm != null)
  245. sm.checkPermission(new AuthPermission("refreshLoginConfiguration"));
  246. java.security.AccessController.doPrivileged
  247. (new java.security.PrivilegedAction() {
  248. public Object run() {
  249. try {
  250. init();
  251. } catch (java.io.IOException ioe) {
  252. throw new SecurityException(ioe.getLocalizedMessage());
  253. }
  254. return null;
  255. }
  256. });
  257. }
  258. private void readConfig(Reader reader, HashMap newConfig)
  259. throws IOException {
  260. int linenum = 1;
  261. if (!(reader instanceof BufferedReader))
  262. reader = new BufferedReader(reader);
  263. st = new StreamTokenizer(reader);
  264. st.quoteChar('"');
  265. st.wordChars('$', '$');
  266. st.wordChars('_', '_');
  267. st.wordChars('-', '-');
  268. st.lowerCaseMode(false);
  269. st.slashSlashComments(true);
  270. st.slashStarComments(true);
  271. st.eolIsSignificant(true);
  272. lookahead = nextToken();
  273. while (lookahead != StreamTokenizer.TT_EOF) {
  274. if (testing)
  275. System.out.print("\tReading next config entry: ");
  276. parseLoginEntry(newConfig);
  277. }
  278. }
  279. private void parseLoginEntry(HashMap newConfig) throws IOException {
  280. String appName;
  281. String moduleClass;
  282. String sflag;
  283. AppConfigurationEntry.LoginModuleControlFlag controlFlag;
  284. LinkedList configEntries = new LinkedList();
  285. // application name
  286. appName = st.sval;
  287. lookahead = nextToken();
  288. if (testing)
  289. System.out.println("appName = " + appName);
  290. match("{");
  291. // get the modules
  292. while (peek("}") == false) {
  293. HashSet argSet = new HashSet();
  294. // get the module class name
  295. moduleClass = match("module class name");
  296. // controlFlag (required, optional, etc)
  297. sflag = match("controlFlag");
  298. if (sflag.equalsIgnoreCase("REQUIRED"))
  299. controlFlag =
  300. AppConfigurationEntry.LoginModuleControlFlag.REQUIRED;
  301. else if (sflag.equalsIgnoreCase("REQUISITE"))
  302. controlFlag =
  303. AppConfigurationEntry.LoginModuleControlFlag.REQUISITE;
  304. else if (sflag.equalsIgnoreCase("SUFFICIENT"))
  305. controlFlag =
  306. AppConfigurationEntry.LoginModuleControlFlag.SUFFICIENT;
  307. else if (sflag.equalsIgnoreCase("OPTIONAL"))
  308. controlFlag =
  309. AppConfigurationEntry.LoginModuleControlFlag.OPTIONAL;
  310. else {
  311. MessageFormat form = new MessageFormat(ResourcesMgr.getString
  312. ("Configuration Error:\n\tInvalid control flag, flag",
  313. "sun.security.util.AuthResources"));
  314. Object[] source = {sflag};
  315. throw new IOException(form.format(source));
  316. }
  317. // get the args
  318. HashMap options = new HashMap();
  319. String key;
  320. String value;
  321. while (peek(";") == false) {
  322. key = match("option key");
  323. match("=");
  324. try {
  325. value = expand(match("option value"));
  326. } catch (PropertyExpander.ExpandException peee) {
  327. throw new IOException(peee.getLocalizedMessage());
  328. }
  329. options.put(key, value);
  330. }
  331. lookahead = nextToken();
  332. // create the new element
  333. if (testing) {
  334. System.out.print("\t\t" + moduleClass + ", " + sflag);
  335. java.util.Iterator i = options.keySet().iterator();
  336. while (i.hasNext()) {
  337. key = (String)i.next();
  338. System.out.print(", " +
  339. key +
  340. "=" +
  341. (String)options.get(key));
  342. }
  343. System.out.println("");
  344. }
  345. AppConfigurationEntry entry = new AppConfigurationEntry
  346. (moduleClass,
  347. controlFlag,
  348. options);
  349. configEntries.add(entry);
  350. }
  351. match("}");
  352. match(";");
  353. // add this configuration entry
  354. if (newConfig.containsKey(appName)) {
  355. MessageFormat form = new MessageFormat(ResourcesMgr.getString
  356. ("Configuration Error:\n\t" +
  357. "Can not specify multiple entries for appName",
  358. "sun.security.util.AuthResources"));
  359. Object[] source = {appName};
  360. throw new IOException(form.format(source));
  361. }
  362. newConfig.put(appName, configEntries);
  363. if (testing)
  364. System.out.println("\t\t***Added entry for " +
  365. appName + " to overall configuration***");
  366. }
  367. private String match(String expect) throws IOException {
  368. String value = null;
  369. switch(lookahead) {
  370. case StreamTokenizer.TT_EOF:
  371. MessageFormat form1 = new MessageFormat(ResourcesMgr.getString
  372. ("Configuration Error:\n\texpected [expect], " +
  373. "read [end of file]",
  374. "sun.security.util.AuthResources"));
  375. Object[] source1 = {expect};
  376. throw new IOException(form1.format(source1));
  377. case '"':
  378. case StreamTokenizer.TT_WORD:
  379. if (expect.equalsIgnoreCase("module class name") ||
  380. expect.equalsIgnoreCase("controlFlag") ||
  381. expect.equalsIgnoreCase("option key") ||
  382. expect.equalsIgnoreCase("option value")) {
  383. value = st.sval;
  384. lookahead = nextToken();
  385. } else {
  386. MessageFormat form = new MessageFormat(ResourcesMgr.getString
  387. ("Configuration Error:\n\tLine line: " +
  388. "expected [expect], found [value]",
  389. "sun.security.util.AuthResources"));
  390. Object[] source = {new Integer(linenum), expect, st.sval};
  391. throw new IOException(form.format(source));
  392. }
  393. break;
  394. case '{':
  395. if (expect.equalsIgnoreCase("{")) {
  396. lookahead = nextToken();
  397. } else {
  398. MessageFormat form = new MessageFormat(ResourcesMgr.getString
  399. ("Configuration Error:\n\tLine line: expected [expect]",
  400. "sun.security.util.AuthResources"));
  401. Object[] source = {new Integer(linenum), expect, st.sval};
  402. throw new IOException(form.format(source));
  403. }
  404. break;
  405. case ';':
  406. if (expect.equalsIgnoreCase(";")) {
  407. lookahead = nextToken();
  408. } else {
  409. MessageFormat form = new MessageFormat(ResourcesMgr.getString
  410. ("Configuration Error:\n\tLine line: expected [expect]",
  411. "sun.security.util.AuthResources"));
  412. Object[] source = {new Integer(linenum), expect, st.sval};
  413. throw new IOException(form.format(source));
  414. }
  415. break;
  416. case '}':
  417. if (expect.equalsIgnoreCase("}")) {
  418. lookahead = nextToken();
  419. } else {
  420. MessageFormat form = new MessageFormat(ResourcesMgr.getString
  421. ("Configuration Error:\n\tLine line: expected [expect]",
  422. "sun.security.util.AuthResources"));
  423. Object[] source = {new Integer(linenum), expect, st.sval};
  424. throw new IOException(form.format(source));
  425. }
  426. break;
  427. case '=':
  428. if (expect.equalsIgnoreCase("=")) {
  429. lookahead = nextToken();
  430. } else {
  431. MessageFormat form = new MessageFormat(ResourcesMgr.getString
  432. ("Configuration Error:\n\tLine line: expected [expect]",
  433. "sun.security.util.AuthResources"));
  434. Object[] source = {new Integer(linenum), expect, st.sval};
  435. throw new IOException(form.format(source));
  436. }
  437. break;
  438. default:
  439. MessageFormat form = new MessageFormat(ResourcesMgr.getString
  440. ("Configuration Error:\n\tLine line: " +
  441. "expected [expect], found [value]",
  442. "sun.security.util.AuthResources"));
  443. Object[] source = {new Integer(linenum), expect, st.sval};
  444. throw new IOException(form.format(source));
  445. }
  446. return value;
  447. }
  448. private boolean peek(String expect) {
  449. boolean found = false;
  450. switch (lookahead) {
  451. case ',':
  452. if (expect.equalsIgnoreCase(","))
  453. found = true;
  454. break;
  455. case ';':
  456. if (expect.equalsIgnoreCase(";"))
  457. found = true;
  458. break;
  459. case '{':
  460. if (expect.equalsIgnoreCase("{"))
  461. found = true;
  462. break;
  463. case '}':
  464. if (expect.equalsIgnoreCase("}"))
  465. found = true;
  466. break;
  467. default:
  468. }
  469. return found;
  470. }
  471. private int nextToken() throws IOException {
  472. int tok;
  473. while ((tok = st.nextToken()) == StreamTokenizer.TT_EOL) {
  474. linenum++;
  475. }
  476. return tok;
  477. }
  478. /*
  479. * Fast path reading from file urls in order to avoid calling
  480. * FileURLConnection.connect() which can be quite slow the first time
  481. * it is called. We really should clean up FileURLConnection so that
  482. * this is not a problem but in the meantime this fix helps reduce
  483. * start up time noticeably for the new launcher. -- DAC
  484. */
  485. private InputStream getInputStream(URL url) throws IOException {
  486. if ("file".equals(url.getProtocol())) {
  487. String path = url.getFile().replace('/', File.separatorChar);
  488. return new FileInputStream(path);
  489. } else {
  490. return url.openStream();
  491. }
  492. }
  493. private String expand(String value)
  494. throws PropertyExpander.ExpandException, IOException {
  495. if ("".equals(value)) {
  496. return value;
  497. }
  498. if (expandProp) {
  499. String s = PropertyExpander.expand(value);
  500. if (s == null || s.length() == 0) {
  501. MessageFormat form = new MessageFormat(ResourcesMgr.getString
  502. ("Configuration Error:\n\tLine line: " +
  503. "system property [value] expanded to empty value",
  504. "sun.security.util.AuthResources"));
  505. Object[] source = {new Integer(linenum), value};
  506. throw new IOException(form.format(source));
  507. }
  508. return s;
  509. } else {
  510. return value;
  511. }
  512. }
  513. }