/* * Copyright (c) 1998, 2010, 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 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 Configuration
specifies the authentication technology, or
* 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 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 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 login
method to authenticate
* a Subject
. The 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 login
method returns without
* throwing an exception, then the overall authentication succeeded.
* The caller can then retrieve
* the newly authenticated Subject by invoking the
* getSubject
method. Principals and Credentials associated
* with the Subject may be retrieved by invoking the Subject's
* respective getPrincipals
, getPublicCredentials
,
* and getPrivateCredentials
methods.
*
*
To logout the Subject, the caller calls
* the logout
method. As with the login
* method, this logout
method invokes the 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: *
Subject
* *
null
Subject
* and a null
value is permitted,
* the LoginContext instantiates a new Subject.
* *
*
Configuration
*
* If the constructor does not have a Configuration
* input parameter, or if the caller specifies a 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 *
Configuration.getAppConfigurationEntry
method.
* If the Configuration has no entries for the specified name,
* then the LoginContext
calls
* getAppConfigurationEntry
with the name, "other"
* (the default entry name). If there is no entry for "other",
* then a LoginException
is thrown.
* *
AccessController.doPrivileged
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.
* *
AccessControlContext
for the caller,
* and invokes the configured modules from within an
* AccessController.doPrivileged 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.
* *
CallbackHandler
* *
null
* CallbackHandler object (and a null
value is permitted),
* the LoginContext queries the
* auth.login.defaultCallbackHandler 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.
*
* *
handle
method implementation invokes the
* specified CallbackHandler's handle
method in a
* java.security.AccessController.doPrivileged
call
* constrained by the caller's current AccessControlContext
.
* Note that Security Properties
* (such as
* @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
*
* @param name the name used as the index into the
*
*
* @param subject the
* @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
*
* @param name the name used as the index into the
*
*
* @param callbackHandler the
* @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
*
* @param name the name used as the index into the
*
*
* @param subject the
*
* @param callbackHandler the
* @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
*
* @param name the name used as the index into the caller-specified
*
*
* @param subject the
*
* @param callbackHandler the
*
* @param config the
* @exception SecurityException if a SecurityManager is set,
* config is This method invokes the This method completes a 2-phase authentication process by
* calling each configured LoginModule's If the If the In the case where multiple LoginModules fail,
* this method propagates the exception raised by the first
* Note that if this method enters the
*
* @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 This method invokes the Note that this method invokes all LoginModules configured for the
* application regardless of their respective
*
*
* @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.
*
*
*
* @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.PrivilegedExceptionActionauth.login.defaultCallbackHandler
)
* can be set programmatically via the
* java.security.Security
class,
* or statically in the Java security properties file located in the
* file named <JAVA_HOME>/lib/security/java.security.
* <JAVA_HOME> refers to the value of the java.home system property,
* and specifies the directory where the JRE is installed.
*
* @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 = java.security.AccessController.doPrivileged
(new java.security.PrivilegedActionLoginContext
object with a name.
*
* @param name the name used as the index into the
* Configuration
.
*
* @exception LoginException if the caller-specified name
* does not appear in the Configuration
* and there is no Configuration
entry
* for "other", or if the
* auth.login.defaultCallbackHandler
* security property was set, but the implementation
* class could not be loaded.
* LoginContext
object with a name
* and a Subject
object.
*
* Configuration
. Subject
to authenticate.
*
* @exception LoginException if the caller-specified name
* does not appear in the Configuration
* and there is no Configuration
entry
* for "other", if the caller-specified subject
* is null
, or if the
* auth.login.defaultCallbackHandler
* security property was set, but the implementation
* class could not be loaded.
* LoginContext
object with a name
* and a CallbackHandler
object.
*
* Configuration
. CallbackHandler
object used by
* LoginModules to communicate with the user.
*
* @exception LoginException if the caller-specified name
* does not appear in the Configuration
* and there is no Configuration
entry
* for "other", or if the caller-specified
* callbackHandler
is null
.
* LoginContext
object with a name,
* a Subject
to be authenticated, and a
* CallbackHandler
object.
*
* Configuration
. Subject
to authenticate. CallbackHandler
object used by
* LoginModules to communicate with the user.
*
* @exception LoginException if the caller-specified name
* does not appear in the Configuration
* and there is no Configuration
entry
* for "other", or if the caller-specified
* subject
is null
,
* or if the caller-specified
* callbackHandler
is null
.
* LoginContext
object with a name,
* a Subject
to be authenticated,
* a CallbackHandler
object, and a login
* Configuration
.
*
* Configuration
. Subject
to authenticate,
* or null
. CallbackHandler
object used by
* LoginModules to communicate with the user, or null
.
* Configuration
that lists the
* login modules to be called to perform the authentication,
* or null
.
*
* @exception LoginException if the caller-specified name
* does not appear in the Configuration
* and there is no Configuration
entry
* for "other".
* 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;
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.
*
* login
method for each
* LoginModule configured for the name specified to the
* LoginContext
constructor, as determined by the login
* Configuration
. Each LoginModule
* then performs its respective type of authentication
* (username/password, smart card pin verification, etc.).
*
* commit
method
* if the overall authentication succeeded (the relevant REQUIRED,
* REQUISITE, SUFFICIENT, and OPTIONAL LoginModules succeeded),
* or by calling each configured LoginModule's abort
method
* if the overall authentication failed. If authentication succeeded,
* each successful LoginModule's commit
method associates
* the relevant Principals and Credentials with the Subject
.
* If authentication failed, each LoginModule's abort
method
* removes/destroys any previously stored state.
*
* commit
phase of the authentication process
* fails, then the overall authentication fails and this method
* invokes the abort
method for each configured
* LoginModule
.
*
* abort
phase
* fails for any reason, then this method propagates the
* original exception thrown either during the login
phase
* or the commit
phase. In either case, the overall
* authentication fails.
*
* LoginModule
which failed.
*
* abort
phase
* (either the login
or commit
phase failed),
* this method invokes all LoginModules configured for the
* application regardless of their respective Configuration
* flag parameters. Essentially this means that Requisite
* and Sufficient
semantics are ignored during the
* abort
phase. This guarantees that proper cleanup
* and state restoration can take place.
*
* Subject
.
*
* logout
method for each
* LoginModule
configured for this LoginContext
.
* Each LoginModule
performs its respective logout procedure
* which may include removing/destroying
* Principal
and Credential
information
* from the Subject
and state cleanup.
*
* Configuration
flag parameters. Essentially this means
* that Requisite
and Sufficient
semantics are
* ignored for this method. This guarantees that proper cleanup
* and state restoration can take place.
*
*