1 /*
   2  * Copyright (c) 1998, 2015, 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.security.AccessController;
  29 import java.security.PrivilegedAction;
  30 import java.util.Map;
  31 import java.util.HashMap;
  32 import java.text.MessageFormat;
  33 import javax.security.auth.Subject;
  34 import javax.security.auth.AuthPermission;
  35 import javax.security.auth.callback.*;
  36 import javax.security.auth.spi.LoginModule;
  37 import java.security.AccessControlContext;
  38 import java.util.ServiceLoader;
  39 
  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  *
 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  *
 109  * <li> If the constructor does <b>not</b> have a Subject
 110  * input parameter, the LoginContext instantiates a new Subject.
 111  * </ul>
 112  *
 113  * <li> {@code Configuration}
 114  * <ul>
 115  * <li> If the constructor has a Configuration
 116  * input parameter and the caller specifies a non-null Configuration,
 117  * the LoginContext uses the caller-specified Configuration.
 118  * <p>
 119  * If the constructor does <b>not</b> have a Configuration
 120  * input parameter, or if the caller specifies a {@code null}
 121  * Configuration object, the constructor uses the following call to
 122  * get the installed Configuration:
 123  * <pre>
 124  *      config = Configuration.getConfiguration();
 125  * </pre>
 126  * For both cases,
 127  * the <i>name</i> argument given to the constructor is passed to the
 128  * {@code Configuration.getAppConfigurationEntry} method.
 129  * If the Configuration has no entries for the specified <i>name</i>,
 130  * then the {@code LoginContext} calls
 131  * {@code getAppConfigurationEntry} with the name, "<i>other</i>"
 132  * (the default entry name).  If there is no entry for "<i>other</i>",
 133  * then a {@code LoginException} is thrown.
 134  *
 135  * <li> When LoginContext uses the installed Configuration, the caller
 136  * requires the createLoginContext.<em>name</em> and possibly
 137  * createLoginContext.other AuthPermissions. Furthermore, the
 138  * LoginContext will invoke configured modules from within an
 139  * {@code AccessController.doPrivileged} call so that modules that
 140  * perform security-sensitive tasks (such as connecting to remote hosts,
 141  * and updating the Subject) will require the respective permissions, but
 142  * the callers of the LoginContext will not require those permissions.
 143  *
 144  * <li> When LoginContext uses a caller-specified Configuration, the caller
 145  * does not require any createLoginContext AuthPermission.  The LoginContext
 146  * saves the {@code AccessControlContext} for the caller,
 147  * and invokes the configured modules from within an
 148  * {@code AccessController.doPrivileged} call constrained by that context.
 149  * This means the caller context (stored when the LoginContext was created)
 150  * must have sufficient permissions to perform any security-sensitive tasks
 151  * that the modules may perform.
 152  * </ul>
 153  *
 154  * <li> {@code CallbackHandler}
 155  * <ul>
 156  * <li> If the constructor has a CallbackHandler
 157  * input parameter, the LoginContext uses the caller-specified
 158  * CallbackHandler object.
 159  *
 160  * <li> If the constructor does <b>not</b> have a CallbackHandler
 161  * input parameter, or if the caller specifies a {@code null}
 162  * CallbackHandler object (and a {@code null} value is permitted),
 163  * the LoginContext queries the
 164  * {@code auth.login.defaultCallbackHandler} security property for the
 165  * fully qualified class name of a default handler
 166  * implementation. If the security property is not set,
 167  * then the underlying modules will not have a
 168  * CallbackHandler for use in communicating
 169  * with users.  The caller thus assumes that the configured
 170  * modules have alternative means for authenticating the user.
 171  *
 172  *
 173  * <li> When the LoginContext uses the installed Configuration (instead of
 174  * a caller-specified Configuration, see above),
 175  * then this LoginContext must wrap any
 176  * caller-specified or default CallbackHandler implementation
 177  * in a new CallbackHandler implementation
 178  * whose {@code handle} method implementation invokes the
 179  * specified CallbackHandler's {@code handle} method in a
 180  * {@code java.security.AccessController.doPrivileged} call
 181  * constrained by the caller's current {@code AccessControlContext}.
 182  * </ul>
 183  * </ol>
 184  *
 185  * @since 1.4
 186  * @see java.security.Security
 187  * @see javax.security.auth.AuthPermission
 188  * @see javax.security.auth.Subject
 189  * @see javax.security.auth.callback.CallbackHandler
 190  * @see javax.security.auth.login.Configuration
 191  * @see javax.security.auth.spi.LoginModule
 192  * @see java.security.Security security properties
 193  */
 194 public class LoginContext {
 195 
 196     private static final String LOGIN_METHOD            = "login";
 197     private static final String COMMIT_METHOD           = "commit";
 198     private static final String ABORT_METHOD            = "abort";
 199     private static final String LOGOUT_METHOD           = "logout";
 200     private static final String OTHER                   = "other";
 201     private static final String DEFAULT_HANDLER         =
 202                                 "auth.login.defaultCallbackHandler";
 203     private Subject subject = null;
 204     private boolean subjectProvided = false;
 205     private boolean loginSucceeded = false;
 206     private CallbackHandler callbackHandler;
 207     private Map<String,?> state = new HashMap<String,Object>();
 208 
 209     private Configuration config;
 210     private AccessControlContext creatorAcc = null;  // customized config only
 211     private ModuleInfo[] moduleStack;
 212     private ClassLoader contextClassLoader = null;
 213 
 214     // state saved in the event a user-specified asynchronous exception
 215     // was specified and thrown
 216 
 217     private int moduleIndex = 0;
 218     private LoginException firstError = null;
 219     private LoginException firstRequiredError = null;
 220     private boolean success = false;
 221 
 222     private static final sun.security.util.Debug debug =
 223         sun.security.util.Debug.getInstance("logincontext", "\t[LoginContext]");
 224 
 225     private void init(String name) throws LoginException {
 226 
 227         SecurityManager sm = System.getSecurityManager();
 228         if (sm != null && creatorAcc == null) {
 229             sm.checkPermission(new AuthPermission
 230                                 ("createLoginContext." + name));
 231         }
 232 
 233         if (name == null)
 234             throw new LoginException
 235                 (ResourcesMgr.getString("Invalid.null.input.name"));
 236 
 237         // get the Configuration
 238         if (config == null) {
 239             config = java.security.AccessController.doPrivileged
 240                 (new java.security.PrivilegedAction<Configuration>() {
 241                 public Configuration run() {
 242                     return Configuration.getConfiguration();
 243                 }
 244             });
 245         }
 246 
 247         // get the LoginModules configured for this application
 248         AppConfigurationEntry[] entries = config.getAppConfigurationEntry(name);
 249         if (entries == null) {
 250 
 251             if (sm != null && creatorAcc == null) {
 252                 sm.checkPermission(new AuthPermission
 253                                 ("createLoginContext." + OTHER));
 254             }
 255 
 256             entries = config.getAppConfigurationEntry(OTHER);
 257             if (entries == null) {
 258                 MessageFormat form = new MessageFormat(ResourcesMgr.getString
 259                         ("No.LoginModules.configured.for.name"));
 260                 Object[] source = {name};
 261                 throw new LoginException(form.format(source));
 262             }
 263         }
 264         moduleStack = new ModuleInfo[entries.length];
 265         for (int i = 0; i < entries.length; i++) {
 266             // clone returned array
 267             moduleStack[i] = new ModuleInfo
 268                                 (new AppConfigurationEntry
 269                                         (entries[i].getLoginModuleName(),
 270                                         entries[i].getControlFlag(),
 271                                         entries[i].getOptions()),
 272                                 null);
 273         }
 274 
 275         contextClassLoader = java.security.AccessController.doPrivileged
 276                 (new java.security.PrivilegedAction<ClassLoader>() {
 277                 public ClassLoader run() {
 278                     ClassLoader loader =
 279                             Thread.currentThread().getContextClassLoader();
 280                     if (loader == null) {
 281                         // Don't use bootstrap class loader directly to ensure
 282                         // proper package access control!
 283                         loader = ClassLoader.getSystemClassLoader();
 284                     }
 285 
 286                     return loader;
 287                 }
 288         });
 289     }
 290 
 291     private void loadDefaultCallbackHandler() throws LoginException {
 292 
 293         // get the default handler class
 294         try {
 295 
 296             final ClassLoader finalLoader = contextClassLoader;
 297 
 298             this.callbackHandler = java.security.AccessController.doPrivileged(
 299                 new java.security.PrivilegedExceptionAction<CallbackHandler>() {
 300                 public CallbackHandler run() throws Exception {
 301                     String defaultHandler = java.security.Security.getProperty
 302                         (DEFAULT_HANDLER);
 303                     if (defaultHandler == null || defaultHandler.length() == 0)
 304                         return null;
 305                     Class<? extends CallbackHandler> c = Class.forName(
 306                             defaultHandler, true,
 307                             finalLoader).asSubclass(CallbackHandler.class);
 308                     @SuppressWarnings("deprecation")
 309                     CallbackHandler result = c.newInstance();
 310                     return result;
 311                 }
 312             });
 313         } catch (java.security.PrivilegedActionException pae) {
 314             throw new LoginException(pae.getException().toString());
 315         }
 316 
 317         // secure it with the caller's ACC
 318         if (this.callbackHandler != null && creatorAcc == null) {
 319             this.callbackHandler = new SecureCallbackHandler
 320                                 (java.security.AccessController.getContext(),
 321                                 this.callbackHandler);
 322         }
 323     }
 324 
 325     /**
 326      * Instantiate a new {@code LoginContext} object with a name.
 327      *
 328      * @param name the name used as the index into the
 329      *          {@code Configuration}.
 330      *
 331      * @exception LoginException if the caller-specified {@code name}
 332      *          does not appear in the {@code Configuration}
 333      *          and there is no {@code Configuration} entry
 334      *          for "{@code other}", or if the
 335      *          {@code auth.login.defaultCallbackHandler}
 336      *          security property was set, but the implementation
 337      *          class could not be loaded.
 338      *
 339      * @exception SecurityException if a SecurityManager is set and
 340      *          the caller does not have
 341      *          AuthPermission("createLoginContext.<i>name</i>"),
 342      *          or if a configuration entry for {@code name} does not exist and
 343      *          the caller does not additionally have
 344      *          AuthPermission("createLoginContext.other")
 345      */
 346     public LoginContext(String name) throws LoginException {
 347         init(name);
 348         loadDefaultCallbackHandler();
 349     }
 350 
 351     /**
 352      * Instantiate a new {@code LoginContext} object with a name
 353      * and a {@code Subject} object.
 354      *
 355      * @param name the name used as the index into the
 356      *          {@code Configuration}.
 357      *
 358      * @param subject the {@code Subject} to authenticate.
 359      *
 360      * @exception LoginException if the caller-specified {@code name}
 361      *          does not appear in the {@code Configuration}
 362      *          and there is no {@code Configuration} entry
 363      *          for "<i>other</i>", if the caller-specified {@code subject}
 364      *          is {@code null}, or if the
 365      *          <i>auth.login.defaultCallbackHandler</i>
 366      *          security property was set, but the implementation
 367      *          class could not be loaded.
 368      *
 369      * @exception SecurityException if a SecurityManager is set and
 370      *          the caller does not have
 371      *          AuthPermission("createLoginContext.<i>name</i>"),
 372      *          or if a configuration entry for <i>name</i> does not exist and
 373      *          the caller does not additionally have
 374      *          AuthPermission("createLoginContext.other")
 375      */
 376     public LoginContext(String name, Subject subject)
 377     throws LoginException {
 378         init(name);
 379         if (subject == null)
 380             throw new LoginException
 381                 (ResourcesMgr.getString("invalid.null.Subject.provided"));
 382         this.subject = subject;
 383         subjectProvided = true;
 384         loadDefaultCallbackHandler();
 385     }
 386 
 387     /**
 388      * Instantiate a new {@code LoginContext} object with a name
 389      * and a {@code CallbackHandler} object.
 390      *
 391      * @param name the name used as the index into the
 392      *          {@code Configuration}.
 393      *
 394      * @param callbackHandler the {@code CallbackHandler} object used by
 395      *          LoginModules to communicate with the user.
 396      *
 397      * @exception LoginException if the caller-specified {@code name}
 398      *          does not appear in the {@code Configuration}
 399      *          and there is no {@code Configuration} entry
 400      *          for "{@code other}", or if the caller-specified
 401      *          {@code callbackHandler} is {@code null}.
 402      *
 403      * @exception SecurityException if a SecurityManager is set and
 404      *          the caller does not have
 405      *          AuthPermission("createLoginContext.<i>name</i>"),
 406      *          or if a configuration entry for <i>name</i> does not exist and
 407      *          the caller does not additionally have
 408      *          AuthPermission("createLoginContext.other")
 409      */
 410     public LoginContext(String name, CallbackHandler callbackHandler)
 411     throws LoginException {
 412         init(name);
 413         if (callbackHandler == null)
 414             throw new LoginException(ResourcesMgr.getString
 415                                 ("invalid.null.CallbackHandler.provided"));
 416         this.callbackHandler = new SecureCallbackHandler
 417                                 (java.security.AccessController.getContext(),
 418                                 callbackHandler);
 419     }
 420 
 421     /**
 422      * Instantiate a new {@code LoginContext} object with a name,
 423      * a {@code Subject} to be authenticated, and a
 424      * {@code CallbackHandler} object.
 425      *
 426      * @param name the name used as the index into the
 427      *          {@code Configuration}.
 428      *
 429      * @param subject the {@code Subject} to authenticate.
 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      *
 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 {@code Configuration}.
 464      *
 465      * @param name the name used as the index into the caller-specified
 466      *          {@code Configuration}.
 467      *
 468      * @param subject the {@code Subject} to authenticate,
 469      *          or {@code null}.
 470      *
 471      * @param callbackHandler the {@code CallbackHandler} object used by
 472      *          LoginModules to communicate with the user, or {@code null}.
 473      *
 474      * @param config the {@code Configuration} that lists the
 475      *          login modules to be called to perform the authentication,
 476      *          or {@code null}.
 477      *
 478      * @exception LoginException if the caller-specified {@code name}
 479      *          does not appear in the {@code Configuration}
 480      *          and there is no {@code Configuration} entry
 481      *          for "<i>other</i>".
 482      *
 483      * @exception SecurityException if a SecurityManager is set,
 484      *          <i>config</i> is {@code null},
 485      *          and either the caller does not have
 486      *          AuthPermission("createLoginContext.<i>name</i>"),
 487      *          or if a configuration entry for <i>name</i> does not exist and
 488      *          the caller does not additionally have
 489      *          AuthPermission("createLoginContext.other")
 490      *
 491      * @since 1.5
 492      */
 493     public LoginContext(String name, Subject subject,
 494                         CallbackHandler callbackHandler,
 495                         Configuration config) throws LoginException {
 496         this.config = config;
 497         if (config != null) {
 498             creatorAcc = java.security.AccessController.getContext();
 499         }
 500 
 501         init(name);
 502         if (subject != null) {
 503             this.subject = subject;
 504             subjectProvided = true;
 505         }
 506         if (callbackHandler == null) {
 507             loadDefaultCallbackHandler();
 508         } else if (creatorAcc == null) {
 509             this.callbackHandler = new SecureCallbackHandler
 510                                 (java.security.AccessController.getContext(),
 511                                 callbackHandler);
 512         } else {
 513             this.callbackHandler = callbackHandler;
 514         }
 515     }
 516 
 517     /**
 518      * Perform the authentication.
 519      *
 520      * <p> This method invokes the {@code login} method for each
 521      * LoginModule configured for the <i>name</i> specified to the
 522      * {@code LoginContext} constructor, as determined by the login
 523      * {@code Configuration}.  Each {@code LoginModule}
 524      * then performs its respective type of authentication
 525      * (username/password, smart card pin verification, etc.).
 526      *
 527      * <p> This method completes a 2-phase authentication process by
 528      * calling each configured LoginModule's {@code commit} method
 529      * if the overall authentication succeeded (the relevant REQUIRED,
 530      * REQUISITE, SUFFICIENT, and OPTIONAL LoginModules succeeded),
 531      * or by calling each configured LoginModule's {@code abort} method
 532      * if the overall authentication failed.  If authentication succeeded,
 533      * each successful LoginModule's {@code commit} method associates
 534      * the relevant Principals and Credentials with the {@code Subject}.
 535      * If authentication failed, each LoginModule's {@code abort} method
 536      * removes/destroys any previously stored state.
 537      *
 538      * <p> If the {@code commit} phase of the authentication process
 539      * fails, then the overall authentication fails and this method
 540      * invokes the {@code abort} method for each configured
 541      * {@code LoginModule}.
 542      *
 543      * <p> If the {@code abort} phase
 544      * fails for any reason, then this method propagates the
 545      * original exception thrown either during the {@code login} phase
 546      * or the {@code commit} phase.  In either case, the overall
 547      * authentication fails.
 548      *
 549      * <p> In the case where multiple LoginModules fail,
 550      * this method propagates the exception raised by the first
 551      * {@code LoginModule} which failed.
 552      *
 553      * <p> Note that if this method enters the {@code abort} phase
 554      * (either the {@code login} or {@code commit} phase failed),
 555      * this method invokes all LoginModules configured for the
 556      * application regardless of their respective {@code Configuration}
 557      * flag parameters.  Essentially this means that {@code Requisite}
 558      * and {@code Sufficient} semantics are ignored during the
 559      * {@code abort} phase.  This guarantees that proper cleanup
 560      * and state restoration can take place.
 561      *
 562      * @exception LoginException if the authentication fails.
 563      */
 564     public void login() throws LoginException {
 565 
 566         loginSucceeded = false;
 567 
 568         if (subject == null) {
 569             subject = new Subject();
 570         }
 571 
 572         try {
 573             // module invoked in doPrivileged
 574             invokePriv(LOGIN_METHOD);
 575             invokePriv(COMMIT_METHOD);
 576             loginSucceeded = true;
 577         } catch (LoginException le) {
 578             try {
 579                 invokePriv(ABORT_METHOD);
 580             } catch (LoginException le2) {
 581                 throw le;
 582             }
 583             throw le;
 584         }
 585     }
 586 
 587     /**
 588      * Logout the {@code Subject}.
 589      *
 590      * <p> This method invokes the {@code logout} method for each
 591      * {@code LoginModule} configured for this {@code LoginContext}.
 592      * Each {@code LoginModule} performs its respective logout procedure
 593      * which may include removing/destroying
 594      * {@code Principal} and {@code Credential} information
 595      * from the {@code Subject} and state cleanup.
 596      *
 597      * <p> Note that this method invokes all LoginModules configured for the
 598      * application regardless of their respective
 599      * {@code Configuration} flag parameters.  Essentially this means
 600      * that {@code Requisite} and {@code Sufficient} semantics are
 601      * ignored for this method.  This guarantees that proper cleanup
 602      * and state restoration can take place.
 603      *
 604      * @exception LoginException if the logout fails.
 605      */
 606     public void logout() throws LoginException {
 607         if (subject == null) {
 608             throw new LoginException(ResourcesMgr.getString
 609                 ("null.subject.logout.called.before.login"));
 610         }
 611 
 612         // module invoked in doPrivileged
 613         invokePriv(LOGOUT_METHOD);
 614     }
 615 
 616     /**
 617      * Return the authenticated Subject.
 618      *
 619      * @return the authenticated Subject.  If the caller specified a
 620      *          Subject to this LoginContext's constructor,
 621      *          this method returns the caller-specified Subject.
 622      *          If a Subject was not specified and authentication succeeds,
 623      *          this method returns the Subject instantiated and used for
 624      *          authentication by this LoginContext.
 625      *          If a Subject was not specified, and authentication fails or
 626      *          has not been attempted, this method returns null.
 627      */
 628     public Subject getSubject() {
 629         if (!loginSucceeded && !subjectProvided)
 630             return null;
 631         return subject;
 632     }
 633 
 634     private void clearState() {
 635         moduleIndex = 0;
 636         firstError = null;
 637         firstRequiredError = null;
 638         success = false;
 639     }
 640 
 641     private void throwException(LoginException originalError, LoginException le)
 642     throws LoginException {
 643 
 644         // first clear state
 645         clearState();
 646 
 647         // throw the exception
 648         LoginException error = (originalError != null) ? originalError : le;
 649         throw error;
 650     }
 651 
 652     /**
 653      * Invokes the login, commit, and logout methods
 654      * from a LoginModule inside a doPrivileged block restricted
 655      * by creatorAcc (may be null).
 656      *
 657      * This version is called if the caller did not instantiate
 658      * the LoginContext with a Configuration object.
 659      */
 660     private void invokePriv(final String methodName) throws LoginException {
 661         try {
 662             java.security.AccessController.doPrivileged
 663                 (new java.security.PrivilegedExceptionAction<Void>() {
 664                 public Void run() throws LoginException {
 665                     invoke(methodName);
 666                     return null;
 667                 }
 668             }, creatorAcc);
 669         } catch (java.security.PrivilegedActionException pae) {
 670             throw (LoginException)pae.getException();
 671         }
 672     }
 673 
 674     private void invoke(String methodName) throws LoginException {
 675 
 676         // start at moduleIndex
 677         // - this can only be non-zero if methodName is LOGIN_METHOD
 678 
 679         for (int i = moduleIndex; i < moduleStack.length; i++, moduleIndex++) {
 680             try {
 681 
 682                 if (moduleStack[i].module == null) {
 683 
 684                     // locate and instantiate the LoginModule
 685                     //
 686                     String name = moduleStack[i].entry.getLoginModuleName();
 687                     ServiceLoader<LoginModule> sc = AccessController.doPrivileged(
 688                             (PrivilegedAction<ServiceLoader<LoginModule>>)
 689                                     () -> ServiceLoader.load(
 690                                         LoginModule.class, contextClassLoader));
 691                     for (LoginModule m: sc) {
 692                         if (m.getClass().getName().equals(name)) {
 693                             moduleStack[i].module = m;
 694                             if (debug != null) {
 695                                 debug.println(name + " loaded as a service");
 696                             }
 697                             break;
 698                         }
 699                     }
 700 
 701                     if (moduleStack[i].module == null) {
 702                         try {
 703                             @SuppressWarnings("deprecation")
 704                             Object tmp = Class.forName(name, false, contextClassLoader).newInstance();
 705                             moduleStack[i].module = (LoginModule) tmp;
 706                             if (debug != null) {
 707                                 debug.println(name + " loaded via reflection");
 708                             }
 709                         } catch (ClassNotFoundException e) {
 710                             throw new LoginException("No LoginModule found for "
 711                                     + name);
 712                         }
 713                     }
 714 
 715                     // invoke the LoginModule initialize method
 716                     moduleStack[i].module.initialize(subject,
 717                             callbackHandler,
 718                             state,
 719                             moduleStack[i].entry.getOptions());
 720                 }
 721 
 722                 // find the requested method in the LoginModule
 723                 boolean status;
 724                 switch (methodName) {
 725                     case LOGIN_METHOD:
 726                         status = moduleStack[i].module.login();
 727                         break;
 728                     case COMMIT_METHOD:
 729                         status = moduleStack[i].module.commit();
 730                         break;
 731                     case LOGOUT_METHOD:
 732                         status = moduleStack[i].module.logout();
 733                         break;
 734                     case ABORT_METHOD:
 735                         status = moduleStack[i].module.abort();
 736                         break;
 737                     default:
 738                         throw new AssertionError("Unknown method " + methodName);
 739                 }
 740 
 741                 if (status == true) {
 742 
 743                     // if SUFFICIENT, return if no prior REQUIRED errors
 744                     if (!methodName.equals(ABORT_METHOD) &&
 745                         !methodName.equals(LOGOUT_METHOD) &&
 746                         moduleStack[i].entry.getControlFlag() ==
 747                     AppConfigurationEntry.LoginModuleControlFlag.SUFFICIENT &&
 748                         firstRequiredError == null) {
 749 
 750                         // clear state
 751                         clearState();
 752 
 753                         if (debug != null)
 754                             debug.println(methodName + " SUFFICIENT success");
 755                         return;
 756                     }
 757 
 758                     if (debug != null)
 759                         debug.println(methodName + " success");
 760                     success = true;
 761                 } else {
 762                     if (debug != null)
 763                         debug.println(methodName + " ignored");
 764                 }
 765             } catch (Exception ite) {
 766 
 767                 // failure cases
 768                 LoginException le;
 769 
 770                 if (ite instanceof PendingException &&
 771                     methodName.equals(LOGIN_METHOD)) {
 772 
 773                     // XXX
 774                     //
 775                     // if a module's LOGIN_METHOD threw a PendingException
 776                     // then immediately throw it.
 777                     //
 778                     // when LoginContext is called again,
 779                     // the module that threw the exception is invoked first
 780                     // (the module list is not invoked from the start).
 781                     // previously thrown exception state is still present.
 782                     //
 783                     // it is assumed that the module which threw
 784                     // the exception can have its
 785                     // LOGIN_METHOD invoked twice in a row
 786                     // without any commit/abort in between.
 787                     //
 788                     // in all cases when LoginContext returns
 789                     // (either via natural return or by throwing an exception)
 790                     // we need to call clearState before returning.
 791                     // the only time that is not true is in this case -
 792                     // do not call throwException here.
 793 
 794                     throw (PendingException)ite;
 795 
 796                 } else if (ite instanceof LoginException) {
 797 
 798                     le = (LoginException)ite;
 799 
 800                 } else if (ite instanceof SecurityException) {
 801 
 802                     // do not want privacy leak
 803                     // (e.g., sensitive file path in exception msg)
 804 
 805                     le = new LoginException("Security Exception");
 806                     le.initCause(new SecurityException());
 807                     if (debug != null) {
 808                         debug.println
 809                             ("original security exception with detail msg " +
 810                             "replaced by new exception with empty detail msg");
 811                         debug.println("original security exception: " +
 812                                 ite.toString());
 813                     }
 814                 } else {
 815 
 816                     // capture an unexpected LoginModule exception
 817                     java.io.StringWriter sw = new java.io.StringWriter();
 818                     ite.printStackTrace
 819                             (new java.io.PrintWriter(sw));
 820                     sw.flush();
 821                     le = new LoginException(sw.toString());
 822                 }
 823 
 824                 if (moduleStack[i].entry.getControlFlag() ==
 825                     AppConfigurationEntry.LoginModuleControlFlag.REQUISITE) {
 826 
 827                     if (debug != null)
 828                         debug.println(methodName + " REQUISITE failure");
 829 
 830                     // if REQUISITE, then immediately throw an exception
 831                     if (methodName.equals(ABORT_METHOD) ||
 832                         methodName.equals(LOGOUT_METHOD)) {
 833                         if (firstRequiredError == null)
 834                             firstRequiredError = le;
 835                     } else {
 836                         throwException(firstRequiredError, le);
 837                     }
 838 
 839                 } else if (moduleStack[i].entry.getControlFlag() ==
 840                     AppConfigurationEntry.LoginModuleControlFlag.REQUIRED) {
 841 
 842                     if (debug != null)
 843                         debug.println(methodName + " REQUIRED failure");
 844 
 845                     // mark down that a REQUIRED module failed
 846                     if (firstRequiredError == null)
 847                         firstRequiredError = le;
 848 
 849                 } else {
 850 
 851                     if (debug != null)
 852                         debug.println(methodName + " OPTIONAL failure");
 853 
 854                     // mark down that an OPTIONAL module failed
 855                     if (firstError == null)
 856                         firstError = le;
 857                 }
 858             }
 859         }
 860 
 861         // we went thru all the LoginModules.
 862         if (firstRequiredError != null) {
 863             // a REQUIRED module failed -- return the error
 864             throwException(firstRequiredError, null);
 865         } else if (success == false && firstError != null) {
 866             // no module succeeded -- return the first error
 867             throwException(firstError, null);
 868         } else if (success == false) {
 869             // no module succeeded -- all modules were IGNORED
 870             throwException(new LoginException
 871                 (ResourcesMgr.getString("Login.Failure.all.modules.ignored")),
 872                 null);
 873         } else {
 874             // success
 875 
 876             clearState();
 877             return;
 878         }
 879     }
 880 
 881     /**
 882      * Wrap the caller-specified CallbackHandler in our own
 883      * and invoke it within a privileged block, constrained by
 884      * the caller's AccessControlContext.
 885      */
 886     private static class SecureCallbackHandler implements CallbackHandler {
 887 
 888         private final java.security.AccessControlContext acc;
 889         private final CallbackHandler ch;
 890 
 891         SecureCallbackHandler(java.security.AccessControlContext acc,
 892                         CallbackHandler ch) {
 893             this.acc = acc;
 894             this.ch = ch;
 895         }
 896 
 897         public void handle(final Callback[] callbacks)
 898                 throws java.io.IOException, UnsupportedCallbackException {
 899             try {
 900                 java.security.AccessController.doPrivileged
 901                     (new java.security.PrivilegedExceptionAction<Void>() {
 902                     public Void run() throws java.io.IOException,
 903                                         UnsupportedCallbackException {
 904                         ch.handle(callbacks);
 905                         return null;
 906                     }
 907                 }, acc);
 908             } catch (java.security.PrivilegedActionException pae) {
 909                 if (pae.getException() instanceof java.io.IOException) {
 910                     throw (java.io.IOException)pae.getException();
 911                 } else {
 912                     throw (UnsupportedCallbackException)pae.getException();
 913                 }
 914             }
 915         }
 916     }
 917 
 918     /**
 919      * LoginModule information -
 920      *          incapsulates Configuration info and actual module instances
 921      */
 922     private static class ModuleInfo {
 923         AppConfigurationEntry entry;
 924         LoginModule module;
 925 
 926         ModuleInfo(AppConfigurationEntry newEntry, LoginModule newModule) {
 927             this.entry = newEntry;
 928             this.module = newModule;
 929         }
 930     }
 931 }