Frames | No Frames |
1: /* GnuConfiguration.java -- GNU Classpath implementation of JAAS Configuration 2: Copyright (C) 2006 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 = Logger.getLogger(GnuConfiguration.class.getName()); 132: /** 133: * The internal map of login modules keyed by application name. Each entry in 134: * this map is a {@link List} of {@link AppConfigurationEntry}s for that 135: * application name. 136: */ 137: private Map loginModulesMap; 138: /** Our reference to our default syntax parser. */ 139: private ConfigFileParser cp; 140: 141: // Constructor(s) 142: // -------------------------------------------------------------------------- 143: 144: /** Trivial 0-arguments Constructor. */ 145: public GnuConfiguration() 146: { 147: super(); 148: 149: loginModulesMap = new HashMap(); 150: cp = new ConfigFileParser(); 151: init(); 152: } 153: 154: // Class methods 155: // -------------------------------------------------------------------------- 156: 157: // Instance methods 158: // -------------------------------------------------------------------------- 159: 160: // Configuration abstract methods implementation ---------------------------- 161: 162: /* (non-Javadoc) 163: * @see javax.security.auth.login.Configuration#getAppConfigurationEntry(java.lang.String) 164: */ 165: public AppConfigurationEntry[] getAppConfigurationEntry(String appName) 166: { 167: if (appName == null) 168: return null; 169: 170: appName = appName.trim(); 171: if (appName.length() == 0) 172: return null; 173: 174: List loginModules = (List) loginModulesMap.get(appName); 175: if (loginModules == null || loginModules.size() == 0) 176: return null; 177: 178: if (gnu.java.security.Configuration.DEBUG) 179: log.fine(appName + " -> " + loginModules.size() + " entry(ies)"); 180: return (AppConfigurationEntry[]) loginModules.toArray(new AppConfigurationEntry[0]); 181: } 182: 183: /** 184: * Refreshes and reloads this <code>Configuration</code>. 185: * 186: * <p>This method causes this <code>Configuration</code> object to refresh / 187: * reload its contents following the locations and logic described above in 188: * the class documentation section.</p> 189: * 190: * @throws SecurityException if the caller does not have an 191: * {@link AuthPermission} for the action named 192: * <code>refreshLoginConfiguration</code>. 193: * @see AuthPermission 194: */ 195: public void refresh() 196: { 197: SecurityManager sm = System.getSecurityManager(); 198: if (sm != null) 199: sm.checkPermission(new AuthPermission("refreshLoginConfiguration")); 200: 201: loginModulesMap.clear(); 202: init(); 203: } 204: 205: // helper methods ----------------------------------------------------------- 206: 207: /** 208: * Attempts to find and parse JAAS Login Configuration file(s) written in 209: * the default syntax. The locations searched are as descibed in the class 210: * documentation. 211: */ 212: private void init() 213: { 214: if (processSecurityProperties()) 215: { 216: if (gnu.java.security.Configuration.DEBUG) 217: log.fine("Using login configuration defined by Security property(ies)"); 218: } 219: else if (processSystemProperty()) 220: { 221: if (gnu.java.security.Configuration.DEBUG) 222: log.fine("Using login configuration defined by System property"); 223: } 224: else if (processUserHome()) 225: { 226: if (gnu.java.security.Configuration.DEBUG) 227: log.fine("Using login configuration defined in ${user.home}"); 228: } 229: else 230: { 231: if (gnu.java.security.Configuration.DEBUG) 232: log.fine("No login configuration file found"); 233: } 234: } 235: 236: /** 237: * Attempts to locate and parse one or more JAAS Login Configuration files 238: * defined as the values of the Security properties 239: * <i>java.security.auth.login.config.url.N</i>. 240: * 241: * @return <code>true</code> if it succeeds, and <code>false</code> 242: * otherwsie. 243: */ 244: private boolean processSecurityProperties() 245: { 246: boolean result = false; 247: int counter = 0; 248: String s; 249: while (true) 250: try 251: { 252: counter++; 253: s = Security.getProperty("java.security.auth.login.config.url." 254: + counter); 255: if (s == null) 256: break; 257: 258: s = s.trim(); 259: if (s.length() != 0) 260: { 261: if (gnu.java.security.Configuration.DEBUG) 262: log.fine("java.security.auth.login.config.url." + counter 263: + " = " + s); 264: parseConfig(getInputStreamFromURL(s)); 265: result = true; 266: } 267: } 268: catch (Throwable t) 269: { 270: if (gnu.java.security.Configuration.DEBUG) 271: log.fine("Exception while handling Security property at #" 272: + counter + ". Continue: " + t); 273: } 274: return result; 275: } 276: 277: /** 278: * Attempts to open a designated string as a well-formed {@link URL}. If a 279: * {@link MalformedURLException} occurs, this method then tries to open that 280: * string as a {@link File} (with the same name). If it succeeds, an 281: * {@link InputStream} is constructed and returned. 282: * 283: * @param s 284: * the designated name of either a {@link URL} or a {@link File} 285: * assumed to contain a JAAS Login Configuration in the default 286: * syntax. 287: * @return an {@link InputStream} around the data source. 288: * @throws IOException 289: * if an exception occurs during the operation. 290: */ 291: private InputStream getInputStreamFromURL(String s) throws IOException 292: { 293: InputStream result = null; 294: try 295: { 296: URL url = new URL(s); 297: result = url.openStream(); 298: } 299: catch (MalformedURLException x) 300: { 301: if (gnu.java.security.Configuration.DEBUG) 302: log.fine("Failed opening as URL: " + s + ". Will try as File"); 303: result = new FileInputStream(s); 304: } 305: return result; 306: } 307: 308: /** 309: * Attempts to locate and parse a JAAS Login Configuration file defined as the 310: * value of the System property <i>java.security.auth.login.config</i>. 311: * 312: * @return <code>true</code> if it succeeds, and <code>false</code> 313: * otherwsie. 314: */ 315: private boolean processSystemProperty() 316: { 317: boolean result = false; 318: try 319: { 320: String s = System.getProperty("java.security.auth.login.config"); 321: if (s != null) 322: { 323: s = s.trim(); 324: if (s.length() != 0) 325: { 326: if (gnu.java.security.Configuration.DEBUG) 327: log.fine("java.security.auth.login.config = " + s); 328: parseConfig(getInputStreamFromURL(s)); 329: result = true; 330: } 331: } 332: } 333: catch (Throwable t) 334: { 335: if (gnu.java.security.Configuration.DEBUG) 336: log.fine("Exception while handling System property. Continue: " + t); 337: } 338: return result; 339: } 340: 341: /** 342: * Attempts to locate and parse a JAAS Login Configuration file named either 343: * as <i>.java.login.config</i> or <i>java.login.config</i> (without the 344: * leading dot) in the folder referenced by the System property 345: * <code>user.home</code>. 346: * 347: * @return <code>true</code> if it succeeds, and <code>false</code> 348: * otherwsie. 349: */ 350: private boolean processUserHome() 351: { 352: boolean result = false; 353: try 354: { 355: File userHome = getUserHome(); 356: if (userHome == null) 357: return result; 358: 359: File jaasFile; 360: jaasFile = getConfigFromUserHome(userHome, ".java.login.config"); 361: if (jaasFile == null) 362: jaasFile = getConfigFromUserHome(userHome, "java.login.config"); 363: 364: if (jaasFile == null) 365: { 366: if (gnu.java.security.Configuration.DEBUG) 367: log.fine("Login Configuration file, in " + userHome 368: + ", does not exist or is inaccessible"); 369: return result; 370: } 371: 372: FileInputStream fis = new FileInputStream(jaasFile); 373: parseConfig(fis); 374: result = true; 375: } 376: catch (Throwable t) 377: { 378: if (gnu.java.security.Configuration.DEBUG) 379: log.fine("Exception (ignored) while handling ${user.home}: " + t); 380: } 381: return result; 382: } 383: 384: private void parseConfig(InputStream configStream) throws IOException 385: { 386: cp.parse(new InputStreamReader(configStream, "UTF-8")); 387: Map loginModulesMap = cp.getLoginModulesMap(); 388: mergeLoginModules(loginModulesMap); 389: } 390: 391: private void mergeLoginModules(Map otherLoginModules) 392: { 393: if (otherLoginModules == null || otherLoginModules.size() < 1) 394: return; 395: 396: for (Iterator it = otherLoginModules.keySet().iterator(); it.hasNext();) 397: { 398: String appName = (String) it.next(); 399: List thatListOfACEs = (List) otherLoginModules.get(appName); 400: if (thatListOfACEs == null || thatListOfACEs.size() < 1) 401: continue; 402: 403: List thisListsOfACEs = (List) loginModulesMap.get(appName); 404: if (thisListsOfACEs == null) 405: loginModulesMap.put(appName, thatListOfACEs); 406: else 407: thisListsOfACEs.addAll(thatListOfACEs); 408: } 409: } 410: 411: private File getUserHome() 412: { 413: String uh = System.getProperty("user.home"); 414: if (uh == null || uh.trim().length() == 0) 415: { 416: if (gnu.java.security.Configuration.DEBUG) 417: log.fine("User home path is not set or is empty"); 418: return null; 419: } 420: uh = uh.trim(); 421: File result = new File(uh); 422: if (! result.exists()) 423: { 424: if (gnu.java.security.Configuration.DEBUG) 425: log.fine("User home '" + uh + "' does not exist"); 426: return null; 427: } 428: if (! result.isDirectory()) 429: { 430: if (gnu.java.security.Configuration.DEBUG) 431: log.fine("User home '" + uh + "' is not a directory"); 432: return null; 433: } 434: if (! result.canRead()) 435: { 436: if (gnu.java.security.Configuration.DEBUG) 437: log.fine("User home '" + uh + "' is not readable"); 438: return null; 439: } 440: return result; 441: } 442: 443: private File getConfigFromUserHome(File userHome, String fileName) 444: { 445: File result = new File(userHome, fileName); 446: if (! result.exists()) 447: { 448: if (gnu.java.security.Configuration.DEBUG) 449: log.fine("File '" + fileName + "' does not exist in user's home"); 450: return null; 451: } 452: if (! result.isFile()) 453: { 454: if (gnu.java.security.Configuration.DEBUG) 455: log.fine("File '" + fileName + "' in user's home is not a file"); 456: return null; 457: } 458: if (! result.canRead()) 459: { 460: if (gnu.java.security.Configuration.DEBUG) 461: log.fine("File '" + fileName + "' in user's home is not readable"); 462: return null; 463: } 464: return result; 465: } 466: }