/* * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ 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; /** *
The {@code LoginContext} 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} specifies the authentication technology, or * {@code LoginModule}, to be used with a particular application. * Different LoginModules can be plugged in under an application * without requiring any modifications to the application itself. * *
In addition to supporting pluggable authentication, this class * also supports the notion of stacked 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. * *
A typical caller instantiates a LoginContext with * a name and a {@code CallbackHandler}. * LoginContext uses the name 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} 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). * *
Once the caller has instantiated a LoginContext, * it invokes the {@code login} method to authenticate * a {@code Subject}. The {@code login} 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. * *
If the {@code login} 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} method. Principals and Credentials associated * with the Subject may be retrieved by invoking the Subject's * respective {@code getPrincipals}, {@code getPublicCredentials}, * and {@code getPrivateCredentials} methods. * *
To logout the Subject, the caller calls * the {@code logout} method. As with the {@code login} * method, this {@code logout} method invokes the {@code logout} * method for the configured modules. * *
A LoginContext should not be used to authenticate * more than one Subject. A separate LoginContext * should be used to authenticate each different Subject. * *
The following documentation applies to all LoginContext constructors: *
*
* If the constructor does not have a Configuration * input parameter, or if the caller specifies a {@code null} * Configuration object, the constructor uses the following call to * get the installed Configuration: *
* config = Configuration.getConfiguration(); ** For both cases, * the name argument given to the constructor is passed to the * {@code Configuration.getAppConfigurationEntry} method. * If the Configuration has no entries for the specified name, * then the {@code LoginContext} calls * {@code getAppConfigurationEntry} with the name, "other" * (the default entry name). If there is no entry for "other", * then a {@code LoginException} is thrown. * *
*
* @exception SecurityException if a SecurityManager is set and * the caller does not have * AuthPermission("createLoginContext.name"), * or if a configuration entry for name 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} object with a name * and a {@code Subject} object. * *
* * @param name the name used as the index into the * {@code Configuration}.
* * @param subject the {@code Subject} to authenticate. * * @exception LoginException if the caller-specified {@code name} * does not appear in the {@code Configuration} * and there is no {@code Configuration} entry * for "other", if the caller-specified {@code subject} * is {@code null}, or if the * auth.login.defaultCallbackHandler * security property was set, but the implementation * class could not be loaded. *
* @exception SecurityException if a SecurityManager is set and * the caller does not have * AuthPermission("createLoginContext.name"), * or if a configuration entry for name 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} object with a name * and a {@code CallbackHandler} object. * *
* * @param name the name used as the index into the * {@code Configuration}.
* * @param callbackHandler the {@code CallbackHandler} object used by * LoginModules to communicate with the user. * * @exception LoginException if the caller-specified {@code name} * does not appear in the {@code Configuration} * and there is no {@code Configuration} entry * for "other", or if the caller-specified * {@code callbackHandler} is {@code null}. *
* @exception SecurityException if a SecurityManager is set and * the caller does not have * AuthPermission("createLoginContext.name"), * or if a configuration entry for name 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} object with a name, * a {@code Subject} to be authenticated, and a * {@code CallbackHandler} object. * *
* * @param name the name used as the index into the * {@code Configuration}.
* * @param subject the {@code Subject} to authenticate.
* * @param callbackHandler the {@code CallbackHandler} object used by * LoginModules to communicate with the user. * * @exception LoginException if the caller-specified {@code name} * does not appear in the {@code Configuration} * and there is no {@code Configuration} entry * for "other", or if the caller-specified * {@code subject} is {@code null}, * or if the caller-specified * {@code callbackHandler} is {@code null}. *
* @exception SecurityException if a SecurityManager is set and * the caller does not have * AuthPermission("createLoginContext.name"), * or if a configuration entry for name 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} object with a name, * a {@code Subject} to be authenticated, * a {@code CallbackHandler} object, and a login * {@code Configuration}. * *
* * @param name the name used as the index into the caller-specified * {@code Configuration}.
* * @param subject the {@code Subject} to authenticate, * or {@code null}.
* * @param callbackHandler the {@code CallbackHandler} object used by * LoginModules to communicate with the user, or {@code null}. *
* * @param config the {@code Configuration} that lists the * login modules to be called to perform the authentication, * or {@code null}. * * @exception LoginException if the caller-specified {@code name} * does not appear in the {@code Configuration} * and there is no {@code Configuration} entry * for "other". *
* @exception SecurityException if a SecurityManager is set, * config is {@code null}, * and either the caller does not have * AuthPermission("createLoginContext.name"), * or if a configuration entry for name 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; if (config != null) { creatorAcc = java.security.AccessController.getContext(); } init(name); if (subject != null) { this.subject = subject; subjectProvided = true; } if (callbackHandler == null) { loadDefaultCallbackHandler(); } else if (creatorAcc == null) { this.callbackHandler = new SecureCallbackHandler (java.security.AccessController.getContext(), callbackHandler); } else { this.callbackHandler = callbackHandler; } } /** * Perform the authentication. * *
This method invokes the {@code login} method for each * LoginModule configured for the name specified to the * {@code LoginContext} constructor, as determined by the login * {@code Configuration}. Each {@code LoginModule} * then performs its respective type of authentication * (username/password, smart card pin verification, etc.). * *
This method completes a 2-phase authentication process by * calling each configured LoginModule's {@code commit} method * if the overall authentication succeeded (the relevant REQUIRED, * REQUISITE, SUFFICIENT, and OPTIONAL LoginModules succeeded), * or by calling each configured LoginModule's {@code abort} method * if the overall authentication failed. If authentication succeeded, * each successful LoginModule's {@code commit} method associates * the relevant Principals and Credentials with the {@code Subject}. * If authentication failed, each LoginModule's {@code abort} method * removes/destroys any previously stored state. * *
If the {@code commit} phase of the authentication process * fails, then the overall authentication fails and this method * invokes the {@code abort} method for each configured * {@code LoginModule}. * *
If the {@code abort} phase * fails for any reason, then this method propagates the * original exception thrown either during the {@code login} phase * or the {@code commit} phase. In either case, the overall * authentication fails. * *
In the case where multiple LoginModules fail, * this method propagates the exception raised by the first * {@code LoginModule} which failed. * *
Note that if this method enters the {@code abort} phase * (either the {@code login} or {@code commit} phase failed), * this method invokes all LoginModules configured for the * application regardless of their respective {@code Configuration} * flag parameters. Essentially this means that {@code Requisite} * and {@code Sufficient} semantics are ignored during the * {@code abort} phase. This guarantees that proper cleanup * and state restoration can take place. * *
* * @exception LoginException if the authentication fails. */ public void login() throws LoginException { loginSucceeded = false; if (subject == null) { subject = new Subject(); } try { // module invoked in doPrivileged invokePriv(LOGIN_METHOD); invokePriv(COMMIT_METHOD); loginSucceeded = true; } catch (LoginException le) { try { invokePriv(ABORT_METHOD); } catch (LoginException le2) { throw le; } throw le; } } /** * Logout the {@code Subject}. * *
This method invokes the {@code logout} method for each * {@code LoginModule} configured for this {@code LoginContext}. * Each {@code LoginModule} performs its respective logout procedure * which may include removing/destroying * {@code Principal} and {@code Credential} information * from the {@code Subject} and state cleanup. * *
Note that this method invokes all LoginModules configured for the * application regardless of their respective * {@code Configuration} flag parameters. Essentially this means * that {@code Requisite} and {@code Sufficient} semantics are * ignored for this method. This guarantees that proper cleanup * and state restoration can take place. * *
* * @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")); } // module invoked in doPrivileged invokePriv(LOGOUT_METHOD); } /** * Return the authenticated Subject. * *
*
* @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 restricted
* by creatorAcc (may be null).
*
* 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