KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > security > auth > login > LoginContext


1 /*
2  * @(#)LoginContext.java 1.98 04/06/28
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package javax.security.auth.login;
9
10 import java.lang.reflect.Constructor JavaDoc;
11 import java.lang.reflect.Method JavaDoc;
12 import java.lang.reflect.InvocationTargetException JavaDoc;
13 import java.util.LinkedList JavaDoc;
14 import java.util.Map JavaDoc;
15 import java.util.HashMap JavaDoc;
16 import java.text.MessageFormat JavaDoc;
17 import javax.security.auth.Subject JavaDoc;
18 import javax.security.auth.AuthPermission JavaDoc;
19 import javax.security.auth.callback.*;
20 import java.security.AccessController JavaDoc;
21 import java.security.AccessControlContext JavaDoc;
22 import sun.security.util.PendingException;
23 import sun.security.util.ResourcesMgr;
24
25 /**
26  * <p> The <code>LoginContext</code> class describes the basic methods used
27  * to authenticate Subjects and provides a way to develop an
28  * application independent of the underlying authentication technology.
29  * A <code>Configuration</code> specifies the authentication technology, or
30  * <code>LoginModule</code>, to be used with a particular application.
31  * Different LoginModules can be plugged in under an application
32  * without requiring any modifications to the application itself.
33  *
34  * <p> In addition to supporting <i>pluggable</i> authentication, this class
35  * also supports the notion of <i>stacked</i> authentication.
36  * Applications may be configured to use more than one
37  * LoginModule. For example, one could
38  * configure both a Kerberos LoginModule and a smart card
39  * LoginModule under an application.
40  *
41  * <p> A typical caller instantiates a LoginContext with
42  * a <i>name</i> and a <code>CallbackHandler</code>.
43  * LoginContext uses the <i>name</i> as the index into a
44  * Configuration to determine which LoginModules should be used,
45  * and which ones must succeed in order for the overall authentication to
46  * succeed. The <code>CallbackHandler</code> is passed to the underlying
47  * LoginModules so they may communicate and interact with users
48  * (prompting for a username and password via a graphical user interface,
49  * for example).
50  *
51  * <p> Once the caller has instantiated a LoginContext,
52  * it invokes the <code>login</code> method to authenticate
53  * a <code>Subject</code>. The <code>login</code> method invokes
54  * the configured modules to perform their respective types of authentication
55  * (username/password, smart card pin verification, etc.).
56  * Note that the LoginModules will not attempt authentication retries nor
57  * introduce delays if the authentication fails.
58  * Such tasks belong to the LoginContext caller.
59  *
60  * <p> If the <code>login</code> method returns without
61  * throwing an exception, then the overall authentication succeeded.
62  * The caller can then retrieve
63  * the newly authenticated Subject by invoking the
64  * <code>getSubject</code> method. Principals and Credentials associated
65  * with the Subject may be retrieved by invoking the Subject's
66  * respective <code>getPrincipals</code>, <code>getPublicCredentials</code>,
67  * and <code>getPrivateCredentials</code> methods.
68  *
69  * <p> To logout the Subject, the caller calls
70  * the <code>logout</code> method. As with the <code>login</code>
71  * method, this <code>logout</code> method invokes the <code>logout</code>
72  * method for the configured modules.
73  *
74  * <p> A LoginContext should not be used to authenticate
75  * more than one Subject. A separate LoginContext
76  * should be used to authenticate each different Subject.
77  *
78  * <p> The following documentation applies to all LoginContext constructors:
79  * <ol>
80  *
81  * <li> <code>Subject</code>
82  * <ul>
83  * <li> If the constructor has a Subject
84  * input parameter, the LoginContext uses the caller-specified
85  * Subject object.
86  * <p>
87  * <li> If the caller specifies a <code>null</code> Subject
88  * and a <code>null</code> value is permitted,
89  * the LoginContext instantiates a new Subject.
90  * <p>
91  * <li> If the constructor does <b>not</b> have a Subject
92  * input parameter, the LoginContext instantiates a new Subject.
93  * <p>
94  * </ul>
95  *
96  * <li> <code>Configuration</code>
97  * <ul>
98  * <li> If the constructor has a Configuration
99  * input parameter and the caller specifies a non-null Configuration,
100  * the LoginContext uses the caller-specified Configuration.
101  * <p>
102  * If the constructor does <b>not</b> have a Configuration
103  * input parameter, or if the caller specifies a <code>null</code>
104  * Configuration object, the constructor uses the following call to
105  * get the installed Configuration:
106  * <pre>
107  * config = Configuration.getConfiguration();
108  * </pre>
109  * For both cases,
110  * the <i>name</i> argument given to the constructor is passed to the
111  * <code>Configuration.getAppConfigurationEntry</code> method.
112  * If the Configuration has no entries for the specified <i>name</i>,
113  * then the <code>LoginContext</code> calls
114  * <code>getAppConfigurationEntry</code> with the name, "<i>other</i>"
115  * (the default entry name). If there is no entry for "<i>other</i>",
116  * then a <code>LoginException</code> is thrown.
117  * <p>
118  * <li> When LoginContext uses the installed Configuration, the caller
119  * requires the createLoginContext.<em>name</em> and possibly
120  * createLoginContext.other AuthPermissions. Furthermore, the
121  * LoginContext will invoke configured modules from within an
122  * <code>AccessController.doPrivileged</code> call so that modules that
123  * perform security-sensitive tasks (such as connecting to remote hosts,
124  * and updating the Subject) will require the respective permissions, but
125  * the callers of the LoginContext will not require those permissions.
126  * <p>
127  * <li> When LoginContext uses a caller-specified Configuration, the caller
128  * does not require any createLoginContext AuthPermission. The LoginContext
129  * saves the <code>AccessControlContext</code> for the caller,
130  * and invokes the configured modules from within an
131  * <tt>AccessController.doPrivileged</tt> call constrained by that context.
132  * This means the caller context (stored when the LoginContext was created)
133  * must have sufficient permissions to perform any security-sensitive tasks
134  * that the modules may perform.
135  * <p>
136  * </ul>
137  *
138  * <li> <code>CallbackHandler</code>
139  * <ul>
140  * <li> If the constructor has a CallbackHandler
141  * input parameter, the LoginContext uses the caller-specified
142  * CallbackHandler object.
143  * <p>
144  * <li> If the constructor does <b>not</b> have a CallbackHandler
145  * input parameter, or if the caller specifies a <code>null</code>
146  * CallbackHandler object (and a <code>null</code> value is permitted),
147  * the LoginContext queries the
148  * <i>auth.login.defaultCallbackHandler</i> security property
149  * for the fully qualified class name of a default handler implementation.
150  * If the security property is not set,
151  * then the underlying modules will not have a
152  * CallbackHandler for use in communicating
153  * with users. The caller thus assumes that the configured
154  * modules have alternative means for authenticating the user.
155  *
156  * <p>
157  * <li> When the LoginContext uses the installed Configuration (instead of
158  * a caller-specified Configuration, see above),
159  * then this LoginContext must wrap any
160  * caller-specified or default CallbackHandler implementation
161  * in a new CallbackHandler implementation
162  * whose <code>handle</code> method implementation invokes the
163  * specified CallbackHandler's <code>handle</code> method in a
164  * <code>java.security.AccessController.doPrivileged</code> call
165  * constrained by the caller's current <code>AccessControlContext</code>.
166  * </ul>
167  * </ol>
168  *
169  * <p> Note that Security Properties
170  * (such as <code>auth.login.defaultCallbackHandler</code>)
171  * can be set programmatically via the
172  * <code>java.security.Security</code> class,
173  * or statically in the Java security properties file located in the
174  * file named &lt;JAVA_HOME&gt;/lib/security/java.security,
175  * where &lt;JAVA_HOME&gt; refers to the directory where the JDK
176  * was installed.
177  *
178  * @version 1.98, 06/28/04
179  * @see java.security.Security
180  * @see javax.security.auth.AuthPermission
181  * @see javax.security.auth.Subject
182  * @see javax.security.auth.callback.CallbackHandler
183  * @see javax.security.auth.login.Configuration
184  * @see javax.security.auth.spi.LoginModule
185  */

186 public class LoginContext {
187
188     private static final String JavaDoc INIT_METHOD = "initialize";
189     private static final String JavaDoc LOGIN_METHOD = "login";
190     private static final String JavaDoc COMMIT_METHOD = "commit";
191     private static final String JavaDoc ABORT_METHOD = "abort";
192     private static final String JavaDoc LOGOUT_METHOD = "logout";
193     private static final String JavaDoc OTHER = "other";
194     private static final String JavaDoc DEFAULT_HANDLER =
195                 "auth.login.defaultCallbackHandler";
196     private Subject JavaDoc subject = null;
197     private boolean subjectProvided = false;
198     private boolean loginSucceeded = false;
199     private CallbackHandler callbackHandler;
200     private Map JavaDoc state = new HashMap JavaDoc();
201
202     private Configuration JavaDoc config;
203     private boolean configProvided = false;
204     private AccessControlContext JavaDoc creatorAcc = null;
205     private ModuleInfo[] moduleStack;
206     private ClassLoader JavaDoc contextClassLoader = null;
207     private static final Class JavaDoc[] PARAMS = { };
208
209     // state saved in the event a user-specified asynchronous exception
210
// was specified and thrown
211

212     private int moduleIndex = 0;
213     private LoginException JavaDoc firstError = null;
214     private LoginException JavaDoc firstRequiredError = null;
215     private boolean success = false;
216
217     private static final sun.security.util.Debug debug =
218     sun.security.util.Debug.getInstance("logincontext", "\t[LoginContext]");
219
220     private void init(String JavaDoc name) throws LoginException JavaDoc {
221
222     SecurityManager JavaDoc sm = System.getSecurityManager();
223     if (sm != null && !configProvided) {
224         sm.checkPermission(new AuthPermission JavaDoc
225                 ("createLoginContext." + name));
226     }
227
228     if (name == null)
229         throw new LoginException JavaDoc
230         (ResourcesMgr.getString("Invalid null input: name"));
231
232     // get the Configuration
233
if (config == null) {
234         config = (Configuration JavaDoc)java.security.AccessController.doPrivileged
235         (new java.security.PrivilegedAction JavaDoc() {
236         public Object JavaDoc run() {
237             return Configuration.getConfiguration();
238         }
239         });
240     }
241
242     // get the LoginModules configured for this application
243
AppConfigurationEntry JavaDoc[] entries = config.getAppConfigurationEntry(name);
244     if (entries == null) {
245
246         if (sm != null && !configProvided) {
247         sm.checkPermission(new AuthPermission JavaDoc
248                 ("createLoginContext." + OTHER));
249         }
250
251         entries = config.getAppConfigurationEntry(OTHER);
252         if (entries == null) {
253         MessageFormat JavaDoc form = new MessageFormat JavaDoc(ResourcesMgr.getString
254             ("No LoginModules configured for name"));
255         Object JavaDoc[] source = {name};
256         throw new LoginException JavaDoc(form.format(source));
257         }
258     }
259     moduleStack = new ModuleInfo[entries.length];
260     for (int i = 0; i < entries.length; i++) {
261         // clone returned array
262
moduleStack[i] = new ModuleInfo
263                 (new AppConfigurationEntry JavaDoc
264                     (entries[i].getLoginModuleName(),
265                     entries[i].getControlFlag(),
266                     entries[i].getOptions()),
267                 null);
268     }
269
270     contextClassLoader =
271         (ClassLoader JavaDoc)java.security.AccessController.doPrivileged
272         (new java.security.PrivilegedAction JavaDoc() {
273         public Object JavaDoc run() {
274             return Thread.currentThread().getContextClassLoader();
275         }
276     });
277     }
278
279     private void loadDefaultCallbackHandler() throws LoginException JavaDoc {
280
281     // get the default handler class
282
try {
283
284         final ClassLoader JavaDoc finalLoader = contextClassLoader;
285
286         this.callbackHandler = (CallbackHandler)
287         java.security.AccessController.doPrivileged
288         (new java.security.PrivilegedExceptionAction JavaDoc() {
289         public Object JavaDoc run() throws Exception JavaDoc {
290             String JavaDoc defaultHandler = java.security.Security.getProperty
291             (DEFAULT_HANDLER);
292             if (defaultHandler == null || defaultHandler.length() == 0)
293             return null;
294             Class JavaDoc c = Class.forName(defaultHandler,
295                     true,
296                     finalLoader);
297             return c.newInstance();
298         }
299         });
300     } catch (java.security.PrivilegedActionException JavaDoc pae) {
301         throw new LoginException JavaDoc(pae.getException().toString());
302     }
303
304     // secure it with the caller's ACC
305
if (this.callbackHandler != null && !configProvided) {
306         this.callbackHandler = new SecureCallbackHandler
307                 (java.security.AccessController.getContext(),
308                 this.callbackHandler);
309     }
310     }
311
312     /**
313      * Instantiate a new <code>LoginContext</code> object with a name.
314      *
315      * @param name the name used as the index into the
316      * <code>Configuration</code>.
317      *
318      * @exception LoginException if the caller-specified <code>name</code>
319      * does not appear in the <code>Configuration</code>
320      * and there is no <code>Configuration</code> entry
321      * for "<i>other</i>", or if the
322      * <i>auth.login.defaultCallbackHandler</i>
323      * security property was set, but the implementation
324      * class could not be loaded.
325      * <p>
326      * @exception SecurityException if a SecurityManager is set and
327      * the caller does not have
328      * AuthPermission("createLoginContext.<i>name</i>"),
329      * or if a configuration entry for <i>name</i> does not exist and
330      * the caller does not additionally have
331      * AuthPermission("createLoginContext.other")
332      */

333     public LoginContext(String JavaDoc name) throws LoginException JavaDoc {
334     init(name);
335     loadDefaultCallbackHandler();
336     }
337
338     /**
339      * Instantiate a new <code>LoginContext</code> object with a name
340      * and a <code>Subject</code> object.
341      *
342      * <p>
343      *
344      * @param name the name used as the index into the
345      * <code>Configuration</code>. <p>
346      *
347      * @param subject the <code>Subject</code> to authenticate.
348      *
349      * @exception LoginException if the caller-specified <code>name</code>
350      * does not appear in the <code>Configuration</code>
351      * and there is no <code>Configuration</code> entry
352      * for "<i>other</i>", if the caller-specified <code>subject</code>
353      * is <code>null</code>, or if the
354      * <i>auth.login.defaultCallbackHandler</i>
355      * security property was set, but the implementation
356      * class could not be loaded.
357      * <p>
358      * @exception SecurityException if a SecurityManager is set and
359      * the caller does not have
360      * AuthPermission("createLoginContext.<i>name</i>"),
361      * or if a configuration entry for <i>name</i> does not exist and
362      * the caller does not additionally have
363      * AuthPermission("createLoginContext.other")
364      */

365     public LoginContext(String JavaDoc name, Subject JavaDoc subject)
366     throws LoginException JavaDoc {
367     init(name);
368     if (subject == null)
369         throw new LoginException JavaDoc
370         (ResourcesMgr.getString("invalid null Subject provided"));
371     this.subject = subject;
372     subjectProvided = true;
373     loadDefaultCallbackHandler();
374     }
375
376     /**
377      * Instantiate a new <code>LoginContext</code> object with a name
378      * and a <code>CallbackHandler</code> object.
379      *
380      * <p>
381      *
382      * @param name the name used as the index into the
383      * <code>Configuration</code>. <p>
384      *
385      * @param callbackHandler the <code>CallbackHandler</code> object used by
386      * LoginModules to communicate with the user.
387      *
388      * @exception LoginException if the caller-specified <code>name</code>
389      * does not appear in the <code>Configuration</code>
390      * and there is no <code>Configuration</code> entry
391      * for "<i>other</i>", or if the caller-specified
392      * <code>callbackHandler</code> is <code>null</code>.
393      * <p>
394      * @exception SecurityException if a SecurityManager is set and
395      * the caller does not have
396      * AuthPermission("createLoginContext.<i>name</i>"),
397      * or if a configuration entry for <i>name</i> does not exist and
398      * the caller does not additionally have
399      * AuthPermission("createLoginContext.other")
400      */

401     public LoginContext(String JavaDoc name, CallbackHandler callbackHandler)
402     throws LoginException JavaDoc {
403     init(name);
404     if (callbackHandler == null)
405         throw new LoginException JavaDoc(ResourcesMgr.getString
406                 ("invalid null CallbackHandler provided"));
407     this.callbackHandler = new SecureCallbackHandler
408                 (java.security.AccessController.getContext(),
409                 callbackHandler);
410     }
411
412     /**
413      * Instantiate a new <code>LoginContext</code> object with a name,
414      * a <code>Subject</code> to be authenticated, and a
415      * <code>CallbackHandler</code> object.
416      *
417      * <p>
418      *
419      * @param name the name used as the index into the
420      * <code>Configuration</code>. <p>
421      *
422      * @param subject the <code>Subject</code> to authenticate. <p>
423      *
424      * @param callbackHandler the <code>CallbackHandler</code> object used by
425      * LoginModules to communicate with the user.
426      *
427      * @exception LoginException if the caller-specified <code>name</code>
428      * does not appear in the <code>Configuration</code>
429      * and there is no <code>Configuration</code> entry
430      * for "<i>other</i>", or if the caller-specified
431      * <code>subject</code> is <code>null</code>,
432      * or if the caller-specified
433      * <code>callbackHandler</code> is <code>null</code>.
434      * <p>
435      * @exception SecurityException if a SecurityManager is set and
436      * the caller does not have
437      * AuthPermission("createLoginContext.<i>name</i>"),
438      * or if a configuration entry for <i>name</i> does not exist and
439      * the caller does not additionally have
440      * AuthPermission("createLoginContext.other")
441      */

442     public LoginContext(String JavaDoc name, Subject JavaDoc subject,
443             CallbackHandler callbackHandler) throws LoginException JavaDoc {
444     this(name, subject);
445     if (callbackHandler == null)
446         throw new LoginException JavaDoc(ResourcesMgr.getString
447                 ("invalid null CallbackHandler provided"));
448     this.callbackHandler = new SecureCallbackHandler
449                 (java.security.AccessController.getContext(),
450                 callbackHandler);
451     }
452
453     /**
454      * Instantiate a new <code>LoginContext</code> object with a name,
455      * a <code>Subject</code> to be authenticated,
456      * a <code>CallbackHandler</code> object, and a login
457      * <code>Configuration</code>.
458      *
459      * <p>
460      *
461      * @param name the name used as the index into the caller-specified
462      * <code>Configuration</code>. <p>
463      *
464      * @param subject the <code>Subject</code> to authenticate,
465      * or <code>null</code>. <p>
466      *
467      * @param callbackHandler the <code>CallbackHandler</code> object used by
468      * LoginModules to communicate with the user, or <code>null</code>.
469      * <p>
470      *
471      * @param config the <code>Configuration</code> that lists the
472      * login modules to be called to perform the authentication,
473      * or <code>null</code>.
474      *
475      * @exception LoginException if the caller-specified <code>name</code>
476      * does not appear in the <code>Configuration</code>
477      * and there is no <code>Configuration</code> entry
478      * for "<i>other</i>".
479      * <p>
480      * @exception SecurityException if a SecurityManager is set,
481      * <i>config</i> is <code>null</code>,
482      * and either the caller does not have
483      * AuthPermission("createLoginContext.<i>name</i>"),
484      * or if a configuration entry for <i>name</i> does not exist and
485      * the caller does not additionally have
486      * AuthPermission("createLoginContext.other")
487      *
488      * @since 1.5
489      */

490     public LoginContext(String JavaDoc name, Subject JavaDoc subject,
491                         CallbackHandler callbackHandler,
492                         Configuration JavaDoc config) throws LoginException JavaDoc {
493     this.config = config;
494     configProvided = (config != null) ? true : false;
495     if (configProvided) {
496         creatorAcc = java.security.AccessController.getContext();
497     }
498     
499     init(name);
500     if (subject != null) {
501         this.subject = subject;
502         subjectProvided = true;
503     }
504     if (callbackHandler == null) {
505         loadDefaultCallbackHandler();
506     } else if (!configProvided) {
507         this.callbackHandler = new SecureCallbackHandler
508                 (java.security.AccessController.getContext(),
509                 callbackHandler);
510     } else {
511         this.callbackHandler = callbackHandler;
512     }
513     }
514
515     /**
516      * Perform the authentication.
517      *
518      * <p> This method invokes the <code>login</code> method for each
519      * LoginModule configured for the <i>name</i> specified to the
520      * <code>LoginContext</code> constructor, as determined by the login
521      * <code>Configuration</code>. Each <code>LoginModule</code>
522      * then performs its respective type of authentication
523      * (username/password, smart card pin verification, etc.).
524      *
525      * <p> This method completes a 2-phase authentication process by
526      * calling each configured LoginModule's <code>commit</code> method
527      * if the overall authentication succeeded (the relevant REQUIRED,
528      * REQUISITE, SUFFICIENT, and OPTIONAL LoginModules succeeded),
529      * or by calling each configured LoginModule's <code>abort</code> method
530      * if the overall authentication failed. If authentication succeeded,
531      * each successful LoginModule's <code>commit</code> method associates
532      * the relevant Principals and Credentials with the <code>Subject</code>.
533      * If authentication failed, each LoginModule's <code>abort</code> method
534      * removes/destroys any previously stored state.
535      *
536      * <p> If the <code>commit</code> phase of the authentication process
537      * fails, then the overall authentication fails and this method
538      * invokes the <code>abort</code> method for each configured
539      * <code>LoginModule</code>.
540      *
541      * <p> If the <code>abort</code> phase
542      * fails for any reason, then this method propagates the
543      * original exception thrown either during the <code>login</code> phase
544      * or the <code>commit</code> phase. In either case, the overall
545      * authentication fails.
546      *
547      * <p> In the case where multiple LoginModules fail,
548      * this method propagates the exception raised by the first
549      * <code>LoginModule</code> which failed.
550      *
551      * <p> Note that if this method enters the <code>abort</code> phase
552      * (either the <code>login</code> or <code>commit</code> phase failed),
553      * this method invokes all LoginModules configured for the
554      * application regardless of their respective <code>Configuration</code>
555      * flag parameters. Essentially this means that <code>Requisite</code>
556      * and <code>Sufficient</code> semantics are ignored during the
557      * <code>abort</code> phase. This guarantees that proper cleanup
558      * and state restoration can take place.
559      *
560      * <p>
561      *
562      * @exception LoginException if the authentication fails.
563      */

564     public void login() throws LoginException JavaDoc {
565
566     loginSucceeded = false;
567
568     if (subject == null) {
569         subject = new Subject JavaDoc();
570     }
571
572     try {
573         if (configProvided) {
574         // module invoked in doPrivileged with creatorAcc
575
invokeCreatorPriv(LOGIN_METHOD);
576         invokeCreatorPriv(COMMIT_METHOD);
577         } else {
578         // module invoked in doPrivileged
579
invokePriv(LOGIN_METHOD);
580         invokePriv(COMMIT_METHOD);
581         }
582         loginSucceeded = true;
583     } catch (LoginException JavaDoc le) {
584         try {
585         if (configProvided) {
586             invokeCreatorPriv(ABORT_METHOD);
587         } else {
588             invokePriv(ABORT_METHOD);
589         }
590         } catch (LoginException JavaDoc le2) {
591         throw le;
592         }
593         throw le;
594     }
595     }
596
597     /**
598      * Logout the <code>Subject</code>.
599      *
600      * <p> This method invokes the <code>logout</code> method for each
601      * <code>LoginModule</code> configured for this <code>LoginContext</code>.
602      * Each <code>LoginModule</code> performs its respective logout procedure
603      * which may include removing/destroying
604      * <code>Principal</code> and <code>Credential</code> information
605      * from the <code>Subject</code> and state cleanup.
606      *
607      * <p> Note that this method invokes all LoginModules configured for the
608      * application regardless of their respective
609      * <code>Configuration</code> flag parameters. Essentially this means
610      * that <code>Requisite</code> and <code>Sufficient</code> semantics are
611      * ignored for this method. This guarantees that proper cleanup
612      * and state restoration can take place.
613      *
614      * <p>
615      *
616      * @exception LoginException if the logout fails.
617      */

618     public void logout() throws LoginException JavaDoc {
619     if (subject == null) {
620         throw new LoginException JavaDoc(ResourcesMgr.getString
621         ("null subject - logout called before login"));
622     }
623
624     if (configProvided) {
625         // module invoked in doPrivileged with creatorAcc
626
invokeCreatorPriv(LOGOUT_METHOD);
627     } else {
628         // module invoked in doPrivileged
629
invokePriv(LOGOUT_METHOD);
630     }
631     }
632
633     /**
634      * Return the authenticated Subject.
635      *
636      * <p>
637      *
638      * @return the authenticated Subject. If the caller specified a
639      * Subject to this LoginContext's constructor,
640      * this method returns the caller-specified Subject.
641      * If a Subject was not specified and authentication succeeds,
642      * this method returns the Subject instantiated and used for
643      * authentication by this LoginContext.
644      * If a Subject was not specified, and authentication fails or
645      * has not been attempted, this method returns null.
646      */

647     public Subject JavaDoc getSubject() {
648     if (!loginSucceeded && !subjectProvided)
649         return null;
650     return subject;
651     }
652
653     private void clearState() {
654     moduleIndex = 0;
655     firstError = null;
656     firstRequiredError = null;
657     success = false;
658     }
659
660     private void throwException(LoginException JavaDoc originalError, LoginException JavaDoc le)
661     throws LoginException JavaDoc {
662
663     // first clear state
664
clearState();
665     
666     // throw the exception
667
LoginException JavaDoc error = (originalError != null) ? originalError : le;
668     throw error;
669     }
670
671     /**
672      * Invokes the login, commit, and logout methods
673      * from a LoginModule inside a doPrivileged block.
674      *
675      * This version is called if the caller did not instantiate
676      * the LoginContext with a Configuration object.
677      */

678     private void invokePriv(final String JavaDoc methodName) throws LoginException JavaDoc {
679     try {
680         java.security.AccessController.doPrivileged
681         (new java.security.PrivilegedExceptionAction JavaDoc() {
682         public Object JavaDoc run() throws LoginException JavaDoc {
683             invoke(methodName);
684             return null;
685         }
686         });
687     } catch (java.security.PrivilegedActionException JavaDoc pae) {
688         throw (LoginException JavaDoc)pae.getException();
689     }
690     }
691
692     /**
693      * Invokes the login, commit, and logout methods
694      * from a LoginModule inside a doPrivileged block restricted
695      * by creatorAcc
696      *
697      * This version is called if the caller instantiated
698      * the LoginContext with a Configuration object.
699      */

700     private void invokeCreatorPriv(final String JavaDoc methodName)
701         throws LoginException JavaDoc {
702     try {
703         java.security.AccessController.doPrivileged
704         (new java.security.PrivilegedExceptionAction JavaDoc() {
705         public Object JavaDoc run() throws LoginException JavaDoc {
706             invoke(methodName);
707             return null;
708         }
709         }, creatorAcc);
710     } catch (java.security.PrivilegedActionException JavaDoc pae) {
711         throw (LoginException JavaDoc)pae.getException();
712     }
713     }
714
715     private void invoke(String JavaDoc methodName) throws LoginException JavaDoc {
716
717     // start at moduleIndex
718
// - this can only be non-zero if methodName is LOGIN_METHOD
719

720     for (int i = moduleIndex; i < moduleStack.length; i++, moduleIndex++) {
721         try {
722
723         int mIndex = 0;
724         Method JavaDoc[] methods = null;
725
726         if (moduleStack[i].module != null) {
727             methods = moduleStack[i].module.getClass().getMethods();
728         } else {
729
730             // instantiate the LoginModule
731
Class JavaDoc c = Class.forName
732                 (moduleStack[i].entry.getLoginModuleName(),
733                 true,
734                 contextClassLoader);
735
736             Constructor JavaDoc constructor = c.getConstructor(PARAMS);
737             Object JavaDoc[] args = { };
738
739             // allow any object to be a LoginModule
740
// as long as it conforms to the interface
741
moduleStack[i].module = constructor.newInstance(args);
742
743             methods = moduleStack[i].module.getClass().getMethods();
744
745             // call the LoginModule's initialize method
746
for (mIndex = 0; mIndex < methods.length; mIndex++) {
747             if (methods[mIndex].getName().equals(INIT_METHOD))
748                 break;
749             }
750
751             Object JavaDoc[] initArgs = {subject,
752                     callbackHandler,
753                     state,
754                     moduleStack[i].entry.getOptions() };
755             // invoke the LoginModule initialize method
756
methods[mIndex].invoke(moduleStack[i].module, initArgs);
757         }
758
759         // find the requested method in the LoginModule
760
for (mIndex = 0; mIndex < methods.length; mIndex++) {
761             if (methods[mIndex].getName().equals(methodName))
762             break;
763         }
764
765         // set up the arguments to be passed to the LoginModule method
766
Object JavaDoc[] args = { };
767
768         // invoke the LoginModule method
769
boolean status = ((Boolean JavaDoc)methods[mIndex].invoke
770                 (moduleStack[i].module, args)).booleanValue();
771
772         if (status == true) {
773
774             // if SUFFICIENT, return if no prior REQUIRED errors
775
if (!methodName.equals(ABORT_METHOD) &&
776                 !methodName.equals(LOGOUT_METHOD) &&
777             moduleStack[i].entry.getControlFlag() ==
778             AppConfigurationEntry.LoginModuleControlFlag.SUFFICIENT &&
779             firstRequiredError == null) {
780
781             // clear state
782
clearState();
783
784             if (debug != null)
785                 debug.println(methodName + " SUFFICIENT success");
786             return;
787             }
788
789             if (debug != null)
790             debug.println(methodName + " success");
791             success = true;
792         } else {
793             if (debug != null)
794             debug.println(methodName + " ignored");
795         }
796
797         } catch (NoSuchMethodException JavaDoc nsme) {
798         MessageFormat JavaDoc form = new MessageFormat JavaDoc(ResourcesMgr.getString
799             ("unable to instantiate LoginModule, module, because " +
800             "it does not provide a no-argument constructor"));
801         Object JavaDoc[] source = {moduleStack[i].entry.getLoginModuleName()};
802         throwException(null, new LoginException JavaDoc(form.format(source)));
803         } catch (InstantiationException JavaDoc ie) {
804         throwException(null, new LoginException JavaDoc(ResourcesMgr.getString
805             ("unable to instantiate LoginModule: ") +
806             ie.getMessage()));
807         } catch (ClassNotFoundException JavaDoc cnfe) {
808         throwException(null, new LoginException JavaDoc(ResourcesMgr.getString
809             ("unable to find LoginModule class: ") +
810             cnfe.getMessage()));
811         } catch (IllegalAccessException JavaDoc iae) {
812         throwException(null, new LoginException JavaDoc(ResourcesMgr.getString
813             ("unable to access LoginModule: ") +
814             iae.getMessage()));
815         } catch (InvocationTargetException JavaDoc ite) {
816
817         // failure cases
818

819         LoginException JavaDoc le;
820
821         if (ite.getCause() instanceof PendingException &&
822             methodName.equals(LOGIN_METHOD)) {
823             
824             // XXX
825
//
826
// if a module's LOGIN_METHOD threw a PendingException
827
// then immediately throw it.
828
//
829
// when LoginContext is called again,
830
// the module that threw the exception is invoked first
831
// (the module list is not invoked from the start).
832
// previously thrown exception state is still present.
833
//
834
// it is assumed that the module which threw
835
// the exception can have its
836
// LOGIN_METHOD invoked twice in a row
837
// without any commit/abort in between.
838
//
839
// in all cases when LoginContext returns
840
// (either via natural return or by throwing an exception)
841
// we need to call clearState before returning.
842
// the only time that is not true is in this case -
843
// do not call throwException here.
844

845             throw (PendingException)ite.getCause();
846
847         } else if (ite.getCause() instanceof LoginException JavaDoc) {
848
849             le = (LoginException JavaDoc)ite.getCause();
850
851         } else if (ite.getCause() instanceof SecurityException JavaDoc) {
852
853             // do not want privacy leak
854
// (e.g., sensitive file path in exception msg)
855

856             le = new LoginException JavaDoc("Security Exception");
857             le.initCause(new SecurityException JavaDoc());
858             if (debug != null) {
859             debug.println
860                 ("original security exception with detail msg " +
861                 "replaced by new exception with empty detail msg");
862             debug.println("original security exception: " +
863                 ite.getCause().toString());
864             }
865         } else {
866
867             // capture an unexpected LoginModule exception
868
java.io.StringWriter JavaDoc sw = new java.io.StringWriter JavaDoc();
869             ite.getCause().printStackTrace
870                         (new java.io.PrintWriter JavaDoc(sw));
871             sw.flush();
872             le = new LoginException JavaDoc(sw.toString());
873         }
874
875         if (moduleStack[i].entry.getControlFlag() ==
876             AppConfigurationEntry.LoginModuleControlFlag.REQUISITE) {
877
878             if (debug != null)
879             debug.println(methodName + " REQUISITE failure");
880
881             // if REQUISITE, then immediately throw an exception
882
if (methodName.equals(ABORT_METHOD) ||
883                 methodName.equals(LOGOUT_METHOD)) {
884             if (firstRequiredError == null)
885                 firstRequiredError = le;
886             } else {
887             throwException(firstRequiredError, le);
888             }
889
890         } else if (moduleStack[i].entry.getControlFlag() ==
891             AppConfigurationEntry.LoginModuleControlFlag.REQUIRED) {
892
893             if (debug != null)
894             debug.println(methodName + " REQUIRED failure");
895
896             // mark down that a REQUIRED module failed
897
if (firstRequiredError == null)
898             firstRequiredError = le;
899
900         } else {
901
902             if (debug != null)
903             debug.println(methodName + " OPTIONAL failure");
904
905             // mark down that an OPTIONAL module failed
906
if (firstError == null)
907             firstError = le;
908         }
909         }
910     }
911
912     // we went thru all the LoginModules.
913
if (firstRequiredError != null) {
914         // a REQUIRED module failed -- return the error
915
throwException(firstRequiredError, null);
916     } else if (success == false && firstError != null) {
917         // no module succeeded -- return the first error
918
throwException(firstError, null);
919     } else if (success == false) {
920         // no module succeeded -- all modules were IGNORED
921
throwException(new LoginException JavaDoc
922         (ResourcesMgr.getString("Login Failure: all modules ignored")),
923         null);
924     } else {
925         // success
926

927         clearState();
928         return;
929     }
930     }
931
932     /**
933      * Wrap the caller-specified CallbackHandler in our own
934      * and invoke it within a privileged block, constrained by
935      * the caller's AccessControlContext.
936      */

937     private static class SecureCallbackHandler implements CallbackHandler {
938
939     private final java.security.AccessControlContext JavaDoc acc;
940     private final CallbackHandler ch;
941
942     SecureCallbackHandler(java.security.AccessControlContext JavaDoc acc,
943             CallbackHandler ch) {
944         this.acc = acc;
945         this.ch = ch;
946     }
947
948     public void handle(final Callback[] callbacks)
949         throws java.io.IOException JavaDoc, UnsupportedCallbackException {
950         try {
951         java.security.AccessController.doPrivileged
952             (new java.security.PrivilegedExceptionAction JavaDoc() {
953             public Object JavaDoc run() throws java.io.IOException JavaDoc,
954                     UnsupportedCallbackException {
955             ch.handle(callbacks);
956             return null;
957             }
958         }, acc);
959         } catch (java.security.PrivilegedActionException JavaDoc pae) {
960         if (pae.getException() instanceof java.io.IOException JavaDoc) {
961             throw (java.io.IOException JavaDoc)pae.getException();
962         } else {
963             throw (UnsupportedCallbackException)pae.getException();
964         }
965         }
966     }
967     }
968
969     /**
970      * LoginModule information -
971      * incapsulates Configuration info and actual module instances
972      */

973     private static class ModuleInfo {
974     AppConfigurationEntry JavaDoc entry;
975     Object JavaDoc module;
976
977     ModuleInfo(AppConfigurationEntry JavaDoc newEntry, Object JavaDoc newModule) {
978         this.entry = newEntry;
979         this.module = newModule;
980     }
981     }
982 }
983
Popular Tags