1 /*	$NetBSD: autogroup.c,v 1.3 2021/08/14 16:14:51 christos 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-2021 The OpenLDAP Foundation.
8  * Portions Copyright 2007 Michał Szulczyński.
9  * Portions Copyright 2009 Howard Chu.
10  * All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted only as authorized by the OpenLDAP
14  * Public License.
15  *
16  * A copy of this license is available in the file LICENSE in the
17  * top-level directory of the distribution or, alternatively, at
18  * <http://www.OpenLDAP.org/license.html>.
19  */
20 /* ACKNOWLEDGEMENTS:
21  * This work was initially developed by Michał Szulczyński for inclusion in
22  * OpenLDAP Software.  Additional significant contributors include:
23  *   Howard Chu
24  *   Raphael Ouazana
25  *   Norbert Pueschel
26  *   Christian Manal
27  */
28 
29 #include <sys/cdefs.h>
30 __RCSID("$NetBSD: autogroup.c,v 1.3 2021/08/14 16:14:51 christos Exp $");
31 
32 #include "portable.h"
33 
34 #include <stdio.h>
35 
36 #include <ac/string.h>
37 
38 #include "slap.h"
39 #include "slap-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
autogroup_add_member_to_group(Operation * op,BerValue * dn,BerValue * ndn,autogroup_entry_t * age)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 );
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
autogroup_add_member_values_to_group(Operation * op,struct berval * dn,autogroup_entry_t * age,Attribute * attr)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 );
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
autogroup_delete_member_from_group(Operation * op,BerValue * dn,BerValue * ndn,autogroup_entry_t * age)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 );
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 );
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
autogroup_delete_member_values_from_group(Operation * op,struct berval * dn,autogroup_entry_t * age,Attribute * attr)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 );
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
autogroup_member_search_cb(Operation * op,SlapReply * rs)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" );
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
autogroup_member_search_modify_cb(Operation * op,SlapReply * rs)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" );
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
autogroup_add_members_from_filter(Operation * op,Entry * e,autogroup_entry_t * age,autogroup_filter_t * agf,int modify)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 );
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 matching 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
autogroup_add_group(Operation * op,autogroup_info_t * agi,autogroup_def_t * agd,Entry * e,BerValue * ndn,int scan,int modify)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 );
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 );
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 );
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" );
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 );
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 );
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 );
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 );
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] );
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 );
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
autogroup_group_add_cb(Operation * op,SlapReply * rs)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" );
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
autogroup_add_entry_cb(Operation * op,SlapReply * rs)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 ourselves.
858 */
859 static int
autogroup_add_entry(Operation * op,SlapReply * rs)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 );
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
autogroup_delete_group(autogroup_info_t * agi,autogroup_entry_t * e)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 );
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 );
958 
959 	return rc;
960 
961 }
962 
963 static int
autogroup_delete_entry(Operation * op,SlapReply * rs)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 );
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 );
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
autogroup_response(Operation * op,SlapReply * rs)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 );
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 );
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 );
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 );
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 );
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 original entry's DN is in the group.
1176 			   2. check 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 					/* Request 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 );
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 );
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 );
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 );
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 );
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 matches 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 );
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
autogroup_memberOf_filter(Filter * f,BerValue * dn,AttributeDescription * memberof_ad)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 modifying a group, we must deny any modifications to the member attribute,
1517 ** because the group would be inconsistent.
1518 */
1519 static int
autogroup_modify_entry(Operation * op,SlapReply * rs)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 );
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 );
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 );
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 );
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
autogroup_modrdn_entry(Operation * op,SlapReply * rs)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 );
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 );
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
autogroup_build_def_filter(autogroup_def_t * agd,Operation * op)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" );
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 ( 'olcAutoGroupAttrSet' '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 ( 'olcAutoGroupMemberOfAd' 'olcAGmemberOfAd' ) "
1731 			"DESC 'memberOf attribute' "
1732 			"EQUALITY caseIgnoreMatch "
1733 			"SYNTAX OMsDirectoryString SINGLE-VALUE )",
1734 			NULL, NULL },
1735 
1736 	{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
1737 };
1738 
1739 static ConfigOCs agocs[] = {
1740 	{ "( OLcfgCtOc:2.1 "
1741 		"NAME 'olcAutoGroupConfig' "
1742 		"DESC 'Automatic groups configuration' "
1743 		"SUP olcOverlayConfig "
1744 		"MAY ( "
1745 			"olcAutoGroupAttrSet "
1746 			"$ olcAutoGroupMemberOfAd "
1747 		    ")"
1748 	  ")",
1749 		Cft_Overlay, agcfg, NULL, NULL },
1750 	{ NULL, 0, NULL }
1751 };
1752 
1753 
1754 static int
ag_cfgen(ConfigArgs * c)1755 ag_cfgen( ConfigArgs *c )
1756 {
1757 	slap_overinst		*on = (slap_overinst *)c->bi;
1758 	autogroup_info_t		*agi = (autogroup_info_t *)on->on_bi.bi_private;
1759 	autogroup_def_t		*agd;
1760 	autogroup_entry_t	*age;
1761 
1762 	int rc = 0, i;
1763 
1764 	Debug( LDAP_DEBUG_TRACE, "==> autogroup_cfgen\n" );
1765 
1766 	if( agi == NULL ) {
1767 		agi = (autogroup_info_t*)ch_calloc( 1, sizeof(autogroup_info_t) );
1768 		ldap_pvt_thread_mutex_init( &agi->agi_mutex );
1769 		agi->agi_def = NULL;
1770 		agi->agi_entry = NULL;
1771 		on->on_bi.bi_private = (void *)agi;
1772 	}
1773 
1774 	agd = agi->agi_def;
1775 	age = agi->agi_entry;
1776 
1777 	if ( c->op == SLAP_CONFIG_EMIT ) {
1778 
1779 		switch( c->type ){
1780 		case AG_ATTRSET:
1781 			for ( i = 0 ; agd ; i++, agd = agd->agd_next ) {
1782 				struct berval	bv;
1783 				char		*ptr = c->cr_msg;
1784 
1785 				assert(agd->agd_oc != NULL);
1786 				assert(agd->agd_member_url_ad != NULL);
1787 				assert(agd->agd_member_ad != NULL);
1788 
1789 				ptr += snprintf( c->cr_msg, sizeof( c->cr_msg ),
1790 					SLAP_X_ORDERED_FMT "%s %s %s", i,
1791 					agd->agd_oc->soc_cname.bv_val,
1792 					agd->agd_member_url_ad->ad_cname.bv_val,
1793 					agd->agd_member_ad->ad_cname.bv_val );
1794 
1795 				bv.bv_val = c->cr_msg;
1796 				bv.bv_len = ptr - bv.bv_val;
1797 				value_add_one ( &c->rvalue_vals, &bv );
1798 
1799 			}
1800 			break;
1801 
1802 		case AG_MEMBER_OF_AD:
1803 			if ( agi->agi_memberof_ad != NULL ){
1804 				value_add_one( &c->rvalue_vals, &agi->agi_memberof_ad->ad_cname );
1805 			}
1806 			break;
1807 
1808 		default:
1809 			assert( 0 );
1810 			return 1;
1811       }
1812 
1813 		return rc;
1814 
1815 	}else if ( c->op == LDAP_MOD_DELETE ) {
1816 		if ( c->valx < 0) {
1817 			autogroup_def_t 		*agd_next;
1818 			autogroup_entry_t	*age_next;
1819 			autogroup_filter_t	*agf = age->age_filter,
1820 						*agf_next;
1821 
1822 			for ( agd_next = agd; agd_next; agd = agd_next ) {
1823 				agd_next = agd->agd_next;
1824 
1825 				ch_free( agd );
1826 			}
1827 
1828 			for ( age_next = age ; age_next ; age = age_next ) {
1829 				age_next = age->age_next;
1830 
1831 				ch_free( age->age_dn.bv_val );
1832 				ch_free( age->age_ndn.bv_val );
1833 
1834 				for( agf_next = agf ; agf_next ; agf = agf_next ){
1835 					agf_next = agf->agf_next;
1836 
1837 					filter_free( agf->agf_filter );
1838 					ch_free( agf->agf_filterstr.bv_val );
1839 					ch_free( agf->agf_dn.bv_val );
1840 					ch_free( agf->agf_ndn.bv_val );
1841 					anlist_free( agf->agf_anlist, 1, NULL );
1842 					ch_free( agf );
1843 				}
1844 
1845 				ldap_pvt_thread_mutex_init( &age->age_mutex );
1846 				ch_free( age );
1847 			}
1848 
1849 			ch_free( agi );
1850 			on->on_bi.bi_private = NULL;
1851 
1852 		} else {
1853 			autogroup_def_t		**agdp;
1854 			autogroup_entry_t	*age_next, *age_prev;
1855 			autogroup_filter_t	*agf,
1856 						*agf_next;
1857 
1858 			for ( i = 0, agdp = &agi->agi_def;
1859 				i < c->valx; i++ )
1860 			{
1861 				if ( *agdp == NULL) {
1862 					return 1;
1863 				}
1864 				agdp = &(*agdp)->agd_next;
1865 			}
1866 
1867 			agd = *agdp;
1868 			*agdp = agd->agd_next;
1869 
1870 			for ( age_next = age , age_prev = NULL ; age_next ; age_prev = age, age = age_next ) {
1871 				age_next = age->age_next;
1872 
1873 				if( age->age_def == agd ) {
1874 					agf = age->age_filter;
1875 
1876 					ch_free( age->age_dn.bv_val );
1877 					ch_free( age->age_ndn.bv_val );
1878 
1879 					for ( agf_next = agf; agf_next ; agf = agf_next ) {
1880 						agf_next = agf->agf_next;
1881 						filter_free( agf->agf_filter );
1882 						ch_free( agf->agf_filterstr.bv_val );
1883 						ch_free( agf->agf_dn.bv_val );
1884 						ch_free( agf->agf_ndn.bv_val );
1885 						anlist_free( agf->agf_anlist, 1, NULL );
1886 						ch_free( agf );
1887 					}
1888 
1889 					ldap_pvt_thread_mutex_destroy( &age->age_mutex );
1890 					ch_free( age );
1891 
1892 					age = age_prev;
1893 
1894 					if( age_prev != NULL ) {
1895 						age_prev->age_next = age_next;
1896 					}
1897 				}
1898 			}
1899 
1900 			ch_free( agd );
1901 			agd = agi->agi_def;
1902 
1903 		}
1904 
1905 		return rc;
1906 	}
1907 
1908 	switch(c->type){
1909 	case AG_ATTRSET: {
1910 		autogroup_def_t		**agdp,
1911 					*agd_next = NULL;
1912 		ObjectClass		*oc = NULL;
1913 		AttributeDescription	*member_url_ad = NULL,
1914 					*member_ad = NULL;
1915 		const char		*text;
1916 
1917 
1918 		oc = oc_find( c->argv[ 1 ] );
1919 		if( oc == NULL ){
1920 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1921 				"\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1922 				"unable to find ObjectClass \"%s\"",
1923 				c->argv[ 1 ] );
1924 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1925 				c->log, c->cr_msg );
1926 			return 1;
1927 		}
1928 
1929 
1930 		rc = slap_str2ad( c->argv[ 2 ], &member_url_ad, &text );
1931 		if( rc != LDAP_SUCCESS ) {
1932 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1933 				"\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1934 				"unable to find AttributeDescription \"%s\"",
1935 				c->argv[ 2 ] );
1936 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1937 				c->log, c->cr_msg );
1938 			return 1;
1939 		}
1940 
1941 		if( !is_at_subtype( member_url_ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
1942 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1943 				"\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1944 				"AttributeDescription \"%s\" ",
1945 				"must be of a subtype \"labeledURI\"",
1946 				c->argv[ 2 ] );
1947 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1948 				c->log, c->cr_msg );
1949 			return 1;
1950 		}
1951 
1952 		rc = slap_str2ad( c->argv[3], &member_ad, &text );
1953 		if( rc != LDAP_SUCCESS ) {
1954 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1955 				"\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1956 				"unable to find AttributeDescription \"%s\"",
1957 				c->argv[ 3 ] );
1958 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1959 				c->log, c->cr_msg );
1960 			return 1;
1961 		}
1962 
1963 		for ( agdp = &agi->agi_def ; *agdp ; agdp = &(*agdp)->agd_next ) {
1964 			/* The same URL attribute / member attribute pair
1965 			* cannot be repeated */
1966 
1967 			if ( (*agdp)->agd_member_url_ad == member_url_ad && (*agdp)->agd_member_ad == member_ad ) {
1968 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
1969 					"\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1970 					"URL attributeDescription \"%s\" already mapped",
1971 					member_ad->ad_cname.bv_val );
1972 				Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1973 					c->log, c->cr_msg );
1974 /*				return 1; //warning*/
1975 			}
1976 		}
1977 
1978 		if ( c->valx > 0 ) {
1979 			int	i;
1980 
1981 			for ( i = 0, agdp = &agi->agi_def ;
1982 				i < c->valx; i++ )
1983 			{
1984 				if ( *agdp == NULL ) {
1985 					snprintf( c->cr_msg, sizeof( c->cr_msg ),
1986 						"\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1987 						"invalid index {%d}",
1988 						c->valx );
1989 					Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1990 						c->log, c->cr_msg );
1991 
1992 					return 1;
1993 				}
1994 				agdp = &(*agdp)->agd_next;
1995 			}
1996 			agd_next = *agdp;
1997 
1998 		} else {
1999 			for ( agdp = &agi->agi_def; *agdp;
2000 				agdp = &(*agdp)->agd_next )
2001 				/* goto last */;
2002 		}
2003 
2004 		*agdp = (autogroup_def_t *)ch_calloc( 1, sizeof(autogroup_info_t));
2005 
2006 		(*agdp)->agd_oc = oc;
2007 		(*agdp)->agd_member_url_ad = member_url_ad;
2008 		(*agdp)->agd_member_ad = member_ad;
2009 		(*agdp)->agd_next = agd_next;
2010 
2011 		} break;
2012 
2013 	case AG_MEMBER_OF_AD: {
2014 		AttributeDescription *memberof_ad = NULL;
2015 		const char     *text;
2016 
2017 		rc = slap_str2ad( c->argv[ 1 ], &memberof_ad, &text );
2018 		if( rc != LDAP_SUCCESS ) {
2019 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2020 				"\"autogroup-memberof-ad <memberof-ad>\": "
2021 				"unable to find AttributeDescription \"%s\"",
2022 				c->argv[ 1 ] );
2023 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
2024 				c->log, c->cr_msg );
2025 			return 1;
2026 		}
2027 
2028 		if ( !is_at_syntax( memberof_ad->ad_type, SLAPD_DN_SYNTAX )    /* e.g. "member" */
2029 		     && !is_at_syntax( memberof_ad->ad_type, SLAPD_NAMEUID_SYNTAX ) )  /* e.g. "uniqueMember" */
2030 		{
2031 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2032 				"memberof attribute=\"%s\" must either "
2033 				"have DN (%s) or nameUID (%s) syntax",
2034 				c->argv[ 1 ], SLAPD_DN_SYNTAX, SLAPD_NAMEUID_SYNTAX );
2035 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
2036 				c->log, c->cr_msg );
2037 			return 1;
2038 		}
2039 
2040 		agi->agi_memberof_ad = memberof_ad;
2041 
2042 		} break;
2043 
2044 	default:
2045 		rc = 1;
2046 		break;
2047 	}
2048 
2049 	return rc;
2050 }
2051 
2052 extern int slapMode;
2053 
2054 /*
2055 ** Do a search for all the groups in the
2056 ** database, and add them to out internal list.
2057 */
2058 static int
autogroup_db_open(BackendDB * be,ConfigReply * cr)2059 autogroup_db_open(
2060 	BackendDB	*be,
2061 	ConfigReply	*cr )
2062 {
2063 	slap_overinst			*on = (slap_overinst *) be->bd_info;
2064 	autogroup_info_t		*agi = on->on_bi.bi_private;
2065 	autogroup_def_t		*agd;
2066 	autogroup_sc_t		ags;
2067 	Operation		*op;
2068 	slap_callback		cb = { 0 };
2069 
2070 	void				*thrctx = ldap_pvt_thread_pool_context();
2071 	Connection			conn = { 0 };
2072 	OperationBuffer 	opbuf;
2073 
2074 	Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_open\n" );
2075 
2076 	if ( agi == NULL || !( slapMode & SLAP_SERVER_MODE )) {
2077 		return 0;
2078 	}
2079 
2080 	connection_fake_init2( &conn, &opbuf, thrctx, 0 );
2081 	op = &opbuf.ob_op;
2082 
2083 	op->ors_attrsonly = 0;
2084 	op->o_tag = LDAP_REQ_SEARCH;
2085 	op->o_dn = be->be_rootdn;
2086 	op->o_ndn = be->be_rootndn;
2087 
2088 	op->o_req_dn = be->be_suffix[0];
2089 	op->o_req_ndn = be->be_nsuffix[0];
2090 
2091 	op->ors_scope = LDAP_SCOPE_SUBTREE;
2092 	op->ors_deref = LDAP_DEREF_NEVER;
2093 	op->ors_limit = NULL;
2094 	op->ors_tlimit = SLAP_NO_LIMIT;
2095 	op->ors_slimit = SLAP_NO_LIMIT;
2096 	op->ors_attrs =  slap_anlist_no_attrs;
2097 	op->o_do_not_cache = 1;
2098 
2099 	op->o_bd = be;
2100 	op->o_bd->bd_info = (BackendInfo *)on->on_info;
2101 
2102 	ags.ags_info = agi;
2103 	cb.sc_private = &ags;
2104 	cb.sc_response = autogroup_group_add_cb;
2105 	cb.sc_cleanup = NULL;
2106 	cb.sc_next = NULL;
2107 
2108 	op->o_callback = &cb;
2109 
2110 	for (agd = agi->agi_def ; agd ; agd = agd->agd_next) {
2111 		SlapReply	rs = { REP_RESULT };
2112 
2113 		autogroup_build_def_filter(agd, op);
2114 
2115 		ags.ags_def = agd;
2116 
2117 		op->o_bd->be_search( op, &rs );
2118 
2119 		filter_free_x( op, op->ors_filter, 1 );
2120 		op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
2121 	}
2122 
2123 	if( ! agi->agi_memberof_ad ){
2124 		int			rc;
2125 		const char		*text = NULL;
2126 
2127 		rc = slap_str2ad( SLAPD_MEMBEROF_ATTR, &agi->agi_memberof_ad, &text );
2128 		if ( rc != LDAP_SUCCESS ) {
2129 			Debug( LDAP_DEBUG_ANY, "autogroup_db_open: "
2130 			"unable to find attribute=\"%s\": %s (%d)\n",
2131 			SLAPD_MEMBEROF_ATTR, text, rc );
2132 			return rc;
2133 		}
2134 	}
2135 
2136 	return 0;
2137 }
2138 
2139 static int
autogroup_db_close(BackendDB * be,ConfigReply * cr)2140 autogroup_db_close(
2141 	BackendDB	*be,
2142 	ConfigReply	*cr )
2143 {
2144 	slap_overinst			*on = (slap_overinst *) be->bd_info;
2145 
2146 	Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_close\n" );
2147 
2148 	if ( on->on_bi.bi_private ) {
2149 		autogroup_info_t		*agi = on->on_bi.bi_private;
2150 		autogroup_entry_t	*age = agi->agi_entry,
2151 					*age_next;
2152 		autogroup_filter_t	*agf, *agf_next;
2153 
2154 		for ( age_next = age; age_next; age = age_next ) {
2155 			age_next = age->age_next;
2156 
2157 			ch_free( age->age_dn.bv_val );
2158 			ch_free( age->age_ndn.bv_val );
2159 
2160 			agf = age->age_filter;
2161 
2162 			for ( agf_next = agf; agf_next; agf = agf_next ) {
2163 				agf_next = agf->agf_next;
2164 
2165 				filter_free( agf->agf_filter );
2166 				ch_free( agf->agf_filterstr.bv_val );
2167 				ch_free( agf->agf_dn.bv_val );
2168 				ch_free( agf->agf_ndn.bv_val );
2169 				anlist_free( agf->agf_anlist, 1, NULL );
2170 				ch_free( agf );
2171 			}
2172 
2173 			ldap_pvt_thread_mutex_destroy( &age->age_mutex );
2174 			ch_free( age );
2175 		}
2176 	}
2177 
2178 	return 0;
2179 }
2180 
2181 static int
autogroup_db_destroy(BackendDB * be,ConfigReply * cr)2182 autogroup_db_destroy(
2183 	BackendDB	*be,
2184 	ConfigReply	*cr )
2185 {
2186 	slap_overinst			*on = (slap_overinst *) be->bd_info;
2187 
2188 	Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_destroy\n" );
2189 
2190 	if ( on->on_bi.bi_private ) {
2191 		autogroup_info_t		*agi = on->on_bi.bi_private;
2192 		autogroup_def_t		*agd = agi->agi_def,
2193 					*agd_next;
2194 
2195 		for ( agd_next = agd; agd_next; agd = agd_next ) {
2196 			agd_next = agd->agd_next;
2197 
2198 			ch_free( agd );
2199 		}
2200 
2201 		ldap_pvt_thread_mutex_destroy( &agi->agi_mutex );
2202 		ch_free( agi );
2203 	}
2204 
2205 	return 0;
2206 }
2207 
2208 static
2209 int
autogroup_initialize(void)2210 autogroup_initialize(void)
2211 {
2212 	int		rc = 0;
2213 	autogroup.on_bi.bi_type = "autogroup";
2214 
2215 	autogroup.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
2216 	autogroup.on_bi.bi_db_open = autogroup_db_open;
2217 	autogroup.on_bi.bi_db_close = autogroup_db_close;
2218 	autogroup.on_bi.bi_db_destroy = autogroup_db_destroy;
2219 
2220 	autogroup.on_bi.bi_op_add = autogroup_add_entry;
2221 	autogroup.on_bi.bi_op_delete = autogroup_delete_entry;
2222 	autogroup.on_bi.bi_op_modify = autogroup_modify_entry;
2223 	autogroup.on_bi.bi_op_modrdn = autogroup_modrdn_entry;
2224 
2225 	autogroup.on_response = autogroup_response;
2226 
2227 	autogroup.on_bi.bi_cf_ocs = agocs;
2228 
2229 	rc = config_register_schema( agcfg, agocs );
2230 	if ( rc ) {
2231 		return rc;
2232 	}
2233 
2234 	return overlay_register( &autogroup );
2235 }
2236 
2237 int
init_module(int argc,char * argv[])2238 init_module( int argc, char *argv[] )
2239 {
2240 	return autogroup_initialize();
2241 }
2242