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