You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by ka...@apache.org on 2010/09/13 12:26:45 UTC
svn commit: r996483 -
/directory/shared/trunk/ldap-client-api/src/main/java/org/apache/directory/ldap/client/api/LdapNetworkConnection.java
Author: kayyagari
Date: Mon Sep 13 10:26:44 2010
New Revision: 996483
URL: http://svn.apache.org/viewvc?rev=996483&view=rev
Log:
o added support for StartTLS
o added support for CRAM-MD5 and DIGEST-MD5 SASL mechanisms
Modified:
directory/shared/trunk/ldap-client-api/src/main/java/org/apache/directory/ldap/client/api/LdapNetworkConnection.java
Modified: directory/shared/trunk/ldap-client-api/src/main/java/org/apache/directory/ldap/client/api/LdapNetworkConnection.java
URL: http://svn.apache.org/viewvc/directory/shared/trunk/ldap-client-api/src/main/java/org/apache/directory/ldap/client/api/LdapNetworkConnection.java?rev=996483&r1=996482&r2=996483&view=diff
==============================================================================
--- directory/shared/trunk/ldap-client-api/src/main/java/org/apache/directory/ldap/client/api/LdapNetworkConnection.java (original)
+++ directory/shared/trunk/ldap-client-api/src/main/java/org/apache/directory/ldap/client/api/LdapNetworkConnection.java Mon Sep 13 10:26:44 2010
@@ -37,7 +37,10 @@ import java.util.concurrent.atomic.Atomi
import java.util.concurrent.atomic.AtomicInteger;
import javax.net.ssl.SSLContext;
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslClient;
+import org.apache.directory.ldap.client.api.callback.SaslCallbackHandler;
import org.apache.directory.ldap.client.api.exception.InvalidConnectionException;
import org.apache.directory.ldap.client.api.future.AddFuture;
import org.apache.directory.ldap.client.api.future.BindFuture;
@@ -57,6 +60,7 @@ import org.apache.directory.shared.ldap.
import org.apache.directory.shared.ldap.codec.MessageEncoderException;
import org.apache.directory.shared.ldap.codec.controls.ControlImpl;
import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.constants.SupportedSaslMechanisms;
import org.apache.directory.shared.ldap.cursor.Cursor;
import org.apache.directory.shared.ldap.entry.DefaultEntry;
import org.apache.directory.shared.ldap.entry.Entry;
@@ -67,6 +71,7 @@ import org.apache.directory.shared.ldap.
import org.apache.directory.shared.ldap.exception.LdapException;
import org.apache.directory.shared.ldap.exception.LdapInvalidDnException;
import org.apache.directory.shared.ldap.exception.LdapNoPermissionException;
+import org.apache.directory.shared.ldap.exception.LdapOperationException;
import org.apache.directory.shared.ldap.filter.SearchScope;
import org.apache.directory.shared.ldap.message.AbandonRequest;
import org.apache.directory.shared.ldap.message.AbandonRequestImpl;
@@ -77,6 +82,7 @@ import org.apache.directory.shared.ldap.
import org.apache.directory.shared.ldap.message.BindRequest;
import org.apache.directory.shared.ldap.message.BindRequestImpl;
import org.apache.directory.shared.ldap.message.BindResponse;
+import org.apache.directory.shared.ldap.message.BindResponseImpl;
import org.apache.directory.shared.ldap.message.CompareRequest;
import org.apache.directory.shared.ldap.message.CompareRequestImpl;
import org.apache.directory.shared.ldap.message.CompareResponse;
@@ -88,6 +94,7 @@ import org.apache.directory.shared.ldap.
import org.apache.directory.shared.ldap.message.ExtendedResponse;
import org.apache.directory.shared.ldap.message.IntermediateResponse;
import org.apache.directory.shared.ldap.message.IntermediateResponseImpl;
+import org.apache.directory.shared.ldap.message.LdapResult;
import org.apache.directory.shared.ldap.message.Message;
import org.apache.directory.shared.ldap.message.ModifyDnRequest;
import org.apache.directory.shared.ldap.message.ModifyDnRequestImpl;
@@ -194,6 +201,12 @@ public class LdapNetworkConnection exten
/** the schema manager */
private SchemaManager schemaManager;
+ /** the SslFilter key */
+ private static final String SSL_FILTER_KEY = "sslFilter";
+
+ /** the StartTLS extended operation's OID */
+ private static final String START_TLS_REQ_OID = "1.3.6.1.4.1.1466.20037";
+
// ~~~~~~~~~~~~~~~~~ common error messages ~~~~~~~~~~~~~~~~~~~~~~~~~~
private static final String OPERATION_CANCELLED = "Operation would have been cancelled";
@@ -462,21 +475,7 @@ public class LdapNetworkConnection exten
// If we use SSL, we have to add the SslFilter to the chain
if ( config.isUseSsl() )
{
- try
- {
- SSLContext sslContext = SSLContext.getInstance( config.getSslProtocol() );
- sslContext.init( config.getKeyManagers(), config.getTrustManagers(), config.getSecureRandom() );
-
- SslFilter sslFilter = new SslFilter( sslContext );
- sslFilter.setUseClientMode( true );
- connector.getFilterChain().addFirst( "sslFilter", sslFilter );
- }
- catch ( Exception e )
- {
- String msg = "Failed to initialize the SSL context";
- LOG.error( msg, e );
- throw new LdapException( msg, e );
- }
+ addSslFilter();
}
// Add an executor so that this connection can be used
@@ -1096,32 +1095,118 @@ public class LdapNetworkConnection exten
// If the session has not been establish, or is closed, we get out immediately
checkSession();
- // Update the messageId
- int newId = messageId.incrementAndGet();
- bindRequest.setMessageId( newId );
+ if ( bindRequest.isSimple() )
+ {
+ // Update the messageId
+ int newId = messageId.incrementAndGet();
+ bindRequest.setMessageId( newId );
- LOG.debug( "-----------------------------------------------------------------" );
- LOG.debug( "Sending request \n{}", bindRequest );
+ LOG.debug( "-----------------------------------------------------------------" );
+ LOG.debug( "Sending request \n{}", bindRequest );
- // Create a future for this Bind operation
- BindFuture bindFuture = new BindFuture( this, newId );
+ // Create a future for this Bind operation
+ BindFuture bindFuture = new BindFuture( this, newId );
- addToFutureMap( newId, bindFuture );
+ addToFutureMap( newId, bindFuture );
- // Send the request to the server
- WriteFuture writeFuture = ldapSession.write( bindRequest );
+ writeBindRequest( bindRequest );
- // Wait for the message to be sent to the server
- if ( !writeFuture.awaitUninterruptibly( getTimeout( 0 ) ) )
+ // Ok, done return the future
+ return bindFuture;
+ }
+ else
{
- // We didn't received anything : this is an error
- LOG.error( "Bind failed : timeout occured" );
+ return bindSasl( new SaslRequest( bindRequest ) );
+ }
+ }
- throw new LdapException( TIME_OUT_ERROR );
+
+ /**
+ * @see #bindCramMd5(String, byte[], String)
+ */
+ public BindResponse bindCramMd5( String name, String credentials, String authzId )
+ throws LdapException,
+ IOException
+ {
+ return bindCramMd5( name, StringTools.getBytesUtf8( credentials ), authzId );
+ }
+
+
+ /**
+ *
+ * bind using CRAM-MD5 SASL mechanism.
+ *
+ * @param name the DN of the user
+ * @param credentials password of the user
+ * @param authzId the authorization ID (can be null)
+ * @return response of the bind operation
+ * @throws LdapException
+ * @throws IOException
+ */
+ public BindResponse bindCramMd5( String name, byte[] credentials, String authzId )
+ throws LdapException,
+ IOException
+ {
+ BindFuture bindFuture = bindSasl( name, credentials, SupportedSaslMechanisms.DIGEST_MD5, authzId, null );
+
+ try
+ {
+ return bindFuture.get();
}
+ catch ( Exception ie )
+ {
+ // Catch all other exceptions
+ LOG.error( NO_RESPONSE_ERROR, ie );
+ LdapException ldapException = new LdapException( NO_RESPONSE_ERROR );
+ ldapException.initCause( ie );
- // Ok, done return the future
- return bindFuture;
+ throw ldapException;
+ }
+ }
+
+
+ /**
+ * @see #bindCramMd5(String, byte[], String)
+ */
+ public BindResponse bindDigestMd5( String name, String credentials, String authzId, String realmName )
+ throws LdapException,
+ IOException
+ {
+ return bindDigestMd5( name, StringTools.getBytesUtf8( credentials ), authzId, realmName );
+ }
+
+
+ /**
+ *
+ * bind using DIGEST-MD5 SASL mechanism.
+ *
+ * @param name the DN of the user
+ * @param credentials password of the user
+ * @param authzId the authorization ID (can be null)
+ * @param realmName the SASL realm name to be used
+ * @return response of the bind operation
+ * @throws LdapException
+ * @throws IOException
+ */
+ public BindResponse bindDigestMd5( String name, byte[] credentials, String authzId, String realmName )
+ throws LdapException,
+ IOException
+ {
+ BindFuture bindFuture = bindSasl( name, credentials, SupportedSaslMechanisms.DIGEST_MD5, authzId, realmName );
+
+ try
+ {
+ return bindFuture.get();
+ }
+ catch ( Exception ie )
+ {
+ // Catch all other exceptions
+ LOG.error( NO_RESPONSE_ERROR, ie );
+ LdapException ldapException = new LdapException( NO_RESPONSE_ERROR );
+ ldapException.initCause( ie );
+
+ throw ldapException;
+ }
}
@@ -3236,4 +3321,224 @@ public class LdapNetworkConnection exten
}
}
+
+ /**
+ * sends the StartTLS extended request to server and adds a security layer
+ * upon receiving a response with successful result
+ *
+ * @throws LdapException
+ */
+ public void startTls() throws LdapException
+ {
+ try
+ {
+ connect();
+
+ checkSession();
+
+ ExtendedResponse resp = extended( START_TLS_REQ_OID );
+ LdapResult result = resp.getLdapResult();
+
+ if ( result.getResultCode() == ResultCodeEnum.SUCCESS )
+ {
+ System.out.println( "received successful result code " + result );
+ addSslFilter();
+ }
+ else
+ {
+ throw new LdapOperationException( result.getResultCode(), result.getErrorMessage() );
+ }
+ }
+ catch ( LdapException e )
+ {
+ throw e;
+ }
+ catch ( Exception e )
+ {
+ throw new LdapException( e );
+ }
+ }
+
+
+ /**
+ * adds {@link SslFilter} to the IOConnector or IOSession's filter chain
+ */
+ private void addSslFilter() throws LdapException
+ {
+ try
+ {
+ SSLContext sslContext = SSLContext.getInstance( config.getSslProtocol() );
+ sslContext.init( config.getKeyManagers(), config.getTrustManagers(), config.getSecureRandom() );
+
+ SslFilter sslFilter = new SslFilter( sslContext );
+ sslFilter.setUseClientMode( true );
+
+ // for LDAPS
+ if ( ldapSession == null )
+ {
+ connector.getFilterChain().addFirst( SSL_FILTER_KEY, sslFilter );
+ }
+ else
+ // for StartTLS
+ {
+ ldapSession.getFilterChain().addFirst( SSL_FILTER_KEY, sslFilter );
+ }
+ }
+ catch ( Exception e )
+ {
+ String msg = "Failed to initialize the SSL context";
+ LOG.error( msg, e );
+ throw new LdapException( msg, e );
+ }
+ }
+
+
+ /**
+ * perform SASL based bind operation @see {@link #bindSasl(SaslRequest)}
+ */
+ private BindFuture bindSasl( String name, byte[] credentials, String saslMech, String authzId, String realmName )
+ throws LdapException, IOException
+ {
+ BindRequest bindReq = createBindRequest( name, credentials );
+ bindReq.setSaslMechanism( saslMech );
+ bindReq.setSimple( false );
+
+ SaslRequest saslReq = new SaslRequest( bindReq );
+ saslReq.setRealmName( realmName );
+ saslReq.setAuthorizationId( authzId );
+
+ return bindSasl( saslReq );
+ }
+
+
+ private BindFuture bindSasl( SaslRequest saslReq ) throws LdapException, IOException
+ {
+ // First switch to anonymous state
+ authenticated.set( false );
+
+ // try to connect, if we aren't already connected.
+ connect();
+
+ // If the session has not been establish, or is closed, we get out immediately
+ checkSession();
+
+ BindRequest bindRequest = saslReq.getBindReq();
+
+ // Update the messageId
+ int newId = messageId.incrementAndGet();
+ bindRequest.setMessageId( newId );
+
+ LOG.debug( "-----------------------------------------------------------------" );
+ LOG.debug( "Sending request \n{}", bindRequest );
+
+ // Create a future for this Bind operation
+ BindFuture bindFuture = new BindFuture( this, newId );
+
+ addToFutureMap( newId, bindFuture );
+
+ try
+ {
+ BindResponse bindResponse = null;
+ byte[] response = null;
+ ResultCodeEnum result = null;
+
+ SaslClient sc = Sasl.createSaslClient( new String[]
+ { bindRequest.getSaslMechanism() }, saslReq.getAuthorizationId(), "ldap", config.getLdapHost(),
+ saslReq.getSaslMechProps(), new SaslCallbackHandler( saslReq ) );
+
+ // handcode bindresponse and return
+ if ( sc == null )
+ {
+ removeFromFutureMaps( newId );
+ bindResponse = new BindResponseImpl( newId );
+ bindResponse.getLdapResult().setResultCode( ResultCodeEnum.AUTH_METHOD_NOT_SUPPORTED );
+ bindFuture.set( bindResponse );
+
+ return bindFuture;
+ }
+
+ if ( sc.hasInitialResponse() )
+ {
+ response = sc.evaluateChallenge( new byte[0] );
+
+ bindRequest.setCredentials( response );
+ writeBindRequest( bindRequest );
+
+ bindResponse = bindFuture.get( timeout, TimeUnit.MILLISECONDS );
+ result = bindResponse.getLdapResult().getResultCode();
+ }
+ else
+ {
+ // clone the bindRequest without setting the credentials
+ BindRequest clonedReq = new BindRequestImpl( newId );
+ clonedReq.setName( bindRequest.getName() );
+ clonedReq.setSaslMechanism( bindRequest.getSaslMechanism() );
+ clonedReq.setSimple( bindRequest.isSimple() );
+ clonedReq.setVersion3( bindRequest.getVersion3() );
+ clonedReq.addAllControls( bindRequest.getControls().values().toArray( new Control[0] ) );
+
+ writeBindRequest( clonedReq );
+
+ bindResponse = bindFuture.get( timeout, TimeUnit.MILLISECONDS );
+ result = bindResponse.getLdapResult().getResultCode();
+ }
+
+ while ( !sc.isComplete()
+ && ( result == ResultCodeEnum.SASL_BIND_IN_PROGRESS || result == ResultCodeEnum.SUCCESS ) )
+ {
+ response = sc.evaluateChallenge( bindResponse.getServerSaslCreds() );
+
+ if ( result == ResultCodeEnum.SUCCESS )
+ {
+ if ( response != null )
+ {
+ throw new LdapException( "protocol error" );
+ }
+ }
+ else
+ {
+ bindRequest.setCredentials( response );
+
+ addToFutureMap( newId, bindFuture );
+
+ writeBindRequest( bindRequest );
+
+ bindResponse = bindFuture.get( timeout, TimeUnit.MILLISECONDS );
+ result = bindResponse.getLdapResult().getResultCode();
+ }
+
+ }
+
+ bindFuture.set( bindResponse );
+ return bindFuture;
+ }
+ catch ( LdapException e )
+ {
+ throw e;
+ }
+ catch ( Exception e )
+ {
+ e.printStackTrace();
+ throw new LdapException( e );
+ }
+ }
+
+
+ /**
+ * a reusable code block to be used in various bind methods
+ */
+ private void writeBindRequest( BindRequest bindRequest ) throws LdapException
+ {
+ // Send the request to the server
+ WriteFuture writeFuture = ldapSession.write( bindRequest );
+
+ // Wait for the message to be sent to the server
+ if ( !writeFuture.awaitUninterruptibly( getTimeout( 0 ) ) )
+ {
+ // We didn't received anything : this is an error
+ LOG.error( "Bind failed : timeout occured" );
+
+ throw new LdapException( TIME_OUT_ERROR );
+ }
+ }
}