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