Home | History | Annotate | Line # | Download | only in autogroup
autogroup.c revision 1.1.1.2
      1 /*	$NetBSD: autogroup.c,v 1.1.1.2 2010/03/08 02:14:20 lukem Exp $	*/
      2 
      3 /* autogroup.c - automatic group overlay */
      4 /* OpenLDAP: pkg/ldap/contrib/slapd-modules/autogroup/autogroup.c,v 1.2.2.5 2009/09/29 21:52:13 quanah Exp */
      5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      6  *
      7  * Copyright 2007-2009 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  */
     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