2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 * Copyright 2005-2017 The OpenLDAP Foundation.
5 * Portions Copyright 2005-2006 SysNet s.n.c.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted only as authorized by the OpenLDAP
12 * A copy of this license is available in the file LICENSE in the
13 * top-level directory of the distribution or, alternatively, at
14 * <http://www.OpenLDAP.org/license.html>.
17 * This work was initially developed by Pierangelo Masarati for inclusion
18 * in OpenLDAP Software, sponsored by SysNet s.n.c.
27 #include <ac/string.h>
36 #define DDS_RF2589_MAX_TTL (31557600) /* 1 year + 6 hours */
37 #define DDS_RF2589_DEFAULT_TTL (86400) /* 1 day */
38 #define DDS_DEFAULT_INTERVAL (3600) /* 1 hour */
40 typedef struct dds_info_t {
42 #define DDS_FOFF (0x1U) /* is this really needed? */
43 #define DDS_SET(di, f) ( (di)->di_flags & (f) )
45 #define DDS_OFF(di) DDS_SET( (di), DDS_FOFF )
49 time_t di_default_ttl;
50 #define DDS_DEFAULT_TTL(di) \
51 ( (di)->di_default_ttl ? (di)->di_default_ttl : (di)->di_max_ttl )
55 /* expire check interval and task */
57 #define DDS_INTERVAL(di) \
58 ( (di)->di_interval ? (di)->di_interval : DDS_DEFAULT_INTERVAL )
59 struct re_s *di_expire_task;
61 /* allows to limit the maximum number of dynamic objects */
62 ldap_pvt_thread_mutex_t di_mutex;
63 int di_num_dynamicObjects;
64 int di_max_dynamicObjects;
66 /* used to advertize the dynamicSubtrees in the root DSE,
67 * and to select the database in the expiration task */
72 static struct berval slap_EXOP_REFRESH = BER_BVC( LDAP_EXOP_REFRESH );
73 static AttributeDescription *ad_entryExpireTimestamp;
75 /* list of expired DNs */
76 typedef struct dds_expire_t {
78 struct dds_expire_t *de_next;
81 typedef struct dds_cb_t {
82 dds_expire_t *dc_ndnlist;
86 dds_expire_cb( Operation *op, SlapReply *rs )
88 dds_cb_t *dc = (dds_cb_t *)op->o_callback->sc_private;
92 switch ( rs->sr_type ) {
94 /* alloc list and buffer for berval all in one */
95 de = op->o_tmpalloc( sizeof( dds_expire_t ) + rs->sr_entry->e_nname.bv_len + 1,
98 de->de_next = dc->dc_ndnlist;
101 de->de_ndn.bv_len = rs->sr_entry->e_nname.bv_len;
102 de->de_ndn.bv_val = (char *)&de[ 1 ];
103 AC_MEMCPY( de->de_ndn.bv_val, rs->sr_entry->e_nname.bv_val,
104 rs->sr_entry->e_nname.bv_len + 1 );
121 dds_expire( void *ctx, dds_info_t *di )
123 Connection conn = { 0 };
124 OperationBuffer opbuf;
126 slap_callback sc = { 0 };
128 dds_expire_t *de = NULL, **dep;
129 SlapReply rs = { REP_RESULT };
132 char tsbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
135 int ndeletes, ntotdeletes;
140 connection_fake_init2( &conn, &opbuf, ctx, 0 );
143 op->o_tag = LDAP_REQ_SEARCH;
144 memset( &op->oq_search, 0, sizeof( op->oq_search ) );
146 op->o_bd = select_backend( &di->di_nsuffix[ 0 ], 0 );
148 op->o_req_dn = op->o_bd->be_suffix[ 0 ];
149 op->o_req_ndn = op->o_bd->be_nsuffix[ 0 ];
151 op->o_dn = op->o_bd->be_rootdn;
152 op->o_ndn = op->o_bd->be_rootndn;
154 op->ors_scope = LDAP_SCOPE_SUBTREE;
155 op->ors_tlimit = DDS_INTERVAL( di )/2 + 1;
156 op->ors_slimit = SLAP_NO_LIMIT;
157 op->ors_attrs = slap_anlist_no_attrs;
159 expire = slap_get_time() - di->di_tolerance;
161 ts.bv_len = sizeof( tsbuf );
162 slap_timestamp( &expire, &ts );
164 op->ors_filterstr.bv_len = STRLENOF( "(&(objectClass=" ")(" "<=" "))" )
165 + slap_schema.si_oc_dynamicObject->soc_cname.bv_len
166 + ad_entryExpireTimestamp->ad_cname.bv_len
168 op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
169 snprintf( op->ors_filterstr.bv_val, op->ors_filterstr.bv_len + 1,
170 "(&(objectClass=%s)(%s<=%s))",
171 slap_schema.si_oc_dynamicObject->soc_cname.bv_val,
172 ad_entryExpireTimestamp->ad_cname.bv_val, ts.bv_val );
174 op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
175 if ( op->ors_filter == NULL ) {
176 rs.sr_err = LDAP_OTHER;
180 op->o_callback = ≻
181 sc.sc_response = dds_expire_cb;
184 (void)op->o_bd->bd_info->bi_op_search( op, &rs );
187 op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
188 filter_free_x( op, op->ors_filter, 1 );
191 switch ( rs.sr_err ) {
195 case LDAP_NO_SUCH_OBJECT:
196 /* (ITS#5267) database not created yet? */
197 rs.sr_err = LDAP_SUCCESS;
198 extra = " (ignored)";
202 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
203 "DDS expired objects lookup failed err=%d%s\n",
208 op->o_tag = LDAP_REQ_DELETE;
209 op->o_callback = ≻
210 sc.sc_response = slap_null_cb;
211 sc.sc_private = NULL;
213 for ( ntotdeletes = 0, ndeletes = 1; dc.dc_ndnlist != NULL && ndeletes > 0; ) {
216 for ( dep = &dc.dc_ndnlist; *dep != NULL; ) {
219 op->o_req_dn = de->de_ndn;
220 op->o_req_ndn = de->de_ndn;
221 (void)op->o_bd->bd_info->bi_op_delete( op, &rs );
222 switch ( rs.sr_err ) {
224 Log1( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
225 "DDS dn=\"%s\" expired.\n",
230 case LDAP_NOT_ALLOWED_ON_NONLEAF:
231 Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_NOTICE,
232 "DDS dn=\"%s\" is non-leaf; "
240 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_NOTICE,
241 "DDS dn=\"%s\" err=%d; "
243 de->de_ndn.bv_val, rs.sr_err );
249 op->o_tmpfree( de, op->o_tmpmemctx );
253 ntotdeletes += ndeletes;
256 rs.sr_err = LDAP_SUCCESS;
258 Log1( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
259 "DDS expired=%d\n", ntotdeletes );
266 dds_expire_fn( void *ctx, void *arg )
268 struct re_s *rtask = arg;
269 dds_info_t *di = rtask->arg;
271 assert( di->di_expire_task == rtask );
273 (void)dds_expire( ctx, di );
275 ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
276 if ( ldap_pvt_runqueue_isrunning( &slapd_rq, rtask )) {
277 ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
279 ldap_pvt_runqueue_resched( &slapd_rq, rtask, 0 );
280 ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
285 /* frees the callback */
287 dds_freeit_cb( Operation *op, SlapReply *rs )
289 op->o_tmpfree( op->o_callback, op->o_tmpmemctx );
290 op->o_callback = NULL;
292 return SLAP_CB_CONTINUE;
295 /* updates counter - installed on add/delete only if required */
297 dds_counter_cb( Operation *op, SlapReply *rs )
299 assert( rs->sr_type == REP_RESULT );
301 if ( rs->sr_err == LDAP_SUCCESS ) {
302 dds_info_t *di = op->o_callback->sc_private;
304 ldap_pvt_thread_mutex_lock( &di->di_mutex );
305 switch ( op->o_tag ) {
306 case LDAP_REQ_DELETE:
307 assert( di->di_num_dynamicObjects > 0 );
308 di->di_num_dynamicObjects--;
312 assert( di->di_num_dynamicObjects < di->di_max_dynamicObjects );
313 di->di_num_dynamicObjects++;
319 ldap_pvt_thread_mutex_unlock( &di->di_mutex );
322 return dds_freeit_cb( op, rs );
326 dds_op_add( Operation *op, SlapReply *rs )
328 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
329 dds_info_t *di = on->on_bi.bi_private;
330 int is_dynamicObject;
332 if ( DDS_OFF( di ) ) {
333 return SLAP_CB_CONTINUE;
336 is_dynamicObject = is_entry_dynamicObject( op->ora_e );
338 /* FIXME: do not allow this right now, pending clarification */
339 if ( is_dynamicObject ) {
340 rs->sr_err = LDAP_SUCCESS;
342 if ( is_entry_referral( op->ora_e ) ) {
343 rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
344 rs->sr_text = "a referral cannot be a dynamicObject";
346 } else if ( is_entry_alias( op->ora_e ) ) {
347 rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
348 rs->sr_text = "an alias cannot be a dynamicObject";
351 if ( rs->sr_err != LDAP_SUCCESS ) {
352 op->o_bd->bd_info = (BackendInfo *)on->on_info;
353 send_ldap_result( op, rs );
358 /* we don't allow dynamicObjects to have static subordinates */
359 if ( !dn_match( &op->o_req_ndn, &op->o_bd->be_nsuffix[ 0 ] ) ) {
363 BackendInfo *bi = op->o_bd->bd_info;
365 dnParent( &op->o_req_ndn, &p_ndn );
366 op->o_bd->bd_info = (BackendInfo *)on->on_info;
367 rc = be_entry_get_rw( op, &p_ndn,
368 slap_schema.si_oc_dynamicObject, NULL, 0, &e );
369 if ( rc == LDAP_SUCCESS && e != NULL ) {
370 if ( !is_dynamicObject ) {
371 /* return referral only if "disclose"
372 * is granted on the object */
373 if ( ! access_allowed( op, e,
374 slap_schema.si_ad_entry,
375 NULL, ACL_DISCLOSE, NULL ) )
377 rc = rs->sr_err = LDAP_NO_SUCH_OBJECT;
378 send_ldap_result( op, rs );
381 rc = rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
382 send_ldap_error( op, rs, rc, "no static subordinate entries allowed for dynamicObject" );
386 be_entry_release_r( op, e );
387 if ( rc != LDAP_SUCCESS ) {
391 op->o_bd->bd_info = bi;
394 /* handle dynamic object operational attr(s) */
395 if ( is_dynamicObject ) {
397 char ttlbuf[STRLENOF("31557600") + 1];
398 char tsbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
401 if ( !be_isroot_dn( op->o_bd, &op->o_req_ndn ) ) {
402 ldap_pvt_thread_mutex_lock( &di->di_mutex );
403 rs->sr_err = ( di->di_max_dynamicObjects &&
404 di->di_num_dynamicObjects >= di->di_max_dynamicObjects );
405 ldap_pvt_thread_mutex_unlock( &di->di_mutex );
407 op->o_bd->bd_info = (BackendInfo *)on->on_info;
408 send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
409 "too many dynamicObjects in context" );
414 ttl = DDS_DEFAULT_TTL( di );
416 /* assert because should be checked at configure */
417 assert( ttl <= DDS_RF2589_MAX_TTL );
420 bv.bv_len = snprintf( ttlbuf, sizeof( ttlbuf ), "%ld", ttl );
421 assert( bv.bv_len < sizeof( ttlbuf ) );
423 /* FIXME: apparently, values in op->ora_e are malloc'ed
424 * on the thread's slab; works fine by chance,
425 * only because the attribute doesn't exist yet. */
426 assert( attr_find( op->ora_e->e_attrs, slap_schema.si_ad_entryTtl ) == NULL );
427 attr_merge_one( op->ora_e, slap_schema.si_ad_entryTtl, &bv, &bv );
429 expire = slap_get_time() + ttl;
431 bv.bv_len = sizeof( tsbuf );
432 slap_timestamp( &expire, &bv );
433 assert( attr_find( op->ora_e->e_attrs, ad_entryExpireTimestamp ) == NULL );
434 attr_merge_one( op->ora_e, ad_entryExpireTimestamp, &bv, &bv );
436 /* if required, install counter callback */
437 if ( di->di_max_dynamicObjects > 0) {
440 sc = op->o_tmpalloc( sizeof( slap_callback ), op->o_tmpmemctx );
441 sc->sc_cleanup = dds_freeit_cb;
442 sc->sc_response = dds_counter_cb;
444 sc->sc_next = op->o_callback;
445 sc->sc_writewait = 0;
451 return SLAP_CB_CONTINUE;
455 dds_op_delete( Operation *op, SlapReply *rs )
457 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
458 dds_info_t *di = on->on_bi.bi_private;
460 /* if required, install counter callback */
461 if ( !DDS_OFF( di ) && di->di_max_dynamicObjects > 0 ) {
463 BackendInfo *bi = op->o_bd->bd_info;
465 op->o_bd->bd_info = (BackendInfo *)on->on_info;
466 rs->sr_err = be_entry_get_rw( op, &op->o_req_ndn,
467 slap_schema.si_oc_dynamicObject, NULL, 0, &e );
469 /* FIXME: couldn't the entry be added before deletion? */
470 if ( rs->sr_err == LDAP_SUCCESS && e != NULL ) {
473 be_entry_release_r( op, e );
476 sc = op->o_tmpalloc( sizeof( slap_callback ), op->o_tmpmemctx );
477 sc->sc_cleanup = dds_freeit_cb;
478 sc->sc_response = dds_counter_cb;
480 sc->sc_writewait = 0;
481 sc->sc_next = op->o_callback;
485 op->o_bd->bd_info = bi;
488 return SLAP_CB_CONTINUE;
492 dds_op_modify( Operation *op, SlapReply *rs )
494 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
495 dds_info_t *di = (dds_info_t *)on->on_bi.bi_private;
498 BackendInfo *bi = op->o_bd->bd_info;
499 int was_dynamicObject = 0,
500 is_dynamicObject = 0;
501 struct berval bv_entryTtl = BER_BVNULL;
503 char textbuf[ SLAP_TEXT_BUFLEN ];
505 if ( DDS_OFF( di ) ) {
506 return SLAP_CB_CONTINUE;
509 /* bv_entryTtl stores the string representation of the entryTtl
510 * across modifies for consistency checks of the final value;
511 * the bv_val points to a static buffer; the bv_len is zero when
512 * the attribute is deleted.
513 * entryTtl stores the integer representation of the entryTtl;
514 * its value is -1 when the attribute is deleted; it is 0 only
515 * if no modifications of the entryTtl occurred, as an entryTtl
516 * of 0 is invalid. */
517 bv_entryTtl.bv_val = textbuf;
519 op->o_bd->bd_info = (BackendInfo *)on->on_info;
520 rs->sr_err = be_entry_get_rw( op, &op->o_req_ndn,
521 slap_schema.si_oc_dynamicObject, slap_schema.si_ad_entryTtl, 0, &e );
522 if ( rs->sr_err == LDAP_SUCCESS && e != NULL ) {
523 Attribute *a = attr_find( e->e_attrs, slap_schema.si_ad_entryTtl );
525 /* the value of the entryTtl is saved for later checks */
530 bv_entryTtl.bv_len = a->a_nvals[ 0 ].bv_len;
531 AC_MEMCPY( bv_entryTtl.bv_val, a->a_nvals[ 0 ].bv_val, bv_entryTtl.bv_len );
532 bv_entryTtl.bv_val[ bv_entryTtl.bv_len ] = '\0';
533 rc = lutil_atoul( &ttl, bv_entryTtl.bv_val );
535 entryTtl = (time_t)ttl;
538 be_entry_release_r( op, e );
540 was_dynamicObject = is_dynamicObject = 1;
542 op->o_bd->bd_info = bi;
544 rs->sr_err = LDAP_SUCCESS;
545 for ( mod = op->orm_modlist; mod; mod = mod->sml_next ) {
546 if ( mod->sml_desc == slap_schema.si_ad_objectClass ) {
550 switch ( mod->sml_op ) {
551 case LDAP_MOD_DELETE:
552 if ( mod->sml_values == NULL ) {
553 is_dynamicObject = 0;
557 for ( i = 0; !BER_BVISNULL( &mod->sml_values[ i ] ); i++ ) {
558 oc = oc_bvfind( &mod->sml_values[ i ] );
559 if ( oc == slap_schema.si_oc_dynamicObject ) {
560 is_dynamicObject = 0;
567 case LDAP_MOD_REPLACE:
568 if ( mod->sml_values == NULL ) {
569 is_dynamicObject = 0;
575 for ( i = 0; !BER_BVISNULL( &mod->sml_values[ i ] ); i++ ) {
576 oc = oc_bvfind( &mod->sml_values[ i ] );
577 if ( oc == slap_schema.si_oc_dynamicObject ) {
578 is_dynamicObject = 1;
585 } else if ( mod->sml_desc == slap_schema.si_ad_entryTtl ) {
590 switch ( mod->sml_op ) {
591 case LDAP_MOD_DELETE:
592 case SLAP_MOD_SOFTDEL: /* FIXME? */
593 if ( mod->sml_values != NULL ) {
594 if ( BER_BVISEMPTY( &bv_entryTtl )
595 || !bvmatch( &bv_entryTtl, &mod->sml_values[ 0 ] ) )
597 rs->sr_err = backend_attribute( op, NULL, &op->o_req_ndn,
598 slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
599 if ( rs->sr_err == LDAP_INSUFFICIENT_ACCESS ) {
600 rs->sr_err = LDAP_NO_SUCH_OBJECT;
603 rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE;
608 bv_entryTtl.bv_len = 0;
612 case LDAP_MOD_REPLACE:
613 bv_entryTtl.bv_len = 0;
618 case SLAP_MOD_SOFTADD: /* FIXME? */
619 case SLAP_MOD_ADD_IF_NOT_PRESENT: /* FIXME? */
620 assert( mod->sml_values != NULL );
621 assert( BER_BVISNULL( &mod->sml_values[ 1 ] ) );
623 if ( !BER_BVISEMPTY( &bv_entryTtl ) ) {
624 rs->sr_err = backend_attribute( op, NULL, &op->o_req_ndn,
625 slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
626 if ( rs->sr_err == LDAP_INSUFFICIENT_ACCESS ) {
627 rs->sr_err = LDAP_NO_SUCH_OBJECT;
630 rs->sr_text = "attribute 'entryTtl' cannot have multiple values";
631 rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
636 rc = lutil_atoul( &uttl, mod->sml_values[ 0 ].bv_val );
639 if ( ttl > DDS_RF2589_MAX_TTL ) {
640 rs->sr_err = LDAP_PROTOCOL_ERROR;
641 rs->sr_text = "invalid time-to-live for dynamicObject";
645 if ( ttl <= 0 || ttl > di->di_max_ttl ) {
646 /* FIXME: I don't understand if this has to be an error,
647 * or an indication that the requested Ttl has been
648 * shortened to di->di_max_ttl >= 1 day */
649 rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
650 rs->sr_text = "time-to-live for dynamicObject exceeds administrative limit";
655 bv_entryTtl.bv_len = mod->sml_values[ 0 ].bv_len;
656 AC_MEMCPY( bv_entryTtl.bv_val, mod->sml_values[ 0 ].bv_val, bv_entryTtl.bv_len );
657 bv_entryTtl.bv_val[ bv_entryTtl.bv_len ] = '\0';
660 case LDAP_MOD_INCREMENT:
661 if ( BER_BVISEMPTY( &bv_entryTtl ) ) {
662 rs->sr_err = backend_attribute( op, NULL, &op->o_req_ndn,
663 slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
664 if ( rs->sr_err == LDAP_INSUFFICIENT_ACCESS ) {
665 rs->sr_err = LDAP_NO_SUCH_OBJECT;
668 rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE;
669 rs->sr_text = "modify/increment: entryTtl: no such attribute";
675 if ( entryTtl > DDS_RF2589_MAX_TTL ) {
676 rs->sr_err = LDAP_PROTOCOL_ERROR;
677 rs->sr_text = "invalid time-to-live for dynamicObject";
679 } else if ( entryTtl <= 0 || entryTtl > di->di_max_ttl ) {
680 /* FIXME: I don't understand if this has to be an error,
681 * or an indication that the requested Ttl has been
682 * shortened to di->di_max_ttl >= 1 day */
683 rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
684 rs->sr_text = "time-to-live for dynamicObject exceeds administrative limit";
687 if ( rs->sr_err != LDAP_SUCCESS ) {
688 rc = backend_attribute( op, NULL, &op->o_req_ndn,
689 slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
690 if ( rc == LDAP_INSUFFICIENT_ACCESS ) {
692 rs->sr_err = LDAP_NO_SUCH_OBJECT;
698 bv_entryTtl.bv_len = snprintf( textbuf, sizeof( textbuf ), "%ld", entryTtl );
706 } else if ( mod->sml_desc == ad_entryExpireTimestamp ) {
707 /* should have been trapped earlier */
708 assert( mod->sml_flags & SLAP_MOD_INTERNAL );
713 if ( rs->sr_err == LDAP_SUCCESS ) {
716 /* FIXME: this could be allowed when the Relax control is used...
722 * entryTtl must be provided; add
723 * entryExpireTimestamp accordingly
726 * entryTtl must be removed; remove
727 * entryTimestamp accordingly
729 * ... but we need to make sure that there are no subordinate
732 rc = is_dynamicObject - was_dynamicObject;
734 #if 0 /* fix subordinate issues first */
735 if ( get_relax( op ) ) {
738 /* need to delete entryTtl to have a consistent entry */
739 if ( entryTtl != -1 ) {
740 rs->sr_text = "objectClass modification from dynamicObject to static entry requires entryTtl deletion";
741 rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
746 /* need to add entryTtl to have a consistent entry */
747 if ( entryTtl <= 0 ) {
748 rs->sr_text = "objectClass modification from static entry to dynamicObject requires entryTtl addition";
749 rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
759 rs->sr_text = "objectClass modification cannot turn dynamicObject into static entry";
763 rs->sr_text = "objectClass modification cannot turn static entry into dynamicObject";
766 rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
769 if ( rc != LDAP_SUCCESS ) {
770 rc = backend_attribute( op, NULL, &op->o_req_ndn,
771 slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
772 if ( rc == LDAP_INSUFFICIENT_ACCESS ) {
774 rs->sr_err = LDAP_NO_SUCH_OBJECT;
780 if ( rs->sr_err == LDAP_SUCCESS && entryTtl != 0 ) {
781 Modifications *tmpmod = NULL, **modp;
783 for ( modp = &op->orm_modlist; *modp; modp = &(*modp)->sml_next )
786 tmpmod = ch_calloc( 1, sizeof( Modifications ) );
787 tmpmod->sml_flags = SLAP_MOD_INTERNAL;
788 tmpmod->sml_type = ad_entryExpireTimestamp->ad_cname;
789 tmpmod->sml_desc = ad_entryExpireTimestamp;
793 if ( entryTtl == -1 ) {
794 /* delete entryExpireTimestamp */
795 tmpmod->sml_op = LDAP_MOD_DELETE;
799 char tsbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
802 /* keep entryExpireTimestamp consistent
804 expire = slap_get_time() + entryTtl;
806 bv.bv_len = sizeof( tsbuf );
807 slap_timestamp( &expire, &bv );
809 tmpmod->sml_op = LDAP_MOD_REPLACE;
810 value_add_one( &tmpmod->sml_values, &bv );
811 value_add_one( &tmpmod->sml_nvalues, &bv );
812 tmpmod->sml_numvals = 1;
817 op->o_bd->bd_info = (BackendInfo *)on->on_info;
818 send_ldap_result( op, rs );
822 return SLAP_CB_CONTINUE;
826 dds_op_rename( Operation *op, SlapReply *rs )
828 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
829 dds_info_t *di = on->on_bi.bi_private;
831 if ( DDS_OFF( di ) ) {
832 return SLAP_CB_CONTINUE;
835 /* we don't allow dynamicObjects to have static subordinates */
836 if ( op->orr_nnewSup != NULL ) {
838 BackendInfo *bi = op->o_bd->bd_info;
839 int is_dynamicObject = 0,
842 rs->sr_err = LDAP_SUCCESS;
844 op->o_bd->bd_info = (BackendInfo *)on->on_info;
845 rc = be_entry_get_rw( op, &op->o_req_ndn,
846 slap_schema.si_oc_dynamicObject, NULL, 0, &e );
847 if ( rc == LDAP_SUCCESS && e != NULL ) {
848 be_entry_release_r( op, e );
850 is_dynamicObject = 1;
853 rc = be_entry_get_rw( op, op->orr_nnewSup,
854 slap_schema.si_oc_dynamicObject, NULL, 0, &e );
855 if ( rc == LDAP_SUCCESS && e != NULL ) {
856 if ( !is_dynamicObject ) {
857 /* return referral only if "disclose"
858 * is granted on the object */
859 if ( ! access_allowed( op, e,
860 slap_schema.si_ad_entry,
861 NULL, ACL_DISCLOSE, NULL ) )
863 rs->sr_err = LDAP_NO_SUCH_OBJECT;
864 send_ldap_result( op, rs );
867 send_ldap_error( op, rs, LDAP_CONSTRAINT_VIOLATION,
868 "static entry cannot have dynamicObject as newSuperior" );
871 be_entry_release_r( op, e );
873 op->o_bd->bd_info = bi;
874 if ( rs->sr_err != LDAP_SUCCESS ) {
879 return SLAP_CB_CONTINUE;
882 /* entryTtl update for client */
884 dds_response( Operation *op, SlapReply *rs )
886 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
887 dds_info_t *di = on->on_bi.bi_private;
891 && rs->sr_type == REP_SEARCH
892 && attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_entryTtl ) )
894 BerVarray vals = NULL;
896 struct lutil_timet tt;
897 char ttlbuf[STRLENOF("31557600") + 1];
898 struct berval ttlvalue;
902 /* User already has access to entryTtl, skip ACL checks on
903 * entryExpireTimestamp */
904 rc = backend_attribute( op, NULL, &rs->sr_entry->e_nname,
905 ad_entryExpireTimestamp, &vals, ACL_NONE );
906 if ( rc != LDAP_SUCCESS ) {
910 assert( vals[0].bv_val[vals[0].bv_len] == '\0' );
911 if ( lutil_parsetime( vals[0].bv_val, &tm ) ) {
915 lutil_tm2time( &tm, &tt );
916 ttl = tt.tt_sec - op->o_time;
917 ttl = (ttl < 0) ? 0 : ttl;
918 assert( ttl <= DDS_RF2589_MAX_TTL );
920 len = snprintf( ttlbuf, sizeof(ttlbuf), "%ld", ttl );
925 ttlvalue.bv_val = ttlbuf;
926 ttlvalue.bv_len = len;
928 rs_entry2modifiable( op, rs, on );
930 if ( attr_delete( &rs->sr_entry->e_attrs,
931 slap_schema.si_ad_entryTtl ) )
935 if ( attr_merge_normalize_one( rs->sr_entry,
936 slap_schema.si_ad_entryTtl,
937 &ttlvalue, op->o_tmpmemctx ) )
943 ber_bvarray_free_x( vals, op->o_tmpmemctx );
945 return SLAP_CB_CONTINUE;
956 int rc = LDAP_SUCCESS;
959 BerElementBuffer berbuf;
960 BerElement *ber = (BerElement *)&berbuf;
961 struct berval reqdata = BER_BVNULL;
970 if ( in == NULL || in->bv_len == 0 ) {
971 *text = "empty request data field in refresh exop";
972 return LDAP_PROTOCOL_ERROR;
975 ber_dupbv_x( &reqdata, in, ctx );
977 /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
978 ber_init2( ber, &reqdata, 0 );
980 tag = ber_scanf( ber, "{" /*}*/ );
982 if ( tag == LBER_ERROR ) {
983 Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
984 "slap_parse_refresh: decoding error.\n" );
988 tag = ber_peek_tag( ber, &len );
989 if ( tag != LDAP_TAG_EXOP_REFRESH_REQ_DN ) {
990 Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
991 "slap_parse_refresh: decoding error.\n" );
998 tag = ber_scanf( ber, "m", &dn );
999 if ( tag == LBER_ERROR ) {
1000 Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
1001 "slap_parse_refresh: DN parse failed.\n" );
1002 goto decoding_error;
1005 rc = dnNormalize( 0, NULL, NULL, &dn, ndn, ctx );
1006 if ( rc != LDAP_SUCCESS ) {
1007 *text = "invalid DN in refresh exop request data";
1012 tag = ber_scanf( ber, "x" /* "m" */ );
1013 if ( tag == LBER_DEFAULT ) {
1014 goto decoding_error;
1018 tag = ber_peek_tag( ber, &len );
1020 if ( tag != LDAP_TAG_EXOP_REFRESH_REQ_TTL ) {
1021 Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
1022 "slap_parse_refresh: decoding error.\n" );
1023 goto decoding_error;
1026 tag = ber_scanf( ber, "i", &tmp );
1027 if ( tag == LBER_ERROR ) {
1028 Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
1029 "slap_parse_refresh: TTL parse failed.\n" );
1030 goto decoding_error;
1037 tag = ber_peek_tag( ber, &len );
1039 if ( tag != LBER_DEFAULT || len != 0 ) {
1041 Log1( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
1042 "slap_parse_refresh: decoding error, len=%ld\n",
1044 rc = LDAP_PROTOCOL_ERROR;
1045 *text = "data decoding error";
1048 if ( ndn && !BER_BVISNULL( ndn ) ) {
1049 slap_sl_free( ndn->bv_val, ctx );
1054 if ( !BER_BVISNULL( &reqdata ) ) {
1055 ber_memfree_x( reqdata.bv_val, ctx );
1062 dds_op_extended( Operation *op, SlapReply *rs )
1064 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1065 dds_info_t *di = on->on_bi.bi_private;
1067 if ( DDS_OFF( di ) ) {
1068 return SLAP_CB_CONTINUE;
1071 if ( bvmatch( &op->ore_reqoid, &slap_EXOP_REFRESH ) ) {
1074 BackendDB db = *op->o_bd;
1075 SlapReply rs2 = { REP_RESULT };
1076 Operation op2 = *op;
1077 slap_callback sc = { 0 };
1078 Modifications ttlmod = { { 0 } };
1079 struct berval ttlvalues[ 2 ];
1080 char ttlbuf[STRLENOF("31557600") + 1];
1082 rs->sr_err = slap_parse_refresh( op->ore_reqdata, NULL, &ttl,
1083 &rs->sr_text, NULL );
1084 assert( rs->sr_err == LDAP_SUCCESS );
1086 if ( ttl <= 0 || ttl > DDS_RF2589_MAX_TTL ) {
1087 rs->sr_err = LDAP_PROTOCOL_ERROR;
1088 rs->sr_text = "invalid time-to-live for dynamicObject";
1092 if ( ttl > di->di_max_ttl ) {
1093 /* FIXME: I don't understand if this has to be an error,
1094 * or an indication that the requested Ttl has been
1095 * shortened to di->di_max_ttl >= 1 day */
1096 rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
1097 rs->sr_text = "time-to-live for dynamicObject exceeds limit";
1101 if ( di->di_min_ttl && ttl < di->di_min_ttl ) {
1102 ttl = di->di_min_ttl;
1105 /* This does not apply to multi-master case */
1106 if ( !( !SLAP_SINGLE_SHADOW( op->o_bd ) || be_isupdate( op ) ) ) {
1107 /* we SHOULD return a referral in this case */
1108 BerVarray defref = op->o_bd->be_update_refs
1109 ? op->o_bd->be_update_refs : default_referral;
1111 if ( defref != NULL ) {
1112 rs->sr_ref = referral_rewrite( op->o_bd->be_update_refs,
1113 NULL, NULL, LDAP_SCOPE_DEFAULT );
1115 rs->sr_flags |= REP_REF_MUSTBEFREED;
1117 rs->sr_ref = defref;
1119 rs->sr_err = LDAP_REFERRAL;
1122 rs->sr_text = "shadow context; no update referral";
1123 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1129 assert( !BER_BVISNULL( &op->o_req_ndn ) );
1133 /* check if exists but not dynamicObject */
1134 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1135 rs->sr_err = be_entry_get_rw( op, &op->o_req_ndn,
1136 slap_schema.si_oc_dynamicObject, NULL, 0, &e );
1137 if ( rs->sr_err != LDAP_SUCCESS ) {
1138 rs->sr_err = be_entry_get_rw( op, &op->o_req_ndn,
1139 NULL, NULL, 0, &e );
1140 if ( rs->sr_err == LDAP_SUCCESS && e != NULL ) {
1141 /* return referral only if "disclose"
1142 * is granted on the object */
1143 if ( ! access_allowed( op, e,
1144 slap_schema.si_ad_entry,
1145 NULL, ACL_DISCLOSE, NULL ) )
1147 rs->sr_err = LDAP_NO_SUCH_OBJECT;
1150 rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
1151 rs->sr_text = "refresh operation only applies to dynamic objects";
1153 be_entry_release_r( op, e );
1156 rs->sr_err = LDAP_NO_SUCH_OBJECT;
1160 } else if ( e != NULL ) {
1161 be_entry_release_r( op, e );
1164 /* we require manage privileges on the entryTtl,
1165 * and fake a Relax control */
1166 op2.o_tag = LDAP_REQ_MODIFY;
1168 db.bd_info = (BackendInfo *)on->on_info;
1169 op2.o_callback = ≻
1170 sc.sc_response = slap_null_cb;
1171 op2.o_relax = SLAP_CONTROL_CRITICAL;
1172 op2.orm_modlist = &ttlmod;
1174 ttlmod.sml_op = LDAP_MOD_REPLACE;
1175 ttlmod.sml_flags = SLAP_MOD_MANAGING;
1176 ttlmod.sml_desc = slap_schema.si_ad_entryTtl;
1177 ttlmod.sml_values = ttlvalues;
1178 ttlmod.sml_numvals = 1;
1179 ttlvalues[ 0 ].bv_val = ttlbuf;
1180 ttlvalues[ 0 ].bv_len = snprintf( ttlbuf, sizeof( ttlbuf ), "%ld", ttl );
1181 BER_BVZERO( &ttlvalues[ 1 ] );
1183 /* the entryExpireTimestamp is added by modify */
1184 rs->sr_err = op2.o_bd->be_modify( &op2, &rs2 );
1186 if ( ttlmod.sml_next != NULL ) {
1187 slap_mods_free( ttlmod.sml_next, 1 );
1190 if ( rs->sr_err == LDAP_SUCCESS ) {
1192 BerElementBuffer berbuf;
1193 BerElement *ber = (BerElement *)&berbuf;
1195 ber_init_w_nullc( ber, LBER_USE_DER );
1197 rc = ber_printf( ber, "{tiN}", LDAP_TAG_EXOP_REFRESH_RES_TTL, (int)ttl );
1200 rs->sr_err = LDAP_OTHER;
1201 rs->sr_text = "internal error";
1204 (void)ber_flatten( ber, &rs->sr_rspdata );
1205 rs->sr_rspoid = ch_strdup( slap_EXOP_REFRESH.bv_val );
1207 Log3( LDAP_DEBUG_TRACE, LDAP_LEVEL_INFO,
1208 "%s REFRESH dn=\"%s\" TTL=%ld\n",
1209 op->o_log_prefix, op->o_req_ndn.bv_val, ttl );
1212 ber_free_buf( ber );
1218 return SLAP_CB_CONTINUE;
1233 static ConfigDriver dds_cfgen;
1235 static ConfigLDAPadd dds_ldadd;
1236 static ConfigCfAdd dds_cfadd;
1239 static ConfigTable dds_cfg[] = {
1240 { "dds-state", "on|off",
1241 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|DDS_STATE, dds_cfgen,
1242 "( OLcfgOvAt:9.1 NAME 'olcDDSstate' "
1243 "DESC 'RFC2589 Dynamic directory services state' "
1244 "SYNTAX OMsBoolean "
1245 "SINGLE-VALUE )", NULL, NULL },
1246 { "dds-max-ttl", "ttl",
1247 2, 2, 0, ARG_MAGIC|DDS_MAXTTL, dds_cfgen,
1248 "( OLcfgOvAt:9.2 NAME 'olcDDSmaxTtl' "
1249 "DESC 'RFC2589 Dynamic directory services max TTL' "
1250 "SYNTAX OMsDirectoryString "
1251 "SINGLE-VALUE )", NULL, NULL },
1252 { "dds-min-ttl", "ttl",
1253 2, 2, 0, ARG_MAGIC|DDS_MINTTL, dds_cfgen,
1254 "( OLcfgOvAt:9.3 NAME 'olcDDSminTtl' "
1255 "DESC 'RFC2589 Dynamic directory services min TTL' "
1256 "SYNTAX OMsDirectoryString "
1257 "SINGLE-VALUE )", NULL, NULL },
1258 { "dds-default-ttl", "ttl",
1259 2, 2, 0, ARG_MAGIC|DDS_DEFAULTTTL, dds_cfgen,
1260 "( OLcfgOvAt:9.4 NAME 'olcDDSdefaultTtl' "
1261 "DESC 'RFC2589 Dynamic directory services default TTL' "
1262 "SYNTAX OMsDirectoryString "
1263 "SINGLE-VALUE )", NULL, NULL },
1264 { "dds-interval", "interval",
1265 2, 2, 0, ARG_MAGIC|DDS_INTERVAL, dds_cfgen,
1266 "( OLcfgOvAt:9.5 NAME 'olcDDSinterval' "
1267 "DESC 'RFC2589 Dynamic directory services expiration "
1268 "task run interval' "
1269 "SYNTAX OMsDirectoryString "
1270 "SINGLE-VALUE )", NULL, NULL },
1271 { "dds-tolerance", "ttl",
1272 2, 2, 0, ARG_MAGIC|DDS_TOLERANCE, dds_cfgen,
1273 "( OLcfgOvAt:9.6 NAME 'olcDDStolerance' "
1274 "DESC 'RFC2589 Dynamic directory services additional "
1275 "TTL in expiration scheduling' "
1276 "SYNTAX OMsDirectoryString "
1277 "SINGLE-VALUE )", NULL, NULL },
1278 { "dds-max-dynamicObjects", "num",
1279 2, 2, 0, ARG_MAGIC|ARG_INT|DDS_MAXDYNAMICOBJS, dds_cfgen,
1280 "( OLcfgOvAt:9.7 NAME 'olcDDSmaxDynamicObjects' "
1281 "DESC 'RFC2589 Dynamic directory services max number of dynamic objects' "
1282 "SYNTAX OMsInteger "
1283 "SINGLE-VALUE )", NULL, NULL },
1284 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1287 static ConfigOCs dds_ocs[] = {
1288 { "( OLcfgOvOc:9.1 "
1289 "NAME 'olcDDSConfig' "
1290 "DESC 'RFC2589 Dynamic directory services configuration' "
1291 "SUP olcOverlayConfig "
1296 "$ olcDDSdefaultTtl "
1298 "$ olcDDStolerance "
1299 "$ olcDDSmaxDynamicObjects "
1301 ")", Cft_Overlay, dds_cfg, NULL, NULL /* dds_cfadd */ },
1307 dds_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
1309 return LDAP_SUCCESS;
1313 dds_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
1320 dds_cfgen( ConfigArgs *c )
1322 slap_overinst *on = (slap_overinst *)c->bi;
1323 dds_info_t *di = on->on_bi.bi_private;
1328 if ( c->op == SLAP_CONFIG_EMIT ) {
1329 char buf[ SLAP_TEXT_BUFLEN ];
1334 c->value_int = !DDS_OFF( di );
1338 lutil_unparse_time( buf, sizeof( buf ), di->di_max_ttl );
1339 ber_str2bv( buf, 0, 0, &bv );
1340 value_add_one( &c->rvalue_vals, &bv );
1344 if ( di->di_min_ttl ) {
1345 lutil_unparse_time( buf, sizeof( buf ), di->di_min_ttl );
1346 ber_str2bv( buf, 0, 0, &bv );
1347 value_add_one( &c->rvalue_vals, &bv );
1354 case DDS_DEFAULTTTL:
1355 if ( di->di_default_ttl ) {
1356 lutil_unparse_time( buf, sizeof( buf ), di->di_default_ttl );
1357 ber_str2bv( buf, 0, 0, &bv );
1358 value_add_one( &c->rvalue_vals, &bv );
1366 if ( di->di_interval ) {
1367 lutil_unparse_time( buf, sizeof( buf ), di->di_interval );
1368 ber_str2bv( buf, 0, 0, &bv );
1369 value_add_one( &c->rvalue_vals, &bv );
1377 if ( di->di_tolerance ) {
1378 lutil_unparse_time( buf, sizeof( buf ), di->di_tolerance );
1379 ber_str2bv( buf, 0, 0, &bv );
1380 value_add_one( &c->rvalue_vals, &bv );
1387 case DDS_MAXDYNAMICOBJS:
1388 if ( di->di_max_dynamicObjects > 0 ) {
1389 c->value_int = di->di_max_dynamicObjects;
1403 } else if ( c->op == LDAP_MOD_DELETE ) {
1406 di->di_flags &= ~DDS_FOFF;
1410 di->di_min_ttl = DDS_RF2589_DEFAULT_TTL;
1417 case DDS_DEFAULTTTL:
1418 di->di_default_ttl = 0;
1422 di->di_interval = 0;
1426 di->di_tolerance = 0;
1429 case DDS_MAXDYNAMICOBJS:
1430 di->di_max_dynamicObjects = 0;
1441 switch ( c->type ) {
1443 if ( c->value_int ) {
1444 di->di_flags &= ~DDS_FOFF;
1447 di->di_flags |= DDS_FOFF;
1452 if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
1453 snprintf( c->cr_msg, sizeof( c->cr_msg),
1454 "DDS unable to parse dds-max-ttl \"%s\"",
1456 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1457 "%s: %s.\n", c->log, c->cr_msg );
1461 if ( t < DDS_RF2589_DEFAULT_TTL || t > DDS_RF2589_MAX_TTL ) {
1462 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1463 "DDS invalid dds-max-ttl=%lu; must be between %d and %d",
1464 t, DDS_RF2589_DEFAULT_TTL, DDS_RF2589_MAX_TTL );
1465 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1466 "%s: %s.\n", c->log, c->cr_msg );
1470 di->di_max_ttl = (time_t)t;
1474 if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
1475 snprintf( c->cr_msg, sizeof( c->cr_msg),
1476 "DDS unable to parse dds-min-ttl \"%s\"",
1478 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1479 "%s: %s.\n", c->log, c->cr_msg );
1483 if ( t > DDS_RF2589_MAX_TTL ) {
1484 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1485 "DDS invalid dds-min-ttl=%lu",
1487 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1488 "%s: %s.\n", c->log, c->cr_msg );
1493 di->di_min_ttl = DDS_RF2589_DEFAULT_TTL;
1496 di->di_min_ttl = (time_t)t;
1500 case DDS_DEFAULTTTL:
1501 if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
1502 snprintf( c->cr_msg, sizeof( c->cr_msg),
1503 "DDS unable to parse dds-default-ttl \"%s\"",
1505 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1506 "%s: %s.\n", c->log, c->cr_msg );
1510 if ( t > DDS_RF2589_MAX_TTL ) {
1511 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1512 "DDS invalid dds-default-ttl=%lu",
1514 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1515 "%s: %s.\n", c->log, c->cr_msg );
1520 di->di_default_ttl = DDS_RF2589_DEFAULT_TTL;
1523 di->di_default_ttl = (time_t)t;
1528 if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
1529 snprintf( c->cr_msg, sizeof( c->cr_msg),
1530 "DDS unable to parse dds-interval \"%s\"",
1532 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1533 "%s: %s.\n", c->log, c->cr_msg );
1538 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1539 "DDS invalid dds-interval=%lu",
1541 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1542 "%s: %s.\n", c->log, c->cr_msg );
1547 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_NOTICE,
1548 "%s: dds-interval=%lu may be too small.\n",
1552 di->di_interval = (time_t)t;
1553 if ( di->di_expire_task ) {
1554 ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
1555 if ( ldap_pvt_runqueue_isrunning( &slapd_rq, di->di_expire_task ) ) {
1556 ldap_pvt_runqueue_stoptask( &slapd_rq, di->di_expire_task );
1558 di->di_expire_task->interval.tv_sec = DDS_INTERVAL( di );
1559 ldap_pvt_runqueue_resched( &slapd_rq, di->di_expire_task, 0 );
1560 ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
1565 if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
1566 snprintf( c->cr_msg, sizeof( c->cr_msg),
1567 "DDS unable to parse dds-tolerance \"%s\"",
1569 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1570 "%s: %s.\n", c->log, c->cr_msg );
1574 if ( t > DDS_RF2589_MAX_TTL ) {
1575 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1576 "DDS invalid dds-tolerance=%lu",
1578 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1579 "%s: %s.\n", c->log, c->cr_msg );
1583 di->di_tolerance = (time_t)t;
1586 case DDS_MAXDYNAMICOBJS:
1587 if ( c->value_int < 0 ) {
1588 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1589 "DDS invalid dds-max-dynamicObjects=%d", c->value_int );
1590 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1591 "%s: %s.\n", c->log, c->cr_msg );
1594 di->di_max_dynamicObjects = c->value_int;
1610 slap_overinst *on = (slap_overinst *)be->bd_info;
1612 BackendInfo *bi = on->on_info->oi_orig;
1614 if ( SLAP_ISGLOBALOVERLAY( be ) ) {
1615 Log0( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1616 "DDS cannot be used as global overlay.\n" );
1620 /* check support for required functions */
1621 /* FIXME: some could be provided by other overlays in between */
1622 if ( bi->bi_op_add == NULL /* object creation */
1623 || bi->bi_op_delete == NULL /* object deletion */
1624 || bi->bi_op_modify == NULL /* object refresh */
1625 || bi->bi_op_search == NULL /* object expiration */
1626 || bi->bi_entry_get_rw == NULL ) /* object type/existence checking */
1628 Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1629 "DDS backend \"%s\" does not provide "
1630 "required functionality.\n",
1635 di = (dds_info_t *)ch_calloc( 1, sizeof( dds_info_t ) );
1636 on->on_bi.bi_private = di;
1638 di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
1639 di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
1641 ldap_pvt_thread_mutex_init( &di->di_mutex );
1643 SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_DYNAMIC;
1648 /* adds dynamicSubtrees to root DSE */
1650 dds_entry_info( void *arg, Entry *e )
1652 dds_info_t *di = (dds_info_t *)arg;
1654 attr_merge( e, slap_schema.si_ad_dynamicSubtrees,
1655 di->di_suffix, di->di_nsuffix );
1660 /* callback that counts the returned entries, since the search
1661 * does not get to the point in slap_send_search_entries where
1662 * the actual count occurs */
1664 dds_count_cb( Operation *op, SlapReply *rs )
1666 int *nump = (int *)op->o_callback->sc_private;
1668 switch ( rs->sr_type ) {
1684 /* count dynamic objects existing in the database at startup */
1686 dds_count( void *ctx, BackendDB *be )
1688 slap_overinst *on = (slap_overinst *)be->bd_info;
1689 dds_info_t *di = (dds_info_t *)on->on_bi.bi_private;
1691 Connection conn = { 0 };
1692 OperationBuffer opbuf;
1694 slap_callback sc = { 0 };
1695 SlapReply rs = { REP_RESULT };
1700 connection_fake_init2( &conn, &opbuf, ctx, 0 );
1703 op->o_tag = LDAP_REQ_SEARCH;
1704 memset( &op->oq_search, 0, sizeof( op->oq_search ) );
1708 op->o_req_dn = op->o_bd->be_suffix[ 0 ];
1709 op->o_req_ndn = op->o_bd->be_nsuffix[ 0 ];
1711 op->o_dn = op->o_bd->be_rootdn;
1712 op->o_ndn = op->o_bd->be_rootndn;
1714 op->ors_scope = LDAP_SCOPE_SUBTREE;
1715 op->ors_tlimit = SLAP_NO_LIMIT;
1716 op->ors_slimit = SLAP_NO_LIMIT;
1717 op->ors_attrs = slap_anlist_no_attrs;
1719 op->ors_filterstr.bv_len = STRLENOF( "(objectClass=" ")" )
1720 + slap_schema.si_oc_dynamicObject->soc_cname.bv_len;
1721 op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
1722 snprintf( op->ors_filterstr.bv_val, op->ors_filterstr.bv_len + 1,
1724 slap_schema.si_oc_dynamicObject->soc_cname.bv_val );
1726 op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
1727 if ( op->ors_filter == NULL ) {
1728 rs.sr_err = LDAP_OTHER;
1732 op->o_callback = ≻
1733 sc.sc_response = dds_count_cb;
1734 sc.sc_private = &di->di_num_dynamicObjects;
1735 di->di_num_dynamicObjects = 0;
1737 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1738 (void)op->o_bd->bd_info->bi_op_search( op, &rs );
1739 op->o_bd->bd_info = (BackendInfo *)on;
1742 op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
1743 filter_free_x( op, op->ors_filter, 1 );
1746 switch ( rs.sr_err ) {
1748 Log1( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
1749 "DDS non-expired=%d\n",
1750 di->di_num_dynamicObjects );
1753 case LDAP_NO_SUCH_OBJECT:
1754 /* (ITS#5267) database not created yet? */
1755 rs.sr_err = LDAP_SUCCESS;
1756 extra = " (ignored)";
1760 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1761 "DDS non-expired objects lookup failed err=%d%s\n",
1774 slap_overinst *on = (slap_overinst *)be->bd_info;
1775 dds_info_t *di = on->on_bi.bi_private;
1777 void *thrctx = ldap_pvt_thread_pool_context();
1779 if ( slapMode & SLAP_TOOL_MODE )
1782 if ( DDS_OFF( di ) ) {
1786 if ( SLAP_SINGLE_SHADOW( be ) ) {
1787 Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1788 "DDS incompatible with shadow database \"%s\".\n",
1789 be->be_suffix[ 0 ].bv_val );
1793 if ( di->di_max_ttl == 0 ) {
1794 di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
1797 if ( di->di_min_ttl == 0 ) {
1798 di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
1801 di->di_suffix = be->be_suffix;
1802 di->di_nsuffix = be->be_nsuffix;
1804 /* count the dynamic objects first */
1805 rc = dds_count( thrctx, be );
1806 if ( rc != LDAP_SUCCESS ) {
1811 /* start expire task */
1812 ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
1813 di->di_expire_task = ldap_pvt_runqueue_insert( &slapd_rq,
1815 dds_expire_fn, di, "dds_expire_fn",
1816 be->be_suffix[ 0 ].bv_val );
1817 ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
1819 /* register dinamicSubtrees root DSE info support */
1820 rc = entry_info_register( dds_entry_info, (void *)di );
1832 slap_overinst *on = (slap_overinst *)be->bd_info;
1833 dds_info_t *di = on->on_bi.bi_private;
1835 /* stop expire task */
1836 if ( di && di->di_expire_task ) {
1837 ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
1838 if ( ldap_pvt_runqueue_isrunning( &slapd_rq, di->di_expire_task ) ) {
1839 ldap_pvt_runqueue_stoptask( &slapd_rq, di->di_expire_task );
1841 ldap_pvt_runqueue_remove( &slapd_rq, di->di_expire_task );
1842 ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
1843 di->di_expire_task = NULL;
1846 (void)entry_info_unregister( dds_entry_info, (void *)di );
1856 slap_overinst *on = (slap_overinst *)be->bd_info;
1857 dds_info_t *di = on->on_bi.bi_private;
1860 ldap_pvt_thread_mutex_destroy( &di->di_mutex );
1873 BackendDB *bd = op->o_bd;
1875 rs->sr_err = slap_parse_refresh( op->ore_reqdata, &op->o_req_ndn, NULL,
1876 &rs->sr_text, op->o_tmpmemctx );
1877 if ( rs->sr_err != LDAP_SUCCESS ) {
1881 Log2( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
1882 "%s REFRESH dn=\"%s\"\n",
1883 op->o_log_prefix, op->o_req_ndn.bv_val );
1884 op->o_req_dn = op->o_req_ndn;
1886 op->o_bd = select_backend( &op->o_req_ndn, 0 );
1887 if ( op->o_bd == NULL ) {
1888 send_ldap_error( op, rs, LDAP_NO_SUCH_OBJECT,
1889 "no global superior knowledge" );
1893 if ( !SLAP_DYNAMIC( op->o_bd ) ) {
1894 send_ldap_error( op, rs, LDAP_UNAVAILABLE_CRITICAL_EXTENSION,
1895 "backend does not support dynamic directory services" );
1899 rs->sr_err = backend_check_restrictions( op, rs,
1900 (struct berval *)&slap_EXOP_REFRESH );
1901 if ( rs->sr_err != LDAP_SUCCESS ) {
1905 if ( op->o_bd->be_extended == NULL ) {
1906 send_ldap_error( op, rs, LDAP_UNAVAILABLE_CRITICAL_EXTENSION,
1907 "backend does not support extended operations" );
1911 op->o_bd->be_extended( op, rs );
1914 if ( !BER_BVISNULL( &op->o_req_ndn ) ) {
1915 op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx );
1916 BER_BVZERO( &op->o_req_ndn );
1917 BER_BVZERO( &op->o_req_dn );
1924 static slap_overinst dds;
1926 static int do_not_load_exop;
1927 static int do_not_replace_exop;
1928 static int do_not_load_schema;
1930 #if SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC
1932 #endif /* SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC */
1939 /* Make sure we don't exceed the bits reserved for userland */
1940 config_check_userland( DDS_LAST );
1942 if ( !do_not_load_schema ) {
1946 AttributeDescription **ad;
1948 { "( 1.3.6.1.4.1.4203.666.1.57 "
1949 "NAME ( 'entryExpireTimestamp' ) "
1950 "DESC 'RFC2589 OpenLDAP extension: expire time of a dynamic object, "
1951 "computed as now + entryTtl' "
1952 "EQUALITY generalizedTimeMatch "
1953 "ORDERING generalizedTimeOrderingMatch "
1954 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
1956 "NO-USER-MODIFICATION "
1957 "USAGE dSAOperation )",
1959 &ad_entryExpireTimestamp },
1963 for ( i = 0; s_at[ i ].desc != NULL; i++ ) {
1964 code = register_at( s_at[ i ].desc, s_at[ i ].ad, 0 );
1966 Debug( LDAP_DEBUG_ANY,
1967 "dds_initialize: register_at failed\n", 0, 0, 0 );
1970 (*s_at[ i ].ad)->ad_type->sat_flags |= SLAP_AT_HIDE;
1974 if ( !do_not_load_exop ) {
1975 rc = load_extop2( (struct berval *)&slap_EXOP_REFRESH,
1976 SLAP_EXOP_WRITES|SLAP_EXOP_HIDE, slap_exop_refresh,
1977 !do_not_replace_exop );
1978 if ( rc != LDAP_SUCCESS ) {
1979 Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1980 "DDS unable to register refresh exop: %d.\n",
1986 dds.on_bi.bi_type = "dds";
1988 dds.on_bi.bi_db_init = dds_db_init;
1989 dds.on_bi.bi_db_open = dds_db_open;
1990 dds.on_bi.bi_db_close = dds_db_close;
1991 dds.on_bi.bi_db_destroy = dds_db_destroy;
1993 dds.on_bi.bi_op_add = dds_op_add;
1994 dds.on_bi.bi_op_delete = dds_op_delete;
1995 dds.on_bi.bi_op_modify = dds_op_modify;
1996 dds.on_bi.bi_op_modrdn = dds_op_rename;
1997 dds.on_bi.bi_extended = dds_op_extended;
1998 dds.on_response = dds_response;
2000 dds.on_bi.bi_cf_ocs = dds_ocs;
2002 rc = config_register_schema( dds_cfg, dds_ocs );
2007 return overlay_register( &dds );
2010 #if SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC
2012 init_module( int argc, char *argv[] )
2016 for ( i = 0; i < argc; i++ ) {
2017 char *arg = argv[ i ];
2020 if ( strncasecmp( arg, "no-", STRLENOF( "no-" ) ) == 0 ) {
2021 arg += STRLENOF( "no-" );
2025 if ( strcasecmp( arg, "exop" ) == 0 ) {
2026 do_not_load_exop = no;
2028 } else if ( strcasecmp( arg, "replace" ) == 0 ) {
2029 do_not_replace_exop = no;
2031 } else if ( strcasecmp( arg, "schema" ) == 0 ) {
2032 do_not_load_schema = no;
2035 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
2036 "DDS unknown module arg[#%d]=\"%s\".\n",
2042 return dds_initialize();
2044 #endif /* SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC */
2046 #endif /* defined(SLAPD_OVER_DDS) */