1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *  
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *  
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License. 
18   *  
19   */
20  package org.apache.directory.server.operations.bind;
21  
22  
23  import java.util.HashMap;
24  import java.util.HashSet;
25  import java.util.Hashtable;
26  import java.util.Map;
27  import java.util.Set;
28  
29  import javax.naming.AuthenticationNotSupportedException;
30  import javax.naming.Context;
31  import javax.naming.NamingEnumeration;
32  import javax.naming.NamingException;
33  import javax.naming.NoPermissionException;
34  import javax.naming.directory.Attribute;
35  import javax.naming.directory.Attributes;
36  import javax.naming.directory.DirContext;
37  import javax.naming.directory.InitialDirContext;
38  
39  import org.apache.commons.net.SocketClient;
40  import org.apache.directory.server.core.DefaultDirectoryService;
41  import org.apache.directory.server.core.DirectoryService;
42  import org.apache.directory.server.core.entry.ServerEntry;
43  import org.apache.directory.server.core.integ.IntegrationUtils;
44  import org.apache.directory.server.core.integ.Level;
45  import org.apache.directory.server.core.integ.annotations.ApplyLdifs;
46  import org.apache.directory.server.core.integ.annotations.CleanupLevel;
47  import org.apache.directory.server.core.integ.annotations.Factory;
48  import org.apache.directory.server.core.partition.Partition;
49  import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmIndex;
50  import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition;
51  import org.apache.directory.server.integ.LdapServerFactory;
52  import org.apache.directory.server.integ.SiRunner;
53  import org.apache.directory.server.ldap.LdapService;
54  import org.apache.directory.server.ldap.handlers.bind.MechanismHandler;
55  import org.apache.directory.server.ldap.handlers.bind.cramMD5.CramMd5MechanismHandler;
56  import org.apache.directory.server.ldap.handlers.bind.digestMD5.DigestMd5MechanismHandler;
57  import org.apache.directory.server.ldap.handlers.bind.gssapi.GssapiMechanismHandler;
58  import org.apache.directory.server.ldap.handlers.bind.ntlm.NtlmMechanismHandler;
59  import org.apache.directory.server.ldap.handlers.bind.ntlm.NtlmProvider;
60  import org.apache.directory.server.ldap.handlers.bind.plain.PlainMechanismHandler;
61  import org.apache.directory.server.ldap.handlers.extended.StoredProcedureExtendedOperationHandler;
62  import org.apache.directory.server.protocol.shared.SocketAcceptor;
63  import org.apache.directory.server.xdbm.Index;
64  import org.apache.directory.shared.ldap.constants.SupportedSaslMechanisms;
65  import org.apache.directory.shared.ldap.message.BindRequestImpl;
66  import org.apache.directory.shared.ldap.message.BindResponse;
67  import org.apache.directory.shared.ldap.message.MessageDecoder;
68  import org.apache.directory.shared.ldap.message.MessageEncoder;
69  import org.apache.directory.shared.ldap.message.ResultCodeEnum;
70  import org.apache.directory.shared.ldap.message.spi.BinaryAttributeDetector;
71  import org.apache.directory.shared.ldap.name.LdapDN;
72  import org.apache.directory.shared.ldap.util.ArrayUtils;
73  import org.apache.mina.common.IoSession;
74  import org.apache.mina.util.AvailablePortFinder;
75  import org.junit.Before;
76  import org.junit.Test;
77  import org.junit.runner.RunWith;
78  import org.slf4j.Logger;
79  import org.slf4j.LoggerFactory;
80  
81  import static org.junit.Assert.fail;
82  import static org.junit.Assert.assertTrue;
83  import static org.junit.Assert.assertEquals;
84  
85  
86  /**
87   * An {@link AbstractServerTest} testing SASL authentication.
88   * 
89   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
90   * @version $Rev$, $Date$
91   */
92  @RunWith ( SiRunner.class ) 
93  @CleanupLevel ( Level.CLASS )
94  @Factory ( SaslBindIT.Factory.class )
95  @ApplyLdifs( {
96      // Entry #0
97      "dn: dc=example,dc=com\n" +
98      "dc: example\n" +
99      "objectClass: top\n" +
100     "objectClass: domain\n\n" +
101     
102     // Entry # 1
103     "dn: ou=users,dc=example,dc=com\n" +
104     "objectClass: organizationalUnit\n" +
105     "objectClass: top\n" +
106     "ou: users\n\n" + 
107     // Entry # 2
108     "dn: uid=hnelson,ou=users,dc=example,dc=com\n" +
109     "objectClass: inetOrgPerson\n" +
110     "objectClass: organizationalPerson\n" +
111     "objectClass: person\n" +
112     "objectClass: top\n" +
113     "uid: hnelson\n" +
114     "userPassword: secret\n" +
115     "cn: Horatio Nelson\n" +
116     "sn: Nelson\n\n" 
117     }
118 )
119 public class SaslBindIT
120 {
121     public static LdapService ldapService;
122     public BogusNtlmProvider provider = new BogusNtlmProvider();
123 
124      
125      public static class Factory implements LdapServerFactory
126      {
127          public LdapService newInstance() throws Exception
128          {
129              DirectoryService service = new DefaultDirectoryService();
130              IntegrationUtils.doDelete( service.getWorkingDirectory() );
131              service.getChangeLog().setEnabled( true );
132              service.setAllowAnonymousAccess( false );
133              service.setShutdownHookEnabled( false );
134 
135              Set<Partition> partitions = new HashSet<Partition>();
136              JdbmPartition partition = new JdbmPartition();
137              partition.setId( "example" );
138              partition.setSuffix( "dc=example,dc=com" );
139 
140              Set<Index<?,ServerEntry>> indexedAttrs = new HashSet<Index<?,ServerEntry>>();
141              indexedAttrs.add( new JdbmIndex<String,ServerEntry>( "ou" ) );
142              indexedAttrs.add( new JdbmIndex<String,ServerEntry>( "dc" ) );
143              indexedAttrs.add( new JdbmIndex<String,ServerEntry>( "objectClass" ) );
144              partition.setIndexedAttributes( indexedAttrs );
145 
146              partitions.add( partition );
147              service.setPartitions( partitions );
148 
149              // change the working directory to something that is unique
150              // on the system and somewhere either under target directory
151              // or somewhere in a temp area of the machine.
152 
153              LdapService ldapService = new LdapService();
154              ldapService.setDirectoryService( service );
155              ldapService.setSocketAcceptor( new SocketAcceptor( null ) );
156              ldapService.setIpPort( AvailablePortFinder.getNextAvailable( 1024 ) );
157              ldapService.setAllowAnonymousAccess( false );
158              ldapService.addExtendedOperationHandler( new StoredProcedureExtendedOperationHandler() );
159 
160              // Setup SASL Mechanisms
161              
162              Map<String, MechanismHandler> mechanismHandlerMap = new HashMap<String,MechanismHandler>();
163              mechanismHandlerMap.put( SupportedSaslMechanisms.PLAIN, new PlainMechanismHandler() );
164 
165              CramMd5MechanismHandler cramMd5MechanismHandler = new CramMd5MechanismHandler();
166              mechanismHandlerMap.put( SupportedSaslMechanisms.CRAM_MD5, cramMd5MechanismHandler );
167 
168              DigestMd5MechanismHandler digestMd5MechanismHandler = new DigestMd5MechanismHandler();
169              mechanismHandlerMap.put( SupportedSaslMechanisms.DIGEST_MD5, digestMd5MechanismHandler );
170 
171              GssapiMechanismHandler gssapiMechanismHandler = new GssapiMechanismHandler();
172              mechanismHandlerMap.put( SupportedSaslMechanisms.GSSAPI, gssapiMechanismHandler );
173 
174              NtlmMechanismHandler ntlmMechanismHandler = new NtlmMechanismHandler();
175              mechanismHandlerMap.put( SupportedSaslMechanisms.NTLM, ntlmMechanismHandler );
176              mechanismHandlerMap.put( SupportedSaslMechanisms.GSS_SPNEGO, ntlmMechanismHandler );
177 
178              ldapService.setSaslMechanismHandlers( mechanismHandlerMap );
179              ldapService.setSaslHost( "localhost" );
180              
181              return ldapService;
182          }
183      }
184      
185      
186      @Before
187      public void setupNewNtlmProvider()
188      {
189          provider = new BogusNtlmProvider();
190          NtlmMechanismHandler handler = ( NtlmMechanismHandler ) 
191              ldapService.getSaslMechanismHandlers().get( SupportedSaslMechanisms.NTLM );
192          handler.setNtlmProvider( provider );
193      }
194      
195 
196      /**
197       * Tests to make sure the server properly returns the supportedSASLMechanisms.
198       */
199      @Test
200      public void testSupportedSASLMechanisms()
201      {
202          try
203          {
204              // We have to tell the server that it should accept anonymous
205              // auth, because we are reading the rootDSE
206              ldapService.setAllowAnonymousAccess( true );
207              ldapService.getDirectoryService().setAllowAnonymousAccess( true );
208              
209              // Point on rootDSE
210              DirContext context = new InitialDirContext();
211 
212              Attributes attrs = context.getAttributes( "ldap://localhost:" 
213                  + ldapService.getIpPort(), new String[]
214                  { "supportedSASLMechanisms" } );
215 
216              NamingEnumeration<? extends Attribute> answer = attrs.getAll();
217              Attribute result = answer.next();
218              assertEquals( 6, result.size() );
219              assertTrue( result.contains( SupportedSaslMechanisms.GSSAPI ) );
220              assertTrue( result.contains( SupportedSaslMechanisms.DIGEST_MD5 ) );
221              assertTrue( result.contains( SupportedSaslMechanisms.CRAM_MD5 ) );
222              assertTrue( result.contains( SupportedSaslMechanisms.NTLM ) );
223              assertTrue( result.contains( SupportedSaslMechanisms.PLAIN ) );
224              assertTrue( result.contains( SupportedSaslMechanisms.GSS_SPNEGO ) );
225          }
226          catch ( NamingException e )
227          {
228              fail( "Should not have caught exception." );
229          }
230      }
231      
232      
233      /**
234       * Tests to make sure PLAIN-binds works
235       */
236      @Test
237      public void testSaslBindPLAIN()
238      {
239          try
240          {
241              Hashtable<String, String> env = new Hashtable<String, String>();
242              env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
243              env.put( Context.PROVIDER_URL, "ldap://localhost:" + ldapService.getIpPort() );
244 
245              env.put( Context.SECURITY_AUTHENTICATION, "PLAIN" );
246              env.put( Context.SECURITY_PRINCIPAL, "uid=hnelson,ou=users,dc=example,dc=com" );
247              env.put( Context.SECURITY_CREDENTIALS, "secret" );
248 
249              DirContext context = new InitialDirContext( env );
250 
251              String[] attrIDs =
252                  { "uid" };
253 
254              Attributes attrs = context.getAttributes( "uid=hnelson,ou=users,dc=example,dc=com", attrIDs );
255              String uid = null;
256 
257              if ( attrs.get( "uid" ) != null )
258              {
259                  uid = ( String ) attrs.get( "uid" ).get();
260              }
261 
262              assertEquals( uid, "hnelson" );
263          }
264          catch ( NamingException e )
265          {
266              fail( "Should not have caught exception." );
267          }
268      }
269 
270 
271      /**
272       * Test a SASL bind with an empty mechanism 
273       */
274      @Test
275      public void testSaslBindNoMech()
276      {
277          try
278          {
279              Hashtable<String, String> env = new Hashtable<String, String>();
280              env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
281              env.put( Context.PROVIDER_URL, "ldap://localhost:" + ldapService.getIpPort() );
282 
283              env.put( Context.SECURITY_AUTHENTICATION, "" );
284              env.put( Context.SECURITY_PRINCIPAL, "uid=hnelson,ou=users,dc=example,dc=com" );
285              env.put( Context.SECURITY_CREDENTIALS, "secret" );
286 
287              new InitialDirContext( env );
288              fail( "Should not be there" );
289          }
290          catch ( AuthenticationNotSupportedException anse )
291          {
292              assertTrue( true );
293          }
294          catch ( NamingException ne )
295          {
296              fail( "Should not have caught exception." );
297          }
298      }
299 
300 
301      /**
302       * Tests to make sure binds below the RootDSE require authentication.
303       */
304      @Test
305      public void testAnonymousBelowRootDSE()
306      {
307          ldapService.getDirectoryService().setAllowAnonymousAccess( false );
308          
309          try
310          {
311              Hashtable<String, String> env = new Hashtable<String, String>();
312              env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
313              env.put( Context.PROVIDER_URL, "ldap://localhost:" + ldapService.getIpPort() );
314 
315              DirContext context = new InitialDirContext( env );
316 
317              String[] attrIDs =
318                  { "vendorName" };
319 
320              context.getAttributes( "dc=example,dc=com", attrIDs );
321 
322              fail( "Should not have gotten here." );
323          }
324          catch ( NoPermissionException npe )
325          {
326              assertTrue( npe.getMessage().contains( "error code 50" ) );
327          }
328          catch ( NamingException ne )
329          {
330              fail( "Should not have gotten here" );
331          }
332      }
333 
334 
335      /**
336       * Tests to make sure CRAM-MD5 binds below the RootDSE work.
337       */
338      @Test
339      public void testSaslCramMd5Bind()
340      {
341          try
342          {
343              Hashtable<String, String> env = new Hashtable<String, String>();
344              env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
345              env.put( Context.PROVIDER_URL, "ldap://localhost:" + ldapService.getIpPort() );
346 
347              env.put( Context.SECURITY_AUTHENTICATION, "CRAM-MD5" );
348              env.put( Context.SECURITY_PRINCIPAL, "hnelson" );
349              env.put( Context.SECURITY_CREDENTIALS, "secret" );
350 
351              DirContext context = new InitialDirContext( env );
352 
353              String[] attrIDs =
354                  { "uid" };
355 
356              Attributes attrs = context.getAttributes( "uid=hnelson,ou=users,dc=example,dc=com", attrIDs );
357 
358              String uid = null;
359 
360              if ( attrs.get( "uid" ) != null )
361              {
362                  uid = ( String ) attrs.get( "uid" ).get();
363              }
364 
365              assertEquals( uid, "hnelson" );
366          }
367          catch ( NamingException e )
368          {
369              fail( "Should not have caught exception." );
370          }
371      }
372      
373      
374      /**
375       * Tests to make sure CRAM-MD5 binds below the RootDSE fail if the password is bad.
376       */
377      @Test
378      public void testSaslCramMd5BindBadPassword()
379      {
380          try
381          {
382              Hashtable<String, String> env = new Hashtable<String, String>();
383              env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
384              env.put( Context.PROVIDER_URL, "ldap://localhost:" + ldapService.getIpPort() );
385 
386              env.put( Context.SECURITY_AUTHENTICATION, "CRAM-MD5" );
387              env.put( Context.SECURITY_PRINCIPAL, "hnelson" );
388              env.put( Context.SECURITY_CREDENTIALS, "badsecret" );
389 
390              DirContext context = new InitialDirContext( env );
391 
392              String[] attrIDs =
393                  { "uid" };
394 
395              context.getAttributes( "uid=hnelson,ou=users,dc=example,dc=com", attrIDs );
396 
397              fail( "Should have thrown exception." );
398          }
399          catch ( NamingException e )
400          {
401              assertTrue( e.getMessage().contains( "Invalid response" ) );
402          }
403      }
404      
405      
406      /**
407       * Tests to make sure DIGEST-MD5 binds below the RootDSE work.
408       */
409      @Test
410      public void testSaslDigestMd5Bind() throws Exception
411      {
412          Hashtable<String, String> env = new Hashtable<String, String>();
413          env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
414          env.put( Context.PROVIDER_URL, "ldap://localhost:" + ldapService.getIpPort() );
415 
416          env.put( Context.SECURITY_AUTHENTICATION, "DIGEST-MD5" );
417          env.put( Context.SECURITY_PRINCIPAL, "hnelson" );
418          env.put( Context.SECURITY_CREDENTIALS, "secret" );
419 
420          // Specify realm
421          env.put( "java.naming.security.sasl.realm", "example.com" );
422 
423          // Request privacy protection
424          env.put( "javax.security.sasl.qop", "auth-conf" );
425 
426          DirContext context = new InitialDirContext( env );
427 
428          String[] attrIDs =
429              { "uid" };
430 
431          Attributes attrs = context.getAttributes( "uid=hnelson,ou=users,dc=example,dc=com", attrIDs );
432 
433          String uid = null;
434 
435          if ( attrs.get( "uid" ) != null )
436          {
437              uid = ( String ) attrs.get( "uid" ).get();
438          }
439 
440          assertEquals( uid, "hnelson" );
441      }
442 
443      
444      /**
445       * Tests to make sure DIGEST-MD5 binds below the RootDSE fail if the realm is bad.
446       */
447      @Test
448      public void testSaslDigestMd5BindBadRealm()
449      {
450          try
451          {
452              Hashtable<String, String> env = new Hashtable<String, String>();
453              env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
454              env.put( Context.PROVIDER_URL, "ldap://localhost:" + ldapService.getIpPort() );
455 
456              env.put( Context.SECURITY_AUTHENTICATION, "DIGEST-MD5" );
457              env.put( Context.SECURITY_PRINCIPAL, "hnelson" );
458              env.put( Context.SECURITY_CREDENTIALS, "secret" );
459 
460              // Bad realm
461              env.put( "java.naming.security.sasl.realm", "badrealm.com" );
462 
463              // Request privacy protection
464              env.put( "javax.security.sasl.qop", "auth-conf" );
465 
466              DirContext context = new InitialDirContext( env );
467 
468              String[] attrIDs =
469                  { "uid" };
470 
471              context.getAttributes( "uid=hnelson,ou=users,dc=example,dc=com", attrIDs );
472 
473              fail( "Should have thrown exception." );
474          }
475          catch ( NamingException e )
476          {
477              assertTrue( e.getMessage().contains( "Nonexistent realm" ) );
478          }
479      }
480 
481 
482      /**
483       * Tests to make sure DIGEST-MD5 binds below the RootDSE fail if the password is bad.
484       */
485      @Test
486      public void testSaslDigestMd5BindBadPassword()
487      {
488          try
489          {
490              Hashtable<String, String> env = new Hashtable<String, String>();
491              env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
492              env.put( Context.PROVIDER_URL, "ldap://localhost:" + ldapService.getIpPort() );
493 
494              env.put( Context.SECURITY_AUTHENTICATION, "DIGEST-MD5" );
495              env.put( Context.SECURITY_PRINCIPAL, "hnelson" );
496              env.put( Context.SECURITY_CREDENTIALS, "badsecret" );
497 
498              DirContext context = new InitialDirContext( env );
499              String[] attrIDs = { "uid" };
500 
501              context.getAttributes( "uid=hnelson,ou=users,dc=example,dc=com", attrIDs );
502              fail( "Should have thrown exception." );
503          }
504          catch ( NamingException e )
505          {
506              assertTrue( e.getMessage().contains( "digest response format violation" ) );
507          }
508      }
509 
510 
511      /**
512       * Tests that the plumbing for NTLM bind works.
513       */
514      @Test
515      public void testNtlmBind() throws Exception
516      {
517          NtlmSaslBindClient client = new NtlmSaslBindClient( SupportedSaslMechanisms.NTLM );
518          BindResponse type2response = client.bindType1( "type1_test".getBytes() );
519          assertEquals( 1, type2response.getMessageId() );
520          assertEquals( ResultCodeEnum.SASL_BIND_IN_PROGRESS, type2response.getLdapResult().getResultCode() );
521          assertTrue( ArrayUtils.isEquals( "type1_test".getBytes(), provider.getType1Response() ) );
522          assertTrue( ArrayUtils.isEquals( "challenge".getBytes(), type2response.getServerSaslCreds() ) );
523          
524          BindResponse finalResponse = client.bindType3( "type3_test".getBytes() );
525          assertEquals( 2, finalResponse.getMessageId() );
526          assertEquals( ResultCodeEnum.SUCCESS, finalResponse.getLdapResult().getResultCode() );
527          assertTrue( ArrayUtils.isEquals( "type3_test".getBytes(), provider.getType3Response() ) );
528      }
529 
530 
531      /**
532       * Tests that the plumbing for NTLM bind works.
533       */
534      @Test
535      public void testGssSpnegoBind() throws Exception
536      {
537          NtlmSaslBindClient client = new NtlmSaslBindClient( SupportedSaslMechanisms.GSS_SPNEGO );
538          BindResponse type2response = client.bindType1( "type1_test".getBytes() );
539          assertEquals( 1, type2response.getMessageId() );
540          assertEquals( ResultCodeEnum.SASL_BIND_IN_PROGRESS, type2response.getLdapResult().getResultCode() );
541          assertTrue( ArrayUtils.isEquals( "type1_test".getBytes(), provider.getType1Response() ) );
542          assertTrue( ArrayUtils.isEquals( "challenge".getBytes(), type2response.getServerSaslCreds() ) );
543          
544          BindResponse finalResponse = client.bindType3( "type3_test".getBytes() );
545          assertEquals( 2, finalResponse.getMessageId() );
546          assertEquals( ResultCodeEnum.SUCCESS, finalResponse.getLdapResult().getResultCode() );
547          assertTrue( ArrayUtils.isEquals( "type3_test".getBytes(), provider.getType3Response() ) );
548      }
549 
550      
551      /**
552       * A fake implementation of the NtlmProvider. We can't use a real one because
553       * its license is not ASL 2.0 compatible.
554       */
555      class BogusNtlmProvider implements NtlmProvider
556      {
557          private byte[] type1response;
558          private byte[] type3response;
559          
560          
561          public boolean authenticate( IoSession session, byte[] type3response ) throws Exception
562          {
563              this.type3response = type3response;
564              return true;
565          }
566 
567 
568          public byte[] generateChallenge( IoSession session, byte[] type1reponse ) throws Exception
569          {
570              this.type1response = type1reponse;
571              return "challenge".getBytes();
572          }
573          
574          
575          public byte[] getType1Response()
576          {
577              return type1response;
578          }
579          
580          
581          public byte[] getType3Response()
582          {
583              return type3response;
584          }
585      }
586 
587 
588      /**
589       * A NTLM client
590       */
591      class NtlmSaslBindClient extends SocketClient
592      {
593          private final Logger LOG = LoggerFactory.getLogger( NtlmSaslBindClient.class );
594          
595          private final String mechanism;
596          
597          
598          NtlmSaslBindClient( String mechanism ) throws Exception
599          {
600              this.mechanism = mechanism;
601              setDefaultPort( ldapService.getIpPort() );
602              connect( "localhost", ldapService.getIpPort() );
603              setTcpNoDelay( false );
604              
605              LOG.debug( "isConnected() = {}", _isConnected_ );
606              LOG.debug( "LocalPort     = {}", getLocalPort() );
607              LOG.debug( "LocalAddress  = {}", getLocalAddress() );
608              LOG.debug( "RemotePort    = {}", getRemotePort() );
609              LOG.debug( "RemoteAddress = {}", getRemoteAddress() );
610          }
611 
612          
613          BindResponse bindType1( byte[] type1response ) throws Exception
614          {
615              if ( ! isConnected() )
616              {
617                  throw new IllegalStateException( "Client is not connected." );
618              }
619              
620              // Setup the bind request
621              BindRequestImpl request = new BindRequestImpl( 1 ) ;
622              request.setName( new LdapDN( "uid=admin,ou=system" ) ) ;
623              request.setSimple( false ) ;
624              request.setCredentials( type1response ) ;
625              request.setSaslMechanism( mechanism );
626              request.setVersion3( true ) ;
627              
628              // Setup the ASN1 Enoder and Decoder
629              MessageEncoder encoder = new MessageEncoder();
630              MessageDecoder decoder = new MessageDecoder( new BinaryAttributeDetector() {
631                  public boolean isBinary( String attributeId )
632                  {
633                      return false;
634                  }
635              } );
636       
637              // Send encoded request to server
638              encoder.encodeBlocking( null, _output_, request );
639              _output_.flush();
640              
641              while ( _input_.available() <= 0 )
642              {
643                  Thread.sleep( 100 );
644              }
645              
646              // Retrieve the response back from server to my last request.
647              return ( BindResponse ) decoder.decode( null, _input_ );
648          }
649          
650          
651          BindResponse bindType3( byte[] type3response ) throws Exception
652          {
653              if ( ! isConnected() )
654              {
655                  throw new IllegalStateException( "Client is not connected." );
656              }
657              
658              // Setup the bind request
659              BindRequestImpl request = new BindRequestImpl( 2 ) ;
660              request.setName( new LdapDN( "uid=admin,ou=system" ) ) ;
661              request.setSimple( false ) ;
662              request.setCredentials( type3response ) ;
663              request.setSaslMechanism( mechanism );
664              request.setVersion3( true ) ;
665              
666              // Setup the ASN1 Enoder and Decoder
667              MessageEncoder encoder = new MessageEncoder();
668              MessageDecoder decoder = new MessageDecoder( new BinaryAttributeDetector() {
669                  public boolean isBinary( String attributeId )
670                  {
671                      return false;
672                  }
673              } );
674       
675              // Send encoded request to server
676              encoder.encodeBlocking( null, _output_, request );
677              
678              _output_.flush();
679              
680              while ( _input_.available() <= 0 )
681              {
682                  Thread.sleep( 100 );
683              }
684              
685              // Retrieve the response back from server to my last request.
686              return ( BindResponse ) decoder.decode( null, _input_ );
687          }
688      }
689 }