History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/com/sun/security/auth/module/KeyStoreLoginModule.java (1 of 18352 files). Show all

Unified · Split

java/pkg/com/sun/security/auth/module/KeyStoreLoginModule.java+947−0
@@ -0,0 +1,947 @@
1/*
2 * Copyright (c) 2000, 2011, 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
26package com.sun.security.auth.module;
27
28import javax.security.auth.x500.X500Principal;
29import java.io.File;
30import java.io.IOException;
31import java.io.InputStream;
32import java.io.PushbackInputStream;
33import java.net.MalformedURLException;
34import java.net.URL;
35import java.security.AuthProvider;
36import java.security.GeneralSecurityException;
37import java.security.Key;
38import java.security.KeyStore;
39import java.security.KeyStoreException;
40import java.security.NoSuchAlgorithmException;
41import java.security.NoSuchProviderException;
42import java.security.Principal;
43import java.security.PrivateKey;
44import java.security.Provider;
45import java.security.UnrecoverableKeyException;
46import java.security.cert.*;
47import java.security.cert.X509Certificate;
48import java.util.Arrays;
49import java.util.Iterator;
50import java.util.LinkedList;
51import java.util.Map;
52import java.util.ResourceBundle;
53import javax.security.auth.Destroyable;
54import javax.security.auth.DestroyFailedException;
55import javax.security.auth.Subject;
56import javax.security.auth.x500.*;
57import javax.security.auth.Subject;
58import javax.security.auth.x500.*;
59import javax.security.auth.callback.Callback;
60import javax.security.auth.callback.CallbackHandler;
61import javax.security.auth.callback.ConfirmationCallback;
62import javax.security.auth.callback.NameCallback;
63import javax.security.auth.callback.PasswordCallback;
64import javax.security.auth.callback.TextOutputCallback;
65import javax.security.auth.callback.UnsupportedCallbackException;
66import javax.security.auth.login.FailedLoginException;
67import javax.security.auth.login.LoginException;
68import javax.security.auth.spi.LoginModule;
69
70import sun.security.util.AuthResources;
71import sun.security.util.Password;
72
73/**
74 * Provides a JAAS login module that prompts for a key store alias and
75 * populates the subject with the alias's principal and credentials. Stores
76 * an <code>X500Principal</code> for the subject distinguished name of the
77 * first certificate in the alias's credentials in the subject's principals,
78 * the alias's certificate path in the subject's public credentials, and a
79 * <code>X500PrivateCredential</code> whose certificate is the first
80 * certificate in the alias's certificate path and whose private key is the
81 * alias's private key in the subject's private credentials. <p>
82 *
83 * Recognizes the following options in the configuration file:
84 * <dl>
85 *
86 * <dt> <code>keyStoreURL</code> </dt>
87 * <dd> A URL that specifies the location of the key store. Defaults to
88 * a URL pointing to the .keystore file in the directory specified by the
89 * <code>user.home</code> system property. The input stream from this
90 * URL is passed to the <code>KeyStore.load</code> method.
91 * "NONE" may be specified if a <code>null</code> stream must be
92 * passed to the <code>KeyStore.load</code> method.
93 * "NONE" should be specified if the KeyStore resides
94 * on a hardware token device, for example.</dd>
95 *
96 * <dt> <code>keyStoreType</code> </dt>
97 * <dd> The key store type. If not specified, defaults to the result of
98 * calling <code>KeyStore.getDefaultType()</code>.
99 * If the type is "PKCS11", then keyStoreURL must be "NONE"
100 * and privateKeyPasswordURL must not be specified.</dd>
101 *
102 * <dt> <code>keyStoreProvider</code> </dt>
103 * <dd> The key store provider. If not specified, uses the standard search
104 * order to find the provider. </dd>
105 *
106 * <dt> <code>keyStoreAlias</code> </dt>
107 * <dd> The alias in the key store to login as. Required when no callback
108 * handler is provided. No default value. </dd>
109 *
110 * <dt> <code>keyStorePasswordURL</code> </dt>
111 * <dd> A URL that specifies the location of the key store password. Required
112 * when no callback handler is provided and
113 * <code>protected</code> is false.
114 * No default value. </dd>
115 *
116 * <dt> <code>privateKeyPasswordURL</code> </dt>
117 * <dd> A URL that specifies the location of the specific private key password
118 * needed to access the private key for this alias.
119 * The keystore password
120 * is used if this value is needed and not specified. </dd>
121 *
122 * <dt> <code>protected</code> </dt>
123 * <dd> This value should be set to "true" if the KeyStore
124 * has a separate, protected authentication path
125 * (for example, a dedicated PIN-pad attached to a smart card).
126 * Defaults to "false". If "true" keyStorePasswordURL and
127 * privateKeyPasswordURL must not be specified.</dd>
128 *
129 * </dl>
130 */
131public class KeyStoreLoginModule implements LoginModule {
132
133 static final java.util.ResourceBundle rb =
134 java.util.ResourceBundle.getBundle("sun.security.util.AuthResources");
135
136 /* -- Fields -- */
137
138 private static final int UNINITIALIZED = 0;
139 private static final int INITIALIZED = 1;
140 private static final int AUTHENTICATED = 2;
141 private static final int LOGGED_IN = 3;
142
143 private static final int PROTECTED_PATH = 0;
144 private static final int TOKEN = 1;
145 private static final int NORMAL = 2;
146
147 private static final String NONE = "NONE";
148 private static final String P11KEYSTORE = "PKCS11";
149
150 private static final TextOutputCallback bannerCallback =
151 new TextOutputCallback
152 (TextOutputCallback.INFORMATION,
153 rb.getString("Please.enter.keystore.information"));
154 private final ConfirmationCallback confirmationCallback =
155 new ConfirmationCallback
156 (ConfirmationCallback.INFORMATION,
157 ConfirmationCallback.OK_CANCEL_OPTION,
158 ConfirmationCallback.OK);
159
160 private Subject subject;
161 private CallbackHandler callbackHandler;
162 private Map sharedState;
163 private Map<String, ?> options;
164
165 private char[] keyStorePassword;
166 private char[] privateKeyPassword;
167 private KeyStore keyStore;
168
169 private String keyStoreURL;
170 private String keyStoreType;
171 private String keyStoreProvider;
172 private String keyStoreAlias;
173 private String keyStorePasswordURL;
174 private String privateKeyPasswordURL;
175 private boolean debug;
176 private javax.security.auth.x500.X500Principal principal;
177 private Certificate[] fromKeyStore;
178 private java.security.cert.CertPath certP = null;
179 private X500PrivateCredential privateCredential;
180 private int status = UNINITIALIZED;
181 private boolean nullStream = false;
182 private boolean token = false;
183 private boolean protectedPath = false;
184
185 /* -- Methods -- */
186
187 /**
188 * Initialize this <code>LoginModule</code>.
189 *
190 * <p>
191 *
192 * @param subject the <code>Subject</code> to be authenticated. <p>
193 *
194 * @param callbackHandler a <code>CallbackHandler</code> for communicating
195 * with the end user (prompting for usernames and
196 * passwords, for example),
197 * which may be <code>null</code>. <p>
198 *
199 * @param sharedState shared <code>LoginModule</code> state. <p>
200 *
201 * @param options options specified in the login
202 * <code>Configuration</code> for this particular
203 * <code>LoginModule</code>.
204 */
205
206 public void initialize(Subject subject,
207 CallbackHandler callbackHandler,
208 Map<String,?> sharedState,
209 Map<String,?> options)
210 {
211 this.subject = subject;
212 this.callbackHandler = callbackHandler;
213 this.sharedState = sharedState;
214 this.options = options;
215
216 processOptions();
217 status = INITIALIZED;
218 }
219
220 private void processOptions() {
221 keyStoreURL = (String) options.get("keyStoreURL");
222 if (keyStoreURL == null) {
223 keyStoreURL =
224 "file:" +
225 System.getProperty("user.home").replace(
226 File.separatorChar, '/') +
227 '/' + ".keystore";
228 } else if (NONE.equals(keyStoreURL)) {
229 nullStream = true;
230 }
231 keyStoreType = (String) options.get("keyStoreType");
232 if (keyStoreType == null) {
233 keyStoreType = KeyStore.getDefaultType();
234 }
235 if (P11KEYSTORE.equalsIgnoreCase(keyStoreType)) {
236 token = true;
237 }
238
239 keyStoreProvider = (String) options.get("keyStoreProvider");
240
241 keyStoreAlias = (String) options.get("keyStoreAlias");
242
243 keyStorePasswordURL = (String) options.get("keyStorePasswordURL");
244
245 privateKeyPasswordURL = (String) options.get("privateKeyPasswordURL");
246
247 protectedPath = "true".equalsIgnoreCase((String)options.get
248 ("protected"));
249
250 debug = "true".equalsIgnoreCase((String) options.get("debug"));
251 if (debug) {
252 debugPrint(null);
253 debugPrint("keyStoreURL=" + keyStoreURL);
254 debugPrint("keyStoreType=" + keyStoreType);
255 debugPrint("keyStoreProvider=" + keyStoreProvider);
256 debugPrint("keyStoreAlias=" + keyStoreAlias);
257 debugPrint("keyStorePasswordURL=" + keyStorePasswordURL);
258 debugPrint("privateKeyPasswordURL=" + privateKeyPasswordURL);
259 debugPrint("protectedPath=" + protectedPath);
260 debugPrint(null);
261 }
262 }
263
264 /**
265 * Authenticate the user.
266 *
267 * <p> Get the Keystore alias and relevant passwords.
268 * Retrieve the alias's principal and credentials from the Keystore.
269 *
270 * <p>
271 *
272 * @exception FailedLoginException if the authentication fails. <p>
273 *
274 * @return true in all cases (this <code>LoginModule</code>
275 * should not be ignored).
276 */
277
278 public boolean login() throws LoginException {
279 switch (status) {
280 case UNINITIALIZED:
281 default:
282 throw new LoginException("The login module is not initialized");
283 case INITIALIZED:
284 case AUTHENTICATED:
285
286 if (token && !nullStream) {
287 throw new LoginException
288 ("if keyStoreType is " + P11KEYSTORE +
289 " then keyStoreURL must be " + NONE);
290 }
291
292 if (token && privateKeyPasswordURL != null) {
293 throw new LoginException
294 ("if keyStoreType is " + P11KEYSTORE +
295 " then privateKeyPasswordURL must not be specified");
296 }
297
298 if (protectedPath &&
299 (keyStorePasswordURL != null ||
300 privateKeyPasswordURL != null)) {
301 throw new LoginException
302 ("if protected is true then keyStorePasswordURL and " +
303 "privateKeyPasswordURL must not be specified");
304 }
305
306 // get relevant alias and password info
307
308 if (protectedPath) {
309 getAliasAndPasswords(PROTECTED_PATH);
310 } else if (token) {
311 getAliasAndPasswords(TOKEN);
312 } else {
313 getAliasAndPasswords(NORMAL);
314 }
315
316 // log into KeyStore to retrieve data,
317 // then clear passwords
318
319 try {
320 getKeyStoreInfo();
321 } finally {
322 if (privateKeyPassword != null &&
323 privateKeyPassword != keyStorePassword) {
324 Arrays.fill(privateKeyPassword, '\0');
325 privateKeyPassword = null;
326 }
327 if (keyStorePassword != null) {
328 Arrays.fill(keyStorePassword, '\0');
329 keyStorePassword = null;
330 }
331 }
332 status = AUTHENTICATED;
333 return true;
334 case LOGGED_IN:
335 return true;
336 }
337 }
338
339 /** Get the alias and passwords to use for looking up in the KeyStore. */
340 private void getAliasAndPasswords(int env) throws LoginException {
341 if (callbackHandler == null) {
342
343 // No callback handler - check for alias and password options
344
345 switch (env) {
346 case PROTECTED_PATH:
347 checkAlias();
348 break;
349 case TOKEN:
350 checkAlias();
351 checkStorePass();
352 break;
353 case NORMAL:
354 checkAlias();
355 checkStorePass();
356 checkKeyPass();
357 break;
358 }
359
360 } else {
361
362 // Callback handler available - prompt for alias and passwords
363
364 NameCallback aliasCallback;
365 if (keyStoreAlias == null || keyStoreAlias.length() == 0) {
366 aliasCallback = new NameCallback(
367 rb.getString("Keystore.alias."));
368 } else {
369 aliasCallback =
370 new NameCallback(rb.getString("Keystore.alias."),
371 keyStoreAlias);
372 }
373
374 PasswordCallback storePassCallback = null;
375 PasswordCallback keyPassCallback = null;
376
377 switch (env) {
378 case PROTECTED_PATH:
379 break;
380 case NORMAL:
381 keyPassCallback = new PasswordCallback
382 (rb.getString("Private.key.password.optional."), false);
383 // fall thru
384 case TOKEN:
385 storePassCallback = new PasswordCallback
386 (rb.getString("Keystore.password."), false);
387 break;
388 }
389 prompt(aliasCallback, storePassCallback, keyPassCallback);
390 }
391
392 if (debug) {
393 debugPrint("alias=" + keyStoreAlias);
394 }
395 }
396
397 private void checkAlias() throws LoginException {
398 if (keyStoreAlias == null) {
399 throw new LoginException
400 ("Need to specify an alias option to use " +
401 "KeyStoreLoginModule non-interactively.");
402 }
403 }
404
405 private void checkStorePass() throws LoginException {
406 if (keyStorePasswordURL == null) {
407 throw new LoginException
408 ("Need to specify keyStorePasswordURL option to use " +
409 "KeyStoreLoginModule non-interactively.");
410 }
411 InputStream in = null;
412 try {
413 in = new URL(keyStorePasswordURL).openStream();
414 keyStorePassword = Password.readPassword(in);
415 } catch (IOException e) {
416 LoginException le = new LoginException
417 ("Problem accessing keystore password \"" +
418 keyStorePasswordURL + "\"");
419 le.initCause(e);
420 throw le;
421 } finally {
422 if (in != null) {
423 try {
424 in.close();
425 } catch (IOException ioe) {
426 LoginException le = new LoginException(
427 "Problem closing the keystore password stream");
428 le.initCause(ioe);
429 throw le;
430 }
431 }
432 }
433 }
434
435 private void checkKeyPass() throws LoginException {
436 if (privateKeyPasswordURL == null) {
437 privateKeyPassword = keyStorePassword;
438 } else {
439 InputStream in = null;
440 try {
441 in = new URL(privateKeyPasswordURL).openStream();
442 privateKeyPassword = Password.readPassword(in);
443 } catch (IOException e) {
444 LoginException le = new LoginException
445 ("Problem accessing private key password \"" +
446 privateKeyPasswordURL + "\"");
447 le.initCause(e);
448 throw le;
449 } finally {
450 if (in != null) {
451 try {
452 in.close();
453 } catch (IOException ioe) {
454 LoginException le = new LoginException(
455 "Problem closing the private key password stream");
456 le.initCause(ioe);
457 throw le;
458 }
459 }
460 }
461 }
462 }
463
464 private void prompt(NameCallback aliasCallback,
465 PasswordCallback storePassCallback,
466 PasswordCallback keyPassCallback)
467 throws LoginException {
468
469 if (storePassCallback == null) {
470
471 // only prompt for alias
472
473 try {
474 callbackHandler.handle(
475 new Callback[] {
476 bannerCallback, aliasCallback, confirmationCallback
477 });
478 } catch (IOException e) {
479 LoginException le = new LoginException
480 ("Problem retrieving keystore alias");
481 le.initCause(e);
482 throw le;
483 } catch (UnsupportedCallbackException e) {
484 throw new LoginException(
485 "Error: " + e.getCallback().toString() +
486 " is not available to retrieve authentication " +
487 " information from the user");
488 }
489
490 int confirmationResult = confirmationCallback.getSelectedIndex();
491
492 if (confirmationResult == ConfirmationCallback.CANCEL) {
493 throw new LoginException("Login cancelled");
494 }
495
496 saveAlias(aliasCallback);
497
498 } else if (keyPassCallback == null) {
499
500 // prompt for alias and key store password
501
502 try {
503 callbackHandler.handle(
504 new Callback[] {
505 bannerCallback, aliasCallback,
506 storePassCallback, confirmationCallback
507 });
508 } catch (IOException e) {
509 LoginException le = new LoginException
510 ("Problem retrieving keystore alias and password");
511 le.initCause(e);
512 throw le;
513 } catch (UnsupportedCallbackException e) {
514 throw new LoginException(
515 "Error: " + e.getCallback().toString() +
516 " is not available to retrieve authentication " +
517 " information from the user");
518 }
519
520 int confirmationResult = confirmationCallback.getSelectedIndex();
521
522 if (confirmationResult == ConfirmationCallback.CANCEL) {
523 throw new LoginException("Login cancelled");
524 }
525
526 saveAlias(aliasCallback);
527 saveStorePass(storePassCallback);
528
529 } else {
530
531 // prompt for alias, key store password, and key password
532
533 try {
534 callbackHandler.handle(
535 new Callback[] {
536 bannerCallback, aliasCallback,
537 storePassCallback, keyPassCallback,
538 confirmationCallback
539 });
540 } catch (IOException e) {
541 LoginException le = new LoginException
542 ("Problem retrieving keystore alias and passwords");
543 le.initCause(e);
544 throw le;
545 } catch (UnsupportedCallbackException e) {
546 throw new LoginException(
547 "Error: " + e.getCallback().toString() +
548 " is not available to retrieve authentication " +
549 " information from the user");
550 }
551
552 int confirmationResult = confirmationCallback.getSelectedIndex();
553
554 if (confirmationResult == ConfirmationCallback.CANCEL) {
555 throw new LoginException("Login cancelled");
556 }
557
558 saveAlias(aliasCallback);
559 saveStorePass(storePassCallback);
560 saveKeyPass(keyPassCallback);
561 }
562 }
563
564 private void saveAlias(NameCallback cb) {
565 keyStoreAlias = cb.getName();
566 }
567
568 private void saveStorePass(PasswordCallback c) {
569 keyStorePassword = c.getPassword();
570 if (keyStorePassword == null) {
571 /* Treat a NULL password as an empty password */
572 keyStorePassword = new char[0];
573 }
574 c.clearPassword();
575 }
576
577 private void saveKeyPass(PasswordCallback c) {
578 privateKeyPassword = c.getPassword();
579 if (privateKeyPassword == null || privateKeyPassword.length == 0) {
580 /*
581 * Use keystore password if no private key password is
582 * specified.
583 */
584 privateKeyPassword = keyStorePassword;
585 }
586 c.clearPassword();
587 }
588
589 /** Get the credentials from the KeyStore. */
590 private void getKeyStoreInfo() throws LoginException {
591
592 /* Get KeyStore instance */
593 try {
594 if (keyStoreProvider == null) {
595 keyStore = KeyStore.getInstance(keyStoreType);
596 } else {
597 keyStore =
598 KeyStore.getInstance(keyStoreType, keyStoreProvider);
599 }
600 } catch (KeyStoreException e) {
601 LoginException le = new LoginException
602 ("The specified keystore type was not available");
603 le.initCause(e);
604 throw le;
605 } catch (NoSuchProviderException e) {
606 LoginException le = new LoginException
607 ("The specified keystore provider was not available");
608 le.initCause(e);
609 throw le;
610 }
611
612 /* Load KeyStore contents from file */
613 InputStream in = null;
614 try {
615 if (nullStream) {
616 // if using protected auth path, keyStorePassword will be null
617 keyStore.load(null, keyStorePassword);
618 } else {
619 in = new URL(keyStoreURL).openStream();
620 keyStore.load(in, keyStorePassword);
621 }
622 } catch (MalformedURLException e) {
623 LoginException le = new LoginException
624 ("Incorrect keyStoreURL option");
625 le.initCause(e);
626 throw le;
627 } catch (GeneralSecurityException e) {
628 LoginException le = new LoginException
629 ("Error initializing keystore");
630 le.initCause(e);
631 throw le;
632 } catch (IOException e) {
633 LoginException le = new LoginException
634 ("Error initializing keystore");
635 le.initCause(e);
636 throw le;
637 } finally {
638 if (in != null) {
639 try {
640 in.close();
641 } catch (IOException ioe) {
642 LoginException le = new LoginException
643 ("Error initializing keystore");
644 le.initCause(ioe);
645 throw le;
646 }
647 }
648 }
649
650 /* Get certificate chain and create a certificate path */
651 try {
652 fromKeyStore =
653 keyStore.getCertificateChain(keyStoreAlias);
654 if (fromKeyStore == null
655 || fromKeyStore.length == 0
656 || !(fromKeyStore[0] instanceof X509Certificate))
657 {
658 throw new FailedLoginException(
659 "Unable to find X.509 certificate chain in keystore");
660 } else {
661 LinkedList<Certificate> certList = new LinkedList<>();
662 for (int i=0; i < fromKeyStore.length; i++) {
663 certList.add(fromKeyStore[i]);
664 }
665 CertificateFactory certF=
666 CertificateFactory.getInstance("X.509");
667 certP =
668 certF.generateCertPath(certList);
669 }
670 } catch (KeyStoreException e) {
671 LoginException le = new LoginException("Error using keystore");
672 le.initCause(e);
673 throw le;
674 } catch (CertificateException ce) {
675 LoginException le = new LoginException
676 ("Error: X.509 Certificate type unavailable");
677 le.initCause(ce);
678 throw le;
679 }
680
681 /* Get principal and keys */
682 try {
683 X509Certificate certificate = (X509Certificate)fromKeyStore[0];
684 principal = new javax.security.auth.x500.X500Principal
685 (certificate.getSubjectDN().getName());
686
687 // if token, privateKeyPassword will be null
688 Key privateKey = keyStore.getKey(keyStoreAlias, privateKeyPassword);
689 if (privateKey == null
690 || !(privateKey instanceof PrivateKey))
691 {
692 throw new FailedLoginException(
693 "Unable to recover key from keystore");
694 }
695
696 privateCredential = new X500PrivateCredential(
697 certificate, (PrivateKey) privateKey, keyStoreAlias);
698 } catch (KeyStoreException e) {
699 LoginException le = new LoginException("Error using keystore");
700 le.initCause(e);
701 throw le;
702 } catch (NoSuchAlgorithmException e) {
703 LoginException le = new LoginException("Error using keystore");
704 le.initCause(e);
705 throw le;
706 } catch (UnrecoverableKeyException e) {
707 FailedLoginException fle = new FailedLoginException
708 ("Unable to recover key from keystore");
709 fle.initCause(e);
710 throw fle;
711 }
712 if (debug) {
713 debugPrint("principal=" + principal +
714 "\n certificate="
715 + privateCredential.getCertificate() +
716 "\n alias =" + privateCredential.getAlias());
717 }
718 }
719
720 /**
721 * Abstract method to commit the authentication process (phase 2).
722 *
723 * <p> This method is called if the LoginContext's
724 * overall authentication succeeded
725 * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules
726 * succeeded).
727 *
728 * <p> If this LoginModule's own authentication attempt
729 * succeeded (checked by retrieving the private state saved by the
730 * <code>login</code> method), then this method associates a
731 * <code>X500Principal</code> for the subject distinguished name of the
732 * first certificate in the alias's credentials in the subject's
733 * principals,the alias's certificate path in the subject's public
734 * credentials, and a<code>X500PrivateCredential</code> whose certificate
735 * is the first certificate in the alias's certificate path and whose
736 * private key is the alias's private key in the subject's private
737 * credentials. If this LoginModule's own
738 * authentication attempted failed, then this method removes
739 * any state that was originally saved.
740 *
741 * <p>
742 *
743 * @exception LoginException if the commit fails
744 *
745 * @return true if this LoginModule's own login and commit
746 * attempts succeeded, or false otherwise.
747 */
748
749 public boolean commit() throws LoginException {
750 switch (status) {
751 case UNINITIALIZED:
752 default:
753 throw new LoginException("The login module is not initialized");
754 case INITIALIZED:
755 logoutInternal();
756 throw new LoginException("Authentication failed");
757 case AUTHENTICATED:
758 if (commitInternal()) {
759 return true;
760 } else {
761 logoutInternal();
762 throw new LoginException("Unable to retrieve certificates");
763 }
764 case LOGGED_IN:
765 return true;
766 }
767 }
768
769 private boolean commitInternal() throws LoginException {
770 /* If the subject is not readonly add to the principal and credentials
771 * set; otherwise just return true
772 */
773 if (subject.isReadOnly()) {
774 throw new LoginException ("Subject is set readonly");
775 } else {
776 subject.getPrincipals().add(principal);
777 subject.getPublicCredentials().add(certP);
778 subject.getPrivateCredentials().add(privateCredential);
779 status = LOGGED_IN;
780 return true;
781 }
782 }
783
784 /**
785 * <p> This method is called if the LoginContext's
786 * overall authentication failed.
787 * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules
788 * did not succeed).
789 *
790 * <p> If this LoginModule's own authentication attempt
791 * succeeded (checked by retrieving the private state saved by the
792 * <code>login</code> and <code>commit</code> methods),
793 * then this method cleans up any state that was originally saved.
794 *
795 * <p> If the loaded KeyStore's provider extends
796 * <code>java.security.AuthProvider</code>,
797 * then the provider's <code>logout</code> method is invoked.
798 *
799 * <p>
800 *
801 * @exception LoginException if the abort fails.
802 *
803 * @return false if this LoginModule's own login and/or commit attempts
804 * failed, and true otherwise.
805 */
806
807 public boolean abort() throws LoginException {
808 switch (status) {
809 case UNINITIALIZED:
810 default:
811 return false;
812 case INITIALIZED:
813 return false;
814 case AUTHENTICATED:
815 logoutInternal();
816 return true;
817 case LOGGED_IN:
818 logoutInternal();
819 return true;
820 }
821 }
822 /**
823 * Logout a user.
824 *
825 * <p> This method removes the Principals, public credentials and the
826 * private credentials that were added by the <code>commit</code> method.
827 *
828 * <p> If the loaded KeyStore's provider extends
829 * <code>java.security.AuthProvider</code>,
830 * then the provider's <code>logout</code> method is invoked.
831 *
832 * <p>
833 *
834 * @exception LoginException if the logout fails.
835 *
836 * @return true in all cases since this <code>LoginModule</code>
837 * should not be ignored.
838 */
839
840 public boolean logout() throws LoginException {
841 if (debug)
842 debugPrint("Entering logout " + status);
843 switch (status) {
844 case UNINITIALIZED:
845 throw new LoginException
846 ("The login module is not initialized");
847 case INITIALIZED:
848 case AUTHENTICATED:
849 default:
850 // impossible for LoginModule to be in AUTHENTICATED
851 // state
852 // assert status != AUTHENTICATED;
853 return false;
854 case LOGGED_IN:
855 logoutInternal();
856 return true;
857 }
858 }
859
860 private void logoutInternal() throws LoginException {
861 if (debug) {
862 debugPrint("Entering logoutInternal");
863 }
864
865 // assumption is that KeyStore.load did a login -
866 // perform explicit logout if possible
867 LoginException logoutException = null;
868 Provider provider = keyStore.getProvider();
869 if (provider instanceof AuthProvider) {
870 AuthProvider ap = (AuthProvider)provider;
871 try {
872 ap.logout();
873 if (debug) {
874 debugPrint("logged out of KeyStore AuthProvider");
875 }
876 } catch (LoginException le) {
877 // save but continue below
878 logoutException = le;
879 }
880 }
881
882 if (subject.isReadOnly()) {
883 // attempt to destroy the private credential
884 // even if the Subject is read-only
885 principal = null;
886 certP = null;
887 status = INITIALIZED;
888 // destroy the private credential
889 Iterator<Object> it = subject.getPrivateCredentials().iterator();
890 while (it.hasNext()) {
891 Object obj = it.next();
892 if (privateCredential.equals(obj)) {
893 privateCredential = null;
894 try {
895 ((Destroyable)obj).destroy();
896 if (debug)
897 debugPrint("Destroyed private credential, " +
898 obj.getClass().getName());
899 break;
900 } catch (DestroyFailedException dfe) {
901 LoginException le = new LoginException
902 ("Unable to destroy private credential, "
903 + obj.getClass().getName());
904 le.initCause(dfe);
905 throw le;
906 }
907 }
908 }
909
910 // throw an exception because we can not remove
911 // the principal and public credential from this
912 // read-only Subject
913 throw new LoginException
914 ("Unable to remove Principal ("
915 + "X500Principal "
916 + ") and public credential (certificatepath) "
917 + "from read-only Subject");
918 }
919 if (principal != null) {
920 subject.getPrincipals().remove(principal);
921 principal = null;
922 }
923 if (certP != null) {
924 subject.getPublicCredentials().remove(certP);
925 certP = null;
926 }
927 if (privateCredential != null) {
928 subject.getPrivateCredentials().remove(privateCredential);
929 privateCredential = null;
930 }
931
932 // throw pending logout exception if there is one
933 if (logoutException != null) {
934 throw logoutException;
935 }
936 status = INITIALIZED;
937 }
938
939 private void debugPrint(String message) {
940 // we should switch to logging API
941 if (message == null) {
942 System.err.println();
943 } else {
944 System.err.println("Debug KeyStoreLoginModule: " + message);
945 }
946 }
947}