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 }