You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by el...@apache.org on 2007/04/18 16:39:16 UTC
svn commit: r530039 -
/directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/authn/SimpleAuthenticator.java
Author: elecharny
Date: Wed Apr 18 07:39:15 2007
New Revision: 530039
URL: http://svn.apache.org/viewvc?view=rev&rev=530039
Log:
Added some more helper functions for clarity sake
Modified:
directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/authn/SimpleAuthenticator.java
Modified: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/authn/SimpleAuthenticator.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/authn/SimpleAuthenticator.java?view=diff&rev=530039&r1=530038&r2=530039
==============================================================================
--- directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/authn/SimpleAuthenticator.java (original)
+++ directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/authn/SimpleAuthenticator.java Wed Apr 18 07:39:15 2007
@@ -62,6 +62,8 @@
* password is stored with a one-way encryption applied (e.g. SHA), the password
* is hashed the same way before comparison.
*
+ * We use a cache to speedup authentication, where the DN/password are stored.
+ *
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
public class SimpleAuthenticator extends AbstractAuthenticator
@@ -139,13 +141,42 @@
credentialCache = new LRUMap( cacheSize > 0 ? cacheSize : DEFAULT_CACHE_SIZE );
}
- private class SaltedPassword
+ /**
+ * A private class to store all informations about the existing
+ * password found in the cache or get from the backend.
+ *
+ * This is necessary as we have to compute :
+ * - the used algorithm
+ * - the salt if any
+ * - the password itself.
+ *
+ * If we have a on-way encrypted password, it is stored using this
+ * format :
+ * {<algorithm>}<encrypted password>
+ * where the encrypted password format can be :
+ * - MD5/SHA : base64([<salt (8 bytes)>]<password>)
+ * - crypt : <salt (2 btytes)><password>
+ *
+ * Algorithm are currently MD5, SMD5, SHA, SSHA, CRYPT and empty
+ */
+ private class EncryptionMethod
{
private byte[] salt;
- private byte[] password;
private String algorithm;
+
+ private EncryptionMethod( String algorithm, byte[] salt )
+ {
+ this.algorithm = algorithm;
+ this.salt = salt;
+ }
}
+ /**
+ * Get the password either from cache or from backend.
+ * @param principalDN The DN from which we want the password
+ * @return A byte array which can be empty if the password was not found
+ * @throws NamingException If we have a problem during the lookup operation
+ */
private byte[] getStoredPassword( LdapDN principalDN ) throws NamingException
{
LdapPrincipal principal = null;
@@ -182,7 +213,71 @@
return storedPassword;
}
-
+
+ /**
+ * Get the user credentials from the environment. It is stored into the
+ * ServcerContext.
+ * @param ctx
+ * @param principalDn
+ * @return
+ * @throws LdapAuthenticationException
+ */
+ private byte[] getCredentials( ServerContext ctx, LdapDN principalDn ) throws LdapAuthenticationException
+ {
+ Object creds = ctx.getEnvironment().get( Context.SECURITY_CREDENTIALS );
+ byte[] credentials = null;
+
+ if ( creds == null )
+ {
+ credentials = ArrayUtils.EMPTY_BYTE_ARRAY;
+ }
+ else if ( creds instanceof String )
+ {
+ credentials = StringTools.getBytesUtf8( ( String ) creds );
+ }
+ else if ( creds instanceof byte[] )
+ {
+ // This is the general case. When dealing with a BindRequest operation,
+ // received by the server, the credentials are always stored into a byte array
+ credentials = (byte[])creds;
+ }
+ else
+ {
+ log.info( "Incorrect credentials stored in {}", Context.SECURITY_CREDENTIALS );
+ throw new LdapAuthenticationException();
+ }
+
+ return credentials;
+ }
+
+ /**
+ * Helper function used to update the cache with the user's password,
+ * if the cache is not containing this information.
+ *
+ * The LdapPrincipal will be empty if this password is not cached.
+ */
+ private LdapPrincipal updateCache( LdapPrincipal principal, LdapDN principalDn, byte[] storedPassword )
+ {
+ if ( principal == null )
+ {
+ // If we have found the credential, we have to store it in the cache
+ principal = new LdapPrincipal( principalDn, AuthenticationLevel.SIMPLE, storedPassword );
+
+ // Now, update the local cache.
+ synchronized( credentialCache )
+ {
+ credentialCache.put( principalDn.getNormName(), principal );
+ }
+ }
+
+ if ( IS_DEBUG )
+ {
+ log.debug( "{} Authenticated", principalDn );
+ }
+
+ return principal;
+ }
+
/**
* Looks up <tt>userPassword</tt> attribute of the entry whose name is the
* value of {@link Context#SECURITY_PRINCIPAL} environment variable, and
@@ -221,29 +316,7 @@
}
// ---- extract password from JNDI environment
- Object creds = ctx.getEnvironment().get( Context.SECURITY_CREDENTIALS );
- byte[] credentials = null;
- String principalNorm = principalDn.getNormName();
-
- if ( creds == null )
- {
- credentials = ArrayUtils.EMPTY_BYTE_ARRAY;
- }
- else if ( creds instanceof String )
- {
- credentials = StringTools.getBytesUtf8( ( String ) creds );
- }
- else if ( creds instanceof byte[] )
- {
- // This is the general case. When dealing with a BindRequest operation,
- // received by the server, the credentials are always stored into a byte array
- credentials = (byte[])creds;
- }
- else
- {
- log.info( "Incorrect credentials stored in {}", Context.SECURITY_CREDENTIALS );
- throw new LdapAuthenticationException();
- }
+ byte[] credentials = getCredentials( ctx, principalDn );
boolean credentialsMatch = false;
LdapPrincipal principal = null;
@@ -253,60 +326,49 @@
// Short circuit for PLAIN TEXT passwords : we compare the byte array directly
// Are the passwords equal ?
- credentialsMatch = Arrays.equals( credentials, storedPassword );
-
+ if ( Arrays.equals( credentials, storedPassword ) )
+ {
+ return updateCache( principal, principalDn, storedPassword );
+ }
+ // Let's see if the stored password was encrypted
+ String algorithm = findAlgorithm( storedPassword );
- if ( !credentialsMatch )
+ if ( algorithm != null )
{
- // Let's see if the stored password was encrypted
- String algorithm = findAlgorithm( storedPassword );
+ EncryptionMethod encryptionMethod = new EncryptionMethod( algorithm, null );
- if ( algorithm != null )
- {
- SaltedPassword saltedPassword = new SaltedPassword();
- saltedPassword.password = storedPassword;
- saltedPassword.salt = null;
- saltedPassword.algorithm = algorithm;
-
- // Let's get the encrypted part of the stored password
- byte[] encryptedStored = splitCredentials( saltedPassword );
-
- saltedPassword.password = credentials;
- byte[] userPassword = encryptPassword( credentials, saltedPassword );
-
- credentialsMatch = Arrays.equals( userPassword, encryptedStored );
- }
-
- }
+ // Let's get the encrypted part of the stored password
+ // We should just keep the password, excluding the algorithm
+ // and the salt, if any.
+ // But we should also get the algorithm and salt to
+ // be able to encrypt the submitted user password in the next step
+ byte[] encryptedStored = splitCredentials( storedPassword, encryptionMethod );
- // If the password match, we can return
- if ( credentialsMatch )
- {
- if ( principal == null )
+ // Reuse the slatedPassword informations to construct the encrypted
+ // password given by the user.
+ byte[] userPassword = encryptPassword( credentials, encryptionMethod );
+
+ // Now, compare the two passwords.
+ if ( Arrays.equals( userPassword, encryptedStored ) )
{
- // Last, if we have found the credential, we have to store it in the cache
- principal = new LdapPrincipal( principalDn, AuthenticationLevel.SIMPLE, storedPassword );
-
- // Now, update the local cache.
- synchronized( credentialCache )
- {
- credentialCache.put( principalNorm, principal );
- }
+ return updateCache( principal, principalDn, storedPassword );
}
-
- if ( IS_DEBUG )
+ else
{
- log.debug( "{} Authenticated", principalDn );
+ // Bad password ...
+ String message = "Password not correct for user '" + principalDn.getUpName() + "'";
+ log.info( message );
+ throw new LdapAuthenticationException(message);
}
-
- return principal;
}
-
- // Bad password ...
- String message = "Password not correct for user '" + principalDn.getUpName() + "'";
- log.info( message );
- throw new LdapAuthenticationException(message);
+ else
+ {
+ // Bad password ...
+ String message = "Password not correct for user '" + principalDn.getUpName() + "'";
+ log.info( message );
+ throw new LdapAuthenticationException(message);
+ }
}
private static void split( byte[] all, int offset, byte[] left, byte[] right )
@@ -315,10 +377,19 @@
System.arraycopy( all, offset + left.length, right, 0, right.length );
}
- private byte[] splitCredentials( SaltedPassword saltedPassword )
+ /**
+ * Decopose the stored password in an algorithm, an eventual salt
+ * and the password itself.
+ *
+ * If the algorithm is SHA, SSHA, MD5 or SMD5, the part following the algorithm
+ * is base64 encoded
+ *
+ * @param encryptionMethod The structure to feed
+ * @return The password
+ */
+ private byte[] splitCredentials( byte[] credentials, EncryptionMethod encryptionMethod )
{
- byte[] credentials = saltedPassword.password;
- String algorithm = saltedPassword.algorithm;
+ String algorithm = encryptionMethod.algorithm;
int pos = algorithm.length() + 2;
@@ -327,6 +398,8 @@
{
try
{
+ // We just have the password just after the algorithm, base64 encoded.
+ // Just decode the password and return it.
return Base64.decode( new String( credentials, pos, credentials.length - ( pos + 1 ), "UTF-8" ).toCharArray() );
}
catch ( UnsupportedEncodingException uee )
@@ -340,13 +413,17 @@
{
try
{
- byte[] password = Base64.decode( new String( credentials, pos, credentials.length - ( pos + 1 ), "UTF-8" ).toCharArray() );
+ // The password is associated with a salt. Decompose it
+ // in two parts, after having decoded the password.
+ // The salt will be stored into the EncryptionMethod structure
+ // The salt is at the end of the credentials, and is 8 bytes long
+ byte[] passwordAndSalt = Base64.decode( new String( credentials, pos, credentials.length - ( pos + 1 ), "UTF-8" ).toCharArray() );
- saltedPassword.salt = new byte[8];
- byte[] hashedPassword = new byte[password.length - saltedPassword.salt.length];
- split( password, 0, hashedPassword, saltedPassword.salt );
+ encryptionMethod.salt = new byte[8];
+ byte[] password = new byte[passwordAndSalt.length - encryptionMethod.salt.length];
+ split( passwordAndSalt, 0, password, encryptionMethod.salt );
- return hashedPassword;
+ return password;
}
catch ( UnsupportedEncodingException uee )
{
@@ -356,11 +433,14 @@
}
else if ( LdapSecurityConstants.HASH_METHOD_CRYPT.equals( algorithm ) )
{
- saltedPassword.salt = new byte[2];
- byte[] hashedPassword = new byte[credentials.length - saltedPassword.salt.length - pos];
- split( credentials, pos, saltedPassword.salt, hashedPassword );
+ // The password is associated with a salt. Decompose it
+ // in two parts, storing the salt into the EncryptionMethod structure.
+ // The salt comes first, not like for SSHA and SMD5, and is 2 bytes long
+ encryptionMethod.salt = new byte[2];
+ byte[] password = new byte[credentials.length - encryptionMethod.salt.length - pos];
+ split( credentials, pos, encryptionMethod.salt, password );
- return hashedPassword;
+ return password;
}
else
{
@@ -369,6 +449,11 @@
}
}
+ /**
+ * Get the algorithm from the stored password.
+ * It can be found on the beginning of the stored password, between
+ * curly brackets.
+ */
private String findAlgorithm( byte[] credentials )
{
if ( ( credentials == null ) || ( credentials.length == 0 ) )
@@ -427,7 +512,11 @@
return null;
}
}
-
+
+ /**
+ * Compute the hashed password given an algorithm, the credentials and
+ * an optional salt.
+ */
private static byte[] digest( String algorithm, byte[] password, byte[] salt )
{
MessageDigest digest;
@@ -453,10 +542,10 @@
}
}
- private byte[] encryptPassword( byte[] credentials, SaltedPassword saltedPassword )
+ private byte[] encryptPassword( byte[] credentials, EncryptionMethod encryptionMethod )
{
- String algorithm = saltedPassword.algorithm;
- byte[] salt = saltedPassword.salt;
+ String algorithm = encryptionMethod.algorithm;
+ byte[] salt = encryptionMethod.salt;
if ( LdapSecurityConstants.HASH_METHOD_SHA.equals( algorithm ) ||
LdapSecurityConstants.HASH_METHOD_SSHA.equals( algorithm ) )