0001 /*
0002 * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved.
0003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004 *
0005 * This code is free software; you can redistribute it and/or modify it
0006 * under the terms of the GNU General Public License version 2 only, as
0007 * published by the Free Software Foundation. Sun designates this
0008 * particular file as subject to the "Classpath" exception as provided
0009 * by Sun in the LICENSE file that accompanied this code.
0010 *
0011 * This code is distributed in the hope that it will be useful, but WITHOUT
0012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014 * version 2 for more details (a copy is included in the LICENSE file that
0015 * accompanied this code).
0016 *
0017 * You should have received a copy of the GNU General Public License version
0018 * 2 along with this work; if not, write to the Free Software Foundation,
0019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020 *
0021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022 * CA 95054 USA or visit www.sun.com if you need additional information or
0023 * have any questions.
0024 */
0025
0026 package javax.security.auth.login;
0027
0028 import java.lang.reflect.Constructor;
0029 import java.lang.reflect.Method;
0030 import java.lang.reflect.InvocationTargetException;
0031 import java.util.LinkedList;
0032 import java.util.Map;
0033 import java.util.HashMap;
0034 import java.text.MessageFormat;
0035 import javax.security.auth.Subject;
0036 import javax.security.auth.AuthPermission;
0037 import javax.security.auth.callback.*;
0038 import java.security.AccessController;
0039 import java.security.AccessControlContext;
0040 import sun.security.util.PendingException;
0041 import sun.security.util.ResourcesMgr;
0042
0043 /**
0044 * <p> The <code>LoginContext</code> class describes the basic methods used
0045 * to authenticate Subjects and provides a way to develop an
0046 * application independent of the underlying authentication technology.
0047 * A <code>Configuration</code> specifies the authentication technology, or
0048 * <code>LoginModule</code>, to be used with a particular application.
0049 * Different LoginModules can be plugged in under an application
0050 * without requiring any modifications to the application itself.
0051 *
0052 * <p> In addition to supporting <i>pluggable</i> authentication, this class
0053 * also supports the notion of <i>stacked</i> authentication.
0054 * Applications may be configured to use more than one
0055 * LoginModule. For example, one could
0056 * configure both a Kerberos LoginModule and a smart card
0057 * LoginModule under an application.
0058 *
0059 * <p> A typical caller instantiates a LoginContext with
0060 * a <i>name</i> and a <code>CallbackHandler</code>.
0061 * LoginContext uses the <i>name</i> as the index into a
0062 * Configuration to determine which LoginModules should be used,
0063 * and which ones must succeed in order for the overall authentication to
0064 * succeed. The <code>CallbackHandler</code> is passed to the underlying
0065 * LoginModules so they may communicate and interact with users
0066 * (prompting for a username and password via a graphical user interface,
0067 * for example).
0068 *
0069 * <p> Once the caller has instantiated a LoginContext,
0070 * it invokes the <code>login</code> method to authenticate
0071 * a <code>Subject</code>. The <code>login</code> method invokes
0072 * the configured modules to perform their respective types of authentication
0073 * (username/password, smart card pin verification, etc.).
0074 * Note that the LoginModules will not attempt authentication retries nor
0075 * introduce delays if the authentication fails.
0076 * Such tasks belong to the LoginContext caller.
0077 *
0078 * <p> If the <code>login</code> method returns without
0079 * throwing an exception, then the overall authentication succeeded.
0080 * The caller can then retrieve
0081 * the newly authenticated Subject by invoking the
0082 * <code>getSubject</code> method. Principals and Credentials associated
0083 * with the Subject may be retrieved by invoking the Subject's
0084 * respective <code>getPrincipals</code>, <code>getPublicCredentials</code>,
0085 * and <code>getPrivateCredentials</code> methods.
0086 *
0087 * <p> To logout the Subject, the caller calls
0088 * the <code>logout</code> method. As with the <code>login</code>
0089 * method, this <code>logout</code> method invokes the <code>logout</code>
0090 * method for the configured modules.
0091 *
0092 * <p> A LoginContext should not be used to authenticate
0093 * more than one Subject. A separate LoginContext
0094 * should be used to authenticate each different Subject.
0095 *
0096 * <p> The following documentation applies to all LoginContext constructors:
0097 * <ol>
0098 *
0099 * <li> <code>Subject</code>
0100 * <ul>
0101 * <li> If the constructor has a Subject
0102 * input parameter, the LoginContext uses the caller-specified
0103 * Subject object.
0104 * <p>
0105 * <li> If the caller specifies a <code>null</code> Subject
0106 * and a <code>null</code> value is permitted,
0107 * the LoginContext instantiates a new Subject.
0108 * <p>
0109 * <li> If the constructor does <b>not</b> have a Subject
0110 * input parameter, the LoginContext instantiates a new Subject.
0111 * <p>
0112 * </ul>
0113 *
0114 * <li> <code>Configuration</code>
0115 * <ul>
0116 * <li> If the constructor has a Configuration
0117 * input parameter and the caller specifies a non-null Configuration,
0118 * the LoginContext uses the caller-specified Configuration.
0119 * <p>
0120 * If the constructor does <b>not</b> have a Configuration
0121 * input parameter, or if the caller specifies a <code>null</code>
0122 * Configuration object, the constructor uses the following call to
0123 * get the installed Configuration:
0124 * <pre>
0125 * config = Configuration.getConfiguration();
0126 * </pre>
0127 * For both cases,
0128 * the <i>name</i> argument given to the constructor is passed to the
0129 * <code>Configuration.getAppConfigurationEntry</code> method.
0130 * If the Configuration has no entries for the specified <i>name</i>,
0131 * then the <code>LoginContext</code> calls
0132 * <code>getAppConfigurationEntry</code> with the name, "<i>other</i>"
0133 * (the default entry name). If there is no entry for "<i>other</i>",
0134 * then a <code>LoginException</code> is thrown.
0135 * <p>
0136 * <li> When LoginContext uses the installed Configuration, the caller
0137 * requires the createLoginContext.<em>name</em> and possibly
0138 * createLoginContext.other AuthPermissions. Furthermore, the
0139 * LoginContext will invoke configured modules from within an
0140 * <code>AccessController.doPrivileged</code> call so that modules that
0141 * perform security-sensitive tasks (such as connecting to remote hosts,
0142 * and updating the Subject) will require the respective permissions, but
0143 * the callers of the LoginContext will not require those permissions.
0144 * <p>
0145 * <li> When LoginContext uses a caller-specified Configuration, the caller
0146 * does not require any createLoginContext AuthPermission. The LoginContext
0147 * saves the <code>AccessControlContext</code> for the caller,
0148 * and invokes the configured modules from within an
0149 * <tt>AccessController.doPrivileged</tt> call constrained by that context.
0150 * This means the caller context (stored when the LoginContext was created)
0151 * must have sufficient permissions to perform any security-sensitive tasks
0152 * that the modules may perform.
0153 * <p>
0154 * </ul>
0155 *
0156 * <li> <code>CallbackHandler</code>
0157 * <ul>
0158 * <li> If the constructor has a CallbackHandler
0159 * input parameter, the LoginContext uses the caller-specified
0160 * CallbackHandler object.
0161 * <p>
0162 * <li> If the constructor does <b>not</b> have a CallbackHandler
0163 * input parameter, or if the caller specifies a <code>null</code>
0164 * CallbackHandler object (and a <code>null</code> value is permitted),
0165 * the LoginContext queries the
0166 * <i>auth.login.defaultCallbackHandler</i> security property
0167 * for the fully qualified class name of a default handler implementation.
0168 * If the security property is not set,
0169 * then the underlying modules will not have a
0170 * CallbackHandler for use in communicating
0171 * with users. The caller thus assumes that the configured
0172 * modules have alternative means for authenticating the user.
0173 *
0174 * <p>
0175 * <li> When the LoginContext uses the installed Configuration (instead of
0176 * a caller-specified Configuration, see above),
0177 * then this LoginContext must wrap any
0178 * caller-specified or default CallbackHandler implementation
0179 * in a new CallbackHandler implementation
0180 * whose <code>handle</code> method implementation invokes the
0181 * specified CallbackHandler's <code>handle</code> method in a
0182 * <code>java.security.AccessController.doPrivileged</code> call
0183 * constrained by the caller's current <code>AccessControlContext</code>.
0184 * </ul>
0185 * </ol>
0186 *
0187 * <p> Note that Security Properties
0188 * (such as <code>auth.login.defaultCallbackHandler</code>)
0189 * can be set programmatically via the
0190 * <code>java.security.Security</code> class,
0191 * or statically in the Java security properties file located in the
0192 * file named <JAVA_HOME>/lib/security/java.security.
0193 * <JAVA_HOME> refers to the value of the java.home system property,
0194 * and specifies the directory where the JRE is installed.
0195 *
0196 * @version 1.107, 05/05/07
0197 * @see java.security.Security
0198 * @see javax.security.auth.AuthPermission
0199 * @see javax.security.auth.Subject
0200 * @see javax.security.auth.callback.CallbackHandler
0201 * @see javax.security.auth.login.Configuration
0202 * @see javax.security.auth.spi.LoginModule
0203 */
0204 public class LoginContext {
0205
0206 private static final String INIT_METHOD = "initialize";
0207 private static final String LOGIN_METHOD = "login";
0208 private static final String COMMIT_METHOD = "commit";
0209 private static final String ABORT_METHOD = "abort";
0210 private static final String LOGOUT_METHOD = "logout";
0211 private static final String OTHER = "other";
0212 private static final String DEFAULT_HANDLER = "auth.login.defaultCallbackHandler";
0213 private Subject subject = null;
0214 private boolean subjectProvided = false;
0215 private boolean loginSucceeded = false;
0216 private CallbackHandler callbackHandler;
0217 private Map state = new HashMap();
0218
0219 private Configuration config;
0220 private boolean configProvided = false;
0221 private AccessControlContext creatorAcc = null;
0222 private ModuleInfo[] moduleStack;
0223 private ClassLoader contextClassLoader = null;
0224 private static final Class[] PARAMS = {};
0225
0226 // state saved in the event a user-specified asynchronous exception
0227 // was specified and thrown
0228
0229 private int moduleIndex = 0;
0230 private LoginException firstError = null;
0231 private LoginException firstRequiredError = null;
0232 private boolean success = false;
0233
0234 private static final sun.security.util.Debug debug = sun.security.util.Debug
0235 .getInstance("logincontext", "\t[LoginContext]");
0236
0237 private void init(String name) throws LoginException {
0238
0239 SecurityManager sm = System.getSecurityManager();
0240 if (sm != null && !configProvided) {
0241 sm.checkPermission(new AuthPermission("createLoginContext."
0242 + name));
0243 }
0244
0245 if (name == null)
0246 throw new LoginException(ResourcesMgr
0247 .getString("Invalid null input: name"));
0248
0249 // get the Configuration
0250 if (config == null) {
0251 config = java.security.AccessController
0252 .doPrivileged(new java.security.PrivilegedAction<Configuration>() {
0253 public Configuration run() {
0254 return Configuration.getConfiguration();
0255 }
0256 });
0257 }
0258
0259 // get the LoginModules configured for this application
0260 AppConfigurationEntry[] entries = config
0261 .getAppConfigurationEntry(name);
0262 if (entries == null) {
0263
0264 if (sm != null && !configProvided) {
0265 sm.checkPermission(new AuthPermission(
0266 "createLoginContext." + OTHER));
0267 }
0268
0269 entries = config.getAppConfigurationEntry(OTHER);
0270 if (entries == null) {
0271 MessageFormat form = new MessageFormat(
0272 ResourcesMgr
0273 .getString("No LoginModules configured for name"));
0274 Object[] source = { name };
0275 throw new LoginException(form.format(source));
0276 }
0277 }
0278 moduleStack = new ModuleInfo[entries.length];
0279 for (int i = 0; i < entries.length; i++) {
0280 // clone returned array
0281 moduleStack[i] = new ModuleInfo(
0282 new AppConfigurationEntry(entries[i]
0283 .getLoginModuleName(), entries[i]
0284 .getControlFlag(), entries[i].getOptions()),
0285 null);
0286 }
0287
0288 contextClassLoader = java.security.AccessController
0289 .doPrivileged(new java.security.PrivilegedAction<ClassLoader>() {
0290 public ClassLoader run() {
0291 return Thread.currentThread()
0292 .getContextClassLoader();
0293 }
0294 });
0295 }
0296
0297 private void loadDefaultCallbackHandler() throws LoginException {
0298
0299 // get the default handler class
0300 try {
0301
0302 final ClassLoader finalLoader = contextClassLoader;
0303
0304 this .callbackHandler = java.security.AccessController
0305 .doPrivileged(new java.security.PrivilegedExceptionAction<CallbackHandler>() {
0306 public CallbackHandler run() throws Exception {
0307 String defaultHandler = java.security.Security
0308 .getProperty(DEFAULT_HANDLER);
0309 if (defaultHandler == null
0310 || defaultHandler.length() == 0)
0311 return null;
0312 Class c = Class.forName(defaultHandler,
0313 true, finalLoader);
0314 return (CallbackHandler) c.newInstance();
0315 }
0316 });
0317 } catch (java.security.PrivilegedActionException pae) {
0318 throw new LoginException(pae.getException().toString());
0319 }
0320
0321 // secure it with the caller's ACC
0322 if (this .callbackHandler != null && !configProvided) {
0323 this .callbackHandler = new SecureCallbackHandler(
0324 java.security.AccessController.getContext(),
0325 this .callbackHandler);
0326 }
0327 }
0328
0329 /**
0330 * Instantiate a new <code>LoginContext</code> object with a name.
0331 *
0332 * @param name the name used as the index into the
0333 * <code>Configuration</code>.
0334 *
0335 * @exception LoginException if the caller-specified <code>name</code>
0336 * does not appear in the <code>Configuration</code>
0337 * and there is no <code>Configuration</code> entry
0338 * for "<i>other</i>", or if the
0339 * <i>auth.login.defaultCallbackHandler</i>
0340 * security property was set, but the implementation
0341 * class could not be loaded.
0342 * <p>
0343 * @exception SecurityException if a SecurityManager is set and
0344 * the caller does not have
0345 * AuthPermission("createLoginContext.<i>name</i>"),
0346 * or if a configuration entry for <i>name</i> does not exist and
0347 * the caller does not additionally have
0348 * AuthPermission("createLoginContext.other")
0349 */
0350 public LoginContext(String name) throws LoginException {
0351 init(name);
0352 loadDefaultCallbackHandler();
0353 }
0354
0355 /**
0356 * Instantiate a new <code>LoginContext</code> object with a name
0357 * and a <code>Subject</code> object.
0358 *
0359 * <p>
0360 *
0361 * @param name the name used as the index into the
0362 * <code>Configuration</code>. <p>
0363 *
0364 * @param subject the <code>Subject</code> to authenticate.
0365 *
0366 * @exception LoginException if the caller-specified <code>name</code>
0367 * does not appear in the <code>Configuration</code>
0368 * and there is no <code>Configuration</code> entry
0369 * for "<i>other</i>", if the caller-specified <code>subject</code>
0370 * is <code>null</code>, or if the
0371 * <i>auth.login.defaultCallbackHandler</i>
0372 * security property was set, but the implementation
0373 * class could not be loaded.
0374 * <p>
0375 * @exception SecurityException if a SecurityManager is set and
0376 * the caller does not have
0377 * AuthPermission("createLoginContext.<i>name</i>"),
0378 * or if a configuration entry for <i>name</i> does not exist and
0379 * the caller does not additionally have
0380 * AuthPermission("createLoginContext.other")
0381 */
0382 public LoginContext(String name, Subject subject)
0383 throws LoginException {
0384 init(name);
0385 if (subject == null)
0386 throw new LoginException(ResourcesMgr
0387 .getString("invalid null Subject provided"));
0388 this .subject = subject;
0389 subjectProvided = true;
0390 loadDefaultCallbackHandler();
0391 }
0392
0393 /**
0394 * Instantiate a new <code>LoginContext</code> object with a name
0395 * and a <code>CallbackHandler</code> object.
0396 *
0397 * <p>
0398 *
0399 * @param name the name used as the index into the
0400 * <code>Configuration</code>. <p>
0401 *
0402 * @param callbackHandler the <code>CallbackHandler</code> object used by
0403 * LoginModules to communicate with the user.
0404 *
0405 * @exception LoginException if the caller-specified <code>name</code>
0406 * does not appear in the <code>Configuration</code>
0407 * and there is no <code>Configuration</code> entry
0408 * for "<i>other</i>", or if the caller-specified
0409 * <code>callbackHandler</code> is <code>null</code>.
0410 * <p>
0411 * @exception SecurityException if a SecurityManager is set and
0412 * the caller does not have
0413 * AuthPermission("createLoginContext.<i>name</i>"),
0414 * or if a configuration entry for <i>name</i> does not exist and
0415 * the caller does not additionally have
0416 * AuthPermission("createLoginContext.other")
0417 */
0418 public LoginContext(String name, CallbackHandler callbackHandler)
0419 throws LoginException {
0420 init(name);
0421 if (callbackHandler == null)
0422 throw new LoginException(ResourcesMgr
0423 .getString("invalid null CallbackHandler provided"));
0424 this .callbackHandler = new SecureCallbackHandler(
0425 java.security.AccessController.getContext(),
0426 callbackHandler);
0427 }
0428
0429 /**
0430 * Instantiate a new <code>LoginContext</code> object with a name,
0431 * a <code>Subject</code> to be authenticated, and a
0432 * <code>CallbackHandler</code> object.
0433 *
0434 * <p>
0435 *
0436 * @param name the name used as the index into the
0437 * <code>Configuration</code>. <p>
0438 *
0439 * @param subject the <code>Subject</code> to authenticate. <p>
0440 *
0441 * @param callbackHandler the <code>CallbackHandler</code> object used by
0442 * LoginModules to communicate with the user.
0443 *
0444 * @exception LoginException if the caller-specified <code>name</code>
0445 * does not appear in the <code>Configuration</code>
0446 * and there is no <code>Configuration</code> entry
0447 * for "<i>other</i>", or if the caller-specified
0448 * <code>subject</code> is <code>null</code>,
0449 * or if the caller-specified
0450 * <code>callbackHandler</code> is <code>null</code>.
0451 * <p>
0452 * @exception SecurityException if a SecurityManager is set and
0453 * the caller does not have
0454 * AuthPermission("createLoginContext.<i>name</i>"),
0455 * or if a configuration entry for <i>name</i> does not exist and
0456 * the caller does not additionally have
0457 * AuthPermission("createLoginContext.other")
0458 */
0459 public LoginContext(String name, Subject subject,
0460 CallbackHandler callbackHandler) throws LoginException {
0461 this (name, subject);
0462 if (callbackHandler == null)
0463 throw new LoginException(ResourcesMgr
0464 .getString("invalid null CallbackHandler provided"));
0465 this .callbackHandler = new SecureCallbackHandler(
0466 java.security.AccessController.getContext(),
0467 callbackHandler);
0468 }
0469
0470 /**
0471 * Instantiate a new <code>LoginContext</code> object with a name,
0472 * a <code>Subject</code> to be authenticated,
0473 * a <code>CallbackHandler</code> object, and a login
0474 * <code>Configuration</code>.
0475 *
0476 * <p>
0477 *
0478 * @param name the name used as the index into the caller-specified
0479 * <code>Configuration</code>. <p>
0480 *
0481 * @param subject the <code>Subject</code> to authenticate,
0482 * or <code>null</code>. <p>
0483 *
0484 * @param callbackHandler the <code>CallbackHandler</code> object used by
0485 * LoginModules to communicate with the user, or <code>null</code>.
0486 * <p>
0487 *
0488 * @param config the <code>Configuration</code> that lists the
0489 * login modules to be called to perform the authentication,
0490 * or <code>null</code>.
0491 *
0492 * @exception LoginException if the caller-specified <code>name</code>
0493 * does not appear in the <code>Configuration</code>
0494 * and there is no <code>Configuration</code> entry
0495 * for "<i>other</i>".
0496 * <p>
0497 * @exception SecurityException if a SecurityManager is set,
0498 * <i>config</i> is <code>null</code>,
0499 * and either the caller does not have
0500 * AuthPermission("createLoginContext.<i>name</i>"),
0501 * or if a configuration entry for <i>name</i> does not exist and
0502 * the caller does not additionally have
0503 * AuthPermission("createLoginContext.other")
0504 *
0505 * @since 1.5
0506 */
0507 public LoginContext(String name, Subject subject,
0508 CallbackHandler callbackHandler, Configuration config)
0509 throws LoginException {
0510 this .config = config;
0511 configProvided = (config != null) ? true : false;
0512 if (configProvided) {
0513 creatorAcc = java.security.AccessController.getContext();
0514 }
0515
0516 init(name);
0517 if (subject != null) {
0518 this .subject = subject;
0519 subjectProvided = true;
0520 }
0521 if (callbackHandler == null) {
0522 loadDefaultCallbackHandler();
0523 } else if (!configProvided) {
0524 this .callbackHandler = new SecureCallbackHandler(
0525 java.security.AccessController.getContext(),
0526 callbackHandler);
0527 } else {
0528 this .callbackHandler = callbackHandler;
0529 }
0530 }
0531
0532 /**
0533 * Perform the authentication.
0534 *
0535 * <p> This method invokes the <code>login</code> method for each
0536 * LoginModule configured for the <i>name</i> specified to the
0537 * <code>LoginContext</code> constructor, as determined by the login
0538 * <code>Configuration</code>. Each <code>LoginModule</code>
0539 * then performs its respective type of authentication
0540 * (username/password, smart card pin verification, etc.).
0541 *
0542 * <p> This method completes a 2-phase authentication process by
0543 * calling each configured LoginModule's <code>commit</code> method
0544 * if the overall authentication succeeded (the relevant REQUIRED,
0545 * REQUISITE, SUFFICIENT, and OPTIONAL LoginModules succeeded),
0546 * or by calling each configured LoginModule's <code>abort</code> method
0547 * if the overall authentication failed. If authentication succeeded,
0548 * each successful LoginModule's <code>commit</code> method associates
0549 * the relevant Principals and Credentials with the <code>Subject</code>.
0550 * If authentication failed, each LoginModule's <code>abort</code> method
0551 * removes/destroys any previously stored state.
0552 *
0553 * <p> If the <code>commit</code> phase of the authentication process
0554 * fails, then the overall authentication fails and this method
0555 * invokes the <code>abort</code> method for each configured
0556 * <code>LoginModule</code>.
0557 *
0558 * <p> If the <code>abort</code> phase
0559 * fails for any reason, then this method propagates the
0560 * original exception thrown either during the <code>login</code> phase
0561 * or the <code>commit</code> phase. In either case, the overall
0562 * authentication fails.
0563 *
0564 * <p> In the case where multiple LoginModules fail,
0565 * this method propagates the exception raised by the first
0566 * <code>LoginModule</code> which failed.
0567 *
0568 * <p> Note that if this method enters the <code>abort</code> phase
0569 * (either the <code>login</code> or <code>commit</code> phase failed),
0570 * this method invokes all LoginModules configured for the
0571 * application regardless of their respective <code>Configuration</code>
0572 * flag parameters. Essentially this means that <code>Requisite</code>
0573 * and <code>Sufficient</code> semantics are ignored during the
0574 * <code>abort</code> phase. This guarantees that proper cleanup
0575 * and state restoration can take place.
0576 *
0577 * <p>
0578 *
0579 * @exception LoginException if the authentication fails.
0580 */
0581 public void login() throws LoginException {
0582
0583 loginSucceeded = false;
0584
0585 if (subject == null) {
0586 subject = new Subject();
0587 }
0588
0589 try {
0590 if (configProvided) {
0591 // module invoked in doPrivileged with creatorAcc
0592 invokeCreatorPriv(LOGIN_METHOD);
0593 invokeCreatorPriv(COMMIT_METHOD);
0594 } else {
0595 // module invoked in doPrivileged
0596 invokePriv(LOGIN_METHOD);
0597 invokePriv(COMMIT_METHOD);
0598 }
0599 loginSucceeded = true;
0600 } catch (LoginException le) {
0601 try {
0602 if (configProvided) {
0603 invokeCreatorPriv(ABORT_METHOD);
0604 } else {
0605 invokePriv(ABORT_METHOD);
0606 }
0607 } catch (LoginException le2) {
0608 throw le;
0609 }
0610 throw le;
0611 }
0612 }
0613
0614 /**
0615 * Logout the <code>Subject</code>.
0616 *
0617 * <p> This method invokes the <code>logout</code> method for each
0618 * <code>LoginModule</code> configured for this <code>LoginContext</code>.
0619 * Each <code>LoginModule</code> performs its respective logout procedure
0620 * which may include removing/destroying
0621 * <code>Principal</code> and <code>Credential</code> information
0622 * from the <code>Subject</code> and state cleanup.
0623 *
0624 * <p> Note that this method invokes all LoginModules configured for the
0625 * application regardless of their respective
0626 * <code>Configuration</code> flag parameters. Essentially this means
0627 * that <code>Requisite</code> and <code>Sufficient</code> semantics are
0628 * ignored for this method. This guarantees that proper cleanup
0629 * and state restoration can take place.
0630 *
0631 * <p>
0632 *
0633 * @exception LoginException if the logout fails.
0634 */
0635 public void logout() throws LoginException {
0636 if (subject == null) {
0637 throw new LoginException(
0638 ResourcesMgr
0639 .getString("null subject - logout called before login"));
0640 }
0641
0642 if (configProvided) {
0643 // module invoked in doPrivileged with creatorAcc
0644 invokeCreatorPriv(LOGOUT_METHOD);
0645 } else {
0646 // module invoked in doPrivileged
0647 invokePriv(LOGOUT_METHOD);
0648 }
0649 }
0650
0651 /**
0652 * Return the authenticated Subject.
0653 *
0654 * <p>
0655 *
0656 * @return the authenticated Subject. If the caller specified a
0657 * Subject to this LoginContext's constructor,
0658 * this method returns the caller-specified Subject.
0659 * If a Subject was not specified and authentication succeeds,
0660 * this method returns the Subject instantiated and used for
0661 * authentication by this LoginContext.
0662 * If a Subject was not specified, and authentication fails or
0663 * has not been attempted, this method returns null.
0664 */
0665 public Subject getSubject() {
0666 if (!loginSucceeded && !subjectProvided)
0667 return null;
0668 return subject;
0669 }
0670
0671 private void clearState() {
0672 moduleIndex = 0;
0673 firstError = null;
0674 firstRequiredError = null;
0675 success = false;
0676 }
0677
0678 private void throwException(LoginException originalError,
0679 LoginException le) throws LoginException {
0680
0681 // first clear state
0682 clearState();
0683
0684 // throw the exception
0685 LoginException error = (originalError != null) ? originalError
0686 : le;
0687 throw error;
0688 }
0689
0690 /**
0691 * Invokes the login, commit, and logout methods
0692 * from a LoginModule inside a doPrivileged block.
0693 *
0694 * This version is called if the caller did not instantiate
0695 * the LoginContext with a Configuration object.
0696 */
0697 private void invokePriv(final String methodName)
0698 throws LoginException {
0699 try {
0700 java.security.AccessController
0701 .doPrivileged(new java.security.PrivilegedExceptionAction<Void>() {
0702 public Void run() throws LoginException {
0703 invoke(methodName);
0704 return null;
0705 }
0706 });
0707 } catch (java.security.PrivilegedActionException pae) {
0708 throw (LoginException) pae.getException();
0709 }
0710 }
0711
0712 /**
0713 * Invokes the login, commit, and logout methods
0714 * from a LoginModule inside a doPrivileged block restricted
0715 * by creatorAcc
0716 *
0717 * This version is called if the caller instantiated
0718 * the LoginContext with a Configuration object.
0719 */
0720 private void invokeCreatorPriv(final String methodName)
0721 throws LoginException {
0722 try {
0723 java.security.AccessController
0724 .doPrivileged(
0725 new java.security.PrivilegedExceptionAction<Void>() {
0726 public Void run() throws LoginException {
0727 invoke(methodName);
0728 return null;
0729 }
0730 }, creatorAcc);
0731 } catch (java.security.PrivilegedActionException pae) {
0732 throw (LoginException) pae.getException();
0733 }
0734 }
0735
0736 private void invoke(String methodName) throws LoginException {
0737
0738 // start at moduleIndex
0739 // - this can only be non-zero if methodName is LOGIN_METHOD
0740
0741 for (int i = moduleIndex; i < moduleStack.length; i++, moduleIndex++) {
0742 try {
0743
0744 int mIndex = 0;
0745 Method[] methods = null;
0746
0747 if (moduleStack[i].module != null) {
0748 methods = moduleStack[i].module.getClass()
0749 .getMethods();
0750 } else {
0751
0752 // instantiate the LoginModule
0753 Class c = Class.forName(moduleStack[i].entry
0754 .getLoginModuleName(), true,
0755 contextClassLoader);
0756
0757 Constructor constructor = c.getConstructor(PARAMS);
0758 Object[] args = {};
0759
0760 // allow any object to be a LoginModule
0761 // as long as it conforms to the interface
0762 moduleStack[i].module = constructor
0763 .newInstance(args);
0764
0765 methods = moduleStack[i].module.getClass()
0766 .getMethods();
0767
0768 // call the LoginModule's initialize method
0769 for (mIndex = 0; mIndex < methods.length; mIndex++) {
0770 if (methods[mIndex].getName().equals(
0771 INIT_METHOD))
0772 break;
0773 }
0774
0775 Object[] initArgs = { subject, callbackHandler,
0776 state, moduleStack[i].entry.getOptions() };
0777 // invoke the LoginModule initialize method
0778 methods[mIndex].invoke(moduleStack[i].module,
0779 initArgs);
0780 }
0781
0782 // find the requested method in the LoginModule
0783 for (mIndex = 0; mIndex < methods.length; mIndex++) {
0784 if (methods[mIndex].getName().equals(methodName))
0785 break;
0786 }
0787
0788 // set up the arguments to be passed to the LoginModule method
0789 Object[] args = {};
0790
0791 // invoke the LoginModule method
0792 boolean status = ((Boolean) methods[mIndex].invoke(
0793 moduleStack[i].module, args)).booleanValue();
0794
0795 if (status == true) {
0796
0797 // if SUFFICIENT, return if no prior REQUIRED errors
0798 if (!methodName.equals(ABORT_METHOD)
0799 && !methodName.equals(LOGOUT_METHOD)
0800 && moduleStack[i].entry.getControlFlag() == AppConfigurationEntry.LoginModuleControlFlag.SUFFICIENT
0801 && firstRequiredError == null) {
0802
0803 // clear state
0804 clearState();
0805
0806 if (debug != null)
0807 debug.println(methodName
0808 + " SUFFICIENT success");
0809 return;
0810 }
0811
0812 if (debug != null)
0813 debug.println(methodName + " success");
0814 success = true;
0815 } else {
0816 if (debug != null)
0817 debug.println(methodName + " ignored");
0818 }
0819
0820 } catch (NoSuchMethodException nsme) {
0821 MessageFormat form = new MessageFormat(
0822 ResourcesMgr
0823 .getString("unable to instantiate LoginModule, module, because "
0824 + "it does not provide a no-argument constructor"));
0825 Object[] source = { moduleStack[i].entry
0826 .getLoginModuleName() };
0827 throwException(null, new LoginException(form
0828 .format(source)));
0829 } catch (InstantiationException ie) {
0830 throwException(
0831 null,
0832 new LoginException(
0833 ResourcesMgr
0834 .getString("unable to instantiate LoginModule: ")
0835 + ie.getMessage()));
0836 } catch (ClassNotFoundException cnfe) {
0837 throwException(
0838 null,
0839 new LoginException(
0840 ResourcesMgr
0841 .getString("unable to find LoginModule class: ")
0842 + cnfe.getMessage()));
0843 } catch (IllegalAccessException iae) {
0844 throwException(null, new LoginException(ResourcesMgr
0845 .getString("unable to access LoginModule: ")
0846 + iae.getMessage()));
0847 } catch (InvocationTargetException ite) {
0848
0849 // failure cases
0850
0851 LoginException le;
0852
0853 if (ite.getCause() instanceof PendingException
0854 && methodName.equals(LOGIN_METHOD)) {
0855
0856 // XXX
0857 //
0858 // if a module's LOGIN_METHOD threw a PendingException
0859 // then immediately throw it.
0860 //
0861 // when LoginContext is called again,
0862 // the module that threw the exception is invoked first
0863 // (the module list is not invoked from the start).
0864 // previously thrown exception state is still present.
0865 //
0866 // it is assumed that the module which threw
0867 // the exception can have its
0868 // LOGIN_METHOD invoked twice in a row
0869 // without any commit/abort in between.
0870 //
0871 // in all cases when LoginContext returns
0872 // (either via natural return or by throwing an exception)
0873 // we need to call clearState before returning.
0874 // the only time that is not true is in this case -
0875 // do not call throwException here.
0876
0877 throw (PendingException) ite.getCause();
0878
0879 } else if (ite.getCause() instanceof LoginException) {
0880
0881 le = (LoginException) ite.getCause();
0882
0883 } else if (ite.getCause() instanceof SecurityException) {
0884
0885 // do not want privacy leak
0886 // (e.g., sensitive file path in exception msg)
0887
0888 le = new LoginException("Security Exception");
0889 le.initCause(new SecurityException());
0890 if (debug != null) {
0891 debug
0892 .println("original security exception with detail msg "
0893 + "replaced by new exception with empty detail msg");
0894 debug.println("original security exception: "
0895 + ite.getCause().toString());
0896 }
0897 } else {
0898
0899 // capture an unexpected LoginModule exception
0900 java.io.StringWriter sw = new java.io.StringWriter();
0901 ite.getCause().printStackTrace(
0902 new java.io.PrintWriter(sw));
0903 sw.flush();
0904 le = new LoginException(sw.toString());
0905 }
0906
0907 if (moduleStack[i].entry.getControlFlag() == AppConfigurationEntry.LoginModuleControlFlag.REQUISITE) {
0908
0909 if (debug != null)
0910 debug
0911 .println(methodName
0912 + " REQUISITE failure");
0913
0914 // if REQUISITE, then immediately throw an exception
0915 if (methodName.equals(ABORT_METHOD)
0916 || methodName.equals(LOGOUT_METHOD)) {
0917 if (firstRequiredError == null)
0918 firstRequiredError = le;
0919 } else {
0920 throwException(firstRequiredError, le);
0921 }
0922
0923 } else if (moduleStack[i].entry.getControlFlag() == AppConfigurationEntry.LoginModuleControlFlag.REQUIRED) {
0924
0925 if (debug != null)
0926 debug.println(methodName + " REQUIRED failure");
0927
0928 // mark down that a REQUIRED module failed
0929 if (firstRequiredError == null)
0930 firstRequiredError = le;
0931
0932 } else {
0933
0934 if (debug != null)
0935 debug.println(methodName + " OPTIONAL failure");
0936
0937 // mark down that an OPTIONAL module failed
0938 if (firstError == null)
0939 firstError = le;
0940 }
0941 }
0942 }
0943
0944 // we went thru all the LoginModules.
0945 if (firstRequiredError != null) {
0946 // a REQUIRED module failed -- return the error
0947 throwException(firstRequiredError, null);
0948 } else if (success == false && firstError != null) {
0949 // no module succeeded -- return the first error
0950 throwException(firstError, null);
0951 } else if (success == false) {
0952 // no module succeeded -- all modules were IGNORED
0953 throwException(new LoginException(ResourcesMgr
0954 .getString("Login Failure: all modules ignored")),
0955 null);
0956 } else {
0957 // success
0958
0959 clearState();
0960 return;
0961 }
0962 }
0963
0964 /**
0965 * Wrap the caller-specified CallbackHandler in our own
0966 * and invoke it within a privileged block, constrained by
0967 * the caller's AccessControlContext.
0968 */
0969 private static class SecureCallbackHandler implements
0970 CallbackHandler {
0971
0972 private final java.security.AccessControlContext acc;
0973 private final CallbackHandler ch;
0974
0975 SecureCallbackHandler(java.security.AccessControlContext acc,
0976 CallbackHandler ch) {
0977 this .acc = acc;
0978 this .ch = ch;
0979 }
0980
0981 public void handle(final Callback[] callbacks)
0982 throws java.io.IOException,
0983 UnsupportedCallbackException {
0984 try {
0985 java.security.AccessController
0986 .doPrivileged(
0987 new java.security.PrivilegedExceptionAction<Void>() {
0988 public Void run()
0989 throws java.io.IOException,
0990 UnsupportedCallbackException {
0991 ch.handle(callbacks);
0992 return null;
0993 }
0994 }, acc);
0995 } catch (java.security.PrivilegedActionException pae) {
0996 if (pae.getException() instanceof java.io.IOException) {
0997 throw (java.io.IOException) pae.getException();
0998 } else {
0999 throw (UnsupportedCallbackException) pae
1000 .getException();
1001 }
1002 }
1003 }
1004 }
1005
1006 /**
1007 * LoginModule information -
1008 * incapsulates Configuration info and actual module instances
1009 */
1010 private static class ModuleInfo {
1011 AppConfigurationEntry entry;
1012 Object module;
1013
1014 ModuleInfo(AppConfigurationEntry newEntry, Object newModule) {
1015 this.entry = newEntry;
1016 this.module = newModule;
1017 }
1018 }
1019 }
|