FileDocCategorySizeDatePackage
FileLoginModule.javaAPI DocJava SE 6 API17899Tue Jun 10 00:22:04 BST 2008com.sun.jmx.remote.security

FileLoginModule

public class FileLoginModule extends Object implements LoginModule
This {@link LoginModule} performs file-based authentication.

A supplied username and password is verified against the corresponding user credentials stored in a designated password file. If successful then a new {@link JMXPrincipal} is created with the user's name and it is associated with the current {@link Subject}. Such principals may be identified and granted management privileges in the access control file for JMX remote management or in a Java security policy.

The password file comprises a list of key-value pairs as specified in {@link Properties}. The key represents a user's name and the value is its associated cleartext password. By default, the following password file is used:

${java.home}/lib/management/jmxremote.password
A different password file can be specified via the passwordFile configuration option.

This module recognizes the following Configuration options:

passwordFile
the path to an alternative password file. It is used instead of the default password file.
useFirstPass
if true, this module retrieves the username and password from the module's shared state, using "javax.security.auth.login.name" and "javax.security.auth.login.password" as the respective keys. The retrieved values are used for authentication. If authentication fails, no attempt for a retry is made, and the failure is reported back to the calling application.
tryFirstPass
if true, this module retrieves the username and password from the module's shared state, using "javax.security.auth.login.name" and "javax.security.auth.login.password" as the respective keys. The retrieved values are used for authentication. If authentication fails, the module uses the CallbackHandler to retrieve a new username and password, and another attempt to authenticate is made. If the authentication fails, the failure is reported back to the calling application.
storePass
if true, this module stores the username and password obtained from the CallbackHandler in the module's shared state, using "javax.security.auth.login.name" and "javax.security.auth.login.password" as the respective keys. This is not performed if existing values already exist for the username and password in the shared state, or if authentication fails.
clearPass
if true, this module clears the username and password stored in the module's shared state after both phases of authentication (login and commit) have completed.

Fields Summary
private static final String
DEFAULT_PASSWORD_FILE_NAME
private static final String
USERNAME_KEY
private static final String
PASSWORD_KEY
private static final ClassLogger
logger
private boolean
useFirstPass
private boolean
tryFirstPass
private boolean
storePass
private boolean
clearPass
private boolean
succeeded
private boolean
commitSucceeded
private String
username
private char[]
password
private JMXPrincipal
user
private Subject
subject
private CallbackHandler
callbackHandler
private Map
sharedState
private Map
options
private String
passwordFile
private String
passwordFileDisplayName
private boolean
userSuppliedPasswordFile
private boolean
hasJavaHomePermission
private Properties
userCredentials
Constructors Summary
Methods Summary
public booleanabort()
Abort user authentication (Authentication Phase 2).

This method is called if the LoginContext's overall authentication failed (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules did not succeed).

If this LoginModule's own authentication attempt succeeded (checked by retrieving the private state saved by the login and commit methods), then this method cleans up any state that was originally saved.

exception
LoginException if the abort fails.
return
false if this LoginModule's own login and/or commit attempts failed, and true otherwise.


	if (logger.debugOn()) {
	    logger.debug("abort", 
		"Authentication has not completed successfully");
	}

	if (succeeded == false) {
	    return false;
	} else if (succeeded == true && commitSucceeded == false) {

	    // Clean out state
	    succeeded = false;
	    cleanState();
	    user = null;
	} else {
	    // overall authentication succeeded and commit succeeded,
	    // but someone else's commit failed
	    logout();
	}
	return true;
    
private voidattemptAuthentication(boolean usePasswdFromSharedState)
Attempt authentication

param
usePasswdFromSharedState a flag to tell this method whether to retrieve the password from the sharedState.


	// get the username and password
	getUsernamePassword(usePasswdFromSharedState);
	
	String localPassword = null;

	// userCredentials is initialized in login()
	if (((localPassword = userCredentials.getProperty(username)) == null) ||
	    (! localPassword.equals(new String(password)))) {

	    // username not found or passwords do not match
	    if (logger.debugOn()) {
		logger.debug("login", "Invalid username or password");
	    }
	    throw new FailedLoginException("Invalid username or password");
	}

	// Save the username and password in the shared state 
	// only if authentication succeeded
	if (storePass &&
	    !sharedState.containsKey(USERNAME_KEY) &&
	    !sharedState.containsKey(PASSWORD_KEY)) {
	    sharedState.put(USERNAME_KEY, username);
	    sharedState.put(PASSWORD_KEY, password);
	}

	// Create a new user principal
	user = new JMXPrincipal(username);

	if (logger.debugOn()) {
	    logger.debug("login", 
		"User '" + username + "' successfully validated");
	}
    
private voidcleanState()
Clean out state because of a failed authentication attempt

	username = null;
	if (password != null) {
	    Arrays.fill(password, ' ");
	    password = null;
	}

	if (clearPass) {
	    sharedState.remove(USERNAME_KEY);
	    sharedState.remove(PASSWORD_KEY);
	}
    
public booleancommit()
Complete user authentication (Authentication Phase 2).

This method is called if the LoginContext's overall authentication has succeeded (all the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules have succeeded).

If this LoginModule's own authentication attempt succeeded (checked by retrieving the private state saved by the login method), then this method associates a JMXPrincipal with the Subject located in the LoginModule. If this LoginModule's own authentication attempted failed, then this method removes any state that was originally saved.

exception
LoginException if the commit fails
return
true if this LoginModule's own login and commit attempts succeeded, or false otherwise.


	if (succeeded == false) {
	    return false;
	} else {
	    if (subject.isReadOnly()) {
		cleanState();
		throw new LoginException("Subject is read-only");
	    } 
	    // add Principals to the Subject
	    if (!subject.getPrincipals().contains(user)) {
		subject.getPrincipals().add(user);
	    }

	    if (logger.debugOn()) {
		logger.debug("commit", 
		    "Authentication has completed successfully");
	    }
	}
	// in any case, clean out state
	cleanState();
	commitSucceeded = true;
	return true;
    
private voidgetUsernamePassword(boolean usePasswdFromSharedState)
Get the username and password. This method does not return any value. Instead, it sets global name and password variables.

Also note that this method will set the username and password values in the shared state in case subsequent LoginModules want to use them via use/tryFirstPass.

param
usePasswdFromSharedState boolean that tells this method whether to retrieve the password from the sharedState.


	if (usePasswdFromSharedState) {
	    // use the password saved by the first module in the stack
	    username = (String)sharedState.get(USERNAME_KEY);
	    password = (char[])sharedState.get(PASSWORD_KEY);
	    return;
	}

	// acquire username and password
        if (callbackHandler == null)
	    throw new LoginException("Error: no CallbackHandler available " +
		"to garner authentication information from the user");

	Callback[] callbacks = new Callback[2];
	callbacks[0] = new NameCallback("username");
	callbacks[1] = new PasswordCallback("password", false);

	try {
	    callbackHandler.handle(callbacks);
	    username = ((NameCallback)callbacks[0]).getName();
	    char[] tmpPassword = ((PasswordCallback)callbacks[1]).getPassword();
	    password = new char[tmpPassword.length];
	    System.arraycopy(tmpPassword, 0,
				password, 0, tmpPassword.length);
	    ((PasswordCallback)callbacks[1]).clearPassword();

	} catch (IOException ioe) {
	    LoginException le = new LoginException(ioe.toString());
	    throw (LoginException) EnvHelp.initCause(le, ioe);
	} catch (UnsupportedCallbackException uce) {
            LoginException le = new LoginException(
                                    "Error: " + uce.getCallback().toString() +
                                    " not available to garner authentication " +
                                    "information from the user");
            throw (LoginException) EnvHelp.initCause(le, uce);
	}
    
public voidinitialize(javax.security.auth.Subject subject, javax.security.auth.callback.CallbackHandler callbackHandler, java.util.Map sharedState, java.util.Map options)
Initialize this LoginModule.

param
subject the Subject to be authenticated.
param
callbackHandler a CallbackHandler to acquire the user's name and password.
param
sharedState shared LoginModule state.
param
options options specified in the login Configuration for this particular LoginModule.


                     			               			   			     
         
			    
			    
    

	this.subject = subject;
	this.callbackHandler = callbackHandler;
	this.sharedState = sharedState;
	this.options = options;

	// initialize any configured options
	tryFirstPass =
		"true".equalsIgnoreCase((String)options.get("tryFirstPass"));
	useFirstPass =
		"true".equalsIgnoreCase((String)options.get("useFirstPass"));
	storePass =
		"true".equalsIgnoreCase((String)options.get("storePass"));
	clearPass =
		"true".equalsIgnoreCase((String)options.get("clearPass"));

	passwordFile = (String)options.get("passwordFile");
	passwordFileDisplayName = passwordFile;
        userSuppliedPasswordFile = true;

	// set the location of the password file
	if (passwordFile == null) {
	    passwordFile = DEFAULT_PASSWORD_FILE_NAME;
            userSuppliedPasswordFile = false;
            try {
                System.getProperty("java.home");
                hasJavaHomePermission = true;
                passwordFileDisplayName = passwordFile;
            } catch (SecurityException e) {
                hasJavaHomePermission = false;
                passwordFileDisplayName =
                        ConnectorBootstrap.DefaultValues.PASSWORD_FILE_NAME;
            }
	}
    
private voidloadPasswordFile()

        FileInputStream fis;
        try {
            fis = new FileInputStream(passwordFile);
        } catch (SecurityException e) {
            if (userSuppliedPasswordFile || hasJavaHomePermission) {
                throw e;
            } else {
                FilePermission fp =
                        new FilePermission(passwordFileDisplayName, "read");
                AccessControlException ace = new AccessControlException(
                        "access denied " + fp.toString());
                ace.setStackTrace(e.getStackTrace());
                throw ace;
            }
        }
        BufferedInputStream bis = new BufferedInputStream(fis);
        userCredentials = new Properties();
        userCredentials.load(bis);
        bis.close();
    
public booleanlogin()
Begin user authentication (Authentication Phase 1).

Acquire the user's name and password and verify them against the corresponding credentials from the password file.

return
true always, since this LoginModule should not be ignored.
exception
FailedLoginException if the authentication fails.
exception
LoginException if this LoginModule is unable to perform the authentication.


	try {
	    loadPasswordFile();
	} catch (IOException ioe) {
	    LoginException le = new LoginException(
                    "Error: unable to load the password file: " +
                    passwordFileDisplayName);
	    throw (LoginException) EnvHelp.initCause(le, ioe);
	}

	if (userCredentials == null) {
	    throw new LoginException
		("Error: unable to locate the users' credentials.");
	}

	if (logger.debugOn()) {
	    logger.debug("login",
                    "Using password file: " + passwordFileDisplayName);
	}

	// attempt the authentication
	if (tryFirstPass) {

	    try {
		// attempt the authentication by getting the
		// username and password from shared state
		attemptAuthentication(true);

		// authentication succeeded
		succeeded = true;
		if (logger.debugOn()) {
		    logger.debug("login", 
			"Authentication using cached password has succeeded");
		}
		return true;

	    } catch (LoginException le) {
		// authentication failed -- try again below by prompting
		cleanState();
		logger.debug("login", 
		    "Authentication using cached password has failed");
	    }

	} else if (useFirstPass) {

	    try {
		// attempt the authentication by getting the
		// username and password from shared state
		attemptAuthentication(true);

		// authentication succeeded
		succeeded = true;
		if (logger.debugOn()) {
		    logger.debug("login", 
			"Authentication using cached password has succeeded");
		}
		return true;

	    } catch (LoginException le) {
		// authentication failed
		cleanState();
		logger.debug("login",
		    "Authentication using cached password has failed");

		throw le;
	    }
	}

	if (logger.debugOn()) {
	    logger.debug("login", "Acquiring password");
	}

	// attempt the authentication using the supplied username and password
	try {
	    attemptAuthentication(false);

	    // authentication succeeded
	    succeeded = true;
	    if (logger.debugOn()) {
		logger.debug("login", "Authentication has succeeded");
	    }
	    return true;

	} catch (LoginException le) {
	    cleanState();
	    logger.debug("login", "Authentication has failed");

	    throw le;
	}
    
public booleanlogout()
Logout a user.

This method removes the Principals that were added by the commit method.

exception
LoginException if the logout fails.
return
true in all cases since this LoginModule should not be ignored.

	if (subject.isReadOnly()) {
	    cleanState();
	    throw new LoginException ("Subject is read-only");
	}
	subject.getPrincipals().remove(user);
    
	// clean out state
	cleanState();
	succeeded = false;
	commitSucceeded = false;
	user = null;

	if (logger.debugOn()) {
	    logger.debug("logout", "Subject is being logged out");
	}

	return true;