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 }