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