xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/overlays/dynlist.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
1 /*	$NetBSD: dynlist.c,v 1.3 2021/08/14 16:15:02 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-2021 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.3 2021/08/14 16:15:02 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 "slap-config.h"
42 #include "lutil.h"
43 
44 static AttributeDescription *ad_dgIdentity, *ad_dgAuthz;
45 static AttributeDescription *ad_memberOf;
46 
47 typedef struct dynlist_map_t {
48 	AttributeDescription	*dlm_member_ad;
49 	AttributeDescription	*dlm_mapped_ad;
50 	AttributeDescription	*dlm_memberOf_ad;
51 	ObjectClass				*dlm_static_oc;
52 	int						 dlm_memberOf_nested;
53 	int						 dlm_member_oper;
54 	int						 dlm_memberOf_oper;
55 	struct dynlist_map_t	*dlm_next;
56 } dynlist_map_t;
57 
58 typedef struct dynlist_info_t {
59 	ObjectClass		*dli_oc;
60 	AttributeDescription	*dli_ad;
61 	struct dynlist_map_t	*dli_dlm;
62 	struct berval		dli_uri;
63 	LDAPURLDesc		*dli_lud;
64 	struct berval		dli_uri_nbase;
65 	Filter			*dli_uri_filter;
66 	struct berval		dli_default_filter;
67 	struct dynlist_info_t	*dli_next;
68 } dynlist_info_t;
69 
70 typedef struct dynlist_gen_t {
71 	dynlist_info_t	*dlg_dli;
72 	int				 dlg_memberOf;
73 } dynlist_gen_t;
74 
75 #define DYNLIST_USAGE \
76 	"\"dynlist-attrset <oc> [uri] <URL-ad> [[<mapped-ad>:]<member-ad>[+<memberOf-ad>[@<static-oc>[*]] ...]\": "
77 
78 static int
ad_infilter(AttributeDescription * ad,Filter * f)79 ad_infilter( AttributeDescription *ad, Filter *f )
80 {
81 	if ( !f )
82 		return 0;
83 
84 	switch( f->f_choice & SLAPD_FILTER_MASK ) {
85 	case SLAPD_FILTER_COMPUTED:
86 		return 0;
87 	case LDAP_FILTER_PRESENT:
88 		return f->f_desc == ad;
89 	case LDAP_FILTER_EQUALITY:
90 	case LDAP_FILTER_GE:
91 	case LDAP_FILTER_LE:
92 	case LDAP_FILTER_APPROX:
93 	case LDAP_FILTER_SUBSTRINGS:
94 	case LDAP_FILTER_EXT:
95 		return f->f_av_desc == ad;
96 	case LDAP_FILTER_AND:
97 	case LDAP_FILTER_OR:
98 	case LDAP_FILTER_NOT: {
99 		for ( f = f->f_list; f; f = f->f_next )
100 			if ( ad_infilter( ad, f ))
101 				return 1;
102 		}
103 	}
104 	return 0;
105 }
106 
107 static int
dynlist_make_filter(Operation * op,Entry * e,dynlist_info_t * dli,const char * url,struct berval * oldf,struct berval * newf)108 dynlist_make_filter( Operation *op, Entry *e, dynlist_info_t *dli, const char *url, struct berval *oldf, struct berval *newf )
109 {
110 	char		*ptr;
111 	int		needBrackets = 0;
112 
113 	assert( oldf != NULL );
114 	assert( newf != NULL );
115 	assert( !BER_BVISNULL( oldf ) );
116 	assert( !BER_BVISEMPTY( oldf ) );
117 
118 	if ( oldf->bv_val[0] != '(' ) {
119 		Debug( LDAP_DEBUG_ANY, "%s: dynlist, DN=\"%s\": missing parentheses in URI=\"%s\" filter\n",
120 			op->o_log_prefix, e->e_name.bv_val, url );
121 		needBrackets = 2;
122 	}
123 
124 	newf->bv_len = STRLENOF( "(&(!(objectClass=" "))" ")" )
125 		+ dli->dli_oc->soc_cname.bv_len + oldf->bv_len + needBrackets;
126 	newf->bv_val = op->o_tmpalloc( newf->bv_len + 1, op->o_tmpmemctx );
127 	if ( newf->bv_val == NULL ) {
128 		return -1;
129 	}
130 	ptr = lutil_strcopy( newf->bv_val, "(&(!(objectClass=" );
131 	ptr = lutil_strcopy( ptr, dli->dli_oc->soc_cname.bv_val );
132 	ptr = lutil_strcopy( ptr, "))" );
133 	if ( needBrackets ) *ptr++ = '(';
134 	ptr = lutil_strcopy( ptr, oldf->bv_val );
135 	if ( needBrackets ) *ptr++ = ')';
136 	ptr = lutil_strcopy( ptr, ")" );
137 	newf->bv_len = ptr - newf->bv_val;
138 
139 	return 0;
140 }
141 
142 /* dynlist_sc_update() callback info set by dynlist_prepare_entry() */
143 typedef struct dynlist_sc_t {
144 	dynlist_info_t    *dlc_dli;
145 	Entry		*dlc_e;
146 	char		**dlc_attrs;
147 } dynlist_sc_t;
148 
149 static int
dynlist_sc_update(Operation * op,SlapReply * rs)150 dynlist_sc_update( Operation *op, SlapReply *rs )
151 {
152 	Entry			*e;
153 	Attribute		*a;
154 	int			opattrs,
155 				userattrs;
156 	AccessControlState	acl_state = ACL_STATE_INIT;
157 
158 	dynlist_sc_t		*dlc;
159 	dynlist_map_t		*dlm;
160 
161 	if ( rs->sr_type != REP_SEARCH ) {
162 		return 0;
163 	}
164 
165 	dlc = (dynlist_sc_t *)op->o_callback->sc_private;
166 	e = dlc->dlc_e;
167 
168 	assert( e != NULL );
169 	assert( rs->sr_entry != NULL );
170 
171 	/* test access to entry */
172 	if ( !access_allowed( op, rs->sr_entry, slap_schema.si_ad_entry,
173 				NULL, ACL_READ, NULL ) )
174 	{
175 		goto done;
176 	}
177 
178 	/* if there is only one member_ad, and it's not mapped,
179 	 * consider it as old-style member listing */
180 	dlm = dlc->dlc_dli->dli_dlm;
181 	if ( dlm && dlm->dlm_mapped_ad == NULL && dlm->dlm_next == NULL && dlc->dlc_attrs == NULL ) {
182 		/* if access allowed, try to add values, emulating permissive
183 		 * control to silently ignore duplicates */
184 		if ( access_allowed( op, rs->sr_entry, slap_schema.si_ad_entry,
185 					NULL, ACL_READ, NULL ) )
186 		{
187 			Modification	mod;
188 			const char	*text = NULL;
189 			char		textbuf[1024];
190 			struct berval	vals[ 2 ], nvals[ 2 ];
191 
192 			vals[ 0 ] = rs->sr_entry->e_name;
193 			BER_BVZERO( &vals[ 1 ] );
194 			nvals[ 0 ] = rs->sr_entry->e_nname;
195 			BER_BVZERO( &nvals[ 1 ] );
196 
197 			mod.sm_op = LDAP_MOD_ADD;
198 			mod.sm_desc = dlm->dlm_member_ad;
199 			mod.sm_type = dlm->dlm_member_ad->ad_cname;
200 			mod.sm_values = vals;
201 			mod.sm_nvalues = nvals;
202 			mod.sm_numvals = 1;
203 
204 			(void)modify_add_values( e, &mod, /* permissive */ 1,
205 					&text, textbuf, sizeof( textbuf ) );
206 		}
207 
208 		goto done;
209 	}
210 
211 	opattrs = SLAP_OPATTRS( rs->sr_attr_flags );
212 	userattrs = SLAP_USERATTRS( rs->sr_attr_flags );
213 
214 	for ( a = rs->sr_entry->e_attrs; a != NULL; a = a->a_next ) {
215 		BerVarray	vals, nvals = NULL;
216 		int		i, j,
217 				is_oc = a->a_desc == slap_schema.si_ad_objectClass;
218 
219 		/* if attribute is not requested, skip it */
220 		if ( rs->sr_attrs == NULL ) {
221 			if ( is_at_operational( a->a_desc->ad_type ) ) {
222 				continue;
223 			}
224 
225 		} else {
226 			if ( is_at_operational( a->a_desc->ad_type ) ) {
227 				if ( !opattrs && !ad_inlist( a->a_desc, rs->sr_attrs ) )
228 				{
229 					continue;
230 				}
231 
232 			} else {
233 				if ( !userattrs && !ad_inlist( a->a_desc, rs->sr_attrs ) )
234 				{
235 					continue;
236 				}
237 			}
238 		}
239 
240 		/* test access to attribute */
241 		if ( op->ors_attrsonly ) {
242 			if ( !access_allowed( op, rs->sr_entry, a->a_desc, NULL,
243 						ACL_READ, &acl_state ) )
244 			{
245 				continue;
246 			}
247 		}
248 
249 		/* single-value check: keep first only */
250 		if ( is_at_single_value( a->a_desc->ad_type ) ) {
251 			if ( attr_find( e->e_attrs, a->a_desc ) != NULL ) {
252 				continue;
253 			}
254 		}
255 
256 		/* test access to attribute */
257 		i = a->a_numvals;
258 
259 		vals = op->o_tmpalloc( ( i + 1 ) * sizeof( struct berval ), op->o_tmpmemctx );
260 		if ( a->a_nvals != a->a_vals ) {
261 			nvals = op->o_tmpalloc( ( i + 1 ) * sizeof( struct berval ), op->o_tmpmemctx );
262 		}
263 
264 		for ( i = 0, j = 0; !BER_BVISNULL( &a->a_vals[i] ); i++ ) {
265 			if ( is_oc ) {
266 				ObjectClass	*soc = oc_bvfind( &a->a_vals[i] );
267 
268 				if ( soc->soc_kind == LDAP_SCHEMA_STRUCTURAL ) {
269 					continue;
270 				}
271 			}
272 
273 			if ( access_allowed( op, rs->sr_entry, a->a_desc,
274 						&a->a_nvals[i], ACL_READ, &acl_state ) )
275 			{
276 				vals[j] = a->a_vals[i];
277 				if ( nvals ) {
278 					nvals[j] = a->a_nvals[i];
279 				}
280 				j++;
281 			}
282 		}
283 
284 		/* if access allowed, try to add values, emulating permissive
285 		 * control to silently ignore duplicates */
286 		if ( j != 0 ) {
287 			Modification	mod;
288 			const char	*text = NULL;
289 			char		textbuf[1024];
290 			dynlist_map_t	*dlm;
291 			AttributeDescription *ad;
292 
293 			BER_BVZERO( &vals[j] );
294 			if ( nvals ) {
295 				BER_BVZERO( &nvals[j] );
296 			}
297 
298 			ad = a->a_desc;
299 			for ( dlm = dlc->dlc_dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
300 				if ( dlm->dlm_member_ad == a->a_desc ) {
301 					if ( dlm->dlm_mapped_ad ) {
302 						ad = dlm->dlm_mapped_ad;
303 					}
304 					break;
305 				}
306 			}
307 
308 			mod.sm_op = LDAP_MOD_ADD;
309 			mod.sm_desc = ad;
310 			mod.sm_type = ad->ad_cname;
311 			mod.sm_values = vals;
312 			mod.sm_nvalues = nvals;
313 			mod.sm_numvals = j;
314 
315 			(void)modify_add_values( e, &mod, /* permissive */ 1,
316 					&text, textbuf, sizeof( textbuf ) );
317 		}
318 
319 		op->o_tmpfree( vals, op->o_tmpmemctx );
320 		if ( nvals ) {
321 			op->o_tmpfree( nvals, op->o_tmpmemctx );
322 		}
323 	}
324 
325 done:;
326 	if ( rs->sr_flags & REP_ENTRY_MUSTBEFREED ) {
327 		entry_free( rs->sr_entry );
328 		rs->sr_entry = NULL;
329 		rs->sr_flags &= ~REP_ENTRY_MASK;
330 	}
331 
332 	return 0;
333 }
334 
335 typedef struct dynlist_name_t {
336 	struct berval dy_name;
337 	dynlist_info_t *dy_dli;
338 	AttributeDescription *dy_staticmember;
339 	int dy_seen;
340 	int dy_numuris;
341 	TAvlnode *dy_subs;
342 	TAvlnode *dy_sups;
343 	LDAPURLDesc *dy_uris[];
344 } dynlist_name_t;
345 
346 static void
dynlist_urlmembers(Operation * op,dynlist_name_t * dyn,slap_callback * sc)347 dynlist_urlmembers( Operation *op, dynlist_name_t *dyn, slap_callback *sc )
348 {
349 	Operation o = *op;
350 	LDAPURLDesc *ludp;
351 	int i;
352 
353 	o.ors_deref = LDAP_DEREF_NEVER;
354 	o.ors_limit = NULL;
355 	o.ors_tlimit = SLAP_NO_LIMIT;
356 	o.ors_slimit = SLAP_NO_LIMIT;
357 	o.ors_attrs = NULL;
358 	memset( o.o_ctrlflag, 0, sizeof( o.o_ctrlflag ));
359 	o.o_callback = sc;
360 
361 	for (i=0; i<dyn->dy_numuris; i++) {
362 		ludp = dyn->dy_uris[i];
363 		if ( ludp->lud_attrs )
364 			continue;
365 		o.o_req_dn.bv_val = ludp->lud_dn;
366 		o.o_req_dn.bv_len = ludp->lud_port;
367 		o.o_req_ndn = o.o_req_dn;
368 		o.ors_scope = ludp->lud_scope;
369 		o.ors_filter = (Filter *)ludp->lud_filter;
370 		filter2bv_x( op, o.ors_filter, &o.ors_filterstr );
371 		o.o_bd = select_backend( &o.o_req_ndn, 1 );
372 		if ( o.o_bd && o.o_bd->be_search ) {
373 			SlapReply r = { REP_SEARCH };
374 			r.sr_attr_flags = slap_attr_flags( o.ors_attrs );
375 			o.o_managedsait = SLAP_CONTROL_CRITICAL;
376 			(void)o.o_bd->be_search( &o, &r );
377 		}
378 		op->o_tmpfree( o.ors_filterstr.bv_val, op->o_tmpmemctx );
379 	}
380 }
381 
382 static void
dynlist_nested_memberOf(Entry * e,AttributeDescription * ad,TAvlnode * sups)383 dynlist_nested_memberOf( Entry *e, AttributeDescription *ad, TAvlnode *sups )
384 {
385 	TAvlnode *ptr;
386 	dynlist_name_t *dyn;
387 	Attribute *a;
388 
389 	a = attr_find( e->e_attrs, ad );
390 	for ( ptr = ldap_tavl_end( sups, TAVL_DIR_LEFT ); ptr;
391 		ptr = ldap_tavl_next( ptr, TAVL_DIR_RIGHT )) {
392 		dyn = ptr->avl_data;
393 		if ( a ) {
394 			unsigned slot;
395 			if ( attr_valfind( a, SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX |
396 				SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH |
397 				SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH,
398 				&dyn->dy_name, &slot, NULL ) == LDAP_SUCCESS )
399 				continue;
400 		}
401 		attr_merge_one( e, ad, &dyn->dy_name, &dyn->dy_name );
402 		if ( !a )
403 			a = attr_find( e->e_attrs, ad );
404 		if ( dyn->dy_sups )
405 			dynlist_nested_memberOf( e, ad, dyn->dy_sups );
406 	}
407 }
408 
409 typedef struct dynlist_member_t {
410 	Entry *dm_e;
411 	AttributeDescription *dm_ad;
412 	Modification dm_mod;
413 	TAvlnode *dm_groups;
414 	struct berval dm_bv[2];
415 	struct berval dm_nbv[2];
416 	const char *dm_text;
417 	char dm_textbuf[1024];
418 } dynlist_member_t;
419 
420 static int
dynlist_ptr_cmp(const void * c1,const void * c2)421 dynlist_ptr_cmp( const void *c1, const void *c2 )
422 {
423 	return ( c1 < c2 ) ? -1 : c1 > c2;
424 }
425 
426 static int
dynlist_nested_member_dg(Operation * op,SlapReply * rs)427 dynlist_nested_member_dg( Operation *op, SlapReply *rs )
428 {
429 	dynlist_member_t *dm = op->o_callback->sc_private;
430 
431 	if ( rs->sr_type != REP_SEARCH )
432 		return LDAP_SUCCESS;
433 
434 	dm->dm_bv[0] = rs->sr_entry->e_name;
435 	dm->dm_nbv[0] = rs->sr_entry->e_nname;
436 	modify_add_values( dm->dm_e, &dm->dm_mod, /* permissive */ 1,
437 		&dm->dm_text, dm->dm_textbuf, sizeof( dm->dm_textbuf ));
438 
439 	return LDAP_SUCCESS;
440 }
441 
442 static void
dynlist_nested_member(Operation * op,dynlist_member_t * dm,TAvlnode * subs)443 dynlist_nested_member( Operation *op, dynlist_member_t *dm, TAvlnode *subs )
444 {
445 	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
446 	TAvlnode *ptr;
447 	dynlist_name_t *dyn;
448 	Entry *ne;
449 	Attribute *a, *b;
450 
451 	a = attr_find( dm->dm_e->e_attrs, dm->dm_ad );
452 	if ( !a )
453 		return;
454 
455 	for ( ptr = ldap_tavl_end( subs, TAVL_DIR_LEFT ); ptr;
456 		ptr = ldap_tavl_next( ptr, TAVL_DIR_RIGHT )) {
457 		dyn = ptr->avl_data;
458 		if ( ldap_tavl_insert( &dm->dm_groups, dyn, dynlist_ptr_cmp, ldap_avl_dup_error ))
459 			continue;
460 		if ( overlay_entry_get_ov( op, &dyn->dy_name, NULL, NULL, 0, &ne, on ) != LDAP_SUCCESS || ne == NULL )
461 			continue;
462 		b = attr_find( ne->e_attrs, dm->dm_ad );
463 		if ( b ) {
464 			dm->dm_mod.sm_values = b->a_vals;
465 			dm->dm_mod.sm_nvalues = b->a_nvals;
466 			dm->dm_mod.sm_numvals = b->a_numvals;
467 			modify_add_values( dm->dm_e, &dm->dm_mod, /* permissive */ 1,
468 				&dm->dm_text, dm->dm_textbuf, sizeof( dm->dm_textbuf ));
469 		}
470 		overlay_entry_release_ov( op, ne, 0, on );
471 		if ( dyn->dy_numuris ) {
472 			slap_callback cb = { 0 };
473 			cb.sc_private = dm;
474 			BER_BVZERO( &dm->dm_bv[1] );
475 			BER_BVZERO( &dm->dm_nbv[1] );
476 			dm->dm_mod.sm_values = dm->dm_bv;
477 			dm->dm_mod.sm_nvalues = dm->dm_nbv;
478 			dm->dm_mod.sm_numvals = 1;
479 			cb.sc_response = dynlist_nested_member_dg;
480 			dynlist_urlmembers( op, dyn, &cb );
481 		}
482 		if ( dyn->dy_subs )
483 			dynlist_nested_member( op, dm, dyn->dy_subs );
484 	}
485 }
486 
487 static int
dynlist_prepare_entry(Operation * op,SlapReply * rs,dynlist_info_t * dli,dynlist_name_t * dyn)488 dynlist_prepare_entry( Operation *op, SlapReply *rs, dynlist_info_t *dli, dynlist_name_t *dyn )
489 {
490 	Attribute	*a, *id = NULL;
491 	slap_callback	cb = { 0 };
492 	Operation	o = *op;
493 	struct berval	*url;
494 	Entry		*e;
495 	int		opattrs,
496 			userattrs;
497 	dynlist_sc_t	dlc = { 0 };
498 	dynlist_map_t	*dlm;
499 
500 	e = rs->sr_entry;
501 	a = attrs_find( rs->sr_entry->e_attrs, dli->dli_ad );
502 	if ( a == NULL ) {
503 		/* FIXME: error? */
504 		goto checkdyn;
505 	}
506 
507 	opattrs = SLAP_OPATTRS( rs->sr_attr_flags );
508 	userattrs = SLAP_USERATTRS( rs->sr_attr_flags );
509 
510 	/* Don't generate member list if it wasn't requested */
511 	for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
512 		AttributeDescription *ad = dlm->dlm_mapped_ad ? dlm->dlm_mapped_ad : dlm->dlm_member_ad;
513 		if ( userattrs || ad_inlist( ad, rs->sr_attrs )
514 			|| ad_infilter( ad, op->ors_filter ))
515 			break;
516 	}
517 	if ( dli->dli_dlm && !dlm )
518 		goto checkdyn;
519 
520 	if ( ad_dgIdentity && ( id = attrs_find( rs->sr_entry->e_attrs, ad_dgIdentity ))) {
521 		Attribute *authz = NULL;
522 
523 		/* if not rootdn and dgAuthz is present,
524 		 * check if user can be authorized as dgIdentity */
525 		if ( ad_dgAuthz && !BER_BVISEMPTY( &id->a_nvals[0] ) && !be_isroot( op )
526 			&& ( authz = attrs_find( rs->sr_entry->e_attrs, ad_dgAuthz ) ) )
527 		{
528 			if ( slap_sasl_matches( op, authz->a_nvals,
529 				&o.o_ndn, &o.o_ndn ) != LDAP_SUCCESS )
530 			{
531 				goto checkdyn;
532 			}
533 		}
534 
535 		o.o_dn = id->a_vals[0];
536 		o.o_ndn = id->a_nvals[0];
537 		o.o_groups = NULL;
538 	}
539 
540 	/* ensure e is modifiable, but do not replace
541 	 * sr_entry yet since we have pointers into it */
542 	if ( !( rs->sr_flags & REP_ENTRY_MODIFIABLE ) ) {
543 		e = entry_dup( rs->sr_entry );
544 	}
545 
546 	dlc.dlc_e = e;
547 	dlc.dlc_dli = dli;
548 	cb.sc_private = &dlc;
549 	cb.sc_response = dynlist_sc_update;
550 
551 	o.o_callback = &cb;
552 	o.ors_deref = LDAP_DEREF_NEVER;
553 	o.ors_limit = NULL;
554 	o.ors_tlimit = SLAP_NO_LIMIT;
555 	o.ors_slimit = SLAP_NO_LIMIT;
556 	memset( o.o_ctrlflag, 0, sizeof( o.o_ctrlflag ));
557 
558 	for ( url = a->a_nvals; !BER_BVISNULL( url ); url++ ) {
559 		LDAPURLDesc	*lud = NULL;
560 		int		i, j;
561 		struct berval	dn;
562 		int		rc;
563 
564 		BER_BVZERO( &o.o_req_dn );
565 		BER_BVZERO( &o.o_req_ndn );
566 		o.ors_filter = NULL;
567 		o.ors_attrs = NULL;
568 		BER_BVZERO( &o.ors_filterstr );
569 
570 		if ( ldap_url_parse( url->bv_val, &lud ) != LDAP_URL_SUCCESS ) {
571 			/* FIXME: error? */
572 			continue;
573 		}
574 
575 		if ( lud->lud_host != NULL ) {
576 			/* FIXME: host not allowed; reject as illegal? */
577 			Debug( LDAP_DEBUG_ANY, "dynlist_prepare_entry(\"%s\"): "
578 				"illegal URI \"%s\"\n",
579 				e->e_name.bv_val, url->bv_val );
580 			goto cleanup;
581 		}
582 
583 		if ( lud->lud_dn == NULL ) {
584 			/* note that an empty base is not honored in terms
585 			 * of defaultSearchBase, because select_backend()
586 			 * is not aware of the defaultSearchBase option;
587 			 * this can be useful in case of a database serving
588 			 * the empty suffix */
589 			BER_BVSTR( &dn, "" );
590 
591 		} else {
592 			ber_str2bv( lud->lud_dn, 0, 0, &dn );
593 		}
594 		rc = dnPrettyNormal( NULL, &dn, &o.o_req_dn, &o.o_req_ndn, op->o_tmpmemctx );
595 		if ( rc != LDAP_SUCCESS ) {
596 			/* FIXME: error? */
597 			goto cleanup;
598 		}
599 		o.ors_scope = lud->lud_scope;
600 
601 		for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
602 			if ( dlm->dlm_mapped_ad != NULL ) {
603 				break;
604 			}
605 		}
606 
607 		if ( dli->dli_dlm && !dlm ) {
608 			/* if ( lud->lud_attrs != NULL ),
609 			 * the URL should be ignored */
610 			o.ors_attrs = slap_anlist_no_attrs;
611 
612 		} else if ( lud->lud_attrs == NULL ) {
613 			o.ors_attrs = rs->sr_attrs;
614 
615 		} else {
616 			for ( i = 0; lud->lud_attrs[i]; i++)
617 				/* just count */ ;
618 
619 			o.ors_attrs = op->o_tmpcalloc( i + 1, sizeof( AttributeName ), op->o_tmpmemctx );
620 			for ( i = 0, j = 0; lud->lud_attrs[i]; i++) {
621 				const char	*text = NULL;
622 
623 				ber_str2bv( lud->lud_attrs[i], 0, 0, &o.ors_attrs[j].an_name );
624 				o.ors_attrs[j].an_desc = NULL;
625 				(void)slap_bv2ad( &o.ors_attrs[j].an_name, &o.ors_attrs[j].an_desc, &text );
626 				/* FIXME: ignore errors... */
627 
628 				if ( ad_infilter( o.ors_attrs[j].an_desc, op->ors_filter )) {
629 					/* if referenced in filter, must retrieve */
630 				} else if ( rs->sr_attrs == NULL ) {
631 					if ( o.ors_attrs[j].an_desc != NULL &&
632 							is_at_operational( o.ors_attrs[j].an_desc->ad_type ) )
633 					{
634 						continue;
635 					}
636 
637 				} else {
638 					if ( o.ors_attrs[j].an_desc != NULL &&
639 							is_at_operational( o.ors_attrs[j].an_desc->ad_type ) )
640 					{
641 						if ( !opattrs ) {
642 							continue;
643 						}
644 
645 						if ( !ad_inlist( o.ors_attrs[j].an_desc, rs->sr_attrs ) ) {
646 							/* lookup if mapped -- linear search,
647 							 * not very efficient unless list
648 							 * is very short */
649 							for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
650 								if ( dlm->dlm_member_ad == o.ors_attrs[j].an_desc ) {
651 									break;
652 								}
653 							}
654 
655 							if ( dlm == NULL ) {
656 								continue;
657 							}
658 						}
659 
660 					} else {
661 						if ( !userattrs &&
662 								o.ors_attrs[j].an_desc != NULL &&
663 								!ad_inlist( o.ors_attrs[j].an_desc, rs->sr_attrs ) )
664 						{
665 							/* lookup if mapped -- linear search,
666 							 * not very efficient unless list
667 							 * is very short */
668 							for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
669 								if ( dlm->dlm_member_ad == o.ors_attrs[j].an_desc ) {
670 									break;
671 								}
672 							}
673 
674 							if ( dlm == NULL ) {
675 								continue;
676 							}
677 						}
678 					}
679 				}
680 
681 				j++;
682 			}
683 
684 			if ( j == 0 ) {
685 				goto cleanup;
686 			}
687 
688 			BER_BVZERO( &o.ors_attrs[j].an_name );
689 		}
690 		dlc.dlc_attrs = lud->lud_attrs;
691 
692 		if ( lud->lud_filter == NULL ) {
693 			ber_dupbv_x( &o.ors_filterstr,
694 					&dli->dli_default_filter, op->o_tmpmemctx );
695 
696 		} else {
697 			/* don't allow recursion in lists */
698 			if ( lud->lud_attrs ) {
699 				struct berval	flt;
700 				ber_str2bv( lud->lud_filter, 0, 0, &flt );
701 				if ( dynlist_make_filter( op, rs->sr_entry, dli, url->bv_val, &flt, &o.ors_filterstr ) ) {
702 					/* error */
703 					goto cleanup;
704 				}
705 			} else {
706 				ber_str2bv( lud->lud_filter, 0, 0, &o.ors_filterstr );
707 			}
708 		}
709 		o.ors_filter = str2filter_x( op, o.ors_filterstr.bv_val );
710 		if ( o.ors_filter == NULL ) {
711 			goto cleanup;
712 		}
713 
714 		o.o_bd = select_backend( &o.o_req_ndn, 1 );
715 		if ( o.o_bd && o.o_bd->be_search ) {
716 			SlapReply	r = { REP_SEARCH };
717 			r.sr_attr_flags = slap_attr_flags( o.ors_attrs );
718 			o.o_managedsait = SLAP_CONTROL_CRITICAL;
719 			(void)o.o_bd->be_search( &o, &r );
720 		}
721 
722 cleanup:;
723 		if ( id ) {
724 			slap_op_groups_free( &o );
725 		}
726 		if ( o.ors_filter ) {
727 			filter_free_x( &o, o.ors_filter, 1 );
728 		}
729 		if ( o.ors_attrs && o.ors_attrs != rs->sr_attrs
730 				&& o.ors_attrs != slap_anlist_no_attrs )
731 		{
732 			op->o_tmpfree( o.ors_attrs, op->o_tmpmemctx );
733 		}
734 		if ( !BER_BVISNULL( &o.o_req_dn ) ) {
735 			op->o_tmpfree( o.o_req_dn.bv_val, op->o_tmpmemctx );
736 		}
737 		if ( !BER_BVISNULL( &o.o_req_ndn ) ) {
738 			op->o_tmpfree( o.o_req_ndn.bv_val, op->o_tmpmemctx );
739 		}
740 		if ( lud->lud_attrs ) {
741 			assert( BER_BVISNULL( &o.ors_filterstr )
742 				|| o.ors_filterstr.bv_val != lud->lud_filter );
743 			op->o_tmpfree( o.ors_filterstr.bv_val, op->o_tmpmemctx );
744 		} else {
745 			if ( o.ors_filterstr.bv_val != lud->lud_filter )
746 				op->o_tmpfree( o.ors_filterstr.bv_val, op->o_tmpmemctx );
747 		}
748 		ldap_free_urldesc( lud );
749 	}
750 
751 checkdyn:
752 	/* handle nested groups */
753 	if ( dyn && ( dyn->dy_sups || dyn->dy_subs )) {
754 		/* ensure e is modifiable */
755 		if ( e == rs->sr_entry && !( rs->sr_flags & REP_ENTRY_MODIFIABLE ) ) {
756 			e = entry_dup( rs->sr_entry );
757 			rs_replace_entry( op, rs, (slap_overinst *)op->o_bd->bd_info, e );
758 			rs->sr_flags |= REP_ENTRY_MODIFIABLE | REP_ENTRY_MUSTBEFREED;
759 		}
760 		if ( dyn->dy_subs ) {
761 			for ( dlm = dyn->dy_dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
762 				if ( dlm->dlm_member_ad ) {
763 					dynlist_member_t dm;
764 					dm.dm_groups = NULL;
765 					dm.dm_mod.sm_op = LDAP_MOD_ADD;
766 					dm.dm_mod.sm_desc = dlm->dlm_member_ad;
767 					dm.dm_mod.sm_type = dlm->dlm_member_ad->ad_cname;
768 					dm.dm_e = e;
769 					dm.dm_ad = dlm->dlm_member_ad;
770 					dynlist_nested_member( op, &dm, dyn->dy_subs );
771 					if ( dm.dm_groups )
772 						ldap_tavl_free( dm.dm_groups, NULL );
773 				}
774 			}
775 		}
776 		if ( dyn->dy_sups ) {
777 			for ( dlm = dyn->dy_dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
778 				if ( dlm->dlm_memberOf_ad ) {
779 					dynlist_nested_memberOf( e, dlm->dlm_memberOf_ad, dyn->dy_sups );
780 				}
781 			}
782 		}
783 	}
784 
785 	if ( e != rs->sr_entry ) {
786 		rs_replace_entry( op, rs, (slap_overinst *)op->o_bd->bd_info, e );
787 		rs->sr_flags |= REP_ENTRY_MODIFIABLE | REP_ENTRY_MUSTBEFREED;
788 	}
789 
790 	return SLAP_CB_CONTINUE;
791 }
792 
793 /* dynlist_sc_compare_entry() callback set by dynlist_compare() */
794 typedef struct dynlist_cc_t {
795 	slap_callback dc_cb;
796 #	define dc_ava	dc_cb.sc_private /* attr:val to compare with */
797 	int *dc_res;
798 } dynlist_cc_t;
799 
800 static int
dynlist_sc_compare_entry(Operation * op,SlapReply * rs)801 dynlist_sc_compare_entry( Operation *op, SlapReply *rs )
802 {
803 	if ( rs->sr_type == REP_SEARCH && rs->sr_entry != NULL ) {
804 		dynlist_cc_t *dc = (dynlist_cc_t *)op->o_callback;
805 		AttributeAssertion *ava = dc->dc_ava;
806 		Attribute *a = attrs_find( rs->sr_entry->e_attrs, ava->aa_desc );
807 
808 		if ( a != NULL ) {
809 			while ( LDAP_SUCCESS != attr_valfind( a,
810 					SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
811 						SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
812 					&ava->aa_value, NULL, op->o_tmpmemctx )
813 				&& (a = attrs_find( a->a_next, ava->aa_desc )) != NULL )
814 				;
815 			*dc->dc_res = a ? LDAP_COMPARE_TRUE : LDAP_COMPARE_FALSE;
816 		}
817 	}
818 
819 	return 0;
820 }
821 
822 static int
dynlist_compare(Operation * op,SlapReply * rs)823 dynlist_compare( Operation *op, SlapReply *rs )
824 {
825 	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
826 	dynlist_gen_t	*dlg = (dynlist_gen_t *)on->on_bi.bi_private;
827 	dynlist_info_t	*dli = dlg->dlg_dli;
828 	Operation o = *op;
829 	Entry *e = NULL;
830 	dynlist_map_t *dlm;
831 	BackendDB *be;
832 	int ret = SLAP_CB_CONTINUE;
833 
834 	if ( get_manageDSAit( op ) )
835 		return SLAP_CB_CONTINUE;
836 
837 	for ( ; dli != NULL; dli = dli->dli_next ) {
838 		for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
839 			AttributeDescription *ad = dlm->dlm_mapped_ad ? dlm->dlm_mapped_ad : dlm->dlm_member_ad;
840 			/* builtin dyngroup evaluator only works for DNs */
841 			if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName )
842 				continue;
843 			if ( op->oq_compare.rs_ava->aa_desc == ad )
844 				break;
845 		}
846 
847 		if ( dlm ) {
848 			/* This compare is for one of the attributes we're
849 			 * interested in. We'll use slapd's existing dyngroup
850 			 * evaluator to get the answer we want.
851 			 */
852 			BerVarray id = NULL, authz = NULL;
853 
854 			o.o_do_not_cache = 1;
855 
856 			if ( ad_dgIdentity && backend_attribute( &o, NULL, &o.o_req_ndn,
857 				ad_dgIdentity, &id, ACL_READ ) == LDAP_SUCCESS )
858 			{
859 				/* if not rootdn and dgAuthz is present,
860 				 * check if user can be authorized as dgIdentity */
861 				if ( ad_dgAuthz && !BER_BVISEMPTY( id ) && !be_isroot( op )
862 					&& backend_attribute( &o, NULL, &o.o_req_ndn,
863 						ad_dgAuthz, &authz, ACL_READ ) == LDAP_SUCCESS )
864 				{
865 
866 					rs->sr_err = slap_sasl_matches( op, authz,
867 						&o.o_ndn, &o.o_ndn );
868 					ber_bvarray_free_x( authz, op->o_tmpmemctx );
869 					if ( rs->sr_err != LDAP_SUCCESS ) {
870 						goto done;
871 					}
872 				}
873 
874 				o.o_dn = *id;
875 				o.o_ndn = *id;
876 				o.o_groups = NULL; /* authz changed, invalidate cached groups */
877 			}
878 
879 			rs->sr_err = backend_group( &o, NULL, &o.o_req_ndn,
880 				&o.oq_compare.rs_ava->aa_value, dli->dli_oc, dli->dli_ad );
881 			switch ( rs->sr_err ) {
882 			case LDAP_SUCCESS:
883 				rs->sr_err = LDAP_COMPARE_TRUE;
884 				break;
885 
886 			case LDAP_NO_SUCH_OBJECT:
887 				/* NOTE: backend_group() returns noSuchObject
888 				 * if op_ndn does not exist; however, since
889 				 * dynamic list expansion means that the
890 				 * member attribute is virtually present, the
891 				 * non-existence of the asserted value implies
892 				 * the assertion is FALSE rather than
893 				 * UNDEFINED */
894 				rs->sr_err = LDAP_COMPARE_FALSE;
895 				break;
896 			}
897 
898 done:;
899 			if ( id ) ber_bvarray_free_x( id, o.o_tmpmemctx );
900 
901 			send_ldap_result( op, rs );
902 			return rs->sr_err;
903 		}
904 	}
905 
906 	be = select_backend( &o.o_req_ndn, 1 );
907 	if ( !be || !be->be_search ) {
908 		return SLAP_CB_CONTINUE;
909 	}
910 
911 	if ( overlay_entry_get_ov( &o, &o.o_req_ndn, NULL, NULL, 0, &e, on ) !=
912 		LDAP_SUCCESS || e == NULL )
913 	{
914 		return SLAP_CB_CONTINUE;
915 	}
916 
917 	/* check for dynlist objectClass; done if not found */
918 	dli = (dynlist_info_t *)dlg->dlg_dli;
919 	while ( dli != NULL && !is_entry_objectclass_or_sub( e, dli->dli_oc ) ) {
920 		dli = dli->dli_next;
921 	}
922 	if ( dli == NULL ) {
923 		goto release;
924 	}
925 
926 	if ( ad_dgIdentity ) {
927 		Attribute *id = attrs_find( e->e_attrs, ad_dgIdentity );
928 		if ( id ) {
929 			Attribute *authz;
930 
931 			/* if not rootdn and dgAuthz is present,
932 			 * check if user can be authorized as dgIdentity */
933 			if ( ad_dgAuthz && !BER_BVISEMPTY( &id->a_nvals[0] ) && !be_isroot( op )
934 				&& ( authz = attrs_find( e->e_attrs, ad_dgAuthz ) ) )
935 			{
936 				if ( slap_sasl_matches( op, authz->a_nvals,
937 					&o.o_ndn, &o.o_ndn ) != LDAP_SUCCESS )
938 				{
939 					goto release;
940 				}
941 			}
942 
943 			o.o_dn = id->a_vals[0];
944 			o.o_ndn = id->a_nvals[0];
945 			o.o_groups = NULL;
946 		}
947 	}
948 
949 	/* generate dynamic list with dynlist_response() and compare */
950 	{
951 		SlapReply	r = { REP_SEARCH };
952 		Attribute *a;
953 		AttributeName an[2];
954 
955 		o.o_tag = LDAP_REQ_SEARCH;
956 		o.ors_limit = NULL;
957 		o.ors_tlimit = SLAP_NO_LIMIT;
958 		o.ors_slimit = SLAP_NO_LIMIT;
959 
960 		o.ors_filterstr = *slap_filterstr_objectClass_pres;
961 		o.ors_filter = (Filter *) slap_filter_objectClass_pres;
962 
963 		o.ors_scope = LDAP_SCOPE_BASE;
964 		o.ors_deref = LDAP_DEREF_NEVER;
965 		an[0].an_name = op->orc_ava->aa_desc->ad_cname;
966 		an[0].an_desc = op->orc_ava->aa_desc;
967 		BER_BVZERO( &an[1].an_name );
968 		o.ors_attrs = an;
969 		o.ors_attrsonly = 0;
970 		r.sr_entry = e;
971 		r.sr_attrs = an;
972 
973 		o.o_acl_priv = ACL_COMPARE;
974 		dynlist_prepare_entry( &o, &r, dli, NULL );
975 		a = attrs_find( r.sr_entry->e_attrs, op->orc_ava->aa_desc );
976 
977 		ret = LDAP_NO_SUCH_ATTRIBUTE;
978 		for ( ; a ; a = attrs_find( a->a_next, op->orc_ava->aa_desc )) {
979 			ret = LDAP_COMPARE_FALSE;
980 			if ( attr_valfind( a,
981 					SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
982 						SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
983 					&op->orc_ava->aa_value, NULL, op->o_tmpmemctx ) == LDAP_SUCCESS ) {
984 				ret = LDAP_COMPARE_TRUE;
985 				break;
986 			}
987 		}
988 		rs->sr_err = ret;
989 
990 		if ( r.sr_entry != e )
991 			entry_free( r.sr_entry );
992 		send_ldap_result( op, rs );
993 	}
994 
995 release:;
996 	if ( e != NULL ) {
997 		overlay_entry_release_ov( &o, e, 0, on );
998 	}
999 
1000 	return ret;
1001 }
1002 
1003 #define	WANT_MEMBEROF	1
1004 #define	WANT_MEMBER	2
1005 
1006 typedef struct dynlist_search_t {
1007 	TAvlnode *ds_names;
1008 	TAvlnode *ds_fnodes;
1009 	dynlist_info_t *ds_dli;
1010 	dynlist_map_t *ds_dlm;
1011 	Filter *ds_origfilter;
1012 	struct berval ds_origfilterbv;
1013 	int ds_want;
1014 	int ds_found;
1015 } dynlist_search_t;
1016 
1017 static int
dynlist_avl_cmp(const void * c1,const void * c2)1018 dynlist_avl_cmp( const void *c1, const void *c2 )
1019 {
1020 	const dynlist_name_t *n1, *n2;
1021 	int rc;
1022 	n1 = c1; n2 = c2;
1023 
1024 	rc = n1->dy_name.bv_len - n2->dy_name.bv_len;
1025 	if ( rc ) return rc;
1026 	return ber_bvcmp( &n1->dy_name, &n2->dy_name );
1027 }
1028 
1029 /* build a list of dynamic entries */
1030 static int
dynlist_search1resp(Operation * op,SlapReply * rs)1031 dynlist_search1resp( Operation *op, SlapReply *rs )
1032 {
1033 	if ( rs->sr_type == REP_SEARCH && rs->sr_entry != NULL ) {
1034 		dynlist_search_t *ds = op->o_callback->sc_private;
1035 		Attribute *a, *b = NULL;
1036 
1037 		if ( ds->ds_dlm && ds->ds_dlm->dlm_static_oc && is_entry_objectclass( rs->sr_entry, ds->ds_dlm->dlm_static_oc, 0 ))
1038 			b = attr_find( rs->sr_entry->e_attrs, ds->ds_dlm->dlm_member_ad );
1039 		a = attr_find( rs->sr_entry->e_attrs, ds->ds_dli->dli_ad );
1040 		if ( a || b ) {
1041 			unsigned len;
1042 			dynlist_name_t *dyn;
1043 			struct berval bv, nbase;
1044 			LDAPURLDesc *ludp;
1045 			int i, j = 0;
1046 
1047 			if ( a )
1048 				len = a->a_numvals * sizeof(LDAPURLDesc *);
1049 			else
1050 				len = 0;
1051 
1052 			dyn = ch_calloc(1, sizeof(dynlist_name_t)+rs->sr_entry->e_nname.bv_len + 1 + len);
1053 			dyn->dy_name.bv_val = ((char *)(dyn+1)) + len;
1054 			dyn->dy_dli = ds->ds_dli;
1055 			dyn->dy_name.bv_len = rs->sr_entry->e_nname.bv_len;
1056 			if ( a ) {
1057 				Filter *f;
1058 				/* parse and validate the URIs */
1059 				for (i=0; i<a->a_numvals; i++) {
1060 					if (ldap_url_parse( a->a_vals[i].bv_val, &ludp ) != LDAP_URL_SUCCESS )
1061 						continue;
1062 					if (( ludp->lud_host && *ludp->lud_host)
1063 						|| ludp->lud_exts ) {
1064 	skipit:
1065 						ldap_free_urldesc( ludp );
1066 						continue;
1067 					}
1068 					ber_str2bv( ludp->lud_dn, 0, 0, &bv );
1069 					if ( dnNormalize( 0, NULL, NULL, &bv, &nbase, op->o_tmpmemctx ) != LDAP_SUCCESS )
1070 						goto skipit;
1071 					ldap_memfree( ludp->lud_dn );
1072 					ludp->lud_dn = ldap_strdup( nbase.bv_val );
1073 					op->o_tmpfree( nbase.bv_val, op->o_tmpmemctx );
1074 					/* cheat here, reuse fields */
1075 					ludp->lud_port = nbase.bv_len;
1076 					if ( ludp->lud_filter && *ludp->lud_filter ) {
1077 						f = str2filter( ludp->lud_filter );
1078 						if ( f == NULL )
1079 							goto skipit;
1080 						ldap_memfree( ludp->lud_filter );
1081 					} else {
1082 						f = ch_malloc( sizeof( Filter ));
1083 						f->f_choice = SLAPD_FILTER_COMPUTED;
1084 						f->f_result = LDAP_COMPARE_TRUE;
1085 						f->f_next = NULL;
1086 					}
1087 					ludp->lud_filter = (char *)f;
1088 					dyn->dy_uris[j] = ludp;
1089 					j++;
1090 				}
1091 			}
1092 			dyn->dy_numuris = j;
1093 			memcpy(dyn->dy_name.bv_val, rs->sr_entry->e_nname.bv_val, rs->sr_entry->e_nname.bv_len );
1094 			if ( b )
1095 				dyn->dy_staticmember = ds->ds_dlm->dlm_member_ad;
1096 
1097 			if ( ldap_tavl_insert( &ds->ds_names, dyn, dynlist_avl_cmp, ldap_avl_dup_error )) {
1098 				for (i=dyn->dy_numuris-1; i>=0; i--) {
1099 					ludp = dyn->dy_uris[i];
1100 					if ( ludp->lud_filter ) {
1101 						filter_free( (Filter *)ludp->lud_filter );
1102 						ludp->lud_filter = NULL;
1103 					}
1104 					ldap_free_urldesc( ludp );
1105 				}
1106 				ch_free( dyn );
1107 			} else {
1108 				ds->ds_found++;
1109 			}
1110 		}
1111 	}
1112 	return 0;
1113 }
1114 
1115 /* replace a filter clause (memberOf=<groupDN>) with an expansion
1116  * of its dynamic members
1117  * using (&(entryDN=<groupURIbase>)<groupURIfilter>)
1118  */
1119 static int
dynlist_filter_dyngroup(Operation * op,Filter * n,Attribute * a)1120 dynlist_filter_dyngroup( Operation *op, Filter *n, Attribute *a )
1121 {
1122 	Filter *andf = NULL, *dnf, *urif, *orf = NULL;
1123 	LDAPURLDesc *ludp;
1124 	struct berval bv, nbase;
1125 	int i;
1126 
1127 	for (i=0; i<a->a_numvals; i++) {
1128 		if ( ldap_url_parse( a->a_vals[i].bv_val, &ludp ) != LDAP_URL_SUCCESS )
1129 			continue;
1130 		if (( ludp->lud_host && *ludp->lud_host )
1131 			|| ludp->lud_attrs
1132 			|| ludp->lud_exts ) {
1133 	skip:
1134 			ldap_free_urldesc( ludp );
1135 			continue;
1136 		}
1137 		ber_str2bv( ludp->lud_dn, 0, 0, &bv );
1138 		if ( dnNormalize( 0, NULL, NULL, &bv, &nbase, op->o_tmpmemctx ) != LDAP_SUCCESS )
1139 			goto skip;
1140 		if ( ludp->lud_filter && *ludp->lud_filter ) {
1141 			urif = str2filter_x( op, ludp->lud_filter );
1142 			if ( urif == NULL ) {
1143 				op->o_tmpfree( nbase.bv_val, op->o_tmpmemctx );
1144 				goto skip;
1145 			}
1146 		} else {
1147 			urif = NULL;
1148 		}
1149 		if ( !andf && n->f_choice == SLAPD_FILTER_COMPUTED ) {
1150 			andf = n;
1151 			andf->f_next = NULL;
1152 		} else {
1153 			orf = n;
1154 			if ( n->f_choice != LDAP_FILTER_OR ) {
1155 				andf = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
1156 				*andf = *n;
1157 				orf->f_choice = LDAP_FILTER_OR;
1158 				orf->f_next = NULL;
1159 				orf->f_list = andf;
1160 			}
1161 			andf = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
1162 			andf->f_next = orf->f_list;
1163 			orf->f_list = andf;
1164 		}
1165 		dnf = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
1166 		andf->f_choice = LDAP_FILTER_AND;
1167 		andf->f_list = dnf;
1168 		dnf->f_next = urif;
1169 		if ( ludp->lud_scope == LDAP_SCOPE_BASE ) {
1170 			dnf->f_choice = LDAP_FILTER_EQUALITY;
1171 			dnf->f_ava = op->o_tmpcalloc( 1, sizeof(AttributeAssertion), op->o_tmpmemctx );
1172 			dnf->f_av_desc = slap_schema.si_ad_entryDN;
1173 			dnf->f_av_value = nbase;
1174 		} else {
1175 			dnf->f_choice = LDAP_FILTER_EXT;
1176 			dnf->f_mra = op->o_tmpcalloc( 1, sizeof(MatchingRuleAssertion), op->o_tmpmemctx );
1177 			dnf->f_mr_desc = slap_schema.si_ad_entryDN;
1178 			dnf->f_mr_value = nbase;
1179 			switch ( ludp->lud_scope ) {
1180 			case LDAP_SCOPE_ONELEVEL:
1181 				dnf->f_mr_rule = slap_schema.si_mr_dnOneLevelMatch;
1182 				break;
1183 			case LDAP_SCOPE_SUBTREE:
1184 				dnf->f_mr_rule = slap_schema.si_mr_dnSubtreeMatch;
1185 				break;
1186 			case LDAP_SCOPE_SUBORDINATE:
1187 				dnf->f_mr_rule = slap_schema.si_mr_dnSubordinateMatch;
1188 				break;
1189 			}
1190 			ber_str2bv( dnf->f_mr_rule->smr_names[0], 0, 0, &dnf->f_mr_rule_text );
1191 		}
1192 		ldap_free_urldesc( ludp );
1193 	}
1194 	if ( !andf )
1195 		return -1;
1196 	return 0;
1197 }
1198 
1199 /* replace a filter clause (memberOf=<groupDN>) with an expansion
1200  * of its static members
1201  * using (|(entryDN=<memberN>)[...])
1202  */
1203 static int
dynlist_filter_stgroup(Operation * op,Filter * n,Attribute * a)1204 dynlist_filter_stgroup( Operation *op, Filter *n, Attribute *a )
1205 {
1206 	Filter *dnf, *orf = NULL;
1207 	int i;
1208 
1209 	if ( a->a_numvals == 1 && n->f_choice == SLAPD_FILTER_COMPUTED ) {
1210 		dnf = n;
1211 		dnf->f_next = NULL;
1212 	} else {
1213 		orf = n;
1214 		if ( n->f_choice != LDAP_FILTER_OR ) {
1215 			dnf = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
1216 			*dnf = *n;
1217 			orf->f_choice = LDAP_FILTER_OR;
1218 			orf->f_next = NULL;
1219 			orf->f_list = dnf;
1220 		}
1221 		dnf = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
1222 		dnf->f_next = orf->f_list;
1223 		orf->f_list = dnf;
1224 	}
1225 
1226 	for (i=0; i<a->a_numvals; i++) {
1227 		if ( i ) {
1228 			dnf = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
1229 			dnf->f_next = orf->f_list;
1230 			orf->f_list = dnf;
1231 		}
1232 		dnf->f_choice = LDAP_FILTER_EQUALITY;
1233 		dnf->f_ava = op->o_tmpcalloc( 1, sizeof(AttributeAssertion), op->o_tmpmemctx );
1234 		dnf->f_av_desc = slap_schema.si_ad_entryDN;
1235 		ber_dupbv_x( &dnf->f_av_value, &a->a_nvals[i], op->o_tmpmemctx );
1236 	}
1237 	return 0;
1238 }
1239 
1240 /* replace a filter clause (memberOf=<groupDN>) with an expansion of
1241  * its members.
1242  */
1243 static int
dynlist_filter_group(Operation * op,dynlist_name_t * dyn,Filter * n,dynlist_search_t * ds)1244 dynlist_filter_group( Operation *op, dynlist_name_t *dyn, Filter *n, dynlist_search_t *ds )
1245 {
1246 	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
1247 	Entry *e;
1248 	Attribute *a;
1249 	int rc = -1;
1250 
1251 	if ( ldap_tavl_insert( &ds->ds_fnodes, dyn, dynlist_ptr_cmp, ldap_avl_dup_error ))
1252 		return 0;
1253 
1254 	if ( overlay_entry_get_ov( op, &dyn->dy_name, NULL, NULL, 0, &e, on ) !=
1255 		LDAP_SUCCESS || e == NULL ) {
1256 		return -1;
1257 	}
1258 	if ( ds->ds_dlm->dlm_static_oc && is_entry_objectclass( e, ds->ds_dlm->dlm_static_oc, 0 )) {
1259 		a = attr_find( e->e_attrs, ds->ds_dlm->dlm_member_ad );
1260 		if ( a ) {
1261 			rc = dynlist_filter_stgroup( op, n, a );
1262 		}
1263 	} else {
1264 		a = attr_find( e->e_attrs, ds->ds_dli->dli_ad );
1265 		if ( a ) {
1266 			rc = dynlist_filter_dyngroup( op, n, a );
1267 		}
1268 	}
1269 	overlay_entry_release_ov( op, e, 0, on );
1270 	if ( dyn->dy_subs && !rc ) {
1271 		TAvlnode *ptr;
1272 		for ( ptr = ldap_tavl_end( dyn->dy_subs, TAVL_DIR_LEFT ); ptr;
1273 			ptr = ldap_tavl_next( ptr, TAVL_DIR_RIGHT )) {
1274 			dyn = ptr->avl_data;
1275 			rc = dynlist_filter_group( op, dyn, n, ds );
1276 			if ( rc )
1277 				break;
1278 		}
1279 	}
1280 	return rc;
1281 }
1282 
1283 /* Dup the filter, replacing any references to given ad with group evaluation */
1284 static Filter *
dynlist_filter_dup(Operation * op,Filter * f,AttributeDescription * ad,dynlist_search_t * ds)1285 dynlist_filter_dup( Operation *op, Filter *f, AttributeDescription *ad, dynlist_search_t *ds )
1286 {
1287 	Filter *n = NULL;
1288 
1289 	if ( !f )
1290 		return NULL;
1291 
1292 	n = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
1293 	n->f_next = NULL;
1294 	switch( f->f_choice & SLAPD_FILTER_MASK ) {
1295 	case SLAPD_FILTER_COMPUTED:
1296 		n->f_choice = f->f_choice;
1297 		n->f_result = f->f_result;
1298 		break;
1299 
1300 	case LDAP_FILTER_PRESENT:
1301 		n->f_choice = f->f_choice;
1302 		n->f_desc = f->f_desc;
1303 		break;
1304 
1305 	case LDAP_FILTER_EQUALITY:
1306 		n->f_choice = SLAPD_FILTER_COMPUTED;
1307 		if ( f->f_av_desc == ad ) {
1308 			dynlist_name_t *dyn = ldap_tavl_find( ds->ds_names, &f->f_av_value, dynlist_avl_cmp );
1309 			if ( dyn && !dynlist_filter_group( op, dyn, n, ds ))
1310 				break;
1311 		}
1312 		/* FALLTHRU */
1313 	case LDAP_FILTER_GE:
1314 	case LDAP_FILTER_LE:
1315 	case LDAP_FILTER_APPROX:
1316 		n->f_choice = f->f_choice;
1317 		n->f_ava = f->f_ava;
1318 		break;
1319 
1320 	case LDAP_FILTER_SUBSTRINGS:
1321 		n->f_choice = f->f_choice;
1322 		n->f_sub = f->f_sub;
1323 		break;
1324 
1325 	case LDAP_FILTER_EXT:
1326 		n->f_choice = f->f_choice;
1327 		n->f_mra = f->f_mra;
1328 		break;
1329 
1330 	case LDAP_FILTER_NOT:
1331 	case LDAP_FILTER_AND:
1332 	case LDAP_FILTER_OR: {
1333 		Filter **p;
1334 
1335 		n->f_choice = f->f_choice;
1336 
1337 		for ( p = &n->f_list, f = f->f_list; f; f = f->f_next ) {
1338 			*p = dynlist_filter_dup( op, f, ad, ds );
1339 			if ( !*p )
1340 				continue;
1341 			p = &(*p)->f_next;
1342 		}
1343 		}
1344 		break;
1345 	}
1346 	return n;
1347 }
1348 
1349 static void
dynlist_filter_free(Operation * op,Filter * f)1350 dynlist_filter_free( Operation *op, Filter *f )
1351 {
1352 	Filter *p, *next;
1353 
1354 	if ( f == NULL )
1355 		return;
1356 
1357 	f->f_choice &= SLAPD_FILTER_MASK;
1358 	switch( f->f_choice ) {
1359 	case LDAP_FILTER_AND:
1360 	case LDAP_FILTER_OR:
1361 	case LDAP_FILTER_NOT:
1362 		for ( p = f->f_list; p; p = next ) {
1363 			next = p->f_next;
1364 			op->o_tmpfree( p, op->o_tmpmemctx );
1365 		}
1366 		break;
1367 	default:
1368 		op->o_tmpfree( f, op->o_tmpmemctx );
1369 	}
1370 }
1371 
1372 static void
dynlist_search_free(void * ptr)1373 dynlist_search_free( void *ptr )
1374 {
1375 	dynlist_name_t *dyn = (dynlist_name_t *)ptr;
1376 	LDAPURLDesc *ludp;
1377 	int i;
1378 
1379 	for (i=dyn->dy_numuris-1; i>=0; i--) {
1380 		ludp = dyn->dy_uris[i];
1381 		if ( ludp->lud_filter ) {
1382 			filter_free( (Filter *)ludp->lud_filter );
1383 			ludp->lud_filter = NULL;
1384 		}
1385 		ldap_free_urldesc( ludp );
1386 	}
1387 	if ( dyn->dy_subs )
1388 		ldap_tavl_free( dyn->dy_subs, NULL );
1389 	if ( dyn->dy_sups )
1390 		ldap_tavl_free( dyn->dy_sups, NULL );
1391 	ch_free( ptr );
1392 }
1393 
1394 static int
dynlist_search_cleanup(Operation * op,SlapReply * rs)1395 dynlist_search_cleanup( Operation *op, SlapReply *rs )
1396 {
1397 	if ( rs->sr_type == REP_RESULT || op->o_abandon ||
1398 		rs->sr_err == SLAPD_ABANDON ) {
1399 		slap_callback *sc = op->o_callback;
1400 		dynlist_search_t *ds = op->o_callback->sc_private;
1401 		ldap_tavl_free( ds->ds_names, dynlist_search_free );
1402 		if ( ds->ds_fnodes )
1403 			ldap_tavl_free( ds->ds_fnodes, NULL );
1404 		if ( ds->ds_origfilter ) {
1405 			op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
1406 			dynlist_filter_free( op, op->ors_filter );
1407 			op->ors_filter = ds->ds_origfilter;
1408 			op->ors_filterstr = ds->ds_origfilterbv;
1409 		}
1410 		op->o_callback = sc->sc_next;
1411 		op->o_tmpfree( sc, op->o_tmpmemctx );
1412 
1413 	}
1414 	return 0;
1415 }
1416 
1417 static int
dynlist_test_membership(Operation * op,dynlist_name_t * dyn,Entry * e)1418 dynlist_test_membership(Operation *op, dynlist_name_t *dyn, Entry *e)
1419 {
1420 	LDAPURLDesc *ludp;
1421 	struct berval nbase, bv;
1422 	int i, rc = LDAP_COMPARE_FALSE;
1423 	if ( dyn->dy_staticmember ) {
1424 		Entry *grp;
1425 		if ( overlay_entry_get_ov( op, &dyn->dy_name, NULL, NULL, 0, &grp, (slap_overinst *)op->o_bd->bd_info ) == LDAP_SUCCESS && grp ) {
1426 			Attribute *a = attr_find( grp->e_attrs, dyn->dy_staticmember );
1427 			if ( a ) {
1428 				i = value_find_ex( dyn->dy_staticmember, SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1429 					SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH, a->a_nvals, &e->e_nname, op->o_tmpmemctx );
1430 			}
1431 			overlay_entry_release_ov( op, grp, 0, (slap_overinst *)op->o_bd->bd_info );
1432 			return i == LDAP_SUCCESS ? LDAP_COMPARE_TRUE : LDAP_COMPARE_FALSE;
1433 		}
1434 	}
1435 	for (i=0; i<dyn->dy_numuris; i++) {
1436 		ludp = dyn->dy_uris[i];
1437 		nbase.bv_val = ludp->lud_dn;
1438 		nbase.bv_len = ludp->lud_port;
1439 		if ( ludp->lud_attrs )
1440 			continue;
1441 		switch( ludp->lud_scope ) {
1442 		case LDAP_SCOPE_BASE:
1443 			if ( !dn_match( &nbase, &e->e_nname ))
1444 				continue;
1445 			break;
1446 		case LDAP_SCOPE_ONELEVEL:
1447 			dnParent( &e->e_nname, &bv );
1448 			if ( !dn_match( &nbase, &bv ))
1449 				continue;
1450 			break;
1451 		case LDAP_SCOPE_SUBTREE:
1452 			if ( !dnIsSuffix( &e->e_nname, &nbase ))
1453 				continue;
1454 			break;
1455 		case LDAP_SCOPE_SUBORDINATE:
1456 			if ( dn_match( &nbase, &e->e_nname ) ||
1457 				!dnIsSuffix( &e->e_nname, &nbase ))
1458 				continue;
1459 			break;
1460 		}
1461 		if ( !ludp->lud_filter )	/* there really should always be a filter */
1462 			rc = LDAP_COMPARE_TRUE;
1463 		else
1464 			rc = test_filter( op, e, (Filter *)ludp->lud_filter );
1465 		if ( rc == LDAP_COMPARE_TRUE )
1466 			break;
1467 	}
1468 	return rc;
1469 }
1470 
1471 static void
dynlist_add_memberOf(Operation * op,SlapReply * rs,dynlist_search_t * ds)1472 dynlist_add_memberOf(Operation *op, SlapReply *rs, dynlist_search_t *ds)
1473 {
1474 	TAvlnode *ptr;
1475 	Entry *e = rs->sr_entry;
1476 	dynlist_name_t *dyn;
1477 	Attribute *a;
1478 
1479 	/* See if there are any memberOf values to attach to this entry */
1480 	for ( ptr = ldap_tavl_end( ds->ds_names, TAVL_DIR_LEFT ); ptr;
1481 		ptr = ldap_tavl_next( ptr, TAVL_DIR_RIGHT )) {
1482 		dynlist_map_t *dlm;
1483 		dyn = ptr->avl_data;
1484 		for ( dlm = dyn->dy_dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
1485 			if ( dlm->dlm_memberOf_ad ) {
1486 				if ( dynlist_test_membership( op, dyn, e ) == LDAP_COMPARE_TRUE ) {
1487 					/* ensure e is modifiable, but do not replace
1488 					 * sr_entry yet since we have pointers into it */
1489 					if ( !( rs->sr_flags & REP_ENTRY_MODIFIABLE ) && e == rs->sr_entry ) {
1490 						e = entry_dup( rs->sr_entry );
1491 					}
1492 					a = attr_find( e->e_attrs, dlm->dlm_memberOf_ad );
1493 					if ( a ) {
1494 						unsigned slot;
1495 						if ( attr_valfind( a, SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX |
1496 							SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH |
1497 							SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH,
1498 							&dyn->dy_name, &slot, NULL ) != LDAP_SUCCESS )
1499 							a = NULL;
1500 					}
1501 					if ( !a )
1502 						attr_merge_one( e, dlm->dlm_memberOf_ad, &dyn->dy_name, &dyn->dy_name );
1503 					if ( dyn->dy_sups ) {
1504 						dynlist_nested_memberOf( e, dlm->dlm_memberOf_ad, dyn->dy_sups );
1505 					}
1506 					break;
1507 				}
1508 			}
1509 		}
1510 	}
1511 	if ( e != rs->sr_entry ) {
1512 		rs_replace_entry( op, rs, (slap_overinst *)op->o_bd->bd_info, e );
1513 		rs->sr_flags |= REP_ENTRY_MODIFIABLE | REP_ENTRY_MUSTBEFREED;
1514 	}
1515 }
1516 
1517 /* process the search responses */
1518 static int
dynlist_search2resp(Operation * op,SlapReply * rs)1519 dynlist_search2resp( Operation *op, SlapReply *rs )
1520 {
1521 	dynlist_search_t *ds = op->o_callback->sc_private;
1522 	dynlist_name_t *dyn;
1523 	int rc;
1524 
1525 	if ( rs->sr_type == REP_SEARCH && rs->sr_entry != NULL ) {
1526 		rc = SLAP_CB_CONTINUE;
1527 		/* See if this is one of our dynamic entries */
1528 		dyn = ldap_tavl_find( ds->ds_names, &rs->sr_entry->e_nname, dynlist_avl_cmp );
1529 		if ( dyn ) {
1530 			dyn->dy_seen = 1;
1531 			rc = dynlist_prepare_entry( op, rs, dyn->dy_dli, dyn );
1532 		} else if ( ds->ds_want )
1533 			dynlist_add_memberOf( op, rs, ds );
1534 		if ( ds->ds_origfilter && test_filter( op, rs->sr_entry, ds->ds_origfilter ) != LDAP_COMPARE_TRUE ) {
1535 			rs_flush_entry( op, rs, NULL );
1536 			return LDAP_SUCCESS;
1537 		}
1538 		return rc;
1539 	} else if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS ) {
1540 		TAvlnode *ptr;
1541 		SlapReply r = *rs;
1542 		Filter *f = ds->ds_origfilter ? ds->ds_origfilter : op->ors_filter;
1543 
1544 		if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED )
1545 			return SLAP_CB_CONTINUE;
1546 
1547 		/* Check for any unexpanded dynamic group entries that weren't picked up
1548 		 * by the original search filter.
1549 		 */
1550 		for ( ptr = ldap_tavl_end( ds->ds_names, TAVL_DIR_LEFT ); ptr;
1551 			ptr = ldap_tavl_next( ptr, TAVL_DIR_RIGHT )) {
1552 			dyn = ptr->avl_data;
1553 			if ( dyn->dy_seen )
1554 				continue;
1555 			if ( !dnIsSuffixScope( &dyn->dy_name, &op->o_req_ndn, op->ors_scope ))
1556 				continue;
1557 			if ( overlay_entry_get_ov( op, &dyn->dy_name, NULL, NULL, 0, &r.sr_entry, (slap_overinst *)op->o_bd->bd_info ) != LDAP_SUCCESS ||
1558 				r.sr_entry == NULL )
1559 				continue;
1560 			r.sr_flags = REP_ENTRY_MUSTRELEASE;
1561 			dynlist_prepare_entry( op, &r, dyn->dy_dli, dyn );
1562 			if ( test_filter( op, r.sr_entry, f ) == LDAP_COMPARE_TRUE ) {
1563 				r.sr_attrs = op->ors_attrs;
1564 				rs->sr_err = send_search_entry( op, &r );
1565 				if ( rs->sr_err != LDAP_SUCCESS )
1566 					break;
1567 			} else {
1568 				rs_flush_entry( op, &r, NULL );
1569 			}
1570 		}
1571 		rs->sr_nentries = r.sr_nentries;
1572 	}
1573 	return SLAP_CB_CONTINUE;
1574 }
1575 
1576 static void
dynlist_fix_filter(Operation * op,AttributeDescription * ad,dynlist_search_t * ds)1577 dynlist_fix_filter( Operation *op, AttributeDescription *ad, dynlist_search_t *ds )
1578 {
1579 	Filter *f;
1580 	f = dynlist_filter_dup( op, op->ors_filter, ad, ds );
1581 	if ( ds->ds_origfilter ) {
1582 		dynlist_filter_free( op, op->ors_filter );
1583 		op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
1584 	} else {
1585 		ds->ds_origfilter = op->ors_filter;
1586 		ds->ds_origfilterbv = op->ors_filterstr;
1587 	}
1588 	op->ors_filter = f;
1589 	filter2bv_x( op, f, &op->ors_filterstr );
1590 }
1591 
1592 typedef struct dynlist_link_t {
1593 	dynlist_search_t *dl_ds;
1594 	dynlist_name_t *dl_sup;
1595 } dynlist_link_t;
1596 
1597 static int
dynlist_nestlink_dg(Operation * op,SlapReply * rs)1598 dynlist_nestlink_dg( Operation *op, SlapReply *rs )
1599 {
1600 	dynlist_link_t *dll = op->o_callback->sc_private;
1601 	dynlist_search_t *ds = dll->dl_ds;
1602 	dynlist_name_t *di = dll->dl_sup, *dj;
1603 
1604 	if ( rs->sr_type != REP_SEARCH )
1605 		return LDAP_SUCCESS;
1606 
1607 	dj = ldap_tavl_find( dll->dl_ds->ds_names, &rs->sr_entry->e_nname, dynlist_avl_cmp );
1608 	if ( dj ) {
1609 		if ( ds->ds_want & WANT_MEMBEROF ) {
1610 			ldap_tavl_insert( &dj->dy_sups, di, dynlist_ptr_cmp, ldap_avl_dup_error );
1611 		}
1612 		if ( ds->ds_want & WANT_MEMBER ) {
1613 			ldap_tavl_insert( &di->dy_subs, dj, dynlist_ptr_cmp, ldap_avl_dup_error );
1614 		}
1615 	}
1616 	return LDAP_SUCCESS;
1617 }
1618 
1619 /* Connect all nested groups to their parents/children */
1620 static void
dynlist_nestlink(Operation * op,dynlist_search_t * ds)1621 dynlist_nestlink( Operation *op, dynlist_search_t *ds )
1622 {
1623 	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
1624 	dynlist_name_t *di, *dj;
1625 	TAvlnode *ptr;
1626 	Entry *e;
1627 	Attribute *a;
1628 	int i;
1629 
1630 	for ( ptr = ldap_tavl_end( ds->ds_names, TAVL_DIR_LEFT ); ptr;
1631 		ptr = ldap_tavl_next( ptr, TAVL_DIR_RIGHT )) {
1632 		di = ptr->avl_data;
1633 		if ( ds->ds_dlm ) {
1634 			if ( overlay_entry_get_ov( op, &di->dy_name, NULL, NULL, 0, &e, on ) != LDAP_SUCCESS || e == NULL )
1635 				continue;
1636 			a = attr_find( e->e_attrs, ds->ds_dlm->dlm_member_ad );
1637 			if ( a ) {
1638 				for ( i=0; i < a->a_numvals; i++ ) {
1639 					dj = ldap_tavl_find( ds->ds_names, &a->a_nvals[i], dynlist_avl_cmp );
1640 					if ( dj ) {
1641 						if ( ds->ds_want & WANT_MEMBEROF ) {
1642 							ldap_tavl_insert( &dj->dy_sups, di, dynlist_ptr_cmp, ldap_avl_dup_error );
1643 						}
1644 						if ( ds->ds_want & WANT_MEMBER ) {
1645 							ldap_tavl_insert( &di->dy_subs, dj, dynlist_ptr_cmp, ldap_avl_dup_error );
1646 						}
1647 					}
1648 				}
1649 			}
1650 			overlay_entry_release_ov( op, e, 0, on );
1651 		}
1652 
1653 		if ( di->dy_numuris ) {
1654 			slap_callback cb = { 0 };
1655 			dynlist_link_t dll;
1656 			dll.dl_ds = ds;
1657 			dll.dl_sup = di;
1658 			cb.sc_private = &dll;
1659 			cb.sc_response = dynlist_nestlink_dg;
1660 			dynlist_urlmembers( op, di, &cb );
1661 		}
1662 	}
1663 }
1664 
1665 static int
dynlist_search(Operation * op,SlapReply * rs)1666 dynlist_search( Operation *op, SlapReply *rs )
1667 {
1668 	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
1669 	dynlist_gen_t	*dlg = (dynlist_gen_t *)on->on_bi.bi_private;
1670 	dynlist_info_t	*dli;
1671 	Operation o = *op;
1672 	dynlist_map_t *dlm;
1673 	Filter f[3];
1674 	AttributeAssertion ava[2];
1675 	AttributeName an[2] = {0};
1676 
1677 	slap_callback *sc;
1678 	dynlist_search_t *ds;
1679 	ObjectClass *static_oc;
1680 	int nested, found, tmpwant;
1681 	int opattrs, userattrs;
1682 
1683 	if ( get_manageDSAit( op ) )
1684 		return SLAP_CB_CONTINUE;
1685 
1686 	sc = op->o_tmpcalloc( 1, sizeof(slap_callback)+sizeof(dynlist_search_t), op->o_tmpmemctx );
1687 	sc->sc_private = (void *)(sc+1);
1688 	ds = sc->sc_private;
1689 
1690 	memset( o.o_ctrlflag, 0, sizeof( o.o_ctrlflag ));
1691 	o.o_managedsait = SLAP_CONTROL_CRITICAL;
1692 
1693 	/* Are we using memberOf, and does it affect this request? */
1694 	if ( dlg->dlg_memberOf ) {
1695 		int attrflags = slap_attr_flags( op->ors_attrs );
1696 		opattrs = SLAP_OPATTRS( attrflags );
1697 		userattrs = SLAP_USERATTRS( attrflags );
1698 	}
1699 
1700 	/* Find all groups in scope. For group expansion
1701 	 * we only need the groups within the search scope, but
1702 	 * for memberOf populating, we need all dyngroups.
1703 	 */
1704 	for ( dli = dlg->dlg_dli; dli; dli = dli->dli_next ) {
1705 		static_oc = NULL;
1706 		nested = 0;
1707 		tmpwant = 0;
1708 		if ( dlg->dlg_memberOf ) {
1709 			for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
1710 				if ( dlm->dlm_memberOf_ad ) {
1711 					int want = 0;
1712 
1713 					/* is attribute in filter? */
1714 					if ( ad_infilter( dlm->dlm_memberOf_ad, op->ors_filter )) {
1715 						want |= WANT_MEMBEROF;
1716 						/* with nesting, filter attributes also require nestlink */
1717 						if ( dlm->dlm_memberOf_nested ) {
1718 						/* WANT_ flags have inverted meaning here:
1719 						 * to satisfy (memberOf=) filter, we need to also
1720 						 * find all subordinate groups. No special
1721 						 * treatment is needed for (member=) since we
1722 						 * already search all group entries.
1723 						 */
1724 							want |= WANT_MEMBER;
1725 						}
1726 					}
1727 
1728 					/* if attribute is not requested, skip it */
1729 					if ( op->ors_attrs == NULL ) {
1730 						if ( !dlm->dlm_memberOf_oper ) {
1731 							want |= WANT_MEMBEROF;
1732 							if ( dlm->dlm_memberOf_nested && !dlm->dlm_member_oper )
1733 								want |= WANT_MEMBER;
1734 						}
1735 					} else {
1736 						if ( ad_inlist( dlm->dlm_memberOf_ad, op->ors_attrs )) {
1737 							want |= WANT_MEMBEROF;
1738 							if ( dlm->dlm_memberOf_nested && ad_inlist( dlm->dlm_member_ad, op->ors_attrs ))
1739 								want |= WANT_MEMBER;
1740 						} else {
1741 							if ( opattrs ) {
1742 								if ( dlm->dlm_memberOf_oper ) {
1743 									want |= WANT_MEMBEROF;
1744 									if ( dlm->dlm_memberOf_nested && dlm->dlm_member_oper )
1745 										want |= WANT_MEMBER;
1746 								}
1747 							}
1748 							if ( userattrs ) {
1749 								if ( !dlm->dlm_memberOf_oper ) {
1750 									want |= WANT_MEMBEROF;
1751 									if ( dlm->dlm_memberOf_nested && !dlm->dlm_member_oper )
1752 										want |= WANT_MEMBER;
1753 								}
1754 							}
1755 						}
1756 					}
1757 					if ( want ) {
1758 						nested = dlm->dlm_memberOf_nested;
1759 						ds->ds_want = tmpwant = want;
1760 						if ( dlm->dlm_static_oc ) {
1761 							static_oc = dlm->dlm_static_oc;
1762 							ds->ds_dlm = dlm;
1763 						}
1764 					}
1765 				}
1766 			}
1767 		}
1768 
1769 		if ( static_oc ) {
1770 			f[0].f_choice = LDAP_FILTER_OR;
1771 			f[0].f_list = &f[1];
1772 			f[0].f_next = NULL;
1773 			f[1].f_choice = LDAP_FILTER_EQUALITY;
1774 			f[1].f_next = &f[2];
1775 			f[1].f_ava = &ava[0];
1776 			f[1].f_av_desc = slap_schema.si_ad_objectClass;
1777 			f[1].f_av_value = dli->dli_oc->soc_cname;
1778 			f[2].f_choice = LDAP_FILTER_EQUALITY;
1779 			f[2].f_ava = &ava[1];
1780 			f[2].f_av_desc = slap_schema.si_ad_objectClass;
1781 			f[2].f_av_value = static_oc->soc_cname;
1782 			f[2].f_next = NULL;
1783 		} else {
1784 			f[0].f_choice = LDAP_FILTER_EQUALITY;
1785 			f[0].f_ava = ava;
1786 			f[0].f_av_desc = slap_schema.si_ad_objectClass;
1787 			f[0].f_av_value = dli->dli_oc->soc_cname;
1788 			f[0].f_next = NULL;
1789 		}
1790 
1791 		if ( o.o_callback != sc ) {
1792 			o.o_callback = sc;
1793 			o.ors_filter = f;
1794 			if ( tmpwant ) {
1795 				o.o_req_dn = op->o_bd->be_suffix[0];
1796 				o.o_req_ndn = op->o_bd->be_nsuffix[0];
1797 				o.ors_scope = LDAP_SCOPE_SUBTREE;
1798 			} else {
1799 				o.o_req_dn = op->o_req_dn;
1800 				o.o_req_ndn = op->o_req_ndn;
1801 				o.ors_scope = op->ors_scope;
1802 			}
1803 			o.ors_attrsonly = 0;
1804 			o.ors_attrs = an;
1805 			o.o_bd = select_backend( op->o_bd->be_nsuffix, 1 );
1806 			BER_BVZERO( &o.ors_filterstr );
1807 			sc->sc_response = dynlist_search1resp;
1808 		}
1809 
1810 		ds->ds_dli = dli;
1811 		if ( o.ors_filterstr.bv_val )
1812 			o.o_tmpfree( o.ors_filterstr.bv_val, o.o_tmpmemctx );
1813 		filter2bv_x( &o, f, &o.ors_filterstr );
1814 		an[0].an_desc = dli->dli_ad;
1815 		an[0].an_name = dli->dli_ad->ad_cname;
1816 		found = ds->ds_found;
1817 		{
1818 			SlapReply	r = { REP_SEARCH };
1819 			(void)o.o_bd->be_search( &o, &r );
1820 		}
1821 		if ( found != ds->ds_found && nested )
1822 			dynlist_nestlink( op, ds );
1823 	}
1824 
1825 	if ( ds->ds_names != NULL ) {
1826 		sc->sc_response = dynlist_search2resp;
1827 		sc->sc_cleanup = dynlist_search_cleanup;
1828 		sc->sc_next = op->o_callback;
1829 		op->o_callback = sc;
1830 
1831 		/* see if filter needs fixing */
1832 		if ( dlg->dlg_memberOf ) {
1833 			for ( dli = dlg->dlg_dli; dli; dli = dli->dli_next ) {
1834 				for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
1835 					if ( dlm->dlm_memberOf_ad ) {
1836 
1837 						/* if attribute is in filter, fix it */
1838 						if ( ad_infilter( dlm->dlm_memberOf_ad, op->ors_filter )) {
1839 							ds->ds_dli = dli;
1840 							ds->ds_dlm = dlm;
1841 							dynlist_fix_filter( op, dlm->dlm_memberOf_ad, ds );
1842 						}
1843 					}
1844 				}
1845 			}
1846 		}
1847 
1848 	} else {
1849 		op->o_tmpfree( sc, op->o_tmpmemctx );
1850 	}
1851 	return SLAP_CB_CONTINUE;
1852 }
1853 
1854 static int
dynlist_build_def_filter(dynlist_info_t * dli)1855 dynlist_build_def_filter( dynlist_info_t *dli )
1856 {
1857 	char	*ptr;
1858 
1859 	dli->dli_default_filter.bv_len = STRLENOF( "(!(objectClass=" "))" )
1860 		+ dli->dli_oc->soc_cname.bv_len;
1861 	dli->dli_default_filter.bv_val = ch_malloc( dli->dli_default_filter.bv_len + 1 );
1862 	if ( dli->dli_default_filter.bv_val == NULL ) {
1863 		Debug( LDAP_DEBUG_ANY, "dynlist_db_open: malloc failed.\n" );
1864 		return -1;
1865 	}
1866 
1867 	ptr = lutil_strcopy( dli->dli_default_filter.bv_val, "(!(objectClass=" );
1868 	ptr = lutil_strcopy( ptr, dli->dli_oc->soc_cname.bv_val );
1869 	ptr = lutil_strcopy( ptr, "))" );
1870 
1871 	assert( ptr == &dli->dli_default_filter.bv_val[dli->dli_default_filter.bv_len] );
1872 
1873 	return 0;
1874 }
1875 
1876 enum {
1877 	DL_ATTRSET = 1,
1878 	DL_ATTRPAIR,
1879 	DL_ATTRPAIR_COMPAT,
1880 	DL_LAST
1881 };
1882 
1883 static ConfigDriver	dl_cfgen;
1884 
1885 /* XXXmanu 255 is the maximum arguments we allow. Can we go beyond? */
1886 static ConfigTable dlcfg[] = {
1887 	{ "dynlist-attrset", "group-oc> [uri] <URL-ad> <[mapped:]member-ad> [...]",
1888 		3, 0, 0, ARG_MAGIC|DL_ATTRSET, dl_cfgen,
1889 		"( OLcfgOvAt:8.1 NAME ( 'olcDynListAttrSet' 'olcDlAttrSet' ) "
1890 			"DESC 'Dynamic list: <group objectClass>, <URL attributeDescription>, <member attributeDescription>' "
1891 			"EQUALITY caseIgnoreMatch "
1892 			"SYNTAX OMsDirectoryString "
1893 			"X-ORDERED 'VALUES' )",
1894 			NULL, NULL },
1895 	{ "dynlist-attrpair", "member-ad> <URL-ad",
1896 		3, 3, 0, ARG_MAGIC|DL_ATTRPAIR, dl_cfgen,
1897 			NULL, NULL, NULL },
1898 #ifdef TAKEOVER_DYNGROUP
1899 	{ "attrpair", "member-ad> <URL-ad",
1900 		3, 3, 0, ARG_MAGIC|DL_ATTRPAIR_COMPAT, dl_cfgen,
1901 			NULL, NULL, NULL },
1902 #endif
1903 	{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
1904 };
1905 
1906 static ConfigOCs dlocs[] = {
1907 	{ "( OLcfgOvOc:8.1 "
1908 		"NAME ( 'olcDynListConfig' 'olcDynamicList' ) "
1909 		"DESC 'Dynamic list configuration' "
1910 		"SUP olcOverlayConfig "
1911 		"MAY olcDynListAttrSet )",
1912 		Cft_Overlay, dlcfg, NULL, NULL },
1913 	{ NULL, 0, NULL }
1914 };
1915 
1916 static int
dl_cfgen(ConfigArgs * c)1917 dl_cfgen( ConfigArgs *c )
1918 {
1919 	slap_overinst	*on = (slap_overinst *)c->bi;
1920 	dynlist_gen_t	*dlg = (dynlist_gen_t *)on->on_bi.bi_private;
1921 	dynlist_info_t	*dli = dlg->dlg_dli;
1922 
1923 	int		rc = 0, i;
1924 
1925 	if ( c->op == SLAP_CONFIG_EMIT ) {
1926 		switch( c->type ) {
1927 		case DL_ATTRSET:
1928 			for ( i = 0; dli; i++, dli = dli->dli_next ) {
1929 				struct berval	bv;
1930 				char		*ptr = c->cr_msg;
1931 				dynlist_map_t	*dlm;
1932 
1933 				assert( dli->dli_oc != NULL );
1934 				assert( dli->dli_ad != NULL );
1935 
1936 				/* FIXME: check buffer overflow! */
1937 				ptr += snprintf( c->cr_msg, sizeof( c->cr_msg ),
1938 					SLAP_X_ORDERED_FMT "%s", i,
1939 					dli->dli_oc->soc_cname.bv_val );
1940 
1941 				if ( !BER_BVISNULL( &dli->dli_uri ) ) {
1942 					*ptr++ = ' ';
1943 					*ptr++ = '"';
1944 					ptr = lutil_strncopy( ptr, dli->dli_uri.bv_val,
1945 						dli->dli_uri.bv_len );
1946 					*ptr++ = '"';
1947 				}
1948 
1949 				*ptr++ = ' ';
1950 				ptr = lutil_strncopy( ptr, dli->dli_ad->ad_cname.bv_val,
1951 					dli->dli_ad->ad_cname.bv_len );
1952 
1953 				for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
1954 					ptr[ 0 ] = ' ';
1955 					ptr++;
1956 					if ( dlm->dlm_mapped_ad ) {
1957 						ptr = lutil_strcopy( ptr, dlm->dlm_mapped_ad->ad_cname.bv_val );
1958 						ptr[ 0 ] = ':';
1959 						ptr++;
1960 					}
1961 
1962 					ptr = lutil_strcopy( ptr, dlm->dlm_member_ad->ad_cname.bv_val );
1963 
1964 					if ( dlm->dlm_memberOf_ad ) {
1965 						*ptr++ = '+';
1966 						ptr = lutil_strcopy( ptr, dlm->dlm_memberOf_ad->ad_cname.bv_val );
1967 						if ( dlm->dlm_static_oc ) {
1968 							*ptr++ = '@';
1969 							ptr = lutil_strcopy( ptr, dlm->dlm_static_oc->soc_cname.bv_val );
1970 						}
1971 						if ( dlm->dlm_memberOf_nested ) {
1972 							*ptr++ = '*';
1973 						}
1974 					}
1975 				}
1976 
1977 				bv.bv_val = c->cr_msg;
1978 				bv.bv_len = ptr - bv.bv_val;
1979 				value_add_one( &c->rvalue_vals, &bv );
1980 			}
1981 			break;
1982 
1983 		case DL_ATTRPAIR_COMPAT:
1984 		case DL_ATTRPAIR:
1985 			rc = 1;
1986 			break;
1987 
1988 		default:
1989 			rc = 1;
1990 			break;
1991 		}
1992 
1993 		return rc;
1994 
1995 	} else if ( c->op == LDAP_MOD_DELETE ) {
1996 		switch( c->type ) {
1997 		case DL_ATTRSET:
1998 			if ( c->valx < 0 ) {
1999 				dynlist_info_t	*dli_next;
2000 
2001 				for ( dli_next = dli; dli_next; dli = dli_next ) {
2002 					dynlist_map_t *dlm = dli->dli_dlm;
2003 					dynlist_map_t *dlm_next;
2004 
2005 					dli_next = dli->dli_next;
2006 
2007 					if ( !BER_BVISNULL( &dli->dli_uri ) ) {
2008 						ch_free( dli->dli_uri.bv_val );
2009 					}
2010 
2011 					if ( dli->dli_lud != NULL ) {
2012 						ldap_free_urldesc( dli->dli_lud );
2013 					}
2014 
2015 					if ( !BER_BVISNULL( &dli->dli_uri_nbase ) ) {
2016 						ber_memfree( dli->dli_uri_nbase.bv_val );
2017 					}
2018 
2019 					if ( dli->dli_uri_filter != NULL ) {
2020 						filter_free( dli->dli_uri_filter );
2021 					}
2022 
2023 					ch_free( dli->dli_default_filter.bv_val );
2024 
2025 					while ( dlm != NULL ) {
2026 						dlm_next = dlm->dlm_next;
2027 						ch_free( dlm );
2028 						dlm = dlm_next;
2029 					}
2030 					ch_free( dli );
2031 				}
2032 
2033 				dlg->dlg_dli = NULL;
2034 				dlg->dlg_memberOf = 0;
2035 
2036 			} else {
2037 				dynlist_info_t	**dlip;
2038 				dynlist_map_t *dlm;
2039 				dynlist_map_t *dlm_next;
2040 
2041 				for ( i = 0, dlip = (dynlist_info_t **)&dlg->dlg_dli;
2042 					i < c->valx; i++ )
2043 				{
2044 					if ( *dlip == NULL ) {
2045 						return 1;
2046 					}
2047 					dlip = &(*dlip)->dli_next;
2048 				}
2049 
2050 				dli = *dlip;
2051 				*dlip = dli->dli_next;
2052 
2053 				if ( !BER_BVISNULL( &dli->dli_uri ) ) {
2054 					ch_free( dli->dli_uri.bv_val );
2055 				}
2056 
2057 				if ( dli->dli_lud != NULL ) {
2058 					ldap_free_urldesc( dli->dli_lud );
2059 				}
2060 
2061 				if ( !BER_BVISNULL( &dli->dli_uri_nbase ) ) {
2062 					ber_memfree( dli->dli_uri_nbase.bv_val );
2063 				}
2064 
2065 				if ( dli->dli_uri_filter != NULL ) {
2066 					filter_free( dli->dli_uri_filter );
2067 				}
2068 
2069 				ch_free( dli->dli_default_filter.bv_val );
2070 
2071 				dlm = dli->dli_dlm;
2072 				while ( dlm != NULL ) {
2073 					dlm_next = dlm->dlm_next;
2074 					if ( dlm->dlm_memberOf_ad )
2075 						dlg->dlg_memberOf--;
2076 					ch_free( dlm );
2077 					dlm = dlm_next;
2078 				}
2079 				ch_free( dli );
2080 
2081 				dli = (dynlist_info_t *)dlg->dlg_dli;
2082 			}
2083 			break;
2084 
2085 		case DL_ATTRPAIR_COMPAT:
2086 		case DL_ATTRPAIR:
2087 			rc = 1;
2088 			break;
2089 
2090 		default:
2091 			rc = 1;
2092 			break;
2093 		}
2094 
2095 		return rc;
2096 	}
2097 
2098 	switch( c->type ) {
2099 	case DL_ATTRSET: {
2100 		dynlist_info_t		**dlip,
2101 					*dli_next = NULL;
2102 		ObjectClass		*oc = NULL;
2103 		AttributeDescription	*ad = NULL;
2104 		int			attridx = 2;
2105 		LDAPURLDesc		*lud = NULL;
2106 		struct berval		nbase = BER_BVNULL;
2107 		Filter			*filter = NULL;
2108 		struct berval		uri = BER_BVNULL;
2109 		dynlist_map_t           *dlm = NULL, *dlml = NULL;
2110 		const char		*text;
2111 
2112 		oc = oc_find( c->argv[ 1 ] );
2113 		if ( oc == NULL ) {
2114 			snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
2115 				"unable to find ObjectClass \"%s\"",
2116 				c->argv[ 1 ] );
2117 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
2118 				c->log, c->cr_msg );
2119 			return 1;
2120 		}
2121 
2122 		if ( strncasecmp( c->argv[ attridx ], "ldap://", STRLENOF("ldap://") ) == 0 ) {
2123 			if ( ldap_url_parse( c->argv[ attridx ], &lud ) != LDAP_URL_SUCCESS ) {
2124 				snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
2125 					"unable to parse URI \"%s\"",
2126 					c->argv[ attridx ] );
2127 				rc = 1;
2128 				goto done_uri;
2129 			}
2130 
2131 			if ( lud->lud_host != NULL ) {
2132 				if ( lud->lud_host[0] == '\0' ) {
2133 					ch_free( lud->lud_host );
2134 					lud->lud_host = NULL;
2135 
2136 				} else {
2137 					snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
2138 						"host not allowed in URI \"%s\"",
2139 						c->argv[ attridx ] );
2140 					rc = 1;
2141 					goto done_uri;
2142 				}
2143 			}
2144 
2145 			if ( lud->lud_attrs != NULL ) {
2146 				snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
2147 					"attrs not allowed in URI \"%s\"",
2148 					c->argv[ attridx ] );
2149 				rc = 1;
2150 				goto done_uri;
2151 			}
2152 
2153 			if ( lud->lud_exts != NULL ) {
2154 				snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
2155 					"extensions not allowed in URI \"%s\"",
2156 					c->argv[ attridx ] );
2157 				rc = 1;
2158 				goto done_uri;
2159 			}
2160 
2161 			if ( lud->lud_dn != NULL && lud->lud_dn[ 0 ] != '\0' ) {
2162 				struct berval dn;
2163 				ber_str2bv( lud->lud_dn, 0, 0, &dn );
2164 				rc = dnNormalize( 0, NULL, NULL, &dn, &nbase, NULL );
2165 				if ( rc != LDAP_SUCCESS ) {
2166 					snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
2167 						"DN normalization failed in URI \"%s\"",
2168 						c->argv[ attridx ] );
2169 					goto done_uri;
2170 				}
2171 			}
2172 
2173 			if ( lud->lud_filter != NULL && lud->lud_filter[ 0 ] != '\0' ) {
2174 				filter = str2filter( lud->lud_filter );
2175 				if ( filter == NULL ) {
2176 					snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
2177 						"filter parsing failed in URI \"%s\"",
2178 						c->argv[ attridx ] );
2179 					rc = 1;
2180 					goto done_uri;
2181 				}
2182 			}
2183 
2184 			ber_str2bv( c->argv[ attridx ], 0, 1, &uri );
2185 
2186 done_uri:;
2187 			if ( rc ) {
2188 				if ( lud ) {
2189 					ldap_free_urldesc( lud );
2190 				}
2191 
2192 				if ( !BER_BVISNULL( &nbase ) ) {
2193 					ber_memfree( nbase.bv_val );
2194 				}
2195 
2196 				if ( filter != NULL ) {
2197 					filter_free( filter );
2198 				}
2199 
2200 				while ( dlm != NULL ) {
2201 					dlml = dlm;
2202 					dlm = dlm->dlm_next;
2203 					ch_free( dlml );
2204 				}
2205 
2206 				Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
2207 					c->log, c->cr_msg );
2208 
2209 				return rc;
2210 			}
2211 
2212 			attridx++;
2213 		}
2214 
2215 		rc = slap_str2ad( c->argv[ attridx ], &ad, &text );
2216 		if ( rc != LDAP_SUCCESS ) {
2217 			snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
2218 				"unable to find AttributeDescription \"%s\"",
2219 				c->argv[ attridx ] );
2220 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
2221 				c->log, c->cr_msg );
2222 			rc = 1;
2223 			goto done_uri;
2224 		}
2225 
2226 		if ( !is_at_subtype( ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
2227 			snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
2228 				"AttributeDescription \"%s\" "
2229 				"must be a subtype of \"labeledURI\"",
2230 				c->argv[ attridx ] );
2231 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
2232 				c->log, c->cr_msg );
2233 			rc = 1;
2234 			goto done_uri;
2235 		}
2236 
2237 		attridx++;
2238 
2239 		for ( i = attridx; i < c->argc; i++ ) {
2240 			char *arg;
2241 			char *cp;
2242 			AttributeDescription *member_ad = NULL;
2243 			AttributeDescription *mapped_ad = NULL;
2244 			AttributeDescription *memberOf_ad = NULL;
2245 			ObjectClass *static_oc = NULL;
2246 			int nested = 0;
2247 			dynlist_map_t *dlmp;
2248 
2249 
2250 			/*
2251 			 * If no mapped attribute is given, dn is used
2252 			 * for backward compatibility.
2253 			 */
2254 			arg = c->argv[i];
2255 			if ( ( cp = strchr( arg, ':' ) ) != NULL ) {
2256 				struct berval bv;
2257 				ber_str2bv( arg, cp - arg, 0, &bv );
2258 				rc = slap_bv2ad( &bv, &mapped_ad, &text );
2259 				if ( rc != LDAP_SUCCESS ) {
2260 					snprintf( c->cr_msg, sizeof( c->cr_msg ),
2261 						DYNLIST_USAGE
2262 						"unable to find mapped AttributeDescription #%d \"%s\"\n",
2263 						i - 3, c->argv[ i ] );
2264 					Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
2265 						c->log, c->cr_msg );
2266 					rc = 1;
2267 					goto done_uri;
2268 				}
2269 				arg = cp + 1;
2270 			}
2271 			if ( ( cp = strchr( arg, '+' ) ) != NULL ) {
2272 				struct berval bv;
2273 				char *ocp, *np;
2274 				np = strrchr( cp+1, '*' );
2275 				if ( np ) {
2276 					nested = 1;
2277 					*np = '\0';
2278 				}
2279 				ocp = strchr( cp+1, '@' );
2280 				if ( ocp ) {
2281 					static_oc = oc_find( ocp+1 );
2282 					if ( !static_oc ) {
2283 						snprintf( c->cr_msg, sizeof( c->cr_msg ),
2284 							DYNLIST_USAGE
2285 							"unable to find static-oc ObjectClass #%d \"%s\"\n",
2286 							i - 3, c->argv[ i ] );
2287 						Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
2288 							c->log, c->cr_msg );
2289 						rc = 1;
2290 						goto done_uri;
2291 					}
2292 					*ocp = '\0';
2293 				}
2294 				ber_str2bv( cp+1, 0, 0, &bv );
2295 				rc = slap_bv2ad( &bv, &memberOf_ad, &text );
2296 				if ( rc != LDAP_SUCCESS ) {
2297 					snprintf( c->cr_msg, sizeof( c->cr_msg ),
2298 						DYNLIST_USAGE
2299 						"unable to find memberOf AttributeDescription #%d \"%s\"\n",
2300 						i - 3, c->argv[ i ] );
2301 					Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
2302 						c->log, c->cr_msg );
2303 					rc = 1;
2304 					goto done_uri;
2305 				}
2306 				dlg->dlg_memberOf++;
2307 				*cp = '\0';
2308 			}
2309 
2310 			rc = slap_str2ad( arg, &member_ad, &text );
2311 			if ( rc != LDAP_SUCCESS ) {
2312 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
2313 					DYNLIST_USAGE
2314 					"unable to find AttributeDescription #%d \"%s\"\n",
2315 					i - 3, c->argv[ i ] );
2316 				Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
2317 					c->log, c->cr_msg );
2318 				rc = 1;
2319 				goto done_uri;
2320 			}
2321 
2322 			dlmp = (dynlist_map_t *)ch_calloc( 1, sizeof( dynlist_map_t ) );
2323 			if ( dlm == NULL ) {
2324 				dlm = dlmp;
2325 			}
2326 			dlmp->dlm_member_ad = member_ad;
2327 			dlmp->dlm_mapped_ad = mapped_ad;
2328 			dlmp->dlm_memberOf_ad = memberOf_ad;
2329 			dlmp->dlm_static_oc = static_oc;
2330 			dlmp->dlm_memberOf_nested = nested;
2331 			dlmp->dlm_member_oper = is_at_operational( member_ad->ad_type );
2332 			if ( memberOf_ad ) {
2333 				dlmp->dlm_memberOf_oper = is_at_operational( memberOf_ad->ad_type );
2334 			} else {
2335 				dlmp->dlm_memberOf_oper = 0;
2336 			}
2337 			dlmp->dlm_next = NULL;
2338 
2339 			if ( dlml != NULL )
2340 				dlml->dlm_next = dlmp;
2341 			dlml = dlmp;
2342 		}
2343 
2344 		if ( c->valx > 0 ) {
2345 			int	i;
2346 
2347 			for ( i = 0, dlip = (dynlist_info_t **)&dlg->dlg_dli;
2348 				i < c->valx; i++ )
2349 			{
2350 				if ( *dlip == NULL ) {
2351 					snprintf( c->cr_msg, sizeof( c->cr_msg ),
2352 						DYNLIST_USAGE
2353 						"invalid index {%d}\n",
2354 						c->valx );
2355 					Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
2356 						c->log, c->cr_msg );
2357 					rc = 1;
2358 					goto done_uri;
2359 				}
2360 				dlip = &(*dlip)->dli_next;
2361 			}
2362 			dli_next = *dlip;
2363 
2364 		} else {
2365 			for ( dlip = (dynlist_info_t **)&dlg->dlg_dli;
2366 				*dlip; dlip = &(*dlip)->dli_next )
2367 				/* goto last */;
2368 		}
2369 
2370 		*dlip = (dynlist_info_t *)ch_calloc( 1, sizeof( dynlist_info_t ) );
2371 
2372 		(*dlip)->dli_oc = oc;
2373 		(*dlip)->dli_ad = ad;
2374 		(*dlip)->dli_dlm = dlm;
2375 		(*dlip)->dli_next = dli_next;
2376 
2377 		(*dlip)->dli_lud = lud;
2378 		(*dlip)->dli_uri_nbase = nbase;
2379 		(*dlip)->dli_uri_filter = filter;
2380 		(*dlip)->dli_uri = uri;
2381 
2382 		rc = dynlist_build_def_filter( *dlip );
2383 
2384 		} break;
2385 
2386 	case DL_ATTRPAIR_COMPAT:
2387 		snprintf( c->cr_msg, sizeof( c->cr_msg ),
2388 			"warning: \"attrpair\" only supported for limited "
2389 			"backward compatibility with overlay \"dyngroup\"" );
2390 		Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
2391 		/* fallthru */
2392 
2393 	case DL_ATTRPAIR: {
2394 		dynlist_info_t		**dlip;
2395 		ObjectClass		*oc = NULL;
2396 		AttributeDescription	*ad = NULL,
2397 					*member_ad = NULL;
2398 		const char		*text;
2399 
2400 		oc = oc_find( "groupOfURLs" );
2401 		if ( oc == NULL ) {
2402 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2403 				"\"dynlist-attrpair <member-ad> <URL-ad>\": "
2404 				"unable to find default ObjectClass \"groupOfURLs\"" );
2405 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
2406 				c->log, c->cr_msg );
2407 			return 1;
2408 		}
2409 
2410 		rc = slap_str2ad( c->argv[ 1 ], &member_ad, &text );
2411 		if ( rc != LDAP_SUCCESS ) {
2412 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2413 				"\"dynlist-attrpair <member-ad> <URL-ad>\": "
2414 				"unable to find AttributeDescription \"%s\"",
2415 				c->argv[ 1 ] );
2416 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
2417 				c->log, c->cr_msg );
2418 			return 1;
2419 		}
2420 
2421 		rc = slap_str2ad( c->argv[ 2 ], &ad, &text );
2422 		if ( rc != LDAP_SUCCESS ) {
2423 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2424 				"\"dynlist-attrpair <member-ad> <URL-ad>\": "
2425 				"unable to find AttributeDescription \"%s\"\n",
2426 				c->argv[ 2 ] );
2427 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
2428 				c->log, c->cr_msg );
2429 			return 1;
2430 		}
2431 
2432 		if ( !is_at_subtype( ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
2433 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2434 				DYNLIST_USAGE
2435 				"AttributeDescription \"%s\" "
2436 				"must be a subtype of \"labeledURI\"",
2437 				c->argv[ 2 ] );
2438 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
2439 				c->log, c->cr_msg );
2440 			return 1;
2441 		}
2442 
2443 		for ( dlip = (dynlist_info_t **)&dlg->dlg_dli;
2444 			*dlip; dlip = &(*dlip)->dli_next )
2445 		{
2446 			/*
2447 			 * The same URL attribute / member attribute pair
2448 			 * cannot be repeated, but we enforce this only
2449 			 * when the member attribute is unique. Performing
2450 			 * the check for multiple values would require
2451 			 * sorting and comparing the lists, which is left
2452 			 * as a future improvement
2453 			 */
2454 			if ( (*dlip)->dli_ad == ad &&
2455 			     (*dlip)->dli_dlm->dlm_next == NULL &&
2456 			     member_ad == (*dlip)->dli_dlm->dlm_member_ad ) {
2457 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
2458 					"\"dynlist-attrpair <member-ad> <URL-ad>\": "
2459 					"URL attributeDescription \"%s\" already mapped.\n",
2460 					ad->ad_cname.bv_val );
2461 				Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
2462 					c->log, c->cr_msg );
2463 #if 0
2464 				/* make it a warning... */
2465 				return 1;
2466 #endif
2467 			}
2468 		}
2469 
2470 		*dlip = (dynlist_info_t *)ch_calloc( 1, sizeof( dynlist_info_t ) );
2471 
2472 		(*dlip)->dli_oc = oc;
2473 		(*dlip)->dli_ad = ad;
2474 		(*dlip)->dli_dlm = (dynlist_map_t *)ch_calloc( 1, sizeof( dynlist_map_t ) );
2475 		(*dlip)->dli_dlm->dlm_member_ad = member_ad;
2476 		(*dlip)->dli_dlm->dlm_mapped_ad = NULL;
2477 
2478 		rc = dynlist_build_def_filter( *dlip );
2479 
2480 		} break;
2481 
2482 	default:
2483 		rc = 1;
2484 		break;
2485 	}
2486 
2487 	return rc;
2488 }
2489 
2490 static int
dynlist_db_init(BackendDB * be,ConfigReply * cr)2491 dynlist_db_init(
2492 	BackendDB *be,
2493 	ConfigReply *cr)
2494 {
2495 	slap_overinst *on = (slap_overinst *)be->bd_info;
2496 	dynlist_gen_t *dlg;
2497 
2498 	dlg = (dynlist_gen_t *)ch_malloc( sizeof( *dlg ));
2499 	on->on_bi.bi_private = dlg;
2500 	dlg->dlg_dli = NULL;
2501 	dlg->dlg_memberOf = 0;
2502 
2503 	return 0;
2504 }
2505 
2506 static int
dynlist_db_open(BackendDB * be,ConfigReply * cr)2507 dynlist_db_open(
2508 	BackendDB	*be,
2509 	ConfigReply	*cr )
2510 {
2511 	slap_overinst		*on = (slap_overinst *) be->bd_info;
2512 	dynlist_gen_t		*dlg = (dynlist_gen_t *)on->on_bi.bi_private;
2513 	dynlist_info_t		*dli = dlg->dlg_dli;
2514 	ObjectClass		*oc = NULL;
2515 	AttributeDescription	*ad = NULL;
2516 	const char	*text;
2517 	int rc;
2518 
2519 	if ( dli == NULL ) {
2520 		dli = ch_calloc( 1, sizeof( dynlist_info_t ) );
2521 		dlg->dlg_dli = dli;
2522 	}
2523 
2524 	for ( ; dli; dli = dli->dli_next ) {
2525 		if ( dli->dli_oc == NULL ) {
2526 			if ( oc == NULL ) {
2527 				oc = oc_find( "groupOfURLs" );
2528 				if ( oc == NULL ) {
2529 					snprintf( cr->msg, sizeof( cr->msg),
2530 						"unable to fetch objectClass \"groupOfURLs\"" );
2531 					Debug( LDAP_DEBUG_ANY, "dynlist_db_open: %s.\n", cr->msg );
2532 					return 1;
2533 				}
2534 			}
2535 
2536 			dli->dli_oc = oc;
2537 		}
2538 
2539 		if ( dli->dli_ad == NULL ) {
2540 			if ( ad == NULL ) {
2541 				rc = slap_str2ad( "memberURL", &ad, &text );
2542 				if ( rc != LDAP_SUCCESS ) {
2543 					snprintf( cr->msg, sizeof( cr->msg),
2544 						"unable to fetch attributeDescription \"memberURL\": %d (%s)",
2545 						rc, text );
2546 					Debug( LDAP_DEBUG_ANY, "dynlist_db_open: %s.\n", cr->msg );
2547 					return 1;
2548 				}
2549 			}
2550 
2551 			dli->dli_ad = ad;
2552 		}
2553 
2554 		if ( BER_BVISNULL( &dli->dli_default_filter ) ) {
2555 			rc = dynlist_build_def_filter( dli );
2556 			if ( rc != 0 ) {
2557 				return rc;
2558 			}
2559 		}
2560 	}
2561 
2562 	if ( ad_dgIdentity == NULL ) {
2563 		rc = slap_str2ad( "dgIdentity", &ad_dgIdentity, &text );
2564 		if ( rc != LDAP_SUCCESS ) {
2565 			snprintf( cr->msg, sizeof( cr->msg),
2566 				"unable to fetch attributeDescription \"dgIdentity\": %d (%s)",
2567 				rc, text );
2568 			Debug( LDAP_DEBUG_ANY, "dynlist_db_open: %s\n", cr->msg );
2569 			/* Just a warning */
2570 		}
2571 	}
2572 
2573 	if ( ad_dgAuthz == NULL ) {
2574 		rc = slap_str2ad( "dgAuthz", &ad_dgAuthz, &text );
2575 		if ( rc != LDAP_SUCCESS ) {
2576 			snprintf( cr->msg, sizeof( cr->msg),
2577 				"unable to fetch attributeDescription \"dgAuthz\": %d (%s)",
2578 				rc, text );
2579 			Debug( LDAP_DEBUG_ANY, "dynlist_db_open: %s\n", cr->msg );
2580 			/* Just a warning */
2581 		}
2582 	}
2583 
2584 	return 0;
2585 }
2586 
2587 static int
dynlist_db_destroy(BackendDB * be,ConfigReply * cr)2588 dynlist_db_destroy(
2589 	BackendDB	*be,
2590 	ConfigReply	*cr )
2591 {
2592 	slap_overinst	*on = (slap_overinst *) be->bd_info;
2593 
2594 	if ( on->on_bi.bi_private ) {
2595 		dynlist_gen_t	*dlg = (dynlist_gen_t *)on->on_bi.bi_private;
2596 		dynlist_info_t	*dli = dlg->dlg_dli,
2597 				*dli_next;
2598 
2599 		for ( dli_next = dli; dli_next; dli = dli_next ) {
2600 			dynlist_map_t *dlm;
2601 			dynlist_map_t *dlm_next;
2602 
2603 			dli_next = dli->dli_next;
2604 
2605 			if ( !BER_BVISNULL( &dli->dli_uri ) ) {
2606 				ch_free( dli->dli_uri.bv_val );
2607 			}
2608 
2609 			if ( dli->dli_lud != NULL ) {
2610 				ldap_free_urldesc( dli->dli_lud );
2611 			}
2612 
2613 			if ( !BER_BVISNULL( &dli->dli_uri_nbase ) ) {
2614 				ber_memfree( dli->dli_uri_nbase.bv_val );
2615 			}
2616 
2617 			if ( dli->dli_uri_filter != NULL ) {
2618 				filter_free( dli->dli_uri_filter );
2619 			}
2620 
2621 			ch_free( dli->dli_default_filter.bv_val );
2622 
2623 			dlm = dli->dli_dlm;
2624 			while ( dlm != NULL ) {
2625 				dlm_next = dlm->dlm_next;
2626 				ch_free( dlm );
2627 				dlm = dlm_next;
2628 			}
2629 			ch_free( dli );
2630 		}
2631 		ch_free( dlg );
2632 	}
2633 
2634 	return 0;
2635 }
2636 
2637 static slap_overinst	dynlist = { { NULL } };
2638 #ifdef TAKEOVER_DYNGROUP
2639 static char		*obsolete_names[] = {
2640 	"dyngroup",
2641 	NULL
2642 };
2643 #endif
2644 
2645 #if SLAPD_OVER_DYNLIST == SLAPD_MOD_DYNAMIC
2646 static
2647 #endif /* SLAPD_OVER_DYNLIST == SLAPD_MOD_DYNAMIC */
2648 int
dynlist_initialize(void)2649 dynlist_initialize(void)
2650 {
2651 	const char *text;
2652 	int	rc = 0;
2653 
2654 	/* See if we need to define memberOf opattr */
2655 	rc = slap_str2ad( "memberOf", &ad_memberOf, &text );
2656 	if ( rc ) {
2657 		rc = register_at(
2658 		"( 1.2.840.113556.1.2.102 "
2659 		"NAME 'memberOf' "
2660 		"DESC 'Group that the entry belongs to' "
2661 		"SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' "
2662 		"EQUALITY distinguishedNameMatch "	/* added */
2663 		"USAGE dSAOperation "			/* added; questioned */
2664 		"NO-USER-MODIFICATION " 		/* added */
2665 		"X-ORIGIN 'iPlanet Delegated Administrator' )",
2666 		&ad_memberOf, 0 );
2667 		if ( rc ) {
2668 			Debug( LDAP_DEBUG_ANY,
2669 				"dynlist_initialize: register_at (memberOf) failed\n" );
2670 			return rc;
2671 		}
2672 	}
2673 
2674 	dynlist.on_bi.bi_type = "dynlist";
2675 
2676 #ifdef TAKEOVER_DYNGROUP
2677 	/* makes dynlist incompatible with dyngroup */
2678 	dynlist.on_bi.bi_obsolete_names = obsolete_names;
2679 #endif
2680 
2681 	dynlist.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
2682 	dynlist.on_bi.bi_db_init = dynlist_db_init;
2683 	dynlist.on_bi.bi_db_config = config_generic_wrapper;
2684 	dynlist.on_bi.bi_db_open = dynlist_db_open;
2685 	dynlist.on_bi.bi_db_destroy = dynlist_db_destroy;
2686 
2687 	dynlist.on_bi.bi_op_search = dynlist_search;
2688 	dynlist.on_bi.bi_op_compare = dynlist_compare;
2689 
2690 	dynlist.on_bi.bi_cf_ocs = dlocs;
2691 
2692 	rc = config_register_schema( dlcfg, dlocs );
2693 	if ( rc ) {
2694 		return rc;
2695 	}
2696 
2697 	return overlay_register( &dynlist );
2698 }
2699 
2700 #if SLAPD_OVER_DYNLIST == SLAPD_MOD_DYNAMIC
2701 int
init_module(int argc,char * argv[])2702 init_module( int argc, char *argv[] )
2703 {
2704 	return dynlist_initialize();
2705 }
2706 #endif
2707 
2708 #endif /* SLAPD_OVER_DYNLIST */
2709