Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/javax/security/auth/login/LoginContext.java
38918 views
1
/*
2
* Copyright (c) 1998, 2013, Oracle and/or its affiliates. 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. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
package javax.security.auth.login;
27
28
import java.lang.reflect.Constructor;
29
import java.lang.reflect.Method;
30
import java.lang.reflect.InvocationTargetException;
31
import java.util.LinkedList;
32
import java.util.Map;
33
import java.util.HashMap;
34
import java.text.MessageFormat;
35
import javax.security.auth.Subject;
36
import javax.security.auth.AuthPermission;
37
import javax.security.auth.callback.*;
38
import java.security.AccessController;
39
import java.security.AccessControlContext;
40
import sun.security.util.PendingException;
41
import sun.security.util.ResourcesMgr;
42
43
/**
44
* <p> The {@code LoginContext} 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} specifies the authentication technology, or
48
* {@code LoginModule}, 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}.
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} 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} method to authenticate
71
* a {@code Subject}. The {@code login} 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} 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} method. Principals and Credentials associated
83
* with the Subject may be retrieved by invoking the Subject's
84
* respective {@code getPrincipals}, {@code getPublicCredentials},
85
* and {@code getPrivateCredentials} methods.
86
*
87
* <p> To logout the Subject, the caller calls
88
* the {@code logout} method. As with the {@code login}
89
* method, this {@code logout} method invokes the {@code logout}
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}
100
* <ul>
101
* <li> If the constructor has a Subject
102
* input parameter, the LoginContext uses the caller-specified
103
* Subject object.
104
*
105
* <li> If the caller specifies a {@code null} Subject
106
* and a {@code null} value is permitted,
107
* the LoginContext instantiates a new Subject.
108
*
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}
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}
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} method.
130
* If the Configuration has no entries for the specified <i>name</i>,
131
* then the {@code LoginContext} calls
132
* {@code getAppConfigurationEntry} 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} is thrown.
135
*
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} 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
*
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} for the caller,
148
* and invokes the configured modules from within an
149
* {@code AccessController.doPrivileged} 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}
157
* <ul>
158
* <li> If the constructor has a CallbackHandler
159
* input parameter, the LoginContext uses the caller-specified
160
* CallbackHandler object.
161
*
162
* <li> If the constructor does <b>not</b> have a CallbackHandler
163
* input parameter, or if the caller specifies a {@code null}
164
* CallbackHandler object (and a {@code null} value is permitted),
165
* the LoginContext queries the
166
* {@code auth.login.defaultCallbackHandler} security property for the
167
* fully qualified class name of a default handler
168
* implementation. 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
*
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} method implementation invokes the
181
* specified CallbackHandler's {@code handle} method in a
182
* {@code java.security.AccessController.doPrivileged} call
183
* constrained by the caller's current {@code AccessControlContext}.
184
* </ul>
185
* </ol>
186
*
187
* @see java.security.Security
188
* @see javax.security.auth.AuthPermission
189
* @see javax.security.auth.Subject
190
* @see javax.security.auth.callback.CallbackHandler
191
* @see javax.security.auth.login.Configuration
192
* @see javax.security.auth.spi.LoginModule
193
* @see java.security.Security security properties
194
*/
195
public class LoginContext {
196
197
private static final String INIT_METHOD = "initialize";
198
private static final String LOGIN_METHOD = "login";
199
private static final String COMMIT_METHOD = "commit";
200
private static final String ABORT_METHOD = "abort";
201
private static final String LOGOUT_METHOD = "logout";
202
private static final String OTHER = "other";
203
private static final String DEFAULT_HANDLER =
204
"auth.login.defaultCallbackHandler";
205
private Subject subject = null;
206
private boolean subjectProvided = false;
207
private boolean loginSucceeded = false;
208
private CallbackHandler callbackHandler;
209
private Map<String,?> state = new HashMap<String,Object>();
210
211
private Configuration config;
212
private AccessControlContext creatorAcc = null; // customized config only
213
private ModuleInfo[] moduleStack;
214
private ClassLoader contextClassLoader = null;
215
private static final Class<?>[] PARAMS = { };
216
217
// state saved in the event a user-specified asynchronous exception
218
// was specified and thrown
219
220
private int moduleIndex = 0;
221
private LoginException firstError = null;
222
private LoginException firstRequiredError = null;
223
private boolean success = false;
224
225
private static final sun.security.util.Debug debug =
226
sun.security.util.Debug.getInstance("logincontext", "\t[LoginContext]");
227
228
private void init(String name) throws LoginException {
229
230
SecurityManager sm = System.getSecurityManager();
231
if (sm != null && creatorAcc == null) {
232
sm.checkPermission(new AuthPermission
233
("createLoginContext." + name));
234
}
235
236
if (name == null)
237
throw new LoginException
238
(ResourcesMgr.getString("Invalid.null.input.name"));
239
240
// get the Configuration
241
if (config == null) {
242
config = java.security.AccessController.doPrivileged
243
(new java.security.PrivilegedAction<Configuration>() {
244
public Configuration run() {
245
return Configuration.getConfiguration();
246
}
247
});
248
}
249
250
// get the LoginModules configured for this application
251
AppConfigurationEntry[] entries = config.getAppConfigurationEntry(name);
252
if (entries == null) {
253
254
if (sm != null && creatorAcc == null) {
255
sm.checkPermission(new AuthPermission
256
("createLoginContext." + OTHER));
257
}
258
259
entries = config.getAppConfigurationEntry(OTHER);
260
if (entries == null) {
261
MessageFormat form = new MessageFormat(ResourcesMgr.getString
262
("No.LoginModules.configured.for.name"));
263
Object[] source = {name};
264
throw new LoginException(form.format(source));
265
}
266
}
267
moduleStack = new ModuleInfo[entries.length];
268
for (int i = 0; i < entries.length; i++) {
269
// clone returned array
270
moduleStack[i] = new ModuleInfo
271
(new AppConfigurationEntry
272
(entries[i].getLoginModuleName(),
273
entries[i].getControlFlag(),
274
entries[i].getOptions()),
275
null);
276
}
277
278
contextClassLoader = java.security.AccessController.doPrivileged
279
(new java.security.PrivilegedAction<ClassLoader>() {
280
public ClassLoader run() {
281
ClassLoader loader =
282
Thread.currentThread().getContextClassLoader();
283
if (loader == null) {
284
// Don't use bootstrap class loader directly to ensure
285
// proper package access control!
286
loader = ClassLoader.getSystemClassLoader();
287
}
288
289
return loader;
290
}
291
});
292
}
293
294
private void loadDefaultCallbackHandler() throws LoginException {
295
296
// get the default handler class
297
try {
298
299
final ClassLoader finalLoader = contextClassLoader;
300
301
this.callbackHandler = java.security.AccessController.doPrivileged(
302
new java.security.PrivilegedExceptionAction<CallbackHandler>() {
303
public CallbackHandler run() throws Exception {
304
String defaultHandler = java.security.Security.getProperty
305
(DEFAULT_HANDLER);
306
if (defaultHandler == null || defaultHandler.length() == 0)
307
return null;
308
Class<? extends CallbackHandler> c = Class.forName(
309
defaultHandler, true,
310
finalLoader).asSubclass(CallbackHandler.class);
311
return c.newInstance();
312
}
313
});
314
} catch (java.security.PrivilegedActionException pae) {
315
throw new LoginException(pae.getException().toString());
316
}
317
318
// secure it with the caller's ACC
319
if (this.callbackHandler != null && creatorAcc == null) {
320
this.callbackHandler = new SecureCallbackHandler
321
(java.security.AccessController.getContext(),
322
this.callbackHandler);
323
}
324
}
325
326
/**
327
* Instantiate a new {@code LoginContext} object with a name.
328
*
329
* @param name the name used as the index into the
330
* {@code Configuration}.
331
*
332
* @exception LoginException if the caller-specified {@code name}
333
* does not appear in the {@code Configuration}
334
* and there is no {@code Configuration} entry
335
* for "<i>other</i>", or if the
336
* <i>auth.login.defaultCallbackHandler</i>
337
* security property was set, but the implementation
338
* class could not be loaded.
339
* <p>
340
* @exception SecurityException if a SecurityManager is set and
341
* the caller does not have
342
* AuthPermission("createLoginContext.<i>name</i>"),
343
* or if a configuration entry for <i>name</i> does not exist and
344
* the caller does not additionally have
345
* AuthPermission("createLoginContext.other")
346
*/
347
public LoginContext(String name) throws LoginException {
348
init(name);
349
loadDefaultCallbackHandler();
350
}
351
352
/**
353
* Instantiate a new {@code LoginContext} object with a name
354
* and a {@code Subject} object.
355
*
356
* <p>
357
*
358
* @param name the name used as the index into the
359
* {@code Configuration}. <p>
360
*
361
* @param subject the {@code Subject} to authenticate.
362
*
363
* @exception LoginException if the caller-specified {@code name}
364
* does not appear in the {@code Configuration}
365
* and there is no {@code Configuration} entry
366
* for "<i>other</i>", if the caller-specified {@code subject}
367
* is {@code null}, or if the
368
* <i>auth.login.defaultCallbackHandler</i>
369
* security property was set, but the implementation
370
* class could not be loaded.
371
* <p>
372
* @exception SecurityException if a SecurityManager is set and
373
* the caller does not have
374
* AuthPermission("createLoginContext.<i>name</i>"),
375
* or if a configuration entry for <i>name</i> does not exist and
376
* the caller does not additionally have
377
* AuthPermission("createLoginContext.other")
378
*/
379
public LoginContext(String name, Subject subject)
380
throws LoginException {
381
init(name);
382
if (subject == null)
383
throw new LoginException
384
(ResourcesMgr.getString("invalid.null.Subject.provided"));
385
this.subject = subject;
386
subjectProvided = true;
387
loadDefaultCallbackHandler();
388
}
389
390
/**
391
* Instantiate a new {@code LoginContext} object with a name
392
* and a {@code CallbackHandler} object.
393
*
394
* <p>
395
*
396
* @param name the name used as the index into the
397
* {@code Configuration}. <p>
398
*
399
* @param callbackHandler the {@code CallbackHandler} object used by
400
* LoginModules to communicate with the user.
401
*
402
* @exception LoginException if the caller-specified {@code name}
403
* does not appear in the {@code Configuration}
404
* and there is no {@code Configuration} entry
405
* for "<i>other</i>", or if the caller-specified
406
* {@code callbackHandler} is {@code null}.
407
* <p>
408
* @exception SecurityException if a SecurityManager is set and
409
* the caller does not have
410
* AuthPermission("createLoginContext.<i>name</i>"),
411
* or if a configuration entry for <i>name</i> does not exist and
412
* the caller does not additionally have
413
* AuthPermission("createLoginContext.other")
414
*/
415
public LoginContext(String name, CallbackHandler callbackHandler)
416
throws LoginException {
417
init(name);
418
if (callbackHandler == null)
419
throw new LoginException(ResourcesMgr.getString
420
("invalid.null.CallbackHandler.provided"));
421
this.callbackHandler = new SecureCallbackHandler
422
(java.security.AccessController.getContext(),
423
callbackHandler);
424
}
425
426
/**
427
* Instantiate a new {@code LoginContext} object with a name,
428
* a {@code Subject} to be authenticated, and a
429
* {@code CallbackHandler} object.
430
*
431
* <p>
432
*
433
* @param name the name used as the index into the
434
* {@code Configuration}. <p>
435
*
436
* @param subject the {@code Subject} to authenticate. <p>
437
*
438
* @param callbackHandler the {@code CallbackHandler} object used by
439
* LoginModules to communicate with the user.
440
*
441
* @exception LoginException if the caller-specified {@code name}
442
* does not appear in the {@code Configuration}
443
* and there is no {@code Configuration} entry
444
* for "<i>other</i>", or if the caller-specified
445
* {@code subject} is {@code null},
446
* or if the caller-specified
447
* {@code callbackHandler} is {@code null}.
448
* <p>
449
* @exception SecurityException if a SecurityManager is set and
450
* the caller does not have
451
* AuthPermission("createLoginContext.<i>name</i>"),
452
* or if a configuration entry for <i>name</i> does not exist and
453
* the caller does not additionally have
454
* AuthPermission("createLoginContext.other")
455
*/
456
public LoginContext(String name, Subject subject,
457
CallbackHandler callbackHandler) throws LoginException {
458
this(name, subject);
459
if (callbackHandler == null)
460
throw new LoginException(ResourcesMgr.getString
461
("invalid.null.CallbackHandler.provided"));
462
this.callbackHandler = new SecureCallbackHandler
463
(java.security.AccessController.getContext(),
464
callbackHandler);
465
}
466
467
/**
468
* Instantiate a new {@code LoginContext} object with a name,
469
* a {@code Subject} to be authenticated,
470
* a {@code CallbackHandler} object, and a login
471
* {@code Configuration}.
472
*
473
* <p>
474
*
475
* @param name the name used as the index into the caller-specified
476
* {@code Configuration}. <p>
477
*
478
* @param subject the {@code Subject} to authenticate,
479
* or {@code null}. <p>
480
*
481
* @param callbackHandler the {@code CallbackHandler} object used by
482
* LoginModules to communicate with the user, or {@code null}.
483
* <p>
484
*
485
* @param config the {@code Configuration} that lists the
486
* login modules to be called to perform the authentication,
487
* or {@code null}.
488
*
489
* @exception LoginException if the caller-specified {@code name}
490
* does not appear in the {@code Configuration}
491
* and there is no {@code Configuration} entry
492
* for "<i>other</i>".
493
* <p>
494
* @exception SecurityException if a SecurityManager is set,
495
* <i>config</i> is {@code null},
496
* and either the caller does not have
497
* AuthPermission("createLoginContext.<i>name</i>"),
498
* or if a configuration entry for <i>name</i> does not exist and
499
* the caller does not additionally have
500
* AuthPermission("createLoginContext.other")
501
*
502
* @since 1.5
503
*/
504
public LoginContext(String name, Subject subject,
505
CallbackHandler callbackHandler,
506
Configuration config) throws LoginException {
507
this.config = config;
508
if (config != null) {
509
creatorAcc = java.security.AccessController.getContext();
510
}
511
512
init(name);
513
if (subject != null) {
514
this.subject = subject;
515
subjectProvided = true;
516
}
517
if (callbackHandler == null) {
518
loadDefaultCallbackHandler();
519
} else if (creatorAcc == null) {
520
this.callbackHandler = new SecureCallbackHandler
521
(java.security.AccessController.getContext(),
522
callbackHandler);
523
} else {
524
this.callbackHandler = callbackHandler;
525
}
526
}
527
528
/**
529
* Perform the authentication.
530
*
531
* <p> This method invokes the {@code login} method for each
532
* LoginModule configured for the <i>name</i> specified to the
533
* {@code LoginContext} constructor, as determined by the login
534
* {@code Configuration}. Each {@code LoginModule}
535
* then performs its respective type of authentication
536
* (username/password, smart card pin verification, etc.).
537
*
538
* <p> This method completes a 2-phase authentication process by
539
* calling each configured LoginModule's {@code commit} method
540
* if the overall authentication succeeded (the relevant REQUIRED,
541
* REQUISITE, SUFFICIENT, and OPTIONAL LoginModules succeeded),
542
* or by calling each configured LoginModule's {@code abort} method
543
* if the overall authentication failed. If authentication succeeded,
544
* each successful LoginModule's {@code commit} method associates
545
* the relevant Principals and Credentials with the {@code Subject}.
546
* If authentication failed, each LoginModule's {@code abort} method
547
* removes/destroys any previously stored state.
548
*
549
* <p> If the {@code commit} phase of the authentication process
550
* fails, then the overall authentication fails and this method
551
* invokes the {@code abort} method for each configured
552
* {@code LoginModule}.
553
*
554
* <p> If the {@code abort} phase
555
* fails for any reason, then this method propagates the
556
* original exception thrown either during the {@code login} phase
557
* or the {@code commit} phase. In either case, the overall
558
* authentication fails.
559
*
560
* <p> In the case where multiple LoginModules fail,
561
* this method propagates the exception raised by the first
562
* {@code LoginModule} which failed.
563
*
564
* <p> Note that if this method enters the {@code abort} phase
565
* (either the {@code login} or {@code commit} phase failed),
566
* this method invokes all LoginModules configured for the
567
* application regardless of their respective {@code Configuration}
568
* flag parameters. Essentially this means that {@code Requisite}
569
* and {@code Sufficient} semantics are ignored during the
570
* {@code abort} phase. This guarantees that proper cleanup
571
* and state restoration can take place.
572
*
573
* <p>
574
*
575
* @exception LoginException if the authentication fails.
576
*/
577
public void login() throws LoginException {
578
579
loginSucceeded = false;
580
581
if (subject == null) {
582
subject = new Subject();
583
}
584
585
try {
586
// module invoked in doPrivileged
587
invokePriv(LOGIN_METHOD);
588
invokePriv(COMMIT_METHOD);
589
loginSucceeded = true;
590
} catch (LoginException le) {
591
try {
592
invokePriv(ABORT_METHOD);
593
} catch (LoginException le2) {
594
throw le;
595
}
596
throw le;
597
}
598
}
599
600
/**
601
* Logout the {@code Subject}.
602
*
603
* <p> This method invokes the {@code logout} method for each
604
* {@code LoginModule} configured for this {@code LoginContext}.
605
* Each {@code LoginModule} performs its respective logout procedure
606
* which may include removing/destroying
607
* {@code Principal} and {@code Credential} information
608
* from the {@code Subject} and state cleanup.
609
*
610
* <p> Note that this method invokes all LoginModules configured for the
611
* application regardless of their respective
612
* {@code Configuration} flag parameters. Essentially this means
613
* that {@code Requisite} and {@code Sufficient} semantics are
614
* ignored for this method. This guarantees that proper cleanup
615
* and state restoration can take place.
616
*
617
* <p>
618
*
619
* @exception LoginException if the logout fails.
620
*/
621
public void logout() throws LoginException {
622
if (subject == null) {
623
throw new LoginException(ResourcesMgr.getString
624
("null.subject.logout.called.before.login"));
625
}
626
627
// module invoked in doPrivileged
628
invokePriv(LOGOUT_METHOD);
629
}
630
631
/**
632
* Return the authenticated Subject.
633
*
634
* <p>
635
*
636
* @return the authenticated Subject. If the caller specified a
637
* Subject to this LoginContext's constructor,
638
* this method returns the caller-specified Subject.
639
* If a Subject was not specified and authentication succeeds,
640
* this method returns the Subject instantiated and used for
641
* authentication by this LoginContext.
642
* If a Subject was not specified, and authentication fails or
643
* has not been attempted, this method returns null.
644
*/
645
public Subject getSubject() {
646
if (!loginSucceeded && !subjectProvided)
647
return null;
648
return subject;
649
}
650
651
private void clearState() {
652
moduleIndex = 0;
653
firstError = null;
654
firstRequiredError = null;
655
success = false;
656
}
657
658
private void throwException(LoginException originalError, LoginException le)
659
throws LoginException {
660
661
// first clear state
662
clearState();
663
664
// throw the exception
665
LoginException error = (originalError != null) ? originalError : le;
666
throw error;
667
}
668
669
/**
670
* Invokes the login, commit, and logout methods
671
* from a LoginModule inside a doPrivileged block restricted
672
* by creatorAcc (may be null).
673
*
674
* This version is called if the caller did not instantiate
675
* the LoginContext with a Configuration object.
676
*/
677
private void invokePriv(final String methodName) throws LoginException {
678
try {
679
java.security.AccessController.doPrivileged
680
(new java.security.PrivilegedExceptionAction<Void>() {
681
public Void run() throws LoginException {
682
invoke(methodName);
683
return null;
684
}
685
}, creatorAcc);
686
} catch (java.security.PrivilegedActionException pae) {
687
throw (LoginException)pae.getException();
688
}
689
}
690
691
private void invoke(String methodName) throws LoginException {
692
693
// start at moduleIndex
694
// - this can only be non-zero if methodName is LOGIN_METHOD
695
696
for (int i = moduleIndex; i < moduleStack.length; i++, moduleIndex++) {
697
try {
698
699
int mIndex = 0;
700
Method[] methods = null;
701
702
if (moduleStack[i].module != null) {
703
methods = moduleStack[i].module.getClass().getMethods();
704
} else {
705
706
// instantiate the LoginModule
707
//
708
// Allow any object to be a LoginModule as long as it
709
// conforms to the interface.
710
Class<?> c = Class.forName(
711
moduleStack[i].entry.getLoginModuleName(),
712
true,
713
contextClassLoader);
714
715
Constructor<?> constructor = c.getConstructor(PARAMS);
716
Object[] args = { };
717
moduleStack[i].module = constructor.newInstance(args);
718
719
// call the LoginModule's initialize method
720
methods = moduleStack[i].module.getClass().getMethods();
721
for (mIndex = 0; mIndex < methods.length; mIndex++) {
722
if (methods[mIndex].getName().equals(INIT_METHOD)) {
723
break;
724
}
725
}
726
727
Object[] initArgs = {subject,
728
callbackHandler,
729
state,
730
moduleStack[i].entry.getOptions() };
731
// invoke the LoginModule initialize method
732
//
733
// Throws ArrayIndexOutOfBoundsException if no such
734
// method defined. May improve to use LoginException in
735
// the future.
736
methods[mIndex].invoke(moduleStack[i].module, initArgs);
737
}
738
739
// find the requested method in the LoginModule
740
for (mIndex = 0; mIndex < methods.length; mIndex++) {
741
if (methods[mIndex].getName().equals(methodName)) {
742
break;
743
}
744
}
745
746
// set up the arguments to be passed to the LoginModule method
747
Object[] args = { };
748
749
// invoke the LoginModule method
750
//
751
// Throws ArrayIndexOutOfBoundsException if no such
752
// method defined. May improve to use LoginException in
753
// the future.
754
boolean status = ((Boolean)methods[mIndex].invoke
755
(moduleStack[i].module, args)).booleanValue();
756
757
if (status == true) {
758
759
// if SUFFICIENT, return if no prior REQUIRED errors
760
if (!methodName.equals(ABORT_METHOD) &&
761
!methodName.equals(LOGOUT_METHOD) &&
762
moduleStack[i].entry.getControlFlag() ==
763
AppConfigurationEntry.LoginModuleControlFlag.SUFFICIENT &&
764
firstRequiredError == null) {
765
766
// clear state
767
clearState();
768
769
if (debug != null)
770
debug.println(methodName + " SUFFICIENT success");
771
return;
772
}
773
774
if (debug != null)
775
debug.println(methodName + " success");
776
success = true;
777
} else {
778
if (debug != null)
779
debug.println(methodName + " ignored");
780
}
781
782
} catch (NoSuchMethodException nsme) {
783
MessageFormat form = new MessageFormat(ResourcesMgr.getString
784
("unable.to.instantiate.LoginModule.module.because.it.does.not.provide.a.no.argument.constructor"));
785
Object[] source = {moduleStack[i].entry.getLoginModuleName()};
786
throwException(null, new LoginException(form.format(source)));
787
} catch (InstantiationException ie) {
788
throwException(null, new LoginException(ResourcesMgr.getString
789
("unable.to.instantiate.LoginModule.") +
790
ie.getMessage()));
791
} catch (ClassNotFoundException cnfe) {
792
throwException(null, new LoginException(ResourcesMgr.getString
793
("unable.to.find.LoginModule.class.") +
794
cnfe.getMessage()));
795
} catch (IllegalAccessException iae) {
796
throwException(null, new LoginException(ResourcesMgr.getString
797
("unable.to.access.LoginModule.") +
798
iae.getMessage()));
799
} catch (InvocationTargetException ite) {
800
801
// failure cases
802
803
LoginException le;
804
805
if (ite.getCause() instanceof PendingException &&
806
methodName.equals(LOGIN_METHOD)) {
807
808
// XXX
809
//
810
// if a module's LOGIN_METHOD threw a PendingException
811
// then immediately throw it.
812
//
813
// when LoginContext is called again,
814
// the module that threw the exception is invoked first
815
// (the module list is not invoked from the start).
816
// previously thrown exception state is still present.
817
//
818
// it is assumed that the module which threw
819
// the exception can have its
820
// LOGIN_METHOD invoked twice in a row
821
// without any commit/abort in between.
822
//
823
// in all cases when LoginContext returns
824
// (either via natural return or by throwing an exception)
825
// we need to call clearState before returning.
826
// the only time that is not true is in this case -
827
// do not call throwException here.
828
829
throw (PendingException)ite.getCause();
830
831
} else if (ite.getCause() instanceof LoginException) {
832
833
le = (LoginException)ite.getCause();
834
835
} else if (ite.getCause() instanceof SecurityException) {
836
837
// do not want privacy leak
838
// (e.g., sensitive file path in exception msg)
839
840
le = new LoginException("Security Exception");
841
le.initCause(new SecurityException());
842
if (debug != null) {
843
debug.println
844
("original security exception with detail msg " +
845
"replaced by new exception with empty detail msg");
846
debug.println("original security exception: " +
847
ite.getCause().toString());
848
}
849
} else {
850
851
// capture an unexpected LoginModule exception
852
java.io.StringWriter sw = new java.io.StringWriter();
853
ite.getCause().printStackTrace
854
(new java.io.PrintWriter(sw));
855
sw.flush();
856
le = new LoginException(sw.toString());
857
}
858
859
if (moduleStack[i].entry.getControlFlag() ==
860
AppConfigurationEntry.LoginModuleControlFlag.REQUISITE) {
861
862
if (debug != null)
863
debug.println(methodName + " REQUISITE failure");
864
865
// if REQUISITE, then immediately throw an exception
866
if (methodName.equals(ABORT_METHOD) ||
867
methodName.equals(LOGOUT_METHOD)) {
868
if (firstRequiredError == null)
869
firstRequiredError = le;
870
} else {
871
throwException(firstRequiredError, le);
872
}
873
874
} else if (moduleStack[i].entry.getControlFlag() ==
875
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED) {
876
877
if (debug != null)
878
debug.println(methodName + " REQUIRED failure");
879
880
// mark down that a REQUIRED module failed
881
if (firstRequiredError == null)
882
firstRequiredError = le;
883
884
} else {
885
886
if (debug != null)
887
debug.println(methodName + " OPTIONAL failure");
888
889
// mark down that an OPTIONAL module failed
890
if (firstError == null)
891
firstError = le;
892
}
893
}
894
}
895
896
// we went thru all the LoginModules.
897
if (firstRequiredError != null) {
898
// a REQUIRED module failed -- return the error
899
throwException(firstRequiredError, null);
900
} else if (success == false && firstError != null) {
901
// no module succeeded -- return the first error
902
throwException(firstError, null);
903
} else if (success == false) {
904
// no module succeeded -- all modules were IGNORED
905
throwException(new LoginException
906
(ResourcesMgr.getString("Login.Failure.all.modules.ignored")),
907
null);
908
} else {
909
// success
910
911
clearState();
912
return;
913
}
914
}
915
916
/**
917
* Wrap the caller-specified CallbackHandler in our own
918
* and invoke it within a privileged block, constrained by
919
* the caller's AccessControlContext.
920
*/
921
private static class SecureCallbackHandler implements CallbackHandler {
922
923
private final java.security.AccessControlContext acc;
924
private final CallbackHandler ch;
925
926
SecureCallbackHandler(java.security.AccessControlContext acc,
927
CallbackHandler ch) {
928
this.acc = acc;
929
this.ch = ch;
930
}
931
932
public void handle(final Callback[] callbacks)
933
throws java.io.IOException, UnsupportedCallbackException {
934
try {
935
java.security.AccessController.doPrivileged
936
(new java.security.PrivilegedExceptionAction<Void>() {
937
public Void run() throws java.io.IOException,
938
UnsupportedCallbackException {
939
ch.handle(callbacks);
940
return null;
941
}
942
}, acc);
943
} catch (java.security.PrivilegedActionException pae) {
944
if (pae.getException() instanceof java.io.IOException) {
945
throw (java.io.IOException)pae.getException();
946
} else {
947
throw (UnsupportedCallbackException)pae.getException();
948
}
949
}
950
}
951
}
952
953
/**
954
* LoginModule information -
955
* incapsulates Configuration info and actual module instances
956
*/
957
private static class ModuleInfo {
958
AppConfigurationEntry entry;
959
Object module;
960
961
ModuleInfo(AppConfigurationEntry newEntry, Object newModule) {
962
this.entry = newEntry;
963
this.module = newModule;
964
}
965
}
966
}
967
968