1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.directory.server.ldap;
21
22
23 import java.io.IOException;
24 import java.net.InetSocketAddress;
25 import java.security.KeyStore;
26 import java.security.Provider;
27 import java.security.Security;
28 import java.util.*;
29
30
31 import org.apache.directory.server.core.DirectoryService;
32 import org.apache.directory.server.core.partition.PartitionNexus;
33 import org.apache.directory.server.core.security.CoreKeyStoreSpi;
34 import org.apache.directory.server.ldap.handlers.LdapRequestHandler;
35 import org.apache.directory.server.ldap.handlers.AbandonHandler;
36 import org.apache.directory.server.ldap.handlers.AddHandler;
37 import org.apache.directory.server.ldap.handlers.BindHandler;
38 import org.apache.directory.server.ldap.handlers.CompareHandler;
39 import org.apache.directory.server.ldap.handlers.DeleteHandler;
40 import org.apache.directory.server.ldap.handlers.ExtendedHandler;
41 import org.apache.directory.server.ldap.handlers.ModifyDnHandler;
42 import org.apache.directory.server.ldap.handlers.ModifyHandler;
43 import org.apache.directory.server.ldap.handlers.SearchHandler;
44 import org.apache.directory.server.ldap.handlers.UnbindHandler;
45 import org.apache.directory.server.ldap.handlers.bind.*;
46 import org.apache.directory.server.ldap.handlers.ssl.LdapsInitializer;
47 import org.apache.directory.server.protocol.shared.DirectoryBackedService;
48 import org.apache.directory.shared.ldap.constants.SaslQoP;
49 import org.apache.directory.shared.ldap.exception.LdapConfigurationException;
50 import org.apache.directory.shared.ldap.message.AbandonRequest;
51 import org.apache.directory.shared.ldap.message.AddRequest;
52 import org.apache.directory.shared.ldap.message.BindRequest;
53 import org.apache.directory.shared.ldap.message.CascadeControl;
54 import org.apache.directory.shared.ldap.message.CompareRequest;
55 import org.apache.directory.shared.ldap.message.DeleteRequest;
56 import org.apache.directory.shared.ldap.message.EntryChangeControl;
57 import org.apache.directory.shared.ldap.message.ExtendedRequest;
58 import org.apache.directory.shared.ldap.message.ManageDsaITControl;
59 import org.apache.directory.shared.ldap.message.ModifyDnRequest;
60 import org.apache.directory.shared.ldap.message.ModifyRequest;
61 import org.apache.directory.shared.ldap.message.PersistentSearchControl;
62 import org.apache.directory.shared.ldap.message.SearchRequest;
63 import org.apache.directory.shared.ldap.message.SubentriesControl;
64 import org.apache.directory.shared.ldap.message.UnbindRequest;
65 import org.apache.directory.shared.ldap.message.extended.NoticeOfDisconnect;
66 import org.apache.mina.common.DefaultIoFilterChainBuilder;
67 import org.apache.mina.common.IoFilterChainBuilder;
68 import org.apache.mina.common.IoHandler;
69 import org.apache.mina.common.IoSession;
70 import org.apache.mina.common.ThreadModel;
71 import org.apache.mina.common.WriteFuture;
72 import org.apache.mina.filter.codec.ProtocolCodecFactory;
73 import org.apache.mina.handler.demux.MessageHandler;
74 import org.apache.mina.transport.socket.nio.SocketAcceptorConfig;
75 import org.slf4j.Logger;
76 import org.slf4j.LoggerFactory;
77
78
79
80
81
82
83
84
85
86
87 public class LdapService extends DirectoryBackedService
88 {
89
90 public static final int NO_SIZE_LIMIT = 0;
91
92
93 public static final int NO_TIME_LIMIT = 0;
94
95
96 public static final String SERVICE_NAME = "ldap";
97
98
99
100 private static final long serialVersionUID = 3757127143811666817L;
101
102
103 private static final Logger LOG = LoggerFactory.getLogger( LdapService.class.getName() );
104
105
106 private static final int MAX_SIZE_LIMIT_DEFAULT = 100;
107
108
109 private static final int MAX_TIME_LIMIT_DEFAULT = 10000;
110
111
112 private static final String SERVICE_PID_DEFAULT = "org.apache.directory.server.ldap";
113
114
115 private static final String SERVICE_NAME_DEFAULT = "ApacheDS LDAP Service";
116
117
118 private static final int IP_PORT_DEFAULT = 389;
119
120
121 private LdapSessionManager ldapSessionManager = new LdapSessionManager();
122
123
124 private Set<String> supportedControls;
125
126
127
128
129
130 private int maxSizeLimit = MAX_SIZE_LIMIT_DEFAULT;
131
132
133
134
135
136 private int maxTimeLimit = MAX_TIME_LIMIT_DEFAULT;
137
138
139 private boolean enableLdaps;
140
141
142 private boolean allowAnonymousAccess = true;
143
144
145 private final Collection<ExtendedOperationHandler> extendedOperationHandlers =
146 new ArrayList<ExtendedOperationHandler>();
147
148
149 private Map<String, MechanismHandler> saslMechanismHandlers =
150 new HashMap<String, MechanismHandler>();
151
152
153 private String saslHost = "ldap.example.com";
154
155
156 private String saslPrincipal = "ldap/ldap.example.com@EXAMPLE.COM";
157
158
159 private Set<String> saslQop;
160 private String saslQopString;
161
162
163 private List<String> saslRealms;
164
165 private LdapRequestHandler<AbandonRequest> abandonHandler;
166 private LdapRequestHandler<AddRequest> addHandler;
167 private LdapRequestHandler<BindRequest> bindHandler;
168 private LdapRequestHandler<CompareRequest> compareHandler;
169 private LdapRequestHandler<DeleteRequest> deleteHandler;
170 private LdapRequestHandler<ExtendedRequest> extendedHandler;
171 private LdapRequestHandler<ModifyRequest> modifyHandler;
172 private LdapRequestHandler<ModifyDnRequest> modifyDnHandler;
173 private LdapRequestHandler<SearchRequest> searchHandler;
174 private LdapRequestHandler<UnbindRequest> unbindHandler;
175
176
177
178 private ProtocolCodecFactory codecFactory;
179
180
181 private final LdapProtocolHandler handler = new LdapProtocolHandler(this);
182
183
184 private boolean started;
185
186
187
188
189
190 private boolean confidentialityRequired;
191
192
193
194
195
196 public LdapService()
197 {
198 super.setIpPort( IP_PORT_DEFAULT );
199 super.setEnabled( true );
200 super.setServiceId( SERVICE_PID_DEFAULT );
201 super.setServiceName( SERVICE_NAME_DEFAULT );
202
203 saslQop = new HashSet<String>();
204 saslQop.add( SaslQoP.QOP_AUTH );
205 saslQop.add( SaslQoP.QOP_AUTH_INT );
206 saslQop.add( SaslQoP.QOP_AUTH_CONF );
207 saslQopString = SaslQoP.QOP_AUTH + ',' + SaslQoP.QOP_AUTH_INT + ',' + SaslQoP.QOP_AUTH_CONF;
208
209 saslRealms = new ArrayList<String>();
210 saslRealms.add( "example.com" );
211
212 this.supportedControls = new HashSet<String>();
213 this.supportedControls.add( PersistentSearchControl.CONTROL_OID );
214 this.supportedControls.add( EntryChangeControl.CONTROL_OID );
215 this.supportedControls.add( SubentriesControl.CONTROL_OID );
216 this.supportedControls.add( ManageDsaITControl.CONTROL_OID );
217 this.supportedControls.add( CascadeControl.CONTROL_OID );
218 }
219
220
221
222
223
224 private void installDefaultHandlers()
225 {
226 if ( getAbandonHandler() == null )
227 {
228 setAbandonHandler( new AbandonHandler() );
229 }
230
231 if ( getAddHandler() == null )
232 {
233 setAddHandler( new AddHandler() );
234 }
235
236 if ( getBindHandler() == null )
237 {
238 BindHandler handler = new BindHandler();
239 handler.setSaslMechanismHandlers( saslMechanismHandlers );
240 setBindHandler( handler );
241 }
242
243 if ( getCompareHandler() == null )
244 {
245 setCompareHandler( new CompareHandler() );
246 }
247
248 if ( getDeleteHandler() == null )
249 {
250 setDeleteHandler( new DeleteHandler() );
251 }
252
253 if ( getExtendedHandler() == null )
254 {
255 setExtendedHandler( new ExtendedHandler() );
256 }
257
258 if ( getModifyHandler() == null )
259 {
260 setModifyHandler( new ModifyHandler() );
261 }
262
263 if ( getModifyDnHandler() == null )
264 {
265 setModifyDnHandler( new ModifyDnHandler() );
266 }
267
268 if ( getSearchHandler() == null )
269 {
270 setSearchHandler( new SearchHandler() );
271 }
272
273 if ( getUnbindHandler() == null )
274 {
275 setUnbindHandler( new UnbindHandler() );
276 }
277 }
278
279
280
281
282
283
284 public void start() throws Exception
285 {
286 if ( ! isEnabled() )
287 {
288 return;
289 }
290
291 IoFilterChainBuilder chain;
292
293 if ( isEnableLdaps() )
294 {
295 Provider provider = Security.getProvider( "SUN" );
296 LOG.debug( "provider = {}", provider );
297 CoreKeyStoreSpi coreKeyStoreSpi = new CoreKeyStoreSpi( getDirectoryService() );
298 KeyStore keyStore = new KeyStore( coreKeyStoreSpi, provider, "JKS" ) {};
299 try
300 {
301 keyStore.load( null, null );
302 }
303 catch ( Exception e )
304 {
305
306 }
307 chain = LdapsInitializer.init( keyStore );
308 }
309 else
310 {
311 chain = new DefaultIoFilterChainBuilder();
312 }
313
314
315
316
317
318
319 installDefaultHandlers();
320
321 startLDAP0( getIpPort(), chain );
322
323 started = true;
324 }
325
326
327 public void stop()
328 {
329 try
330 {
331
332
333 List<WriteFuture> writeFutures = new ArrayList<WriteFuture>();
334
335
336
337
338
339 List<IoSession> sessions;
340
341 try
342 {
343 sessions = new ArrayList<IoSession>(
344 getSocketAcceptor().getManagedSessions( new InetSocketAddress( getIpPort() ) ) );
345 }
346 catch ( IllegalArgumentException e )
347 {
348 LOG.warn( "Seems like the LDAP service (" + getIpPort() + ") has already been unbound." );
349 return;
350 }
351
352 getSocketAcceptor().unbind( new InetSocketAddress( getIpPort() ) );
353
354 if ( LOG.isInfoEnabled() )
355 {
356 LOG.info( "Unbind of an LDAP service (" + getIpPort() + ") is complete." );
357 LOG.info( "Sending notice of disconnect to existing clients sessions." );
358 }
359
360
361 if ( sessions != null )
362 {
363 for ( IoSession session:sessions )
364 {
365 writeFutures.add( session.write( NoticeOfDisconnect.UNAVAILABLE ) );
366 }
367 }
368
369
370 Iterator<IoSession> sessionIt = sessions.iterator();
371
372 for ( WriteFuture future:writeFutures )
373 {
374 future.join( 1000 );
375 sessionIt.next().close();
376 }
377 }
378 catch ( Exception e )
379 {
380 LOG.warn( "Failed to sent NoD.", e );
381 }
382 }
383
384
385 private void startLDAP0( int port, IoFilterChainBuilder chainBuilder )
386 throws Exception
387 {
388 PartitionNexus nexus = getDirectoryService().getPartitionNexus();
389
390 for ( ExtendedOperationHandler h : extendedOperationHandlers )
391 {
392 LOG.info( "Added Extended Request Handler: " + h.getOid() );
393 h.setLdapServer( this );
394 nexus.registerSupportedExtensions( h.getExtensionOids() );
395 }
396
397 nexus.registerSupportedSaslMechanisms( saslMechanismHandlers.keySet() );
398
399 try
400 {
401 SocketAcceptorConfig acceptorCfg = new SocketAcceptorConfig();
402
403
404 acceptorCfg.setDisconnectOnUnbind( false );
405 acceptorCfg.setReuseAddress( true );
406 acceptorCfg.setFilterChainBuilder( chainBuilder );
407 acceptorCfg.setThreadModel( ThreadModel.MANUAL );
408
409 acceptorCfg.getSessionConfig().setTcpNoDelay( true );
410
411 getSocketAcceptor().bind( new InetSocketAddress( port ), getHandler(), acceptorCfg );
412 started = true;
413
414 if ( LOG.isInfoEnabled() )
415 {
416 LOG.info( "Successful bind of an LDAP Service (" + port + ") is complete." );
417 }
418 }
419 catch ( IOException e )
420 {
421 String msg = "Failed to bind an LDAP service (" + port + ") to the service registry.";
422 LdapConfigurationException lce = new LdapConfigurationException( msg );
423 lce.setRootCause( e );
424 LOG.error( msg, e );
425 throw lce;
426 }
427 }
428
429
430 public String getName()
431 {
432 return SERVICE_NAME;
433 }
434
435
436 public IoHandler getHandler()
437 {
438 return handler;
439 }
440
441
442 public LdapSessionManager getLdapSessionManager()
443 {
444 return ldapSessionManager;
445 }
446
447
448 public ProtocolCodecFactory getProtocolCodecFactory()
449 {
450 return codecFactory;
451 }
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466 public void addExtendedOperationHandler( ExtendedOperationHandler eoh ) throws Exception
467 {
468 if ( started )
469 {
470 eoh.setLdapServer( this );
471 PartitionNexus nexus = getDirectoryService().getPartitionNexus();
472 nexus.registerSupportedExtensions( eoh.getExtensionOids() );
473 }
474 else
475 {
476 extendedOperationHandlers.add( eoh );
477 }
478 }
479
480
481
482
483
484
485
486
487
488 public void removeExtendedOperationHandler( String oid )
489 {
490
491
492
493
494 ExtendedOperationHandler handler = null;
495 for ( ExtendedOperationHandler h : extendedOperationHandlers )
496 {
497 if ( h.getOid().equals( oid ) )
498 {
499 handler = h;
500 break;
501 }
502 }
503 extendedOperationHandlers.remove( handler );
504 }
505
506
507
508
509
510
511
512
513
514
515 public ExtendedOperationHandler getExtendedOperationHandler( String oid )
516 {
517 for ( ExtendedOperationHandler h : extendedOperationHandlers )
518 {
519 if ( h.getOid().equals( oid ) )
520 {
521 return h;
522 }
523 }
524
525 return null;
526 }
527
528
529
530
531
532
533
534
535
536 public void setConfidentialityRequired( boolean confidentialityRequired )
537 {
538 this.confidentialityRequired = confidentialityRequired;
539 }
540
541
542
543
544
545
546
547
548 public boolean isConfidentialityRequired()
549 {
550 return confidentialityRequired;
551 }
552
553
554
555
556
557
558
559 public boolean isEnableLdaps()
560 {
561 return enableLdaps;
562 }
563
564
565
566
567
568
569
570 public void setEnableLdaps( boolean enableLdaps )
571 {
572 this.enableLdaps = enableLdaps;
573 }
574
575
576
577
578
579
580
581 public boolean isAllowAnonymousAccess()
582 {
583 return allowAnonymousAccess;
584 }
585
586
587
588
589
590
591
592 public void setAllowAnonymousAccess( boolean enableAnonymousAccess )
593 {
594 this.allowAnonymousAccess = enableAnonymousAccess;
595 }
596
597
598
599
600
601
602
603 public void setMaxSizeLimit( int maxSizeLimit )
604 {
605 this.maxSizeLimit = maxSizeLimit;
606 }
607
608
609
610
611
612
613
614 public int getMaxSizeLimit()
615 {
616 return maxSizeLimit;
617 }
618
619
620
621
622
623
624
625 public void setMaxTimeLimit( int maxTimeLimit )
626 {
627 this.maxTimeLimit = maxTimeLimit;
628 }
629
630
631
632
633
634
635
636 public int getMaxTimeLimit()
637 {
638 return maxTimeLimit;
639 }
640
641
642
643
644
645
646
647 public Collection<ExtendedOperationHandler> getExtendedOperationHandlers()
648 {
649 return new ArrayList<ExtendedOperationHandler>( extendedOperationHandlers );
650 }
651
652
653
654
655
656
657
658
659
660 public void setExtendedOperationHandlers( Collection<ExtendedOperationHandler> handlers )
661 {
662 this.extendedOperationHandlers.clear();
663 this.extendedOperationHandlers.addAll( handlers );
664 }
665
666
667
668
669
670
671
672 public String getSaslHost()
673 {
674 return saslHost;
675 }
676
677
678
679
680
681
682
683 public void setSaslHost( String saslHost )
684 {
685 this.saslHost = saslHost;
686 }
687
688
689
690
691
692
693
694 public String getSaslPrincipal()
695 {
696 return saslPrincipal;
697 }
698
699
700
701
702
703
704
705 public void setSaslPrincipal( String saslPrincipal )
706 {
707 this.saslPrincipal = saslPrincipal;
708 }
709
710
711
712
713
714
715
716 public String getSaslQopString()
717 {
718 return saslQopString;
719 }
720
721
722
723
724
725
726
727 public Set<String> getSaslQop()
728 {
729 return saslQop;
730 }
731
732
733
734
735
736
737
738
739
740
741
742 public void setSaslQop( Set<String> saslQop )
743 {
744 StringBuilder qopList = new StringBuilder();
745 boolean isFirst = true;
746
747 for ( String qop:saslQop )
748 {
749 if ( isFirst )
750 {
751 isFirst = false;
752 }
753 else
754 {
755 qopList.append( ',' );
756 }
757
758 qopList.append( qop );
759 }
760
761 this.saslQopString = qopList.toString();
762 this.saslQop = saslQop;
763 }
764
765
766
767
768
769
770
771 public List<String> getSaslRealms()
772 {
773 return saslRealms;
774 }
775
776
777
778
779
780
781
782
783
784 public void setSaslRealms( List<String> saslRealms )
785 {
786 this.saslRealms = saslRealms;
787 }
788
789
790
791
792
793 public Map<String, MechanismHandler> getSaslMechanismHandlers()
794 {
795 return saslMechanismHandlers;
796 }
797
798 public void setSaslMechanismHandlers( Map<String, MechanismHandler> saslMechanismHandlers )
799 {
800 this.saslMechanismHandlers = saslMechanismHandlers;
801 }
802
803
804 public MechanismHandler addSaslMechanismHandler( String mechanism, MechanismHandler handler )
805 {
806 return this.saslMechanismHandlers.put( mechanism, handler );
807 }
808
809
810 public MechanismHandler removeSaslMechanismHandler( String mechanism )
811 {
812 return this.saslMechanismHandlers.remove( mechanism );
813 }
814
815
816 public MechanismHandler getMechanismHandler( String mechanism )
817 {
818 return this.saslMechanismHandlers.get( mechanism );
819 }
820
821
822 public Set<String> getSupportedMechanisms()
823 {
824 return saslMechanismHandlers.keySet();
825 }
826
827
828 public void setDirectoryService( DirectoryService directoryService )
829 {
830 super.setDirectoryService( directoryService );
831 this.codecFactory = new LdapProtocolCodecFactory( directoryService );
832 }
833
834
835 public Set<String> getSupportedControls()
836 {
837 return supportedControls;
838 }
839
840
841 public void setSupportedControls( Set<String> supportedControls )
842 {
843 this.supportedControls = supportedControls;
844 }
845
846
847 public MessageHandler<AbandonRequest> getAbandonHandler()
848 {
849 return abandonHandler;
850 }
851
852
853 public void setAbandonHandler( LdapRequestHandler<AbandonRequest> abandonHandler )
854 {
855 this.handler.removeMessageHandler( AbandonRequest.class );
856 this.abandonHandler = abandonHandler;
857 this.abandonHandler.setLdapServer( this );
858 this.handler.addMessageHandler( AbandonRequest.class, this.abandonHandler );
859 }
860
861
862 public LdapRequestHandler<AddRequest> getAddHandler()
863 {
864 return addHandler;
865 }
866
867
868 public void setAddHandler( LdapRequestHandler<AddRequest> addHandler )
869 {
870 this.handler.removeMessageHandler( AddRequest.class );
871 this.addHandler = addHandler;
872 this.addHandler.setLdapServer( this );
873 this.handler.addMessageHandler( AddRequest.class, this.addHandler );
874 }
875
876
877 public LdapRequestHandler<BindRequest> getBindHandler()
878 {
879 return bindHandler;
880 }
881
882
883 public void setBindHandler( LdapRequestHandler<BindRequest> bindHandler )
884 {
885 this.handler.removeMessageHandler( BindRequest.class );
886 this.bindHandler = bindHandler;
887 this.bindHandler.setLdapServer( this );
888 this.handler.addMessageHandler( BindRequest.class, this.bindHandler );
889 }
890
891
892 public LdapRequestHandler<CompareRequest> getCompareHandler()
893 {
894 return compareHandler;
895 }
896
897
898 public void setCompareHandler( LdapRequestHandler<CompareRequest> compareHandler )
899 {
900 this.handler.removeMessageHandler( CompareRequest.class );
901 this.compareHandler = compareHandler;
902 this.compareHandler.setLdapServer( this );
903 this.handler.addMessageHandler( CompareRequest.class, this.compareHandler );
904 }
905
906
907 public LdapRequestHandler<DeleteRequest> getDeleteHandler()
908 {
909 return deleteHandler;
910 }
911
912
913 public void setDeleteHandler( LdapRequestHandler<DeleteRequest> deleteHandler )
914 {
915 this.handler.removeMessageHandler( DeleteRequest.class );
916 this.deleteHandler = deleteHandler;
917 this.deleteHandler.setLdapServer( this );
918 this.handler.addMessageHandler( DeleteRequest.class, this.deleteHandler );
919 }
920
921
922 public LdapRequestHandler<ExtendedRequest> getExtendedHandler()
923 {
924 return extendedHandler;
925 }
926
927
928 public void setExtendedHandler( LdapRequestHandler<ExtendedRequest> extendedHandler )
929 {
930 this.handler.removeMessageHandler( ExtendedRequest.class );
931 this.extendedHandler = extendedHandler;
932 this.extendedHandler.setLdapServer( this );
933 this.handler.addMessageHandler( ExtendedRequest.class, this.extendedHandler );
934 }
935
936
937 public LdapRequestHandler<ModifyRequest> getModifyHandler()
938 {
939 return modifyHandler;
940 }
941
942
943 public void setModifyHandler( LdapRequestHandler<ModifyRequest> modifyHandler )
944 {
945 this.handler.removeMessageHandler( ModifyRequest.class );
946 this.modifyHandler = modifyHandler;
947 this.modifyHandler.setLdapServer( this );
948 this.handler.addMessageHandler( ModifyRequest.class, this.modifyHandler );
949 }
950
951
952 public LdapRequestHandler<ModifyDnRequest> getModifyDnHandler()
953 {
954 return modifyDnHandler;
955 }
956
957
958 public void setModifyDnHandler( LdapRequestHandler<ModifyDnRequest> modifyDnHandler )
959 {
960 this.handler.removeMessageHandler( ModifyDnRequest.class );
961 this.modifyDnHandler = modifyDnHandler;
962 this.modifyDnHandler.setLdapServer( this );
963 this.handler.addMessageHandler( ModifyDnRequest.class, this.modifyDnHandler );
964 }
965
966
967 public LdapRequestHandler<SearchRequest> getSearchHandler()
968 {
969 return searchHandler;
970 }
971
972
973 public void setSearchHandler( LdapRequestHandler<SearchRequest> searchHandler )
974 {
975 this.handler.removeMessageHandler( SearchRequest.class );
976 this.searchHandler = searchHandler;
977 this.searchHandler.setLdapServer( this );
978 this.handler.addMessageHandler( SearchRequest.class, this.searchHandler );
979 }
980
981
982 public LdapRequestHandler<UnbindRequest> getUnbindHandler()
983 {
984 return unbindHandler;
985 }
986
987
988 public void setUnbindHandler( LdapRequestHandler<UnbindRequest> unbindHandler )
989 {
990 this.handler.removeMessageHandler( UnbindRequest.class );
991 this.unbindHandler = unbindHandler;
992 this.unbindHandler.setLdapServer( this );
993 this.handler.addMessageHandler( UnbindRequest.class, this.unbindHandler );
994 }
995
996
997 public boolean isStarted()
998 {
999 return started;
1000 }
1001
1002
1003 public void setStarted( boolean started )
1004 {
1005 this.started = started;
1006 }
1007 }