/* * @(#)Krb5LoginModule.java 1.28 04/05/05 * * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ package com.sun.security.auth.module; import java.io.*; import java.net.*; import java.text.MessageFormat; import java.util.*; import javax.security.auth.*; import javax.security.auth.kerberos.*; import javax.security.auth.callback.*; import javax.security.auth.login.*; import javax.security.auth.spi.*; import sun.security.krb5.*; import sun.security.krb5.Config; import sun.security.krb5.RealmException; import sun.security.util.AuthResources; import sun.security.jgss.krb5.Krb5Util; import sun.security.krb5.Credentials; import sun.misc.HexDumpEncoder; /** *

This LoginModule authenticates users using * Kerberos protocols. * *

The configuration entry for Krb5LoginModule has * several options that control the authentication process and * additions to the Subject's private credential * set. Irrespective of these options, the Subject's * principal set and private credentials set are updated only when * commit is called. * When commit is called, the KerberosPrincipal * is added to the Subject's * principal set and KerberosTicket is * added to the Subject's private credentials. * *

If the configuration entry for KerberosLoginModule * has the option storeKey set to true, then * KerberosKey will also be added to the * subject's private credentials. KerberosKey, the principal's * key will be either obtained from the keytab or * derived from user's password. * *

This LoginModule recognizes the doNotPrompt * option. If set to true the user will not be prompted for the password. * *

The user can specify the location of the ticket cache by using * the option ticketCache in the configuration entry. * *

The user can specify the keytab location by using * the option keyTab * in the configuration entry. * *

The principal name can be specified in the configuration entry * by using the option principal. The principal name * can either be a simple user name or a service name such as * host/mission.eng.sun.com. The principal can also * be set using the system property sun.security.krb5.principal. * This property is checked during login. If this property is not set, then * the principal name from the configuration is used. In the * case where the principal property is not set and the principal * entry also does not exist, the user is prompted for the name. * *

The following is a list of configuration options supported * for Krb5LoginModule: *

*
refreshKrb5Config:
*
Set this to true, if you want the configuration * to be refreshed before the login method is called.
*

*

useTicketCache:
*
Set this to true, if you want the * TGT to be obtained * from the ticket cache. Set this option * to false if you do not want this module to use the ticket cache. * (Default is False). * This module will * search for the tickect * cache in the following locations: * For Windows 2000, it will use Local Security Authority (LSA) API * to get the TGT. On Solaris and Linux * it will look for the ticket cache in /tmp/krb5cc_uid * where the uid is numeric user * identifier. If the ticket cache is * not available in either of the above locations, or if we are on a * different Windows platform, it will look for the cache as * {user.home}{file.separator}krb5cc_{user.name}. * You can override the ticket cache location by using * ticketCache *

*

ticketCache:
*
Set this to the name of the ticket * cache that contains user's TGT. * If this is set, useTicketCache * must also be set to true; Otherwise a configuration error will * be returned.
*

*

renewTGT:
*
Set this to true, if you want to renew * the TGT. If this is set, useTicketCache must also be * set to true; otherwise a configuration error will be returned.
*

*

doNotPrompt:
*
Set this to true if you do not want to be * prompted for the password * if credentials can * not be obtained from the cache or keytab.(Default is false) * If set to true authentication will fail if credentials can * not be obtained from the cache or keytab.
*

*

useKeyTab:
*
Set this to true if you * want the module to get the principal's key from the * the keytab.(default value is False) * If keyatb * is not set then * the module will locate the keytab from the * Kerberos configuration file.
* If it is not specifed in the Kerberos configuration file * then it will look for the file * {user.home}{file.separator}krb5.keytab. *

*

keyTab:
*
Set this to the file name of the * keytab to get principal's secret key.
*

*

storeKey:
*
Set this to true to if you want the * principal's key to be stored in the Subject's private credentials.
*

*

principal:
*
The name of the principal that should * be used. The principal can be a simple username such as * "testuser" or a service name such as * "host/testhost.eng.sun.com". You can use the * principal option to set the principal when there are * credentials for multiple principals in the * keyTab or when you want a specific ticket cache only. * The principal can also be set using the system property * sun.security.krb5.principal. In addition, if this * system property is defined, then it will be used. If this property * is not set, then the principal name from the configuration will be * used.
*
* *

This LoginModule also recognizes the following additional * Configuration * options that enable you to share username and passwords across different * authentication modules: *

 *
 *    useFirstPass   if, true, this LoginModule retrieves the
 *                   username and password from the module's shared state,
 *                   using "javax.security.auth.login.name" and
 *                   "javax.security.auth.login.password" as the respective
 *                   keys. The retrieved values are used for authentication.
 *                   If authentication fails, no attempt for a retry
 *                   is made, and the failure is reported back to the
 *                   calling application.
 *
 *    tryFirstPass   if, true, this LoginModule retrieves the
 *                   the username and password from the module's shared
 *                   state using "javax.security.auth.login.name" and
 *                   "javax.security.auth.login.password" as the respective
 *                   keys.  The retrieved values are used for
 *                   authentication.
 *                   If authentication fails, the module uses the
 *                   CallbackHandler to retrieve a new username
 *                   and password, and another attempt to authenticate
 *                   is made. If the authentication fails, 
 *                   the failure is reported back to the calling application
 *
 *    storePass      if, true, this LoginModule stores the username and
 *                   password obtained from the CallbackHandler in the
 *                   modules shared state, using 
 *                   "javax.security.auth.login.name" and 
 *                   "javax.security.auth.login.password" as the respective
 *                   keys.  This is not performed if existing values already
 *                   exist for the username and password in the shared
 *                   state, or if authentication fails.
 *
 *    clearPass     if, true, this LoginModule clears the
 *                  username and password stored in the module's shared
 *                  state  after both phases of authentication
 *                  (login and commit)  have completed.
 * 
*

Examples of some configuration values for Krb5LoginModule in * JAAS config file and the results are: *

*

This is an illegal combination since useTicketCache * is not set and the user can not be prompted for the password. *

*

This is an illegal combination since useTicketCache * is not set to true and the ticketCache is set. A configuration error * will occur. *

*

This is an illegal combination since useTicketCache is * not set to true and renewTGT is set. A configuration error will occur. *

*

This is an illegal combination since storeKey is set to * true but the key can not be obtained either by prompting the user or from * the keytab.A configuration error will occur. *

*

This is an illegal combination since useKeyTab is not set to true and * the keyTab is set. A configuration error will occur. *

*

Prompt the user for the principal name and the password. * Use the authentication exchange to get TGT from the KDC and * populate the Subject with the principal and TGT. * Output debug messages. *

*

Check the default cache for TGT and populate the Subject * with the principal and TGT. If the TGT is not available, * do not prompt the user, instead fail the authentication. *

*

Get the TGT from the default cache for the principal and populate the * Subject's principal and private creds set. If ticket cache is * not available or does not contain the principal's TGT * authentication will fail. *

*

Search the cache for the principal's TGT. If it is not available * use the key in the keytab to perform authentication exchange with the * KDC and acquire the TGT. * The Subject will be populated with the principal and the TGT. * If the key is not available or valid then authentication will fail. *

*

The TGT will be obtained from the cache specified. * The Kerberos principal name used will be the principal name in * the Ticket cache. If the TGT is not available in the * ticket cache the user will be prompted for the principal name * and the password. The TGT will be obtained using the authentication * exchange with the KDC. * The Subject will be populated with the TGT. *

*

The key for the principal will be retrieved from the keytab. * If the key is not available in the keytab the user will be prompted * for the principal's password. The Subject will be populated * with the principal's key either from the keytab or derived from the * password entered. *

*

The user will be prompted for the service principal name. * If the principal's * longterm key is available in the keytab , it will be added to the * Subject's private credentials. An authentication exchange will be * attempted with the principal name and the key from the Keytab. * If successful the TGT will be added to the * Subject's private credentials set. Otherwise the authentication will * fail. *

*

The principal's key will be retrieved from the keytab and added * to the Subject's private credentials. If the key * is not available, the * user will be prompted for the password; the key derived from the password * will be added to the Subject's private credentials set. The * client's TGT will be retrieved from the ticket cache and added to the * Subject's private credentials. If the TGT is not available * in the ticket cache, it will be obtained using the authentication * exchange and added to the Subject's private credentials. * * * @version 1.18, 01/11/00 * @author Ram Marti */ public class Krb5LoginModule implements LoginModule { // initial state private Subject subject; private CallbackHandler callbackHandler; private Map sharedState; private Map options; // configurable option private boolean debug = false; private boolean storeKey = false; private boolean doNotPrompt = false; private boolean useTicketCache = false; private boolean useKeyTab = false; private String ticketCacheName = null; private String keyTabName = null; private String princName = null; private boolean useFirstPass = false; private boolean tryFirstPass = false; private boolean storePass = false; private boolean clearPass = false; private boolean refreshKrb5Config = false; private boolean renewTGT = false; // the authentication status private boolean succeeded = false; private boolean commitSucceeded = false; private String username; private EncryptionKey[] encKeys = null; private Credentials cred = null; private PrincipalName principal = null; private KerberosPrincipal kerbClientPrinc = null; private KerberosTicket kerbTicket = null; private KerberosKey[] kerbKeys = null; private StringBuffer krb5PrincName = null; private char[] password = null; private static final String NAME = "javax.security.auth.login.name"; private static final String PWD = "javax.security.auth.login.password"; static final java.util.ResourceBundle rb = java.util.ResourceBundle.getBundle("sun.security.util.AuthResources"); /** * Initialize this LoginModule. * *

* @param subject the Subject to be authenticated.

* * @param callbackHandler a CallbackHandler for * communication with the end user (prompting for * usernames and passwords, for example).

* * @param sharedState shared LoginModule state.

* * @param options options specified in the login * Configuration for this particular * LoginModule. */ public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) { this.subject = subject; this.callbackHandler = callbackHandler; this.sharedState = sharedState; this.options = options; // initialize any configured options debug = "true".equalsIgnoreCase((String)options.get("debug")); storeKey = "true".equalsIgnoreCase((String)options.get("storeKey")); doNotPrompt = "true".equalsIgnoreCase((String)options.get ("doNotPrompt")); useTicketCache = "true".equalsIgnoreCase((String)options.get ("useTicketCache")); useKeyTab = "true".equalsIgnoreCase((String)options.get("useKeyTab")); ticketCacheName = (String)options.get("ticketCache"); keyTabName = (String)options.get("keyTab"); princName = (String)options.get("principal"); refreshKrb5Config = "true".equalsIgnoreCase((String)options.get("refreshKrb5Config")); renewTGT = "true".equalsIgnoreCase((String)options.get("renewTGT")); tryFirstPass = "true".equalsIgnoreCase ((String)options.get("tryFirstPass")); useFirstPass = "true".equalsIgnoreCase ((String)options.get("useFirstPass")); storePass = "true".equalsIgnoreCase((String)options.get("storePass")); clearPass = "true".equalsIgnoreCase((String)options.get("clearPass")); if (debug) { System.out.print("Debug is " + debug + " storeKey " + storeKey + " useTicketCache " + useTicketCache + " useKeyTab " + useKeyTab + " doNotPrompt " + doNotPrompt + " ticketCache is " + ticketCacheName + " KeyTab is " + keyTabName + " refreshKrb5Config is " + refreshKrb5Config + " principal is " + princName + " tryFirstPass is " + tryFirstPass + " useFirstPass is " + useFirstPass + " storePass is " + storePass + " clearPass is " + clearPass + "\n"); } } /** * Authenticate the user * *

* * @return true in all cases since this LoginModule * should not be ignored. * * @exception FailedLoginException if the authentication fails.

* * @exception LoginException if this LoginModule * is unable to perform the authentication. */ public boolean login() throws LoginException { int len; validateConfiguration(); if (refreshKrb5Config) { try { if (debug) { System.out.println("Refreshing Kerberos configuration"); } sun.security.krb5.Config.refresh(); } catch (KrbException ke) { LoginException le = new LoginException(ke.getMessage()); le.initCause(ke); throw le; } } String principalProperty = System.getProperty ("sun.security.krb5.principal"); if (principalProperty != null) { krb5PrincName = new StringBuffer(principalProperty); } else { if (princName != null) { krb5PrincName = new StringBuffer(princName); } } if (tryFirstPass) { try { attemptAuthentication(true); if (debug) System.out.println("\t\t[Krb5LoginModule] " + "authentication succeeded"); succeeded = true; cleanState(); return true; } catch (LoginException le) { // authentication failed -- try again below by prompting cleanState(); if (debug) { System.out.println("\t\t[Krb5LoginModule] " + "tryFirstPass failed with:" + le.getMessage()); } } } else if (useFirstPass) { try { attemptAuthentication(true); succeeded = true; cleanState(); return true; } catch (LoginException e) { // authentication failed -- clean out state if (debug) { System.out.println("\t\t[Krb5LoginModule] " + "authentication failed \n" + e.getMessage()); } succeeded = false; cleanState(); throw e; } } // attempt the authentication by getting the username and pwd // by prompting or configuration i.e. not from shared state try { attemptAuthentication(false); succeeded = true; cleanState(); return true; } catch (LoginException e) { // authentication failed -- clean out state if (debug) { System.out.println("\t\t[Krb5LoginModule] " + "authentication failed \n" + e.getMessage()); } succeeded = false; cleanState(); throw e; } } /** * process the configuration options * Get the TGT either out of * cache or from the KDC using the password entered * Check the permission before getting the TGT */ private void attemptAuthentication(boolean getPasswdFromSharedState) throws LoginException { /* * Check the creds cache to see whether * we have TGT for this client principal */ if (krb5PrincName != null) { try { principal = new PrincipalName (krb5PrincName.toString(), PrincipalName.KRB_NT_PRINCIPAL); } catch (KrbException e) { LoginException le = new LoginException(e.getMessage()); le.initCause(e); throw le; } } try { if (useTicketCache) { // ticketCacheName == null implies the default cache if (debug) System.out.println("Acquire TGT from Cache"); cred = Credentials.acquireTGTFromCache (principal, ticketCacheName); if (cred != null) { // check to renew credentials if (!isCurrent(cred)) { if (renewTGT) { cred = renewCredentials(cred); } else { // credentials have expired cred = null; if (debug) System.out.println("Credentials are" + " no longer valid"); } } } if (cred != null) { // get the principal name from the ticket cache if (principal == null) { principal = cred.getClient(); } } if (debug) { System.out.println("Principal is " + principal); if (cred == null) { System.out.println ("null credentials from Ticket Cache"); } } } // cred = null indicates that we didn't get the creds // from the cache or useTicketCache was false if (cred == null) { // We need the principal name whether we use keytab // or AS Exchange if (principal == null) { promptForName(getPasswdFromSharedState); principal = new PrincipalName (krb5PrincName.toString(), PrincipalName.KRB_NT_PRINCIPAL); } if (useKeyTab) { encKeys = EncryptionKey.acquireSecretKeys(principal, keyTabName); if (debug) { if (encKeys != null) System.out.println ("principal's key obtained from the keytab"); else System.out.println ("Key for the principal " + principal + " not available in " + ((keyTabName == null) ? "default key tab" : keyTabName)); } } // We can't get the key from the keytab so prompt if (encKeys == null) { promptForPass(getPasswdFromSharedState); encKeys = EncryptionKey.acquireSecretKeys( password, principal.getSalt()); } // Get the TGT using AS Exchange if (debug) { System.out.println("principal is " + principal); System.out.println("Acquire TGT using AS Exchange"); HexDumpEncoder hd = new HexDumpEncoder(); for (int i = 0; i < encKeys.length; i++) { System.out.println("EncryptionKey: keyType=" + encKeys[i].getEType() + " keyBytes (hex dump)=" + hd.encode(encKeys[i].getBytes())); } } cred = Credentials.acquireTGT(principal, encKeys); // we should hava a non-null cred if (cred == null) { throw new LoginException ("TGT Can not be obtained from the KDC "); } } } catch (KrbException e) { LoginException le = new LoginException(e.getMessage()); le.initCause(e); throw le; } catch (IOException ioe) { LoginException ie = new LoginException(ioe.getMessage()); ie.initCause(ioe); throw ie; } } private void promptForName(boolean getPasswdFromSharedState) throws LoginException { krb5PrincName = new StringBuffer(""); if (getPasswdFromSharedState) { // use the name saved by the first module in the stack username = (String)sharedState.get(NAME); if (debug) { System.out.println ("username from shared state is " + username + "\n"); } if (username == null) { System.out.println ("username from shared state is null\n"); throw new LoginException ("Username can not be obtained from sharedstate "); } if (debug) { System.out.println ("username from shared state is " + username + "\n"); } if (username != null && username.length() > 0) { krb5PrincName.insert(0, username); return; } } if (doNotPrompt) { throw new LoginException ("Unable to obtain Princpal Name for authentication "); } else { if (callbackHandler == null) throw new LoginException("No CallbackHandler " + "available " + "to garner authentication " + "information from the user"); try { String defUsername = System.getProperty("user.name"); Callback[] callbacks = new Callback[1]; MessageFormat form = new MessageFormat( rb.getString( "Kerberos username [[defUsername]]: ")); Object[] source = {defUsername}; callbacks[0] = new NameCallback(form.format(source)); callbackHandler.handle(callbacks); username = ((NameCallback)callbacks[0]).getName(); if (username == null || username.length() == 0) username = defUsername; krb5PrincName.insert(0, username); } catch (java.io.IOException ioe) { throw new LoginException(ioe.getMessage()); } catch (UnsupportedCallbackException uce) { throw new LoginException (uce.getMessage() +" not available to garner " +" authentication information " +" from the user"); } } } private void promptForPass(boolean getPasswdFromSharedState) throws LoginException { if (getPasswdFromSharedState) { // use the password saved by the first module in the stack password = (char[])sharedState.get(PWD); if (password == null) { if (debug) { System.out.println ("Password from shared state is null"); } throw new LoginException ("Password can not be obtained from sharedstate "); } if (debug) { System.out.println ("password is " + new String(password)); } return; } if (doNotPrompt) { throw new LoginException ("Unable to obtain password from user\n"); } else { if (callbackHandler == null) throw new LoginException("No CallbackHandler " + "available " + "to garner authentication " + "information from the user"); try { Callback[] callbacks = new Callback[1]; String userName = krb5PrincName.toString(); MessageFormat form = new MessageFormat( rb.getString( "Kerberos password for [username]: ")); Object[] source = {userName}; callbacks[0] = new PasswordCallback( form.format(source), false); callbackHandler.handle(callbacks); char[] tmpPassword = ((PasswordCallback) callbacks[0]).getPassword(); if (tmpPassword == null) { // treat a NULL password as an empty password tmpPassword = new char[0]; } password = new char[tmpPassword.length]; System.arraycopy(tmpPassword, 0, password, 0, tmpPassword.length); ((PasswordCallback)callbacks[0]).clearPassword(); // clear tmpPassword for (int i = 0; i < tmpPassword.length; i++) tmpPassword[i] = ' '; tmpPassword = null; if (debug) { System.out.println("\t\t[Krb5LoginModule] " + "user entered username: " + krb5PrincName); System.out.println(); } } catch (java.io.IOException ioe) { throw new LoginException(ioe.getMessage()); } catch (UnsupportedCallbackException uce) { throw new LoginException(uce.getMessage() +" not available to garner " +" authentication information " + "from the user"); } } } private void validateConfiguration() throws LoginException { if (doNotPrompt && !useTicketCache && !useKeyTab) throw new LoginException ("Configuration Error" + " - either doNotPrompt should be " + " false or useTicketCache/useKeyTab " + " should be true"); if (ticketCacheName != null && !useTicketCache) throw new LoginException ("Configuration Error " + " - useTicketCache should be set " + "to true to use the ticket cache" + ticketCacheName); if (keyTabName != null & !useKeyTab) throw new LoginException ("Configuration Error - useKeyTab should be set to true " + "to use the keytab" + keyTabName); if (storeKey && doNotPrompt && !useKeyTab) throw new LoginException ("Configuration Error - either doNotPrompt " + "should be set to false or " + "useKeyTab must be set to true for storeKey option"); if (renewTGT && !useTicketCache) throw new LoginException ("Configuration Error" + " - either useTicketCache should be " + " true or renewTGT should be false"); } private boolean isCurrent(Credentials creds) { Date endTime = creds.getEndTime(); if (endTime != null) { return (System.currentTimeMillis() <= endTime.getTime()); } return true; } private Credentials renewCredentials(Credentials creds) { Credentials lcreds; try { if (!creds.isRenewable()) throw new RefreshFailedException("This ticket" + " is not renewable"); if (System.currentTimeMillis() > cred.getRenewTill().getTime()) throw new RefreshFailedException("This ticket is past " + "its last renewal time."); lcreds = creds.renew(); if (debug) System.out.println("Renewed Kerberos Ticket"); } catch (Exception e) { lcreds = null; if (debug) System.out.println("Ticket could not be renewed : " + e.getMessage()); } return lcreds; } /** *

This method is called if the LoginContext's * overall authentication succeeded * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL * LoginModules succeeded). * *

If this LoginModule's own authentication attempt * succeeded (checked by retrieving the private state saved by the * login method), then this method associates a * Krb5Principal * with the Subject located in the * LoginModule. It adds Kerberos Credentials to the * the Subject's private credentials set. If this LoginModule's own * authentication attempted failed, then this method removes * any state that was originally saved. * *

* * @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 { /* * Let us add the Krb5 Creds to the Subject's * private credentials. The credentials are of type * KerberosKey or KerberosTicket */ if (succeeded == false) { return false; } else { if (cred == null) { succeeded = false; throw new LoginException("Null Client Credential"); } if (subject.isReadOnly()) { cleanKerberosCred(); throw new LoginException("Subject is Readonly"); } /* * Add the Principal (authenticated identity) * to the Subject's principal set and * add the credentials (TGT or Service key) to the * Subject's private credentials */ Set privCredSet = subject.getPrivateCredentials(); Set princSet = subject.getPrincipals(); kerbClientPrinc = new KerberosPrincipal(principal.getName()); // create Kerberos Ticket kerbTicket = Krb5Util.credsToTicket(cred); if (storeKey) { if (encKeys == null || encKeys.length <= 0) { succeeded = false; throw new LoginException("Null Server Key "); } kerbKeys = new KerberosKey[encKeys.length]; for (int i = 0; i < encKeys.length; i ++) { Integer temp = encKeys[i].getKeyVersionNumber(); kerbKeys[i] = new KerberosKey(kerbClientPrinc, encKeys[i].getBytes(), encKeys[i].getEType(), (temp == null? 0: temp.intValue())); } } // Let us add the kerbClientPrinc,kerbTicket and kerbKey (if // storeKey is true) if (!princSet.contains(kerbClientPrinc)) princSet.add(kerbClientPrinc); if (!privCredSet.contains(kerbTicket)) privCredSet.add(kerbTicket); if (storeKey) { for (int i = 0; i < kerbKeys.length; i++) { if (!privCredSet.contains(kerbKeys[i])) { privCredSet.add(kerbKeys[i]); } encKeys[i].destroy(); encKeys[i] = null; if (debug) { System.out.println("Added server's key" + kerbKeys[i]); System.out.println("\t\t[Krb5LoginModule] " + "added Krb5Principal " + kerbClientPrinc.toString() + " to Subject"); } } } } commitSucceeded = true; if (debug) System.out.println("Commit Succeeded \n"); return true; } /** *

This method is called if the LoginContext's * overall authentication failed. * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL * LoginModules did not succeed). * *

If this LoginModule's own authentication attempt * succeeded (checked by retrieving the private state saved by the * login and commit methods), * then this method cleans up any state that was originally saved. * *

* * @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 { if (succeeded == false) { return false; } else if (succeeded == true && commitSucceeded == false) { // login succeeded but overall authentication failed succeeded = false; cleanKerberosCred(); } else { // overall authentication succeeded and commit succeeded, // but someone else's commit failed logout(); } return true; } /** * Logout the user. * *

This method removes the Krb5Principal * that was added by the commit method. * *

* * @exception LoginException if the logout fails. * * @return true in all cases since this LoginModule * should not be ignored. */ public boolean logout() throws LoginException { if (debug) { System.out.println("\t\t[Krb5LoginModule]: " + "Entering logout"); } if (subject.isReadOnly()) { cleanKerberosCred(); throw new LoginException("Subject is Readonly"); } subject.getPrincipals().remove(kerbClientPrinc); // Let us remove all Kerberos credentials stored in the Subject Iterator it = subject.getPrivateCredentials().iterator(); while (it.hasNext()) { Object o = it.next(); if (o instanceof KerberosTicket || o instanceof KerberosKey) { it.remove(); } } // clean the kerberos ticket and keys cleanKerberosCred(); succeeded = false; commitSucceeded = false; if (debug) { System.out.println("\t\t[Krb5LoginModule]: " + "logged out Subject"); } return true; } /** * Clean Kerberos credentials */ private void cleanKerberosCred() throws LoginException { // Clean the ticket and server key try { if (kerbTicket != null) kerbTicket.destroy(); if (kerbKeys != null) { for (int i = 0; i < kerbKeys.length; i++) { kerbKeys[i].destroy(); } } } catch (DestroyFailedException e) { throw new LoginException ("Destroy Failed on Kerberos Private Credentials"); } kerbTicket = null; kerbKeys = null; kerbClientPrinc = null; } /** * Clean out the state */ private void cleanState() { // save input as shared state only if // authentication succeeded if (succeeded) { if (storePass && !sharedState.containsKey(NAME) && !sharedState.containsKey(PWD)) { sharedState.put(NAME, username); sharedState.put(PWD, password); } } username = null; password = null; if (krb5PrincName != null && krb5PrincName.length() != 0) krb5PrincName.delete(0, krb5PrincName.length()); krb5PrincName = null; if (clearPass) { sharedState.remove(NAME); sharedState.remove(PWD); } } }