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