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