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