diff -r fd16c54261b3 -r 90ce3da70b43 jdk/src/share/classes/com/sun/security/auth/module/Krb5LoginModule.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/com/sun/security/auth/module/Krb5LoginModule.java Sat Dec 01 00:00:00 2007 +0000 @@ -0,0 +1,1159 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + + +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.
+ *

+ *

isInitiator:
+ *
Set this to true, if initiator. Set this to false, if acceptor only. + * (Default is true). + * Note: Do not set this value to false for initiators.
+ *
+ * + *

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. + *

+ *

Configured to act as acceptor only, credentials are not acquired + * via AS exchange. For acceptors only, set this value to false. + * For initiators, do not set this value to false. + *

+ *

Configured to act as initiator, credentials are acquired + * via AS exchange. For initiators, set this value to true, or leave this + * option unset, in which case default value (true) will be used. + * + * @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; + + // specify if initiator. + // perform authentication exchange if initiator + private boolean isInitiator = true; + + // 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")); + + // check isInitiator value + String isInitiatorValue = ((String)options.get("isInitiator")); + if (isInitiatorValue == null) { + // use default, if value not set + } else { + isInitiator = "true".equalsIgnoreCase(isInitiatorValue); + } + + 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 + + " isInitiator " + isInitiator + + " 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()); + + if (isInitiator) { + if (debug) + System.out.println("Acquire TGT using AS Exchange"); + cred = Credentials.acquireTGT(principal, + encKeys, password); + // update keys after pre-auth + encKeys = EncryptionKey.acquireSecretKeys(password, + principal.getSalt()); + } + } else { + if (isInitiator) { + if (debug) + System.out.println("Acquire TGT using AS Exchange"); + cred = Credentials.acquireTGT(principal, + encKeys, password); + } + } + + // Get the TGT using AS Exchange + if (debug) { + System.out.println("principal is " + principal); + 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())); + } + } + + // we should hava a non-null cred + if (isInitiator && (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 (isInitiator && (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 + if (isInitiator) { + 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); + + // add the TGT + if (kerbTicket != null) { + 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); + } + } +}