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