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