xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/overlays/dynlist.c (revision dd255ccea4286b0c44fa8fd48a9a19a768afe8e1)
1 /*	$NetBSD: dynlist.c,v 1.1.1.4 2010/12/12 15:23:35 adam Exp $	*/
2 
3 /* dynlist.c - dynamic list overlay */
4 /* OpenLDAP: pkg/ldap/servers/slapd/overlays/dynlist.c,v 1.20.2.33 2010/04/14 22:42:53 quanah Exp */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6  *
7  * Copyright 2003-2010 The OpenLDAP Foundation.
8  * Portions Copyright 2004-2005 Pierangelo Masarati.
9  * Portions Copyright 2008 Emmanuel Dreyfus.
10  * All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted only as authorized by the OpenLDAP
14  * Public License.
15  *
16  * A copy of this license is available in the file LICENSE in the
17  * top-level directory of the distribution or, alternatively, at
18  * <http://www.OpenLDAP.org/license.html>.
19  */
20 /* ACKNOWLEDGEMENTS:
21  * This work was initially developed by Pierangelo Masarati
22  * for SysNet s.n.c., for inclusion in OpenLDAP Software.
23  */
24 
25 #include "portable.h"
26 
27 #ifdef SLAPD_OVER_DYNLIST
28 
29 #if LDAP_VENDOR_VERSION_MINOR == X || LDAP_VENDOR_VERSION_MINOR > 3
30 #if SLAPD_OVER_DYNGROUP != SLAPD_MOD_STATIC
31 #define TAKEOVER_DYNGROUP
32 #endif
33 #else
34 #if LDAP_VENDOR_VERSION_MINOR < 3
35 #define OL_2_2_COMPAT
36 #endif
37 #endif
38 
39 #include <stdio.h>
40 
41 #include <ac/string.h>
42 
43 #include "slap.h"
44 #ifndef OL_2_2_COMPAT
45 #include "config.h"
46 #endif
47 #include "lutil.h"
48 
49 /* FIXME: the code differs if SLAP_OPATTRS is defined or not;
50  * SLAP_OPATTRS is not defined in 2.2 yet, while this overlay
51  * expects HEAD code at least later than August 6, 2004. */
52 /* FIXME: slap_anlist_no_attrs was introduced in 2.3; here it
53  * is anticipated to allow using this overlay with 2.2. */
54 
55 #ifdef OL_2_2_COMPAT
56 static AttributeName anlist_no_attrs[] = {
57 	{ BER_BVC( LDAP_NO_ATTRS ), NULL, 0, NULL },
58 	{ BER_BVNULL, NULL, 0, NULL }
59 };
60 
61 static AttributeName *slap_anlist_no_attrs = anlist_no_attrs;
62 #endif
63 
64 static AttributeDescription *ad_dgIdentity, *ad_dgAuthz;
65 
66 typedef struct dynlist_map_t {
67 	AttributeDescription	*dlm_member_ad;
68 	AttributeDescription	*dlm_mapped_ad;
69 	struct dynlist_map_t	*dlm_next;
70 } dynlist_map_t;
71 
72 typedef struct dynlist_info_t {
73 	ObjectClass		*dli_oc;
74 	AttributeDescription	*dli_ad;
75 	struct dynlist_map_t	*dli_dlm;
76 	struct berval		dli_uri;
77 	LDAPURLDesc		*dli_lud;
78 	struct berval		dli_uri_nbase;
79 	Filter			*dli_uri_filter;
80 	struct berval		dli_default_filter;
81 	struct dynlist_info_t	*dli_next;
82 } dynlist_info_t;
83 
84 #define DYNLIST_USAGE \
85 	"\"dynlist-attrset <oc> [uri] <URL-ad> [[<mapped-ad>:]<member-ad> ...]\": "
86 
87 static dynlist_info_t *
88 dynlist_is_dynlist_next( Operation *op, SlapReply *rs, dynlist_info_t *old_dli )
89 {
90 	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
91 	dynlist_info_t	*dli;
92 
93 	Attribute	*a;
94 
95 	if ( old_dli == NULL ) {
96 		dli = (dynlist_info_t *)on->on_bi.bi_private;
97 
98 	} else {
99 		dli = old_dli->dli_next;
100 	}
101 
102 	a = attrs_find( rs->sr_entry->e_attrs, slap_schema.si_ad_objectClass );
103 	if ( a == NULL ) {
104 		/* FIXME: objectClass must be present; for non-storage
105 		 * backends, like back-ldap, it needs to be added
106 		 * to the requested attributes */
107 		return NULL;
108 	}
109 
110 	for ( ; dli; dli = dli->dli_next ) {
111 		if ( dli->dli_lud != NULL ) {
112 			/* check base and scope */
113 			if ( !BER_BVISNULL( &dli->dli_uri_nbase )
114 				&& !dnIsSuffixScope( &rs->sr_entry->e_nname,
115 					&dli->dli_uri_nbase,
116 					dli->dli_lud->lud_scope ) )
117 			{
118 				continue;
119 			}
120 
121 			/* check filter */
122 			if ( dli->dli_uri_filter && test_filter( op, rs->sr_entry, dli->dli_uri_filter ) != LDAP_COMPARE_TRUE ) {
123 				continue;
124 			}
125 		}
126 
127 		if ( attr_valfind( a,
128 				SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
129 				SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
130 				&dli->dli_oc->soc_cname, NULL,
131 				op->o_tmpmemctx ) == 0 )
132 		{
133 			return dli;
134 		}
135 	}
136 
137 	return NULL;
138 }
139 
140 static int
141 dynlist_make_filter( Operation *op, Entry *e, const char *url, struct berval *oldf, struct berval *newf )
142 {
143 	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
144 	dynlist_info_t	*dli = (dynlist_info_t *)on->on_bi.bi_private;
145 
146 	char		*ptr;
147 	int		needBrackets = 0;
148 
149 	assert( oldf != NULL );
150 	assert( newf != NULL );
151 	assert( !BER_BVISNULL( oldf ) );
152 	assert( !BER_BVISEMPTY( oldf ) );
153 
154 	if ( oldf->bv_val[0] != '(' ) {
155 		Debug( LDAP_DEBUG_ANY, "%s: dynlist, DN=\"%s\": missing brackets in URI=\"%s\" filter\n",
156 			op->o_log_prefix, e->e_name.bv_val, url );
157 		needBrackets = 2;
158 	}
159 
160 	newf->bv_len = STRLENOF( "(&(!(objectClass=" "))" ")" )
161 		+ dli->dli_oc->soc_cname.bv_len + oldf->bv_len + needBrackets;
162 	newf->bv_val = op->o_tmpalloc( newf->bv_len + 1, op->o_tmpmemctx );
163 	if ( newf->bv_val == NULL ) {
164 		return -1;
165 	}
166 	ptr = lutil_strcopy( newf->bv_val, "(&(!(objectClass=" );
167 	ptr = lutil_strcopy( ptr, dli->dli_oc->soc_cname.bv_val );
168 	ptr = lutil_strcopy( ptr, "))" );
169 	if ( needBrackets ) *ptr++ = '(';
170 	ptr = lutil_strcopy( ptr, oldf->bv_val );
171 	if ( needBrackets ) *ptr++ = ')';
172 	ptr = lutil_strcopy( ptr, ")" );
173 	newf->bv_len = ptr - newf->bv_val;
174 
175 	return 0;
176 }
177 
178 typedef struct dynlist_sc_t {
179 	dynlist_info_t    *dlc_dli;
180 	Entry		*dlc_e;
181 } dynlist_sc_t;
182 
183 static int
184 dynlist_sc_update( Operation *op, SlapReply *rs )
185 {
186 	Entry			*e;
187 	Attribute		*a;
188 	int			opattrs,
189 				userattrs;
190 	AccessControlState	acl_state = ACL_STATE_INIT;
191 
192 	dynlist_sc_t		*dlc;
193 	dynlist_map_t		*dlm;
194 
195 	if ( rs->sr_type != REP_SEARCH ) {
196 		return 0;
197 	}
198 
199 	dlc = (dynlist_sc_t *)op->o_callback->sc_private;
200 	e = dlc->dlc_e;
201 
202 	assert( e != NULL );
203 	assert( rs->sr_entry != NULL );
204 
205 	/* test access to entry */
206 	if ( !access_allowed( op, rs->sr_entry, slap_schema.si_ad_entry,
207 				NULL, ACL_READ, NULL ) )
208 	{
209 		goto done;
210 	}
211 
212 	/* if there is only one member_ad, and it's not mapped,
213 	 * consider it as old-style member listing */
214 	dlm = dlc->dlc_dli->dli_dlm;
215 	if ( dlm && dlm->dlm_mapped_ad == NULL && dlm->dlm_next == NULL ) {
216 		/* if access allowed, try to add values, emulating permissive
217 		 * control to silently ignore duplicates */
218 		if ( access_allowed( op, rs->sr_entry, slap_schema.si_ad_entry,
219 					NULL, ACL_READ, NULL ) )
220 		{
221 			Modification	mod;
222 			const char	*text = NULL;
223 			char		textbuf[1024];
224 			struct berval	vals[ 2 ], nvals[ 2 ];
225 
226 			vals[ 0 ] = rs->sr_entry->e_name;
227 			BER_BVZERO( &vals[ 1 ] );
228 			nvals[ 0 ] = rs->sr_entry->e_nname;
229 			BER_BVZERO( &nvals[ 1 ] );
230 
231 			mod.sm_op = LDAP_MOD_ADD;
232 			mod.sm_desc = dlm->dlm_member_ad;
233 			mod.sm_type = dlm->dlm_member_ad->ad_cname;
234 			mod.sm_values = vals;
235 			mod.sm_nvalues = nvals;
236 			mod.sm_numvals = 1;
237 
238 			(void)modify_add_values( e, &mod, /* permissive */ 1,
239 					&text, textbuf, sizeof( textbuf ) );
240 		}
241 
242 		goto done;
243 	}
244 
245 #ifndef SLAP_OPATTRS
246 	opattrs = ( rs->sr_attrs == NULL ) ? 0 : an_find( rs->sr_attrs, slap_bv_operational_attrs );
247 	userattrs = ( rs->sr_attrs == NULL ) ? 1 : an_find( rs->sr_attrs, slap_bv_user_attrs );
248 #else /* SLAP_OPATTRS */
249 	opattrs = SLAP_OPATTRS( rs->sr_attr_flags );
250 	userattrs = SLAP_USERATTRS( rs->sr_attr_flags );
251 #endif /* SLAP_OPATTRS */
252 
253 	for ( a = rs->sr_entry->e_attrs; a != NULL; a = a->a_next ) {
254 		BerVarray	vals, nvals = NULL;
255 		int		i, j,
256 				is_oc = a->a_desc == slap_schema.si_ad_objectClass;
257 
258 		/* if attribute is not requested, skip it */
259 		if ( rs->sr_attrs == NULL ) {
260 			if ( is_at_operational( a->a_desc->ad_type ) ) {
261 				continue;
262 			}
263 
264 		} else {
265 			if ( is_at_operational( a->a_desc->ad_type ) ) {
266 				if ( !opattrs && !ad_inlist( a->a_desc, rs->sr_attrs ) )
267 				{
268 					continue;
269 				}
270 
271 			} else {
272 				if ( !userattrs && !ad_inlist( a->a_desc, rs->sr_attrs ) )
273 				{
274 					continue;
275 				}
276 			}
277 		}
278 
279 		/* test access to attribute */
280 		if ( op->ors_attrsonly ) {
281 			if ( !access_allowed( op, rs->sr_entry, a->a_desc, NULL,
282 						ACL_READ, &acl_state ) )
283 			{
284 				continue;
285 			}
286 		}
287 
288 		/* single-value check: keep first only */
289 		if ( is_at_single_value( a->a_desc->ad_type ) ) {
290 			if ( attr_find( e->e_attrs, a->a_desc ) != NULL ) {
291 				continue;
292 			}
293 		}
294 
295 		/* test access to attribute */
296 		i = a->a_numvals;
297 
298 		vals = op->o_tmpalloc( ( i + 1 ) * sizeof( struct berval ), op->o_tmpmemctx );
299 		if ( a->a_nvals != a->a_vals ) {
300 			nvals = op->o_tmpalloc( ( i + 1 ) * sizeof( struct berval ), op->o_tmpmemctx );
301 		}
302 
303 		for ( i = 0, j = 0; !BER_BVISNULL( &a->a_vals[i] ); i++ ) {
304 			if ( is_oc ) {
305 				ObjectClass	*soc = oc_bvfind( &a->a_vals[i] );
306 
307 				if ( soc->soc_kind == LDAP_SCHEMA_STRUCTURAL ) {
308 					continue;
309 				}
310 			}
311 
312 			if ( access_allowed( op, rs->sr_entry, a->a_desc,
313 						&a->a_nvals[i], ACL_READ, &acl_state ) )
314 			{
315 				vals[j] = a->a_vals[i];
316 				if ( nvals ) {
317 					nvals[j] = a->a_nvals[i];
318 				}
319 				j++;
320 			}
321 		}
322 
323 		/* if access allowed, try to add values, emulating permissive
324 		 * control to silently ignore duplicates */
325 		if ( j != 0 ) {
326 			Modification	mod;
327 			const char	*text = NULL;
328 			char		textbuf[1024];
329 			dynlist_map_t	*dlm;
330 			AttributeDescription *ad;
331 
332 			BER_BVZERO( &vals[j] );
333 			if ( nvals ) {
334 				BER_BVZERO( &nvals[j] );
335 			}
336 
337 			ad = a->a_desc;
338 			for ( dlm = dlc->dlc_dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
339 				if ( dlm->dlm_member_ad == a->a_desc ) {
340 					if ( dlm->dlm_mapped_ad ) {
341 						ad = dlm->dlm_mapped_ad;
342 					}
343 					break;
344 				}
345 			}
346 
347 			mod.sm_op = LDAP_MOD_ADD;
348 			mod.sm_desc = ad;
349 			mod.sm_type = ad->ad_cname;
350 			mod.sm_values = vals;
351 			mod.sm_nvalues = nvals;
352 			mod.sm_numvals = j;
353 
354 			(void)modify_add_values( e, &mod, /* permissive */ 1,
355 					&text, textbuf, sizeof( textbuf ) );
356 		}
357 
358 		op->o_tmpfree( vals, op->o_tmpmemctx );
359 		if ( nvals ) {
360 			op->o_tmpfree( nvals, op->o_tmpmemctx );
361 		}
362 	}
363 
364 done:;
365 	if ( rs->sr_flags & REP_ENTRY_MUSTBEFREED ) {
366 		entry_free( rs->sr_entry );
367 		rs->sr_entry = NULL;
368 		rs->sr_flags ^= REP_ENTRY_MUSTBEFREED;
369 	}
370 
371 	return 0;
372 }
373 
374 static int
375 dynlist_prepare_entry( Operation *op, SlapReply *rs, dynlist_info_t *dli )
376 {
377 	Attribute	*a, *id = NULL;
378 	slap_callback	cb;
379 	Operation	o = *op;
380 	SlapReply	r = { REP_SEARCH };
381 	struct berval	*url;
382 	Entry		*e;
383 	slap_mask_t	e_flags;
384 	int		opattrs,
385 			userattrs;
386 	dynlist_sc_t	dlc = { 0 };
387 	dynlist_map_t	*dlm;
388 
389 	a = attrs_find( rs->sr_entry->e_attrs, dli->dli_ad );
390 	if ( a == NULL ) {
391 		/* FIXME: error? */
392 		return SLAP_CB_CONTINUE;
393 	}
394 
395 #ifndef SLAP_OPATTRS
396 	opattrs = ( rs->sr_attrs == NULL ) ? 0 : an_find( rs->sr_attrs, slap_bv_operational_attrs );
397 	userattrs = ( rs->sr_attrs == NULL ) ? 1 : an_find( rs->sr_attrs, slap_bv_user_attrs );
398 #else /* SLAP_OPATTRS */
399 	opattrs = SLAP_OPATTRS( rs->sr_attr_flags );
400 	userattrs = SLAP_USERATTRS( rs->sr_attr_flags );
401 #endif /* SLAP_OPATTRS */
402 
403 	/* Don't generate member list if it wasn't requested */
404 	for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
405 		AttributeDescription *ad = dlm->dlm_mapped_ad ? dlm->dlm_mapped_ad : dlm->dlm_member_ad;
406 		if ( userattrs || ad_inlist( ad, rs->sr_attrs ) )
407 			break;
408 	}
409 	if ( dli->dli_dlm && !dlm )
410 		return SLAP_CB_CONTINUE;
411 
412 	if ( ad_dgIdentity && ( id = attrs_find( rs->sr_entry->e_attrs, ad_dgIdentity ))) {
413 		Attribute *authz = NULL;
414 
415 		/* if not rootdn and dgAuthz is present,
416 		 * check if user can be authorized as dgIdentity */
417 		if ( ad_dgAuthz && !BER_BVISEMPTY( &id->a_nvals[0] ) && !be_isroot( op )
418 			&& ( authz = attrs_find( rs->sr_entry->e_attrs, ad_dgAuthz ) ) )
419 		{
420 			if ( slap_sasl_matches( op, authz->a_nvals,
421 				&o.o_ndn, &o.o_ndn ) != LDAP_SUCCESS )
422 			{
423 				return SLAP_CB_CONTINUE;
424 			}
425 		}
426 
427 		o.o_dn = id->a_vals[0];
428 		o.o_ndn = id->a_nvals[0];
429 		o.o_groups = NULL;
430 	}
431 
432 	e_flags = rs->sr_flags;
433 	if ( !( rs->sr_flags & REP_ENTRY_MODIFIABLE ) ) {
434 		e = entry_dup( rs->sr_entry );
435 		e_flags |= ( REP_ENTRY_MODIFIABLE | REP_ENTRY_MUSTBEFREED );
436 		e_flags &= ~REP_ENTRY_MUSTRELEASE;
437 	} else {
438 		e = rs->sr_entry;
439 	}
440 
441 	dlc.dlc_e = e;
442 	dlc.dlc_dli = dli;
443 	cb.sc_private = &dlc;
444 	cb.sc_response = dynlist_sc_update;
445 	cb.sc_cleanup = NULL;
446 	cb.sc_next = NULL;
447 
448 	o.o_callback = &cb;
449 	o.ors_deref = LDAP_DEREF_NEVER;
450 	o.ors_limit = NULL;
451 	o.ors_tlimit = SLAP_NO_LIMIT;
452 	o.ors_slimit = SLAP_NO_LIMIT;
453 
454 	for ( url = a->a_nvals; !BER_BVISNULL( url ); url++ ) {
455 		LDAPURLDesc	*lud = NULL;
456 		int		i, j;
457 		struct berval	dn;
458 		int		rc;
459 
460 		BER_BVZERO( &o.o_req_dn );
461 		BER_BVZERO( &o.o_req_ndn );
462 		o.ors_filter = NULL;
463 		o.ors_attrs = NULL;
464 		BER_BVZERO( &o.ors_filterstr );
465 
466 		if ( ldap_url_parse( url->bv_val, &lud ) != LDAP_URL_SUCCESS ) {
467 			/* FIXME: error? */
468 			continue;
469 		}
470 
471 		if ( lud->lud_host != NULL ) {
472 			/* FIXME: host not allowed; reject as illegal? */
473 			Debug( LDAP_DEBUG_ANY, "dynlist_prepare_entry(\"%s\"): "
474 				"illegal URI \"%s\"\n",
475 				e->e_name.bv_val, url->bv_val, 0 );
476 			goto cleanup;
477 		}
478 
479 		if ( lud->lud_dn == NULL ) {
480 			/* note that an empty base is not honored in terms
481 			 * of defaultSearchBase, because select_backend()
482 			 * is not aware of the defaultSearchBase option;
483 			 * this can be useful in case of a database serving
484 			 * the empty suffix */
485 			BER_BVSTR( &dn, "" );
486 
487 		} else {
488 			ber_str2bv( lud->lud_dn, 0, 0, &dn );
489 		}
490 		rc = dnPrettyNormal( NULL, &dn, &o.o_req_dn, &o.o_req_ndn, op->o_tmpmemctx );
491 		if ( rc != LDAP_SUCCESS ) {
492 			/* FIXME: error? */
493 			goto cleanup;
494 		}
495 		o.ors_scope = lud->lud_scope;
496 
497 		for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
498 			if ( dlm->dlm_mapped_ad != NULL ) {
499 				break;
500 			}
501 		}
502 
503 		if ( dli->dli_dlm && !dlm ) {
504 			/* if ( lud->lud_attrs != NULL ),
505 			 * the URL should be ignored */
506 			o.ors_attrs = slap_anlist_no_attrs;
507 
508 		} else if ( lud->lud_attrs == NULL ) {
509 			o.ors_attrs = rs->sr_attrs;
510 
511 		} else {
512 			for ( i = 0; lud->lud_attrs[i]; i++)
513 				/* just count */ ;
514 
515 			o.ors_attrs = op->o_tmpcalloc( i + 1, sizeof( AttributeName ), op->o_tmpmemctx );
516 			for ( i = 0, j = 0; lud->lud_attrs[i]; i++) {
517 				const char	*text = NULL;
518 
519 				ber_str2bv( lud->lud_attrs[i], 0, 0, &o.ors_attrs[j].an_name );
520 				o.ors_attrs[j].an_desc = NULL;
521 				(void)slap_bv2ad( &o.ors_attrs[j].an_name, &o.ors_attrs[j].an_desc, &text );
522 				/* FIXME: ignore errors... */
523 
524 				if ( rs->sr_attrs == NULL ) {
525 					if ( o.ors_attrs[j].an_desc != NULL &&
526 							is_at_operational( o.ors_attrs[j].an_desc->ad_type ) )
527 					{
528 						continue;
529 					}
530 
531 				} else {
532 					if ( o.ors_attrs[j].an_desc != NULL &&
533 							is_at_operational( o.ors_attrs[j].an_desc->ad_type ) )
534 					{
535 						if ( !opattrs ) {
536 							continue;
537 						}
538 
539 						if ( !ad_inlist( o.ors_attrs[j].an_desc, rs->sr_attrs ) ) {
540 							/* lookup if mapped -- linear search,
541 							 * not very efficient unless list
542 							 * is very short */
543 							for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
544 								if ( dlm->dlm_member_ad == o.ors_attrs[j].an_desc ) {
545 									break;
546 								}
547 							}
548 
549 							if ( dlm == NULL ) {
550 								continue;
551 							}
552 						}
553 
554 					} else {
555 						if ( !userattrs &&
556 								o.ors_attrs[j].an_desc != NULL &&
557 								!ad_inlist( o.ors_attrs[j].an_desc, rs->sr_attrs ) )
558 						{
559 							/* lookup if mapped -- linear search,
560 							 * not very efficient unless list
561 							 * is very short */
562 							for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
563 								if ( dlm->dlm_member_ad == o.ors_attrs[j].an_desc ) {
564 									break;
565 								}
566 							}
567 
568 							if ( dlm == NULL ) {
569 								continue;
570 							}
571 						}
572 					}
573 				}
574 
575 				j++;
576 			}
577 
578 			if ( j == 0 ) {
579 				goto cleanup;
580 			}
581 
582 			BER_BVZERO( &o.ors_attrs[j].an_name );
583 		}
584 
585 		if ( lud->lud_filter == NULL ) {
586 			ber_dupbv_x( &o.ors_filterstr,
587 					&dli->dli_default_filter, op->o_tmpmemctx );
588 
589 		} else {
590 			struct berval	flt;
591 			ber_str2bv( lud->lud_filter, 0, 0, &flt );
592 			if ( dynlist_make_filter( op, rs->sr_entry, url->bv_val, &flt, &o.ors_filterstr ) ) {
593 				/* error */
594 				goto cleanup;
595 			}
596 		}
597 		o.ors_filter = str2filter_x( op, o.ors_filterstr.bv_val );
598 		if ( o.ors_filter == NULL ) {
599 			goto cleanup;
600 		}
601 
602 		o.o_bd = select_backend( &o.o_req_ndn, 1 );
603 		if ( o.o_bd && o.o_bd->be_search ) {
604 #ifdef SLAP_OPATTRS
605 			r.sr_attr_flags = slap_attr_flags( o.ors_attrs );
606 #endif /* SLAP_OPATTRS */
607 			(void)o.o_bd->be_search( &o, &r );
608 		}
609 
610 cleanup:;
611 		if ( id ) {
612 			slap_op_groups_free( &o );
613 		}
614 		if ( o.ors_filter ) {
615 			filter_free_x( &o, o.ors_filter, 1 );
616 		}
617 		if ( o.ors_attrs && o.ors_attrs != rs->sr_attrs
618 				&& o.ors_attrs != slap_anlist_no_attrs )
619 		{
620 			op->o_tmpfree( o.ors_attrs, op->o_tmpmemctx );
621 		}
622 		if ( !BER_BVISNULL( &o.o_req_dn ) ) {
623 			op->o_tmpfree( o.o_req_dn.bv_val, op->o_tmpmemctx );
624 		}
625 		if ( !BER_BVISNULL( &o.o_req_ndn ) ) {
626 			op->o_tmpfree( o.o_req_ndn.bv_val, op->o_tmpmemctx );
627 		}
628 		assert( BER_BVISNULL( &o.ors_filterstr )
629 			|| o.ors_filterstr.bv_val != lud->lud_filter );
630 		op->o_tmpfree( o.ors_filterstr.bv_val, op->o_tmpmemctx );
631 		ldap_free_urldesc( lud );
632 	}
633 
634 	rs->sr_entry = e;
635 	rs->sr_flags = e_flags;
636 
637 	return SLAP_CB_CONTINUE;
638 }
639 
640 static int
641 dynlist_sc_save_entry( Operation *op, SlapReply *rs )
642 {
643 	/* save the entry in the private field of the callback,
644 	 * so it doesn't get freed (it's temporary!) */
645 	if ( rs->sr_entry != NULL ) {
646 		dynlist_sc_t	*dlc = (dynlist_sc_t *)op->o_callback->sc_private;
647 		dlc->dlc_e = rs->sr_entry;
648 		rs->sr_entry = NULL;
649 	}
650 
651 	return 0;
652 }
653 
654 static int
655 dynlist_compare( Operation *op, SlapReply *rs )
656 {
657 	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
658 	dynlist_info_t	*dli = (dynlist_info_t *)on->on_bi.bi_private;
659 	Operation o = *op;
660 	Entry *e = NULL;
661 	dynlist_map_t *dlm;
662 
663 	for ( ; dli != NULL; dli = dli->dli_next ) {
664 		for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next )
665 			if ( op->oq_compare.rs_ava->aa_desc == dlm->dlm_member_ad )
666 				break;
667 
668 		if ( dli->dli_dlm && dlm ) {
669 			/* This compare is for one of the attributes we're
670 			 * interested in. We'll use slapd's existing dyngroup
671 			 * evaluator to get the answer we want.
672 			 */
673 			BerVarray id = NULL, authz = NULL;
674 
675 			o.o_do_not_cache = 1;
676 
677 			if ( ad_dgIdentity && backend_attribute( &o, NULL, &o.o_req_ndn,
678 				ad_dgIdentity, &id, ACL_READ ) == LDAP_SUCCESS )
679 			{
680 				/* if not rootdn and dgAuthz is present,
681 				 * check if user can be authorized as dgIdentity */
682 				if ( ad_dgAuthz && !BER_BVISEMPTY( id ) && !be_isroot( op )
683 					&& backend_attribute( &o, NULL, &o.o_req_ndn,
684 						ad_dgAuthz, &authz, ACL_READ ) == LDAP_SUCCESS )
685 				{
686 
687 					rs->sr_err = slap_sasl_matches( op, authz,
688 						&o.o_ndn, &o.o_ndn );
689 					ber_bvarray_free_x( authz, op->o_tmpmemctx );
690 					if ( rs->sr_err != LDAP_SUCCESS ) {
691 						goto done;
692 					}
693 				}
694 
695 				o.o_dn = *id;
696 				o.o_ndn = *id;
697 				o.o_groups = NULL; /* authz changed, invalidate cached groups */
698 			}
699 
700 			rs->sr_err = backend_group( &o, NULL, &o.o_req_ndn,
701 				&o.oq_compare.rs_ava->aa_value, dli->dli_oc, dli->dli_ad );
702 			switch ( rs->sr_err ) {
703 			case LDAP_SUCCESS:
704 				rs->sr_err = LDAP_COMPARE_TRUE;
705 				break;
706 
707 			case LDAP_NO_SUCH_OBJECT:
708 				/* NOTE: backend_group() returns noSuchObject
709 				 * if op_ndn does not exist; however, since
710 				 * dynamic list expansion means that the
711 				 * member attribute is virtually present, the
712 				 * non-existence of the asserted value implies
713 				 * the assertion is FALSE rather than
714 				 * UNDEFINED */
715 				rs->sr_err = LDAP_COMPARE_FALSE;
716 				break;
717 			}
718 
719 done:;
720 			if ( id ) ber_bvarray_free_x( id, o.o_tmpmemctx );
721 
722 			return SLAP_CB_CONTINUE;
723 		}
724 	}
725 
726 	if ( overlay_entry_get_ov( &o, &o.o_req_ndn, NULL, NULL, 0, &e, on ) !=
727 		LDAP_SUCCESS || e == NULL )
728 	{
729 		return SLAP_CB_CONTINUE;
730 	}
731 
732 	if ( ad_dgIdentity ) {
733 		Attribute *id = attrs_find( e->e_attrs, ad_dgIdentity );
734 		if ( id ) {
735 			Attribute *authz;
736 
737 			/* if not rootdn and dgAuthz is present,
738 			 * check if user can be authorized as dgIdentity */
739 			if ( ad_dgAuthz && !BER_BVISEMPTY( &id->a_nvals[0] ) && !be_isroot( op )
740 				&& ( authz = attrs_find( e->e_attrs, ad_dgAuthz ) ) )
741 			{
742 				if ( slap_sasl_matches( op, authz->a_nvals,
743 					&o.o_ndn, &o.o_ndn ) != LDAP_SUCCESS )
744 				{
745 					goto release;
746 				}
747 			}
748 
749 			o.o_dn = id->a_vals[0];
750 			o.o_ndn = id->a_nvals[0];
751 			o.o_groups = NULL;
752 		}
753 	}
754 
755 	dli = (dynlist_info_t *)on->on_bi.bi_private;
756 	for ( ; dli != NULL && rs->sr_err != LDAP_COMPARE_TRUE; dli = dli->dli_next ) {
757 		Attribute	*a;
758 		slap_callback	cb;
759 		SlapReply	r = { REP_SEARCH };
760 		AttributeName	an[2];
761 		int		rc;
762 		dynlist_sc_t	dlc = { 0 };
763 
764 		if ( !is_entry_objectclass_or_sub( e, dli->dli_oc ))
765 			continue;
766 
767 		/* if the entry has the right objectClass, generate
768 		 * the dynamic list and compare */
769 		dlc.dlc_dli = dli;
770 		cb.sc_private = &dlc;
771 		cb.sc_response = dynlist_sc_save_entry;
772 		cb.sc_cleanup = NULL;
773 		cb.sc_next = NULL;
774 		o.o_callback = &cb;
775 
776 		o.o_tag = LDAP_REQ_SEARCH;
777 		o.ors_limit = NULL;
778 		o.ors_tlimit = SLAP_NO_LIMIT;
779 		o.ors_slimit = SLAP_NO_LIMIT;
780 
781 		o.o_bd = select_backend( &o.o_req_ndn, 1 );
782 		if ( !o.o_bd || !o.o_bd->be_search ) {
783 			goto release;
784 		}
785 
786 		o.ors_filterstr = *slap_filterstr_objectClass_pres;
787 		o.ors_filter = (Filter *) slap_filter_objectClass_pres;
788 
789 		o.ors_scope = LDAP_SCOPE_BASE;
790 		o.ors_deref = LDAP_DEREF_NEVER;
791 		an[0].an_name = op->orc_ava->aa_desc->ad_cname;
792 		an[0].an_desc = op->orc_ava->aa_desc;
793 		BER_BVZERO( &an[1].an_name );
794 		o.ors_attrs = an;
795 		o.ors_attrsonly = 0;
796 
797 		o.o_acl_priv = ACL_COMPARE;
798 
799 		rc = o.o_bd->be_search( &o, &r );
800 
801 		if ( o.o_dn.bv_val != op->o_dn.bv_val ) {
802 			slap_op_groups_free( &o );
803 		}
804 
805 		if ( rc != 0 ) {
806 			goto release;
807 		}
808 
809 		if ( dlc.dlc_e != NULL ) {
810 			r.sr_entry = dlc.dlc_e;
811 		}
812 
813 		if ( r.sr_err != LDAP_SUCCESS || r.sr_entry == NULL ) {
814 			/* error? */
815 			goto release;
816 		}
817 
818 		for ( a = attrs_find( r.sr_entry->e_attrs, op->orc_ava->aa_desc );
819 			a != NULL;
820 			a = attrs_find( a->a_next, op->orc_ava->aa_desc ) )
821 		{
822 			/* if we're here, we got a match... */
823 			rs->sr_err = LDAP_COMPARE_FALSE;
824 
825 			if ( attr_valfind( a,
826 				SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
827 					SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
828 				&op->orc_ava->aa_value, NULL, op->o_tmpmemctx ) == 0 )
829 			{
830 				rs->sr_err = LDAP_COMPARE_TRUE;
831 				break;
832 			}
833 		}
834 
835 		if ( r.sr_flags & REP_ENTRY_MUSTBEFREED ) {
836 			entry_free( r.sr_entry );
837 			r.sr_entry = NULL;
838 			r.sr_flags ^= REP_ENTRY_MUSTBEFREED;
839 		}
840 	}
841 
842 release:;
843 	if ( e != NULL ) {
844 		overlay_entry_release_ov( &o, e, 0, on );
845 	}
846 
847 	return SLAP_CB_CONTINUE;
848 }
849 
850 static int
851 dynlist_response( Operation *op, SlapReply *rs )
852 {
853 	dynlist_info_t	*dli;
854 
855 	switch ( op->o_tag ) {
856 	case LDAP_REQ_SEARCH:
857 		if ( rs->sr_type == REP_SEARCH && !get_manageDSAit( op ) )
858 		{
859 			int	rc = LDAP_OTHER;
860 
861 			for ( dli = dynlist_is_dynlist_next( op, rs, NULL );
862 				dli;
863 				dli = dynlist_is_dynlist_next( op, rs, dli ) )
864 			{
865 				rc = dynlist_prepare_entry( op, rs, dli );
866 			}
867 
868 			if ( rc != LDAP_OTHER ) {
869 				return rc;
870 			}
871 		}
872 		break;
873 
874 	case LDAP_REQ_COMPARE:
875 		switch ( rs->sr_err ) {
876 		/* NOTE: we waste a few cycles running the dynamic list
877 		 * also when the result is FALSE, which occurs if the
878 		 * dynamic entry itself contains the AVA attribute  */
879 		/* FIXME: this approach is less than optimal; a dedicated
880 		 * compare op should be implemented, that fetches the
881 		 * entry, checks if it has the appropriate objectClass
882 		 * and, in case, runs a compare thru all the URIs,
883 		 * stopping at the first positive occurrence; see ITS#3756 */
884 		case LDAP_COMPARE_FALSE:
885 		case LDAP_NO_SUCH_ATTRIBUTE:
886 			return dynlist_compare( op, rs );
887 		}
888 		break;
889 
890 	default:
891 		break;
892 	}
893 
894 	return SLAP_CB_CONTINUE;
895 }
896 
897 static int
898 dynlist_build_def_filter( dynlist_info_t *dli )
899 {
900 	char	*ptr;
901 
902 	dli->dli_default_filter.bv_len = STRLENOF( "(!(objectClass=" "))" )
903 		+ dli->dli_oc->soc_cname.bv_len;
904 	dli->dli_default_filter.bv_val = ch_malloc( dli->dli_default_filter.bv_len + 1 );
905 	if ( dli->dli_default_filter.bv_val == NULL ) {
906 		Debug( LDAP_DEBUG_ANY, "dynlist_db_open: malloc failed.\n",
907 			0, 0, 0 );
908 		return -1;
909 	}
910 
911 	ptr = lutil_strcopy( dli->dli_default_filter.bv_val, "(!(objectClass=" );
912 	ptr = lutil_strcopy( ptr, dli->dli_oc->soc_cname.bv_val );
913 	ptr = lutil_strcopy( ptr, "))" );
914 
915 	assert( ptr == &dli->dli_default_filter.bv_val[dli->dli_default_filter.bv_len] );
916 
917 	return 0;
918 }
919 
920 #ifdef OL_2_2_COMPAT
921 static int
922 dynlist_db_config(
923 	BackendDB	*be,
924 	const char	*fname,
925 	int		lineno,
926 	int		argc,
927 	char		**argv )
928 {
929 	slap_overinst	*on = (slap_overinst *)be->bd_info;
930 
931 	int		rc = 0;
932 
933 	if ( strcasecmp( argv[0], "dynlist-attrset" ) == 0 ) {
934 		dynlist_info_t		**dlip;
935 		ObjectClass		*oc;
936 		AttributeDescription	*ad = NULL,
937 					*member_ad = NULL;
938 		dynlist_map_t		*dlm = NULL, *dlml = NULL;
939 		const char		*text;
940 
941 		if ( argc < 3 ) {
942 			Debug( LDAP_DEBUG_ANY, "%s: line %d: " DYNLIST_USAGE
943 				"invalid arg number #%d.\n",
944 				fname, lineno, argc );
945 			return 1;
946 		}
947 
948 		oc = oc_find( argv[1] );
949 		if ( oc == NULL ) {
950 			Debug( LDAP_DEBUG_ANY, "%s: line %d: " DYNLIST_USAGE
951 				"unable to find ObjectClass \"%s\"\n",
952 				fname, lineno, argv[ 1 ] );
953 			return 1;
954 		}
955 
956 		rc = slap_str2ad( argv[2], &ad, &text );
957 		if ( rc != LDAP_SUCCESS ) {
958 			Debug( LDAP_DEBUG_ANY, "%s: line %d: " DYNLIST_USAGE
959 				"unable to find AttributeDescription \"%s\"\n",
960 				fname, lineno, argv[2] );
961 			return 1;
962 		}
963 
964 		if ( !is_at_subtype( ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
965 			Debug( LDAP_DEBUG_ANY, "%s: line %d: " DYNLIST_USAGE
966 				"AttributeDescription \"%s\" "
967 				"must be a subtype of \"labeledURI\"\n",
968 				fname, lineno, argv[2] );
969 			return 1;
970 		}
971 
972 		for ( i = 3; i < argc; i++ ) {
973 			char *arg;
974 			char *cp;
975 			AttributeDescription *member_ad = NULL;
976 			AttributeDescription *mapped_ad = NULL;
977 			dynlist_map_t *dlmp;
978 
979 
980 			/*
981 			 * If no mapped attribute is given, dn is used
982 			 * for backward compatibility.
983 			 */
984 			arg = argv[i];
985 			if ( cp = strchr( arg, (int)':' ) != NULL ) {
986 				struct berval bv;
987 				ber_str2bv( arg, cp - arg, 0, &bv );
988 				rc = slap_bv2ad( &bv, &mapped_ad, &text );
989 				if ( rc != LDAP_SUCCESS ) {
990 					Debug( LDAP_DEBUG_ANY, "%s: line %d: "
991 						DYNLIST_USAGE
992 						"unable to find mapped AttributeDescription \"%s\"\n",
993 						fname, lineno, arg );
994 					return 1;
995 				}
996 
997 				arg = cp + 1;
998 			}
999 
1000 			rc = slap_str2ad( arg, &member_ad, &text );
1001 			if ( rc != LDAP_SUCCESS ) {
1002 				Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1003 					DYNLIST_USAGE
1004 					"unable to find AttributeDescription \"%s\"\n",
1005 					fname, lineno, arg );
1006 				return 1;
1007 			}
1008 
1009 			dlmp = (dynlist_map_t *)ch_calloc( 1, sizeof( dynlist_map_t ) );
1010 			if ( dlm == NULL ) {
1011 				dlm = dlmp;
1012 			}
1013 			dlmp->dlm_member_ad = member_ad;
1014 			dlmp->dlm_mapped_ad = mapped_ad;
1015 			dlmp->dlm_next = NULL;
1016 
1017 			if ( dlml != NULL )
1018 				dlml->dlm_next = dlmp;
1019 			dlml = dlmp;
1020 		}
1021 
1022 		for ( dlip = (dynlist_info_t **)&on->on_bi.bi_private;
1023 			*dlip; dlip = &(*dlip)->dli_next )
1024 		{
1025 			/*
1026 			 * The same URL attribute / member attribute pair
1027 			 * cannot be repeated, but we enforce this only
1028 			 * when the member attribute is unique. Performing
1029 			 * the check for multiple values would require
1030 			 * sorting and comparing the lists, which is left
1031 			 * as a future improvement
1032 			 */
1033 			if ( (*dlip)->dli_ad == ad &&
1034 			     (*dlip)->dli_dlm->dlm_next == NULL &&
1035 			     dlm->dlm_next == NULL &&
1036 			     dlm->dlm_member_ad == (*dlip)->dli_dlm->dlm_member_ad &&
1037 			     dlm->dlm_mapped_ad == (*dlip)->dli_dlm->dlm_mapped_ad ) {
1038 				Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1039 					DYNLIST_USAGE
1040 					"URL attributeDescription \"%s\" already mapped.\n",
1041 					fname, lineno, ad->ad_cname.bv_val );
1042 #if 0
1043 				/* make it a warning... */
1044 				return 1;
1045 #endif
1046 			}
1047 		}
1048 
1049 		*dlip = (dynlist_info_t *)ch_calloc( 1, sizeof( dynlist_info_t ) );
1050 		(*dlip)->dli_oc = oc;
1051 		(*dlip)->dli_ad = ad;
1052 		(*dlip)->dli_dlm = dlm;
1053 
1054 		if ( dynlist_build_def_filter( *dlip ) ) {
1055 			dynlist_map_t *dlm = (*dlip)->ldi_dlm;
1056 			dynlist_map_t *dlm_next;
1057 
1058 			while ( dlm != NULL ) {
1059 				dlm_next = dlm->dlm_next;
1060 				ch_free( dlm );
1061 				dlm = dlm_next;
1062 			}
1063 
1064 			ch_free( *dlip );
1065 			*dlip = NULL;
1066 			return 1;
1067 		}
1068 
1069 	/* allow dyngroup syntax */
1070 	} else if ( strcasecmp( argv[0], "dynlist-attrpair" ) == 0 ) {
1071 		dynlist_info_t		**dlip;
1072 		ObjectClass		*oc;
1073 		AttributeDescription	*ad = NULL,
1074 					*member_ad = NULL;
1075 		const char		*text;
1076 
1077 		if ( argc != 3 ) {
1078 			Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1079 				"\"dynlist-attrpair <member-ad> <URL-ad>\": "
1080 				"invalid arg number #%d.\n",
1081 				fname, lineno, argc );
1082 			return 1;
1083 		}
1084 
1085 		oc = oc_find( "groupOfURLs" );
1086 		if ( oc == NULL ) {
1087 			Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1088 				"\"dynlist-attrpair <member-ad> <URL-ad>\": "
1089 				"unable to find default ObjectClass \"groupOfURLs\"\n",
1090 				fname, lineno, 0 );
1091 			return 1;
1092 		}
1093 
1094 		rc = slap_str2ad( argv[1], &member_ad, &text );
1095 		if ( rc != LDAP_SUCCESS ) {
1096 			Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1097 				"\"dynlist-attrpair <member-ad> <URL-ad>\": "
1098 				"unable to find AttributeDescription \"%s\"\n",
1099 				fname, lineno, argv[1] );
1100 			return 1;
1101 		}
1102 
1103 		rc = slap_str2ad( argv[2], &ad, &text );
1104 		if ( rc != LDAP_SUCCESS ) {
1105 			Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1106 				"\"dynlist-attrpair <member-ad> <URL-ad>\": "
1107 				"unable to find AttributeDescription \"%s\"\n",
1108 				fname, lineno, argv[2] );
1109 			return 1;
1110 		}
1111 
1112 		if ( !is_at_subtype( ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
1113 			Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1114 				"\"dynlist-attrpair <member-ad> <URL-ad>\": "
1115 				"AttributeDescription \"%s\" "
1116 				"must be a subtype of \"labeledURI\"\n",
1117 				fname, lineno, argv[2] );
1118 			return 1;
1119 		}
1120 
1121 		for ( dlip = (dynlist_info_t **)&on->on_bi.bi_private;
1122 			*dlip; dlip = &(*dlip)->dli_next )
1123 		{
1124 			/*
1125 			 * The same URL attribute / member attribute pair
1126 			 * cannot be repeated, but we enforce this only
1127 			 * when the member attribute is unique. Performing
1128 			 * the check for multiple values would require
1129 			 * sorting and comparing the lists, which is left
1130 			 * as a future improvement
1131 			 */
1132 			if ( (*dlip)->dli_ad == ad &&
1133 			     (*dlip)->dli_dlm->dlm_next == NULL &&
1134 			     member_ad == (*dlip)->dli_dlm->dlm_member_ad ) {
1135 				Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1136 					"\"dynlist-attrpair <member-ad> <URL-ad>\": "
1137 					"URL attributeDescription \"%s\" already mapped.\n",
1138 					fname, lineno, ad->ad_cname.bv_val );
1139 #if 0
1140 				/* make it a warning... */
1141 				return 1;
1142 #endif
1143 			}
1144 		}
1145 
1146 		*dlip = (dynlist_info_t *)ch_calloc( 1, sizeof( dynlist_info_t ) );
1147 		(*dlip)->dli_oc = oc;
1148 		(*dlip)->dli_ad = ad;
1149 		(*dlip)->dli_dlm = (dynlist_map_t *)ch_calloc( 1, sizeof( dynlist_map_t ) );
1150 		(*dlip)->dli_dlm->dlm_member_ad = member_ad;
1151 		(*dlip)->dli_dlm->dlm_mapped_ad = NULL;
1152 
1153 		if ( dynlist_build_def_filter( *dlip ) ) {
1154 			ch_free( (*dlip)->dli_dlm );
1155 			ch_free( *dlip );
1156 			*dlip = NULL;
1157 			return 1;
1158 		}
1159 
1160 	} else {
1161 		rc = SLAP_CONF_UNKNOWN;
1162 	}
1163 
1164 	return rc;
1165 }
1166 
1167 #else
1168 enum {
1169 	DL_ATTRSET = 1,
1170 	DL_ATTRPAIR,
1171 	DL_ATTRPAIR_COMPAT,
1172 	DL_LAST
1173 };
1174 
1175 static ConfigDriver	dl_cfgen;
1176 
1177 /* XXXmanu 255 is the maximum arguments we allow. Can we go beyond? */
1178 static ConfigTable dlcfg[] = {
1179 	{ "dynlist-attrset", "group-oc> [uri] <URL-ad> <[mapped:]member-ad> [...]",
1180 		3, 0, 0, ARG_MAGIC|DL_ATTRSET, dl_cfgen,
1181 		"( OLcfgOvAt:8.1 NAME 'olcDlAttrSet' "
1182 			"DESC 'Dynamic list: <group objectClass>, <URL attributeDescription>, <member attributeDescription>' "
1183 			"EQUALITY caseIgnoreMatch "
1184 			"SYNTAX OMsDirectoryString "
1185 			"X-ORDERED 'VALUES' )",
1186 			NULL, NULL },
1187 	{ "dynlist-attrpair", "member-ad> <URL-ad",
1188 		3, 3, 0, ARG_MAGIC|DL_ATTRPAIR, dl_cfgen,
1189 			NULL, NULL, NULL },
1190 #ifdef TAKEOVER_DYNGROUP
1191 	{ "attrpair", "member-ad> <URL-ad",
1192 		3, 3, 0, ARG_MAGIC|DL_ATTRPAIR_COMPAT, dl_cfgen,
1193 			NULL, NULL, NULL },
1194 #endif
1195 	{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
1196 };
1197 
1198 static ConfigOCs dlocs[] = {
1199 	{ "( OLcfgOvOc:8.1 "
1200 		"NAME 'olcDynamicList' "
1201 		"DESC 'Dynamic list configuration' "
1202 		"SUP olcOverlayConfig "
1203 		"MAY olcDLattrSet )",
1204 		Cft_Overlay, dlcfg, NULL, NULL },
1205 	{ NULL, 0, NULL }
1206 };
1207 
1208 static int
1209 dl_cfgen( ConfigArgs *c )
1210 {
1211 	slap_overinst	*on = (slap_overinst *)c->bi;
1212 	dynlist_info_t	*dli = (dynlist_info_t *)on->on_bi.bi_private;
1213 
1214 	int		rc = 0, i;
1215 
1216 	if ( c->op == SLAP_CONFIG_EMIT ) {
1217 		switch( c->type ) {
1218 		case DL_ATTRSET:
1219 			for ( i = 0; dli; i++, dli = dli->dli_next ) {
1220 				struct berval	bv;
1221 				char		*ptr = c->cr_msg;
1222 				dynlist_map_t	*dlm;
1223 
1224 				assert( dli->dli_oc != NULL );
1225 				assert( dli->dli_ad != NULL );
1226 
1227 				/* FIXME: check buffer overflow! */
1228 				ptr += snprintf( c->cr_msg, sizeof( c->cr_msg ),
1229 					SLAP_X_ORDERED_FMT "%s", i,
1230 					dli->dli_oc->soc_cname.bv_val );
1231 
1232 				if ( !BER_BVISNULL( &dli->dli_uri ) ) {
1233 					*ptr++ = ' ';
1234 					*ptr++ = '"';
1235 					ptr = lutil_strncopy( ptr, dli->dli_uri.bv_val,
1236 						dli->dli_uri.bv_len );
1237 					*ptr++ = '"';
1238 				}
1239 
1240 				*ptr++ = ' ';
1241 				ptr = lutil_strncopy( ptr, dli->dli_ad->ad_cname.bv_val,
1242 					dli->dli_ad->ad_cname.bv_len );
1243 
1244 				for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
1245 					ptr[ 0 ] = ' ';
1246 					ptr++;
1247 					if ( dlm->dlm_mapped_ad ) {
1248 						ptr = lutil_strcopy( ptr, dlm->dlm_mapped_ad->ad_cname.bv_val );
1249 						ptr[ 0 ] = ':';
1250 						ptr++;
1251 					}
1252 
1253 					ptr = lutil_strcopy( ptr, dlm->dlm_member_ad->ad_cname.bv_val );
1254 				}
1255 
1256 				bv.bv_val = c->cr_msg;
1257 				bv.bv_len = ptr - bv.bv_val;
1258 				value_add_one( &c->rvalue_vals, &bv );
1259 			}
1260 			break;
1261 
1262 		case DL_ATTRPAIR_COMPAT:
1263 		case DL_ATTRPAIR:
1264 			rc = 1;
1265 			break;
1266 
1267 		default:
1268 			rc = 1;
1269 			break;
1270 		}
1271 
1272 		return rc;
1273 
1274 	} else if ( c->op == LDAP_MOD_DELETE ) {
1275 		switch( c->type ) {
1276 		case DL_ATTRSET:
1277 			if ( c->valx < 0 ) {
1278 				dynlist_info_t	*dli_next;
1279 
1280 				for ( dli_next = dli; dli_next; dli = dli_next ) {
1281 					dynlist_map_t *dlm = dli->dli_dlm;
1282 					dynlist_map_t *dlm_next;
1283 
1284 					dli_next = dli->dli_next;
1285 
1286 					if ( !BER_BVISNULL( &dli->dli_uri ) ) {
1287 						ch_free( dli->dli_uri.bv_val );
1288 					}
1289 
1290 					if ( dli->dli_lud != NULL ) {
1291 						ldap_free_urldesc( dli->dli_lud );
1292 					}
1293 
1294 					if ( !BER_BVISNULL( &dli->dli_uri_nbase ) ) {
1295 						ber_memfree( dli->dli_uri_nbase.bv_val );
1296 					}
1297 
1298 					if ( dli->dli_uri_filter != NULL ) {
1299 						filter_free( dli->dli_uri_filter );
1300 					}
1301 
1302 					ch_free( dli->dli_default_filter.bv_val );
1303 
1304 					while ( dlm != NULL ) {
1305 						dlm_next = dlm->dlm_next;
1306 						ch_free( dlm );
1307 						dlm = dlm_next;
1308 					}
1309 					ch_free( dli );
1310 				}
1311 
1312 				on->on_bi.bi_private = NULL;
1313 
1314 			} else {
1315 				dynlist_info_t	**dlip;
1316 				dynlist_map_t *dlm;
1317 				dynlist_map_t *dlm_next;
1318 
1319 				for ( i = 0, dlip = (dynlist_info_t **)&on->on_bi.bi_private;
1320 					i < c->valx; i++ )
1321 				{
1322 					if ( *dlip == NULL ) {
1323 						return 1;
1324 					}
1325 					dlip = &(*dlip)->dli_next;
1326 				}
1327 
1328 				dli = *dlip;
1329 				*dlip = dli->dli_next;
1330 
1331 				if ( !BER_BVISNULL( &dli->dli_uri ) ) {
1332 					ch_free( dli->dli_uri.bv_val );
1333 				}
1334 
1335 				if ( dli->dli_lud != NULL ) {
1336 					ldap_free_urldesc( dli->dli_lud );
1337 				}
1338 
1339 				if ( !BER_BVISNULL( &dli->dli_uri_nbase ) ) {
1340 					ber_memfree( dli->dli_uri_nbase.bv_val );
1341 				}
1342 
1343 				if ( dli->dli_uri_filter != NULL ) {
1344 					filter_free( dli->dli_uri_filter );
1345 				}
1346 
1347 				ch_free( dli->dli_default_filter.bv_val );
1348 
1349 				dlm = dli->dli_dlm;
1350 				while ( dlm != NULL ) {
1351 					dlm_next = dlm->dlm_next;
1352 					ch_free( dlm );
1353 					dlm = dlm_next;
1354 				}
1355 				ch_free( dli );
1356 
1357 				dli = (dynlist_info_t *)on->on_bi.bi_private;
1358 			}
1359 			break;
1360 
1361 		case DL_ATTRPAIR_COMPAT:
1362 		case DL_ATTRPAIR:
1363 			rc = 1;
1364 			break;
1365 
1366 		default:
1367 			rc = 1;
1368 			break;
1369 		}
1370 
1371 		return rc;
1372 	}
1373 
1374 	switch( c->type ) {
1375 	case DL_ATTRSET: {
1376 		dynlist_info_t		**dlip,
1377 					*dli_next = NULL;
1378 		ObjectClass		*oc = NULL;
1379 		AttributeDescription	*ad = NULL;
1380 		int			attridx = 2;
1381 		LDAPURLDesc		*lud = NULL;
1382 		struct berval		nbase = BER_BVNULL;
1383 		Filter			*filter = NULL;
1384 		struct berval		uri = BER_BVNULL;
1385 		dynlist_map_t           *dlm = NULL, *dlml = NULL;
1386 		const char		*text;
1387 
1388 		oc = oc_find( c->argv[ 1 ] );
1389 		if ( oc == NULL ) {
1390 			snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
1391 				"unable to find ObjectClass \"%s\"",
1392 				c->argv[ 1 ] );
1393 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1394 				c->log, c->cr_msg, 0 );
1395 			return 1;
1396 		}
1397 
1398 		if ( strncasecmp( c->argv[ attridx ], "ldap://", STRLENOF("ldap://") ) == 0 ) {
1399 			if ( ldap_url_parse( c->argv[ attridx ], &lud ) != LDAP_URL_SUCCESS ) {
1400 				snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
1401 					"unable to parse URI \"%s\"",
1402 					c->argv[ attridx ] );
1403 				rc = 1;
1404 				goto done_uri;
1405 			}
1406 
1407 			if ( lud->lud_host != NULL ) {
1408 				if ( lud->lud_host[0] == '\0' ) {
1409 					ch_free( lud->lud_host );
1410 					lud->lud_host = NULL;
1411 
1412 				} else {
1413 					snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
1414 						"host not allowed in URI \"%s\"",
1415 						c->argv[ attridx ] );
1416 					rc = 1;
1417 					goto done_uri;
1418 				}
1419 			}
1420 
1421 			if ( lud->lud_attrs != NULL ) {
1422 				snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
1423 					"attrs not allowed in URI \"%s\"",
1424 					c->argv[ attridx ] );
1425 				rc = 1;
1426 				goto done_uri;
1427 			}
1428 
1429 			if ( lud->lud_exts != NULL ) {
1430 				snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
1431 					"extensions not allowed in URI \"%s\"",
1432 					c->argv[ attridx ] );
1433 				rc = 1;
1434 				goto done_uri;
1435 			}
1436 
1437 			if ( lud->lud_dn != NULL && lud->lud_dn[ 0 ] != '\0' ) {
1438 				struct berval dn;
1439 				ber_str2bv( lud->lud_dn, 0, 0, &dn );
1440 				rc = dnNormalize( 0, NULL, NULL, &dn, &nbase, NULL );
1441 				if ( rc != LDAP_SUCCESS ) {
1442 					snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
1443 						"DN normalization failed in URI \"%s\"",
1444 						c->argv[ attridx ] );
1445 					goto done_uri;
1446 				}
1447 			}
1448 
1449 			if ( lud->lud_filter != NULL && lud->lud_filter[ 0 ] != '\0' ) {
1450 				filter = str2filter( lud->lud_filter );
1451 				if ( filter == NULL ) {
1452 					snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
1453 						"filter parsing failed in URI \"%s\"",
1454 						c->argv[ attridx ] );
1455 					rc = 1;
1456 					goto done_uri;
1457 				}
1458 			}
1459 
1460 			ber_str2bv( c->argv[ attridx ], 0, 1, &uri );
1461 
1462 done_uri:;
1463 			if ( rc ) {
1464 				if ( lud ) {
1465 					ldap_free_urldesc( lud );
1466 				}
1467 
1468 				if ( !BER_BVISNULL( &nbase ) ) {
1469 					ber_memfree( nbase.bv_val );
1470 				}
1471 
1472 				if ( filter != NULL ) {
1473 					filter_free( filter );
1474 				}
1475 
1476 				Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1477 					c->log, c->cr_msg, 0 );
1478 
1479 				return rc;
1480 			}
1481 
1482 			attridx++;
1483 		}
1484 
1485 		rc = slap_str2ad( c->argv[ attridx ], &ad, &text );
1486 		if ( rc != LDAP_SUCCESS ) {
1487 			snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
1488 				"unable to find AttributeDescription \"%s\"",
1489 				c->argv[ attridx ] );
1490 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1491 				c->log, c->cr_msg, 0 );
1492 			return 1;
1493 		}
1494 
1495 		if ( !is_at_subtype( ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
1496 			snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
1497 				"AttributeDescription \"%s\" "
1498 				"must be a subtype of \"labeledURI\"",
1499 				c->argv[ attridx ] );
1500 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1501 				c->log, c->cr_msg, 0 );
1502 			return 1;
1503 		}
1504 
1505 		attridx++;
1506 
1507 		for ( i = attridx; i < c->argc; i++ ) {
1508 			char *arg;
1509 			char *cp;
1510 			AttributeDescription *member_ad = NULL;
1511 			AttributeDescription *mapped_ad = NULL;
1512 			dynlist_map_t *dlmp;
1513 
1514 
1515 			/*
1516 			 * If no mapped attribute is given, dn is used
1517 			 * for backward compatibility.
1518 			 */
1519 			arg = c->argv[i];
1520 			if ( ( cp = strchr( arg, ':' ) ) != NULL ) {
1521 				struct berval bv;
1522 				ber_str2bv( arg, cp - arg, 0, &bv );
1523 				rc = slap_bv2ad( &bv, &mapped_ad, &text );
1524 				if ( rc != LDAP_SUCCESS ) {
1525 					snprintf( c->cr_msg, sizeof( c->cr_msg ),
1526 						DYNLIST_USAGE
1527 						"unable to find mapped AttributeDescription #%d \"%s\"\n",
1528 						i - 3, c->argv[ i ] );
1529 					Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1530 						c->log, c->cr_msg, 0 );
1531 					return 1;
1532 				}
1533 				arg = cp + 1;
1534 			}
1535 
1536 			rc = slap_str2ad( arg, &member_ad, &text );
1537 			if ( rc != LDAP_SUCCESS ) {
1538 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
1539 					DYNLIST_USAGE
1540 					"unable to find AttributeDescription #%d \"%s\"\n",
1541 					i - 3, c->argv[ i ] );
1542 				Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1543 					c->log, c->cr_msg, 0 );
1544 				return 1;
1545 			}
1546 
1547 			dlmp = (dynlist_map_t *)ch_calloc( 1, sizeof( dynlist_map_t ) );
1548 			if ( dlm == NULL ) {
1549 				dlm = dlmp;
1550 			}
1551 			dlmp->dlm_member_ad = member_ad;
1552 			dlmp->dlm_mapped_ad = mapped_ad;
1553 			dlmp->dlm_next = NULL;
1554 
1555 			if ( dlml != NULL )
1556 				dlml->dlm_next = dlmp;
1557 			dlml = dlmp;
1558 		}
1559 
1560 		if ( c->valx > 0 ) {
1561 			int	i;
1562 
1563 			for ( i = 0, dlip = (dynlist_info_t **)&on->on_bi.bi_private;
1564 				i < c->valx; i++ )
1565 			{
1566 				if ( *dlip == NULL ) {
1567 					snprintf( c->cr_msg, sizeof( c->cr_msg ),
1568 						DYNLIST_USAGE
1569 						"invalid index {%d}\n",
1570 						c->valx );
1571 					Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1572 						c->log, c->cr_msg, 0 );
1573 					return 1;
1574 				}
1575 				dlip = &(*dlip)->dli_next;
1576 			}
1577 			dli_next = *dlip;
1578 
1579 		} else {
1580 			for ( dlip = (dynlist_info_t **)&on->on_bi.bi_private;
1581 				*dlip; dlip = &(*dlip)->dli_next )
1582 				/* goto last */;
1583 		}
1584 
1585 		*dlip = (dynlist_info_t *)ch_calloc( 1, sizeof( dynlist_info_t ) );
1586 
1587 		(*dlip)->dli_oc = oc;
1588 		(*dlip)->dli_ad = ad;
1589 		(*dlip)->dli_dlm = dlm;
1590 		(*dlip)->dli_next = dli_next;
1591 
1592 		(*dlip)->dli_lud = lud;
1593 		(*dlip)->dli_uri_nbase = nbase;
1594 		(*dlip)->dli_uri_filter = filter;
1595 		(*dlip)->dli_uri = uri;
1596 
1597 		rc = dynlist_build_def_filter( *dlip );
1598 
1599 		} break;
1600 
1601 	case DL_ATTRPAIR_COMPAT:
1602 		snprintf( c->cr_msg, sizeof( c->cr_msg ),
1603 			"warning: \"attrpair\" only supported for limited "
1604 			"backward compatibility with overlay \"dyngroup\"" );
1605 		Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1606 		/* fallthru */
1607 
1608 	case DL_ATTRPAIR: {
1609 		dynlist_info_t		**dlip;
1610 		ObjectClass		*oc = NULL;
1611 		AttributeDescription	*ad = NULL,
1612 					*member_ad = NULL;
1613 		const char		*text;
1614 
1615 		oc = oc_find( "groupOfURLs" );
1616 		if ( oc == NULL ) {
1617 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1618 				"\"dynlist-attrpair <member-ad> <URL-ad>\": "
1619 				"unable to find default ObjectClass \"groupOfURLs\"" );
1620 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1621 				c->log, c->cr_msg, 0 );
1622 			return 1;
1623 		}
1624 
1625 		rc = slap_str2ad( c->argv[ 1 ], &member_ad, &text );
1626 		if ( rc != LDAP_SUCCESS ) {
1627 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1628 				"\"dynlist-attrpair <member-ad> <URL-ad>\": "
1629 				"unable to find AttributeDescription \"%s\"",
1630 				c->argv[ 1 ] );
1631 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1632 				c->log, c->cr_msg, 0 );
1633 			return 1;
1634 		}
1635 
1636 		rc = slap_str2ad( c->argv[ 2 ], &ad, &text );
1637 		if ( rc != LDAP_SUCCESS ) {
1638 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1639 				"\"dynlist-attrpair <member-ad> <URL-ad>\": "
1640 				"unable to find AttributeDescription \"%s\"\n",
1641 				c->argv[ 2 ] );
1642 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1643 				c->log, c->cr_msg, 0 );
1644 			return 1;
1645 		}
1646 
1647 		if ( !is_at_subtype( ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
1648 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1649 				DYNLIST_USAGE
1650 				"AttributeDescription \"%s\" "
1651 				"must be a subtype of \"labeledURI\"",
1652 				c->argv[ 2 ] );
1653 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1654 				c->log, c->cr_msg, 0 );
1655 			return 1;
1656 		}
1657 
1658 		for ( dlip = (dynlist_info_t **)&on->on_bi.bi_private;
1659 			*dlip; dlip = &(*dlip)->dli_next )
1660 		{
1661 			/*
1662 			 * The same URL attribute / member attribute pair
1663 			 * cannot be repeated, but we enforce this only
1664 			 * when the member attribute is unique. Performing
1665 			 * the check for multiple values would require
1666 			 * sorting and comparing the lists, which is left
1667 			 * as a future improvement
1668 			 */
1669 			if ( (*dlip)->dli_ad == ad &&
1670 			     (*dlip)->dli_dlm->dlm_next == NULL &&
1671 			     member_ad == (*dlip)->dli_dlm->dlm_member_ad ) {
1672 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
1673 					"\"dynlist-attrpair <member-ad> <URL-ad>\": "
1674 					"URL attributeDescription \"%s\" already mapped.\n",
1675 					ad->ad_cname.bv_val );
1676 				Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1677 					c->log, c->cr_msg, 0 );
1678 #if 0
1679 				/* make it a warning... */
1680 				return 1;
1681 #endif
1682 			}
1683 		}
1684 
1685 		*dlip = (dynlist_info_t *)ch_calloc( 1, sizeof( dynlist_info_t ) );
1686 
1687 		(*dlip)->dli_oc = oc;
1688 		(*dlip)->dli_ad = ad;
1689 		(*dlip)->dli_dlm = (dynlist_map_t *)ch_calloc( 1, sizeof( dynlist_map_t ) );
1690 		(*dlip)->dli_dlm->dlm_member_ad = member_ad;
1691 		(*dlip)->dli_dlm->dlm_mapped_ad = NULL;
1692 
1693 		rc = dynlist_build_def_filter( *dlip );
1694 
1695 		} break;
1696 
1697 	default:
1698 		rc = 1;
1699 		break;
1700 	}
1701 
1702 	return rc;
1703 }
1704 #endif
1705 
1706 static int
1707 dynlist_db_open(
1708 	BackendDB	*be,
1709 	ConfigReply	*cr )
1710 {
1711 	slap_overinst		*on = (slap_overinst *) be->bd_info;
1712 	dynlist_info_t		*dli = (dynlist_info_t *)on->on_bi.bi_private;
1713 	ObjectClass		*oc = NULL;
1714 	AttributeDescription	*ad = NULL;
1715 	const char	*text;
1716 	int rc;
1717 
1718 	if ( dli == NULL ) {
1719 		dli = ch_calloc( 1, sizeof( dynlist_info_t ) );
1720 		on->on_bi.bi_private = (void *)dli;
1721 	}
1722 
1723 	for ( ; dli; dli = dli->dli_next ) {
1724 		if ( dli->dli_oc == NULL ) {
1725 			if ( oc == NULL ) {
1726 				oc = oc_find( "groupOfURLs" );
1727 				if ( oc == NULL ) {
1728 					snprintf( cr->msg, sizeof( cr->msg),
1729 						"unable to fetch objectClass \"groupOfURLs\"" );
1730 					Debug( LDAP_DEBUG_ANY, "dynlist_db_open: %s.\n", cr->msg, 0, 0 );
1731 					return 1;
1732 				}
1733 			}
1734 
1735 			dli->dli_oc = oc;
1736 		}
1737 
1738 		if ( dli->dli_ad == NULL ) {
1739 			if ( ad == NULL ) {
1740 				rc = slap_str2ad( "memberURL", &ad, &text );
1741 				if ( rc != LDAP_SUCCESS ) {
1742 					snprintf( cr->msg, sizeof( cr->msg),
1743 						"unable to fetch attributeDescription \"memberURL\": %d (%s)",
1744 						rc, text );
1745 					Debug( LDAP_DEBUG_ANY, "dynlist_db_open: %s.\n", cr->msg, 0, 0 );
1746 					return 1;
1747 				}
1748 			}
1749 
1750 			dli->dli_ad = ad;
1751 		}
1752 
1753 		if ( BER_BVISNULL( &dli->dli_default_filter ) ) {
1754 			rc = dynlist_build_def_filter( dli );
1755 			if ( rc != 0 ) {
1756 				return rc;
1757 			}
1758 		}
1759 	}
1760 
1761 	if ( ad_dgIdentity == NULL ) {
1762 		rc = slap_str2ad( "dgIdentity", &ad_dgIdentity, &text );
1763 		if ( rc != LDAP_SUCCESS ) {
1764 			snprintf( cr->msg, sizeof( cr->msg),
1765 				"unable to fetch attributeDescription \"dgIdentity\": %d (%s)",
1766 				rc, text );
1767 			Debug( LDAP_DEBUG_ANY, "dynlist_db_open: %s\n", cr->msg, 0, 0 );
1768 			/* Just a warning */
1769 		}
1770 	}
1771 
1772 	if ( ad_dgAuthz == NULL ) {
1773 		rc = slap_str2ad( "dgAuthz", &ad_dgAuthz, &text );
1774 		if ( rc != LDAP_SUCCESS ) {
1775 			snprintf( cr->msg, sizeof( cr->msg),
1776 				"unable to fetch attributeDescription \"dgAuthz\": %d (%s)",
1777 				rc, text );
1778 			Debug( LDAP_DEBUG_ANY, "dynlist_db_open: %s\n", cr->msg, 0, 0 );
1779 			/* Just a warning */
1780 		}
1781 	}
1782 
1783 	return 0;
1784 }
1785 
1786 static int
1787 dynlist_db_destroy(
1788 	BackendDB	*be,
1789 	ConfigReply	*cr )
1790 {
1791 	slap_overinst	*on = (slap_overinst *) be->bd_info;
1792 
1793 	if ( on->on_bi.bi_private ) {
1794 		dynlist_info_t	*dli = (dynlist_info_t *)on->on_bi.bi_private,
1795 				*dli_next;
1796 
1797 		for ( dli_next = dli; dli_next; dli = dli_next ) {
1798 			dynlist_map_t *dlm;
1799 			dynlist_map_t *dlm_next;
1800 
1801 			dli_next = dli->dli_next;
1802 
1803 			if ( !BER_BVISNULL( &dli->dli_uri ) ) {
1804 				ch_free( dli->dli_uri.bv_val );
1805 			}
1806 
1807 			if ( dli->dli_lud != NULL ) {
1808 				ldap_free_urldesc( dli->dli_lud );
1809 			}
1810 
1811 			if ( !BER_BVISNULL( &dli->dli_uri_nbase ) ) {
1812 				ber_memfree( dli->dli_uri_nbase.bv_val );
1813 			}
1814 
1815 			if ( dli->dli_uri_filter != NULL ) {
1816 				filter_free( dli->dli_uri_filter );
1817 			}
1818 
1819 			ch_free( dli->dli_default_filter.bv_val );
1820 
1821 			dlm = dli->dli_dlm;
1822 			while ( dlm != NULL ) {
1823 				dlm_next = dlm->dlm_next;
1824 				ch_free( dlm );
1825 				dlm = dlm_next;
1826 			}
1827 			ch_free( dli );
1828 		}
1829 	}
1830 
1831 	return 0;
1832 }
1833 
1834 static slap_overinst	dynlist = { { NULL } };
1835 #ifdef TAKEOVER_DYNGROUP
1836 static char		*obsolete_names[] = {
1837 	"dyngroup",
1838 	NULL
1839 };
1840 #endif
1841 
1842 #if SLAPD_OVER_DYNLIST == SLAPD_MOD_DYNAMIC
1843 static
1844 #endif /* SLAPD_OVER_DYNLIST == SLAPD_MOD_DYNAMIC */
1845 int
1846 dynlist_initialize(void)
1847 {
1848 #ifndef OL_2_2_COMPAT
1849 	int	rc = 0;
1850 #endif
1851 
1852 	dynlist.on_bi.bi_type = "dynlist";
1853 
1854 #ifdef TAKEOVER_DYNGROUP
1855 	/* makes dynlist incompatible with dyngroup */
1856 	dynlist.on_bi.bi_obsolete_names = obsolete_names;
1857 #endif
1858 
1859 #ifdef OL_2_2_COMPAT
1860 	dynlist.on_bi.bi_db_config = dynlist_db_config;
1861 #else
1862 	dynlist.on_bi.bi_db_config = config_generic_wrapper;
1863 #endif
1864 	dynlist.on_bi.bi_db_open = dynlist_db_open;
1865 	dynlist.on_bi.bi_db_destroy = dynlist_db_destroy;
1866 
1867 	dynlist.on_response = dynlist_response;
1868 
1869 #ifndef OL_2_2_COMPAT
1870 	dynlist.on_bi.bi_cf_ocs = dlocs;
1871 
1872 	rc = config_register_schema( dlcfg, dlocs );
1873 	if ( rc ) {
1874 		return rc;
1875 	}
1876 #endif
1877 
1878 	return overlay_register( &dynlist );
1879 }
1880 
1881 #if SLAPD_OVER_DYNLIST == SLAPD_MOD_DYNAMIC
1882 int
1883 init_module( int argc, char *argv[] )
1884 {
1885 	return dynlist_initialize();
1886 }
1887 #endif
1888 
1889 #endif /* SLAPD_OVER_DYNLIST */
1890