| /* |
| * Copyright (c) 2000, 2011, Oracle and/or its affiliates. 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. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package com.sun.security.auth.module; |
| |
| import javax.security.auth.*; |
| import javax.security.auth.callback.*; |
| import javax.security.auth.login.*; |
| import javax.security.auth.spi.*; |
| import javax.naming.*; |
| import javax.naming.directory.*; |
| |
| import java.io.IOException; |
| import java.util.Map; |
| import java.util.LinkedList; |
| import java.util.ResourceBundle; |
| |
| import com.sun.security.auth.UnixPrincipal; |
| import com.sun.security.auth.UnixNumericUserPrincipal; |
| import com.sun.security.auth.UnixNumericGroupPrincipal; |
| |
| import sun.security.util.AuthResources; |
| |
| /** |
| * <p> The module prompts for a username and password |
| * and then verifies the password against the password stored in |
| * a directory service configured under JNDI. |
| * |
| * <p> This <code>LoginModule</code> interoperates with |
| * any conformant JNDI service provider. To direct this |
| * <code>LoginModule</code> to use a specific JNDI service provider, |
| * two options must be specified in the login <code>Configuration</code> |
| * for this <code>LoginModule</code>. |
| * <pre> |
| * user.provider.url=<b>name_service_url</b> |
| * group.provider.url=<b>name_service_url</b> |
| * </pre> |
| * |
| * <b>name_service_url</b> specifies |
| * the directory service and path where this <code>LoginModule</code> |
| * can access the relevant user and group information. Because this |
| * <code>LoginModule</code> only performs one-level searches to |
| * find the relevant user information, the <code>URL</code> |
| * must point to a directory one level above where the user and group |
| * information is stored in the directory service. |
| * For example, to instruct this <code>LoginModule</code> |
| * to contact a NIS server, the following URLs must be specified: |
| * <pre> |
| * user.provider.url="nis://<b>NISServerHostName</b>/<b>NISDomain</b>/user" |
| * group.provider.url="nis://<b>NISServerHostName</b>/<b>NISDomain</b>/system/group" |
| * </pre> |
| * |
| * <b>NISServerHostName</b> specifies the server host name of the |
| * NIS server (for example, <i>nis.sun.com</i>, and <b>NISDomain</b> |
| * specifies the domain for that NIS server (for example, <i>jaas.sun.com</i>. |
| * To contact an LDAP server, the following URLs must be specified: |
| * <pre> |
| * user.provider.url="ldap://<b>LDAPServerHostName</b>/<b>LDAPName</b>" |
| * group.provider.url="ldap://<b>LDAPServerHostName</b>/<b>LDAPName</b>" |
| * </pre> |
| * |
| * <b>LDAPServerHostName</b> specifies the server host name of the |
| * LDAP server, which may include a port number |
| * (for example, <i>ldap.sun.com:389</i>), |
| * and <b>LDAPName</b> specifies the entry name in the LDAP directory |
| * (for example, <i>ou=People,o=Sun,c=US</i> and <i>ou=Groups,o=Sun,c=US</i> |
| * for user and group information, respectively). |
| * |
| * <p> The format in which the user's information must be stored in |
| * the directory service is specified in RFC 2307. Specifically, |
| * this <code>LoginModule</code> will search for the user's entry in the |
| * directory service using the user's <i>uid</i> attribute, |
| * where <i>uid=<b>username</b></i>. If the search succeeds, |
| * this <code>LoginModule</code> will then |
| * obtain the user's encrypted password from the retrieved entry |
| * using the <i>userPassword</i> attribute. |
| * This <code>LoginModule</code> assumes that the password is stored |
| * as a byte array, which when converted to a <code>String</code>, |
| * has the following format: |
| * <pre> |
| * "{crypt}<b>encrypted_password</b>" |
| * </pre> |
| * |
| * The LDAP directory server must be configured |
| * to permit read access to the userPassword attribute. |
| * If the user entered a valid username and password, |
| * this <code>LoginModule</code> associates a |
| * <code>UnixPrincipal</code>, <code>UnixNumericUserPrincipal</code>, |
| * and the relevant UnixNumericGroupPrincipals with the |
| * <code>Subject</code>. |
| * |
| * <p> This LoginModule also recognizes the following <code>Configuration</code> |
| * options: |
| * <pre> |
| * debug if, true, debug messages are output to System.out. |
| * |
| * 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 module's |
| * 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 <code>LoginModule</code> clears the |
| * username and password stored in the module's shared state |
| * after both phases of authentication (login and commit) |
| * have completed. |
| * </pre> |
| * |
| */ |
| public class JndiLoginModule implements LoginModule { |
| |
| static final java.util.ResourceBundle rb = |
| java.util.ResourceBundle.getBundle("sun.security.util.AuthResources"); |
| |
| /** JNDI Provider */ |
| public final String USER_PROVIDER = "user.provider.url"; |
| public final String GROUP_PROVIDER = "group.provider.url"; |
| |
| // configurable options |
| private boolean debug = false; |
| private boolean strongDebug = false; |
| private String userProvider; |
| private String groupProvider; |
| private boolean useFirstPass = false; |
| private boolean tryFirstPass = false; |
| private boolean storePass = false; |
| private boolean clearPass = false; |
| |
| // the authentication status |
| private boolean succeeded = false; |
| private boolean commitSucceeded = false; |
| |
| // username, password, and JNDI context |
| private String username; |
| private char[] password; |
| DirContext ctx; |
| |
| // the user (assume it is a UnixPrincipal) |
| private UnixPrincipal userPrincipal; |
| private UnixNumericUserPrincipal UIDPrincipal; |
| private UnixNumericGroupPrincipal GIDPrincipal; |
| private LinkedList<UnixNumericGroupPrincipal> supplementaryGroups = |
| new LinkedList<>(); |
| |
| // initial state |
| private Subject subject; |
| private CallbackHandler callbackHandler; |
| private Map sharedState; |
| private Map<String, ?> options; |
| |
| private static final String CRYPT = "{crypt}"; |
| private static final String USER_PWD = "userPassword"; |
| private static final String USER_UID = "uidNumber"; |
| private static final String USER_GID = "gidNumber"; |
| private static final String GROUP_ID = "gidNumber"; |
| private static final String NAME = "javax.security.auth.login.name"; |
| private static final String PWD = "javax.security.auth.login.password"; |
| |
| /** |
| * 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). <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>. |
| */ |
| public void initialize(Subject subject, CallbackHandler callbackHandler, |
| Map<String,?> sharedState, |
| Map<String,?> options) { |
| |
| this.subject = subject; |
| this.callbackHandler = callbackHandler; |
| this.sharedState = sharedState; |
| this.options = options; |
| |
| // initialize any configured options |
| debug = "true".equalsIgnoreCase((String)options.get("debug")); |
| strongDebug = |
| "true".equalsIgnoreCase((String)options.get("strongDebug")); |
| userProvider = (String)options.get(USER_PROVIDER); |
| groupProvider = (String)options.get(GROUP_PROVIDER); |
| 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")); |
| } |
| |
| /** |
| * <p> Prompt for username and password. |
| * Verify the password against the relevant name service. |
| * |
| * <p> |
| * |
| * @return true always, since this <code>LoginModule</code> |
| * should not be ignored. |
| * |
| * @exception FailedLoginException if the authentication fails. <p> |
| * |
| * @exception LoginException if this <code>LoginModule</code> |
| * is unable to perform the authentication. |
| */ |
| public boolean login() throws LoginException { |
| |
| if (userProvider == null) { |
| throw new LoginException |
| ("Error: Unable to locate JNDI user provider"); |
| } |
| if (groupProvider == null) { |
| throw new LoginException |
| ("Error: Unable to locate JNDI group provider"); |
| } |
| |
| if (debug) { |
| System.out.println("\t\t[JndiLoginModule] user provider: " + |
| userProvider); |
| System.out.println("\t\t[JndiLoginModule] group provider: " + |
| groupProvider); |
| } |
| |
| // attempt the authentication |
| if (tryFirstPass) { |
| |
| try { |
| // attempt the authentication by getting the |
| // username and password from shared state |
| attemptAuthentication(true); |
| |
| // authentication succeeded |
| succeeded = true; |
| if (debug) { |
| System.out.println("\t\t[JndiLoginModule] " + |
| "tryFirstPass succeeded"); |
| } |
| return true; |
| } catch (LoginException le) { |
| // authentication failed -- try again below by prompting |
| cleanState(); |
| if (debug) { |
| System.out.println("\t\t[JndiLoginModule] " + |
| "tryFirstPass failed with:" + |
| le.toString()); |
| } |
| } |
| |
| } else if (useFirstPass) { |
| |
| try { |
| // attempt the authentication by getting the |
| // username and password from shared state |
| attemptAuthentication(true); |
| |
| // authentication succeeded |
| succeeded = true; |
| if (debug) { |
| System.out.println("\t\t[JndiLoginModule] " + |
| "useFirstPass succeeded"); |
| } |
| return true; |
| } catch (LoginException le) { |
| // authentication failed |
| cleanState(); |
| if (debug) { |
| System.out.println("\t\t[JndiLoginModule] " + |
| "useFirstPass failed"); |
| } |
| throw le; |
| } |
| } |
| |
| // attempt the authentication by prompting for the username and pwd |
| try { |
| attemptAuthentication(false); |
| |
| // authentication succeeded |
| succeeded = true; |
| if (debug) { |
| System.out.println("\t\t[JndiLoginModule] " + |
| "regular authentication succeeded"); |
| } |
| return true; |
| } catch (LoginException le) { |
| cleanState(); |
| if (debug) { |
| System.out.println("\t\t[JndiLoginModule] " + |
| "regular authentication failed"); |
| } |
| throw le; |
| } |
| } |
| |
| /** |
| * 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>UnixPrincipal</code> |
| * with the <code>Subject</code> located in the |
| * <code>LoginModule</code>. 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 { |
| |
| if (succeeded == false) { |
| return false; |
| } else { |
| if (subject.isReadOnly()) { |
| cleanState(); |
| throw new LoginException ("Subject is Readonly"); |
| } |
| // add Principals to the Subject |
| if (!subject.getPrincipals().contains(userPrincipal)) |
| subject.getPrincipals().add(userPrincipal); |
| if (!subject.getPrincipals().contains(UIDPrincipal)) |
| subject.getPrincipals().add(UIDPrincipal); |
| if (!subject.getPrincipals().contains(GIDPrincipal)) |
| subject.getPrincipals().add(GIDPrincipal); |
| for (int i = 0; i < supplementaryGroups.size(); i++) { |
| if (!subject.getPrincipals().contains |
| (supplementaryGroups.get(i))) |
| subject.getPrincipals().add(supplementaryGroups.get(i)); |
| } |
| |
| if (debug) { |
| System.out.println("\t\t[JndiLoginModule]: " + |
| "added UnixPrincipal,"); |
| System.out.println("\t\t\t\tUnixNumericUserPrincipal,"); |
| System.out.println("\t\t\t\tUnixNumericGroupPrincipal(s),"); |
| System.out.println("\t\t\t to Subject"); |
| } |
| } |
| // in any case, clean out state |
| cleanState(); |
| commitSucceeded = true; |
| 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> |
| * |
| * @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 (debug) |
| System.out.println("\t\t[JndiLoginModule]: " + |
| "aborted authentication failed"); |
| |
| if (succeeded == false) { |
| return false; |
| } else if (succeeded == true && commitSucceeded == false) { |
| |
| // Clean out state |
| succeeded = false; |
| cleanState(); |
| |
| userPrincipal = null; |
| UIDPrincipal = null; |
| GIDPrincipal = null; |
| supplementaryGroups = new LinkedList<UnixNumericGroupPrincipal>(); |
| } else { |
| // overall authentication succeeded and commit succeeded, |
| // but someone else's commit failed |
| logout(); |
| } |
| return true; |
| } |
| |
| /** |
| * Logout a user. |
| * |
| * <p> This method removes the Principals |
| * that were added by the <code>commit</code> method. |
| * |
| * <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 (subject.isReadOnly()) { |
| cleanState(); |
| throw new LoginException ("Subject is Readonly"); |
| } |
| subject.getPrincipals().remove(userPrincipal); |
| subject.getPrincipals().remove(UIDPrincipal); |
| subject.getPrincipals().remove(GIDPrincipal); |
| for (int i = 0; i < supplementaryGroups.size(); i++) { |
| subject.getPrincipals().remove(supplementaryGroups.get(i)); |
| } |
| |
| |
| // clean out state |
| cleanState(); |
| succeeded = false; |
| commitSucceeded = false; |
| |
| userPrincipal = null; |
| UIDPrincipal = null; |
| GIDPrincipal = null; |
| supplementaryGroups = new LinkedList<UnixNumericGroupPrincipal>(); |
| |
| if (debug) { |
| System.out.println("\t\t[JndiLoginModule]: " + |
| "logged out Subject"); |
| } |
| return true; |
| } |
| |
| /** |
| * Attempt authentication |
| * |
| * <p> |
| * |
| * @param getPasswdFromSharedState boolean that tells this method whether |
| * to retrieve the password from the sharedState. |
| */ |
| private void attemptAuthentication(boolean getPasswdFromSharedState) |
| throws LoginException { |
| |
| String encryptedPassword = null; |
| |
| // first get the username and password |
| getUsernamePassword(getPasswdFromSharedState); |
| |
| try { |
| |
| // get the user's passwd entry from the user provider URL |
| InitialContext iCtx = new InitialContext(); |
| ctx = (DirContext)iCtx.lookup(userProvider); |
| |
| /* |
| SearchControls controls = new SearchControls |
| (SearchControls.ONELEVEL_SCOPE, |
| 0, |
| 5000, |
| new String[] { USER_PWD }, |
| false, |
| false); |
| */ |
| |
| SearchControls controls = new SearchControls(); |
| NamingEnumeration<SearchResult> ne = ctx.search("", |
| "(uid=" + username + ")", |
| controls); |
| if (ne.hasMore()) { |
| SearchResult result = ne.next(); |
| Attributes attributes = result.getAttributes(); |
| |
| // get the password |
| |
| // this module works only if the LDAP directory server |
| // is configured to permit read access to the userPassword |
| // attribute. The directory administrator need to grant |
| // this access. |
| // |
| // A workaround would be to make the server do authentication |
| // by setting the Context.SECURITY_PRINCIPAL |
| // and Context.SECURITY_CREDENTIALS property. |
| // However, this would make it not work with systems that |
| // don't do authentication at the server (like NIS). |
| // |
| // Setting the SECURITY_* properties and using "simple" |
| // authentication for LDAP is recommended only for secure |
| // channels. For nonsecure channels, SSL is recommended. |
| |
| Attribute pwd = attributes.get(USER_PWD); |
| String encryptedPwd = new String((byte[])pwd.get(), "UTF8"); |
| encryptedPassword = encryptedPwd.substring(CRYPT.length()); |
| |
| // check the password |
| if (verifyPassword |
| (encryptedPassword, new String(password)) == true) { |
| |
| // authentication succeeded |
| if (debug) |
| System.out.println("\t\t[JndiLoginModule] " + |
| "attemptAuthentication() succeeded"); |
| |
| } else { |
| // authentication failed |
| if (debug) |
| System.out.println("\t\t[JndiLoginModule] " + |
| "attemptAuthentication() failed"); |
| throw new FailedLoginException("Login incorrect"); |
| } |
| |
| // save input as shared state only if |
| // authentication succeeded |
| if (storePass && |
| !sharedState.containsKey(NAME) && |
| !sharedState.containsKey(PWD)) { |
| sharedState.put(NAME, username); |
| sharedState.put(PWD, password); |
| } |
| |
| // create the user principal |
| userPrincipal = new UnixPrincipal(username); |
| |
| // get the UID |
| Attribute uid = attributes.get(USER_UID); |
| String uidNumber = (String)uid.get(); |
| UIDPrincipal = new UnixNumericUserPrincipal(uidNumber); |
| if (debug && uidNumber != null) { |
| System.out.println("\t\t[JndiLoginModule] " + |
| "user: '" + username + "' has UID: " + |
| uidNumber); |
| } |
| |
| // get the GID |
| Attribute gid = attributes.get(USER_GID); |
| String gidNumber = (String)gid.get(); |
| GIDPrincipal = new UnixNumericGroupPrincipal |
| (gidNumber, true); |
| if (debug && gidNumber != null) { |
| System.out.println("\t\t[JndiLoginModule] " + |
| "user: '" + username + "' has GID: " + |
| gidNumber); |
| } |
| |
| // get the supplementary groups from the group provider URL |
| ctx = (DirContext)iCtx.lookup(groupProvider); |
| ne = ctx.search("", new BasicAttributes("memberUid", username)); |
| |
| while (ne.hasMore()) { |
| result = ne.next(); |
| attributes = result.getAttributes(); |
| |
| gid = attributes.get(GROUP_ID); |
| String suppGid = (String)gid.get(); |
| if (!gidNumber.equals(suppGid)) { |
| UnixNumericGroupPrincipal suppPrincipal = |
| new UnixNumericGroupPrincipal(suppGid, false); |
| supplementaryGroups.add(suppPrincipal); |
| if (debug && suppGid != null) { |
| System.out.println("\t\t[JndiLoginModule] " + |
| "user: '" + username + |
| "' has Supplementary Group: " + |
| suppGid); |
| } |
| } |
| } |
| |
| } else { |
| // bad username |
| if (debug) { |
| System.out.println("\t\t[JndiLoginModule]: User not found"); |
| } |
| throw new FailedLoginException("User not found"); |
| } |
| } catch (NamingException ne) { |
| // bad username |
| if (debug) { |
| System.out.println("\t\t[JndiLoginModule]: User not found"); |
| ne.printStackTrace(); |
| } |
| throw new FailedLoginException("User not found"); |
| } catch (java.io.UnsupportedEncodingException uee) { |
| // password stored in incorrect format |
| if (debug) { |
| System.out.println("\t\t[JndiLoginModule]: " + |
| "password incorrectly encoded"); |
| uee.printStackTrace(); |
| } |
| throw new LoginException("Login failure due to incorrect " + |
| "password encoding in the password database"); |
| } |
| |
| // authentication succeeded |
| } |
| |
| /** |
| * Get the username and password. |
| * This method does not return any value. |
| * Instead, it sets global name and password variables. |
| * |
| * <p> Also note that this method will set the username and password |
| * values in the shared state in case subsequent LoginModules |
| * want to use them via use/tryFirstPass. |
| * |
| * <p> |
| * |
| * @param getPasswdFromSharedState boolean that tells this method whether |
| * to retrieve the password from the sharedState. |
| */ |
| private void getUsernamePassword(boolean getPasswdFromSharedState) |
| throws LoginException { |
| |
| if (getPasswdFromSharedState) { |
| // use the password saved by the first module in the stack |
| username = (String)sharedState.get(NAME); |
| password = (char[])sharedState.get(PWD); |
| return; |
| } |
| |
| // prompt for a username and password |
| if (callbackHandler == null) |
| throw new LoginException("Error: no CallbackHandler available " + |
| "to garner authentication information from the user"); |
| |
| String protocol = userProvider.substring(0, userProvider.indexOf(":")); |
| |
| Callback[] callbacks = new Callback[2]; |
| callbacks[0] = new NameCallback(protocol + " " |
| + rb.getString("username.")); |
| callbacks[1] = new PasswordCallback(protocol + " " + |
| rb.getString("password."), |
| false); |
| |
| try { |
| callbackHandler.handle(callbacks); |
| username = ((NameCallback)callbacks[0]).getName(); |
| char[] tmpPassword = ((PasswordCallback)callbacks[1]).getPassword(); |
| password = new char[tmpPassword.length]; |
| System.arraycopy(tmpPassword, 0, |
| password, 0, tmpPassword.length); |
| ((PasswordCallback)callbacks[1]).clearPassword(); |
| |
| } catch (java.io.IOException ioe) { |
| throw new LoginException(ioe.toString()); |
| } catch (UnsupportedCallbackException uce) { |
| throw new LoginException("Error: " + uce.getCallback().toString() + |
| " not available to garner authentication information " + |
| "from the user"); |
| } |
| |
| // print debugging information |
| if (strongDebug) { |
| System.out.println("\t\t[JndiLoginModule] " + |
| "user entered username: " + |
| username); |
| System.out.print("\t\t[JndiLoginModule] " + |
| "user entered password: "); |
| for (int i = 0; i < password.length; i++) |
| System.out.print(password[i]); |
| System.out.println(); |
| } |
| } |
| |
| /** |
| * Verify a password against the encrypted passwd from /etc/shadow |
| */ |
| private boolean verifyPassword(String encryptedPassword, String password) { |
| |
| if (encryptedPassword == null) |
| return false; |
| |
| Crypt c = new Crypt(); |
| try { |
| byte oldCrypt[] = encryptedPassword.getBytes("UTF8"); |
| byte newCrypt[] = c.crypt(password.getBytes("UTF8"), |
| oldCrypt); |
| if (newCrypt.length != oldCrypt.length) |
| return false; |
| for (int i = 0; i < newCrypt.length; i++) { |
| if (oldCrypt[i] != newCrypt[i]) |
| return false; |
| } |
| } catch (java.io.UnsupportedEncodingException uee) { |
| // cannot happen, but return false just to be safe |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * Clean out state because of a failed authentication attempt |
| */ |
| private void cleanState() { |
| username = null; |
| if (password != null) { |
| for (int i = 0; i < password.length; i++) |
| password[i] = ' '; |
| password = null; |
| } |
| ctx = null; |
| |
| if (clearPass) { |
| sharedState.remove(NAME); |
| sharedState.remove(PWD); |
| } |
| } |
| } |