Frames | No Frames |
1: /* GnuConfiguration.java -- GNU Classpath implementation of JAAS Configuration 2: Copyright (C) 2006, 2010 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: 39: package gnu.javax.security.auth.login; 40: 41: import java.io.File; 42: import java.io.FileInputStream; 43: import java.io.IOException; 44: import java.io.InputStream; 45: import java.io.InputStreamReader; 46: import java.net.MalformedURLException; 47: import java.net.URL; 48: import java.security.Security; 49: import java.util.HashMap; 50: import java.util.Iterator; 51: import java.util.List; 52: import java.util.Map; 53: import java.util.logging.Logger; 54: 55: import javax.security.auth.AuthPermission; 56: import javax.security.auth.login.AppConfigurationEntry; 57: import javax.security.auth.login.Configuration; 58: 59: /** 60: * An implementation of the {@link Configuration} class which interprets JAAS 61: * Login Configuration files written in the <i>default</i> syntax described in 62: * the publicly available documentation of that class. A more formal definition 63: * of this syntax is as follows: 64: * 65: * <pre> 66: * CONFIG ::= APP_OR_OTHER_ENTRY+ 67: * APP_OR_OTHER_ENTRY ::= APP_NAME_OR_OTHER JAAS_CONFIG_BLOCK 68: * APP_NAME_OR_OTHER ::= APP_NAME 69: * | 'other' 70: * JAAS_CONFIG_BLOCK ::= '{' (LOGIN_MODULE_ENTRY ';')+ '}' ';' 71: * LOGIN_MODULE_ENTRY ::= MODULE_CLASS FLAG MODULE_OPTION* ';' 72: * FLAG ::= 'required' 73: * | 'requisite' 74: * | 'sufficient' 75: * | 'optional' 76: * MODULE_OPTION ::= PARAM_NAME '=' PARAM_VALUE 77: * 78: * APP_NAME ::= JAVA_IDENTIFIER 79: * MODULE_CLASS ::= JAVA_IDENTIFIER ('.' JAVA_IDENTIFIER)* 80: * PARAM_NAME ::= STRING 81: * PARAM_VALUE ::= '"' STRING '"' | ''' STRING ''' | STRING 82: * </pre> 83: * 84: * <p>This implementation will specifically attempt to process one or more 85: * Login Configuration files in the following locations, and when found parse 86: * them and merge their contents. The locations, and the order in which they are 87: * investigated, follows:</p> 88: * 89: * <ol> 90: * <li>If the following Security properties: 91: * <i>java.security.auth.login.config.url.<b>N</b></i>, where <i><b>N</b></i> 92: * is a digit, from <code>1</code> to an arbitrary number, are defined, then 93: * the value of each of those properties will be considered as a JAAS Login 94: * Configuration file written in the default syntax. This implementation will 95: * attempt parsing all such files. 96: * 97: * <p>It is worth noting the following: 98: * <ul> 99: * <li>The GNU Classpath security file, named <i>classpath.security</i>, 100: * where all Security properties are encoded, is usually located in 101: * <i>/usr/local/classpath/lib/security</i> folder.</li> 102: * 103: * <li>The numbers used in the properties 104: * <i>java.security.auth.login.config.url.<b>N</b></i> MUST be sequential, 105: * with no breaks in-between.</li> 106: * </ul> 107: * </p> 108: * 109: * <p>If at least one of the designated Configuration files was found, and 110: * was parsed correctly, then no other location will be inspected.</p></li> 111: * 112: * <li>If the System property named <i>java.security.auth.login.config</i> 113: * is not null or empty, its contents are then interpreted as a URL to a 114: * JAAS Login Configuration file written in the default syntax. 115: * 116: * <p>If this System property is defined, and the file it refers to was 117: * parsed correctly, then no other location will be inspected.</p></li> 118: * 119: * <li>If a file named <i>.java.login.config</i> or <i>java.login.config</i> 120: * (in that order) is found in the location referenced by the value of the 121: * System property <i>user.home</i>, then that file is parsed as a JAAS Login 122: * Configuration written in the default syntax.</li> 123: * 124: * <li>If none of the above resulted in a correctly parsed JAAS Login 125: * Configuration file, then this implementation will install a <i>Null 126: * Configuration</i> which basically does not recognize any Application.</li> 127: * </ol> 128: */ 129: public final class GnuConfiguration extends Configuration 130: { 131: private static final Logger log = gnu.java.security.Configuration.DEBUG ? 132: Logger.getLogger(GnuConfiguration.class.getName()) : null; 133: 134: /** 135: * The internal map of login modules keyed by application name. Each entry in 136: * this map is a {@link List} of {@link AppConfigurationEntry}s for that 137: * application name. 138: */ 139: private Map loginModulesMap; 140: /** Our reference to our default syntax parser. */ 141: private ConfigFileParser cp; 142: 143: // Constructor(s) 144: // -------------------------------------------------------------------------- 145: 146: /** Trivial 0-arguments Constructor. */ 147: public GnuConfiguration() 148: { 149: super(); 150: 151: loginModulesMap = new HashMap(); 152: cp = new ConfigFileParser(); 153: init(); 154: } 155: 156: // Class methods 157: // -------------------------------------------------------------------------- 158: 159: // Instance methods 160: // -------------------------------------------------------------------------- 161: 162: // Configuration abstract methods implementation ---------------------------- 163: 164: /* (non-Javadoc) 165: * @see javax.security.auth.login.Configuration#getAppConfigurationEntry(java.lang.String) 166: */ 167: public AppConfigurationEntry[] getAppConfigurationEntry(String appName) 168: { 169: if (appName == null) 170: return null; 171: 172: appName = appName.trim(); 173: if (appName.length() == 0) 174: return null; 175: 176: List loginModules = (List) loginModulesMap.get(appName); 177: if (loginModules == null || loginModules.size() == 0) 178: return null; 179: 180: if (gnu.java.security.Configuration.DEBUG) 181: log.fine(appName + " -> " + loginModules.size() + " entry(ies)"); 182: return (AppConfigurationEntry[]) loginModules.toArray(new AppConfigurationEntry[0]); 183: } 184: 185: /** 186: * Refreshes and reloads this <code>Configuration</code>. 187: * 188: * <p>This method causes this <code>Configuration</code> object to refresh / 189: * reload its contents following the locations and logic described above in 190: * the class documentation section.</p> 191: * 192: * @throws SecurityException if the caller does not have an 193: * {@link AuthPermission} for the action named 194: * <code>refreshLoginConfiguration</code>. 195: * @see AuthPermission 196: */ 197: public void refresh() 198: { 199: SecurityManager sm = System.getSecurityManager(); 200: if (sm != null) 201: sm.checkPermission(new AuthPermission("refreshLoginConfiguration")); 202: 203: loginModulesMap.clear(); 204: init(); 205: } 206: 207: // helper methods ----------------------------------------------------------- 208: 209: /** 210: * Attempts to find and parse JAAS Login Configuration file(s) written in 211: * the default syntax. The locations searched are as descibed in the class 212: * documentation. 213: */ 214: private void init() 215: { 216: if (processSecurityProperties()) 217: { 218: if (gnu.java.security.Configuration.DEBUG) 219: log.fine("Using login configuration defined by Security property(ies)"); 220: } 221: else if (processSystemProperty()) 222: { 223: if (gnu.java.security.Configuration.DEBUG) 224: log.fine("Using login configuration defined by System property"); 225: } 226: else if (processUserHome()) 227: { 228: if (gnu.java.security.Configuration.DEBUG) 229: log.fine("Using login configuration defined in ${user.home}"); 230: } 231: else 232: { 233: if (gnu.java.security.Configuration.DEBUG) 234: log.fine("No login configuration file found"); 235: } 236: } 237: 238: /** 239: * Attempts to locate and parse one or more JAAS Login Configuration files 240: * defined as the values of the Security properties 241: * <i>java.security.auth.login.config.url.N</i>. 242: * 243: * @return <code>true</code> if it succeeds, and <code>false</code> 244: * otherwsie. 245: */ 246: private boolean processSecurityProperties() 247: { 248: boolean result = false; 249: int counter = 0; 250: String s; 251: while (true) 252: try 253: { 254: counter++; 255: s = Security.getProperty("java.security.auth.login.config.url." 256: + counter); 257: if (s == null) 258: break; 259: 260: s = s.trim(); 261: if (s.length() != 0) 262: { 263: if (gnu.java.security.Configuration.DEBUG) 264: log.fine("java.security.auth.login.config.url." + counter 265: + " = " + s); 266: parseConfig(getInputStreamFromURL(s)); 267: result = true; 268: } 269: } 270: catch (Throwable t) 271: { 272: if (gnu.java.security.Configuration.DEBUG) 273: log.fine("Exception while handling Security property at #" 274: + counter + ". Continue: " + t); 275: } 276: return result; 277: } 278: 279: /** 280: * Attempts to open a designated string as a well-formed {@link URL}. If a 281: * {@link MalformedURLException} occurs, this method then tries to open that 282: * string as a {@link File} (with the same name). If it succeeds, an 283: * {@link InputStream} is constructed and returned. 284: * 285: * @param s 286: * the designated name of either a {@link URL} or a {@link File} 287: * assumed to contain a JAAS Login Configuration in the default 288: * syntax. 289: * @return an {@link InputStream} around the data source. 290: * @throws IOException 291: * if an exception occurs during the operation. 292: */ 293: private InputStream getInputStreamFromURL(String s) throws IOException 294: { 295: InputStream result = null; 296: try 297: { 298: URL url = new URL(s); 299: result = url.openStream(); 300: } 301: catch (MalformedURLException x) 302: { 303: if (gnu.java.security.Configuration.DEBUG) 304: log.fine("Failed opening as URL: " + s + ". Will try as File"); 305: result = new FileInputStream(s); 306: } 307: return result; 308: } 309: 310: /** 311: * Attempts to locate and parse a JAAS Login Configuration file defined as the 312: * value of the System property <i>java.security.auth.login.config</i>. 313: * 314: * @return <code>true</code> if it succeeds, and <code>false</code> 315: * otherwsie. 316: */ 317: private boolean processSystemProperty() 318: { 319: boolean result = false; 320: try 321: { 322: String s = System.getProperty("java.security.auth.login.config"); 323: if (s != null) 324: { 325: s = s.trim(); 326: if (s.length() != 0) 327: { 328: if (gnu.java.security.Configuration.DEBUG) 329: log.fine("java.security.auth.login.config = " + s); 330: parseConfig(getInputStreamFromURL(s)); 331: result = true; 332: } 333: } 334: } 335: catch (Throwable t) 336: { 337: if (gnu.java.security.Configuration.DEBUG) 338: log.fine("Exception while handling System property. Continue: " + t); 339: } 340: return result; 341: } 342: 343: /** 344: * Attempts to locate and parse a JAAS Login Configuration file named either 345: * as <i>.java.login.config</i> or <i>java.login.config</i> (without the 346: * leading dot) in the folder referenced by the System property 347: * <code>user.home</code>. 348: * 349: * @return <code>true</code> if it succeeds, and <code>false</code> 350: * otherwsie. 351: */ 352: private boolean processUserHome() 353: { 354: boolean result = false; 355: try 356: { 357: File userHome = getUserHome(); 358: if (userHome == null) 359: return result; 360: 361: File jaasFile; 362: jaasFile = getConfigFromUserHome(userHome, ".java.login.config"); 363: if (jaasFile == null) 364: jaasFile = getConfigFromUserHome(userHome, "java.login.config"); 365: 366: if (jaasFile == null) 367: { 368: if (gnu.java.security.Configuration.DEBUG) 369: log.fine("Login Configuration file, in " + userHome 370: + ", does not exist or is inaccessible"); 371: return result; 372: } 373: 374: FileInputStream fis = new FileInputStream(jaasFile); 375: parseConfig(fis); 376: result = true; 377: } 378: catch (Throwable t) 379: { 380: if (gnu.java.security.Configuration.DEBUG) 381: log.fine("Exception (ignored) while handling ${user.home}: " + t); 382: } 383: return result; 384: } 385: 386: private void parseConfig(InputStream configStream) throws IOException 387: { 388: cp.parse(new InputStreamReader(configStream, "UTF-8")); 389: Map loginModulesMap = cp.getLoginModulesMap(); 390: mergeLoginModules(loginModulesMap); 391: } 392: 393: private void mergeLoginModules(Map otherLoginModules) 394: { 395: if (otherLoginModules == null || otherLoginModules.size() < 1) 396: return; 397: 398: for (Iterator it = otherLoginModules.keySet().iterator(); it.hasNext();) 399: { 400: String appName = (String) it.next(); 401: List thatListOfACEs = (List) otherLoginModules.get(appName); 402: if (thatListOfACEs == null || thatListOfACEs.size() < 1) 403: continue; 404: 405: List thisListsOfACEs = (List) loginModulesMap.get(appName); 406: if (thisListsOfACEs == null) 407: loginModulesMap.put(appName, thatListOfACEs); 408: else 409: thisListsOfACEs.addAll(thatListOfACEs); 410: } 411: } 412: 413: private File getUserHome() 414: { 415: String uh = System.getProperty("user.home"); 416: if (uh == null || uh.trim().length() == 0) 417: { 418: if (gnu.java.security.Configuration.DEBUG) 419: log.fine("User home path is not set or is empty"); 420: return null; 421: } 422: uh = uh.trim(); 423: File result = new File(uh); 424: if (! result.exists()) 425: { 426: if (gnu.java.security.Configuration.DEBUG) 427: log.fine("User home '" + uh + "' does not exist"); 428: return null; 429: } 430: if (! result.isDirectory()) 431: { 432: if (gnu.java.security.Configuration.DEBUG) 433: log.fine("User home '" + uh + "' is not a directory"); 434: return null; 435: } 436: if (! result.canRead()) 437: { 438: if (gnu.java.security.Configuration.DEBUG) 439: log.fine("User home '" + uh + "' is not readable"); 440: return null; 441: } 442: return result; 443: } 444: 445: private File getConfigFromUserHome(File userHome, String fileName) 446: { 447: File result = new File(userHome, fileName); 448: if (! result.exists()) 449: { 450: if (gnu.java.security.Configuration.DEBUG) 451: log.fine("File '" + fileName + "' does not exist in user's home"); 452: return null; 453: } 454: if (! result.isFile()) 455: { 456: if (gnu.java.security.Configuration.DEBUG) 457: log.fine("File '" + fileName + "' in user's home is not a file"); 458: return null; 459: } 460: if (! result.canRead()) 461: { 462: if (gnu.java.security.Configuration.DEBUG) 463: log.fine("File '" + fileName + "' in user's home is not readable"); 464: return null; 465: } 466: return result; 467: } 468: }