xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/overlays/dds.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
1 /*	$NetBSD: dds.c,v 1.3 2021/08/14 16:15:02 christos Exp $	*/
2 
3 /* $OpenLDAP$ */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5  *
6  * Copyright 2005-2021 The OpenLDAP Foundation.
7  * Portions Copyright 2005-2006 SysNet s.n.c.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted only as authorized by the OpenLDAP
12  * Public License.
13  *
14  * A copy of this license is available in the file LICENSE in the
15  * top-level directory of the distribution or, alternatively, at
16  * <http://www.OpenLDAP.org/license.html>.
17  */
18 /* ACKNOWLEDGEMENTS:
19  * This work was initially developed by Pierangelo Masarati for inclusion
20  * in OpenLDAP Software, sponsored by SysNet s.n.c.
21  */
22 
23 #include <sys/cdefs.h>
24 __RCSID("$NetBSD: dds.c,v 1.3 2021/08/14 16:15:02 christos Exp $");
25 
26 #include "portable.h"
27 
28 #ifdef SLAPD_OVER_DDS
29 
30 #include <stdio.h>
31 
32 #include <ac/string.h>
33 #include <ac/time.h>
34 
35 #include "slap.h"
36 #include "lutil.h"
37 #include "ldap_rq.h"
38 
39 #include "slap-config.h"
40 
41 #define	DDS_RF2589_MAX_TTL		(31557600)	/* 1 year + 6 hours */
42 #define	DDS_RF2589_DEFAULT_TTL		(86400)		/* 1 day */
43 #define	DDS_DEFAULT_INTERVAL		(3600)		/* 1 hour */
44 
45 typedef struct dds_info_t {
46 	unsigned		di_flags;
47 #define	DDS_FOFF		(0x1U)		/* is this really needed? */
48 #define	DDS_SET(di, f)		( (di)->di_flags & (f) )
49 
50 #define DDS_OFF(di)		DDS_SET( (di), DDS_FOFF )
51 
52 	time_t			di_max_ttl;
53 	time_t			di_min_ttl;
54 	time_t			di_default_ttl;
55 #define	DDS_DEFAULT_TTL(di)	\
56 	( (di)->di_default_ttl ? (di)->di_default_ttl : (di)->di_max_ttl )
57 
58 	time_t			di_tolerance;
59 
60 	/* expire check interval and task */
61 	time_t			di_interval;
62 #define	DDS_INTERVAL(di)	\
63 	( (di)->di_interval ? (di)->di_interval : DDS_DEFAULT_INTERVAL )
64 	struct re_s		*di_expire_task;
65 
66 	/* allows to limit the maximum number of dynamic objects */
67 	ldap_pvt_thread_mutex_t	di_mutex;
68 	int			di_num_dynamicObjects;
69 	int			di_max_dynamicObjects;
70 
71 	/* used to advertise the dynamicSubtrees in the root DSE,
72 	 * and to select the database in the expiration task */
73 	BerVarray		di_suffix;
74 	BerVarray		di_nsuffix;
75 } dds_info_t;
76 
77 static struct berval slap_EXOP_REFRESH = BER_BVC( LDAP_EXOP_REFRESH );
78 static AttributeDescription	*ad_entryExpireTimestamp;
79 
80 /* list of expired DNs */
81 typedef struct dds_expire_t {
82 	struct berval		de_ndn;
83 	struct dds_expire_t	*de_next;
84 } dds_expire_t;
85 
86 typedef struct dds_cb_t {
87 	dds_expire_t	*dc_ndnlist;
88 } dds_cb_t;
89 
90 static int
dds_expire_cb(Operation * op,SlapReply * rs)91 dds_expire_cb( Operation *op, SlapReply *rs )
92 {
93 	dds_cb_t	*dc = (dds_cb_t *)op->o_callback->sc_private;
94 	dds_expire_t	*de;
95 	int		rc;
96 
97 	switch ( rs->sr_type ) {
98 	case REP_SEARCH:
99 		/* alloc list and buffer for berval all in one */
100 		de = op->o_tmpalloc( sizeof( dds_expire_t ) + rs->sr_entry->e_nname.bv_len + 1,
101 			op->o_tmpmemctx );
102 
103 		de->de_next = dc->dc_ndnlist;
104 		dc->dc_ndnlist = de;
105 
106 		de->de_ndn.bv_len = rs->sr_entry->e_nname.bv_len;
107 		de->de_ndn.bv_val = (char *)&de[ 1 ];
108 		AC_MEMCPY( de->de_ndn.bv_val, rs->sr_entry->e_nname.bv_val,
109 			rs->sr_entry->e_nname.bv_len + 1 );
110 		rc = 0;
111 		break;
112 
113 	case REP_SEARCHREF:
114 	case REP_RESULT:
115 		rc = rs->sr_err;
116 		break;
117 
118 	default:
119 		assert( 0 );
120 	}
121 
122 	return rc;
123 }
124 
125 static int
dds_expire(void * ctx,dds_info_t * di)126 dds_expire( void *ctx, dds_info_t *di )
127 {
128 	Connection	conn = { 0 };
129 	OperationBuffer opbuf;
130 	Operation	*op;
131 	slap_callback	sc = { 0 };
132 	dds_cb_t	dc = { 0 };
133 	dds_expire_t	*de = NULL, **dep;
134 	SlapReply	rs = { REP_RESULT };
135 
136 	time_t		expire;
137 	char		tsbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
138 	struct berval	ts;
139 
140 	int		ndeletes, ntotdeletes;
141 
142 	int		rc;
143 	char		*extra = "";
144 
145 	connection_fake_init2( &conn, &opbuf, ctx, 0 );
146 	op = &opbuf.ob_op;
147 
148 	op->o_tag = LDAP_REQ_SEARCH;
149 	memset( &op->oq_search, 0, sizeof( op->oq_search ) );
150 
151 	op->o_bd = select_backend( &di->di_nsuffix[ 0 ], 0 );
152 
153 	op->o_req_dn = op->o_bd->be_suffix[ 0 ];
154 	op->o_req_ndn = op->o_bd->be_nsuffix[ 0 ];
155 
156 	op->o_dn = op->o_bd->be_rootdn;
157 	op->o_ndn = op->o_bd->be_rootndn;
158 
159 	op->ors_scope = LDAP_SCOPE_SUBTREE;
160 	op->ors_tlimit = DDS_INTERVAL( di )/2 + 1;
161 	op->ors_slimit = SLAP_NO_LIMIT;
162 	op->ors_attrs = slap_anlist_no_attrs;
163 	op->o_do_not_cache = 1;
164 
165 	expire = slap_get_time() - di->di_tolerance;
166 	ts.bv_val = tsbuf;
167 	ts.bv_len = sizeof( tsbuf );
168 	slap_timestamp( &expire, &ts );
169 
170 	op->ors_filterstr.bv_len = STRLENOF( "(&(objectClass=" ")(" "<=" "))" )
171 		+ slap_schema.si_oc_dynamicObject->soc_cname.bv_len
172 		+ ad_entryExpireTimestamp->ad_cname.bv_len
173 		+ ts.bv_len;
174 	op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
175 	snprintf( op->ors_filterstr.bv_val, op->ors_filterstr.bv_len + 1,
176 		"(&(objectClass=%s)(%s<=%s))",
177 		slap_schema.si_oc_dynamicObject->soc_cname.bv_val,
178 		ad_entryExpireTimestamp->ad_cname.bv_val, ts.bv_val );
179 
180 	op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
181 	if ( op->ors_filter == NULL ) {
182 		rs.sr_err = LDAP_OTHER;
183 		goto done_search;
184 	}
185 
186 	op->o_callback = &sc;
187 	sc.sc_response = dds_expire_cb;
188 	sc.sc_private = &dc;
189 
190 	(void)op->o_bd->bd_info->bi_op_search( op, &rs );
191 
192 done_search:;
193 	op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
194 	filter_free_x( op, op->ors_filter, 1 );
195 
196 	rc = rs.sr_err;
197 	switch ( rs.sr_err ) {
198 	case LDAP_SUCCESS:
199 		break;
200 
201 	case LDAP_NO_SUCH_OBJECT:
202 		/* (ITS#5267) database not created yet? */
203 		rs.sr_err = LDAP_SUCCESS;
204 		extra = " (ignored)";
205 		/* fallthru */
206 
207 	default:
208 		Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
209 			"DDS expired objects lookup failed err=%d%s\n",
210 			rc, extra );
211 		goto done;
212 	}
213 
214 	op->o_tag = LDAP_REQ_DELETE;
215 	op->o_callback = &sc;
216 	sc.sc_response = slap_null_cb;
217 	sc.sc_private = NULL;
218 
219 	for ( ntotdeletes = 0, ndeletes = 1; dc.dc_ndnlist != NULL  && ndeletes > 0; ) {
220 		ndeletes = 0;
221 
222 		for ( dep = &dc.dc_ndnlist; *dep != NULL; ) {
223 			de = *dep;
224 
225 			op->o_req_dn = de->de_ndn;
226 			op->o_req_ndn = de->de_ndn;
227 			(void)op->o_bd->bd_info->bi_op_delete( op, &rs );
228 			switch ( rs.sr_err ) {
229 			case LDAP_SUCCESS:
230 				Log( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
231 					"DDS dn=\"%s\" expired.\n",
232 					de->de_ndn.bv_val );
233 				ndeletes++;
234 				break;
235 
236 			case LDAP_NOT_ALLOWED_ON_NONLEAF:
237 				Log( LDAP_DEBUG_ANY, LDAP_LEVEL_NOTICE,
238 					"DDS dn=\"%s\" is non-leaf; "
239 					"deferring.\n",
240 					de->de_ndn.bv_val );
241 				dep = &de->de_next;
242 				de = NULL;
243 				break;
244 
245 			default:
246 				Log( LDAP_DEBUG_ANY, LDAP_LEVEL_NOTICE,
247 					"DDS dn=\"%s\" err=%d; "
248 					"deferring.\n",
249 					de->de_ndn.bv_val, rs.sr_err );
250 				break;
251 			}
252 
253 			if ( de != NULL ) {
254 				*dep = de->de_next;
255 				op->o_tmpfree( de, op->o_tmpmemctx );
256 			}
257 		}
258 
259 		ntotdeletes += ndeletes;
260 	}
261 
262 	rs.sr_err = LDAP_SUCCESS;
263 
264 	Log( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
265 		"DDS expired=%d\n", ntotdeletes );
266 
267 done:;
268 	return rs.sr_err;
269 }
270 
271 static void *
dds_expire_fn(void * ctx,void * arg)272 dds_expire_fn( void *ctx, void *arg )
273 {
274 	struct re_s     *rtask = arg;
275 	dds_info_t	*di = rtask->arg;
276 
277 	assert( di->di_expire_task == rtask );
278 
279 	(void)dds_expire( ctx, di );
280 
281 	ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
282 	if ( ldap_pvt_runqueue_isrunning( &slapd_rq, rtask )) {
283 		ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
284 	}
285 	ldap_pvt_runqueue_resched( &slapd_rq, rtask, 0 );
286 	ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
287 
288 	return NULL;
289 }
290 
291 /* frees the callback */
292 static int
dds_freeit_cb(Operation * op,SlapReply * rs)293 dds_freeit_cb( Operation *op, SlapReply *rs )
294 {
295 	op->o_tmpfree( op->o_callback, op->o_tmpmemctx );
296 	op->o_callback = NULL;
297 
298 	return SLAP_CB_CONTINUE;
299 }
300 
301 /* updates counter - installed on add/delete only if required */
302 static int
dds_counter_cb(Operation * op,SlapReply * rs)303 dds_counter_cb( Operation *op, SlapReply *rs )
304 {
305 	assert( rs->sr_type == REP_RESULT );
306 
307 	if ( rs->sr_err == LDAP_SUCCESS ) {
308 		dds_info_t	*di = op->o_callback->sc_private;
309 
310 		ldap_pvt_thread_mutex_lock( &di->di_mutex );
311 		switch ( op->o_tag ) {
312 		case LDAP_REQ_DELETE:
313 			assert( di->di_num_dynamicObjects > 0 );
314 			di->di_num_dynamicObjects--;
315 			break;
316 
317 		case LDAP_REQ_ADD:
318 			assert( di->di_num_dynamicObjects < di->di_max_dynamicObjects );
319 			di->di_num_dynamicObjects++;
320 			break;
321 
322 		default:
323 			assert( 0 );
324 		}
325 		ldap_pvt_thread_mutex_unlock( &di->di_mutex );
326 	}
327 
328 	return dds_freeit_cb( op, rs );
329 }
330 
331 static int
dds_op_add(Operation * op,SlapReply * rs)332 dds_op_add( Operation *op, SlapReply *rs )
333 {
334 	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
335 	dds_info_t	*di = on->on_bi.bi_private;
336 	int		is_dynamicObject;
337 
338 	if ( DDS_OFF( di ) ) {
339 		return SLAP_CB_CONTINUE;
340 	}
341 
342 	is_dynamicObject = is_entry_dynamicObject( op->ora_e );
343 
344 	/* FIXME: do not allow this right now, pending clarification */
345 	if ( is_dynamicObject ) {
346 		rs->sr_err = LDAP_SUCCESS;
347 
348 		if ( is_entry_referral( op->ora_e ) ) {
349 			rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
350 			rs->sr_text = "a referral cannot be a dynamicObject";
351 
352 		} else if ( is_entry_alias( op->ora_e ) ) {
353 			rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
354 			rs->sr_text = "an alias cannot be a dynamicObject";
355 		}
356 
357 		if ( rs->sr_err != LDAP_SUCCESS ) {
358 			op->o_bd->bd_info = (BackendInfo *)on->on_info;
359 			send_ldap_result( op, rs );
360 			return rs->sr_err;
361 		}
362 	}
363 
364 	/* we don't allow dynamicObjects to have static subordinates */
365 	if ( !dn_match( &op->o_req_ndn, &op->o_bd->be_nsuffix[ 0 ] ) ) {
366 		struct berval	p_ndn;
367 		Entry		*e = NULL;
368 		int		rc;
369 		BackendInfo	*bi = op->o_bd->bd_info;
370 
371 		dnParent( &op->o_req_ndn, &p_ndn );
372 		op->o_bd->bd_info = (BackendInfo *)on->on_info;
373 		rc = be_entry_get_rw( op, &p_ndn,
374 			slap_schema.si_oc_dynamicObject, NULL, 0, &e );
375 		if ( rc == LDAP_SUCCESS && e != NULL ) {
376 			if ( !is_dynamicObject ) {
377 				/* return referral only if "disclose"
378 				 * is granted on the object */
379 				if ( ! access_allowed( op, e,
380 						slap_schema.si_ad_entry,
381 						NULL, ACL_DISCLOSE, NULL ) )
382 				{
383 					rc = rs->sr_err = LDAP_NO_SUCH_OBJECT;
384 					send_ldap_result( op, rs );
385 
386 				} else {
387 					rc = rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
388 					send_ldap_error( op, rs, rc, "no static subordinate entries allowed for dynamicObject" );
389 				}
390 			}
391 
392 			be_entry_release_r( op, e );
393 			if ( rc != LDAP_SUCCESS ) {
394 				return rc;
395 			}
396 		}
397 		op->o_bd->bd_info = bi;
398 	}
399 
400 	/* handle dynamic object operational attr(s) */
401 	if ( is_dynamicObject ) {
402 		time_t		ttl, expire;
403 		char		ttlbuf[STRLENOF("31557600") + 1];
404 		char		tsbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
405 		struct berval	bv;
406 
407 		if ( !be_isroot_dn( op->o_bd, &op->o_req_ndn ) ) {
408 			ldap_pvt_thread_mutex_lock( &di->di_mutex );
409 			rs->sr_err = ( di->di_max_dynamicObjects &&
410 				di->di_num_dynamicObjects >= di->di_max_dynamicObjects );
411 			ldap_pvt_thread_mutex_unlock( &di->di_mutex );
412 			if ( rs->sr_err ) {
413 				op->o_bd->bd_info = (BackendInfo *)on->on_info;
414 				send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
415 					"too many dynamicObjects in context" );
416 				return rs->sr_err;
417 			}
418 		}
419 
420 		ttl = DDS_DEFAULT_TTL( di );
421 
422 		/* assert because should be checked at configure */
423 		assert( ttl <= DDS_RF2589_MAX_TTL );
424 
425 		bv.bv_val = ttlbuf;
426 		bv.bv_len = snprintf( ttlbuf, sizeof( ttlbuf ), "%ld", ttl );
427 		assert( bv.bv_len < sizeof( ttlbuf ) );
428 
429 		/* FIXME: apparently, values in op->ora_e are malloc'ed
430 		 * on the thread's slab; works fine by chance,
431 		 * only because the attribute doesn't exist yet. */
432 		assert( attr_find( op->ora_e->e_attrs, slap_schema.si_ad_entryTtl ) == NULL );
433 		attr_merge_one( op->ora_e, slap_schema.si_ad_entryTtl, &bv, &bv );
434 
435 		expire = slap_get_time() + ttl;
436 		bv.bv_val = tsbuf;
437 		bv.bv_len = sizeof( tsbuf );
438 		slap_timestamp( &expire, &bv );
439 		assert( attr_find( op->ora_e->e_attrs, ad_entryExpireTimestamp ) == NULL );
440 		attr_merge_one( op->ora_e, ad_entryExpireTimestamp, &bv, &bv );
441 
442 		/* if required, install counter callback */
443 		if ( di->di_max_dynamicObjects > 0) {
444 			slap_callback	*sc;
445 
446 			sc = op->o_tmpalloc( sizeof( slap_callback ), op->o_tmpmemctx );
447 			sc->sc_cleanup = dds_freeit_cb;
448 			sc->sc_response = dds_counter_cb;
449 			sc->sc_private = di;
450 			sc->sc_next = op->o_callback;
451 			sc->sc_writewait = 0;
452 
453 			op->o_callback = sc;
454 		}
455 	}
456 
457 	return SLAP_CB_CONTINUE;
458 }
459 
460 static int
dds_op_delete(Operation * op,SlapReply * rs)461 dds_op_delete( Operation *op, SlapReply *rs )
462 {
463 	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
464 	dds_info_t	*di = on->on_bi.bi_private;
465 
466 	/* if required, install counter callback */
467 	if ( !DDS_OFF( di ) && di->di_max_dynamicObjects > 0 ) {
468 		Entry		*e = NULL;
469 		BackendInfo	*bi = op->o_bd->bd_info;
470 
471 		op->o_bd->bd_info = (BackendInfo *)on->on_info;
472 		rs->sr_err = be_entry_get_rw( op, &op->o_req_ndn,
473 			slap_schema.si_oc_dynamicObject, NULL, 0, &e );
474 
475 		/* FIXME: couldn't the entry be added before deletion? */
476 		if ( rs->sr_err == LDAP_SUCCESS && e != NULL ) {
477 			slap_callback	*sc;
478 
479 			be_entry_release_r( op, e );
480 			e = NULL;
481 
482 			sc = op->o_tmpalloc( sizeof( slap_callback ), op->o_tmpmemctx );
483 			sc->sc_cleanup = dds_freeit_cb;
484 			sc->sc_response = dds_counter_cb;
485 			sc->sc_private = di;
486 			sc->sc_writewait = 0;
487 			sc->sc_next = op->o_callback;
488 
489 			op->o_callback = sc;
490 		}
491 		op->o_bd->bd_info = bi;
492 	}
493 
494 	return SLAP_CB_CONTINUE;
495 }
496 
497 static int
dds_op_modify(Operation * op,SlapReply * rs)498 dds_op_modify( Operation *op, SlapReply *rs )
499 {
500 	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
501 	dds_info_t	*di = (dds_info_t *)on->on_bi.bi_private;
502 	Modifications	*mod;
503 	Entry		*e = NULL;
504 	BackendInfo	*bi = op->o_bd->bd_info;
505 	int		was_dynamicObject = 0,
506 			is_dynamicObject = 0;
507 	struct berval	bv_entryTtl = BER_BVNULL;
508 	time_t		entryTtl = 0;
509 	char		textbuf[ SLAP_TEXT_BUFLEN ];
510 
511 	if ( DDS_OFF( di ) ) {
512 		return SLAP_CB_CONTINUE;
513 	}
514 
515 	/* bv_entryTtl stores the string representation of the entryTtl
516 	 * across modifies for consistency checks of the final value;
517 	 * the bv_val points to a static buffer; the bv_len is zero when
518 	 * the attribute is deleted.
519 	 * entryTtl stores the integer representation of the entryTtl;
520 	 * its value is -1 when the attribute is deleted; it is 0 only
521 	 * if no modifications of the entryTtl occurred, as an entryTtl
522 	 * of 0 is invalid. */
523 	bv_entryTtl.bv_val = textbuf;
524 
525 	op->o_bd->bd_info = (BackendInfo *)on->on_info;
526 	rs->sr_err = be_entry_get_rw( op, &op->o_req_ndn,
527 		slap_schema.si_oc_dynamicObject, slap_schema.si_ad_entryTtl, 0, &e );
528 	if ( rs->sr_err == LDAP_SUCCESS && e != NULL ) {
529 		Attribute	*a = attr_find( e->e_attrs, slap_schema.si_ad_entryTtl );
530 
531 		/* the value of the entryTtl is saved for later checks */
532 		if ( a != NULL ) {
533 			unsigned long	ttl;
534 			int		rc;
535 
536 			bv_entryTtl.bv_len = a->a_nvals[ 0 ].bv_len;
537 			AC_MEMCPY( bv_entryTtl.bv_val, a->a_nvals[ 0 ].bv_val, bv_entryTtl.bv_len );
538 			bv_entryTtl.bv_val[ bv_entryTtl.bv_len ] = '\0';
539 			rc = lutil_atoul( &ttl, bv_entryTtl.bv_val );
540 			assert( rc == 0 );
541 			entryTtl = (time_t)ttl;
542 		}
543 
544 		be_entry_release_r( op, e );
545 		e = NULL;
546 		was_dynamicObject = is_dynamicObject = 1;
547 	}
548 	op->o_bd->bd_info = bi;
549 
550 	rs->sr_err = LDAP_SUCCESS;
551 	for ( mod = op->orm_modlist; mod; mod = mod->sml_next ) {
552 		if ( mod->sml_desc == slap_schema.si_ad_objectClass ) {
553 			int		i;
554 			ObjectClass	*oc;
555 
556 			switch ( mod->sml_op ) {
557 			case LDAP_MOD_DELETE:
558 				if ( mod->sml_values == NULL ) {
559 					is_dynamicObject = 0;
560 					break;
561 				}
562 
563 				for ( i = 0; !BER_BVISNULL( &mod->sml_values[ i ] ); i++ ) {
564 					oc = oc_bvfind( &mod->sml_values[ i ] );
565 					if ( oc == slap_schema.si_oc_dynamicObject ) {
566 						is_dynamicObject = 0;
567 						break;
568 					}
569 				}
570 
571 				break;
572 
573 			case LDAP_MOD_REPLACE:
574 				if ( mod->sml_values == NULL ) {
575 					is_dynamicObject = 0;
576 					break;
577 				}
578 				/* fallthru */
579 
580 			case LDAP_MOD_ADD:
581 				for ( i = 0; !BER_BVISNULL( &mod->sml_values[ i ] ); i++ ) {
582 					oc = oc_bvfind( &mod->sml_values[ i ] );
583 					if ( oc == slap_schema.si_oc_dynamicObject ) {
584 						is_dynamicObject = 1;
585 						break;
586 					}
587 				}
588 				break;
589 			}
590 
591 		} else if ( mod->sml_desc == slap_schema.si_ad_entryTtl ) {
592 			unsigned long	uttl;
593 			time_t		ttl;
594 			int		rc;
595 
596 			switch ( mod->sml_op ) {
597 			case LDAP_MOD_DELETE:
598 			case SLAP_MOD_SOFTDEL: /* FIXME? */
599 				if ( mod->sml_values != NULL ) {
600 					if ( BER_BVISEMPTY( &bv_entryTtl )
601 						|| !bvmatch( &bv_entryTtl, &mod->sml_values[ 0 ] ) )
602 					{
603 						rs->sr_err = backend_attribute( op, NULL, &op->o_req_ndn,
604 							slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
605 						if ( rs->sr_err == LDAP_INSUFFICIENT_ACCESS ) {
606 							rs->sr_err = LDAP_NO_SUCH_OBJECT;
607 
608 						} else {
609 							rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE;
610 						}
611 						goto done;
612 					}
613 				}
614 				bv_entryTtl.bv_len = 0;
615 				entryTtl = -1;
616 				break;
617 
618 			case LDAP_MOD_REPLACE:
619 				bv_entryTtl.bv_len = 0;
620 				entryTtl = -1;
621 				/* fallthru */
622 
623 			case LDAP_MOD_ADD:
624 			case SLAP_MOD_SOFTADD: /* FIXME? */
625 			case SLAP_MOD_ADD_IF_NOT_PRESENT: /* FIXME? */
626 				assert( mod->sml_values != NULL );
627 				assert( BER_BVISNULL( &mod->sml_values[ 1 ] ) );
628 
629 				if ( !BER_BVISEMPTY( &bv_entryTtl ) ) {
630 					rs->sr_err = backend_attribute( op, NULL, &op->o_req_ndn,
631 						slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
632 					if ( rs->sr_err == LDAP_INSUFFICIENT_ACCESS ) {
633 						rs->sr_err = LDAP_NO_SUCH_OBJECT;
634 
635 					} else {
636 						rs->sr_text = "attribute 'entryTtl' cannot have multiple values";
637 						rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
638 					}
639 					goto done;
640 				}
641 
642 				rc = lutil_atoul( &uttl, mod->sml_values[ 0 ].bv_val );
643 				ttl = (time_t)uttl;
644 				assert( rc == 0 );
645 				if ( ttl > DDS_RF2589_MAX_TTL ) {
646 					rs->sr_err = LDAP_PROTOCOL_ERROR;
647 					rs->sr_text = "invalid time-to-live for dynamicObject";
648 					goto done;
649 				}
650 
651 				if ( ttl <= 0 || ttl > di->di_max_ttl ) {
652 					/* FIXME: I don't understand if this has to be an error,
653 					 * or an indication that the requested Ttl has been
654 					 * shortened to di->di_max_ttl >= 1 day */
655 					rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
656 					rs->sr_text = "time-to-live for dynamicObject exceeds administrative limit";
657 					goto done;
658 				}
659 
660 				entryTtl = ttl;
661 				bv_entryTtl.bv_len = mod->sml_values[ 0 ].bv_len;
662 				AC_MEMCPY( bv_entryTtl.bv_val, mod->sml_values[ 0 ].bv_val, bv_entryTtl.bv_len );
663 				bv_entryTtl.bv_val[ bv_entryTtl.bv_len ] = '\0';
664 				break;
665 
666 			case LDAP_MOD_INCREMENT:
667 				if ( BER_BVISEMPTY( &bv_entryTtl ) ) {
668 					rs->sr_err = backend_attribute( op, NULL, &op->o_req_ndn,
669 						slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
670 					if ( rs->sr_err == LDAP_INSUFFICIENT_ACCESS ) {
671 						rs->sr_err = LDAP_NO_SUCH_OBJECT;
672 
673 					} else {
674 						rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE;
675 						rs->sr_text = "modify/increment: entryTtl: no such attribute";
676 					}
677 					goto done;
678 				}
679 
680 				entryTtl++;
681 				if ( entryTtl > DDS_RF2589_MAX_TTL ) {
682 					rs->sr_err = LDAP_PROTOCOL_ERROR;
683 					rs->sr_text = "invalid time-to-live for dynamicObject";
684 
685 				} else if ( entryTtl <= 0 || entryTtl > di->di_max_ttl ) {
686 					/* FIXME: I don't understand if this has to be an error,
687 					 * or an indication that the requested Ttl has been
688 					 * shortened to di->di_max_ttl >= 1 day */
689 					rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
690 					rs->sr_text = "time-to-live for dynamicObject exceeds administrative limit";
691 				}
692 
693 				if ( rs->sr_err != LDAP_SUCCESS ) {
694 					rc = backend_attribute( op, NULL, &op->o_req_ndn,
695 						slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
696 					if ( rc == LDAP_INSUFFICIENT_ACCESS ) {
697 						rs->sr_text = NULL;
698 						rs->sr_err = LDAP_NO_SUCH_OBJECT;
699 
700 					}
701 					goto done;
702 				}
703 
704 				bv_entryTtl.bv_len = snprintf( textbuf, sizeof( textbuf ), "%ld", entryTtl );
705 				break;
706 
707 			default:
708 				assert( 0 );
709 				break;
710 			}
711 
712 		} else if ( mod->sml_desc == ad_entryExpireTimestamp ) {
713 			/* should have been trapped earlier */
714 			assert( mod->sml_flags & SLAP_MOD_INTERNAL );
715 		}
716 	}
717 
718 done:;
719 	if ( rs->sr_err == LDAP_SUCCESS ) {
720 		int	rc;
721 
722 		/* FIXME: this could be allowed when the Relax control is used...
723 		 * in that case:
724 		 *
725 		 * TODO
726 		 *
727 		 *	static => dynamic:
728 		 *		entryTtl must be provided; add
729 		 *		entryExpireTimestamp accordingly
730 		 *
731 		 *	dynamic => static:
732 		 *		entryTtl must be removed; remove
733 		 *		entryTimestamp accordingly
734 		 *
735 		 * ... but we need to make sure that there are no subordinate
736 		 * issues...
737 		 */
738 		rc = is_dynamicObject - was_dynamicObject;
739 		if ( rc ) {
740 #if 0 /* fix subordinate issues first */
741 			if ( get_relax( op ) ) {
742 				switch ( rc ) {
743 				case -1:
744 					/* need to delete entryTtl to have a consistent entry */
745 					if ( entryTtl != -1 ) {
746 						rs->sr_text = "objectClass modification from dynamicObject to static entry requires entryTtl deletion";
747 						rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
748 					}
749 					break;
750 
751 				case 1:
752 					/* need to add entryTtl to have a consistent entry */
753 					if ( entryTtl <= 0 ) {
754 						rs->sr_text = "objectClass modification from static entry to dynamicObject requires entryTtl addition";
755 						rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
756 					}
757 					break;
758 				}
759 
760 			} else
761 #endif
762 			{
763 				switch ( rc ) {
764 				case -1:
765 					rs->sr_text = "objectClass modification cannot turn dynamicObject into static entry";
766 					break;
767 
768 				case 1:
769 					rs->sr_text = "objectClass modification cannot turn static entry into dynamicObject";
770 					break;
771 				}
772 				rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
773 			}
774 
775 			if ( rc != LDAP_SUCCESS ) {
776 				rc = backend_attribute( op, NULL, &op->o_req_ndn,
777 					slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
778 				if ( rc == LDAP_INSUFFICIENT_ACCESS ) {
779 					rs->sr_text = NULL;
780 					rs->sr_err = LDAP_NO_SUCH_OBJECT;
781 				}
782 			}
783 		}
784 	}
785 
786 	if ( rs->sr_err == LDAP_SUCCESS && entryTtl != 0 ) {
787 		Modifications	*tmpmod = NULL, **modp;
788 
789 		for ( modp = &op->orm_modlist; *modp; modp = &(*modp)->sml_next )
790 			;
791 
792 		tmpmod = ch_calloc( 1, sizeof( Modifications ) );
793 		tmpmod->sml_flags = SLAP_MOD_INTERNAL;
794 		tmpmod->sml_type = ad_entryExpireTimestamp->ad_cname;
795 		tmpmod->sml_desc = ad_entryExpireTimestamp;
796 
797 		*modp = tmpmod;
798 
799 		if ( entryTtl == -1 ) {
800 			/* delete entryExpireTimestamp */
801 			tmpmod->sml_op = LDAP_MOD_DELETE;
802 
803 		} else {
804 			time_t		expire;
805 			char		tsbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
806 			struct berval	bv;
807 
808 			/* keep entryExpireTimestamp consistent
809 			 * with entryTtl */
810 			expire = slap_get_time() + entryTtl;
811 			bv.bv_val = tsbuf;
812 			bv.bv_len = sizeof( tsbuf );
813 			slap_timestamp( &expire, &bv );
814 
815 			tmpmod->sml_op = LDAP_MOD_REPLACE;
816 			value_add_one( &tmpmod->sml_values, &bv );
817 			value_add_one( &tmpmod->sml_nvalues, &bv );
818 			tmpmod->sml_numvals = 1;
819 		}
820 	}
821 
822 	if ( rs->sr_err ) {
823 		op->o_bd->bd_info = (BackendInfo *)on->on_info;
824 		send_ldap_result( op, rs );
825 		return rs->sr_err;
826 	}
827 
828 	return SLAP_CB_CONTINUE;
829 }
830 
831 static int
dds_op_rename(Operation * op,SlapReply * rs)832 dds_op_rename( Operation *op, SlapReply *rs )
833 {
834 	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
835 	dds_info_t	*di = on->on_bi.bi_private;
836 
837 	if ( DDS_OFF( di ) ) {
838 		return SLAP_CB_CONTINUE;
839 	}
840 
841 	/* we don't allow dynamicObjects to have static subordinates */
842 	if ( op->orr_nnewSup != NULL ) {
843 		Entry		*e = NULL;
844 		BackendInfo	*bi = op->o_bd->bd_info;
845 		int		is_dynamicObject = 0,
846 				rc;
847 
848 		rs->sr_err = LDAP_SUCCESS;
849 
850 		op->o_bd->bd_info = (BackendInfo *)on->on_info;
851 		rc = be_entry_get_rw( op, &op->o_req_ndn,
852 			slap_schema.si_oc_dynamicObject, NULL, 0, &e );
853 		if ( rc == LDAP_SUCCESS && e != NULL ) {
854 			be_entry_release_r( op, e );
855 			e = NULL;
856 			is_dynamicObject = 1;
857 		}
858 
859 		rc = be_entry_get_rw( op, op->orr_nnewSup,
860 			slap_schema.si_oc_dynamicObject, NULL, 0, &e );
861 		if ( rc == LDAP_SUCCESS && e != NULL ) {
862 			if ( !is_dynamicObject ) {
863 				/* return referral only if "disclose"
864 				 * is granted on the object */
865 				if ( ! access_allowed( op, e,
866 						slap_schema.si_ad_entry,
867 						NULL, ACL_DISCLOSE, NULL ) )
868 				{
869 					rs->sr_err = LDAP_NO_SUCH_OBJECT;
870 					send_ldap_result( op, rs );
871 
872 				} else {
873 					send_ldap_error( op, rs, LDAP_CONSTRAINT_VIOLATION,
874 						"static entry cannot have dynamicObject as newSuperior" );
875 				}
876 			}
877 			be_entry_release_r( op, e );
878 		}
879 		op->o_bd->bd_info = bi;
880 		if ( rs->sr_err != LDAP_SUCCESS ) {
881 			return rs->sr_err;
882 		}
883 	}
884 
885 	return SLAP_CB_CONTINUE;
886 }
887 
888 /* entryTtl update for client */
889 static int
dds_response(Operation * op,SlapReply * rs)890 dds_response( Operation *op, SlapReply *rs )
891 {
892 	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
893 	dds_info_t	*di = on->on_bi.bi_private;
894 	int		rc;
895 
896 	if ( !DDS_OFF( di )
897 	     && rs->sr_type == REP_SEARCH
898 	     && attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_entryTtl ) )
899 	{
900 		BerVarray		vals = NULL;
901 		struct lutil_tm		tm;
902 		struct lutil_timet	tt;
903 		char			ttlbuf[STRLENOF("31557600") + 1];
904 		struct berval		ttlvalue;
905 		time_t			ttl;
906 		int			len;
907 
908 		/* User already has access to entryTtl, skip ACL checks on
909 		 * entryExpireTimestamp */
910 		rc = backend_attribute( op, NULL, &rs->sr_entry->e_nname,
911 			ad_entryExpireTimestamp, &vals, ACL_NONE );
912 		if ( rc != LDAP_SUCCESS ) {
913 			return rc;
914 		}
915 
916 		assert( vals[0].bv_val[vals[0].bv_len] == '\0' );
917 		if ( lutil_parsetime( vals[0].bv_val, &tm ) ) {
918 			goto done;
919 		}
920 
921 		lutil_tm2time( &tm, &tt );
922 		ttl = tt.tt_sec - op->o_time;
923 		ttl = (ttl < 0) ? 0 : ttl;
924 		assert( ttl <= DDS_RF2589_MAX_TTL );
925 
926 		len = snprintf( ttlbuf, sizeof(ttlbuf), "%ld", ttl );
927 		if ( len < 0 )
928 		{
929 			goto done;
930 		}
931 		ttlvalue.bv_val = ttlbuf;
932 		ttlvalue.bv_len = len;
933 
934 		rs_entry2modifiable( op, rs, on );
935 
936 		if ( attr_delete( &rs->sr_entry->e_attrs,
937 				slap_schema.si_ad_entryTtl ) )
938 		{
939 			goto done;
940 		}
941 		if ( attr_merge_normalize_one( rs->sr_entry,
942 				slap_schema.si_ad_entryTtl,
943 				&ttlvalue, op->o_tmpmemctx ) )
944 		{
945 			goto done;
946 		}
947 
948 done:;
949 		ber_bvarray_free_x( vals, op->o_tmpmemctx );
950 	}
951 	return SLAP_CB_CONTINUE;
952 }
953 
954 static int
slap_parse_refresh(struct berval * in,struct berval * ndn,time_t * ttl,const char ** text,void * ctx)955 slap_parse_refresh(
956 	struct berval	*in,
957 	struct berval	*ndn,
958 	time_t		*ttl,
959 	const char	**text,
960 	void		*ctx )
961 {
962 	int			rc = LDAP_SUCCESS;
963 	ber_tag_t		tag;
964 	ber_len_t		len = -1;
965 	BerElementBuffer	berbuf;
966 	BerElement		*ber = (BerElement *)&berbuf;
967 	struct berval		reqdata = BER_BVNULL;
968 	int			tmp;
969 
970 	*text = NULL;
971 
972 	if ( ndn ) {
973 		BER_BVZERO( ndn );
974 	}
975 
976 	if ( in == NULL || in->bv_len == 0 ) {
977 		*text = "empty request data field in refresh exop";
978 		return LDAP_PROTOCOL_ERROR;
979 	}
980 
981 	ber_dupbv_x( &reqdata, in, ctx );
982 
983 	/* ber_init2 uses reqdata directly, doesn't allocate new buffers */
984 	ber_init2( ber, &reqdata, 0 );
985 
986 	tag = ber_scanf( ber, "{" /*}*/ );
987 
988 	if ( tag == LBER_ERROR ) {
989 		Log( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
990 			"slap_parse_refresh: decoding error.\n" );
991 		goto decoding_error;
992 	}
993 
994 	tag = ber_peek_tag( ber, &len );
995 	if ( tag != LDAP_TAG_EXOP_REFRESH_REQ_DN ) {
996 		Log( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
997 			"slap_parse_refresh: decoding error.\n" );
998 		goto decoding_error;
999 	}
1000 
1001 	if ( ndn ) {
1002 		struct berval	dn;
1003 
1004 		tag = ber_scanf( ber, "m", &dn );
1005 		if ( tag == LBER_ERROR ) {
1006 			Log( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
1007 				"slap_parse_refresh: DN parse failed.\n" );
1008 			goto decoding_error;
1009 		}
1010 
1011 		rc = dnNormalize( 0, NULL, NULL, &dn, ndn, ctx );
1012 		if ( rc != LDAP_SUCCESS ) {
1013 			*text = "invalid DN in refresh exop request data";
1014 			goto done;
1015 		}
1016 
1017 	} else {
1018 		tag = ber_scanf( ber, "x" /* "m" */ );
1019 		if ( tag == LBER_DEFAULT ) {
1020 			goto decoding_error;
1021 		}
1022 	}
1023 
1024 	tag = ber_peek_tag( ber, &len );
1025 
1026 	if ( tag != LDAP_TAG_EXOP_REFRESH_REQ_TTL ) {
1027 		Log( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
1028 			"slap_parse_refresh: decoding error.\n" );
1029 		goto decoding_error;
1030 	}
1031 
1032 	tag = ber_scanf( ber, "i", &tmp );
1033 	if ( tag == LBER_ERROR ) {
1034 		Log( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
1035 			"slap_parse_refresh: TTL parse failed.\n" );
1036 		goto decoding_error;
1037 	}
1038 
1039 	if ( ttl ) {
1040 		*ttl = tmp;
1041 	}
1042 
1043 	tag = ber_peek_tag( ber, &len );
1044 
1045 	if ( tag != LBER_DEFAULT || len != 0 ) {
1046 decoding_error:;
1047 		Log( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
1048 			"slap_parse_refresh: decoding error, len=%ld\n",
1049 			(long)len );
1050 		rc = LDAP_PROTOCOL_ERROR;
1051 		*text = "data decoding error";
1052 
1053 done:;
1054 		if ( ndn && !BER_BVISNULL( ndn ) ) {
1055 			slap_sl_free( ndn->bv_val, ctx );
1056 			BER_BVZERO( ndn );
1057 		}
1058 	}
1059 
1060 	if ( !BER_BVISNULL( &reqdata ) ) {
1061 		ber_memfree_x( reqdata.bv_val, ctx );
1062 	}
1063 
1064 	return rc;
1065 }
1066 
1067 static int
dds_op_extended(Operation * op,SlapReply * rs)1068 dds_op_extended( Operation *op, SlapReply *rs )
1069 {
1070 	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
1071 	dds_info_t	*di = on->on_bi.bi_private;
1072 
1073 	if ( DDS_OFF( di ) ) {
1074 		return SLAP_CB_CONTINUE;
1075 	}
1076 
1077 	if ( bvmatch( &op->ore_reqoid, &slap_EXOP_REFRESH ) ) {
1078 		Entry		*e = NULL;
1079 		time_t		ttl;
1080 		BackendDB	db = *op->o_bd;
1081 		SlapReply	rs2 = { REP_RESULT };
1082 		Operation	op2 = *op;
1083 		slap_callback	sc = { 0 };
1084 		Modifications	ttlmod = { { 0 } };
1085 		struct berval	ttlvalues[ 2 ];
1086 		char		ttlbuf[STRLENOF("31557600") + 1];
1087 
1088 		rs->sr_err = slap_parse_refresh( op->ore_reqdata, NULL, &ttl,
1089 			&rs->sr_text, NULL );
1090 		assert( rs->sr_err == LDAP_SUCCESS );
1091 
1092 		if ( ttl <= 0 || ttl > DDS_RF2589_MAX_TTL ) {
1093 			rs->sr_err = LDAP_PROTOCOL_ERROR;
1094 			rs->sr_text = "invalid time-to-live for dynamicObject";
1095 			return rs->sr_err;
1096 		}
1097 
1098 		if ( ttl > di->di_max_ttl ) {
1099 			/* FIXME: I don't understand if this has to be an error,
1100 			 * or an indication that the requested Ttl has been
1101 			 * shortened to di->di_max_ttl >= 1 day */
1102 			rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
1103 			rs->sr_text = "time-to-live for dynamicObject exceeds limit";
1104 			return rs->sr_err;
1105 		}
1106 
1107 		if ( di->di_min_ttl && ttl < di->di_min_ttl ) {
1108 			ttl = di->di_min_ttl;
1109 		}
1110 
1111 		/* This does not apply to multi-provider case */
1112 		if ( !( !SLAP_SINGLE_SHADOW( op->o_bd ) || be_isupdate( op ) ) ) {
1113 			/* we SHOULD return a referral in this case */
1114 			BerVarray defref = op->o_bd->be_update_refs
1115 				? op->o_bd->be_update_refs : default_referral;
1116 
1117 			if ( defref != NULL ) {
1118 				rs->sr_ref = referral_rewrite( op->o_bd->be_update_refs,
1119 					NULL, NULL, LDAP_SCOPE_DEFAULT );
1120 				if ( rs->sr_ref ) {
1121 					rs->sr_flags |= REP_REF_MUSTBEFREED;
1122 				} else {
1123 					rs->sr_ref = defref;
1124 				}
1125 				rs->sr_err = LDAP_REFERRAL;
1126 
1127 			} else {
1128 				rs->sr_text = "shadow context; no update referral";
1129 				rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1130 			}
1131 
1132 			return rs->sr_err;
1133 		}
1134 
1135 		assert( !BER_BVISNULL( &op->o_req_ndn ) );
1136 
1137 
1138 
1139 		/* check if exists but not dynamicObject */
1140 		op->o_bd->bd_info = (BackendInfo *)on->on_info;
1141 		rs->sr_err = be_entry_get_rw( op, &op->o_req_ndn,
1142 			slap_schema.si_oc_dynamicObject, NULL, 0, &e );
1143 		if ( rs->sr_err != LDAP_SUCCESS ) {
1144 			rs->sr_err = be_entry_get_rw( op, &op->o_req_ndn,
1145 				NULL, NULL, 0, &e );
1146 			if ( rs->sr_err == LDAP_SUCCESS && e != NULL ) {
1147 				/* return referral only if "disclose"
1148 				 * is granted on the object */
1149 				if ( ! access_allowed( op, e,
1150 						slap_schema.si_ad_entry,
1151 						NULL, ACL_DISCLOSE, NULL ) )
1152 				{
1153 					rs->sr_err = LDAP_NO_SUCH_OBJECT;
1154 
1155 				} else {
1156 					rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
1157 					rs->sr_text = "refresh operation only applies to dynamic objects";
1158 				}
1159 				be_entry_release_r( op, e );
1160 
1161 			} else {
1162 				rs->sr_err = LDAP_NO_SUCH_OBJECT;
1163 			}
1164 			return rs->sr_err;
1165 
1166 		} else if ( e != NULL ) {
1167 			be_entry_release_r( op, e );
1168 		}
1169 
1170 		/* we require manage privileges on the entryTtl,
1171 		 * and fake a Relax control */
1172 		op2.o_tag = LDAP_REQ_MODIFY;
1173 		op2.o_bd = &db;
1174 		db.bd_info = (BackendInfo *)on->on_info;
1175 		op2.o_callback = &sc;
1176 		sc.sc_response = slap_null_cb;
1177 		op2.o_relax = SLAP_CONTROL_CRITICAL;
1178 		op2.orm_modlist = &ttlmod;
1179 
1180 		ttlmod.sml_op = LDAP_MOD_REPLACE;
1181 		ttlmod.sml_flags = SLAP_MOD_MANAGING;
1182 		ttlmod.sml_desc = slap_schema.si_ad_entryTtl;
1183 		ttlmod.sml_values = ttlvalues;
1184 		ttlmod.sml_numvals = 1;
1185 		ttlvalues[ 0 ].bv_val = ttlbuf;
1186 		ttlvalues[ 0 ].bv_len = snprintf( ttlbuf, sizeof( ttlbuf ), "%ld", ttl );
1187 		BER_BVZERO( &ttlvalues[ 1 ] );
1188 
1189 		/* the entryExpireTimestamp is added by modify */
1190 		rs->sr_err = op2.o_bd->be_modify( &op2, &rs2 );
1191 
1192 		if ( ttlmod.sml_next != NULL ) {
1193 			slap_mods_free( ttlmod.sml_next, 1 );
1194 		}
1195 
1196 		if ( rs->sr_err == LDAP_SUCCESS ) {
1197 			int			rc;
1198 			BerElementBuffer	berbuf;
1199 			BerElement		*ber = (BerElement *)&berbuf;
1200 
1201 			ber_init_w_nullc( ber, LBER_USE_DER );
1202 
1203 			rc = ber_printf( ber, "{tiN}", LDAP_TAG_EXOP_REFRESH_RES_TTL, (int)ttl );
1204 
1205 			if ( rc < 0 ) {
1206 				rs->sr_err = LDAP_OTHER;
1207 				rs->sr_text = "internal error";
1208 
1209 			} else {
1210 				(void)ber_flatten( ber, &rs->sr_rspdata );
1211 				rs->sr_rspoid = ch_strdup( slap_EXOP_REFRESH.bv_val );
1212 
1213 				Log( LDAP_DEBUG_TRACE, LDAP_LEVEL_INFO,
1214 					"%s REFRESH dn=\"%s\" TTL=%ld\n",
1215 					op->o_log_prefix, op->o_req_ndn.bv_val, ttl );
1216 			}
1217 
1218 			ber_free_buf( ber );
1219 		}
1220 
1221 		return rs->sr_err;
1222 	}
1223 
1224 	return SLAP_CB_CONTINUE;
1225 }
1226 
1227 enum {
1228 	DDS_STATE = 1,
1229 	DDS_MAXTTL,
1230 	DDS_MINTTL,
1231 	DDS_DEFAULTTTL,
1232 	DDS_INTERVAL,
1233 	DDS_TOLERANCE,
1234 	DDS_MAXDYNAMICOBJS,
1235 
1236 	DDS_LAST
1237 };
1238 
1239 static ConfigDriver dds_cfgen;
1240 #if 0
1241 static ConfigLDAPadd dds_ldadd;
1242 static ConfigCfAdd dds_cfadd;
1243 #endif
1244 
1245 static ConfigTable dds_cfg[] = {
1246 	{ "dds-state", "on|off",
1247 		2, 2, 0, ARG_MAGIC|ARG_ON_OFF|DDS_STATE, dds_cfgen,
1248 		"( OLcfgOvAt:9.1 NAME 'olcDDSstate' "
1249 			"DESC 'RFC2589 Dynamic directory services state' "
1250 			"EQUALITY booleanMatch "
1251 			"SYNTAX OMsBoolean "
1252 			"SINGLE-VALUE )", NULL, NULL },
1253 	{ "dds-max-ttl", "ttl",
1254 		2, 2, 0, ARG_MAGIC|DDS_MAXTTL, dds_cfgen,
1255 		"( OLcfgOvAt:9.2 NAME 'olcDDSmaxTtl' "
1256 			"DESC 'RFC2589 Dynamic directory services max TTL' "
1257 			"EQUALITY caseIgnoreMatch "
1258 			"SYNTAX OMsDirectoryString "
1259 			"SINGLE-VALUE )", NULL, NULL },
1260 	{ "dds-min-ttl", "ttl",
1261 		2, 2, 0, ARG_MAGIC|DDS_MINTTL, dds_cfgen,
1262 		"( OLcfgOvAt:9.3 NAME 'olcDDSminTtl' "
1263 			"DESC 'RFC2589 Dynamic directory services min TTL' "
1264 			"EQUALITY caseIgnoreMatch "
1265 			"SYNTAX OMsDirectoryString "
1266 			"SINGLE-VALUE )", NULL, NULL },
1267 	{ "dds-default-ttl", "ttl",
1268 		2, 2, 0, ARG_MAGIC|DDS_DEFAULTTTL, dds_cfgen,
1269 		"( OLcfgOvAt:9.4 NAME 'olcDDSdefaultTtl' "
1270 			"DESC 'RFC2589 Dynamic directory services default TTL' "
1271 			"EQUALITY caseIgnoreMatch "
1272 			"SYNTAX OMsDirectoryString "
1273 			"SINGLE-VALUE )", NULL, NULL },
1274 	{ "dds-interval", "interval",
1275 		2, 2, 0, ARG_MAGIC|DDS_INTERVAL, dds_cfgen,
1276 		"( OLcfgOvAt:9.5 NAME 'olcDDSinterval' "
1277 			"DESC 'RFC2589 Dynamic directory services expiration "
1278 				"task run interval' "
1279 			"EQUALITY caseIgnoreMatch "
1280 			"SYNTAX OMsDirectoryString "
1281 			"SINGLE-VALUE )", NULL, NULL },
1282 	{ "dds-tolerance", "ttl",
1283 		2, 2, 0, ARG_MAGIC|DDS_TOLERANCE, dds_cfgen,
1284 		"( OLcfgOvAt:9.6 NAME 'olcDDStolerance' "
1285 			"DESC 'RFC2589 Dynamic directory services additional "
1286 				"TTL in expiration scheduling' "
1287 			"EQUALITY caseIgnoreMatch "
1288 			"SYNTAX OMsDirectoryString "
1289 			"SINGLE-VALUE )", NULL, NULL },
1290 	{ "dds-max-dynamicObjects", "num",
1291 		2, 2, 0, ARG_MAGIC|ARG_INT|DDS_MAXDYNAMICOBJS, dds_cfgen,
1292 		"( OLcfgOvAt:9.7 NAME 'olcDDSmaxDynamicObjects' "
1293 			"DESC 'RFC2589 Dynamic directory services max number of dynamic objects' "
1294 			"EQUALITY integerMatch "
1295 			"SYNTAX OMsInteger "
1296 			"SINGLE-VALUE )", NULL, NULL },
1297 	{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
1298 };
1299 
1300 static ConfigOCs dds_ocs[] = {
1301 	{ "( OLcfgOvOc:9.1 "
1302 		"NAME 'olcDDSConfig' "
1303 		"DESC 'RFC2589 Dynamic directory services configuration' "
1304 		"SUP olcOverlayConfig "
1305 		"MAY ( "
1306 			"olcDDSstate "
1307 			"$ olcDDSmaxTtl "
1308 			"$ olcDDSminTtl "
1309 			"$ olcDDSdefaultTtl "
1310 			"$ olcDDSinterval "
1311 			"$ olcDDStolerance "
1312 			"$ olcDDSmaxDynamicObjects "
1313 		" ) "
1314 		")", Cft_Overlay, dds_cfg, NULL, NULL /* dds_cfadd */ },
1315 	{ NULL, 0, NULL }
1316 };
1317 
1318 #if 0
1319 static int
1320 dds_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
1321 {
1322 	return LDAP_SUCCESS;
1323 }
1324 
1325 static int
1326 dds_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
1327 {
1328 	return 0;
1329 }
1330 #endif
1331 
1332 static int
dds_cfgen(ConfigArgs * c)1333 dds_cfgen( ConfigArgs *c )
1334 {
1335 	slap_overinst	*on = (slap_overinst *)c->bi;
1336 	dds_info_t	*di = on->on_bi.bi_private;
1337 	int		rc = 0;
1338 	unsigned long	t;
1339 
1340 
1341 	if ( c->op == SLAP_CONFIG_EMIT ) {
1342 		char		buf[ SLAP_TEXT_BUFLEN ];
1343 		struct berval	bv;
1344 
1345 		switch( c->type ) {
1346 		case DDS_STATE:
1347 			c->value_int = !DDS_OFF( di );
1348 			break;
1349 
1350 		case DDS_MAXTTL:
1351 			lutil_unparse_time( buf, sizeof( buf ), di->di_max_ttl );
1352 			ber_str2bv( buf, 0, 0, &bv );
1353 			value_add_one( &c->rvalue_vals, &bv );
1354 			break;
1355 
1356 		case DDS_MINTTL:
1357 			if ( di->di_min_ttl ) {
1358 				lutil_unparse_time( buf, sizeof( buf ), di->di_min_ttl );
1359 				ber_str2bv( buf, 0, 0, &bv );
1360 				value_add_one( &c->rvalue_vals, &bv );
1361 
1362 			} else {
1363 				rc = 1;
1364 			}
1365 			break;
1366 
1367 		case DDS_DEFAULTTTL:
1368 			if ( di->di_default_ttl ) {
1369 				lutil_unparse_time( buf, sizeof( buf ), di->di_default_ttl );
1370 				ber_str2bv( buf, 0, 0, &bv );
1371 				value_add_one( &c->rvalue_vals, &bv );
1372 
1373 			} else {
1374 				rc = 1;
1375 			}
1376 			break;
1377 
1378 		case DDS_INTERVAL:
1379 			if ( di->di_interval ) {
1380 				lutil_unparse_time( buf, sizeof( buf ), di->di_interval );
1381 				ber_str2bv( buf, 0, 0, &bv );
1382 				value_add_one( &c->rvalue_vals, &bv );
1383 
1384 			} else {
1385 				rc = 1;
1386 			}
1387 			break;
1388 
1389 		case DDS_TOLERANCE:
1390 			if ( di->di_tolerance ) {
1391 				lutil_unparse_time( buf, sizeof( buf ), di->di_tolerance );
1392 				ber_str2bv( buf, 0, 0, &bv );
1393 				value_add_one( &c->rvalue_vals, &bv );
1394 
1395 			} else {
1396 				rc = 1;
1397 			}
1398 			break;
1399 
1400 		case DDS_MAXDYNAMICOBJS:
1401 			if ( di->di_max_dynamicObjects > 0 ) {
1402 				c->value_int = di->di_max_dynamicObjects;
1403 
1404 			} else {
1405 				rc = 1;
1406 			}
1407 			break;
1408 
1409 		default:
1410 			rc = 1;
1411 			break;
1412 		}
1413 
1414 		return rc;
1415 
1416 	} else if ( c->op == LDAP_MOD_DELETE ) {
1417 		switch( c->type ) {
1418 		case DDS_STATE:
1419 			di->di_flags &= ~DDS_FOFF;
1420 			break;
1421 
1422 		case DDS_MAXTTL:
1423 			di->di_min_ttl = DDS_RF2589_DEFAULT_TTL;
1424 			break;
1425 
1426 		case DDS_MINTTL:
1427 			di->di_min_ttl = 0;
1428 			break;
1429 
1430 		case DDS_DEFAULTTTL:
1431 			di->di_default_ttl = 0;
1432 			break;
1433 
1434 		case DDS_INTERVAL:
1435 			di->di_interval = 0;
1436 			break;
1437 
1438 		case DDS_TOLERANCE:
1439 			di->di_tolerance = 0;
1440 			break;
1441 
1442 		case DDS_MAXDYNAMICOBJS:
1443 			di->di_max_dynamicObjects = 0;
1444 			break;
1445 
1446 		default:
1447 			rc = 1;
1448 			break;
1449 		}
1450 
1451 		return rc;
1452 	}
1453 
1454 	switch ( c->type ) {
1455 	case DDS_STATE:
1456 		if ( c->value_int ) {
1457 			di->di_flags &= ~DDS_FOFF;
1458 
1459 		} else {
1460 			di->di_flags |= DDS_FOFF;
1461 		}
1462 		break;
1463 
1464 	case DDS_MAXTTL:
1465 		if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
1466 			snprintf( c->cr_msg, sizeof( c->cr_msg),
1467 				"DDS unable to parse dds-max-ttl \"%s\"",
1468 				c->argv[ 1 ] );
1469 			Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1470 				"%s: %s.\n", c->log, c->cr_msg );
1471 			return 1;
1472 		}
1473 
1474 		if ( t < DDS_RF2589_DEFAULT_TTL || t > DDS_RF2589_MAX_TTL ) {
1475 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1476 				"DDS invalid dds-max-ttl=%lu; must be between %d and %d",
1477 				t, DDS_RF2589_DEFAULT_TTL, DDS_RF2589_MAX_TTL );
1478 			Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1479 				"%s: %s.\n", c->log, c->cr_msg );
1480 			return 1;
1481 		}
1482 
1483 		di->di_max_ttl = (time_t)t;
1484 		break;
1485 
1486 	case DDS_MINTTL:
1487 		if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
1488 			snprintf( c->cr_msg, sizeof( c->cr_msg),
1489 				"DDS unable to parse dds-min-ttl \"%s\"",
1490 				c->argv[ 1 ] );
1491 			Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1492 				"%s: %s.\n", c->log, c->cr_msg );
1493 			return 1;
1494 		}
1495 
1496 		if ( t > DDS_RF2589_MAX_TTL ) {
1497 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1498 				"DDS invalid dds-min-ttl=%lu",
1499 				t );
1500 			Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1501 				"%s: %s.\n", c->log, c->cr_msg );
1502 			return 1;
1503 		}
1504 
1505 		if ( t == 0 ) {
1506 			di->di_min_ttl = DDS_RF2589_DEFAULT_TTL;
1507 
1508 		} else {
1509 			di->di_min_ttl = (time_t)t;
1510 		}
1511 		break;
1512 
1513 	case DDS_DEFAULTTTL:
1514 		if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
1515 			snprintf( c->cr_msg, sizeof( c->cr_msg),
1516 				"DDS unable to parse dds-default-ttl \"%s\"",
1517 				c->argv[ 1 ] );
1518 			Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1519 				"%s: %s.\n", c->log, c->cr_msg );
1520 			return 1;
1521 		}
1522 
1523 		if ( t > DDS_RF2589_MAX_TTL ) {
1524 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1525 				"DDS invalid dds-default-ttl=%lu",
1526 				t );
1527 			Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1528 				"%s: %s.\n", c->log, c->cr_msg );
1529 			return 1;
1530 		}
1531 
1532 		if ( t == 0 ) {
1533 			di->di_default_ttl = DDS_RF2589_DEFAULT_TTL;
1534 
1535 		} else {
1536 			di->di_default_ttl = (time_t)t;
1537 		}
1538 		break;
1539 
1540 	case DDS_INTERVAL:
1541 		if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
1542 			snprintf( c->cr_msg, sizeof( c->cr_msg),
1543 				"DDS unable to parse dds-interval \"%s\"",
1544 				c->argv[ 1 ] );
1545 			Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1546 				"%s: %s.\n", c->log, c->cr_msg );
1547 			return 1;
1548 		}
1549 
1550 		if ( t <= 0 ) {
1551 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1552 				"DDS invalid dds-interval=%lu",
1553 				t );
1554 			Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1555 				"%s: %s.\n", c->log, c->cr_msg );
1556 			return 1;
1557 		}
1558 
1559 		if ( t < 60 ) {
1560 			Log( LDAP_DEBUG_ANY, LDAP_LEVEL_NOTICE,
1561 				"%s: dds-interval=%lu may be too small.\n",
1562 				c->log, t );
1563 		}
1564 
1565 		di->di_interval = (time_t)t;
1566 		if ( di->di_expire_task ) {
1567 			ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
1568 			if ( ldap_pvt_runqueue_isrunning( &slapd_rq, di->di_expire_task ) ) {
1569 				ldap_pvt_runqueue_stoptask( &slapd_rq, di->di_expire_task );
1570 			}
1571 			di->di_expire_task->interval.tv_sec = DDS_INTERVAL( di );
1572 			ldap_pvt_runqueue_resched( &slapd_rq, di->di_expire_task, 0 );
1573 			ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
1574 		}
1575 		break;
1576 
1577 	case DDS_TOLERANCE:
1578 		if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
1579 			snprintf( c->cr_msg, sizeof( c->cr_msg),
1580 				"DDS unable to parse dds-tolerance \"%s\"",
1581 				c->argv[ 1 ] );
1582 			Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1583 				"%s: %s.\n", c->log, c->cr_msg );
1584 			return 1;
1585 		}
1586 
1587 		if ( t > DDS_RF2589_MAX_TTL ) {
1588 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1589 				"DDS invalid dds-tolerance=%lu",
1590 				t );
1591 			Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1592 				"%s: %s.\n", c->log, c->cr_msg );
1593 			return 1;
1594 		}
1595 
1596 		di->di_tolerance = (time_t)t;
1597 		break;
1598 
1599 	case DDS_MAXDYNAMICOBJS:
1600 		if ( c->value_int < 0 ) {
1601 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1602 				"DDS invalid dds-max-dynamicObjects=%d", c->value_int );
1603 			Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1604 				"%s: %s.\n", c->log, c->cr_msg );
1605 			return 1;
1606 		}
1607 		di->di_max_dynamicObjects = c->value_int;
1608 		break;
1609 
1610 	default:
1611 		rc = 1;
1612 		break;
1613 	}
1614 
1615 	return rc;
1616 }
1617 
1618 static int
dds_db_init(BackendDB * be,ConfigReply * cr)1619 dds_db_init(
1620 	BackendDB	*be,
1621 	ConfigReply	*cr)
1622 {
1623 	slap_overinst	*on = (slap_overinst *)be->bd_info;
1624 	dds_info_t	*di;
1625 	BackendInfo	*bi = on->on_info->oi_orig;
1626 
1627 	if ( SLAP_ISGLOBALOVERLAY( be ) ) {
1628 		Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1629 			"DDS cannot be used as global overlay.\n" );
1630 		return 1;
1631 	}
1632 
1633 	/* check support for required functions */
1634 	/* FIXME: some could be provided by other overlays in between */
1635 	if ( bi->bi_op_add == NULL			/* object creation */
1636 		|| bi->bi_op_delete == NULL		/* object deletion */
1637 		|| bi->bi_op_modify == NULL		/* object refresh */
1638 		|| bi->bi_op_search == NULL		/* object expiration */
1639 		|| bi->bi_entry_get_rw == NULL )	/* object type/existence checking */
1640 	{
1641 		Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1642 			"DDS backend \"%s\" does not provide "
1643 			"required functionality.\n",
1644 			bi->bi_type );
1645 		return 1;
1646 	}
1647 
1648 	di = (dds_info_t *)ch_calloc( 1, sizeof( dds_info_t ) );
1649 	on->on_bi.bi_private = di;
1650 
1651 	di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
1652 	di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
1653 
1654 	ldap_pvt_thread_mutex_init( &di->di_mutex );
1655 
1656 	SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_DYNAMIC;
1657 
1658 	return 0;
1659 }
1660 
1661 /* adds dynamicSubtrees to root DSE */
1662 static int
dds_entry_info(void * arg,Entry * e)1663 dds_entry_info( void *arg, Entry *e )
1664 {
1665 	dds_info_t	*di = (dds_info_t *)arg;
1666 
1667 	attr_merge( e, slap_schema.si_ad_dynamicSubtrees,
1668 		di->di_suffix, di->di_nsuffix );
1669 
1670 	return 0;
1671 }
1672 
1673 /* callback that counts the returned entries, since the search
1674  * does not get to the point in slap_send_search_entries where
1675  * the actual count occurs */
1676 static int
dds_count_cb(Operation * op,SlapReply * rs)1677 dds_count_cb( Operation *op, SlapReply *rs )
1678 {
1679 	int	*nump = (int *)op->o_callback->sc_private;
1680 
1681 	switch ( rs->sr_type ) {
1682 	case REP_SEARCH:
1683 		(*nump)++;
1684 		break;
1685 
1686 	case REP_SEARCHREF:
1687 	case REP_RESULT:
1688 		break;
1689 
1690 	default:
1691 		assert( 0 );
1692 	}
1693 
1694 	return 0;
1695 }
1696 
1697 /* count dynamic objects existing in the database at startup */
1698 static int
dds_count(void * ctx,BackendDB * be)1699 dds_count( void *ctx, BackendDB *be )
1700 {
1701 	slap_overinst	*on = (slap_overinst *)be->bd_info;
1702 	dds_info_t	*di = (dds_info_t *)on->on_bi.bi_private;
1703 
1704 	Connection	conn = { 0 };
1705 	OperationBuffer opbuf;
1706 	Operation	*op;
1707 	slap_callback	sc = { 0 };
1708 	SlapReply	rs = { REP_RESULT };
1709 
1710 	int		rc;
1711 	char		*extra = "";
1712 
1713 	connection_fake_init2( &conn, &opbuf, ctx, 0 );
1714 	op = &opbuf.ob_op;
1715 
1716 	op->o_tag = LDAP_REQ_SEARCH;
1717 	memset( &op->oq_search, 0, sizeof( op->oq_search ) );
1718 
1719 	op->o_bd = be;
1720 
1721 	op->o_req_dn = op->o_bd->be_suffix[ 0 ];
1722 	op->o_req_ndn = op->o_bd->be_nsuffix[ 0 ];
1723 
1724 	op->o_dn = op->o_bd->be_rootdn;
1725 	op->o_ndn = op->o_bd->be_rootndn;
1726 
1727 	op->ors_scope = LDAP_SCOPE_SUBTREE;
1728 	op->ors_tlimit = SLAP_NO_LIMIT;
1729 	op->ors_slimit = SLAP_NO_LIMIT;
1730 	op->ors_attrs = slap_anlist_no_attrs;
1731 	op->o_do_not_cache = 1;
1732 
1733 	op->ors_filterstr.bv_len = STRLENOF( "(objectClass=" ")" )
1734 		+ slap_schema.si_oc_dynamicObject->soc_cname.bv_len;
1735 	op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
1736 	snprintf( op->ors_filterstr.bv_val, op->ors_filterstr.bv_len + 1,
1737 		"(objectClass=%s)",
1738 		slap_schema.si_oc_dynamicObject->soc_cname.bv_val );
1739 
1740 	op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
1741 	if ( op->ors_filter == NULL ) {
1742 		rs.sr_err = LDAP_OTHER;
1743 		goto done_search;
1744 	}
1745 
1746 	op->o_callback = &sc;
1747 	sc.sc_response = dds_count_cb;
1748 	sc.sc_private = &di->di_num_dynamicObjects;
1749 	di->di_num_dynamicObjects = 0;
1750 
1751 	op->o_bd->bd_info = (BackendInfo *)on->on_info;
1752 	(void)op->o_bd->bd_info->bi_op_search( op, &rs );
1753 	op->o_bd->bd_info = (BackendInfo *)on;
1754 
1755 done_search:;
1756 	op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
1757 	filter_free_x( op, op->ors_filter, 1 );
1758 
1759 	rc = rs.sr_err;
1760 	switch ( rs.sr_err ) {
1761 	case LDAP_SUCCESS:
1762 		Log( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
1763 			"DDS non-expired=%d\n",
1764 			di->di_num_dynamicObjects );
1765 		break;
1766 
1767 	case LDAP_NO_SUCH_OBJECT:
1768 		/* (ITS#5267) database not created yet? */
1769 		rs.sr_err = LDAP_SUCCESS;
1770 		extra = " (ignored)";
1771 		/* fallthru */
1772 
1773 	default:
1774 		Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1775 			"DDS non-expired objects lookup failed err=%d%s\n",
1776 			rc, extra );
1777 		break;
1778 	}
1779 
1780 	return rs.sr_err;
1781 }
1782 
1783 static int
dds_db_open(BackendDB * be,ConfigReply * cr)1784 dds_db_open(
1785 	BackendDB	*be,
1786 	ConfigReply	*cr )
1787 {
1788 	slap_overinst	*on = (slap_overinst *)be->bd_info;
1789 	dds_info_t	*di = on->on_bi.bi_private;
1790 	int		rc = 0;
1791 	void		*thrctx = ldap_pvt_thread_pool_context();
1792 
1793 	if ( slapMode & SLAP_TOOL_MODE )
1794 		return 0;
1795 
1796 	if ( DDS_OFF( di ) ) {
1797 		goto done;
1798 	}
1799 
1800 	if ( SLAP_SINGLE_SHADOW( be ) ) {
1801 		Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1802 			"DDS incompatible with shadow database \"%s\".\n",
1803 			be->be_suffix[ 0 ].bv_val );
1804 		return 1;
1805 	}
1806 
1807 	if ( di->di_max_ttl == 0 ) {
1808 		di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
1809 	}
1810 
1811 	if ( di->di_min_ttl == 0 ) {
1812 		di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
1813 	}
1814 
1815 	di->di_suffix = be->be_suffix;
1816 	di->di_nsuffix = be->be_nsuffix;
1817 
1818 	/* count the dynamic objects first */
1819 	rc = dds_count( thrctx, be );
1820 	if ( rc != LDAP_SUCCESS ) {
1821 		rc = 1;
1822 		goto done;
1823 	}
1824 
1825 	/* start expire task */
1826 	ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
1827 	di->di_expire_task = ldap_pvt_runqueue_insert( &slapd_rq,
1828 		DDS_INTERVAL( di ),
1829 		dds_expire_fn, di, "dds_expire_fn",
1830 		be->be_suffix[ 0 ].bv_val );
1831 	ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
1832 
1833 	/* register dinamicSubtrees root DSE info support */
1834 	rc = entry_info_register( dds_entry_info, (void *)di );
1835 
1836 done:;
1837 
1838 	return rc;
1839 }
1840 
1841 static int
dds_db_close(BackendDB * be,ConfigReply * cr)1842 dds_db_close(
1843 	BackendDB	*be,
1844 	ConfigReply	*cr )
1845 {
1846 	slap_overinst	*on = (slap_overinst *)be->bd_info;
1847 	dds_info_t	*di = on->on_bi.bi_private;
1848 
1849 	/* stop expire task */
1850 	if ( di && di->di_expire_task ) {
1851 		ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
1852 		if ( ldap_pvt_runqueue_isrunning( &slapd_rq, di->di_expire_task ) ) {
1853 			ldap_pvt_runqueue_stoptask( &slapd_rq, di->di_expire_task );
1854 		}
1855 		ldap_pvt_runqueue_remove( &slapd_rq, di->di_expire_task );
1856 		ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
1857 		di->di_expire_task = NULL;
1858 	}
1859 
1860 	(void)entry_info_unregister( dds_entry_info, (void *)di );
1861 
1862 	return 0;
1863 }
1864 
1865 static int
dds_db_destroy(BackendDB * be,ConfigReply * cr)1866 dds_db_destroy(
1867 	BackendDB	*be,
1868 	ConfigReply	*cr )
1869 {
1870 	slap_overinst	*on = (slap_overinst *)be->bd_info;
1871 	dds_info_t	*di = on->on_bi.bi_private;
1872 
1873 	if ( di != NULL ) {
1874 		ldap_pvt_thread_mutex_destroy( &di->di_mutex );
1875 
1876 		free( di );
1877 	}
1878 
1879 	return 0;
1880 }
1881 
1882 static int
slap_exop_refresh(Operation * op,SlapReply * rs)1883 slap_exop_refresh(
1884 		Operation	*op,
1885 		SlapReply	*rs )
1886 {
1887 	BackendDB		*bd = op->o_bd;
1888 
1889 	rs->sr_err = slap_parse_refresh( op->ore_reqdata, &op->o_req_ndn, NULL,
1890 		&rs->sr_text, op->o_tmpmemctx );
1891 	if ( rs->sr_err != LDAP_SUCCESS ) {
1892 		return rs->sr_err;
1893 	}
1894 
1895 	Log( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
1896 		"%s REFRESH dn=\"%s\"\n",
1897 		op->o_log_prefix, op->o_req_ndn.bv_val );
1898 	op->o_req_dn = op->o_req_ndn;
1899 
1900 	op->o_bd = select_backend( &op->o_req_ndn, 0 );
1901 	if ( op->o_bd == NULL ) {
1902 		send_ldap_error( op, rs, LDAP_NO_SUCH_OBJECT,
1903 			"no global superior knowledge" );
1904 		goto done;
1905 	}
1906 
1907 	if ( !SLAP_DYNAMIC( op->o_bd ) ) {
1908 		send_ldap_error( op, rs, LDAP_UNAVAILABLE_CRITICAL_EXTENSION,
1909 			"backend does not support dynamic directory services" );
1910 		goto done;
1911 	}
1912 
1913 	rs->sr_err = backend_check_restrictions( op, rs,
1914 		(struct berval *)&slap_EXOP_REFRESH );
1915 	if ( rs->sr_err != LDAP_SUCCESS ) {
1916 		goto done;
1917 	}
1918 
1919 	if ( op->o_bd->be_extended == NULL ) {
1920 		send_ldap_error( op, rs, LDAP_UNAVAILABLE_CRITICAL_EXTENSION,
1921 			"backend does not support extended operations" );
1922 		goto done;
1923 	}
1924 
1925 	op->o_bd->be_extended( op, rs );
1926 
1927 done:;
1928 	if ( !BER_BVISNULL( &op->o_req_ndn ) ) {
1929 		op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx );
1930 		BER_BVZERO( &op->o_req_ndn );
1931 		BER_BVZERO( &op->o_req_dn );
1932 	}
1933 	op->o_bd = bd;
1934 
1935         return rs->sr_err;
1936 }
1937 
1938 static slap_overinst dds;
1939 
1940 static int do_not_load_exop;
1941 static int do_not_replace_exop;
1942 static int do_not_load_schema;
1943 
1944 #if SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC
1945 static
1946 #endif /* SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC */
1947 int
dds_initialize()1948 dds_initialize()
1949 {
1950 	int		rc = 0;
1951 	int		i, code;
1952 
1953 	/* Make sure we don't exceed the bits reserved for userland */
1954 	config_check_userland( DDS_LAST );
1955 
1956 	if ( !do_not_load_schema ) {
1957 		static struct {
1958 			char			*desc;
1959 			slap_mask_t		flags;
1960 			AttributeDescription	**ad;
1961 		}		s_at[] = {
1962 			{ "( 1.3.6.1.4.1.4203.666.1.57 "
1963 				"NAME ( 'entryExpireTimestamp' ) "
1964 				"DESC 'RFC2589 OpenLDAP extension: expire time of a dynamic object, "
1965 					"computed as now + entryTtl' "
1966 				"EQUALITY generalizedTimeMatch "
1967 				"ORDERING generalizedTimeOrderingMatch "
1968 				"SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
1969 				"SINGLE-VALUE "
1970 				"NO-USER-MODIFICATION "
1971 				"USAGE dSAOperation )",
1972 				SLAP_AT_HIDE,
1973 				&ad_entryExpireTimestamp },
1974 			{ NULL }
1975 		};
1976 
1977 		for ( i = 0; s_at[ i ].desc != NULL; i++ ) {
1978 			code = register_at( s_at[ i ].desc, s_at[ i ].ad, 0 );
1979 			if ( code ) {
1980 				Debug( LDAP_DEBUG_ANY,
1981 					"dds_initialize: register_at failed\n" );
1982 				return code;
1983 			}
1984 			(*s_at[ i ].ad)->ad_type->sat_flags |= SLAP_AT_HIDE;
1985 		}
1986 	}
1987 
1988 	if ( !do_not_load_exop ) {
1989 		rc = load_extop2( (struct berval *)&slap_EXOP_REFRESH,
1990 			SLAP_EXOP_WRITES|SLAP_EXOP_HIDE, slap_exop_refresh,
1991 			!do_not_replace_exop );
1992 		if ( rc != LDAP_SUCCESS ) {
1993 			Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1994 				"DDS unable to register refresh exop: %d.\n",
1995 				rc );
1996 			return rc;
1997 		}
1998 	}
1999 
2000 	dds.on_bi.bi_type = "dds";
2001 
2002 	dds.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
2003 	dds.on_bi.bi_db_init = dds_db_init;
2004 	dds.on_bi.bi_db_open = dds_db_open;
2005 	dds.on_bi.bi_db_close = dds_db_close;
2006 	dds.on_bi.bi_db_destroy = dds_db_destroy;
2007 
2008 	dds.on_bi.bi_op_add = dds_op_add;
2009 	dds.on_bi.bi_op_delete = dds_op_delete;
2010 	dds.on_bi.bi_op_modify = dds_op_modify;
2011 	dds.on_bi.bi_op_modrdn = dds_op_rename;
2012 	dds.on_bi.bi_extended = dds_op_extended;
2013 	dds.on_response = dds_response;
2014 
2015 	dds.on_bi.bi_cf_ocs = dds_ocs;
2016 
2017 	rc = config_register_schema( dds_cfg, dds_ocs );
2018 	if ( rc ) {
2019 		return rc;
2020 	}
2021 
2022 	return overlay_register( &dds );
2023 }
2024 
2025 #if SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC
2026 int
init_module(int argc,char * argv[])2027 init_module( int argc, char *argv[] )
2028 {
2029 	int	i;
2030 
2031 	for ( i = 0; i < argc; i++ ) {
2032 		char	*arg = argv[ i ];
2033 		int	no = 0;
2034 
2035 		if ( strncasecmp( arg, "no-", STRLENOF( "no-" ) ) == 0 ) {
2036 			arg += STRLENOF( "no-" );
2037 			no = 1;
2038 		}
2039 
2040 		if ( strcasecmp( arg, "exop" ) == 0 ) {
2041 			do_not_load_exop = no;
2042 
2043 		} else if ( strcasecmp( arg, "replace" ) == 0 ) {
2044 			do_not_replace_exop = no;
2045 
2046 		} else if ( strcasecmp( arg, "schema" ) == 0 ) {
2047 			do_not_load_schema = no;
2048 
2049 		} else {
2050 			Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
2051 				"DDS unknown module arg[#%d]=\"%s\".\n",
2052 				i, argv[ i ] );
2053 			return 1;
2054 		}
2055 	}
2056 
2057 	return dds_initialize();
2058 }
2059 #endif /* SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC */
2060 
2061 #endif	/* defined(SLAPD_OVER_DDS) */
2062