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