1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.ldap.server;
18
19 import java.util.Hashtable;
20 import java.util.Iterator;
21
22 import javax.naming.Context;
23 import javax.naming.Name;
24 import javax.naming.NamingException;
25 import javax.naming.directory.Attribute;
26 import javax.naming.directory.Attributes;
27
28 import org.apache.ldap.common.exception.LdapAuthenticationNotSupportedException;
29 import org.apache.ldap.common.exception.LdapConfigurationException;
30 import org.apache.ldap.common.exception.LdapNoPermissionException;
31 import org.apache.ldap.common.message.LockableAttributeImpl;
32 import org.apache.ldap.common.message.LockableAttributesImpl;
33 import org.apache.ldap.common.message.ResultCodeEnum;
34 import org.apache.ldap.common.name.DnParser;
35 import org.apache.ldap.common.name.LdapName;
36 import org.apache.ldap.common.name.NameComponentNormalizer;
37 import org.apache.ldap.common.util.DateUtils;
38 import org.apache.ldap.server.authz.AuthorizationService;
39 import org.apache.ldap.server.configuration.Configuration;
40 import org.apache.ldap.server.configuration.ConfigurationException;
41 import org.apache.ldap.server.configuration.StartupConfiguration;
42 import org.apache.ldap.server.interceptor.InterceptorChain;
43 import org.apache.ldap.server.jndi.DeadContext;
44 import org.apache.ldap.server.jndi.ServerLdapContext;
45 import org.apache.ldap.server.partition.DefaultDirectoryPartitionNexus;
46 import org.apache.ldap.server.partition.DirectoryPartitionNexus;
47 import org.apache.ldap.server.schema.AttributeTypeRegistry;
48 import org.apache.ldap.server.schema.ConcreteNameComponentNormalizer;
49 import org.apache.ldap.server.schema.GlobalRegistries;
50 import org.apache.ldap.server.schema.bootstrap.BootstrapRegistries;
51 import org.apache.ldap.server.schema.bootstrap.BootstrapSchemaLoader;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54
55
56 /***
57 * Default implementation of {@link DirectoryService}.
58 *
59 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
60 * @version $Rev: 328323 $
61 */
62 class DefaultDirectoryService extends DirectoryService
63 {
64 private static final Logger log = LoggerFactory.getLogger( DefaultDirectoryService.class );
65
66 private final String instanceId;
67
68 private final DirectoryServiceConfiguration configuration = new DefaultDirectoryServiceConfiguration( this );
69
70 private DirectoryServiceListener serviceListener;
71
72 /*** the initial context environment that fired up the backend subsystem */
73 private Hashtable environment;
74
75 /*** the configuration */
76 private StartupConfiguration startupConfiguration;
77
78 /*** the registries for system schema objects */
79 private GlobalRegistries globalRegistries;
80
81 /*** the root nexus */
82 private DefaultDirectoryPartitionNexus partitionNexus;
83
84 /*** whether or not server is started for the first time */
85 private boolean firstStart;
86
87 /*** The interceptor (or interceptor chain) for this service */
88 private InterceptorChain interceptorChain;
89
90 /*** whether or not this instance has been shutdown */
91 private boolean started = false;
92
93
94
95
96
97
98 /***
99 * Creates a new instance.
100 */
101 public DefaultDirectoryService( String instanceId )
102 {
103 if( instanceId == null )
104 {
105 throw new NullPointerException( "instanceId" );
106 }
107
108 this.instanceId = instanceId;
109
110
111 Runtime.getRuntime().addShutdownHook( new Thread( new Runnable() {
112 public void run()
113 {
114 try
115 {
116 shutdown();
117 }
118 catch( NamingException e )
119 {
120 log.warn(
121 "Failed to shut down the directory service: " +
122 DefaultDirectoryService.this.instanceId, e );
123 }
124 }
125 }, "ApacheDS Shutdown Hook (" + instanceId + ')' ) );
126 }
127
128
129
130
131
132 public Context getJndiContext( String rootDN ) throws NamingException
133 {
134 return this.getJndiContext( null, null, "none", rootDN );
135 }
136
137 public synchronized Context getJndiContext( String principal, byte[] credential, String authentication, String rootDN ) throws NamingException
138 {
139 checkSecuritySettings( principal, credential, authentication );
140
141 if ( !started )
142 {
143 return new DeadContext();
144 }
145
146 Hashtable environment = getEnvironment();
147 environment.remove( Context.SECURITY_PRINCIPAL );
148 environment.remove( Context.SECURITY_CREDENTIALS );
149 environment.remove( Context.SECURITY_AUTHENTICATION );
150
151 if( principal != null )
152 {
153 environment.put( Context.SECURITY_PRINCIPAL, principal );
154 }
155
156 if( credential != null )
157 {
158 environment.put( Context.SECURITY_CREDENTIALS, credential );
159 }
160
161 if( authentication != null )
162 {
163 environment.put( Context.SECURITY_AUTHENTICATION, authentication );
164 }
165
166 if( rootDN == null )
167 {
168 rootDN = "";
169 }
170 environment.put( Context.PROVIDER_URL, rootDN );
171
172 return new ServerLdapContext( this, environment );
173 }
174
175 public synchronized void startup( DirectoryServiceListener listener, Hashtable env ) throws NamingException
176 {
177 Hashtable envCopy = ( Hashtable ) env.clone();
178
179 if( started )
180 {
181 return;
182 }
183
184 StartupConfiguration cfg = ( StartupConfiguration ) Configuration.toConfiguration( env );
185 envCopy.put( Context.PROVIDER_URL, "" );
186
187 try
188 {
189 cfg.validate();
190 }
191 catch( ConfigurationException e )
192 {
193 NamingException ne = new LdapConfigurationException( "Invalid configuration." );
194 ne.initCause( e );
195 throw ne;
196 }
197
198 this.environment = envCopy;
199 this.startupConfiguration = cfg;
200
201 listener.beforeStartup( this );
202
203 initialize();
204 firstStart = createBootstrapEntries();
205 showSecurityWarnings();
206 createTestEntries();
207 this.serviceListener = listener;
208 started = true;
209 listener.afterStartup( this );
210 }
211
212 public synchronized void sync() throws NamingException
213 {
214 if ( !started )
215 {
216 return;
217 }
218
219 serviceListener.beforeSync( this );
220 try
221 {
222 this.partitionNexus.sync();
223 }
224 finally
225 {
226 serviceListener.afterSync( this );
227 }
228 }
229
230
231 public synchronized void shutdown() throws NamingException
232 {
233 if ( !started )
234 {
235 return;
236 }
237
238 serviceListener.beforeShutdown( this );
239 try
240 {
241 this.partitionNexus.sync();
242 this.partitionNexus.destroy();
243 this.interceptorChain.destroy();
244 this.started = false;
245 }
246 finally
247 {
248 environment = null;
249 interceptorChain = null;
250 startupConfiguration = null;
251 serviceListener.afterShutdown( this );
252 }
253 }
254
255 public String getInstanceId()
256 {
257 return instanceId;
258 }
259
260 public DirectoryServiceConfiguration getConfiguration()
261 {
262 return configuration;
263 }
264
265
266 public Hashtable getEnvironment()
267 {
268 return ( Hashtable ) environment.clone();
269 }
270
271 public DirectoryServiceListener getServiceListener()
272 {
273 return serviceListener;
274 }
275
276 public StartupConfiguration getStartupConfiguration()
277 {
278 return startupConfiguration;
279 }
280
281 public GlobalRegistries getGlobalRegistries()
282 {
283 return globalRegistries;
284 }
285
286 public DirectoryPartitionNexus getPartitionNexus()
287 {
288 return partitionNexus;
289 }
290
291 public InterceptorChain getInterceptorChain()
292 {
293 return interceptorChain;
294 }
295
296 public boolean isFirstStart()
297 {
298 return firstStart;
299 }
300
301 public boolean isStarted()
302 {
303 return started;
304 }
305
306 /***
307 * Checks to make sure security environment parameters are set correctly.
308 *
309 * @throws javax.naming.NamingException if the security settings are not correctly configured.
310 */
311 private void checkSecuritySettings( String principal, byte[] credential, String authentication ) throws NamingException
312 {
313 if( authentication == null )
314 {
315 authentication = "";
316 }
317
318
319
320
321
322 if ( "simple".equalsIgnoreCase( authentication ) )
323 {
324 if ( credential == null )
325 {
326 throw new LdapConfigurationException( "missing required "
327 + Context.SECURITY_CREDENTIALS + " property for simple authentication" );
328 }
329
330 if ( principal == null )
331 {
332 throw new LdapConfigurationException( "missing required "
333 + Context.SECURITY_PRINCIPAL + " property for simple authentication" );
334 }
335 }
336
337
338
339
340 else if ( "none".equalsIgnoreCase( authentication ) )
341 {
342 if ( credential != null )
343 {
344 throw new LdapConfigurationException( "ambiguous bind "
345 + "settings encountered where bind is anonymous yet "
346 + Context.SECURITY_CREDENTIALS + " property is set" );
347 }
348 if ( principal != null )
349 {
350 throw new LdapConfigurationException( "ambiguous bind "
351 + "settings encountered where bind is anonymous yet "
352 + Context.SECURITY_PRINCIPAL + " property is set" );
353 }
354
355 if( !startupConfiguration.isAllowAnonymousAccess() )
356 {
357 throw new LdapNoPermissionException( "Anonymous access disabled." );
358 }
359 }
360 else
361 {
362
363
364
365
366 throw new LdapAuthenticationNotSupportedException( "Unknown authentication type: '" + authentication + "'", ResultCodeEnum.AUTHMETHODNOTSUPPORTED );
367 }
368 }
369
370
371 /***
372 * Returns true if we had to create the bootstrap entries on the first
373 * start of the server. Otherwise if all entries exist, meaning none
374 * had to be created, then we are not starting for the first time.
375 *
376 * @throws javax.naming.NamingException
377 */
378 private boolean createBootstrapEntries() throws NamingException
379 {
380 boolean firstStart = false;
381
382
383
384
385
386
387
388
389 if ( !partitionNexus.hasEntry( DirectoryPartitionNexus.getAdminName() ) )
390 {
391 firstStart = true;
392
393 Attributes attributes = new LockableAttributesImpl();
394 Attribute objectClass = new LockableAttributeImpl( "objectClass" );
395 objectClass.add( "top" );
396 objectClass.add( "person" );
397 objectClass.add( "organizationalPerson" );
398 objectClass.add( "inetOrgPerson" );
399 attributes.put( objectClass );
400
401 attributes.put( "uid", DirectoryPartitionNexus.ADMIN_UID );
402 attributes.put( "userPassword", DirectoryPartitionNexus.ADMIN_PASSWORD );
403 attributes.put( "displayName", "Directory Superuser" );
404 attributes.put( "cn", "system administrator" );
405 attributes.put( "sn", "administrator" );
406 attributes.put( "creatorsName", DirectoryPartitionNexus.ADMIN_PRINCIPAL );
407 attributes.put( "createTimestamp", DateUtils.getGeneralizedTime() );
408 attributes.put( "displayName", "Directory Superuser" );
409
410 partitionNexus.add( DirectoryPartitionNexus.ADMIN_PRINCIPAL, DirectoryPartitionNexus.getAdminName(), attributes );
411 }
412
413
414
415
416
417 if ( !partitionNexus.hasEntry( new LdapName( "ou=users,ou=system" ) ) )
418 {
419 firstStart = true;
420
421 Attributes attributes = new LockableAttributesImpl();
422 Attribute objectClass = new LockableAttributeImpl( "objectClass" );
423 objectClass.add( "top" );
424 objectClass.add( "organizationalUnit" );
425 attributes.put( objectClass );
426
427 attributes.put( "ou", "users" );
428 attributes.put( "creatorsName", DirectoryPartitionNexus.ADMIN_PRINCIPAL );
429 attributes.put( "createTimestamp", DateUtils.getGeneralizedTime() );
430
431 partitionNexus.add( "ou=users,ou=system", new LdapName( "ou=users,ou=system" ), attributes );
432 }
433
434
435
436
437
438 if ( !partitionNexus.hasEntry( new LdapName( "ou=groups,ou=system" ) ) )
439 {
440 firstStart = true;
441
442 Attributes attributes = new LockableAttributesImpl();
443 Attribute objectClass = new LockableAttributeImpl( "objectClass" );
444 objectClass.add( "top" );
445 objectClass.add( "organizationalUnit" );
446 attributes.put( objectClass );
447
448 attributes.put( "ou", "groups" );
449 attributes.put( "creatorsName", DirectoryPartitionNexus.ADMIN_PRINCIPAL );
450 attributes.put( "createTimestamp", DateUtils.getGeneralizedTime() );
451
452 partitionNexus.add( "ou=groups,ou=system", new LdapName( "ou=groups,ou=system" ), attributes );
453 }
454
455
456
457
458
459 String upName = "cn=Administrators,ou=groups,ou=system";
460 Name normName = new LdapName( "cn=administrators,ou=groups,ou=system" );
461 if ( !partitionNexus.hasEntry( normName ) )
462 {
463 firstStart = true;
464
465 Attributes attributes = new LockableAttributesImpl();
466 Attribute objectClass = new LockableAttributeImpl( "objectClass" );
467 objectClass.add( "top" );
468 objectClass.add( "groupOfUniqueNames" );
469 attributes.put( objectClass );
470 attributes.put( "cn", "Administrators" );
471 attributes.put( "uniqueMember", DirectoryPartitionNexus.ADMIN_PRINCIPAL );
472 attributes.put( "creatorsName", DirectoryPartitionNexus.ADMIN_PRINCIPAL );
473 attributes.put( "createTimestamp", DateUtils.getGeneralizedTime() );
474
475 partitionNexus.add( upName, normName, attributes );
476 AuthorizationService authzSrvc = ( AuthorizationService ) interceptorChain.get( "authorizationService" );
477 authzSrvc.cacheNewGroup( upName, normName, attributes );
478 }
479
480
481
482
483
484 if ( !partitionNexus.hasEntry( new LdapName( "ou=configuration,ou=system" ) ) )
485 {
486 firstStart = true;
487
488 Attributes attributes = new LockableAttributesImpl();
489 Attribute objectClass = new LockableAttributeImpl( "objectClass" );
490 objectClass.add( "top" );
491 objectClass.add( "organizationalUnit" );
492 attributes.put( objectClass );
493
494 attributes.put( "ou", "configuration" );
495 attributes.put( "creatorsName", DirectoryPartitionNexus.ADMIN_PRINCIPAL );
496 attributes.put( "createTimestamp", DateUtils.getGeneralizedTime() );
497
498 partitionNexus.add( "ou=configuration,ou=system", new LdapName( "ou=configuration,ou=system" ), attributes );
499 }
500
501
502
503
504
505 if ( !partitionNexus.hasEntry( new LdapName( "ou=partitions,ou=configuration,ou=system" ) ) )
506 {
507 firstStart = true;
508
509 Attributes attributes = new LockableAttributesImpl();
510 Attribute objectClass = new LockableAttributeImpl( "objectClass" );
511 objectClass.add( "top" );
512 objectClass.add( "organizationalUnit" );
513 attributes.put( objectClass );
514
515 attributes.put( "ou", "partitions" );
516 attributes.put( "creatorsName", DirectoryPartitionNexus.ADMIN_PRINCIPAL );
517 attributes.put( "createTimestamp", DateUtils.getGeneralizedTime() );
518
519 partitionNexus.add( "ou=partitions,ou=configuration,ou=system",
520 new LdapName( "ou=partitions,ou=configuration,ou=system" ), attributes );
521 }
522
523
524
525
526
527 if ( !partitionNexus.hasEntry( new LdapName( "ou=services,ou=configuration,ou=system" ) ) )
528 {
529 firstStart = true;
530
531 Attributes attributes = new LockableAttributesImpl();
532 Attribute objectClass = new LockableAttributeImpl( "objectClass" );
533 objectClass.add( "top" );
534 objectClass.add( "organizationalUnit" );
535 attributes.put( objectClass );
536
537 attributes.put( "ou", "services" );
538 attributes.put( "creatorsName", DirectoryPartitionNexus.ADMIN_PRINCIPAL );
539 attributes.put( "createTimestamp", DateUtils.getGeneralizedTime() );
540
541 partitionNexus.add( "ou=services,ou=configuration,ou=system",
542 new LdapName( "ou=services,ou=configuration,ou=system" ), attributes );
543 }
544
545
546
547
548
549 if ( !partitionNexus.hasEntry( new LdapName( "ou=interceptors,ou=configuration,ou=system" ) ) )
550 {
551 firstStart = true;
552
553 Attributes attributes = new LockableAttributesImpl();
554 Attribute objectClass = new LockableAttributeImpl( "objectClass" );
555 objectClass.add( "top" );
556 objectClass.add( "organizationalUnit" );
557 attributes.put( objectClass );
558
559 attributes.put( "ou", "interceptors" );
560 attributes.put( "creatorsName", DirectoryPartitionNexus.ADMIN_PRINCIPAL );
561 attributes.put( "createTimestamp", DateUtils.getGeneralizedTime() );
562
563 partitionNexus.add( "ou=interceptors,ou=configuration,ou=system",
564 new LdapName( "ou=interceptors,ou=configuration,ou=system" ), attributes );
565 }
566
567
568
569
570
571 if ( !partitionNexus.hasEntry( new LdapName( "prefNodeName=sysPrefRoot,ou=system" ) ) )
572 {
573 firstStart = true;
574
575 Attributes attributes = new LockableAttributesImpl();
576 Attribute objectClass = new LockableAttributeImpl( "objectClass" );
577 objectClass.add( "top" );
578 objectClass.add( "organizationalUnit" );
579 attributes.put( objectClass );
580
581 attributes.put( "objectClass", "extensibleObject" );
582 attributes.put( "prefNodeName", "sysPrefRoot" );
583 attributes.put( "creatorsName", DirectoryPartitionNexus.ADMIN_PRINCIPAL );
584 attributes.put( "createTimestamp", DateUtils.getGeneralizedTime() );
585
586 LdapName dn = new LdapName( "prefNodeName=sysPrefRoot,ou=system" );
587
588 partitionNexus.add( "prefNodeName=sysPrefRoot,ou=system", dn, attributes );
589 }
590
591 return firstStart;
592 }
593
594 /***
595 * Displays security warning messages if any possible secutiry issue is found.
596 */
597 private void showSecurityWarnings() throws NamingException
598 {
599
600 boolean needToChangeAdminPassword = false;
601
602 Attributes adminEntry = partitionNexus.lookup( new LdapName( DirectoryPartitionNexus.ADMIN_PRINCIPAL ) );
603 Object userPassword = adminEntry.get( "userPassword" ).get();
604 if( userPassword instanceof byte[] )
605 {
606 needToChangeAdminPassword = DirectoryPartitionNexus.ADMIN_PASSWORD.equals( new String( ( byte[] ) userPassword ) );
607 }
608 else if ( userPassword.toString().equals( new String( DirectoryPartitionNexus.ADMIN_PASSWORD ) ) )
609 {
610 needToChangeAdminPassword = DirectoryPartitionNexus.ADMIN_PASSWORD.equals( userPassword.toString() );
611 }
612
613 if( needToChangeAdminPassword )
614 {
615 log.warn(
616 "You didn't change the admin password of directory service " +
617 "instance '" + instanceId + "'. " +
618 "Please update the admin password as soon as possible " +
619 "to prevent a possible security breach." );
620 }
621 }
622
623 private void createTestEntries() throws NamingException
624 {
625
626
627
628
629
630
631 Iterator i = startupConfiguration.getTestEntries().iterator();
632 while( i.hasNext() )
633 {
634 Attributes entry = ( Attributes ) i.next();
635 entry.put( "creatorsName", DirectoryPartitionNexus.ADMIN_PRINCIPAL );
636 entry.put( "createTimestamp", DateUtils.getGeneralizedTime() );
637
638 Attribute dn = ( Attribute ) entry.get( "dn" ).clone();
639 AttributeTypeRegistry registry = globalRegistries.getAttributeTypeRegistry();
640 NameComponentNormalizer ncn = new ConcreteNameComponentNormalizer( registry );
641 DnParser parser = new DnParser( ncn );
642 Name ndn = parser.parse( ( String ) dn.get() );
643
644 partitionNexus.add( ( String ) dn.get(), ndn, entry );
645 }
646 }
647
648 /***
649 * Kicks off the initialization of the entire system.
650 *
651 * @throws javax.naming.NamingException if there are problems along the way
652 */
653 private void initialize() throws NamingException
654 {
655
656
657
658
659 BootstrapRegistries bootstrapRegistries = new BootstrapRegistries();
660
661 BootstrapSchemaLoader loader = new BootstrapSchemaLoader();
662 loader.load( startupConfiguration.getBootstrapSchemas(), bootstrapRegistries );
663
664 java.util.List errors = bootstrapRegistries.checkRefInteg();
665 if ( !errors.isEmpty() )
666 {
667 NamingException e = new NamingException();
668
669 e.setRootCause( ( Throwable ) errors.get( 0 ) );
670
671 throw e;
672 }
673
674 globalRegistries = new GlobalRegistries( bootstrapRegistries );
675
676 partitionNexus = new DefaultDirectoryPartitionNexus( new LockableAttributesImpl() );
677 partitionNexus.init( configuration, null );
678
679 interceptorChain = new InterceptorChain();
680 interceptorChain.init( configuration );
681 }
682 }