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