1 /*	$NetBSD: autogroup.c,v 1.1.1.3 2010/12/12 15:18:55 adam Exp $	*/
2 
3 /* autogroup.c - automatic group overlay */
4 /* OpenLDAP: pkg/ldap/contrib/slapd-modules/autogroup/autogroup.c,v 1.2.2.6 2010/04/13 20:22:26 kurt Exp */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6  *
7  * Copyright 2007-2010 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  */
25 
26 #include "portable.h"
27 
28 #include <stdio.h>
29 
30 #include <ac/string.h>
31 
32 #include "slap.h"
33 #include "config.h"
34 #include "lutil.h"
35 
36 /* Filter represents the memberURL of a group. */
37 typedef struct autogroup_filter_t {
38 	struct berval			agf_dn;	/* The base DN in memberURL */
39 	struct berval			agf_ndn;
40 	struct berval			agf_filterstr;
41 	Filter				*agf_filter;
42 	int				agf_scope;
43 	struct autogroup_filter_t	*agf_next;
44 } autogroup_filter_t;
45 
46 /* Description of group attributes. */
47 typedef struct autogroup_def_t {
48 	ObjectClass		*agd_oc;
49 	AttributeDescription	*agd_member_url_ad;
50 	AttributeDescription	*agd_member_ad;
51 	struct autogroup_def_t	*agd_next;
52 } autogroup_def_t;
53 
54 /* Represents the group entry. */
55 typedef struct autogroup_entry_t {
56 	BerValue		age_dn;
57 	BerValue		age_ndn;
58 	autogroup_filter_t	*age_filter; /* List of filters made from memberURLs */
59 	autogroup_def_t		*age_def; /* Attribute definition */
60 	ldap_pvt_thread_mutex_t age_mutex;
61 	struct autogroup_entry_t	*age_next;
62 } autogroup_entry_t;
63 
64 /* Holds pointers to attribute definitions and groups. */
65 typedef struct autogroup_info_t {
66 	autogroup_def_t		*agi_def;	/* Group attributes definitions. */
67 	autogroup_entry_t	*agi_entry;	/* Group entries.  */
68 	ldap_pvt_thread_mutex_t agi_mutex;
69 } autogroup_info_t;
70 
71 /* Search callback for adding groups initially. */
72 typedef struct autogroup_sc_t {
73 	autogroup_info_t		*ags_info;	/* Group definitions and entries.  */
74 	autogroup_def_t		*ags_def;	/* Attributes definition of the group being added. */
75 } autogroup_sc_t;
76 
77 /* Used for adding members, found when searching, to a group. */
78 typedef struct autogroup_ga_t {
79 	autogroup_entry_t	*agg_group;	/* The group to which the members will be added. */
80 	Entry			*agg_entry;	/* Used in autogroup_member_search_cb to modify
81 						this entry with the search results. */
82 
83 	Modifications		*agg_mod;	/* Used in autogroup_member_search_modify_cb to hold the
84 						search results which will be added to the group. */
85 
86 	Modifications		*agg_mod_last; /* Used in autogroup_member_search_modify_cb so we don't
87 						have to search for the last mod added. */
88 } autogroup_ga_t;
89 
90 
91 /*
92 **	dn, ndn	- the DN of the member to add
93 **	age	- the group to which the member DN will be added
94 */
95 static int
96 autogroup_add_member_to_group( Operation *op, BerValue *dn, BerValue *ndn, autogroup_entry_t *age )
97 {
98 	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
99 	Modifications	modlist;
100 	SlapReply	sreply = {REP_RESULT};
101 	BerValue	vals[ 2 ], nvals[ 2 ];
102 	slap_callback	cb = { NULL, slap_null_cb, NULL, NULL };
103 	Operation	o = *op;
104 
105 	Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_member_to_group adding <%s> to <%s>\n",
106 		dn->bv_val, age->age_dn.bv_val, 0);
107 
108 	assert( dn != NULL );
109 	assert( ndn != NULL );
110 
111 	vals[ 0 ] = *dn;
112 	BER_BVZERO( &vals[ 1 ] );
113 	nvals[ 0 ] = *ndn;
114 	BER_BVZERO( &nvals[ 1 ] );
115 
116 	modlist.sml_op = LDAP_MOD_ADD;
117 	modlist.sml_desc = age->age_def->agd_member_ad;
118 	modlist.sml_type = age->age_def->agd_member_ad->ad_cname;
119 	modlist.sml_values = vals;
120 	modlist.sml_nvalues = nvals;
121 	modlist.sml_numvals = 1;
122 	modlist.sml_flags = SLAP_MOD_INTERNAL;
123 	modlist.sml_next = NULL;
124 
125 	o.o_tag = LDAP_REQ_MODIFY;
126 	o.o_callback = &cb;
127 	o.orm_modlist = &modlist;
128 	o.o_req_dn = age->age_dn;
129 	o.o_req_ndn = age->age_ndn;
130 	o.o_permissive_modify = 1;
131 	o.o_managedsait = SLAP_CONTROL_CRITICAL;
132 	o.o_relax = SLAP_CONTROL_CRITICAL;
133 
134 	o.o_bd->bd_info = (BackendInfo *)on->on_info;
135 	(void)op->o_bd->be_modify( &o, &sreply );
136 	o.o_bd->bd_info = (BackendInfo *)on;
137 
138 	return sreply.sr_err;
139 }
140 
141 /*
142 ** dn,ndn	- the DN to be deleted
143 ** age		- the group from which the DN will be deleted
144 ** If we pass a NULL dn and ndn, all members are deleted from the group.
145 */
146 static int
147 autogroup_delete_member_from_group( Operation *op, BerValue *dn, BerValue *ndn, autogroup_entry_t *age )
148 {
149 	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
150 	Modifications	modlist;
151 	SlapReply	sreply = {REP_RESULT};
152 	BerValue	vals[ 2 ], nvals[ 2 ];
153 	slap_callback	cb = { NULL, slap_null_cb, NULL, NULL };
154 	Operation	o = *op;
155 
156 	if ( dn == NULL || ndn == NULL ) {
157 		Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_from_group removing all members from <%s>\n",
158 			age->age_dn.bv_val, 0 ,0);
159 
160 		modlist.sml_values = NULL;
161 		modlist.sml_nvalues = NULL;
162 		modlist.sml_numvals = 0;
163 	} else {
164 		Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_from_group removing <%s> from <%s>\n",
165 			dn->bv_val, age->age_dn.bv_val, 0);
166 
167 		vals[ 0 ] = *dn;
168 		BER_BVZERO( &vals[ 1 ] );
169 		nvals[ 0 ] = *ndn;
170 		BER_BVZERO( &nvals[ 1 ] );
171 
172 		modlist.sml_values = vals;
173 		modlist.sml_nvalues = nvals;
174 		modlist.sml_numvals = 1;
175 	}
176 
177 
178 	modlist.sml_op = LDAP_MOD_DELETE;
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_flags = SLAP_MOD_INTERNAL;
182 	modlist.sml_next = NULL;
183 
184 	o.o_callback = &cb;
185 	o.o_tag = LDAP_REQ_MODIFY;
186 	o.orm_modlist = &modlist;
187 	o.o_req_dn = age->age_dn;
188 	o.o_req_ndn = age->age_ndn;
189 	o.o_relax = SLAP_CONTROL_CRITICAL;
190 	o.o_managedsait = SLAP_CONTROL_CRITICAL;
191 	o.o_permissive_modify = 1;
192 
193 	o.o_bd->bd_info = (BackendInfo *)on->on_info;
194 	(void)op->o_bd->be_modify( &o, &sreply );
195 	o.o_bd->bd_info = (BackendInfo *)on;
196 
197 	return sreply.sr_err;
198 }
199 
200 /*
201 ** Callback used to add entries to a group,
202 ** which are going to be written in the database
203 ** (used in bi_op_add)
204 ** The group is passed in autogroup_ga_t->agg_group
205 */
206 static int
207 autogroup_member_search_cb( Operation *op, SlapReply *rs )
208 {
209 	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
210 
211 	assert( op->o_tag == LDAP_REQ_SEARCH );
212 
213 	if ( rs->sr_type == REP_SEARCH ) {
214 		autogroup_ga_t		*agg = (autogroup_ga_t *)op->o_callback->sc_private;
215 		autogroup_entry_t	*age = agg->agg_group;
216 		Modification		mod;
217 		const char		*text = NULL;
218 		char			textbuf[1024];
219 		struct berval		vals[ 2 ], nvals[ 2 ];
220 
221 		Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_cb <%s>\n",
222 			rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
223 
224 		vals[ 0 ] = rs->sr_entry->e_name;
225 		BER_BVZERO( &vals[ 1 ] );
226 		nvals[ 0 ] = rs->sr_entry->e_nname;
227 		BER_BVZERO( &nvals[ 1 ] );
228 
229 		mod.sm_op = LDAP_MOD_ADD;
230 		mod.sm_desc = age->age_def->agd_member_ad;
231 		mod.sm_type = age->age_def->agd_member_ad->ad_cname;
232 		mod.sm_values = vals;
233 		mod.sm_nvalues = nvals;
234 		mod.sm_numvals = 1;
235 
236 		modify_add_values( agg->agg_entry, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
237 	}
238 
239 	return 0;
240 }
241 
242 /*
243 ** Callback used to add entries to a group, which is already in the database.
244 ** (used in on_response)
245 ** The group is passed in autogroup_ga_t->agg_group
246 ** NOTE: Very slow.
247 */
248 static int
249 autogroup_member_search_modify_cb( Operation *op, SlapReply *rs )
250 {
251 	assert( op->o_tag == LDAP_REQ_SEARCH );
252 
253 	if ( rs->sr_type == REP_SEARCH ) {
254 		autogroup_ga_t		*agg = (autogroup_ga_t *)op->o_callback->sc_private;
255 		autogroup_entry_t	*age = agg->agg_group;
256 		Modifications		*modlist;
257 		struct berval		vals[ 2 ], nvals[ 2 ];
258 
259 		Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_modify_cb <%s>\n",
260 			rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
261 
262 		vals[ 0 ] = rs->sr_entry->e_name;
263 		BER_BVZERO( &vals[ 1 ] );
264 		nvals[ 0 ] = rs->sr_entry->e_nname;
265 		BER_BVZERO( &nvals[ 1 ] );
266 
267 		modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
268 
269 		modlist->sml_op = LDAP_MOD_ADD;
270 		modlist->sml_desc = age->age_def->agd_member_ad;
271 		modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
272 
273 		ber_bvarray_dup_x( &modlist->sml_values, vals, NULL );
274 		ber_bvarray_dup_x( &modlist->sml_nvalues, nvals, NULL );
275 		modlist->sml_numvals = 1;
276 
277 		modlist->sml_flags = SLAP_MOD_INTERNAL;
278 		modlist->sml_next = NULL;
279 
280 		if ( agg->agg_mod == NULL ) {
281 			agg->agg_mod = modlist;
282 			agg->agg_mod_last = modlist;
283 		} else {
284 			agg->agg_mod_last->sml_next = modlist;
285 			agg->agg_mod_last = modlist;
286 		}
287 
288 	}
289 
290 	return 0;
291 }
292 
293 
294 /*
295 ** Adds all entries matching the passed filter to the specified group.
296 ** If modify == 1, then we modify the group's entry in the database using be_modify.
297 ** If modify == 0, then, we must supply a rw entry for the group,
298 **	because we only modify the entry, without calling be_modify.
299 ** e	- the group entry, to which the members will be added
300 ** age	- the group
301 ** agf	- the filter
302 */
303 static int
304 autogroup_add_members_from_filter( Operation *op, Entry *e, autogroup_entry_t *age, autogroup_filter_t *agf, int modify)
305 {
306 	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
307 	Operation		o = *op;
308 	SlapReply		rs = { REP_SEARCH };
309 	slap_callback		cb = { 0 };
310 	slap_callback		null_cb = { NULL, slap_null_cb, NULL, NULL };
311 	autogroup_ga_t		agg;
312 
313 	Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_members_from_filter <%s>\n",
314 		age->age_dn.bv_val, 0, 0);
315 
316 	o.ors_attrsonly = 0;
317 	o.o_tag = LDAP_REQ_SEARCH;
318 
319 	o.o_req_dn = agf->agf_dn;
320 	o.o_req_ndn = agf->agf_ndn;
321 
322 	o.ors_filterstr = agf->agf_filterstr;
323 	o.ors_filter = agf->agf_filter;
324 
325 	o.ors_scope = agf->agf_scope;
326 	o.ors_deref = LDAP_DEREF_NEVER;
327 	o.ors_limit = NULL;
328 	o.ors_tlimit = SLAP_NO_LIMIT;
329 	o.ors_slimit = SLAP_NO_LIMIT;
330 	o.ors_attrs =  slap_anlist_no_attrs;
331 
332 	agg.agg_group = age;
333 	agg.agg_mod = NULL;
334 	agg.agg_mod_last = NULL;
335 	agg.agg_entry = e;
336 	cb.sc_private = &agg;
337 
338 	if ( modify == 1 ) {
339 		cb.sc_response = autogroup_member_search_modify_cb;
340 	} else {
341 		cb.sc_response = autogroup_member_search_cb;
342 	}
343 
344 	cb.sc_cleanup = NULL;
345 	cb.sc_next = NULL;
346 
347 	o.o_callback = &cb;
348 
349 	o.o_bd->bd_info = (BackendInfo *)on->on_info;
350 	op->o_bd->be_search( &o, &rs );
351 	o.o_bd->bd_info = (BackendInfo *)on;
352 
353 	if ( modify == 1 ) {
354 		o = *op;
355 		o.o_callback = &null_cb;
356 		o.o_tag = LDAP_REQ_MODIFY;
357 		o.orm_modlist = agg.agg_mod;
358 		o.o_req_dn = age->age_dn;
359 		o.o_req_ndn = age->age_ndn;
360 		o.o_relax = SLAP_CONTROL_CRITICAL;
361 		o.o_managedsait = SLAP_CONTROL_NONCRITICAL;
362 		o.o_permissive_modify = 1;
363 
364 		o.o_bd->bd_info = (BackendInfo *)on->on_info;
365 		(void)op->o_bd->be_modify( &o, &rs );
366 		o.o_bd->bd_info = (BackendInfo *)on;
367 
368 		slap_mods_free(agg.agg_mod, 1);
369 	}
370 
371 	return 0;
372 }
373 
374 /*
375 ** Adds a group to the internal list from the passed entry.
376 ** scan specifies whether to add all maching members to the group.
377 ** modify specifies whether to modify the given group entry (when modify == 0),
378 **	or to modify the group entry in the database (when modify == 1 and e = NULL and ndn != NULL).
379 ** agi	- pointer to the groups and the attribute definitions
380 ** agd - the attribute definition of the added group
381 ** e	- the entry representing the group, can be NULL if the ndn is specified, and modify == 1
382 ** ndn	- the DN of the group, can be NULL if we give a non-NULL e
383 */
384 static int
385 autogroup_add_group( Operation *op, autogroup_info_t *agi, autogroup_def_t *agd, Entry *e, BerValue *ndn, int scan, int modify)
386 {
387 	autogroup_entry_t	**agep = &agi->agi_entry;
388 	autogroup_filter_t	*agf, *agf_prev = NULL;
389 	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
390 	LDAPURLDesc		*lud = NULL;
391 	Attribute		*a;
392 	BerValue		*bv, dn;
393 	int			rc = 0, match = 1, null_entry = 0;
394 
395 	if ( e == NULL ) {
396 		if ( overlay_entry_get_ov( op, ndn, NULL, NULL, 0, &e, on ) !=
397 			LDAP_SUCCESS || e == NULL ) {
398 			Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot get entry for <%s>\n", ndn->bv_val, 0, 0);
399 			return 1;
400 		}
401 
402 		null_entry = 1;
403 	}
404 
405 	Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_group <%s>\n",
406 		e->e_name.bv_val, 0, 0);
407 
408 	if ( agi->agi_entry != NULL ) {
409 		for ( ; *agep ; agep = &(*agep)->age_next ) {
410 			dnMatch( &match, 0, NULL, NULL, &e->e_nname, &(*agep)->age_ndn );
411 			if ( match == 0 ) {
412 				Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: group already exists: <%s>\n", e->e_name.bv_val,0,0);
413 				return 1;
414 			}
415 			/* goto last */;
416 		}
417 	}
418 
419 
420 	*agep = (autogroup_entry_t *)ch_calloc( 1, sizeof( autogroup_entry_t ) );
421 	ldap_pvt_thread_mutex_init( &(*agep)->age_mutex );
422 	(*agep)->age_def = agd;
423 	(*agep)->age_filter = NULL;
424 
425 	ber_dupbv( &(*agep)->age_dn, &e->e_name );
426 	ber_dupbv( &(*agep)->age_ndn, &e->e_nname );
427 
428 	a = attrs_find( e->e_attrs, agd->agd_member_url_ad );
429 
430 	if ( null_entry == 1 ) {
431 		a = attrs_dup( a );
432 		overlay_entry_release_ov( op, e, 0, on );
433 	}
434 
435 	if( a == NULL ) {
436 		Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: group has no memberURL\n", 0,0,0);
437 	} else {
438 		for ( bv = a->a_nvals; !BER_BVISNULL( bv ); bv++ ) {
439 
440 			agf = (autogroup_filter_t*)ch_calloc( 1, sizeof( autogroup_filter_t ) );
441 
442 			if ( ldap_url_parse( bv->bv_val, &lud ) != LDAP_URL_SUCCESS ) {
443 				Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot parse url <%s>\n", bv->bv_val,0,0);
444 				/* FIXME: error? */
445 				ch_free( agf );
446 				continue;
447 			}
448 
449 			agf->agf_scope = lud->lud_scope;
450 
451 			if ( lud->lud_dn == NULL ) {
452 				BER_BVSTR( &dn, "" );
453 			} else {
454 				ber_str2bv( lud->lud_dn, 0, 0, &dn );
455 			}
456 
457 			rc = dnPrettyNormal( NULL, &dn, &agf->agf_dn, &agf->agf_ndn, NULL );
458 			if ( rc != LDAP_SUCCESS ) {
459 				Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot normalize DN <%s>\n", dn.bv_val,0,0);
460 				/* FIXME: error? */
461 				goto cleanup;
462 			}
463 
464 			if ( lud->lud_filter != NULL ) {
465 				ber_str2bv( lud->lud_filter, 0, 1, &agf->agf_filterstr);
466 				agf->agf_filter = str2filter( lud->lud_filter );
467 			}
468 
469 			agf->agf_next = NULL;
470 
471 
472 			if( (*agep)->age_filter == NULL ) {
473 				(*agep)->age_filter = agf;
474 			}
475 
476 			if( agf_prev != NULL ) {
477 				agf_prev->agf_next = agf;
478 			}
479 
480 			agf_prev = agf;
481 
482 			if ( scan == 1 ){
483 				autogroup_add_members_from_filter( op, e, (*agep), agf, modify );
484 			}
485 
486 			Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: added memberURL DN <%s> with filter <%s>\n",
487 				agf->agf_ndn.bv_val, agf->agf_filterstr.bv_val, 0);
488 
489 			ldap_free_urldesc( lud );
490 
491 			continue;
492 
493 
494 cleanup:;
495 
496 			ldap_free_urldesc( lud );
497 			ch_free( agf );
498 		}
499 	}
500 
501 	if ( null_entry == 1 ) {
502 		attrs_free( a );
503 	}
504 	return rc;
505 }
506 
507 /*
508 ** Used when opening the database to add all existing
509 ** groups from the database to our internal list.
510 */
511 static int
512 autogroup_group_add_cb( Operation *op, SlapReply *rs )
513 {
514 	assert( op->o_tag == LDAP_REQ_SEARCH );
515 
516 	if ( rs->sr_type == REP_SEARCH ) {
517 		autogroup_sc_t		*ags = (autogroup_sc_t *)op->o_callback->sc_private;
518 
519 		Debug(LDAP_DEBUG_TRACE, "==> autogroup_group_add_cb <%s>\n",
520 			rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
521 
522 		autogroup_add_group( op, ags->ags_info, ags->ags_def, rs->sr_entry, NULL, 0, 0);
523 	}
524 
525 	return 0;
526 }
527 
528 
529 /*
530 ** When adding a group, we first strip any existing members,
531 ** and add all which match the filters ourselfs.
532 */
533 static int
534 autogroup_add_entry( Operation *op, SlapReply *rs)
535 {
536 		slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
537 	autogroup_info_t		*agi = (autogroup_info_t *)on->on_bi.bi_private;
538 	autogroup_def_t		*agd = agi->agi_def;
539 	autogroup_entry_t	*age = agi->agi_entry;
540 	autogroup_filter_t	*agf;
541 	int			rc = 0;
542 
543 	Debug( LDAP_DEBUG_TRACE, "==> autogroup_add_entry <%s>\n",
544 		op->ora_e->e_name.bv_val, 0, 0);
545 
546 	ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
547 
548 	/* Check if it's a group. */
549 	for ( ; agd ; agd = agd->agd_next ) {
550 		if ( is_entry_objectclass_or_sub( op->ora_e, agd->agd_oc ) ) {
551 			Modification		mod;
552 			const char		*text = NULL;
553 			char			textbuf[1024];
554 
555 			mod.sm_op = LDAP_MOD_DELETE;
556 			mod.sm_desc = agd->agd_member_ad;
557 			mod.sm_type = agd->agd_member_ad->ad_cname;
558 			mod.sm_values = NULL;
559 			mod.sm_nvalues = NULL;
560 
561 			/* We don't want any member attributes added by the user. */
562 			modify_delete_values( op->ora_e, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
563 
564 			autogroup_add_group( op, agi, agd, op->ora_e, NULL, 1 , 0);
565 			ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
566 			return SLAP_CB_CONTINUE;
567 		}
568 	}
569 
570 	for ( ; age ; age = age->age_next ) {
571 		ldap_pvt_thread_mutex_lock( &age->age_mutex );
572 
573 		/* Check if any of the filters are the suffix to the entry DN.
574 		   If yes, we can test that filter against the entry. */
575 
576 		for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
577 			if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
578 				rc = test_filter( op, op->ora_e, agf->agf_filter );
579 				if ( rc == LDAP_COMPARE_TRUE ) {
580 				autogroup_add_member_to_group( op, &op->ora_e->e_name, &op->ora_e->e_nname, age );
581 					break;
582 				}
583 			}
584 		}
585 		ldap_pvt_thread_mutex_unlock( &age->age_mutex );
586 	}
587 
588 	ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
589 
590 	return SLAP_CB_CONTINUE;
591 }
592 
593 /*
594 ** agi	- internal group and attribute definitions list
595 ** e	- the group to remove from the internal list
596 */
597 static int
598 autogroup_delete_group( autogroup_info_t *agi, autogroup_entry_t *e )
599 {
600 	autogroup_entry_t	*age = agi->agi_entry,
601 				*age_prev = NULL,
602 				*age_next;
603 	int			rc = 1;
604 
605 	Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_group <%s>\n",
606 		age->age_dn.bv_val, 0, 0);
607 
608 	for ( age_next = age ; age_next ; age_prev = age, age = age_next ) {
609 		age_next = age->age_next;
610 
611 		if ( age == e ) {
612 			autogroup_filter_t	*agf = age->age_filter,
613 							*agf_next;
614 
615 			if ( age_prev != NULL ) {
616 				age_prev->age_next = age_next;
617 			} else {
618 				agi->agi_entry = NULL;
619 			}
620 
621 			ch_free( age->age_dn.bv_val );
622 			ch_free( age->age_ndn.bv_val );
623 
624 			for( agf_next = agf ; agf_next ; agf = agf_next ){
625 				agf_next = agf->agf_next;
626 
627 				filter_free( agf->agf_filter );
628 				ch_free( agf->agf_filterstr.bv_val );
629 				ch_free( agf->agf_dn.bv_val );
630 				ch_free( agf->agf_ndn.bv_val );
631 			}
632 
633 			ldap_pvt_thread_mutex_unlock( &age->age_mutex );
634 			ldap_pvt_thread_mutex_destroy( &age->age_mutex );
635 			ch_free( age );
636 
637 			rc = 0;
638 			return rc;
639 
640 		}
641 	}
642 
643 	Debug( LDAP_DEBUG_TRACE, "autogroup_delete_group: group <%s> not found, should not happen\n", age->age_dn.bv_val, 0, 0);
644 
645 	return rc;
646 
647 }
648 
649 static int
650 autogroup_delete_entry( Operation *op, SlapReply *rs)
651 {
652 	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
653 	autogroup_info_t		*agi = (autogroup_info_t *)on->on_bi.bi_private;
654 	autogroup_entry_t	*age = agi->agi_entry,
655 				*age_prev, *age_next;
656 	autogroup_filter_t	*agf;
657 	Entry			*e;
658 	int			matched_group = 0, rc = 0;
659 
660 	Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
661 
662 	ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
663 
664 	if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
665 		LDAP_SUCCESS || e == NULL ) {
666 		Debug( LDAP_DEBUG_TRACE, "autogroup_delete_entry: cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
667 		ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
668 		return SLAP_CB_CONTINUE;
669 	}
670 
671 	/* Check if the entry to be deleted is one of our groups. */
672 	for ( age_next = age ; age_next ; age_prev = age, age = age_next ) {
673 		ldap_pvt_thread_mutex_lock( &age->age_mutex );
674 		age_next = age->age_next;
675 
676 		if ( is_entry_objectclass_or_sub( e, age->age_def->agd_oc ) ) {
677 			int match = 1;
678 
679 			matched_group = 1;
680 
681 			dnMatch( &match, 0, NULL, NULL, &e->e_nname, &age->age_ndn );
682 
683 			if ( match == 0 ) {
684 				autogroup_delete_group( agi, age );
685 				break;
686 			}
687 		}
688 
689 		ldap_pvt_thread_mutex_unlock( &age->age_mutex );
690 	}
691 
692 	if ( matched_group == 1 ) {
693 		overlay_entry_release_ov( op, e, 0, on );
694 		ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
695 		return SLAP_CB_CONTINUE;
696 	}
697 
698 	/* Check if the entry matches any of the groups.
699 	   If yes, we can delete the entry from that group. */
700 
701 	for ( age = agi->agi_entry ; age ; age = age->age_next ) {
702 		ldap_pvt_thread_mutex_lock( &age->age_mutex );
703 
704 		for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
705 			if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
706 				rc = test_filter( op, e, agf->agf_filter );
707 				if ( rc == LDAP_COMPARE_TRUE ) {
708 				autogroup_delete_member_from_group( op, &e->e_name, &e->e_nname, age );
709 					break;
710 				}
711 			}
712 		}
713 		ldap_pvt_thread_mutex_unlock( &age->age_mutex );
714 	}
715 
716 	overlay_entry_release_ov( op, e, 0, on );
717 	ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
718 
719 	return SLAP_CB_CONTINUE;
720 }
721 
722 static int
723 autogroup_response( Operation *op, SlapReply *rs )
724 {
725 	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
726 	autogroup_info_t		*agi = (autogroup_info_t *)on->on_bi.bi_private;
727 	autogroup_def_t		*agd = agi->agi_def;
728 	autogroup_entry_t	*age = agi->agi_entry;
729 	autogroup_filter_t	*agf;
730 	BerValue		new_dn, new_ndn, pdn;
731 	Entry			*e, *group;
732 	Attribute		*a;
733 	int			is_olddn, is_newdn, dn_equal;
734 
735 	if ( op->o_tag == LDAP_REQ_MODRDN ) {
736 		if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op )) {
737 
738 			Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODRDN from <%s>\n", op->o_req_dn.bv_val, 0, 0);
739 
740 			ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
741 
742 			if ( op->oq_modrdn.rs_newSup ) {
743 				pdn = *op->oq_modrdn.rs_newSup;
744 			} else {
745 				dnParent( &op->o_req_dn, &pdn );
746 			}
747 			build_new_dn( &new_dn, &pdn, &op->orr_newrdn, op->o_tmpmemctx );
748 
749 			if ( op->oq_modrdn.rs_nnewSup ) {
750 				pdn = *op->oq_modrdn.rs_nnewSup;
751 			} else {
752 				dnParent( &op->o_req_ndn, &pdn );
753 			}
754 			build_new_dn( &new_ndn, &pdn, &op->orr_nnewrdn, op->o_tmpmemctx );
755 
756 			Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN to <%s>\n", new_dn.bv_val, 0, 0);
757 
758 			dnMatch( &dn_equal, 0, NULL, NULL, &op->o_req_ndn, &new_ndn );
759 
760 			if ( overlay_entry_get_ov( op, &new_ndn, NULL, NULL, 0, &e, on ) !=
761 				LDAP_SUCCESS || e == NULL ) {
762 				Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get entry for <%s>\n", new_dn.bv_val, 0, 0);
763 				ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
764 				return SLAP_CB_CONTINUE;
765 			}
766 
767 			a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
768 
769 
770 			if ( a == NULL ) {
771 				Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN entry <%s> has no objectClass\n", new_dn.bv_val, 0, 0);
772 				overlay_entry_release_ov( op, e, 0, on );
773 				ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
774 				return SLAP_CB_CONTINUE;
775 			}
776 
777 
778 			/* If a groups DN is modified, just update age_dn/ndn of that group with the new DN. */
779 			for ( ; agd; agd = agd->agd_next ) {
780 
781 				if ( value_find_ex( slap_schema.si_ad_objectClass,
782 						SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
783 						SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
784 						a->a_nvals, &agd->agd_oc->soc_cname,
785 						op->o_tmpmemctx ) == 0 )
786 				{
787 					for ( age = agi->agi_entry ; age ; age = age->age_next ) {
788 						int match = 1;
789 
790 						dnMatch( &match, 0, NULL, NULL, &age->age_ndn, &op->o_req_ndn );
791 						if ( match == 0 ) {
792 							Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN updating group's DN to <%s>\n", new_dn.bv_val, 0, 0);
793 							ber_dupbv( &age->age_dn, &new_dn );
794 							ber_dupbv( &age->age_ndn, &new_ndn );
795 
796 							op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx  );
797 							op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
798 							overlay_entry_release_ov( op, e, 0, on );
799 							ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
800 							return SLAP_CB_CONTINUE;
801 						}
802 					}
803 
804 				}
805 			}
806 
807 			overlay_entry_release_ov( op, e, 0, on );
808 
809 			/* For each group:
810 			   1. check if the orginal entry's DN is in the group.
811 			   2. chceck if the any of the group filter's base DN is a suffix of the new DN
812 
813 			   If 1 and 2 are both false, we do nothing.
814 			   If 1 and 2 is true, we remove the old DN from the group, and add the new DN.
815 			   If 1 is false, and 2 is true, we check the entry against the group's filters,
816 				and add it's DN to the group.
817 			   If 1 is true, and 2 is false, we delete the entry's DN from the group.
818 			*/
819 			for ( age = agi->agi_entry ; age ; age = age->age_next ) {
820 				is_olddn = 0;
821 				is_newdn = 0;
822 
823 
824 				ldap_pvt_thread_mutex_lock( &age->age_mutex );
825 
826 				if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
827 					LDAP_SUCCESS || group == NULL ) {
828 					Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get group entry <%s>\n", age->age_dn.bv_val, 0, 0);
829 
830 					op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
831 					op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
832 
833 					ldap_pvt_thread_mutex_unlock( &age->age_mutex );
834 					ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
835 					return SLAP_CB_CONTINUE;
836 				}
837 
838 				a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
839 
840 				if ( a != NULL ) {
841 					if ( value_find_ex( age->age_def->agd_member_ad,
842 							SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
843 							SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
844 							a->a_nvals, &op->o_req_ndn, op->o_tmpmemctx ) == 0 )
845 					{
846 						is_olddn = 1;
847 					}
848 
849 				}
850 
851 				overlay_entry_release_ov( op, group, 0, on );
852 
853 				for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
854 					if ( dnIsSuffix( &new_ndn, &agf->agf_ndn ) ) {
855 						is_newdn = 1;
856 						break;
857 					}
858 				}
859 
860 
861 				if ( is_olddn == 1 && is_newdn == 0 ) {
862 					autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
863 				} else
864 				if ( is_olddn == 0 && is_newdn == 1 ) {
865 					for ( agf = age->age_filter; agf; agf = agf->agf_next ) {
866 						if ( test_filter( op, e, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
867 							autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
868 							break;
869 						}
870 					}
871 				} else
872 				if ( is_olddn == 1 && is_newdn == 1 && dn_equal != 0 ) {
873 					autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
874 					autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
875 				}
876 
877 				ldap_pvt_thread_mutex_unlock( &age->age_mutex );
878 			}
879 
880 			op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
881 			op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
882 
883 			ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
884 		}
885 	}
886 
887 	if ( op->o_tag == LDAP_REQ_MODIFY ) {
888 		if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS  && !get_manageDSAit( op ) ) {
889 			Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODIFY <%s>\n", op->o_req_dn.bv_val, 0, 0);
890 
891 			ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
892 
893 			if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
894 				LDAP_SUCCESS || e == NULL ) {
895 				Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
896 				ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
897 				return SLAP_CB_CONTINUE;
898 			}
899 
900 			a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
901 
902 
903 			if ( a == NULL ) {
904 				Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
905 				overlay_entry_release_ov( op, e, 0, on );
906 				ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
907 				return SLAP_CB_CONTINUE;
908 			}
909 
910 
911 			/* If we modify a group's memberURL, we have to delete all of it's members,
912 			   and add them anew, because we cannot tell from which memberURL a member was added. */
913 			for ( ; agd; agd = agd->agd_next ) {
914 
915 				if ( value_find_ex( slap_schema.si_ad_objectClass,
916 						SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
917 						SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
918 						a->a_nvals, &agd->agd_oc->soc_cname,
919 						op->o_tmpmemctx ) == 0 )
920 				{
921 					Modifications	*m;
922 					int		match = 1;
923 
924 					m = op->orm_modlist;
925 
926 					for ( ; age ; age = age->age_next ) {
927 						ldap_pvt_thread_mutex_lock( &age->age_mutex );
928 
929 						dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
930 
931 						if ( match == 0 ) {
932 							for ( ; m ; m = m->sml_next ) {
933 								if ( m->sml_desc == age->age_def->agd_member_url_ad ) {
934 									autogroup_def_t	*group_agd = age->age_def;
935 									Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY changing memberURL for group <%s>\n",
936 										op->o_req_dn.bv_val, 0, 0);
937 
938 									overlay_entry_release_ov( op, e, 0, on );
939 
940 									autogroup_delete_member_from_group( op, NULL, NULL, age );
941 									autogroup_delete_group( agi, age );
942 
943 									autogroup_add_group( op, agi, group_agd, NULL, &op->o_req_ndn, 1, 1);
944 
945 									ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
946 									return SLAP_CB_CONTINUE;
947 								}
948 							}
949 
950 							ldap_pvt_thread_mutex_unlock( &age->age_mutex );
951 							break;
952 						}
953 
954 						ldap_pvt_thread_mutex_unlock( &age->age_mutex );
955 					}
956 
957 					overlay_entry_release_ov( op, e, 0, on );
958 					ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
959 					return SLAP_CB_CONTINUE;
960 				}
961 			}
962 
963 			overlay_entry_release_ov( op, e, 0, on );
964 
965 			/* When modifing any of the attributes of an entry, we must
966 			   check if the entry is in any of our groups, and if
967 			   the modified entry maches any of the filters of that group.
968 
969 			   If the entry exists in a group, but the modified attributes do
970 				not match any of the group's filters, we delete the entry from that group.
971 			   If the entry doesn't exist in a group, but matches a filter,
972 				we add it to that group.
973 			*/
974 			for ( age = agi->agi_entry ; age ; age = age->age_next ) {
975 				is_olddn = 0;
976 				is_newdn = 0;
977 
978 
979 				ldap_pvt_thread_mutex_lock( &age->age_mutex );
980 
981 				if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
982 					LDAP_SUCCESS || group == NULL ) {
983 					Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n",
984 						age->age_dn.bv_val, 0, 0);
985 
986 					ldap_pvt_thread_mutex_unlock( &age->age_mutex );
987 					ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
988 					return SLAP_CB_CONTINUE;
989 				}
990 
991 				a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
992 
993 				if ( a != NULL ) {
994 					if ( value_find_ex( age->age_def->agd_member_ad,
995 							SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
996 							SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
997 							a->a_nvals, &op->o_req_ndn, op->o_tmpmemctx ) == 0 )
998 					{
999 						is_olddn = 1;
1000 					}
1001 
1002 				}
1003 
1004 				overlay_entry_release_ov( op, group, 0, on );
1005 
1006 				for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1007 					if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1008 						if ( test_filter( op, e, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
1009 							is_newdn = 1;
1010 							break;
1011 						}
1012 					}
1013 				}
1014 
1015 				if ( is_olddn == 1 && is_newdn == 0 ) {
1016 					autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1017 				} else
1018 				if ( is_olddn == 0 && is_newdn == 1 ) {
1019 					autogroup_add_member_to_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1020 				}
1021 
1022 				ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1023 			}
1024 
1025 			ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1026 		}
1027 	}
1028 
1029 	return SLAP_CB_CONTINUE;
1030 }
1031 
1032 /*
1033 ** When modifing a group, we must deny any modifications to the member attribute,
1034 ** because the group would be inconsistent.
1035 */
1036 static int
1037 autogroup_modify_entry( Operation *op, SlapReply *rs)
1038 {
1039 	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
1040 	autogroup_info_t		*agi = (autogroup_info_t *)on->on_bi.bi_private;
1041 	autogroup_def_t		*agd = agi->agi_def;
1042 	autogroup_entry_t	*age = agi->agi_entry;
1043 	Entry			*e;
1044 	Attribute		*a;
1045 
1046 	if ( get_manageDSAit( op ) ) {
1047 		return SLAP_CB_CONTINUE;
1048 	}
1049 
1050 	Debug( LDAP_DEBUG_TRACE, "==> autogroup_modify_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
1051 	ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1052 
1053 	if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1054 		LDAP_SUCCESS || e == NULL ) {
1055 		Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1056 		ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1057 		return SLAP_CB_CONTINUE;
1058 	}
1059 
1060 	a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1061 
1062 	if ( a == NULL ) {
1063 		Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
1064 		ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1065 		return SLAP_CB_CONTINUE;
1066 	}
1067 
1068 
1069 	for ( ; agd; agd = agd->agd_next ) {
1070 
1071 		if ( value_find_ex( slap_schema.si_ad_objectClass,
1072 				SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1073 				SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1074 				a->a_nvals, &agd->agd_oc->soc_cname,
1075 				op->o_tmpmemctx ) == 0 )
1076 		{
1077 			Modifications	*m;
1078 			int		match = 1;
1079 
1080 			m = op->orm_modlist;
1081 
1082 			for ( ; age ; age = age->age_next ) {
1083 				dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
1084 
1085 				if ( match == 0 ) {
1086 					for ( ; m ; m = m->sml_next ) {
1087 						if ( m->sml_desc == age->age_def->agd_member_ad ) {
1088 							overlay_entry_release_ov( op, e, 0, on );
1089 							ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1090 							Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry attempted to modify group's <%s> member attribute\n", op->o_req_dn.bv_val, 0, 0);
1091 							send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION, "attempt to modify dynamic group member attribute");
1092 							return LDAP_CONSTRAINT_VIOLATION;
1093 						}
1094 					}
1095 					break;
1096 				}
1097 			}
1098 
1099 			overlay_entry_release_ov( op, e, 0, on );
1100 			ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1101 			return SLAP_CB_CONTINUE;
1102 		}
1103 	}
1104 
1105 	overlay_entry_release_ov( op, e, 0, on );
1106 	ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1107 	return SLAP_CB_CONTINUE;
1108 }
1109 
1110 /*
1111 ** Builds a filter for searching for the
1112 ** group entries, according to the objectClass.
1113 */
1114 static int
1115 autogroup_build_def_filter( autogroup_def_t *agd, Operation *op )
1116 {
1117 	char	*ptr;
1118 
1119 	Debug( LDAP_DEBUG_TRACE, "==> autogroup_build_def_filter\n", 0, 0, 0);
1120 
1121 	op->ors_filterstr.bv_len = STRLENOF( "(=)" )
1122 			+ slap_schema.si_ad_objectClass->ad_cname.bv_len
1123 			+ agd->agd_oc->soc_cname.bv_len;
1124 	ptr = op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
1125 	*ptr++ = '(';
1126 	ptr = lutil_strcopy( ptr, slap_schema.si_ad_objectClass->ad_cname.bv_val );
1127 	*ptr++ = '=';
1128 	ptr = lutil_strcopy( ptr, agd->agd_oc->soc_cname.bv_val );
1129 	*ptr++ = ')';
1130 	*ptr = '\0';
1131 
1132 	op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
1133 
1134 	assert( op->ors_filterstr.bv_len == ptr - op->ors_filterstr.bv_val );
1135 
1136 	return 0;
1137 }
1138 
1139 enum {
1140 	AG_ATTRSET = 1,
1141 	AG_LAST
1142 };
1143 
1144 static ConfigDriver	ag_cfgen;
1145 
1146 static ConfigTable agcfg[] = {
1147 	{ "autogroup-attrset", "group-oc> <URL-ad> <member-ad",
1148 		3, 4, 0, ARG_MAGIC|AG_ATTRSET, ag_cfgen,
1149 		"( OLcfgCtAt:2.1 NAME 'olcAGattrSet' "
1150 			"DESC 'Automatic groups: <group objectClass>, <URL attributeDescription>, <member attributeDescription>' "
1151 			"EQUALITY caseIgnoreMatch "
1152 			"SYNTAX OMsDirectoryString "
1153 			"X-ORDERED 'VALUES' )",
1154 			NULL, NULL },
1155 	{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
1156 };
1157 
1158 static ConfigOCs agocs[] = {
1159 	{ "( OLcfgCtOc:2.1 "
1160 		"NAME 'olcAutomaticGroups' "
1161 		"DESC 'Automatic groups configuration' "
1162 		"SUP olcOverlayConfig "
1163 		"MAY olcAGattrSet )",
1164 		Cft_Overlay, agcfg, NULL, NULL },
1165 	{ NULL, 0, NULL }
1166 };
1167 
1168 
1169 static int
1170 ag_cfgen( ConfigArgs *c )
1171 {
1172 	slap_overinst		*on = (slap_overinst *)c->bi;
1173 	autogroup_info_t		*agi = (autogroup_info_t *)on->on_bi.bi_private;
1174 	autogroup_def_t		*agd;
1175 	autogroup_entry_t	*age;
1176 
1177 	int rc = 0, i;
1178 
1179 	Debug( LDAP_DEBUG_TRACE, "==> autogroup_cfgen\n", 0, 0, 0);
1180 
1181 	if( agi == NULL ) {
1182 		agi = (autogroup_info_t*)ch_calloc( 1, sizeof(autogroup_info_t) );
1183 		ldap_pvt_thread_mutex_init( &agi->agi_mutex );
1184 		agi->agi_def = NULL;
1185 		agi->agi_entry = NULL;
1186 		on->on_bi.bi_private = (void *)agi;
1187 	}
1188 
1189 	agd = agi->agi_def;
1190 	age = agi->agi_entry;
1191 
1192 	if ( c->op == SLAP_CONFIG_EMIT ) {
1193 
1194 		ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1195 
1196 		for ( i = 0 ; agd ; i++, agd = agd->agd_next ) {
1197 			struct berval	bv;
1198 			char		*ptr = c->cr_msg;
1199 
1200 			assert(agd->agd_oc != NULL);
1201 			assert(agd->agd_member_url_ad != NULL);
1202 			assert(agd->agd_member_ad != NULL);
1203 
1204 			ptr += snprintf( c->cr_msg, sizeof( c->cr_msg ),
1205 				SLAP_X_ORDERED_FMT "%s %s %s", i,
1206 				agd->agd_oc->soc_cname.bv_val,
1207 				agd->agd_member_url_ad->ad_cname.bv_val,
1208 				agd->agd_member_ad->ad_cname.bv_val );
1209 
1210 			bv.bv_val = c->cr_msg;
1211 			bv.bv_len = ptr - bv.bv_val;
1212 			value_add_one ( &c->rvalue_vals, &bv );
1213 
1214 		}
1215 		ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1216 
1217 		return rc;
1218 
1219 	}else if ( c->op == LDAP_MOD_DELETE ) {
1220 		if ( c->valx < 0) {
1221 			autogroup_def_t 		*agd_next;
1222 			autogroup_entry_t	*age_next;
1223 			autogroup_filter_t	*agf = age->age_filter,
1224 						*agf_next;
1225 
1226 			ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1227 
1228 			for ( agd_next = agd; agd_next; agd = agd_next ) {
1229 				agd_next = agd->agd_next;
1230 
1231 				ch_free( agd );
1232 			}
1233 
1234 			for ( age_next = age ; age_next ; age = age_next ) {
1235 				age_next = age->age_next;
1236 
1237 				ch_free( age->age_dn.bv_val );
1238 				ch_free( age->age_ndn.bv_val );
1239 
1240 				for( agf_next = agf ; agf_next ; agf = agf_next ){
1241 					agf_next = agf->agf_next;
1242 
1243 					filter_free( agf->agf_filter );
1244 					ch_free( agf->agf_filterstr.bv_val );
1245 					ch_free( agf->agf_dn.bv_val );
1246 					ch_free( agf->agf_ndn.bv_val );
1247 				}
1248 
1249 				ldap_pvt_thread_mutex_init( &age->age_mutex );
1250 				ch_free( age );
1251 			}
1252 
1253 			ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1254 
1255 			ldap_pvt_thread_mutex_destroy( &agi->agi_mutex );
1256 			ch_free( agi );
1257 			on->on_bi.bi_private = NULL;
1258 
1259 		} else {
1260 			autogroup_def_t		**agdp;
1261 			autogroup_entry_t	*age_next, *age_prev;
1262 			autogroup_filter_t	*agf,
1263 						*agf_next;
1264 
1265 			ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1266 
1267 			for ( i = 0, agdp = &agi->agi_def;
1268 				i < c->valx; i++ )
1269 			{
1270 				if ( *agdp == NULL) {
1271 					return 1;
1272 				}
1273 				agdp = &(*agdp)->agd_next;
1274 			}
1275 
1276 			agd = *agdp;
1277 			*agdp = agd->agd_next;
1278 
1279 			for ( age_next = age , age_prev = NULL ; age_next ; age_prev = age, age = age_next ) {
1280 				age_next = age->age_next;
1281 
1282 				if( age->age_def == agd ) {
1283 					agf = age->age_filter;
1284 
1285 					ch_free( age->age_dn.bv_val );
1286 					ch_free( age->age_ndn.bv_val );
1287 
1288 					for ( agf_next = agf; agf_next ; agf = agf_next ) {
1289 						agf_next = agf->agf_next;
1290 						filter_free( agf->agf_filter );
1291 						ch_free( agf->agf_filterstr.bv_val );
1292 						ch_free( agf->agf_dn.bv_val );
1293 						ch_free( agf->agf_ndn.bv_val );
1294 					}
1295 
1296 					ldap_pvt_thread_mutex_destroy( &age->age_mutex );
1297 					ch_free( age );
1298 
1299 					age = age_prev;
1300 
1301 					if( age_prev != NULL ) {
1302 						age_prev->age_next = age_next;
1303 					}
1304 				}
1305 			}
1306 
1307 			ch_free( agd );
1308 			agd = agi->agi_def;
1309 			ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1310 
1311 		}
1312 
1313 		return rc;
1314 	}
1315 
1316 	switch(c->type){
1317 	case AG_ATTRSET: {
1318 		autogroup_def_t		**agdp,
1319 					*agd_next = NULL;
1320 		ObjectClass		*oc = NULL;
1321 		AttributeDescription	*member_url_ad = NULL,
1322 					*member_ad = NULL;
1323 		const char		*text;
1324 
1325 
1326 		oc = oc_find( c->argv[ 1 ] );
1327 		if( oc == NULL ){
1328 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1329 				"\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1330 				"unable to find ObjectClass \"%s\"",
1331 				c->argv[ 1 ] );
1332 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1333 				c->log, c->cr_msg, 0 );
1334 			return 1;
1335 		}
1336 
1337 
1338 		rc = slap_str2ad( c->argv[ 2 ], &member_url_ad, &text );
1339 		if( rc != LDAP_SUCCESS ) {
1340 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1341 				"\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1342 				"unable to find AttributeDescription \"%s\"",
1343 				c->argv[ 2 ] );
1344 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1345 				c->log, c->cr_msg, 0 );
1346 			return 1;
1347 		}
1348 
1349 		if( !is_at_subtype( member_url_ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
1350 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1351 				"\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1352 				"AttributeDescription \"%s\" ",
1353 				"must be of a subtype \"labeledURI\"",
1354 				c->argv[ 2 ] );
1355 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1356 				c->log, c->cr_msg, 0 );
1357 			return 1;
1358 		}
1359 
1360 		rc = slap_str2ad( c->argv[3], &member_ad, &text );
1361 		if( rc != LDAP_SUCCESS ) {
1362 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1363 				"\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1364 				"unable to find AttributeDescription \"%s\"",
1365 				c->argv[ 3 ] );
1366 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1367 				c->log, c->cr_msg, 0 );
1368 			return 1;
1369 		}
1370 
1371 		ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1372 
1373 		for ( agdp = &agi->agi_def ; *agdp ; agdp = &(*agdp)->agd_next ) {
1374 			/* The same URL attribute / member attribute pair
1375 			* cannot be repeated */
1376 
1377 			if ( (*agdp)->agd_member_url_ad == member_url_ad && (*agdp)->agd_member_ad == member_ad ) {
1378 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
1379 					"\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1380 					"URL attributeDescription \"%s\" already mapped",
1381 					member_ad->ad_cname.bv_val );
1382 				Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1383 					c->log, c->cr_msg, 0 );
1384 /*				return 1; //warning*/
1385 			}
1386 		}
1387 
1388 		if ( c->valx > 0 ) {
1389 			int	i;
1390 
1391 			for ( i = 0, agdp = &agi->agi_def ;
1392 				i < c->valx; i++ )
1393 			{
1394 				if ( *agdp == NULL ) {
1395 					snprintf( c->cr_msg, sizeof( c->cr_msg ),
1396 						"\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1397 						"invalid index {%d}",
1398 						c->valx );
1399 					Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1400 						c->log, c->cr_msg, 0 );
1401 
1402 					ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1403 					return 1;
1404 				}
1405 				agdp = &(*agdp)->agd_next;
1406 			}
1407 			agd_next = *agdp;
1408 
1409 		} else {
1410 			for ( agdp = &agi->agi_def; *agdp;
1411 				agdp = &(*agdp)->agd_next )
1412 				/* goto last */;
1413 		}
1414 
1415 		*agdp = (autogroup_def_t *)ch_calloc( 1, sizeof(autogroup_info_t));
1416 
1417 		(*agdp)->agd_oc = oc;
1418 		(*agdp)->agd_member_url_ad = member_url_ad;
1419 		(*agdp)->agd_member_ad = member_ad;
1420 		(*agdp)->agd_next = agd_next;
1421 
1422 		ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1423 
1424 		} break;
1425 
1426 	default:
1427 		rc = 1;
1428 		break;
1429 	}
1430 
1431 	return rc;
1432 }
1433 
1434 /*
1435 ** Do a search for all the groups in the
1436 ** database, and add them to out internal list.
1437 */
1438 static int
1439 autogroup_db_open(
1440 	BackendDB	*be,
1441 	ConfigReply	*cr )
1442 {
1443 	slap_overinst			*on = (slap_overinst *) be->bd_info;
1444 	autogroup_info_t		*agi = on->on_bi.bi_private;
1445 	autogroup_def_t		*agd;
1446 	autogroup_sc_t		ags;
1447 	Operation		*op;
1448 	SlapReply		rs = { REP_RESULT };
1449 	slap_callback		cb = { 0 };
1450 
1451 	void				*thrctx = ldap_pvt_thread_pool_context();
1452 	Connection			conn = { 0 };
1453 	OperationBuffer 	opbuf;
1454 
1455 	Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_open\n", 0, 0, 0);
1456 
1457 	if ( agi == NULL ) {
1458 		return 0;
1459 	}
1460 
1461 	connection_fake_init( &conn, &opbuf, thrctx );
1462 	op = &opbuf.ob_op;
1463 
1464 	op->ors_attrsonly = 0;
1465 	op->o_tag = LDAP_REQ_SEARCH;
1466 	op->o_dn = be->be_rootdn;
1467 	op->o_ndn = be->be_rootndn;
1468 
1469 	op->o_req_dn = be->be_suffix[0];
1470 	op->o_req_ndn = be->be_nsuffix[0];
1471 
1472 	op->ors_scope = LDAP_SCOPE_SUBTREE;
1473 	op->ors_deref = LDAP_DEREF_NEVER;
1474 	op->ors_limit = NULL;
1475 	op->ors_tlimit = SLAP_NO_LIMIT;
1476 	op->ors_slimit = SLAP_NO_LIMIT;
1477 	op->ors_attrs =  slap_anlist_no_attrs;
1478 
1479 	op->o_bd = be;
1480 	op->o_bd->bd_info = (BackendInfo *)on->on_info;
1481 
1482 	ags.ags_info = agi;
1483 	cb.sc_private = &ags;
1484 	cb.sc_response = autogroup_group_add_cb;
1485 	cb.sc_cleanup = NULL;
1486 	cb.sc_next = NULL;
1487 
1488 	op->o_callback = &cb;
1489 
1490 	for (agd = agi->agi_def ; agd ; agd = agd->agd_next) {
1491 
1492 		autogroup_build_def_filter(agd, op);
1493 
1494 		ags.ags_def = agd;
1495 
1496 		op->o_bd->be_search( op, &rs );
1497 
1498 		filter_free_x( op, op->ors_filter, 1 );
1499 		op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
1500 	}
1501 
1502 	return 0;
1503 }
1504 
1505 static int
1506 autogroup_db_close(
1507 	BackendDB	*be,
1508 	ConfigReply	*cr )
1509 {
1510 	slap_overinst			*on = (slap_overinst *) be->bd_info;
1511 
1512 	Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_close\n", 0, 0, 0);
1513 
1514 	if ( on->on_bi.bi_private ) {
1515 		autogroup_info_t		*agi = on->on_bi.bi_private;
1516 		autogroup_entry_t	*age = agi->agi_entry,
1517 					*age_next;
1518 		autogroup_filter_t	*agf, *agf_next;
1519 
1520 		for ( age_next = age; age_next; age = age_next ) {
1521 			age_next = age->age_next;
1522 
1523 			ch_free( age->age_dn.bv_val );
1524 			ch_free( age->age_ndn.bv_val );
1525 
1526 			agf = age->age_filter;
1527 
1528 			for ( agf_next = agf; agf_next; agf = agf_next ) {
1529 				agf_next = agf->agf_next;
1530 
1531 				filter_free( agf->agf_filter );
1532 				ch_free( agf->agf_filterstr.bv_val );
1533 				ch_free( agf->agf_dn.bv_val );
1534 				ch_free( agf->agf_ndn.bv_val );
1535 				ch_free( agf );
1536 			}
1537 
1538 			ldap_pvt_thread_mutex_destroy( &age->age_mutex );
1539 			ch_free( age );
1540 		}
1541 	}
1542 
1543 	return 0;
1544 }
1545 
1546 static int
1547 autogroup_db_destroy(
1548 	BackendDB	*be,
1549 	ConfigReply	*cr )
1550 {
1551 	slap_overinst			*on = (slap_overinst *) be->bd_info;
1552 
1553 	Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_destroy\n", 0, 0, 0);
1554 
1555 	if ( on->on_bi.bi_private ) {
1556 		autogroup_info_t		*agi = on->on_bi.bi_private;
1557 		autogroup_def_t		*agd = agi->agi_def,
1558 					*agd_next;
1559 
1560 		for ( agd_next = agd; agd_next; agd = agd_next ) {
1561 			agd_next = agd->agd_next;
1562 
1563 			ch_free( agd );
1564 		}
1565 
1566 		ldap_pvt_thread_mutex_destroy( &agi->agi_mutex );
1567 		ch_free( agi );
1568 	}
1569 
1570 	return 0;
1571 }
1572 
1573 static slap_overinst	autogroup = { { NULL } };
1574 
1575 static
1576 int
1577 autogroup_initialize(void)
1578 {
1579 	int		rc = 0;
1580 	autogroup.on_bi.bi_type = "autogroup";
1581 
1582 	autogroup.on_bi.bi_db_open = autogroup_db_open;
1583 	autogroup.on_bi.bi_db_close = autogroup_db_close;
1584 	autogroup.on_bi.bi_db_destroy = autogroup_db_destroy;
1585 
1586 	autogroup.on_bi.bi_op_add = autogroup_add_entry;
1587 	autogroup.on_bi.bi_op_delete = autogroup_delete_entry;
1588 	autogroup.on_bi.bi_op_modify = autogroup_modify_entry;
1589 
1590 	autogroup.on_response = autogroup_response;
1591 
1592 	autogroup.on_bi.bi_cf_ocs = agocs;
1593 
1594 	rc = config_register_schema( agcfg, agocs );
1595 	if ( rc ) {
1596 		return rc;
1597 	}
1598 
1599 	return overlay_register( &autogroup );
1600 }
1601 
1602 int
1603 init_module( int argc, char *argv[] )
1604 {
1605 	return autogroup_initialize();
1606 }
1607