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