- /*
- * @(#)LoginContext.java 1.98 04/06/28
- *
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
- * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
- */
-
- package javax.security.auth.login;
-
- import java.lang.reflect.Constructor;
- import java.lang.reflect.Method;
- import java.lang.reflect.InvocationTargetException;
- import java.util.LinkedList;
- import java.util.Map;
- import java.util.HashMap;
- import java.text.MessageFormat;
- import javax.security.auth.Subject;
- import javax.security.auth.AuthPermission;
- import javax.security.auth.callback.*;
- import java.security.AccessController;
- import java.security.AccessControlContext;
- import sun.security.util.PendingException;
- import sun.security.util.ResourcesMgr;
-
- /**
- * <p> The <code>LoginContext</code> class describes the basic methods used
- * to authenticate Subjects and provides a way to develop an
- * application independent of the underlying authentication technology.
- * A <code>Configuration</code> specifies the authentication technology, or
- * <code>LoginModule</code>, to be used with a particular application.
- * Different LoginModules can be plugged in under an application
- * without requiring any modifications to the application itself.
- *
- * <p> In addition to supporting <i>pluggable</i> authentication, this class
- * also supports the notion of <i>stacked</i> authentication.
- * Applications may be configured to use more than one
- * LoginModule. For example, one could
- * configure both a Kerberos LoginModule and a smart card
- * LoginModule under an application.
- *
- * <p> A typical caller instantiates a LoginContext with
- * a <i>name</i> and a <code>CallbackHandler</code>.
- * LoginContext uses the <i>name</i> as the index into a
- * Configuration to determine which LoginModules should be used,
- * and which ones must succeed in order for the overall authentication to
- * succeed. The <code>CallbackHandler</code> is passed to the underlying
- * LoginModules so they may communicate and interact with users
- * (prompting for a username and password via a graphical user interface,
- * for example).
- *
- * <p> Once the caller has instantiated a LoginContext,
- * it invokes the <code>login</code> method to authenticate
- * a <code>Subject</code>. The <code>login</code> method invokes
- * the configured modules to perform their respective types of authentication
- * (username/password, smart card pin verification, etc.).
- * Note that the LoginModules will not attempt authentication retries nor
- * introduce delays if the authentication fails.
- * Such tasks belong to the LoginContext caller.
- *
- * <p> If the <code>login</code> method returns without
- * throwing an exception, then the overall authentication succeeded.
- * The caller can then retrieve
- * the newly authenticated Subject by invoking the
- * <code>getSubject</code> method. Principals and Credentials associated
- * with the Subject may be retrieved by invoking the Subject's
- * respective <code>getPrincipals</code>, <code>getPublicCredentials</code>,
- * and <code>getPrivateCredentials</code> methods.
- *
- * <p> To logout the Subject, the caller calls
- * the <code>logout</code> method. As with the <code>login</code>
- * method, this <code>logout</code> method invokes the <code>logout</code>
- * method for the configured modules.
- *
- * <p> A LoginContext should not be used to authenticate
- * more than one Subject. A separate LoginContext
- * should be used to authenticate each different Subject.
- *
- * <p> The following documentation applies to all LoginContext constructors:
- * <ol>
- *
- * <li> <code>Subject</code>
- * <ul>
- * <li> If the constructor has a Subject
- * input parameter, the LoginContext uses the caller-specified
- * Subject object.
- * <p>
- * <li> If the caller specifies a <code>null</code> Subject
- * and a <code>null</code> value is permitted,
- * the LoginContext instantiates a new Subject.
- * <p>
- * <li> If the constructor does <b>not</b> have a Subject
- * input parameter, the LoginContext instantiates a new Subject.
- * <p>
- * </ul>
- *
- * <li> <code>Configuration</code>
- * <ul>
- * <li> If the constructor has a Configuration
- * input parameter and the caller specifies a non-null Configuration,
- * the LoginContext uses the caller-specified Configuration.
- * <p>
- * If the constructor does <b>not</b> have a Configuration
- * input parameter, or if the caller specifies a <code>null</code>
- * Configuration object, the constructor uses the following call to
- * get the installed Configuration:
- * <pre>
- * config = Configuration.getConfiguration();
- * </pre>
- * For both cases,
- * the <i>name</i> argument given to the constructor is passed to the
- * <code>Configuration.getAppConfigurationEntry</code> method.
- * If the Configuration has no entries for the specified <i>name</i>,
- * then the <code>LoginContext</code> calls
- * <code>getAppConfigurationEntry</code> with the name, "<i>other</i>"
- * (the default entry name). If there is no entry for "<i>other</i>",
- * then a <code>LoginException</code> is thrown.
- * <p>
- * <li> When LoginContext uses the installed Configuration, the caller
- * requires the createLoginContext.<em>name</em> and possibly
- * createLoginContext.other AuthPermissions. Furthermore, the
- * LoginContext will invoke configured modules from within an
- * <code>AccessController.doPrivileged</code> call so that modules that
- * perform security-sensitive tasks (such as connecting to remote hosts,
- * and updating the Subject) will require the respective permissions, but
- * the callers of the LoginContext will not require those permissions.
- * <p>
- * <li> When LoginContext uses a caller-specified Configuration, the caller
- * does not require any createLoginContext AuthPermission. The LoginContext
- * saves the <code>AccessControlContext</code> for the caller,
- * and invokes the configured modules from within an
- * <tt>AccessController.doPrivileged</tt> call constrained by that context.
- * This means the caller context (stored when the LoginContext was created)
- * must have sufficient permissions to perform any security-sensitive tasks
- * that the modules may perform.
- * <p>
- * </ul>
- *
- * <li> <code>CallbackHandler</code>
- * <ul>
- * <li> If the constructor has a CallbackHandler
- * input parameter, the LoginContext uses the caller-specified
- * CallbackHandler object.
- * <p>
- * <li> If the constructor does <b>not</b> have a CallbackHandler
- * input parameter, or if the caller specifies a <code>null</code>
- * CallbackHandler object (and a <code>null</code> value is permitted),
- * the LoginContext queries the
- * <i>auth.login.defaultCallbackHandler</i> security property
- * for the fully qualified class name of a default handler implementation.
- * If the security property is not set,
- * then the underlying modules will not have a
- * CallbackHandler for use in communicating
- * with users. The caller thus assumes that the configured
- * modules have alternative means for authenticating the user.
- *
- * <p>
- * <li> When the LoginContext uses the installed Configuration (instead of
- * a caller-specified Configuration, see above),
- * then this LoginContext must wrap any
- * caller-specified or default CallbackHandler implementation
- * in a new CallbackHandler implementation
- * whose <code>handle</code> method implementation invokes the
- * specified CallbackHandler's <code>handle</code> method in a
- * <code>java.security.AccessController.doPrivileged</code> call
- * constrained by the caller's current <code>AccessControlContext</code>.
- * </ul>
- * </ol>
- *
- * <p> Note that Security Properties
- * (such as <code>auth.login.defaultCallbackHandler</code>)
- * can be set programmatically via the
- * <code>java.security.Security</code> class,
- * or statically in the Java security properties file located in the
- * file named <JAVA_HOME>/lib/security/java.security,
- * where <JAVA_HOME> refers to the directory where the JDK
- * was installed.
- *
- * @version 1.98, 06/28/04
- * @see java.security.Security
- * @see javax.security.auth.AuthPermission
- * @see javax.security.auth.Subject
- * @see javax.security.auth.callback.CallbackHandler
- * @see javax.security.auth.login.Configuration
- * @see javax.security.auth.spi.LoginModule
- */
- public class LoginContext {
-
- private static final String INIT_METHOD = "initialize";
- private static final String LOGIN_METHOD = "login";
- private static final String COMMIT_METHOD = "commit";
- private static final String ABORT_METHOD = "abort";
- private static final String LOGOUT_METHOD = "logout";
- private static final String OTHER = "other";
- private static final String DEFAULT_HANDLER =
- "auth.login.defaultCallbackHandler";
- private Subject subject = null;
- private boolean subjectProvided = false;
- private boolean loginSucceeded = false;
- private CallbackHandler callbackHandler;
- private Map state = new HashMap();
-
- private Configuration config;
- private boolean configProvided = false;
- private AccessControlContext creatorAcc = null;
- private ModuleInfo[] moduleStack;
- private ClassLoader contextClassLoader = null;
- private static final Class[] PARAMS = { };
-
- // state saved in the event a user-specified asynchronous exception
- // was specified and thrown
-
- private int moduleIndex = 0;
- private LoginException firstError = null;
- private LoginException firstRequiredError = null;
- private boolean success = false;
-
- private static final sun.security.util.Debug debug =
- sun.security.util.Debug.getInstance("logincontext", "\t[LoginContext]");
-
- private void init(String name) throws LoginException {
-
- SecurityManager sm = System.getSecurityManager();
- if (sm != null && !configProvided) {
- sm.checkPermission(new AuthPermission
- ("createLoginContext." + name));
- }
-
- if (name == null)
- throw new LoginException
- (ResourcesMgr.getString("Invalid null input: name"));
-
- // get the Configuration
- if (config == null) {
- config = (Configuration)java.security.AccessController.doPrivileged
- (new java.security.PrivilegedAction() {
- public Object run() {
- return Configuration.getConfiguration();
- }
- });
- }
-
- // get the LoginModules configured for this application
- AppConfigurationEntry[] entries = config.getAppConfigurationEntry(name);
- if (entries == null) {
-
- if (sm != null && !configProvided) {
- sm.checkPermission(new AuthPermission
- ("createLoginContext." + OTHER));
- }
-
- entries = config.getAppConfigurationEntry(OTHER);
- if (entries == null) {
- MessageFormat form = new MessageFormat(ResourcesMgr.getString
- ("No LoginModules configured for name"));
- Object[] source = {name};
- throw new LoginException(form.format(source));
- }
- }
- moduleStack = new ModuleInfo[entries.length];
- for (int i = 0; i < entries.length; i++) {
- // clone returned array
- moduleStack[i] = new ModuleInfo
- (new AppConfigurationEntry
- (entries[i].getLoginModuleName(),
- entries[i].getControlFlag(),
- entries[i].getOptions()),
- null);
- }
-
- contextClassLoader =
- (ClassLoader)java.security.AccessController.doPrivileged
- (new java.security.PrivilegedAction() {
- public Object run() {
- return Thread.currentThread().getContextClassLoader();
- }
- });
- }
-
- private void loadDefaultCallbackHandler() throws LoginException {
-
- // get the default handler class
- try {
-
- final ClassLoader finalLoader = contextClassLoader;
-
- this.callbackHandler = (CallbackHandler)
- java.security.AccessController.doPrivileged
- (new java.security.PrivilegedExceptionAction() {
- public Object run() throws Exception {
- String defaultHandler = java.security.Security.getProperty
- (DEFAULT_HANDLER);
- if (defaultHandler == null || defaultHandler.length() == 0)
- return null;
- Class c = Class.forName(defaultHandler,
- true,
- finalLoader);
- return c.newInstance();
- }
- });
- } catch (java.security.PrivilegedActionException pae) {
- throw new LoginException(pae.getException().toString());
- }
-
- // secure it with the caller's ACC
- if (this.callbackHandler != null && !configProvided) {
- this.callbackHandler = new SecureCallbackHandler
- (java.security.AccessController.getContext(),
- this.callbackHandler);
- }
- }
-
- /**
- * Instantiate a new <code>LoginContext</code> object with a name.
- *
- * @param name the name used as the index into the
- * <code>Configuration</code>.
- *
- * @exception LoginException if the caller-specified <code>name</code>
- * does not appear in the <code>Configuration</code>
- * and there is no <code>Configuration</code> entry
- * for "<i>other</i>", or if the
- * <i>auth.login.defaultCallbackHandler</i>
- * security property was set, but the implementation
- * class could not be loaded.
- * <p>
- * @exception SecurityException if a SecurityManager is set and
- * the caller does not have
- * AuthPermission("createLoginContext.<i>name</i>"),
- * or if a configuration entry for <i>name</i> does not exist and
- * the caller does not additionally have
- * AuthPermission("createLoginContext.other")
- */
- public LoginContext(String name) throws LoginException {
- init(name);
- loadDefaultCallbackHandler();
- }
-
- /**
- * Instantiate a new <code>LoginContext</code> object with a name
- * and a <code>Subject</code> object.
- *
- * <p>
- *
- * @param name the name used as the index into the
- * <code>Configuration</code>. <p>
- *
- * @param subject the <code>Subject</code> to authenticate.
- *
- * @exception LoginException if the caller-specified <code>name</code>
- * does not appear in the <code>Configuration</code>
- * and there is no <code>Configuration</code> entry
- * for "<i>other</i>", if the caller-specified <code>subject</code>
- * is <code>null</code>, or if the
- * <i>auth.login.defaultCallbackHandler</i>
- * security property was set, but the implementation
- * class could not be loaded.
- * <p>
- * @exception SecurityException if a SecurityManager is set and
- * the caller does not have
- * AuthPermission("createLoginContext.<i>name</i>"),
- * or if a configuration entry for <i>name</i> does not exist and
- * the caller does not additionally have
- * AuthPermission("createLoginContext.other")
- */
- public LoginContext(String name, Subject subject)
- throws LoginException {
- init(name);
- if (subject == null)
- throw new LoginException
- (ResourcesMgr.getString("invalid null Subject provided"));
- this.subject = subject;
- subjectProvided = true;
- loadDefaultCallbackHandler();
- }
-
- /**
- * Instantiate a new <code>LoginContext</code> object with a name
- * and a <code>CallbackHandler</code> object.
- *
- * <p>
- *
- * @param name the name used as the index into the
- * <code>Configuration</code>. <p>
- *
- * @param callbackHandler the <code>CallbackHandler</code> object used by
- * LoginModules to communicate with the user.
- *
- * @exception LoginException if the caller-specified <code>name</code>
- * does not appear in the <code>Configuration</code>
- * and there is no <code>Configuration</code> entry
- * for "<i>other</i>", or if the caller-specified
- * <code>callbackHandler</code> is <code>null</code>.
- * <p>
- * @exception SecurityException if a SecurityManager is set and
- * the caller does not have
- * AuthPermission("createLoginContext.<i>name</i>"),
- * or if a configuration entry for <i>name</i> does not exist and
- * the caller does not additionally have
- * AuthPermission("createLoginContext.other")
- */
- public LoginContext(String name, CallbackHandler callbackHandler)
- throws LoginException {
- init(name);
- if (callbackHandler == null)
- throw new LoginException(ResourcesMgr.getString
- ("invalid null CallbackHandler provided"));
- this.callbackHandler = new SecureCallbackHandler
- (java.security.AccessController.getContext(),
- callbackHandler);
- }
-
- /**
- * Instantiate a new <code>LoginContext</code> object with a name,
- * a <code>Subject</code> to be authenticated, and a
- * <code>CallbackHandler</code> object.
- *
- * <p>
- *
- * @param name the name used as the index into the
- * <code>Configuration</code>. <p>
- *
- * @param subject the <code>Subject</code> to authenticate. <p>
- *
- * @param callbackHandler the <code>CallbackHandler</code> object used by
- * LoginModules to communicate with the user.
- *
- * @exception LoginException if the caller-specified <code>name</code>
- * does not appear in the <code>Configuration</code>
- * and there is no <code>Configuration</code> entry
- * for "<i>other</i>", or if the caller-specified
- * <code>subject</code> is <code>null</code>,
- * or if the caller-specified
- * <code>callbackHandler</code> is <code>null</code>.
- * <p>
- * @exception SecurityException if a SecurityManager is set and
- * the caller does not have
- * AuthPermission("createLoginContext.<i>name</i>"),
- * or if a configuration entry for <i>name</i> does not exist and
- * the caller does not additionally have
- * AuthPermission("createLoginContext.other")
- */
- public LoginContext(String name, Subject subject,
- CallbackHandler callbackHandler) throws LoginException {
- this(name, subject);
- if (callbackHandler == null)
- throw new LoginException(ResourcesMgr.getString
- ("invalid null CallbackHandler provided"));
- this.callbackHandler = new SecureCallbackHandler
- (java.security.AccessController.getContext(),
- callbackHandler);
- }
-
- /**
- * Instantiate a new <code>LoginContext</code> object with a name,
- * a <code>Subject</code> to be authenticated,
- * a <code>CallbackHandler</code> object, and a login
- * <code>Configuration</code>.
- *
- * <p>
- *
- * @param name the name used as the index into the caller-specified
- * <code>Configuration</code>. <p>
- *
- * @param subject the <code>Subject</code> to authenticate,
- * or <code>null</code>. <p>
- *
- * @param callbackHandler the <code>CallbackHandler</code> object used by
- * LoginModules to communicate with the user, or <code>null</code>.
- * <p>
- *
- * @param config the <code>Configuration</code> that lists the
- * login modules to be called to perform the authentication,
- * or <code>null</code>.
- *
- * @exception LoginException if the caller-specified <code>name</code>
- * does not appear in the <code>Configuration</code>
- * and there is no <code>Configuration</code> entry
- * for "<i>other</i>".
- * <p>
- * @exception SecurityException if a SecurityManager is set,
- * <i>config</i> is <code>null</code>,
- * and either the caller does not have
- * AuthPermission("createLoginContext.<i>name</i>"),
- * or if a configuration entry for <i>name</i> does not exist and
- * the caller does not additionally have
- * AuthPermission("createLoginContext.other")
- *
- * @since 1.5
- */
- public LoginContext(String name, Subject subject,
- CallbackHandler callbackHandler,
- Configuration config) throws LoginException {
- this.config = config;
- configProvided = (config != null) ? true : false;
- if (configProvided) {
- creatorAcc = java.security.AccessController.getContext();
- }
-
- init(name);
- if (subject != null) {
- this.subject = subject;
- subjectProvided = true;
- }
- if (callbackHandler == null) {
- loadDefaultCallbackHandler();
- } else if (!configProvided) {
- this.callbackHandler = new SecureCallbackHandler
- (java.security.AccessController.getContext(),
- callbackHandler);
- } else {
- this.callbackHandler = callbackHandler;
- }
- }
-
- /**
- * Perform the authentication.
- *
- * <p> This method invokes the <code>login</code> method for each
- * LoginModule configured for the <i>name</i> specified to the
- * <code>LoginContext</code> constructor, as determined by the login
- * <code>Configuration</code>. Each <code>LoginModule</code>
- * then performs its respective type of authentication
- * (username/password, smart card pin verification, etc.).
- *
- * <p> This method completes a 2-phase authentication process by
- * calling each configured LoginModule's <code>commit</code> method
- * if the overall authentication succeeded (the relevant REQUIRED,
- * REQUISITE, SUFFICIENT, and OPTIONAL LoginModules succeeded),
- * or by calling each configured LoginModule's <code>abort</code> method
- * if the overall authentication failed. If authentication succeeded,
- * each successful LoginModule's <code>commit</code> method associates
- * the relevant Principals and Credentials with the <code>Subject</code>.
- * If authentication failed, each LoginModule's <code>abort</code> method
- * removes/destroys any previously stored state.
- *
- * <p> If the <code>commit</code> phase of the authentication process
- * fails, then the overall authentication fails and this method
- * invokes the <code>abort</code> method for each configured
- * <code>LoginModule</code>.
- *
- * <p> If the <code>abort</code> phase
- * fails for any reason, then this method propagates the
- * original exception thrown either during the <code>login</code> phase
- * or the <code>commit</code> phase. In either case, the overall
- * authentication fails.
- *
- * <p> In the case where multiple LoginModules fail,
- * this method propagates the exception raised by the first
- * <code>LoginModule</code> which failed.
- *
- * <p> Note that if this method enters the <code>abort</code> phase
- * (either the <code>login</code> or <code>commit</code> phase failed),
- * this method invokes all LoginModules configured for the
- * application regardless of their respective <code>Configuration</code>
- * flag parameters. Essentially this means that <code>Requisite</code>
- * and <code>Sufficient</code> semantics are ignored during the
- * <code>abort</code> phase. This guarantees that proper cleanup
- * and state restoration can take place.
- *
- * <p>
- *
- * @exception LoginException if the authentication fails.
- */
- public void login() throws LoginException {
-
- loginSucceeded = false;
-
- if (subject == null) {
- subject = new Subject();
- }
-
- try {
- if (configProvided) {
- // module invoked in doPrivileged with creatorAcc
- invokeCreatorPriv(LOGIN_METHOD);
- invokeCreatorPriv(COMMIT_METHOD);
- } else {
- // module invoked in doPrivileged
- invokePriv(LOGIN_METHOD);
- invokePriv(COMMIT_METHOD);
- }
- loginSucceeded = true;
- } catch (LoginException le) {
- try {
- if (configProvided) {
- invokeCreatorPriv(ABORT_METHOD);
- } else {
- invokePriv(ABORT_METHOD);
- }
- } catch (LoginException le2) {
- throw le;
- }
- throw le;
- }
- }
-
- /**
- * Logout the <code>Subject</code>.
- *
- * <p> This method invokes the <code>logout</code> method for each
- * <code>LoginModule</code> configured for this <code>LoginContext</code>.
- * Each <code>LoginModule</code> performs its respective logout procedure
- * which may include removing/destroying
- * <code>Principal</code> and <code>Credential</code> information
- * from the <code>Subject</code> and state cleanup.
- *
- * <p> Note that this method invokes all LoginModules configured for the
- * application regardless of their respective
- * <code>Configuration</code> flag parameters. Essentially this means
- * that <code>Requisite</code> and <code>Sufficient</code> semantics are
- * ignored for this method. This guarantees that proper cleanup
- * and state restoration can take place.
- *
- * <p>
- *
- * @exception LoginException if the logout fails.
- */
- public void logout() throws LoginException {
- if (subject == null) {
- throw new LoginException(ResourcesMgr.getString
- ("null subject - logout called before login"));
- }
-
- if (configProvided) {
- // module invoked in doPrivileged with creatorAcc
- invokeCreatorPriv(LOGOUT_METHOD);
- } else {
- // module invoked in doPrivileged
- invokePriv(LOGOUT_METHOD);
- }
- }
-
- /**
- * Return the authenticated Subject.
- *
- * <p>
- *
- * @return the authenticated Subject. If the caller specified a
- * Subject to this LoginContext's constructor,
- * this method returns the caller-specified Subject.
- * If a Subject was not specified and authentication succeeds,
- * this method returns the Subject instantiated and used for
- * authentication by this LoginContext.
- * If a Subject was not specified, and authentication fails or
- * has not been attempted, this method returns null.
- */
- public Subject getSubject() {
- if (!loginSucceeded && !subjectProvided)
- return null;
- return subject;
- }
-
- private void clearState() {
- moduleIndex = 0;
- firstError = null;
- firstRequiredError = null;
- success = false;
- }
-
- private void throwException(LoginException originalError, LoginException le)
- throws LoginException {
-
- // first clear state
- clearState();
-
- // throw the exception
- LoginException error = (originalError != null) ? originalError : le;
- throw error;
- }
-
- /**
- * Invokes the login, commit, and logout methods
- * from a LoginModule inside a doPrivileged block.
- *
- * This version is called if the caller did not instantiate
- * the LoginContext with a Configuration object.
- */
- private void invokePriv(final String methodName) throws LoginException {
- try {
- java.security.AccessController.doPrivileged
- (new java.security.PrivilegedExceptionAction() {
- public Object run() throws LoginException {
- invoke(methodName);
- return null;
- }
- });
- } catch (java.security.PrivilegedActionException pae) {
- throw (LoginException)pae.getException();
- }
- }
-
- /**
- * Invokes the login, commit, and logout methods
- * from a LoginModule inside a doPrivileged block restricted
- * by creatorAcc
- *
- * This version is called if the caller instantiated
- * the LoginContext with a Configuration object.
- */
- private void invokeCreatorPriv(final String methodName)
- throws LoginException {
- try {
- java.security.AccessController.doPrivileged
- (new java.security.PrivilegedExceptionAction() {
- public Object run() throws LoginException {
- invoke(methodName);
- return null;
- }
- }, creatorAcc);
- } catch (java.security.PrivilegedActionException pae) {
- throw (LoginException)pae.getException();
- }
- }
-
- private void invoke(String methodName) throws LoginException {
-
- // start at moduleIndex
- // - this can only be non-zero if methodName is LOGIN_METHOD
-
- for (int i = moduleIndex; i < moduleStack.length; i++, moduleIndex++) {
- try {
-
- int mIndex = 0;
- Method[] methods = null;
-
- if (moduleStack[i].module != null) {
- methods = moduleStack[i].module.getClass().getMethods();
- } else {
-
- // instantiate the LoginModule
- Class c = Class.forName
- (moduleStack[i].entry.getLoginModuleName(),
- true,
- contextClassLoader);
-
- Constructor constructor = c.getConstructor(PARAMS);
- Object[] args = { };
-
- // allow any object to be a LoginModule
- // as long as it conforms to the interface
- moduleStack[i].module = constructor.newInstance(args);
-
- methods = moduleStack[i].module.getClass().getMethods();
-
- // call the LoginModule's initialize method
- for (mIndex = 0; mIndex < methods.length; mIndex++) {
- if (methods[mIndex].getName().equals(INIT_METHOD))
- break;
- }
-
- Object[] initArgs = {subject,
- callbackHandler,
- state,
- moduleStack[i].entry.getOptions() };
- // invoke the LoginModule initialize method
- methods[mIndex].invoke(moduleStack[i].module, initArgs);
- }
-
- // find the requested method in the LoginModule
- for (mIndex = 0; mIndex < methods.length; mIndex++) {
- if (methods[mIndex].getName().equals(methodName))
- break;
- }
-
- // set up the arguments to be passed to the LoginModule method
- Object[] args = { };
-
- // invoke the LoginModule method
- boolean status = ((Boolean)methods[mIndex].invoke
- (moduleStack[i].module, args)).booleanValue();
-
- if (status == true) {
-
- // if SUFFICIENT, return if no prior REQUIRED errors
- if (!methodName.equals(ABORT_METHOD) &&
- !methodName.equals(LOGOUT_METHOD) &&
- moduleStack[i].entry.getControlFlag() ==
- AppConfigurationEntry.LoginModuleControlFlag.SUFFICIENT &&
- firstRequiredError == null) {
-
- // clear state
- clearState();
-
- if (debug != null)
- debug.println(methodName + " SUFFICIENT success");
- return;
- }
-
- if (debug != null)
- debug.println(methodName + " success");
- success = true;
- } else {
- if (debug != null)
- debug.println(methodName + " ignored");
- }
-
- } catch (NoSuchMethodException nsme) {
- MessageFormat form = new MessageFormat(ResourcesMgr.getString
- ("unable to instantiate LoginModule, module, because " +
- "it does not provide a no-argument constructor"));
- Object[] source = {moduleStack[i].entry.getLoginModuleName()};
- throwException(null, new LoginException(form.format(source)));
- } catch (InstantiationException ie) {
- throwException(null, new LoginException(ResourcesMgr.getString
- ("unable to instantiate LoginModule: ") +
- ie.getMessage()));
- } catch (ClassNotFoundException cnfe) {
- throwException(null, new LoginException(ResourcesMgr.getString
- ("unable to find LoginModule class: ") +
- cnfe.getMessage()));
- } catch (IllegalAccessException iae) {
- throwException(null, new LoginException(ResourcesMgr.getString
- ("unable to access LoginModule: ") +
- iae.getMessage()));
- } catch (InvocationTargetException ite) {
-
- // failure cases
-
- LoginException le;
-
- if (ite.getCause() instanceof PendingException &&
- methodName.equals(LOGIN_METHOD)) {
-
- // XXX
- //
- // if a module's LOGIN_METHOD threw a PendingException
- // then immediately throw it.
- //
- // when LoginContext is called again,
- // the module that threw the exception is invoked first
- // (the module list is not invoked from the start).
- // previously thrown exception state is still present.
- //
- // it is assumed that the module which threw
- // the exception can have its
- // LOGIN_METHOD invoked twice in a row
- // without any commit/abort in between.
- //
- // in all cases when LoginContext returns
- // (either via natural return or by throwing an exception)
- // we need to call clearState before returning.
- // the only time that is not true is in this case -
- // do not call throwException here.
-
- throw (PendingException)ite.getCause();
-
- } else if (ite.getCause() instanceof LoginException) {
-
- le = (LoginException)ite.getCause();
-
- } else if (ite.getCause() instanceof SecurityException) {
-
- // do not want privacy leak
- // (e.g., sensitive file path in exception msg)
-
- le = new LoginException("Security Exception");
- le.initCause(new SecurityException());
- if (debug != null) {
- debug.println
- ("original security exception with detail msg " +
- "replaced by new exception with empty detail msg");
- debug.println("original security exception: " +
- ite.getCause().toString());
- }
- } else {
-
- // capture an unexpected LoginModule exception
- java.io.StringWriter sw = new java.io.StringWriter();
- ite.getCause().printStackTrace
- (new java.io.PrintWriter(sw));
- sw.flush();
- le = new LoginException(sw.toString());
- }
-
- if (moduleStack[i].entry.getControlFlag() ==
- AppConfigurationEntry.LoginModuleControlFlag.REQUISITE) {
-
- if (debug != null)
- debug.println(methodName + " REQUISITE failure");
-
- // if REQUISITE, then immediately throw an exception
- if (methodName.equals(ABORT_METHOD) ||
- methodName.equals(LOGOUT_METHOD)) {
- if (firstRequiredError == null)
- firstRequiredError = le;
- } else {
- throwException(firstRequiredError, le);
- }
-
- } else if (moduleStack[i].entry.getControlFlag() ==
- AppConfigurationEntry.LoginModuleControlFlag.REQUIRED) {
-
- if (debug != null)
- debug.println(methodName + " REQUIRED failure");
-
- // mark down that a REQUIRED module failed
- if (firstRequiredError == null)
- firstRequiredError = le;
-
- } else {
-
- if (debug != null)
- debug.println(methodName + " OPTIONAL failure");
-
- // mark down that an OPTIONAL module failed
- if (firstError == null)
- firstError = le;
- }
- }
- }
-
- // we went thru all the LoginModules.
- if (firstRequiredError != null) {
- // a REQUIRED module failed -- return the error
- throwException(firstRequiredError, null);
- } else if (success == false && firstError != null) {
- // no module succeeded -- return the first error
- throwException(firstError, null);
- } else if (success == false) {
- // no module succeeded -- all modules were IGNORED
- throwException(new LoginException
- (ResourcesMgr.getString("Login Failure: all modules ignored")),
- null);
- } else {
- // success
-
- clearState();
- return;
- }
- }
-
- /**
- * Wrap the caller-specified CallbackHandler in our own
- * and invoke it within a privileged block, constrained by
- * the caller's AccessControlContext.
- */
- private static class SecureCallbackHandler implements CallbackHandler {
-
- private final java.security.AccessControlContext acc;
- private final CallbackHandler ch;
-
- SecureCallbackHandler(java.security.AccessControlContext acc,
- CallbackHandler ch) {
- this.acc = acc;
- this.ch = ch;
- }
-
- public void handle(final Callback[] callbacks)
- throws java.io.IOException, UnsupportedCallbackException {
- try {
- java.security.AccessController.doPrivileged
- (new java.security.PrivilegedExceptionAction() {
- public Object run() throws java.io.IOException,
- UnsupportedCallbackException {
- ch.handle(callbacks);
- return null;
- }
- }, acc);
- } catch (java.security.PrivilegedActionException pae) {
- if (pae.getException() instanceof java.io.IOException) {
- throw (java.io.IOException)pae.getException();
- } else {
- throw (UnsupportedCallbackException)pae.getException();
- }
- }
- }
- }
-
- /**
- * LoginModule information -
- * incapsulates Configuration info and actual module instances
- */
- private static class ModuleInfo {
- AppConfigurationEntry entry;
- Object module;
-
- ModuleInfo(AppConfigurationEntry newEntry, Object newModule) {
- this.entry = newEntry;
- this.module = newModule;
- }
- }
- }