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