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