1 /*
   2  * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javax.security.auth.login;
  27 
  28 import java.lang.reflect.Constructor;
  29 import java.lang.reflect.Method;
  30 import java.lang.reflect.InvocationTargetException;
  31 import java.util.LinkedList;
  32 import java.util.Map;
  33 import java.util.HashMap;
  34 import java.text.MessageFormat;
  35 import javax.security.auth.Subject;
  36 import javax.security.auth.AuthPermission;
  37 import javax.security.auth.callback.*;
  38 import java.security.AccessController;
  39 import java.security.AccessControlContext;
  40 import sun.security.util.PendingException;
  41 import sun.security.util.ResourcesMgr;
  42 
  43 /**
  44  * <p> The {@code LoginContext} class describes the basic methods used
  45  * to authenticate Subjects and provides a way to develop an
  46  * application independent of the underlying authentication technology.
  47  * A {@code Configuration} specifies the authentication technology, or
  48  * {@code LoginModule}, to be used with a particular application.
  49  * Different LoginModules can be plugged in under an application
  50  * without requiring any modifications to the application itself.
  51  *
  52  * <p> In addition to supporting <i>pluggable</i> authentication, this class
  53  * also supports the notion of <i>stacked</i> authentication.
  54  * Applications may be configured to use more than one
  55  * LoginModule.  For example, one could
  56  * configure both a Kerberos LoginModule and a smart card
  57  * LoginModule under an application.
  58  *
  59  * <p> A typical caller instantiates a LoginContext with
  60  * a <i>name</i> and a {@code CallbackHandler}.
  61  * LoginContext uses the <i>name</i> as the index into a
  62  * Configuration to determine which LoginModules should be used,
  63  * and which ones must succeed in order for the overall authentication to
  64  * succeed.  The {@code CallbackHandler} is passed to the underlying
  65  * LoginModules so they may communicate and interact with users
  66  * (prompting for a username and password via a graphical user interface,
  67  * for example).
  68  *
  69  * <p> Once the caller has instantiated a LoginContext,
  70  * it invokes the {@code login} method to authenticate
  71  * a {@code Subject}.  The {@code login} method invokes
  72  * the configured modules to perform their respective types of authentication
  73  * (username/password, smart card pin verification, etc.).
  74  * Note that the LoginModules will not attempt authentication retries nor
  75  * introduce delays if the authentication fails.
  76  * Such tasks belong to the LoginContext caller.
  77  *
  78  * <p> If the {@code login} method returns without
  79  * throwing an exception, then the overall authentication succeeded.
  80  * The caller can then retrieve
  81  * the newly authenticated Subject by invoking the
  82  * {@code getSubject} method.  Principals and Credentials associated
  83  * with the Subject may be retrieved by invoking the Subject's
  84  * respective {@code getPrincipals}, {@code getPublicCredentials},
  85  * and {@code getPrivateCredentials} methods.
  86  *
  87  * <p> To logout the Subject, the caller calls
  88  * the {@code logout} method.  As with the {@code login}
  89  * method, this {@code logout} method invokes the {@code logout}
  90  * method for the configured modules.
  91  *
  92  * <p> A LoginContext should not be used to authenticate
  93  * more than one Subject.  A separate LoginContext
  94  * should be used to authenticate each different Subject.
  95  *
  96  * <p> The following documentation applies to all LoginContext constructors:
  97  * <ol>
  98  *
  99  * <li> {@code Subject}
 100  * <ul>
 101  * <li> If the constructor has a Subject
 102  * input parameter, the LoginContext uses the caller-specified
 103  * Subject object.
 104  * <p>
 105  * <li> If the caller specifies a {@code null} Subject
 106  * and a {@code null} value is permitted,
 107  * the LoginContext instantiates a new Subject.
 108  * <p>
 109  * <li> If the constructor does <b>not</b> have a Subject
 110  * input parameter, the LoginContext instantiates a new Subject.
 111  * <p>
 112  * </ul>
 113  *
 114  * <li> {@code Configuration}
 115  * <ul>
 116  * <li> If the constructor has a Configuration
 117  * input parameter and the caller specifies a non-null Configuration,
 118  * the LoginContext uses the caller-specified Configuration.
 119  * <p>
 120  * If the constructor does <b>not</b> have a Configuration
 121  * input parameter, or if the caller specifies a {@code null}
 122  * Configuration object, the constructor uses the following call to
 123  * get the installed Configuration:
 124  * <pre>
 125  *      config = Configuration.getConfiguration();
 126  * </pre>
 127  * For both cases,
 128  * the <i>name</i> argument given to the constructor is passed to the
 129  * {@code Configuration.getAppConfigurationEntry} method.
 130  * If the Configuration has no entries for the specified <i>name</i>,
 131  * then the {@code LoginContext} calls
 132  * {@code getAppConfigurationEntry} with the name, "<i>other</i>"
 133  * (the default entry name).  If there is no entry for "<i>other</i>",
 134  * then a {@code LoginException} is thrown.
 135  * <p>
 136  * <li> When LoginContext uses the installed Configuration, the caller
 137  * requires the createLoginContext.<em>name</em> and possibly
 138  * createLoginContext.other AuthPermissions. Furthermore, the
 139  * LoginContext will invoke configured modules from within an
 140  * {@code AccessController.doPrivileged} call so that modules that
 141  * perform security-sensitive tasks (such as connecting to remote hosts,
 142  * and updating the Subject) will require the respective permissions, but
 143  * the callers of the LoginContext will not require those permissions.
 144  * <p>
 145  * <li> When LoginContext uses a caller-specified Configuration, the caller
 146  * does not require any createLoginContext AuthPermission.  The LoginContext
 147  * saves the {@code AccessControlContext} for the caller,
 148  * and invokes the configured modules from within an
 149  * {@code AccessController.doPrivileged} call constrained by that context.
 150  * This means the caller context (stored when the LoginContext was created)
 151  * must have sufficient permissions to perform any security-sensitive tasks
 152  * that the modules may perform.
 153  * <p>
 154  * </ul>
 155  *
 156  * <li> {@code CallbackHandler}
 157  * <ul>
 158  * <li> If the constructor has a CallbackHandler
 159  * input parameter, the LoginContext uses the caller-specified
 160  * CallbackHandler object.
 161  * <p>
 162  * <li> If the constructor does <b>not</b> have a CallbackHandler
 163  * input parameter, or if the caller specifies a {@code null}
 164  * CallbackHandler object (and a {@code null} value is permitted),
 165  * the LoginContext queries the
 166  * {@code auth.login.defaultCallbackHandler} security property for the
 167  * fully qualified class name of a default handler
 168  * implementation. If the security property is not set,
 169  * then the underlying modules will not have a
 170  * CallbackHandler for use in communicating
 171  * with users.  The caller thus assumes that the configured
 172  * modules have alternative means for authenticating the user.
 173  *
 174  * <p>
 175  * <li> When the LoginContext uses the installed Configuration (instead of
 176  * a caller-specified Configuration, see above),
 177  * then this LoginContext must wrap any
 178  * caller-specified or default CallbackHandler implementation
 179  * in a new CallbackHandler implementation
 180  * whose {@code handle} method implementation invokes the
 181  * specified CallbackHandler's {@code handle} method in a
 182  * {@code java.security.AccessController.doPrivileged} call
 183  * constrained by the caller's current {@code AccessControlContext}.
 184  * </ul>
 185  * </ol>
 186  *
 187  * @see java.security.Security
 188  * @see javax.security.auth.AuthPermission
 189  * @see javax.security.auth.Subject
 190  * @see javax.security.auth.callback.CallbackHandler
 191  * @see javax.security.auth.login.Configuration
 192  * @see javax.security.auth.spi.LoginModule
 193  * @see java.security.Security security properties
 194  */
 195 public class LoginContext {
 196 
 197     private static final String INIT_METHOD             = "initialize";
 198     private static final String LOGIN_METHOD            = "login";
 199     private static final String COMMIT_METHOD           = "commit";
 200     private static final String ABORT_METHOD            = "abort";
 201     private static final String LOGOUT_METHOD           = "logout";
 202     private static final String OTHER                   = "other";
 203     private static final String DEFAULT_HANDLER         =
 204                                 "auth.login.defaultCallbackHandler";
 205     private Subject subject = null;
 206     private boolean subjectProvided = false;
 207     private boolean loginSucceeded = false;
 208     private CallbackHandler callbackHandler;
 209     private Map<String,?> state = new HashMap<String,Object>();
 210 
 211     private Configuration config;
 212     private boolean configProvided = false;
 213     private AccessControlContext creatorAcc = null;
 214     private ModuleInfo[] moduleStack;
 215     private ClassLoader contextClassLoader = null;
 216     private static final Class<?>[] PARAMS = { };
 217 
 218     // state saved in the event a user-specified asynchronous exception
 219     // was specified and thrown
 220 
 221     private int moduleIndex = 0;
 222     private LoginException firstError = null;
 223     private LoginException firstRequiredError = null;
 224     private boolean success = false;
 225 
 226     private static final sun.security.util.Debug debug =
 227         sun.security.util.Debug.getInstance("logincontext", "\t[LoginContext]");
 228 
 229     private void init(String name) throws LoginException {
 230 
 231         SecurityManager sm = System.getSecurityManager();
 232         if (sm != null && !configProvided) {
 233             sm.checkPermission(new AuthPermission
 234                                 ("createLoginContext." + name));
 235         }
 236 
 237         if (name == null)
 238             throw new LoginException
 239                 (ResourcesMgr.getString("Invalid.null.input.name"));
 240 
 241         // get the Configuration
 242         if (config == null) {
 243             config = java.security.AccessController.doPrivileged
 244                 (new java.security.PrivilegedAction<Configuration>() {
 245                 public Configuration run() {
 246                     return Configuration.getConfiguration();
 247                 }
 248             });
 249         }
 250 
 251         // get the LoginModules configured for this application
 252         AppConfigurationEntry[] entries = config.getAppConfigurationEntry(name);
 253         if (entries == null) {
 254 
 255             if (sm != null && !configProvided) {
 256                 sm.checkPermission(new AuthPermission
 257                                 ("createLoginContext." + OTHER));
 258             }
 259 
 260             entries = config.getAppConfigurationEntry(OTHER);
 261             if (entries == null) {
 262                 MessageFormat form = new MessageFormat(ResourcesMgr.getString
 263                         ("No.LoginModules.configured.for.name"));
 264                 Object[] source = {name};
 265                 throw new LoginException(form.format(source));
 266             }
 267         }
 268         moduleStack = new ModuleInfo[entries.length];
 269         for (int i = 0; i < entries.length; i++) {
 270             // clone returned array
 271             moduleStack[i] = new ModuleInfo
 272                                 (new AppConfigurationEntry
 273                                         (entries[i].getLoginModuleName(),
 274                                         entries[i].getControlFlag(),
 275                                         entries[i].getOptions()),
 276                                 null);
 277         }
 278 
 279         contextClassLoader = java.security.AccessController.doPrivileged
 280                 (new java.security.PrivilegedAction<ClassLoader>() {
 281                 public ClassLoader run() {
 282                     return Thread.currentThread().getContextClassLoader();
 283                 }
 284         });
 285     }
 286 
 287     private void loadDefaultCallbackHandler() throws LoginException {
 288 
 289         // get the default handler class
 290         try {
 291 
 292             final ClassLoader finalLoader = contextClassLoader;
 293 
 294             this.callbackHandler = java.security.AccessController.doPrivileged(
 295                 new java.security.PrivilegedExceptionAction<CallbackHandler>() {
 296                 public CallbackHandler run() throws Exception {
 297                     String defaultHandler = java.security.Security.getProperty
 298                         (DEFAULT_HANDLER);
 299                     if (defaultHandler == null || defaultHandler.length() == 0)
 300                         return null;
 301                     Class<?> c = Class.forName(defaultHandler,
 302                                         true,
 303                                         finalLoader);
 304                     return (CallbackHandler)c.newInstance();
 305                 }
 306             });
 307         } catch (java.security.PrivilegedActionException pae) {
 308             throw new LoginException(pae.getException().toString());
 309         }
 310 
 311         // secure it with the caller's ACC
 312         if (this.callbackHandler != null && !configProvided) {
 313             this.callbackHandler = new SecureCallbackHandler
 314                                 (java.security.AccessController.getContext(),
 315                                 this.callbackHandler);
 316         }
 317     }
 318 
 319     /**
 320      * Instantiate a new {@code LoginContext} object with a name.
 321      *
 322      * @param name the name used as the index into the
 323      *          {@code Configuration}.
 324      *
 325      * @exception LoginException if the caller-specified {@code name}
 326      *          does not appear in the {@code Configuration}
 327      *          and there is no {@code Configuration} entry
 328      *          for "<i>other</i>", or if the
 329      *          <i>auth.login.defaultCallbackHandler</i>
 330      *          security property was set, but the implementation
 331      *          class could not be loaded.
 332      *          <p>
 333      * @exception SecurityException if a SecurityManager is set and
 334      *          the caller does not have
 335      *          AuthPermission("createLoginContext.<i>name</i>"),
 336      *          or if a configuration entry for <i>name</i> does not exist and
 337      *          the caller does not additionally have
 338      *          AuthPermission("createLoginContext.other")
 339      */
 340     public LoginContext(String name) throws LoginException {
 341         init(name);
 342         loadDefaultCallbackHandler();
 343     }
 344 
 345     /**
 346      * Instantiate a new {@code LoginContext} object with a name
 347      * and a {@code Subject} object.
 348      *
 349      * <p>
 350      *
 351      * @param name the name used as the index into the
 352      *          {@code Configuration}. <p>
 353      *
 354      * @param subject the {@code Subject} to authenticate.
 355      *
 356      * @exception LoginException if the caller-specified {@code name}
 357      *          does not appear in the {@code Configuration}
 358      *          and there is no {@code Configuration} entry
 359      *          for "<i>other</i>", if the caller-specified {@code subject}
 360      *          is {@code null}, or if the
 361      *          <i>auth.login.defaultCallbackHandler</i>
 362      *          security property was set, but the implementation
 363      *          class could not be loaded.
 364      *          <p>
 365      * @exception SecurityException if a SecurityManager is set and
 366      *          the caller does not have
 367      *          AuthPermission("createLoginContext.<i>name</i>"),
 368      *          or if a configuration entry for <i>name</i> does not exist and
 369      *          the caller does not additionally have
 370      *          AuthPermission("createLoginContext.other")
 371      */
 372     public LoginContext(String name, Subject subject)
 373     throws LoginException {
 374         init(name);
 375         if (subject == null)
 376             throw new LoginException
 377                 (ResourcesMgr.getString("invalid.null.Subject.provided"));
 378         this.subject = subject;
 379         subjectProvided = true;
 380         loadDefaultCallbackHandler();
 381     }
 382 
 383     /**
 384      * Instantiate a new {@code LoginContext} object with a name
 385      * and a {@code CallbackHandler} object.
 386      *
 387      * <p>
 388      *
 389      * @param name the name used as the index into the
 390      *          {@code Configuration}. <p>
 391      *
 392      * @param callbackHandler the {@code CallbackHandler} object used by
 393      *          LoginModules to communicate with the user.
 394      *
 395      * @exception LoginException if the caller-specified {@code name}
 396      *          does not appear in the {@code Configuration}
 397      *          and there is no {@code Configuration} entry
 398      *          for "<i>other</i>", or if the caller-specified
 399      *          {@code callbackHandler} is {@code null}.
 400      *          <p>
 401      * @exception SecurityException if a SecurityManager is set and
 402      *          the caller does not have
 403      *          AuthPermission("createLoginContext.<i>name</i>"),
 404      *          or if a configuration entry for <i>name</i> does not exist and
 405      *          the caller does not additionally have
 406      *          AuthPermission("createLoginContext.other")
 407      */
 408     public LoginContext(String name, CallbackHandler callbackHandler)
 409     throws LoginException {
 410         init(name);
 411         if (callbackHandler == null)
 412             throw new LoginException(ResourcesMgr.getString
 413                                 ("invalid.null.CallbackHandler.provided"));
 414         this.callbackHandler = new SecureCallbackHandler
 415                                 (java.security.AccessController.getContext(),
 416                                 callbackHandler);
 417     }
 418 
 419     /**
 420      * Instantiate a new {@code LoginContext} object with a name,
 421      * a {@code Subject} to be authenticated, and a
 422      * {@code CallbackHandler} object.
 423      *
 424      * <p>
 425      *
 426      * @param name the name used as the index into the
 427      *          {@code Configuration}. <p>
 428      *
 429      * @param subject the {@code Subject} to authenticate. <p>
 430      *
 431      * @param callbackHandler the {@code CallbackHandler} object used by
 432      *          LoginModules to communicate with the user.
 433      *
 434      * @exception LoginException if the caller-specified {@code name}
 435      *          does not appear in the {@code Configuration}
 436      *          and there is no {@code Configuration} entry
 437      *          for "<i>other</i>", or if the caller-specified
 438      *          {@code subject} is {@code null},
 439      *          or if the caller-specified
 440      *          {@code callbackHandler} is {@code null}.
 441      *          <p>
 442      * @exception SecurityException if a SecurityManager is set and
 443      *          the caller does not have
 444      *          AuthPermission("createLoginContext.<i>name</i>"),
 445      *          or if a configuration entry for <i>name</i> does not exist and
 446      *          the caller does not additionally have
 447      *          AuthPermission("createLoginContext.other")
 448      */
 449     public LoginContext(String name, Subject subject,
 450                         CallbackHandler callbackHandler) throws LoginException {
 451         this(name, subject);
 452         if (callbackHandler == null)
 453             throw new LoginException(ResourcesMgr.getString
 454                                 ("invalid.null.CallbackHandler.provided"));
 455         this.callbackHandler = new SecureCallbackHandler
 456                                 (java.security.AccessController.getContext(),
 457                                 callbackHandler);
 458     }
 459 
 460     /**
 461      * Instantiate a new {@code LoginContext} object with a name,
 462      * a {@code Subject} to be authenticated,
 463      * a {@code CallbackHandler} object, and a login
 464      * {@code Configuration}.
 465      *
 466      * <p>
 467      *
 468      * @param name the name used as the index into the caller-specified
 469      *          {@code Configuration}. <p>
 470      *
 471      * @param subject the {@code Subject} to authenticate,
 472      *          or {@code null}. <p>
 473      *
 474      * @param callbackHandler the {@code CallbackHandler} object used by
 475      *          LoginModules to communicate with the user, or {@code null}.
 476      *          <p>
 477      *
 478      * @param config the {@code Configuration} that lists the
 479      *          login modules to be called to perform the authentication,
 480      *          or {@code null}.
 481      *
 482      * @exception LoginException if the caller-specified {@code name}
 483      *          does not appear in the {@code Configuration}
 484      *          and there is no {@code Configuration} entry
 485      *          for "<i>other</i>".
 486      *          <p>
 487      * @exception SecurityException if a SecurityManager is set,
 488      *          <i>config</i> is {@code null},
 489      *          and either the caller does not have
 490      *          AuthPermission("createLoginContext.<i>name</i>"),
 491      *          or if a configuration entry for <i>name</i> does not exist and
 492      *          the caller does not additionally have
 493      *          AuthPermission("createLoginContext.other")
 494      *
 495      * @since 1.5
 496      */
 497     public LoginContext(String name, Subject subject,
 498                         CallbackHandler callbackHandler,
 499                         Configuration config) throws LoginException {
 500         this.config = config;
 501         configProvided = (config != null) ? true : false;
 502         if (configProvided) {
 503             creatorAcc = java.security.AccessController.getContext();
 504         }
 505 
 506         init(name);
 507         if (subject != null) {
 508             this.subject = subject;
 509             subjectProvided = true;
 510         }
 511         if (callbackHandler == null) {
 512             loadDefaultCallbackHandler();
 513         } else if (!configProvided) {
 514             this.callbackHandler = new SecureCallbackHandler
 515                                 (java.security.AccessController.getContext(),
 516                                 callbackHandler);
 517         } else {
 518             this.callbackHandler = callbackHandler;
 519         }
 520     }
 521 
 522     /**
 523      * Perform the authentication.
 524      *
 525      * <p> This method invokes the {@code login} method for each
 526      * LoginModule configured for the <i>name</i> specified to the
 527      * {@code LoginContext} constructor, as determined by the login
 528      * {@code Configuration}.  Each {@code LoginModule}
 529      * then performs its respective type of authentication
 530      * (username/password, smart card pin verification, etc.).
 531      *
 532      * <p> This method completes a 2-phase authentication process by
 533      * calling each configured LoginModule's {@code commit} method
 534      * if the overall authentication succeeded (the relevant REQUIRED,
 535      * REQUISITE, SUFFICIENT, and OPTIONAL LoginModules succeeded),
 536      * or by calling each configured LoginModule's {@code abort} method
 537      * if the overall authentication failed.  If authentication succeeded,
 538      * each successful LoginModule's {@code commit} method associates
 539      * the relevant Principals and Credentials with the {@code Subject}.
 540      * If authentication failed, each LoginModule's {@code abort} method
 541      * removes/destroys any previously stored state.
 542      *
 543      * <p> If the {@code commit} phase of the authentication process
 544      * fails, then the overall authentication fails and this method
 545      * invokes the {@code abort} method for each configured
 546      * {@code LoginModule}.
 547      *
 548      * <p> If the {@code abort} phase
 549      * fails for any reason, then this method propagates the
 550      * original exception thrown either during the {@code login} phase
 551      * or the {@code commit} phase.  In either case, the overall
 552      * authentication fails.
 553      *
 554      * <p> In the case where multiple LoginModules fail,
 555      * this method propagates the exception raised by the first
 556      * {@code LoginModule} which failed.
 557      *
 558      * <p> Note that if this method enters the {@code abort} phase
 559      * (either the {@code login} or {@code commit} phase failed),
 560      * this method invokes all LoginModules configured for the
 561      * application regardless of their respective {@code Configuration}
 562      * flag parameters.  Essentially this means that {@code Requisite}
 563      * and {@code Sufficient} semantics are ignored during the
 564      * {@code abort} phase.  This guarantees that proper cleanup
 565      * and state restoration can take place.
 566      *
 567      * <p>
 568      *
 569      * @exception LoginException if the authentication fails.
 570      */
 571     public void login() throws LoginException {
 572 
 573         loginSucceeded = false;
 574 
 575         if (subject == null) {
 576             subject = new Subject();
 577         }
 578 
 579         try {
 580             if (configProvided) {
 581                 // module invoked in doPrivileged with creatorAcc
 582                 invokeCreatorPriv(LOGIN_METHOD);
 583                 invokeCreatorPriv(COMMIT_METHOD);
 584             } else {
 585                 // module invoked in doPrivileged
 586                 invokePriv(LOGIN_METHOD);
 587                 invokePriv(COMMIT_METHOD);
 588             }
 589             loginSucceeded = true;
 590         } catch (LoginException le) {
 591             try {
 592                 if (configProvided) {
 593                     invokeCreatorPriv(ABORT_METHOD);
 594                 } else {
 595                     invokePriv(ABORT_METHOD);
 596                 }
 597             } catch (LoginException le2) {
 598                 throw le;
 599             }
 600             throw le;
 601         }
 602     }
 603 
 604     /**
 605      * Logout the {@code Subject}.
 606      *
 607      * <p> This method invokes the {@code logout} method for each
 608      * {@code LoginModule} configured for this {@code LoginContext}.
 609      * Each {@code LoginModule} performs its respective logout procedure
 610      * which may include removing/destroying
 611      * {@code Principal} and {@code Credential} information
 612      * from the {@code Subject} and state cleanup.
 613      *
 614      * <p> Note that this method invokes all LoginModules configured for the
 615      * application regardless of their respective
 616      * {@code Configuration} flag parameters.  Essentially this means
 617      * that {@code Requisite} and {@code Sufficient} semantics are
 618      * ignored for this method.  This guarantees that proper cleanup
 619      * and state restoration can take place.
 620      *
 621      * <p>
 622      *
 623      * @exception LoginException if the logout fails.
 624      */
 625     public void logout() throws LoginException {
 626         if (subject == null) {
 627             throw new LoginException(ResourcesMgr.getString
 628                 ("null.subject.logout.called.before.login"));
 629         }
 630 
 631         if (configProvided) {
 632             // module invoked in doPrivileged with creatorAcc
 633             invokeCreatorPriv(LOGOUT_METHOD);
 634         } else {
 635             // module invoked in doPrivileged
 636             invokePriv(LOGOUT_METHOD);
 637         }
 638     }
 639 
 640     /**
 641      * Return the authenticated Subject.
 642      *
 643      * <p>
 644      *
 645      * @return the authenticated Subject.  If the caller specified a
 646      *          Subject to this LoginContext's constructor,
 647      *          this method returns the caller-specified Subject.
 648      *          If a Subject was not specified and authentication succeeds,
 649      *          this method returns the Subject instantiated and used for
 650      *          authentication by this LoginContext.
 651      *          If a Subject was not specified, and authentication fails or
 652      *          has not been attempted, this method returns null.
 653      */
 654     public Subject getSubject() {
 655         if (!loginSucceeded && !subjectProvided)
 656             return null;
 657         return subject;
 658     }
 659 
 660     private void clearState() {
 661         moduleIndex = 0;
 662         firstError = null;
 663         firstRequiredError = null;
 664         success = false;
 665     }
 666 
 667     private void throwException(LoginException originalError, LoginException le)
 668     throws LoginException {
 669 
 670         // first clear state
 671         clearState();
 672 
 673         // throw the exception
 674         LoginException error = (originalError != null) ? originalError : le;
 675         throw error;
 676     }
 677 
 678     /**
 679      * Invokes the login, commit, and logout methods
 680      * from a LoginModule inside a doPrivileged block.
 681      *
 682      * This version is called if the caller did not instantiate
 683      * the LoginContext with a Configuration object.
 684      */
 685     private void invokePriv(final String methodName) throws LoginException {
 686         try {
 687             java.security.AccessController.doPrivileged
 688                 (new java.security.PrivilegedExceptionAction<Void>() {
 689                 public Void run() throws LoginException {
 690                     invoke(methodName);
 691                     return null;
 692                 }
 693             });
 694         } catch (java.security.PrivilegedActionException pae) {
 695             throw (LoginException)pae.getException();
 696         }
 697     }
 698 
 699     /**
 700      * Invokes the login, commit, and logout methods
 701      * from a LoginModule inside a doPrivileged block restricted
 702      * by creatorAcc
 703      *
 704      * This version is called if the caller instantiated
 705      * the LoginContext with a Configuration object.
 706      */
 707     private void invokeCreatorPriv(final String methodName)
 708                 throws LoginException {
 709         try {
 710             java.security.AccessController.doPrivileged
 711                 (new java.security.PrivilegedExceptionAction<Void>() {
 712                 public Void run() throws LoginException {
 713                     invoke(methodName);
 714                     return null;
 715                 }
 716             }, creatorAcc);
 717         } catch (java.security.PrivilegedActionException pae) {
 718             throw (LoginException)pae.getException();
 719         }
 720     }
 721 
 722     private void invoke(String methodName) throws LoginException {
 723 
 724         // start at moduleIndex
 725         // - this can only be non-zero if methodName is LOGIN_METHOD
 726 
 727         for (int i = moduleIndex; i < moduleStack.length; i++, moduleIndex++) {
 728             try {
 729 
 730                 int mIndex = 0;
 731                 Method[] methods = null;
 732 
 733                 if (moduleStack[i].module != null) {
 734                     methods = moduleStack[i].module.getClass().getMethods();
 735                 } else {
 736 
 737                     // instantiate the LoginModule
 738                     Class<?> c = Class.forName
 739                                 (moduleStack[i].entry.getLoginModuleName(),
 740                                 true,
 741                                 contextClassLoader);
 742 
 743                     Constructor<?> constructor = c.getConstructor(PARAMS);
 744                     Object[] args = { };
 745 
 746                     // allow any object to be a LoginModule
 747                     // as long as it conforms to the interface
 748                     moduleStack[i].module = constructor.newInstance(args);
 749 
 750                     methods = moduleStack[i].module.getClass().getMethods();
 751 
 752                     // call the LoginModule's initialize method
 753                     for (mIndex = 0; mIndex < methods.length; mIndex++) {
 754                         if (methods[mIndex].getName().equals(INIT_METHOD))
 755                             break;
 756                     }
 757 
 758                     Object[] initArgs = {subject,
 759                                         callbackHandler,
 760                                         state,
 761                                         moduleStack[i].entry.getOptions() };
 762                     // invoke the LoginModule initialize method
 763                     methods[mIndex].invoke(moduleStack[i].module, initArgs);
 764                 }
 765 
 766                 // find the requested method in the LoginModule
 767                 for (mIndex = 0; mIndex < methods.length; mIndex++) {
 768                     if (methods[mIndex].getName().equals(methodName))
 769                         break;
 770                 }
 771 
 772                 // set up the arguments to be passed to the LoginModule method
 773                 Object[] args = { };
 774 
 775                 // invoke the LoginModule method
 776                 boolean status = ((Boolean)methods[mIndex].invoke
 777                                 (moduleStack[i].module, args)).booleanValue();
 778 
 779                 if (status == true) {
 780 
 781                     // if SUFFICIENT, return if no prior REQUIRED errors
 782                     if (!methodName.equals(ABORT_METHOD) &&
 783                         !methodName.equals(LOGOUT_METHOD) &&
 784                         moduleStack[i].entry.getControlFlag() ==
 785                     AppConfigurationEntry.LoginModuleControlFlag.SUFFICIENT &&
 786                         firstRequiredError == null) {
 787 
 788                         // clear state
 789                         clearState();
 790 
 791                         if (debug != null)
 792                             debug.println(methodName + " SUFFICIENT success");
 793                         return;
 794                     }
 795 
 796                     if (debug != null)
 797                         debug.println(methodName + " success");
 798                     success = true;
 799                 } else {
 800                     if (debug != null)
 801                         debug.println(methodName + " ignored");
 802                 }
 803 
 804             } catch (NoSuchMethodException nsme) {
 805                 MessageFormat form = new MessageFormat(ResourcesMgr.getString
 806                         ("unable.to.instantiate.LoginModule.module.because.it.does.not.provide.a.no.argument.constructor"));
 807                 Object[] source = {moduleStack[i].entry.getLoginModuleName()};
 808                 throwException(null, new LoginException(form.format(source)));
 809             } catch (InstantiationException ie) {
 810                 throwException(null, new LoginException(ResourcesMgr.getString
 811                         ("unable.to.instantiate.LoginModule.") +
 812                         ie.getMessage()));
 813             } catch (ClassNotFoundException cnfe) {
 814                 throwException(null, new LoginException(ResourcesMgr.getString
 815                         ("unable.to.find.LoginModule.class.") +
 816                         cnfe.getMessage()));
 817             } catch (IllegalAccessException iae) {
 818                 throwException(null, new LoginException(ResourcesMgr.getString
 819                         ("unable.to.access.LoginModule.") +
 820                         iae.getMessage()));
 821             } catch (InvocationTargetException ite) {
 822 
 823                 // failure cases
 824 
 825                 LoginException le;
 826 
 827                 if (ite.getCause() instanceof PendingException &&
 828                     methodName.equals(LOGIN_METHOD)) {
 829 
 830                     // XXX
 831                     //
 832                     // if a module's LOGIN_METHOD threw a PendingException
 833                     // then immediately throw it.
 834                     //
 835                     // when LoginContext is called again,
 836                     // the module that threw the exception is invoked first
 837                     // (the module list is not invoked from the start).
 838                     // previously thrown exception state is still present.
 839                     //
 840                     // it is assumed that the module which threw
 841                     // the exception can have its
 842                     // LOGIN_METHOD invoked twice in a row
 843                     // without any commit/abort in between.
 844                     //
 845                     // in all cases when LoginContext returns
 846                     // (either via natural return or by throwing an exception)
 847                     // we need to call clearState before returning.
 848                     // the only time that is not true is in this case -
 849                     // do not call throwException here.
 850 
 851                     throw (PendingException)ite.getCause();
 852 
 853                 } else if (ite.getCause() instanceof LoginException) {
 854 
 855                     le = (LoginException)ite.getCause();
 856 
 857                 } else if (ite.getCause() instanceof SecurityException) {
 858 
 859                     // do not want privacy leak
 860                     // (e.g., sensitive file path in exception msg)
 861 
 862                     le = new LoginException("Security Exception");
 863                     le.initCause(new SecurityException());
 864                     if (debug != null) {
 865                         debug.println
 866                             ("original security exception with detail msg " +
 867                             "replaced by new exception with empty detail msg");
 868                         debug.println("original security exception: " +
 869                                 ite.getCause().toString());
 870                     }
 871                 } else {
 872 
 873                     // capture an unexpected LoginModule exception
 874                     java.io.StringWriter sw = new java.io.StringWriter();
 875                     ite.getCause().printStackTrace
 876                                                 (new java.io.PrintWriter(sw));
 877                     sw.flush();
 878                     le = new LoginException(sw.toString());
 879                 }
 880 
 881                 if (moduleStack[i].entry.getControlFlag() ==
 882                     AppConfigurationEntry.LoginModuleControlFlag.REQUISITE) {
 883 
 884                     if (debug != null)
 885                         debug.println(methodName + " REQUISITE failure");
 886 
 887                     // if REQUISITE, then immediately throw an exception
 888                     if (methodName.equals(ABORT_METHOD) ||
 889                         methodName.equals(LOGOUT_METHOD)) {
 890                         if (firstRequiredError == null)
 891                             firstRequiredError = le;
 892                     } else {
 893                         throwException(firstRequiredError, le);
 894                     }
 895 
 896                 } else if (moduleStack[i].entry.getControlFlag() ==
 897                     AppConfigurationEntry.LoginModuleControlFlag.REQUIRED) {
 898 
 899                     if (debug != null)
 900                         debug.println(methodName + " REQUIRED failure");
 901 
 902                     // mark down that a REQUIRED module failed
 903                     if (firstRequiredError == null)
 904                         firstRequiredError = le;
 905 
 906                 } else {
 907 
 908                     if (debug != null)
 909                         debug.println(methodName + " OPTIONAL failure");
 910 
 911                     // mark down that an OPTIONAL module failed
 912                     if (firstError == null)
 913                         firstError = le;
 914                 }
 915             }
 916         }
 917 
 918         // we went thru all the LoginModules.
 919         if (firstRequiredError != null) {
 920             // a REQUIRED module failed -- return the error
 921             throwException(firstRequiredError, null);
 922         } else if (success == false && firstError != null) {
 923             // no module succeeded -- return the first error
 924             throwException(firstError, null);
 925         } else if (success == false) {
 926             // no module succeeded -- all modules were IGNORED
 927             throwException(new LoginException
 928                 (ResourcesMgr.getString("Login.Failure.all.modules.ignored")),
 929                 null);
 930         } else {
 931             // success
 932 
 933             clearState();
 934             return;
 935         }
 936     }
 937 
 938     /**
 939      * Wrap the caller-specified CallbackHandler in our own
 940      * and invoke it within a privileged block, constrained by
 941      * the caller's AccessControlContext.
 942      */
 943     private static class SecureCallbackHandler implements CallbackHandler {
 944 
 945         private final java.security.AccessControlContext acc;
 946         private final CallbackHandler ch;
 947 
 948         SecureCallbackHandler(java.security.AccessControlContext acc,
 949                         CallbackHandler ch) {
 950             this.acc = acc;
 951             this.ch = ch;
 952         }
 953 
 954         public void handle(final Callback[] callbacks)
 955                 throws java.io.IOException, UnsupportedCallbackException {
 956             try {
 957                 java.security.AccessController.doPrivileged
 958                     (new java.security.PrivilegedExceptionAction<Void>() {
 959                     public Void run() throws java.io.IOException,
 960                                         UnsupportedCallbackException {
 961                         ch.handle(callbacks);
 962                         return null;
 963                     }
 964                 }, acc);
 965             } catch (java.security.PrivilegedActionException pae) {
 966                 if (pae.getException() instanceof java.io.IOException) {
 967                     throw (java.io.IOException)pae.getException();
 968                 } else {
 969                     throw (UnsupportedCallbackException)pae.getException();
 970                 }
 971             }
 972         }
 973     }
 974 
 975     /**
 976      * LoginModule information -
 977      *          incapsulates Configuration info and actual module instances
 978      */
 979     private static class ModuleInfo {
 980         AppConfigurationEntry entry;
 981         Object module;
 982 
 983         ModuleInfo(AppConfigurationEntry newEntry, Object newModule) {
 984             this.entry = newEntry;
 985             this.module = newModule;
 986         }
 987     }
 988 }