|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package com.sun.security.auth.module; |
|
|
|
import java.io.File; |
|
import java.io.IOException; |
|
import java.io.InputStream; |
|
import java.net.MalformedURLException; |
|
import java.net.URL; |
|
import java.security.*; |
|
import java.security.cert.*; |
|
import java.security.cert.Certificate; |
|
import java.security.cert.X509Certificate; |
|
import java.util.*; |
|
import javax.security.auth.Destroyable; |
|
import javax.security.auth.DestroyFailedException; |
|
import javax.security.auth.Subject; |
|
import javax.security.auth.x500.*; |
|
import javax.security.auth.callback.Callback; |
|
import javax.security.auth.callback.CallbackHandler; |
|
import javax.security.auth.callback.ConfirmationCallback; |
|
import javax.security.auth.callback.NameCallback; |
|
import javax.security.auth.callback.PasswordCallback; |
|
import javax.security.auth.callback.TextOutputCallback; |
|
import javax.security.auth.callback.UnsupportedCallbackException; |
|
import javax.security.auth.login.FailedLoginException; |
|
import javax.security.auth.login.LoginException; |
|
import javax.security.auth.spi.LoginModule; |
|
|
|
import sun.security.util.Password; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@jdk.Exported |
|
public class KeyStoreLoginModule implements LoginModule { |
|
|
|
private static final ResourceBundle rb = AccessController.doPrivileged( |
|
new PrivilegedAction<ResourceBundle>() { |
|
public ResourceBundle run() { |
|
return ResourceBundle.getBundle( |
|
"sun.security.util.AuthResources"); |
|
} |
|
} |
|
); |
|
|
|
/* -- Fields -- */ |
|
|
|
private static final int UNINITIALIZED = 0; |
|
private static final int INITIALIZED = 1; |
|
private static final int AUTHENTICATED = 2; |
|
private static final int LOGGED_IN = 3; |
|
|
|
private static final int PROTECTED_PATH = 0; |
|
private static final int TOKEN = 1; |
|
private static final int NORMAL = 2; |
|
|
|
private static final String NONE = "NONE"; |
|
private static final String P11KEYSTORE = "PKCS11"; |
|
|
|
private static final TextOutputCallback bannerCallback = |
|
new TextOutputCallback |
|
(TextOutputCallback.INFORMATION, |
|
rb.getString("Please.enter.keystore.information")); |
|
private final ConfirmationCallback confirmationCallback = |
|
new ConfirmationCallback |
|
(ConfirmationCallback.INFORMATION, |
|
ConfirmationCallback.OK_CANCEL_OPTION, |
|
ConfirmationCallback.OK); |
|
|
|
private Subject subject; |
|
private CallbackHandler callbackHandler; |
|
private Map<String, Object> sharedState; |
|
private Map<String, ?> options; |
|
|
|
private char[] keyStorePassword; |
|
private char[] privateKeyPassword; |
|
private KeyStore keyStore; |
|
|
|
private String keyStoreURL; |
|
private String keyStoreType; |
|
private String keyStoreProvider; |
|
private String keyStoreAlias; |
|
private String keyStorePasswordURL; |
|
private String privateKeyPasswordURL; |
|
private boolean debug; |
|
private javax.security.auth.x500.X500Principal principal; |
|
private Certificate[] fromKeyStore; |
|
private java.security.cert.CertPath certP = null; |
|
private X500PrivateCredential privateCredential; |
|
private int status = UNINITIALIZED; |
|
private boolean nullStream = false; |
|
private boolean token = false; |
|
private boolean protectedPath = false; |
|
|
|
/* -- Methods -- */ |
|
|
|
/** |
|
* Initialize this <code>LoginModule</code>. |
|
* |
|
* <p> |
|
* |
|
* @param subject the <code>Subject</code> to be authenticated. <p> |
|
* |
|
* @param callbackHandler a <code>CallbackHandler</code> for communicating |
|
* with the end user (prompting for usernames and |
|
* passwords, for example), |
|
* which may be <code>null</code>. <p> |
|
* |
|
* @param sharedState shared <code>LoginModule</code> state. <p> |
|
* |
|
* @param options options specified in the login |
|
* <code>Configuration</code> for this particular |
|
* <code>LoginModule</code>. |
|
*/ |
|
// Unchecked warning from (Map<String, Object>)sharedState is safe |
|
|
|
@SuppressWarnings("unchecked") |
|
public void initialize(Subject subject, |
|
CallbackHandler callbackHandler, |
|
Map<String,?> sharedState, |
|
Map<String,?> options) |
|
{ |
|
this.subject = subject; |
|
this.callbackHandler = callbackHandler; |
|
this.sharedState = (Map<String, Object>)sharedState; |
|
this.options = options; |
|
|
|
processOptions(); |
|
status = INITIALIZED; |
|
} |
|
|
|
private void processOptions() { |
|
keyStoreURL = (String) options.get("keyStoreURL"); |
|
if (keyStoreURL == null) { |
|
keyStoreURL = |
|
"file:" + |
|
System.getProperty("user.home").replace( |
|
File.separatorChar, '/') + |
|
'/' + ".keystore"; |
|
} else if (NONE.equals(keyStoreURL)) { |
|
nullStream = true; |
|
} |
|
keyStoreType = (String) options.get("keyStoreType"); |
|
if (keyStoreType == null) { |
|
keyStoreType = KeyStore.getDefaultType(); |
|
} |
|
if (P11KEYSTORE.equalsIgnoreCase(keyStoreType)) { |
|
token = true; |
|
} |
|
|
|
keyStoreProvider = (String) options.get("keyStoreProvider"); |
|
|
|
keyStoreAlias = (String) options.get("keyStoreAlias"); |
|
|
|
keyStorePasswordURL = (String) options.get("keyStorePasswordURL"); |
|
|
|
privateKeyPasswordURL = (String) options.get("privateKeyPasswordURL"); |
|
|
|
protectedPath = "true".equalsIgnoreCase((String)options.get |
|
("protected")); |
|
|
|
debug = "true".equalsIgnoreCase((String) options.get("debug")); |
|
if (debug) { |
|
debugPrint(null); |
|
debugPrint("keyStoreURL=" + keyStoreURL); |
|
debugPrint("keyStoreType=" + keyStoreType); |
|
debugPrint("keyStoreProvider=" + keyStoreProvider); |
|
debugPrint("keyStoreAlias=" + keyStoreAlias); |
|
debugPrint("keyStorePasswordURL=" + keyStorePasswordURL); |
|
debugPrint("privateKeyPasswordURL=" + privateKeyPasswordURL); |
|
debugPrint("protectedPath=" + protectedPath); |
|
debugPrint(null); |
|
} |
|
} |
|
|
|
/** |
|
* Authenticate the user. |
|
* |
|
* <p> Get the Keystore alias and relevant passwords. |
|
* Retrieve the alias's principal and credentials from the Keystore. |
|
* |
|
* <p> |
|
* |
|
* @exception FailedLoginException if the authentication fails. <p> |
|
* |
|
* @return true in all cases (this <code>LoginModule</code> |
|
* should not be ignored). |
|
*/ |
|
|
|
public boolean login() throws LoginException { |
|
switch (status) { |
|
case UNINITIALIZED: |
|
default: |
|
throw new LoginException("The login module is not initialized"); |
|
case INITIALIZED: |
|
case AUTHENTICATED: |
|
|
|
if (token && !nullStream) { |
|
throw new LoginException |
|
("if keyStoreType is " + P11KEYSTORE + |
|
" then keyStoreURL must be " + NONE); |
|
} |
|
|
|
if (token && privateKeyPasswordURL != null) { |
|
throw new LoginException |
|
("if keyStoreType is " + P11KEYSTORE + |
|
" then privateKeyPasswordURL must not be specified"); |
|
} |
|
|
|
if (protectedPath && |
|
(keyStorePasswordURL != null || |
|
privateKeyPasswordURL != null)) { |
|
throw new LoginException |
|
("if protected is true then keyStorePasswordURL and " + |
|
"privateKeyPasswordURL must not be specified"); |
|
} |
|
|
|
// get relevant alias and password info |
|
|
|
if (protectedPath) { |
|
getAliasAndPasswords(PROTECTED_PATH); |
|
} else if (token) { |
|
getAliasAndPasswords(TOKEN); |
|
} else { |
|
getAliasAndPasswords(NORMAL); |
|
} |
|
|
|
// log into KeyStore to retrieve data, |
|
// then clear passwords |
|
|
|
try { |
|
getKeyStoreInfo(); |
|
} finally { |
|
if (privateKeyPassword != null && |
|
privateKeyPassword != keyStorePassword) { |
|
Arrays.fill(privateKeyPassword, '\0'); |
|
privateKeyPassword = null; |
|
} |
|
if (keyStorePassword != null) { |
|
Arrays.fill(keyStorePassword, '\0'); |
|
keyStorePassword = null; |
|
} |
|
} |
|
status = AUTHENTICATED; |
|
return true; |
|
case LOGGED_IN: |
|
return true; |
|
} |
|
} |
|
|
|
|
|
@SuppressWarnings("fallthrough") |
|
private void getAliasAndPasswords(int env) throws LoginException { |
|
if (callbackHandler == null) { |
|
|
|
// No callback handler - check for alias and password options |
|
|
|
switch (env) { |
|
case PROTECTED_PATH: |
|
checkAlias(); |
|
break; |
|
case TOKEN: |
|
checkAlias(); |
|
checkStorePass(); |
|
break; |
|
case NORMAL: |
|
checkAlias(); |
|
checkStorePass(); |
|
checkKeyPass(); |
|
break; |
|
} |
|
|
|
} else { |
|
|
|
// Callback handler available - prompt for alias and passwords |
|
|
|
NameCallback aliasCallback; |
|
if (keyStoreAlias == null || keyStoreAlias.length() == 0) { |
|
aliasCallback = new NameCallback( |
|
rb.getString("Keystore.alias.")); |
|
} else { |
|
aliasCallback = |
|
new NameCallback(rb.getString("Keystore.alias."), |
|
keyStoreAlias); |
|
} |
|
|
|
PasswordCallback storePassCallback = null; |
|
PasswordCallback keyPassCallback = null; |
|
|
|
switch (env) { |
|
case PROTECTED_PATH: |
|
break; |
|
case NORMAL: |
|
keyPassCallback = new PasswordCallback |
|
(rb.getString("Private.key.password.optional."), false); |
|
|
|
case TOKEN: |
|
storePassCallback = new PasswordCallback |
|
(rb.getString("Keystore.password."), false); |
|
break; |
|
} |
|
prompt(aliasCallback, storePassCallback, keyPassCallback); |
|
} |
|
|
|
if (debug) { |
|
debugPrint("alias=" + keyStoreAlias); |
|
} |
|
} |
|
|
|
private void checkAlias() throws LoginException { |
|
if (keyStoreAlias == null) { |
|
throw new LoginException |
|
("Need to specify an alias option to use " + |
|
"KeyStoreLoginModule non-interactively."); |
|
} |
|
} |
|
|
|
private void checkStorePass() throws LoginException { |
|
if (keyStorePasswordURL == null) { |
|
throw new LoginException |
|
("Need to specify keyStorePasswordURL option to use " + |
|
"KeyStoreLoginModule non-interactively."); |
|
} |
|
InputStream in = null; |
|
try { |
|
in = new URL(keyStorePasswordURL).openStream(); |
|
keyStorePassword = Password.readPassword(in); |
|
} catch (IOException e) { |
|
LoginException le = new LoginException |
|
("Problem accessing keystore password \"" + |
|
keyStorePasswordURL + "\""); |
|
le.initCause(e); |
|
throw le; |
|
} finally { |
|
if (in != null) { |
|
try { |
|
in.close(); |
|
} catch (IOException ioe) { |
|
LoginException le = new LoginException( |
|
"Problem closing the keystore password stream"); |
|
le.initCause(ioe); |
|
throw le; |
|
} |
|
} |
|
} |
|
} |
|
|
|
private void checkKeyPass() throws LoginException { |
|
if (privateKeyPasswordURL == null) { |
|
privateKeyPassword = keyStorePassword; |
|
} else { |
|
InputStream in = null; |
|
try { |
|
in = new URL(privateKeyPasswordURL).openStream(); |
|
privateKeyPassword = Password.readPassword(in); |
|
} catch (IOException e) { |
|
LoginException le = new LoginException |
|
("Problem accessing private key password \"" + |
|
privateKeyPasswordURL + "\""); |
|
le.initCause(e); |
|
throw le; |
|
} finally { |
|
if (in != null) { |
|
try { |
|
in.close(); |
|
} catch (IOException ioe) { |
|
LoginException le = new LoginException( |
|
"Problem closing the private key password stream"); |
|
le.initCause(ioe); |
|
throw le; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
private void prompt(NameCallback aliasCallback, |
|
PasswordCallback storePassCallback, |
|
PasswordCallback keyPassCallback) |
|
throws LoginException { |
|
|
|
if (storePassCallback == null) { |
|
|
|
// only prompt for alias |
|
|
|
try { |
|
callbackHandler.handle( |
|
new Callback[] { |
|
bannerCallback, aliasCallback, confirmationCallback |
|
}); |
|
} catch (IOException e) { |
|
LoginException le = new LoginException |
|
("Problem retrieving keystore alias"); |
|
le.initCause(e); |
|
throw le; |
|
} catch (UnsupportedCallbackException e) { |
|
throw new LoginException( |
|
"Error: " + e.getCallback().toString() + |
|
" is not available to retrieve authentication " + |
|
" information from the user"); |
|
} |
|
|
|
int confirmationResult = confirmationCallback.getSelectedIndex(); |
|
|
|
if (confirmationResult == ConfirmationCallback.CANCEL) { |
|
throw new LoginException("Login cancelled"); |
|
} |
|
|
|
saveAlias(aliasCallback); |
|
|
|
} else if (keyPassCallback == null) { |
|
|
|
// prompt for alias and key store password |
|
|
|
try { |
|
callbackHandler.handle( |
|
new Callback[] { |
|
bannerCallback, aliasCallback, |
|
storePassCallback, confirmationCallback |
|
}); |
|
} catch (IOException e) { |
|
LoginException le = new LoginException |
|
("Problem retrieving keystore alias and password"); |
|
le.initCause(e); |
|
throw le; |
|
} catch (UnsupportedCallbackException e) { |
|
throw new LoginException( |
|
"Error: " + e.getCallback().toString() + |
|
" is not available to retrieve authentication " + |
|
" information from the user"); |
|
} |
|
|
|
int confirmationResult = confirmationCallback.getSelectedIndex(); |
|
|
|
if (confirmationResult == ConfirmationCallback.CANCEL) { |
|
throw new LoginException("Login cancelled"); |
|
} |
|
|
|
saveAlias(aliasCallback); |
|
saveStorePass(storePassCallback); |
|
|
|
} else { |
|
|
|
// prompt for alias, key store password, and key password |
|
|
|
try { |
|
callbackHandler.handle( |
|
new Callback[] { |
|
bannerCallback, aliasCallback, |
|
storePassCallback, keyPassCallback, |
|
confirmationCallback |
|
}); |
|
} catch (IOException e) { |
|
LoginException le = new LoginException |
|
("Problem retrieving keystore alias and passwords"); |
|
le.initCause(e); |
|
throw le; |
|
} catch (UnsupportedCallbackException e) { |
|
throw new LoginException( |
|
"Error: " + e.getCallback().toString() + |
|
" is not available to retrieve authentication " + |
|
" information from the user"); |
|
} |
|
|
|
int confirmationResult = confirmationCallback.getSelectedIndex(); |
|
|
|
if (confirmationResult == ConfirmationCallback.CANCEL) { |
|
throw new LoginException("Login cancelled"); |
|
} |
|
|
|
saveAlias(aliasCallback); |
|
saveStorePass(storePassCallback); |
|
saveKeyPass(keyPassCallback); |
|
} |
|
} |
|
|
|
private void saveAlias(NameCallback cb) { |
|
keyStoreAlias = cb.getName(); |
|
} |
|
|
|
private void saveStorePass(PasswordCallback c) { |
|
keyStorePassword = c.getPassword(); |
|
if (keyStorePassword == null) { |
|
|
|
keyStorePassword = new char[0]; |
|
} |
|
c.clearPassword(); |
|
} |
|
|
|
private void saveKeyPass(PasswordCallback c) { |
|
privateKeyPassword = c.getPassword(); |
|
if (privateKeyPassword == null || privateKeyPassword.length == 0) { |
|
|
|
|
|
|
|
*/ |
|
privateKeyPassword = keyStorePassword; |
|
} |
|
c.clearPassword(); |
|
} |
|
|
|
|
|
private void getKeyStoreInfo() throws LoginException { |
|
|
|
|
|
try { |
|
if (keyStoreProvider == null) { |
|
keyStore = KeyStore.getInstance(keyStoreType); |
|
} else { |
|
keyStore = |
|
KeyStore.getInstance(keyStoreType, keyStoreProvider); |
|
} |
|
} catch (KeyStoreException e) { |
|
LoginException le = new LoginException |
|
("The specified keystore type was not available"); |
|
le.initCause(e); |
|
throw le; |
|
} catch (NoSuchProviderException e) { |
|
LoginException le = new LoginException |
|
("The specified keystore provider was not available"); |
|
le.initCause(e); |
|
throw le; |
|
} |
|
|
|
|
|
InputStream in = null; |
|
try { |
|
if (nullStream) { |
|
|
|
keyStore.load(null, keyStorePassword); |
|
} else { |
|
in = new URL(keyStoreURL).openStream(); |
|
keyStore.load(in, keyStorePassword); |
|
} |
|
} catch (MalformedURLException e) { |
|
LoginException le = new LoginException |
|
("Incorrect keyStoreURL option"); |
|
le.initCause(e); |
|
throw le; |
|
} catch (GeneralSecurityException e) { |
|
LoginException le = new LoginException |
|
("Error initializing keystore"); |
|
le.initCause(e); |
|
throw le; |
|
} catch (IOException e) { |
|
LoginException le = new LoginException |
|
("Error initializing keystore"); |
|
le.initCause(e); |
|
throw le; |
|
} finally { |
|
if (in != null) { |
|
try { |
|
in.close(); |
|
} catch (IOException ioe) { |
|
LoginException le = new LoginException |
|
("Error initializing keystore"); |
|
le.initCause(ioe); |
|
throw le; |
|
} |
|
} |
|
} |
|
|
|
|
|
try { |
|
fromKeyStore = |
|
keyStore.getCertificateChain(keyStoreAlias); |
|
if (fromKeyStore == null |
|
|| fromKeyStore.length == 0 |
|
|| !(fromKeyStore[0] instanceof X509Certificate)) |
|
{ |
|
throw new FailedLoginException( |
|
"Unable to find X.509 certificate chain in keystore"); |
|
} else { |
|
LinkedList<Certificate> certList = new LinkedList<>(); |
|
for (int i=0; i < fromKeyStore.length; i++) { |
|
certList.add(fromKeyStore[i]); |
|
} |
|
CertificateFactory certF= |
|
CertificateFactory.getInstance("X.509"); |
|
certP = |
|
certF.generateCertPath(certList); |
|
} |
|
} catch (KeyStoreException e) { |
|
LoginException le = new LoginException("Error using keystore"); |
|
le.initCause(e); |
|
throw le; |
|
} catch (CertificateException ce) { |
|
LoginException le = new LoginException |
|
("Error: X.509 Certificate type unavailable"); |
|
le.initCause(ce); |
|
throw le; |
|
} |
|
|
|
|
|
try { |
|
X509Certificate certificate = (X509Certificate)fromKeyStore[0]; |
|
principal = new javax.security.auth.x500.X500Principal |
|
(certificate.getSubjectDN().getName()); |
|
|
|
|
|
Key privateKey = keyStore.getKey(keyStoreAlias, privateKeyPassword); |
|
if (privateKey == null |
|
|| !(privateKey instanceof PrivateKey)) |
|
{ |
|
throw new FailedLoginException( |
|
"Unable to recover key from keystore"); |
|
} |
|
|
|
privateCredential = new X500PrivateCredential( |
|
certificate, (PrivateKey) privateKey, keyStoreAlias); |
|
} catch (KeyStoreException e) { |
|
LoginException le = new LoginException("Error using keystore"); |
|
le.initCause(e); |
|
throw le; |
|
} catch (NoSuchAlgorithmException e) { |
|
LoginException le = new LoginException("Error using keystore"); |
|
le.initCause(e); |
|
throw le; |
|
} catch (UnrecoverableKeyException e) { |
|
FailedLoginException fle = new FailedLoginException |
|
("Unable to recover key from keystore"); |
|
fle.initCause(e); |
|
throw fle; |
|
} |
|
if (debug) { |
|
debugPrint("principal=" + principal + |
|
"\n certificate=" |
|
+ privateCredential.getCertificate() + |
|
"\n alias =" + privateCredential.getAlias()); |
|
} |
|
} |
|
|
|
/** |
|
* Abstract method to commit the authentication process (phase 2). |
|
* |
|
* <p> This method is called if the LoginContext's |
|
* overall authentication succeeded |
|
* (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules |
|
* succeeded). |
|
* |
|
* <p> If this LoginModule's own authentication attempt |
|
* succeeded (checked by retrieving the private state saved by the |
|
* <code>login</code> method), then this method associates a |
|
* <code>X500Principal</code> for the subject distinguished name of the |
|
* first certificate in the alias's credentials in the subject's |
|
* principals,the alias's certificate path in the subject's public |
|
* credentials, and a<code>X500PrivateCredential</code> whose certificate |
|
* is the first certificate in the alias's certificate path and whose |
|
* private key is the alias's private key in the subject's private |
|
* credentials. If this LoginModule's own |
|
* authentication attempted failed, then this method removes |
|
* any state that was originally saved. |
|
* |
|
* <p> |
|
* |
|
* @exception LoginException if the commit fails |
|
* |
|
* @return true if this LoginModule's own login and commit |
|
* attempts succeeded, or false otherwise. |
|
*/ |
|
|
|
public boolean commit() throws LoginException { |
|
switch (status) { |
|
case UNINITIALIZED: |
|
default: |
|
throw new LoginException("The login module is not initialized"); |
|
case INITIALIZED: |
|
logoutInternal(); |
|
throw new LoginException("Authentication failed"); |
|
case AUTHENTICATED: |
|
if (commitInternal()) { |
|
return true; |
|
} else { |
|
logoutInternal(); |
|
throw new LoginException("Unable to retrieve certificates"); |
|
} |
|
case LOGGED_IN: |
|
return true; |
|
} |
|
} |
|
|
|
private boolean commitInternal() throws LoginException { |
|
|
|
|
|
*/ |
|
if (subject.isReadOnly()) { |
|
throw new LoginException ("Subject is set readonly"); |
|
} else { |
|
subject.getPrincipals().add(principal); |
|
subject.getPublicCredentials().add(certP); |
|
subject.getPrivateCredentials().add(privateCredential); |
|
status = LOGGED_IN; |
|
return true; |
|
} |
|
} |
|
|
|
/** |
|
* <p> This method is called if the LoginContext's |
|
* overall authentication failed. |
|
* (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules |
|
* did not succeed). |
|
* |
|
* <p> If this LoginModule's own authentication attempt |
|
* succeeded (checked by retrieving the private state saved by the |
|
* <code>login</code> and <code>commit</code> methods), |
|
* then this method cleans up any state that was originally saved. |
|
* |
|
* <p> If the loaded KeyStore's provider extends |
|
* <code>java.security.AuthProvider</code>, |
|
* then the provider's <code>logout</code> method is invoked. |
|
* |
|
* <p> |
|
* |
|
* @exception LoginException if the abort fails. |
|
* |
|
* @return false if this LoginModule's own login and/or commit attempts |
|
* failed, and true otherwise. |
|
*/ |
|
|
|
public boolean abort() throws LoginException { |
|
switch (status) { |
|
case UNINITIALIZED: |
|
default: |
|
return false; |
|
case INITIALIZED: |
|
return false; |
|
case AUTHENTICATED: |
|
logoutInternal(); |
|
return true; |
|
case LOGGED_IN: |
|
logoutInternal(); |
|
return true; |
|
} |
|
} |
|
/** |
|
* Logout a user. |
|
* |
|
* <p> This method removes the Principals, public credentials and the |
|
* private credentials that were added by the <code>commit</code> method. |
|
* |
|
* <p> If the loaded KeyStore's provider extends |
|
* <code>java.security.AuthProvider</code>, |
|
* then the provider's <code>logout</code> method is invoked. |
|
* |
|
* <p> |
|
* |
|
* @exception LoginException if the logout fails. |
|
* |
|
* @return true in all cases since this <code>LoginModule</code> |
|
* should not be ignored. |
|
*/ |
|
|
|
public boolean logout() throws LoginException { |
|
if (debug) |
|
debugPrint("Entering logout " + status); |
|
switch (status) { |
|
case UNINITIALIZED: |
|
throw new LoginException |
|
("The login module is not initialized"); |
|
case INITIALIZED: |
|
case AUTHENTICATED: |
|
default: |
|
// impossible for LoginModule to be in AUTHENTICATED |
|
// state |
|
|
|
return false; |
|
case LOGGED_IN: |
|
logoutInternal(); |
|
return true; |
|
} |
|
} |
|
|
|
private void logoutInternal() throws LoginException { |
|
if (debug) { |
|
debugPrint("Entering logoutInternal"); |
|
} |
|
|
|
// assumption is that KeyStore.load did a login - |
|
|
|
LoginException logoutException = null; |
|
Provider provider = keyStore.getProvider(); |
|
if (provider instanceof AuthProvider) { |
|
AuthProvider ap = (AuthProvider)provider; |
|
try { |
|
ap.logout(); |
|
if (debug) { |
|
debugPrint("logged out of KeyStore AuthProvider"); |
|
} |
|
} catch (LoginException le) { |
|
|
|
logoutException = le; |
|
} |
|
} |
|
|
|
if (subject.isReadOnly()) { |
|
// attempt to destroy the private credential |
|
|
|
principal = null; |
|
certP = null; |
|
status = INITIALIZED; |
|
|
|
Iterator<Object> it = subject.getPrivateCredentials().iterator(); |
|
while (it.hasNext()) { |
|
Object obj = it.next(); |
|
if (privateCredential.equals(obj)) { |
|
privateCredential = null; |
|
try { |
|
((Destroyable)obj).destroy(); |
|
if (debug) |
|
debugPrint("Destroyed private credential, " + |
|
obj.getClass().getName()); |
|
break; |
|
} catch (DestroyFailedException dfe) { |
|
LoginException le = new LoginException |
|
("Unable to destroy private credential, " |
|
+ obj.getClass().getName()); |
|
le.initCause(dfe); |
|
throw le; |
|
} |
|
} |
|
} |
|
|
|
// throw an exception because we can not remove |
|
// the principal and public credential from this |
|
|
|
throw new LoginException |
|
("Unable to remove Principal (" |
|
+ "X500Principal " |
|
+ ") and public credential (certificatepath) " |
|
+ "from read-only Subject"); |
|
} |
|
if (principal != null) { |
|
subject.getPrincipals().remove(principal); |
|
principal = null; |
|
} |
|
if (certP != null) { |
|
subject.getPublicCredentials().remove(certP); |
|
certP = null; |
|
} |
|
if (privateCredential != null) { |
|
subject.getPrivateCredentials().remove(privateCredential); |
|
privateCredential = null; |
|
} |
|
|
|
|
|
if (logoutException != null) { |
|
throw logoutException; |
|
} |
|
status = INITIALIZED; |
|
} |
|
|
|
private void debugPrint(String message) { |
|
|
|
if (message == null) { |
|
System.err.println(); |
|
} else { |
|
System.err.println("Debug KeyStoreLoginModule: " + message); |
|
} |
|
} |
|
} |