1/* autogroup.c - automatic group overlay */
2/* $OpenLDAP$ */
3/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 *
5 * Copyright 2007-2011 The OpenLDAP Foundation.
6 * Portions Copyright 2007 Michał Szulczyński.
7 * Portions Copyright 2009 Howard Chu.
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 Michał Szulczyński for inclusion in
20 * OpenLDAP Software.  Additional significant contributors include:
21 *   Howard Chu
22 *   Raphael Ouazana
23 *   Norbert Pueschel
24 *   Christian Manal
25 */
26
27#include "portable.h"
28
29#include <stdio.h>
30
31#include <ac/string.h>
32
33#include "slap.h"
34#include "config.h"
35#include "lutil.h"
36
37#ifndef SLAPD_MEMBEROF_ATTR
38#define	SLAPD_MEMBEROF_ATTR	"memberOf"
39#endif
40
41/* Filter represents the memberURL of a group. */
42typedef struct autogroup_filter_t {
43	struct berval			agf_dn;	/* The base DN in memberURL */
44	struct berval			agf_ndn;
45	struct berval			agf_filterstr;
46	Filter				*agf_filter;
47	int				agf_scope;
48	AttributeName			*agf_anlist;
49	struct autogroup_filter_t	*agf_next;
50} autogroup_filter_t;
51
52/* Description of group attributes. */
53typedef struct autogroup_def_t {
54	ObjectClass		*agd_oc;
55	AttributeDescription	*agd_member_url_ad;
56	AttributeDescription	*agd_member_ad;
57	struct autogroup_def_t	*agd_next;
58} autogroup_def_t;
59
60/* Represents the group entry. */
61typedef struct autogroup_entry_t {
62	BerValue		age_dn;
63	BerValue		age_ndn;
64	autogroup_filter_t	*age_filter; /* List of filters made from memberURLs */
65	autogroup_def_t		*age_def; /* Attribute definition */
66	ldap_pvt_thread_mutex_t age_mutex;
67	int			age_mustrefresh; /* Defined in request to refresh in response */
68	int			age_modrdn_olddnmodified; /* Defined in request to refresh in response */
69	struct autogroup_entry_t	*age_next;
70} autogroup_entry_t;
71
72/* Holds pointers to attribute definitions and groups. */
73typedef struct autogroup_info_t {
74	autogroup_def_t		*agi_def;	/* Group attributes definitions. */
75	autogroup_entry_t	*agi_entry;	/* Group entries.  */
76	AttributeDescription	*agi_memberof_ad;	/* memberOf attribute description  */
77	ldap_pvt_thread_mutex_t agi_mutex;
78} autogroup_info_t;
79
80/* Search callback for adding groups initially. */
81typedef struct autogroup_sc_t {
82	autogroup_info_t		*ags_info;	/* Group definitions and entries.  */
83	autogroup_def_t		*ags_def;	/* Attributes definition of the group being added. */
84} autogroup_sc_t;
85
86/* Used for adding members, found when searching, to a group. */
87typedef struct autogroup_ga_t {
88	autogroup_entry_t	*agg_group;	/* The group to which the members will be added. */
89	autogroup_filter_t	*agg_filter;	/* Current filter */
90	Entry			*agg_entry;	/* Used in autogroup_member_search_cb to modify
91						this entry with the search results. */
92
93	Modifications		*agg_mod;	/* Used in autogroup_member_search_modify_cb to hold the
94						search results which will be added to the group. */
95
96	Modifications		*agg_mod_last; /* Used in autogroup_member_search_modify_cb so we don't
97						have to search for the last mod added. */
98} autogroup_ga_t;
99
100
101/*
102**	dn, ndn	- the DN of the member to add
103**	age	- the group to which the member DN will be added
104*/
105static int
106autogroup_add_member_to_group( Operation *op, BerValue *dn, BerValue *ndn, autogroup_entry_t *age )
107{
108	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
109	Modifications	*modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
110	SlapReply	sreply = {REP_RESULT};
111	BerValue	*vals, *nvals;
112	slap_callback	cb = { NULL, slap_null_cb, NULL, NULL };
113	Operation	o = *op;
114
115	assert( dn != NULL );
116	assert( ndn != NULL );
117	Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_member_to_group adding <%s> to <%s>\n",
118		dn->bv_val, age->age_dn.bv_val, 0);
119
120	vals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
121	nvals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
122	ber_dupbv( vals, dn );
123	BER_BVZERO( &vals[ 1 ] );
124	ber_dupbv( nvals, ndn );
125	BER_BVZERO( &nvals[ 1 ] );
126
127	modlist->sml_op = LDAP_MOD_ADD;
128	modlist->sml_desc = age->age_def->agd_member_ad;
129	modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
130	modlist->sml_values = vals;
131	modlist->sml_nvalues = nvals;
132	modlist->sml_numvals = 1;
133	modlist->sml_flags = SLAP_MOD_INTERNAL;
134	modlist->sml_next = NULL;
135
136	o.o_tag = LDAP_REQ_MODIFY;
137	o.o_callback = &cb;
138	o.orm_modlist = modlist;
139	o.o_req_dn = age->age_dn;
140	o.o_req_ndn = age->age_ndn;
141	o.o_permissive_modify = 1;
142	o.o_managedsait = SLAP_CONTROL_CRITICAL;
143	o.o_relax = SLAP_CONTROL_CRITICAL;
144
145	o.o_bd->bd_info = (BackendInfo *)on->on_info;
146	(void)op->o_bd->be_modify( &o, &sreply );
147	o.o_bd->bd_info = (BackendInfo *)on;
148
149	slap_mods_free( modlist, 1 );
150
151	return sreply.sr_err;
152}
153
154/*
155**	e	- the entry where to get the attribute values
156**	age	- the group to which the values will be added
157*/
158static int
159autogroup_add_member_values_to_group( Operation *op, Entry *e, autogroup_entry_t *age, AttributeDescription *attrdesc )
160{
161	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
162	Modifications	modlist;
163	SlapReply	sreply = {REP_RESULT};
164	Attribute	*attr;
165	slap_callback	cb = { NULL, slap_null_cb, NULL, NULL };
166	Operation	o = *op;
167
168	assert( e != NULL );
169	Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_member_values_to_group adding <%s> to <%s>\n",
170		e->e_name.bv_val, age->age_dn.bv_val, 0);
171
172	attr = attrs_find( e->e_attrs, attrdesc );
173	if (!attr) {
174		// Nothing to add
175		return LDAP_SUCCESS;
176	}
177
178	modlist.sml_op = LDAP_MOD_ADD;
179	modlist.sml_desc = age->age_def->agd_member_ad;
180	modlist.sml_type = age->age_def->agd_member_ad->ad_cname;
181	modlist.sml_values = attr->a_vals;
182	modlist.sml_nvalues = attr->a_nvals;
183	modlist.sml_numvals = attr->a_numvals;
184	modlist.sml_flags = SLAP_MOD_INTERNAL;
185	modlist.sml_next = NULL;
186
187	o.o_tag = LDAP_REQ_MODIFY;
188	o.o_callback = &cb;
189	o.orm_modlist = &modlist;
190	o.o_req_dn = age->age_dn;
191	o.o_req_ndn = age->age_ndn;
192	o.o_permissive_modify = 1;
193	o.o_managedsait = SLAP_CONTROL_CRITICAL;
194	o.o_relax = SLAP_CONTROL_CRITICAL;
195
196	o.o_bd->bd_info = (BackendInfo *)on->on_info;
197	(void)op->o_bd->be_modify( &o, &sreply );
198	o.o_bd->bd_info = (BackendInfo *)on;
199
200	return sreply.sr_err;
201}
202
203/*
204** dn,ndn	- the DN to be deleted
205** age		- the group from which the DN will be deleted
206** If we pass a NULL dn and ndn, all members are deleted from the group.
207*/
208static int
209autogroup_delete_member_from_group( Operation *op, BerValue *dn, BerValue *ndn, autogroup_entry_t *age )
210{
211	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
212	Modifications	*modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
213	SlapReply	sreply = {REP_RESULT};
214	BerValue	*vals, *nvals;
215	slap_callback	cb = { NULL, slap_null_cb, NULL, NULL };
216	Operation	o = *op;
217
218	if ( dn == NULL || ndn == NULL ) {
219		Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_from_group removing all members from <%s>\n",
220			age->age_dn.bv_val, 0 ,0);
221
222		modlist->sml_values = NULL;
223		modlist->sml_nvalues = NULL;
224		modlist->sml_numvals = 0;
225	} else {
226		Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_from_group removing <%s> from <%s>\n",
227			dn->bv_val, age->age_dn.bv_val, 0);
228
229		vals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
230		nvals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
231		ber_dupbv( vals, dn );
232		BER_BVZERO( &vals[ 1 ] );
233		ber_dupbv( nvals, ndn );
234		BER_BVZERO( &nvals[ 1 ] );
235
236		modlist->sml_values = vals;
237		modlist->sml_nvalues = nvals;
238		modlist->sml_numvals = 1;
239	}
240
241
242	modlist->sml_op = LDAP_MOD_DELETE;
243	modlist->sml_desc = age->age_def->agd_member_ad;
244	modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
245	modlist->sml_flags = SLAP_MOD_INTERNAL;
246	modlist->sml_next = NULL;
247
248	o.o_callback = &cb;
249	o.o_tag = LDAP_REQ_MODIFY;
250	o.orm_modlist = modlist;
251	o.o_req_dn = age->age_dn;
252	o.o_req_ndn = age->age_ndn;
253	o.o_relax = SLAP_CONTROL_CRITICAL;
254	o.o_managedsait = SLAP_CONTROL_CRITICAL;
255	o.o_permissive_modify = 1;
256
257	o.o_bd->bd_info = (BackendInfo *)on->on_info;
258	(void)op->o_bd->be_modify( &o, &sreply );
259	o.o_bd->bd_info = (BackendInfo *)on;
260
261	slap_mods_free( modlist, 1 );
262
263	return sreply.sr_err;
264}
265
266/*
267**      e       - the entry where to get the attribute values
268**      age     - the group from which the values will be deleted
269*/
270static int
271autogroup_delete_member_values_from_group( Operation *op, Entry *e, autogroup_entry_t *age, AttributeDescription *attrdesc )
272{
273        slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
274        Modifications   modlist;
275        SlapReply       sreply = {REP_RESULT};
276        Attribute       *attr;
277        slap_callback   cb = { NULL, slap_null_cb, NULL, NULL };
278        Operation       o = *op;
279
280        assert( e != NULL );
281        Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_values_from_group removing <%s> from <%s>\n",
282                e->e_name.bv_val, age->age_dn.bv_val, 0);
283
284        attr = attrs_find( e->e_attrs, attrdesc );
285        if (!attr) {
286                // Nothing to add
287                return LDAP_SUCCESS;
288        }
289
290        modlist.sml_op = LDAP_MOD_DELETE;
291        modlist.sml_desc = age->age_def->agd_member_ad;
292        modlist.sml_type = age->age_def->agd_member_ad->ad_cname;
293        modlist.sml_values = attr->a_vals;
294        modlist.sml_nvalues = attr->a_nvals;
295        modlist.sml_numvals = attr->a_numvals;
296        modlist.sml_flags = SLAP_MOD_INTERNAL;
297        modlist.sml_next = NULL;
298
299        o.o_tag = LDAP_REQ_MODIFY;
300        o.o_callback = &cb;
301        o.orm_modlist = &modlist;
302        o.o_req_dn = age->age_dn;
303        o.o_req_ndn = age->age_ndn;
304        o.o_permissive_modify = 1;
305        o.o_managedsait = SLAP_CONTROL_CRITICAL;
306        o.o_relax = SLAP_CONTROL_CRITICAL;
307
308        o.o_bd->bd_info = (BackendInfo *)on->on_info;
309        (void)op->o_bd->be_modify( &o, &sreply );
310        o.o_bd->bd_info = (BackendInfo *)on;
311
312        return sreply.sr_err;
313}
314
315/*
316** Callback used to add entries to a group,
317** which are going to be written in the database
318** (used in bi_op_add)
319** The group is passed in autogroup_ga_t->agg_group
320*/
321static int
322autogroup_member_search_cb( Operation *op, SlapReply *rs )
323{
324	assert( op->o_tag == LDAP_REQ_SEARCH );
325
326	if ( rs->sr_type == REP_SEARCH ) {
327		autogroup_ga_t		*agg = (autogroup_ga_t *)op->o_callback->sc_private;
328		autogroup_entry_t	*age = agg->agg_group;
329		autogroup_filter_t	*agf = agg->agg_filter;
330		Modification		mod;
331		const char		*text = NULL;
332		char			textbuf[1024];
333		struct berval		*vals, *nvals;
334		int			numvals;
335
336		Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_cb <%s>\n",
337			rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
338
339		if ( agf->agf_anlist ) {
340			Attribute *attr = attrs_find( rs->sr_entry->e_attrs, agf->agf_anlist[0].an_desc );
341			if (attr) {
342				vals = attr->a_vals;
343				nvals = attr->a_nvals;
344				numvals = attr->a_numvals;
345			} else {
346				// Nothing to add
347				return 0;
348			}
349		} else {
350			struct berval		lvals[ 2 ], lnvals[ 2 ];
351			lvals[ 0 ] = rs->sr_entry->e_name;
352			BER_BVZERO( &lvals[ 1 ] );
353			lnvals[ 0 ] = rs->sr_entry->e_nname;
354			BER_BVZERO( &lnvals[ 1 ] );
355			vals = lvals;
356			nvals = lnvals;
357			numvals = 1;
358		}
359
360		mod.sm_op = LDAP_MOD_ADD;
361		mod.sm_desc = age->age_def->agd_member_ad;
362		mod.sm_type = age->age_def->agd_member_ad->ad_cname;
363		mod.sm_values = vals;
364		mod.sm_nvalues = nvals;
365		mod.sm_numvals = numvals;
366
367		modify_add_values( agg->agg_entry, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
368	}
369
370	return 0;
371}
372
373/*
374** Callback used to add entries to a group, which is already in the database.
375** (used in on_response)
376** The group is passed in autogroup_ga_t->agg_group
377** NOTE: Very slow.
378*/
379static int
380autogroup_member_search_modify_cb( Operation *op, SlapReply *rs )
381{
382	assert( op->o_tag == LDAP_REQ_SEARCH );
383
384	if ( rs->sr_type == REP_SEARCH ) {
385		autogroup_ga_t		*agg = (autogroup_ga_t *)op->o_callback->sc_private;
386		autogroup_entry_t	*age = agg->agg_group;
387		autogroup_filter_t	*agf = agg->agg_filter;
388		Modifications		*modlist;
389		struct berval		*vals, *nvals;
390		int			numvals;
391
392		Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_modify_cb <%s>\n",
393			rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
394
395		if ( agf->agf_anlist ) {
396			Attribute *attr = attrs_find( rs->sr_entry->e_attrs, agf->agf_anlist[0].an_desc );
397			if (attr) {
398				vals = attr->a_vals;
399				nvals = attr->a_nvals;
400				numvals = attr->a_numvals;
401			} else {
402				// Nothing to add
403				return 0;
404			}
405		} else {
406			struct berval		lvals[ 2 ], lnvals[ 2 ];
407			lvals[ 0 ] = rs->sr_entry->e_name;
408			BER_BVZERO( &lvals[ 1 ] );
409			lnvals[ 0 ] = rs->sr_entry->e_nname;
410			BER_BVZERO( &lnvals[ 1 ] );
411			vals = lvals;
412			nvals = lnvals;
413			numvals = 1;
414		}
415
416		if ( numvals ) {
417			modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
418
419			modlist->sml_op = LDAP_MOD_ADD;
420			modlist->sml_desc = age->age_def->agd_member_ad;
421			modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
422
423			ber_bvarray_dup_x( &modlist->sml_values, vals, NULL );
424			ber_bvarray_dup_x( &modlist->sml_nvalues, nvals, NULL );
425			modlist->sml_numvals = numvals;
426
427			modlist->sml_flags = SLAP_MOD_INTERNAL;
428			modlist->sml_next = NULL;
429
430			if ( agg->agg_mod == NULL ) {
431				agg->agg_mod = modlist;
432				agg->agg_mod_last = modlist;
433			} else {
434				agg->agg_mod_last->sml_next = modlist;
435				agg->agg_mod_last = modlist;
436			}
437		}
438
439	}
440
441	return 0;
442}
443
444
445/*
446** Adds all entries matching the passed filter to the specified group.
447** If modify == 1, then we modify the group's entry in the database using be_modify.
448** If modify == 0, then, we must supply a rw entry for the group,
449**	because we only modify the entry, without calling be_modify.
450** e	- the group entry, to which the members will be added
451** age	- the group
452** agf	- the filter
453*/
454static int
455autogroup_add_members_from_filter( Operation *op, Entry *e, autogroup_entry_t *age, autogroup_filter_t *agf, int modify)
456{
457	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
458	Operation		o = *op;
459	SlapReply		rs = { REP_SEARCH };
460	slap_callback		cb = { 0 };
461	slap_callback		null_cb = { NULL, slap_null_cb, NULL, NULL };
462	autogroup_ga_t		agg;
463
464	Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_members_from_filter <%s>\n",
465		age->age_dn.bv_val, 0, 0);
466
467	o.ors_attrsonly = 0;
468	o.o_tag = LDAP_REQ_SEARCH;
469
470	o.o_req_dn = agf->agf_dn;
471	o.o_req_ndn = agf->agf_ndn;
472
473	o.ors_filterstr = agf->agf_filterstr;
474	o.ors_filter = agf->agf_filter;
475
476	o.ors_scope = agf->agf_scope;
477	o.ors_deref = LDAP_DEREF_NEVER;
478	o.ors_limit = NULL;
479	o.ors_tlimit = SLAP_NO_LIMIT;
480	o.ors_slimit = SLAP_NO_LIMIT;
481	o.ors_attrs =  agf->agf_anlist ? agf->agf_anlist : slap_anlist_no_attrs;
482
483	agg.agg_group = age;
484	agg.agg_filter = agf;
485	agg.agg_mod = NULL;
486	agg.agg_mod_last = NULL;
487	agg.agg_entry = e;
488	cb.sc_private = &agg;
489
490	if ( modify == 1 ) {
491		cb.sc_response = autogroup_member_search_modify_cb;
492	} else {
493		cb.sc_response = autogroup_member_search_cb;
494	}
495
496	cb.sc_cleanup = NULL;
497	cb.sc_next = NULL;
498
499	o.o_callback = &cb;
500
501	o.o_bd->bd_info = (BackendInfo *)on->on_info;
502	op->o_bd->be_search( &o, &rs );
503	o.o_bd->bd_info = (BackendInfo *)on;
504
505	if ( modify == 1 && agg.agg_mod ) {
506		rs_reinit( &rs, REP_RESULT );
507
508		o = *op;
509		o.o_callback = &null_cb;
510		o.o_tag = LDAP_REQ_MODIFY;
511		o.orm_modlist = agg.agg_mod;
512		o.o_req_dn = age->age_dn;
513		o.o_req_ndn = age->age_ndn;
514		o.o_relax = SLAP_CONTROL_CRITICAL;
515		o.o_managedsait = SLAP_CONTROL_NONCRITICAL;
516		o.o_permissive_modify = 1;
517
518		o.o_bd->bd_info = (BackendInfo *)on->on_info;
519		(void)op->o_bd->be_modify( &o, &rs );
520		o.o_bd->bd_info = (BackendInfo *)on;
521
522		slap_mods_free(agg.agg_mod, 1);
523	}
524
525	return 0;
526}
527
528/*
529** Adds a group to the internal list from the passed entry.
530** scan specifies whether to add all maching members to the group.
531** modify specifies whether to modify the given group entry (when modify == 0),
532**	or to modify the group entry in the database (when modify == 1 and e = NULL and ndn != NULL).
533** agi	- pointer to the groups and the attribute definitions
534** agd - the attribute definition of the added group
535** e	- the entry representing the group, can be NULL if the ndn is specified, and modify == 1
536** ndn	- the DN of the group, can be NULL if we give a non-NULL e
537*/
538static int
539autogroup_add_group( Operation *op, autogroup_info_t *agi, autogroup_def_t *agd, Entry *e, BerValue *ndn, int scan, int modify)
540{
541	autogroup_entry_t	**agep = &agi->agi_entry;
542	autogroup_filter_t	*agf, *agf_prev = NULL;
543	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
544	LDAPURLDesc		*lud = NULL;
545	Attribute		*a;
546	BerValue		*bv, dn;
547	int			rc = 0, match = 1, null_entry = 0;
548
549	if ( e == NULL ) {
550		if ( overlay_entry_get_ov( op, ndn, NULL, NULL, 0, &e, on ) !=
551			LDAP_SUCCESS || e == NULL ) {
552			Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot get entry for <%s>\n", ndn->bv_val, 0, 0);
553			return 1;
554		}
555
556		null_entry = 1;
557	}
558
559	Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_group <%s>\n",
560		e->e_name.bv_val, 0, 0);
561
562	if ( agi->agi_entry != NULL ) {
563		for ( ; *agep ; agep = &(*agep)->age_next ) {
564			dnMatch( &match, 0, NULL, NULL, &e->e_nname, &(*agep)->age_ndn );
565			if ( match == 0 ) {
566				Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: group already exists: <%s>\n", e->e_name.bv_val,0,0);
567				return 1;
568			}
569			/* goto last */;
570		}
571	}
572
573
574	*agep = (autogroup_entry_t *)ch_calloc( 1, sizeof( autogroup_entry_t ) );
575	ldap_pvt_thread_mutex_init( &(*agep)->age_mutex );
576	(*agep)->age_def = agd;
577	(*agep)->age_filter = NULL;
578	(*agep)->age_mustrefresh = 0;
579	(*agep)->age_modrdn_olddnmodified = 0;
580
581	ber_dupbv( &(*agep)->age_dn, &e->e_name );
582	ber_dupbv( &(*agep)->age_ndn, &e->e_nname );
583
584	a = attrs_find( e->e_attrs, agd->agd_member_url_ad );
585
586	if ( null_entry == 1 ) {
587		a = attrs_dup( a );
588		overlay_entry_release_ov( op, e, 0, on );
589	}
590
591	if( a == NULL ) {
592		Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: group has no memberURL\n", 0,0,0);
593	} else {
594		for ( bv = a->a_nvals; !BER_BVISNULL( bv ); bv++ ) {
595
596			agf = (autogroup_filter_t*)ch_calloc( 1, sizeof( autogroup_filter_t ) );
597
598			if ( ldap_url_parse( bv->bv_val, &lud ) != LDAP_URL_SUCCESS ) {
599				Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot parse url <%s>\n", bv->bv_val,0,0);
600				/* FIXME: error? */
601				ch_free( agf );
602				continue;
603			}
604
605			agf->agf_scope = lud->lud_scope;
606
607			if ( lud->lud_dn == NULL ) {
608				BER_BVSTR( &dn, "" );
609			} else {
610				ber_str2bv( lud->lud_dn, 0, 0, &dn );
611			}
612
613			rc = dnPrettyNormal( NULL, &dn, &agf->agf_dn, &agf->agf_ndn, NULL );
614			if ( rc != LDAP_SUCCESS ) {
615				Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot normalize DN <%s>\n", dn.bv_val,0,0);
616				/* FIXME: error? */
617				goto cleanup;
618			}
619
620			if ( lud->lud_filter != NULL ) {
621				ber_str2bv( lud->lud_filter, 0, 1, &agf->agf_filterstr);
622				agf->agf_filter = str2filter( lud->lud_filter );
623			}
624
625			if ( lud->lud_attrs != NULL ) {
626				int i;
627
628				for ( i=0 ; lud->lud_attrs[i]!=NULL ; i++) {
629					/* Just counting */;
630				}
631
632				if ( i > 1 ) {
633					Debug( LDAP_DEBUG_ANY, "autogroup_add_group: too many attributes specified in url <%s>\n",
634						bv->bv_val, 0, 0);
635					/* FIXME: error? */
636					ldap_free_urldesc( lud );
637					ch_free( agf );
638					continue;
639				}
640
641				agf->agf_anlist = str2anlist( NULL, lud->lud_attrs[0], "," );
642
643				if ( agf->agf_anlist == NULL ) {
644					Debug( LDAP_DEBUG_ANY, "autogroup_add_group: unable to find AttributeDescription \"%s\".\n",
645						lud->lud_attrs[0], 0, 0 );
646					/* FIXME: error? */
647					ldap_free_urldesc( lud );
648					ch_free( agf );
649					continue;
650				}
651			}
652
653			agf->agf_next = NULL;
654
655
656			if( (*agep)->age_filter == NULL ) {
657				(*agep)->age_filter = agf;
658			}
659
660			if( agf_prev != NULL ) {
661				agf_prev->agf_next = agf;
662			}
663
664			agf_prev = agf;
665
666			if ( scan == 1 ){
667				autogroup_add_members_from_filter( op, e, (*agep), agf, modify );
668			}
669
670			Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: added memberURL DN <%s> with filter <%s>\n",
671				agf->agf_ndn.bv_val, agf->agf_filterstr.bv_val, 0);
672
673			ldap_free_urldesc( lud );
674
675			continue;
676
677
678cleanup:;
679
680			ldap_free_urldesc( lud );
681			ch_free( agf );
682		}
683	}
684
685	if ( null_entry == 1 ) {
686		attrs_free( a );
687	}
688	return rc;
689}
690
691/*
692** Used when opening the database to add all existing
693** groups from the database to our internal list.
694*/
695static int
696autogroup_group_add_cb( Operation *op, SlapReply *rs )
697{
698	assert( op->o_tag == LDAP_REQ_SEARCH );
699
700	if ( rs->sr_type == REP_SEARCH ) {
701		autogroup_sc_t		*ags = (autogroup_sc_t *)op->o_callback->sc_private;
702
703		Debug(LDAP_DEBUG_TRACE, "==> autogroup_group_add_cb <%s>\n",
704			rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
705
706		autogroup_add_group( op, ags->ags_info, ags->ags_def, rs->sr_entry, NULL, 0, 0);
707	}
708
709	return 0;
710}
711
712
713/*
714** When adding a group, we first strip any existing members,
715** and add all which match the filters ourselfs.
716*/
717static int
718autogroup_add_entry( Operation *op, SlapReply *rs)
719{
720	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
721	autogroup_info_t	*agi = (autogroup_info_t *)on->on_bi.bi_private;
722	autogroup_def_t		*agd = agi->agi_def;
723	autogroup_entry_t	*age;
724	autogroup_filter_t	*agf;
725	int			rc = 0;
726
727	Debug( LDAP_DEBUG_TRACE, "==> autogroup_add_entry <%s>\n",
728		op->ora_e->e_name.bv_val, 0, 0);
729
730	ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
731
732	/* Check if it's a group. */
733	for ( ; agd ; agd = agd->agd_next ) {
734		if ( is_entry_objectclass_or_sub( op->ora_e, agd->agd_oc ) ) {
735			Modification		mod;
736			const char		*text = NULL;
737			char			textbuf[1024];
738
739			mod.sm_op = LDAP_MOD_DELETE;
740			mod.sm_desc = agd->agd_member_ad;
741			mod.sm_type = agd->agd_member_ad->ad_cname;
742			mod.sm_values = NULL;
743			mod.sm_nvalues = NULL;
744
745			/* We don't want any member attributes added by the user. */
746			modify_delete_values( op->ora_e, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
747
748			autogroup_add_group( op, agi, agd, op->ora_e, NULL, 1 , 0);
749			ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
750			return SLAP_CB_CONTINUE;
751		}
752	}
753
754
755	for ( age = agi->agi_entry; age ; age = age->age_next ) {
756		ldap_pvt_thread_mutex_lock( &age->age_mutex );
757
758		/* Check if any of the filters are the suffix to the entry DN.
759		   If yes, we can test that filter against the entry. */
760
761		for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
762			if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
763				rc = test_filter( op, op->ora_e, agf->agf_filter );
764				if ( rc == LDAP_COMPARE_TRUE ) {
765					if ( agf->agf_anlist ) {
766						autogroup_add_member_values_to_group( op, op->ora_e, age, agf->agf_anlist[0].an_desc );
767					} else {
768						autogroup_add_member_to_group( op, &op->ora_e->e_name, &op->ora_e->e_nname, age );
769					}
770					break;
771				}
772			}
773		}
774		ldap_pvt_thread_mutex_unlock( &age->age_mutex );
775	}
776
777	ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
778
779	return SLAP_CB_CONTINUE;
780}
781
782/*
783** agi	- internal group and attribute definitions list
784** e	- the group to remove from the internal list
785*/
786static int
787autogroup_delete_group( autogroup_info_t *agi, autogroup_entry_t *e )
788{
789	autogroup_entry_t	*age = agi->agi_entry,
790				*age_prev = NULL,
791				*age_next;
792	int			rc = 1;
793
794	Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_group <%s>\n",
795		age->age_dn.bv_val, 0, 0);
796
797	for ( age_next = age ; age_next ; age_prev = age, age = age_next ) {
798		age_next = age->age_next;
799
800		if ( age == e ) {
801			autogroup_filter_t	*agf = age->age_filter,
802							*agf_next;
803
804			if ( age_prev != NULL ) {
805				age_prev->age_next = age_next;
806			} else {
807				agi->agi_entry = NULL;
808			}
809
810			ch_free( age->age_dn.bv_val );
811			ch_free( age->age_ndn.bv_val );
812
813			for( agf_next = agf ; agf_next ; agf = agf_next ){
814				agf_next = agf->agf_next;
815
816				filter_free( agf->agf_filter );
817				ch_free( agf->agf_filterstr.bv_val );
818				ch_free( agf->agf_dn.bv_val );
819				ch_free( agf->agf_ndn.bv_val );
820				anlist_free( agf->agf_anlist, 1, NULL );
821				ch_free( agf );
822			}
823
824			ldap_pvt_thread_mutex_unlock( &age->age_mutex );
825			ldap_pvt_thread_mutex_destroy( &age->age_mutex );
826			ch_free( age );
827
828			rc = 0;
829			return rc;
830
831		}
832	}
833
834	Debug( LDAP_DEBUG_TRACE, "autogroup_delete_group: group <%s> not found, should not happen\n", age->age_dn.bv_val, 0, 0);
835
836	return rc;
837
838}
839
840static int
841autogroup_delete_entry( Operation *op, SlapReply *rs)
842{
843	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
844	autogroup_info_t	*agi = (autogroup_info_t *)on->on_bi.bi_private;
845	autogroup_entry_t	*age, *age_prev, *age_next;
846	autogroup_filter_t	*agf;
847	Entry			*e;
848	int			matched_group = 0, rc = 0;
849
850	Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
851
852	ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
853
854	if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
855		LDAP_SUCCESS || e == NULL ) {
856		Debug( LDAP_DEBUG_TRACE, "autogroup_delete_entry: cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
857		ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
858		return SLAP_CB_CONTINUE;
859	}
860
861	/* Check if the entry to be deleted is one of our groups. */
862	for ( age_next = agi->agi_entry ; age_next ; age_prev = age ) {
863		age = age_next;
864		ldap_pvt_thread_mutex_lock( &age->age_mutex );
865		age_next = age->age_next;
866
867		if ( is_entry_objectclass_or_sub( e, age->age_def->agd_oc ) ) {
868			int match = 1;
869
870			matched_group = 1;
871
872			dnMatch( &match, 0, NULL, NULL, &e->e_nname, &age->age_ndn );
873
874			if ( match == 0 ) {
875				autogroup_delete_group( agi, age );
876				break;
877			}
878		}
879
880		ldap_pvt_thread_mutex_unlock( &age->age_mutex );
881	}
882
883	if ( matched_group == 1 ) {
884		overlay_entry_release_ov( op, e, 0, on );
885		ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
886		return SLAP_CB_CONTINUE;
887	}
888
889	/* Check if the entry matches any of the groups.
890	   If yes, we can delete the entry from that group. */
891
892	for ( age = agi->agi_entry ; age ; age = age->age_next ) {
893		ldap_pvt_thread_mutex_lock( &age->age_mutex );
894
895		for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
896			if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
897				rc = test_filter( op, e, agf->agf_filter );
898				if ( rc == LDAP_COMPARE_TRUE ) {
899					/* If the attribute is retrieved from the entry, we don't know what to delete
900					** So the group must be entirely refreshed
901					** But the refresh can't be done now because the entry is not deleted
902					** So the group is marked as mustrefresh
903					*/
904					if ( agf->agf_anlist ) {
905						age->age_mustrefresh = 1;
906					} else {
907						autogroup_delete_member_from_group( op, &e->e_name, &e->e_nname, age );
908					}
909					break;
910				}
911			}
912		}
913		ldap_pvt_thread_mutex_unlock( &age->age_mutex );
914	}
915
916	overlay_entry_release_ov( op, e, 0, on );
917	ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
918
919	return SLAP_CB_CONTINUE;
920}
921
922static int
923autogroup_response( Operation *op, SlapReply *rs )
924{
925	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
926	autogroup_info_t	*agi = (autogroup_info_t *)on->on_bi.bi_private;
927	autogroup_def_t		*agd = agi->agi_def;
928	autogroup_entry_t	*age;
929	autogroup_filter_t	*agf;
930	BerValue		new_dn, new_ndn, pdn;
931	Entry			*e, *group;
932	Attribute		*a, *ea;
933	int			is_olddn, is_newdn, is_value_refresh, dn_equal;
934
935	/* Handle all cases where a refresh of the group is needed */
936	if ( op->o_tag == LDAP_REQ_DELETE || op->o_tag == LDAP_REQ_MODIFY ) {
937		if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op ) ) {
938
939			ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
940
941			for ( age = agi->agi_entry ; age ; age = age->age_next ) {
942				/* Request detected that the group must be refreshed */
943
944				ldap_pvt_thread_mutex_lock( &age->age_mutex );
945
946				if ( age->age_mustrefresh ) {
947					autogroup_delete_member_from_group( op, NULL, NULL, age) ;
948
949					for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
950						autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
951					}
952				}
953
954				ldap_pvt_thread_mutex_unlock( &age->age_mutex );
955			}
956
957			ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
958		}
959	} else if ( op->o_tag == LDAP_REQ_MODRDN ) {
960		if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op )) {
961
962			Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODRDN from <%s>\n", op->o_req_dn.bv_val, 0, 0);
963
964			ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
965
966			if ( op->oq_modrdn.rs_newSup ) {
967				pdn = *op->oq_modrdn.rs_newSup;
968			} else {
969				dnParent( &op->o_req_dn, &pdn );
970			}
971			build_new_dn( &new_dn, &pdn, &op->orr_newrdn, op->o_tmpmemctx );
972
973			if ( op->oq_modrdn.rs_nnewSup ) {
974				pdn = *op->oq_modrdn.rs_nnewSup;
975			} else {
976				dnParent( &op->o_req_ndn, &pdn );
977			}
978			build_new_dn( &new_ndn, &pdn, &op->orr_nnewrdn, op->o_tmpmemctx );
979
980			Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN to <%s>\n", new_dn.bv_val, 0, 0);
981
982			dnMatch( &dn_equal, 0, NULL, NULL, &op->o_req_ndn, &new_ndn );
983
984			if ( overlay_entry_get_ov( op, &new_ndn, NULL, NULL, 0, &e, on ) !=
985				LDAP_SUCCESS || e == NULL ) {
986				Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get entry for <%s>\n", new_dn.bv_val, 0, 0);
987				ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
988				return SLAP_CB_CONTINUE;
989			}
990
991			a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
992
993
994			if ( a == NULL ) {
995				Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN entry <%s> has no objectClass\n", new_dn.bv_val, 0, 0);
996				overlay_entry_release_ov( op, e, 0, on );
997				ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
998				return SLAP_CB_CONTINUE;
999			}
1000
1001
1002			/* If a groups DN is modified, just update age_dn/ndn of that group with the new DN. */
1003			for ( ; agd; agd = agd->agd_next ) {
1004
1005				if ( value_find_ex( slap_schema.si_ad_objectClass,
1006						SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1007						SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1008						a->a_nvals, &agd->agd_oc->soc_cname,
1009						op->o_tmpmemctx ) == 0 )
1010				{
1011					for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1012						int match = 1;
1013
1014						dnMatch( &match, 0, NULL, NULL, &age->age_ndn, &op->o_req_ndn );
1015						if ( match == 0 ) {
1016							Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN updating group's DN to <%s>\n", new_dn.bv_val, 0, 0);
1017							ber_dupbv( &age->age_dn, &new_dn );
1018							ber_dupbv( &age->age_ndn, &new_ndn );
1019
1020							op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx  );
1021							op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1022							overlay_entry_release_ov( op, e, 0, on );
1023							ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1024							return SLAP_CB_CONTINUE;
1025						}
1026					}
1027
1028				}
1029			}
1030
1031			/* For each group:
1032			   1. check if the orginal entry's DN is in the group.
1033			   2. chceck if the any of the group filter's base DN is a suffix of the new DN
1034
1035			   If 1 and 2 are both false, we do nothing.
1036			   If 1 and 2 is true, we remove the old DN from the group, and add the new DN.
1037			   If 1 is false, and 2 is true, we check the entry against the group's filters,
1038				and add it's DN to the group.
1039			   If 1 is true, and 2 is false, we delete the entry's DN from the group.
1040			*/
1041			for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1042				is_olddn = 0;
1043				is_newdn = 0;
1044				is_value_refresh = 0;
1045
1046
1047				ldap_pvt_thread_mutex_lock( &age->age_mutex );
1048
1049				if ( age->age_filter && age->age_filter->agf_anlist ) {
1050					ea = attrs_find( e->e_attrs, age->age_filter->agf_anlist[0].an_desc );
1051				}
1052				else {
1053					ea = NULL;
1054				}
1055
1056				if ( age->age_modrdn_olddnmodified ) {
1057					/* Resquest already marked this group to be updated */
1058					is_olddn = 1;
1059					is_value_refresh = 1;
1060					age->age_modrdn_olddnmodified = 0;
1061				} else {
1062
1063					if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
1064						LDAP_SUCCESS || group == NULL ) {
1065						Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get group entry <%s>\n", age->age_dn.bv_val, 0, 0);
1066
1067						op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1068						op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1069
1070						overlay_entry_release_ov( op, e, 0, on );
1071						ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1072						ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1073						return SLAP_CB_CONTINUE;
1074					}
1075
1076					a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
1077
1078					if ( a != NULL ) {
1079						if ( value_find_ex( age->age_def->agd_member_ad,
1080								SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1081								SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1082								a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 )
1083						{
1084							is_olddn = 1;
1085						}
1086
1087					}
1088
1089					overlay_entry_release_ov( op, group, 0, on );
1090
1091				}
1092
1093				for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1094					if ( dnIsSuffix( &new_ndn, &agf->agf_ndn ) ) {
1095						/* TODO: should retest filter as it could imply conditions on the dn */
1096						is_newdn = 1;
1097						break;
1098					}
1099				}
1100
1101
1102				if ( is_value_refresh ) {
1103					if ( is_olddn != is_newdn ) {
1104						/* group refresh */
1105						autogroup_delete_member_from_group( op, NULL, NULL, age) ;
1106
1107						for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1108							autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
1109						}
1110					}
1111					ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1112					continue;
1113				}
1114				if ( is_olddn == 1 && is_newdn == 0 ) {
1115					if ( ea )
1116						autogroup_delete_member_values_from_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
1117					else
1118						autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1119				} else
1120				if ( is_olddn == 0 && is_newdn == 1 ) {
1121					for ( agf = age->age_filter; agf; agf = agf->agf_next ) {
1122						if ( test_filter( op, e, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
1123							if ( ea )
1124								autogroup_add_member_values_to_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
1125							else
1126								autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
1127							break;
1128						}
1129					}
1130				} else
1131				if ( is_olddn == 1 && is_newdn == 1 && dn_equal != 0 ) {
1132					if ( ea ) {
1133						/* group refresh */
1134						autogroup_delete_member_from_group( op, NULL, NULL, age) ;
1135
1136						for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1137							autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
1138						}
1139					}
1140					else {
1141						autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1142						autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
1143					}
1144				}
1145
1146				ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1147			}
1148
1149			op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1150			op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1151
1152			overlay_entry_release_ov( op, e, 0, on );
1153
1154			ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1155		}
1156	}
1157
1158	if ( op->o_tag == LDAP_REQ_MODIFY ) {
1159		if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS  && !get_manageDSAit( op ) ) {
1160			Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODIFY <%s>\n", op->o_req_dn.bv_val, 0, 0);
1161
1162			ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1163
1164			if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1165				LDAP_SUCCESS || e == NULL ) {
1166				Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1167				ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1168				return SLAP_CB_CONTINUE;
1169			}
1170
1171			a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1172
1173
1174			if ( a == NULL ) {
1175				Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
1176				overlay_entry_release_ov( op, e, 0, on );
1177				ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1178				return SLAP_CB_CONTINUE;
1179			}
1180
1181			/* If we modify a group's memberURL, we have to delete all of it's members,
1182			   and add them anew, because we cannot tell from which memberURL a member was added. */
1183			for ( ; agd; agd = agd->agd_next ) {
1184
1185				if ( value_find_ex( slap_schema.si_ad_objectClass,
1186						SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1187						SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1188						a->a_nvals, &agd->agd_oc->soc_cname,
1189						op->o_tmpmemctx ) == 0 )
1190				{
1191					Modifications	*m;
1192					int		match = 1;
1193
1194					m = op->orm_modlist;
1195
1196					for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1197						ldap_pvt_thread_mutex_lock( &age->age_mutex );
1198
1199						dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
1200
1201						if ( match == 0 ) {
1202							for ( ; m ; m = m->sml_next ) {
1203								if ( m->sml_desc == age->age_def->agd_member_url_ad ) {
1204									autogroup_def_t	*group_agd = age->age_def;
1205									Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY changing memberURL for group <%s>\n",
1206										op->o_req_dn.bv_val, 0, 0);
1207
1208									overlay_entry_release_ov( op, e, 0, on );
1209
1210									autogroup_delete_member_from_group( op, NULL, NULL, age );
1211									autogroup_delete_group( agi, age );
1212
1213									autogroup_add_group( op, agi, group_agd, NULL, &op->o_req_ndn, 1, 1);
1214
1215									overlay_entry_release_ov( op, e, 0, on );
1216									ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1217									return SLAP_CB_CONTINUE;
1218								}
1219							}
1220
1221							ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1222							break;
1223						}
1224
1225						ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1226					}
1227
1228					overlay_entry_release_ov( op, e, 0, on );
1229					ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1230					return SLAP_CB_CONTINUE;
1231				}
1232			}
1233
1234			/* When modifing any of the attributes of an entry, we must
1235			   check if the entry is in any of our groups, and if
1236			   the modified entry maches any of the filters of that group.
1237
1238			   If the entry exists in a group, but the modified attributes do
1239				not match any of the group's filters, we delete the entry from that group.
1240			   If the entry doesn't exist in a group, but matches a filter,
1241				we add it to that group.
1242			*/
1243			for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1244				is_olddn = 0;
1245				is_newdn = 0;
1246
1247
1248				ldap_pvt_thread_mutex_lock( &age->age_mutex );
1249
1250				if ( age->age_filter && age->age_filter->agf_anlist ) {
1251					ea = attrs_find( e->e_attrs, age->age_filter->agf_anlist[0].an_desc );
1252				}
1253				else {
1254					ea = NULL;
1255				}
1256
1257				if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
1258					LDAP_SUCCESS || group == NULL ) {
1259					Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n",
1260						age->age_dn.bv_val, 0, 0);
1261
1262					overlay_entry_release_ov( op, e, 0, on );
1263					ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1264					ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1265					return SLAP_CB_CONTINUE;
1266				}
1267
1268				a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
1269
1270				if ( a != NULL ) {
1271					if ( value_find_ex( age->age_def->agd_member_ad,
1272							SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1273							SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1274							a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 )
1275					{
1276						is_olddn = 1;
1277					}
1278
1279				}
1280
1281				overlay_entry_release_ov( op, group, 0, on );
1282
1283				for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1284					if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1285						if ( test_filter( op, e, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
1286							is_newdn = 1;
1287							break;
1288						}
1289					}
1290				}
1291
1292				if ( is_olddn == 1 && is_newdn == 0 ) {
1293					if(ea)
1294						autogroup_delete_member_values_from_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
1295					else
1296						autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1297				} else
1298				if ( is_olddn == 0 && is_newdn == 1 ) {
1299					if(ea)
1300						autogroup_add_member_values_to_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
1301					else
1302						autogroup_add_member_to_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1303				}
1304
1305				ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1306			}
1307
1308			overlay_entry_release_ov( op, e, 0, on );
1309
1310			ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1311		}
1312	}
1313
1314	return SLAP_CB_CONTINUE;
1315}
1316
1317/*
1318** Detect if filter contains a memberOf check for dn
1319*/
1320static int
1321autogroup_memberOf_filter( Filter *f, BerValue *dn, AttributeDescription *memberof_ad )
1322{
1323	int result = 0;
1324	if ( f == NULL ) return 0;
1325
1326  	switch ( f->f_choice & SLAPD_FILTER_MASK ) {
1327		case LDAP_FILTER_AND:
1328		case LDAP_FILTER_OR:
1329		case LDAP_FILTER_NOT:
1330			for ( f = f->f_un.f_un_complex; f && !result; f = f->f_next ) {
1331				result = result || autogroup_memberOf_filter( f, dn, memberof_ad );
1332			}
1333			break;
1334		case LDAP_FILTER_EQUALITY:
1335			result = ( f->f_ava->aa_desc == memberof_ad &&
1336			           ber_bvcmp( &f->f_ava->aa_value, dn ) == 0 );
1337			break;
1338		default:
1339			break;
1340	}
1341
1342	return result;
1343}
1344
1345/*
1346** When modifing a group, we must deny any modifications to the member attribute,
1347** because the group would be inconsistent.
1348*/
1349static int
1350autogroup_modify_entry( Operation *op, SlapReply *rs)
1351{
1352	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
1353	autogroup_info_t		*agi = (autogroup_info_t *)on->on_bi.bi_private;
1354	autogroup_def_t		*agd = agi->agi_def;
1355	autogroup_entry_t	*age;
1356	Entry			*e;
1357	Attribute		*a;
1358
1359	if ( get_manageDSAit( op ) ) {
1360		return SLAP_CB_CONTINUE;
1361	}
1362
1363	Debug( LDAP_DEBUG_TRACE, "==> autogroup_modify_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
1364	ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1365
1366	if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1367		LDAP_SUCCESS || e == NULL ) {
1368		Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1369		ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1370		return SLAP_CB_CONTINUE;
1371	}
1372
1373	/* Must refresh groups if a matching member value is modified OR filter contains memberOf=DN */
1374	for ( age = agi->agi_entry; age ; age = age->age_next ) {
1375		autogroup_filter_t	*agf;
1376		for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1377			if ( agf->agf_anlist ) {
1378				Modifications	*m;
1379				for ( m = op->orm_modlist ; m ; m = m->sml_next ) {
1380					if ( m->sml_desc == agf->agf_anlist[0].an_desc ) {
1381						if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1382							int rc = test_filter( op, e, agf->agf_filter );
1383							if ( rc == LDAP_COMPARE_TRUE ) {
1384								age->age_mustrefresh = 1;
1385							}
1386						}
1387					}
1388				}
1389			}
1390
1391			if ( autogroup_memberOf_filter( agf->agf_filter, &op->o_req_ndn, agi->agi_memberof_ad ) ) {
1392				age->age_mustrefresh = 1;
1393			}
1394		}
1395	}
1396
1397	a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1398
1399	if ( a == NULL ) {
1400		Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
1401		ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1402		return SLAP_CB_CONTINUE;
1403	}
1404
1405
1406	for ( ; agd; agd = agd->agd_next ) {
1407
1408		if ( value_find_ex( slap_schema.si_ad_objectClass,
1409				SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1410				SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1411				a->a_nvals, &agd->agd_oc->soc_cname,
1412				op->o_tmpmemctx ) == 0 )
1413		{
1414			Modifications	*m;
1415			int		match = 1;
1416
1417			m = op->orm_modlist;
1418
1419			for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1420				dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
1421
1422				if ( match == 0 ) {
1423					for ( ; m ; m = m->sml_next ) {
1424						if ( m->sml_desc == age->age_def->agd_member_ad ) {
1425							overlay_entry_release_ov( op, e, 0, on );
1426							ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1427							Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry attempted to modify group's <%s> member attribute\n", op->o_req_dn.bv_val, 0, 0);
1428							send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION, "attempt to modify dynamic group member attribute");
1429							return LDAP_CONSTRAINT_VIOLATION;
1430						}
1431					}
1432					break;
1433				}
1434			}
1435
1436			overlay_entry_release_ov( op, e, 0, on );
1437			ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1438			return SLAP_CB_CONTINUE;
1439		}
1440	}
1441
1442	overlay_entry_release_ov( op, e, 0, on );
1443	ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1444	return SLAP_CB_CONTINUE;
1445}
1446
1447/*
1448** Detect if the olddn is part of a group and so if the group should be refreshed
1449*/
1450static int
1451autogroup_modrdn_entry( Operation *op, SlapReply *rs)
1452{
1453	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
1454	autogroup_info_t	*agi = (autogroup_info_t *)on->on_bi.bi_private;
1455	autogroup_entry_t	*age;
1456	Entry			*e;
1457
1458	if ( get_manageDSAit( op ) ) {
1459		return SLAP_CB_CONTINUE;
1460	}
1461
1462	Debug( LDAP_DEBUG_TRACE, "==> autogroup_modrdn_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
1463	ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1464
1465	if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1466		LDAP_SUCCESS || e == NULL ) {
1467		Debug( LDAP_DEBUG_TRACE, "autogroup_modrdn_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1468		ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1469		return SLAP_CB_CONTINUE;
1470	}
1471
1472	/* Must check if a dn is modified */
1473	for ( age = agi->agi_entry; age ; age = age->age_next ) {
1474		autogroup_filter_t	*agf;
1475		for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1476			if ( agf->agf_anlist ) {
1477				if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1478					int rc = test_filter( op, e, agf->agf_filter );
1479					if ( rc == LDAP_COMPARE_TRUE ) {
1480						age->age_modrdn_olddnmodified = 1;
1481					}
1482				}
1483			}
1484		}
1485	}
1486
1487	overlay_entry_release_ov( op, e, 0, on );
1488	ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1489	return SLAP_CB_CONTINUE;
1490}
1491
1492/*
1493** Builds a filter for searching for the
1494** group entries, according to the objectClass.
1495*/
1496static int
1497autogroup_build_def_filter( autogroup_def_t *agd, Operation *op )
1498{
1499	char	*ptr;
1500
1501	Debug( LDAP_DEBUG_TRACE, "==> autogroup_build_def_filter\n", 0, 0, 0);
1502
1503	op->ors_filterstr.bv_len = STRLENOF( "(=)" )
1504			+ slap_schema.si_ad_objectClass->ad_cname.bv_len
1505			+ agd->agd_oc->soc_cname.bv_len;
1506	ptr = op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
1507	*ptr++ = '(';
1508	ptr = lutil_strcopy( ptr, slap_schema.si_ad_objectClass->ad_cname.bv_val );
1509	*ptr++ = '=';
1510	ptr = lutil_strcopy( ptr, agd->agd_oc->soc_cname.bv_val );
1511	*ptr++ = ')';
1512	*ptr = '\0';
1513
1514	op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
1515
1516	assert( op->ors_filterstr.bv_len == ptr - op->ors_filterstr.bv_val );
1517
1518	return 0;
1519}
1520
1521enum {
1522	AG_ATTRSET = 1,
1523	AG_MEMBER_OF_AD,
1524	AG_LAST
1525};
1526
1527static ConfigDriver	ag_cfgen;
1528
1529static ConfigTable agcfg[] = {
1530	{ "autogroup-attrset", "group-oc> <URL-ad> <member-ad",
1531		3, 4, 0, ARG_MAGIC|AG_ATTRSET, ag_cfgen,
1532		"( OLcfgCtAt:2.1 NAME 'olcAGattrSet' "
1533			"DESC 'Automatic groups: <group objectClass>, <URL attributeDescription>, <member attributeDescription>' "
1534			"EQUALITY caseIgnoreMatch "
1535			"SYNTAX OMsDirectoryString "
1536			"X-ORDERED 'VALUES' )",
1537			NULL, NULL },
1538
1539	{ "autogroup-memberof-ad", "memberOf attribute",
1540		2, 2, 0, ARG_MAGIC|AG_MEMBER_OF_AD, ag_cfgen,
1541		"( OLcfgCtAt:2.2 NAME 'olcAGmemberOfAd' "
1542			"DESC 'memberOf attribute' "
1543			"SYNTAX OMsDirectoryString SINGLE-VALUE )",
1544			NULL, NULL },
1545
1546	{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
1547};
1548
1549static ConfigOCs agocs[] = {
1550	{ "( OLcfgCtOc:2.1 "
1551		"NAME 'olcAutomaticGroups' "
1552		"DESC 'Automatic groups configuration' "
1553		"SUP olcOverlayConfig "
1554		"MAY ( "
1555			"olcAGattrSet "
1556			"$ olcAGmemberOfAd "
1557		    ")"
1558	  ")",
1559		Cft_Overlay, agcfg, NULL, NULL },
1560	{ NULL, 0, NULL }
1561};
1562
1563
1564static int
1565ag_cfgen( ConfigArgs *c )
1566{
1567	slap_overinst		*on = (slap_overinst *)c->bi;
1568	autogroup_info_t		*agi = (autogroup_info_t *)on->on_bi.bi_private;
1569	autogroup_def_t		*agd;
1570	autogroup_entry_t	*age;
1571
1572	int rc = 0, i;
1573
1574	Debug( LDAP_DEBUG_TRACE, "==> autogroup_cfgen\n", 0, 0, 0);
1575
1576	if( agi == NULL ) {
1577		agi = (autogroup_info_t*)ch_calloc( 1, sizeof(autogroup_info_t) );
1578		ldap_pvt_thread_mutex_init( &agi->agi_mutex );
1579		agi->agi_def = NULL;
1580		agi->agi_entry = NULL;
1581		on->on_bi.bi_private = (void *)agi;
1582	}
1583
1584	agd = agi->agi_def;
1585	age = agi->agi_entry;
1586
1587	if ( c->op == SLAP_CONFIG_EMIT ) {
1588
1589		switch( c->type ){
1590		case AG_ATTRSET:
1591			for ( i = 0 ; agd ; i++, agd = agd->agd_next ) {
1592				struct berval	bv;
1593				char		*ptr = c->cr_msg;
1594
1595				assert(agd->agd_oc != NULL);
1596				assert(agd->agd_member_url_ad != NULL);
1597				assert(agd->agd_member_ad != NULL);
1598
1599				ptr += snprintf( c->cr_msg, sizeof( c->cr_msg ),
1600					SLAP_X_ORDERED_FMT "%s %s %s", i,
1601					agd->agd_oc->soc_cname.bv_val,
1602					agd->agd_member_url_ad->ad_cname.bv_val,
1603					agd->agd_member_ad->ad_cname.bv_val );
1604
1605				bv.bv_val = c->cr_msg;
1606				bv.bv_len = ptr - bv.bv_val;
1607				value_add_one ( &c->rvalue_vals, &bv );
1608
1609			}
1610			break;
1611
1612		case AG_MEMBER_OF_AD:
1613			if ( agi->agi_memberof_ad != NULL ){
1614				value_add_one( &c->rvalue_vals, &agi->agi_memberof_ad->ad_cname );
1615			}
1616			break;
1617
1618		default:
1619			assert( 0 );
1620			return 1;
1621      }
1622
1623		return rc;
1624
1625	}else if ( c->op == LDAP_MOD_DELETE ) {
1626		if ( c->valx < 0) {
1627			autogroup_def_t 		*agd_next;
1628			autogroup_entry_t	*age_next;
1629			autogroup_filter_t	*agf = age->age_filter,
1630						*agf_next;
1631
1632			for ( agd_next = agd; agd_next; agd = agd_next ) {
1633				agd_next = agd->agd_next;
1634
1635				ch_free( agd );
1636			}
1637
1638			for ( age_next = age ; age_next ; age = age_next ) {
1639				age_next = age->age_next;
1640
1641				ch_free( age->age_dn.bv_val );
1642				ch_free( age->age_ndn.bv_val );
1643
1644				for( agf_next = agf ; agf_next ; agf = agf_next ){
1645					agf_next = agf->agf_next;
1646
1647					filter_free( agf->agf_filter );
1648					ch_free( agf->agf_filterstr.bv_val );
1649					ch_free( agf->agf_dn.bv_val );
1650					ch_free( agf->agf_ndn.bv_val );
1651					anlist_free( agf->agf_anlist, 1, NULL );
1652					ch_free( agf );
1653				}
1654
1655				ldap_pvt_thread_mutex_init( &age->age_mutex );
1656				ch_free( age );
1657			}
1658
1659			ch_free( agi );
1660			on->on_bi.bi_private = NULL;
1661
1662		} else {
1663			autogroup_def_t		**agdp;
1664			autogroup_entry_t	*age_next, *age_prev;
1665			autogroup_filter_t	*agf,
1666						*agf_next;
1667
1668			for ( i = 0, agdp = &agi->agi_def;
1669				i < c->valx; i++ )
1670			{
1671				if ( *agdp == NULL) {
1672					return 1;
1673				}
1674				agdp = &(*agdp)->agd_next;
1675			}
1676
1677			agd = *agdp;
1678			*agdp = agd->agd_next;
1679
1680			for ( age_next = age , age_prev = NULL ; age_next ; age_prev = age, age = age_next ) {
1681				age_next = age->age_next;
1682
1683				if( age->age_def == agd ) {
1684					agf = age->age_filter;
1685
1686					ch_free( age->age_dn.bv_val );
1687					ch_free( age->age_ndn.bv_val );
1688
1689					for ( agf_next = agf; agf_next ; agf = agf_next ) {
1690						agf_next = agf->agf_next;
1691						filter_free( agf->agf_filter );
1692						ch_free( agf->agf_filterstr.bv_val );
1693						ch_free( agf->agf_dn.bv_val );
1694						ch_free( agf->agf_ndn.bv_val );
1695						anlist_free( agf->agf_anlist, 1, NULL );
1696						ch_free( agf );
1697					}
1698
1699					ldap_pvt_thread_mutex_destroy( &age->age_mutex );
1700					ch_free( age );
1701
1702					age = age_prev;
1703
1704					if( age_prev != NULL ) {
1705						age_prev->age_next = age_next;
1706					}
1707				}
1708			}
1709
1710			ch_free( agd );
1711			agd = agi->agi_def;
1712
1713		}
1714
1715		return rc;
1716	}
1717
1718	switch(c->type){
1719	case AG_ATTRSET: {
1720		autogroup_def_t		**agdp,
1721					*agd_next = NULL;
1722		ObjectClass		*oc = NULL;
1723		AttributeDescription	*member_url_ad = NULL,
1724					*member_ad = NULL;
1725		const char		*text;
1726
1727
1728		oc = oc_find( c->argv[ 1 ] );
1729		if( oc == NULL ){
1730			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1731				"\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1732				"unable to find ObjectClass \"%s\"",
1733				c->argv[ 1 ] );
1734			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1735				c->log, c->cr_msg, 0 );
1736			return 1;
1737		}
1738
1739
1740		rc = slap_str2ad( c->argv[ 2 ], &member_url_ad, &text );
1741		if( rc != LDAP_SUCCESS ) {
1742			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1743				"\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1744				"unable to find AttributeDescription \"%s\"",
1745				c->argv[ 2 ] );
1746			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1747				c->log, c->cr_msg, 0 );
1748			return 1;
1749		}
1750
1751		if( !is_at_subtype( member_url_ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
1752			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1753				"\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1754				"AttributeDescription \"%s\" ",
1755				"must be of a subtype \"labeledURI\"",
1756				c->argv[ 2 ] );
1757			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1758				c->log, c->cr_msg, 0 );
1759			return 1;
1760		}
1761
1762		rc = slap_str2ad( c->argv[3], &member_ad, &text );
1763		if( rc != LDAP_SUCCESS ) {
1764			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1765				"\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1766				"unable to find AttributeDescription \"%s\"",
1767				c->argv[ 3 ] );
1768			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1769				c->log, c->cr_msg, 0 );
1770			return 1;
1771		}
1772
1773		for ( agdp = &agi->agi_def ; *agdp ; agdp = &(*agdp)->agd_next ) {
1774			/* The same URL attribute / member attribute pair
1775			* cannot be repeated */
1776
1777			if ( (*agdp)->agd_member_url_ad == member_url_ad && (*agdp)->agd_member_ad == member_ad ) {
1778				snprintf( c->cr_msg, sizeof( c->cr_msg ),
1779					"\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1780					"URL attributeDescription \"%s\" already mapped",
1781					member_ad->ad_cname.bv_val );
1782				Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1783					c->log, c->cr_msg, 0 );
1784/*				return 1; //warning*/
1785			}
1786		}
1787
1788		if ( c->valx > 0 ) {
1789			int	i;
1790
1791			for ( i = 0, agdp = &agi->agi_def ;
1792				i < c->valx; i++ )
1793			{
1794				if ( *agdp == NULL ) {
1795					snprintf( c->cr_msg, sizeof( c->cr_msg ),
1796						"\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1797						"invalid index {%d}",
1798						c->valx );
1799					Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1800						c->log, c->cr_msg, 0 );
1801
1802					return 1;
1803				}
1804				agdp = &(*agdp)->agd_next;
1805			}
1806			agd_next = *agdp;
1807
1808		} else {
1809			for ( agdp = &agi->agi_def; *agdp;
1810				agdp = &(*agdp)->agd_next )
1811				/* goto last */;
1812		}
1813
1814		*agdp = (autogroup_def_t *)ch_calloc( 1, sizeof(autogroup_info_t));
1815
1816		(*agdp)->agd_oc = oc;
1817		(*agdp)->agd_member_url_ad = member_url_ad;
1818		(*agdp)->agd_member_ad = member_ad;
1819		(*agdp)->agd_next = agd_next;
1820
1821		} break;
1822
1823	case AG_MEMBER_OF_AD: {
1824		AttributeDescription *memberof_ad = NULL;
1825		const char     *text;
1826
1827		rc = slap_str2ad( c->argv[ 1 ], &memberof_ad, &text );
1828		if( rc != LDAP_SUCCESS ) {
1829			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1830				"\"autogroup-memberof-ad <memberof-ad>\": "
1831				"unable to find AttributeDescription \"%s\"",
1832				c->argv[ 1 ] );
1833			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1834				c->log, c->cr_msg, 0 );
1835			return 1;
1836		}
1837
1838		if ( !is_at_syntax( memberof_ad->ad_type, SLAPD_DN_SYNTAX )    /* e.g. "member" */
1839		     && !is_at_syntax( memberof_ad->ad_type, SLAPD_NAMEUID_SYNTAX ) )  /* e.g. "uniqueMember" */
1840		{
1841			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1842				"memberof attribute=\"%s\" must either "
1843				"have DN (%s) or nameUID (%s) syntax",
1844				c->argv[ 1 ], SLAPD_DN_SYNTAX, SLAPD_NAMEUID_SYNTAX );
1845			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1846				c->log, c->cr_msg, 0 );
1847			return 1;
1848		}
1849
1850		agi->agi_memberof_ad = memberof_ad;
1851
1852		} break;
1853
1854	default:
1855		rc = 1;
1856		break;
1857	}
1858
1859	return rc;
1860}
1861
1862extern int slapMode;
1863
1864/*
1865** Do a search for all the groups in the
1866** database, and add them to out internal list.
1867*/
1868static int
1869autogroup_db_open(
1870	BackendDB	*be,
1871	ConfigReply	*cr )
1872{
1873	slap_overinst			*on = (slap_overinst *) be->bd_info;
1874	autogroup_info_t		*agi = on->on_bi.bi_private;
1875	autogroup_def_t		*agd;
1876	autogroup_sc_t		ags;
1877	Operation		*op;
1878	slap_callback		cb = { 0 };
1879
1880	void				*thrctx = ldap_pvt_thread_pool_context();
1881	Connection			conn = { 0 };
1882	OperationBuffer 	opbuf;
1883
1884	Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_open\n", 0, 0, 0);
1885
1886	if ( agi == NULL || !( slapMode & SLAP_SERVER_MODE )) {
1887		return 0;
1888	}
1889
1890	connection_fake_init( &conn, &opbuf, thrctx );
1891	op = &opbuf.ob_op;
1892
1893	op->ors_attrsonly = 0;
1894	op->o_tag = LDAP_REQ_SEARCH;
1895	op->o_dn = be->be_rootdn;
1896	op->o_ndn = be->be_rootndn;
1897
1898	op->o_req_dn = be->be_suffix[0];
1899	op->o_req_ndn = be->be_nsuffix[0];
1900
1901	op->ors_scope = LDAP_SCOPE_SUBTREE;
1902	op->ors_deref = LDAP_DEREF_NEVER;
1903	op->ors_limit = NULL;
1904	op->ors_tlimit = SLAP_NO_LIMIT;
1905	op->ors_slimit = SLAP_NO_LIMIT;
1906	op->ors_attrs =  slap_anlist_no_attrs;
1907
1908	op->o_bd = be;
1909	op->o_bd->bd_info = (BackendInfo *)on->on_info;
1910
1911	ags.ags_info = agi;
1912	cb.sc_private = &ags;
1913	cb.sc_response = autogroup_group_add_cb;
1914	cb.sc_cleanup = NULL;
1915	cb.sc_next = NULL;
1916
1917	op->o_callback = &cb;
1918
1919	for (agd = agi->agi_def ; agd ; agd = agd->agd_next) {
1920		SlapReply	rs = { REP_RESULT };
1921
1922		autogroup_build_def_filter(agd, op);
1923
1924		ags.ags_def = agd;
1925
1926		op->o_bd->be_search( op, &rs );
1927
1928		filter_free_x( op, op->ors_filter, 1 );
1929		op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
1930	}
1931
1932	if( ! agi->agi_memberof_ad ){
1933		int			rc;
1934		const char		*text = NULL;
1935
1936		rc = slap_str2ad( SLAPD_MEMBEROF_ATTR, &agi->agi_memberof_ad, &text );
1937		if ( rc != LDAP_SUCCESS ) {
1938			Debug( LDAP_DEBUG_ANY, "autogroup_db_open: "
1939			"unable to find attribute=\"%s\": %s (%d)\n",
1940			SLAPD_MEMBEROF_ATTR, text, rc );
1941			return rc;
1942		}
1943	}
1944
1945	return 0;
1946}
1947
1948static int
1949autogroup_db_close(
1950	BackendDB	*be,
1951	ConfigReply	*cr )
1952{
1953	slap_overinst			*on = (slap_overinst *) be->bd_info;
1954
1955	Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_close\n", 0, 0, 0);
1956
1957	if ( on->on_bi.bi_private ) {
1958		autogroup_info_t		*agi = on->on_bi.bi_private;
1959		autogroup_entry_t	*age = agi->agi_entry,
1960					*age_next;
1961		autogroup_filter_t	*agf, *agf_next;
1962
1963		for ( age_next = age; age_next; age = age_next ) {
1964			age_next = age->age_next;
1965
1966			ch_free( age->age_dn.bv_val );
1967			ch_free( age->age_ndn.bv_val );
1968
1969			agf = age->age_filter;
1970
1971			for ( agf_next = agf; agf_next; agf = agf_next ) {
1972				agf_next = agf->agf_next;
1973
1974				filter_free( agf->agf_filter );
1975				ch_free( agf->agf_filterstr.bv_val );
1976				ch_free( agf->agf_dn.bv_val );
1977				ch_free( agf->agf_ndn.bv_val );
1978				anlist_free( agf->agf_anlist, 1, NULL );
1979				ch_free( agf );
1980			}
1981
1982			ldap_pvt_thread_mutex_destroy( &age->age_mutex );
1983			ch_free( age );
1984		}
1985	}
1986
1987	return 0;
1988}
1989
1990static int
1991autogroup_db_destroy(
1992	BackendDB	*be,
1993	ConfigReply	*cr )
1994{
1995	slap_overinst			*on = (slap_overinst *) be->bd_info;
1996
1997	Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_destroy\n", 0, 0, 0);
1998
1999	if ( on->on_bi.bi_private ) {
2000		autogroup_info_t		*agi = on->on_bi.bi_private;
2001		autogroup_def_t		*agd = agi->agi_def,
2002					*agd_next;
2003
2004		for ( agd_next = agd; agd_next; agd = agd_next ) {
2005			agd_next = agd->agd_next;
2006
2007			ch_free( agd );
2008		}
2009
2010		ldap_pvt_thread_mutex_destroy( &agi->agi_mutex );
2011		ch_free( agi );
2012	}
2013
2014	return 0;
2015}
2016
2017static slap_overinst	autogroup = { { NULL } };
2018
2019static
2020int
2021autogroup_initialize(void)
2022{
2023	int		rc = 0;
2024	autogroup.on_bi.bi_type = "autogroup";
2025
2026	autogroup.on_bi.bi_db_open = autogroup_db_open;
2027	autogroup.on_bi.bi_db_close = autogroup_db_close;
2028	autogroup.on_bi.bi_db_destroy = autogroup_db_destroy;
2029
2030	autogroup.on_bi.bi_op_add = autogroup_add_entry;
2031	autogroup.on_bi.bi_op_delete = autogroup_delete_entry;
2032	autogroup.on_bi.bi_op_modify = autogroup_modify_entry;
2033	autogroup.on_bi.bi_op_modrdn = autogroup_modrdn_entry;
2034
2035	autogroup.on_response = autogroup_response;
2036
2037	autogroup.on_bi.bi_cf_ocs = agocs;
2038
2039	rc = config_register_schema( agcfg, agocs );
2040	if ( rc ) {
2041		return rc;
2042	}
2043
2044	return overlay_register( &autogroup );
2045}
2046
2047int
2048init_module( int argc, char *argv[] )
2049{
2050	return autogroup_initialize();
2051}
2052