xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/overlays/memberof.c (revision b7b7574d3bf8eeb51a1fa3977b59142ec6434a55)
1 /*	$NetBSD: memberof.c,v 1.1.1.5 2014/05/28 09:58:52 tron Exp $	*/
2 
3 /* memberof.c - back-reference for group membership */
4 /* $OpenLDAP$ */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6  *
7  * Copyright 2005-2007 Pierangelo Masarati <ando@sys-net.it>
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted only as authorized by the OpenLDAP
12  * Public License.
13  *
14  * A copy of this license is available in the file LICENSE in the
15  * top-level directory of the distribution or, alternatively, at
16  * <http://www.OpenLDAP.org/license.html>.
17  */
18 /* ACKNOWLEDGMENTS:
19  * This work was initially developed by Pierangelo Masarati for inclusion
20  * in OpenLDAP Software, sponsored by SysNet s.r.l.
21  */
22 
23 #include "portable.h"
24 
25 #ifdef SLAPD_OVER_MEMBEROF
26 
27 #include <stdio.h>
28 
29 #include "ac/string.h"
30 #include "ac/socket.h"
31 
32 #include "slap.h"
33 #include "config.h"
34 #include "lutil.h"
35 
36 /*
37  *	Glossary:
38  *
39  *		GROUP		a group object (an entry with GROUP_OC
40  *				objectClass)
41  *		MEMBER		a member object (an entry whose DN is
42  *				listed as MEMBER_AT value of a GROUP)
43  *		GROUP_OC	the objectClass of the group object
44  *				(default: groupOfNames)
45  *		MEMBER_AT	the membership attribute, DN-valued;
46  *				note: nameAndOptionalUID is tolerated
47  *				as soon as the optionalUID is absent
48  *				(default: member)
49  *		MEMBER_OF	reverse membership attribute
50  *				(default: memberOf)
51  *
52  * 	- add:
53  *		- if the entry that is being added is a GROUP,
54  *		  the MEMBER_AT defined as values of the add operation
55  *		  get the MEMBER_OF value directly from the request.
56  *
57  *		  if configured to do so, the MEMBER objects do not exist,
58  *		  and no relax control is issued, either:
59  *			- fail
60  *			- drop non-existing members
61  *		  (by default: don't muck with values)
62  *
63  *		- if (configured to do so,) the referenced GROUP exists,
64  *		  the relax control is set and the user has
65  *		  "manage" privileges, allow to add MEMBER_OF values to
66  *		  generic entries.
67  *
68  *	- modify:
69  *		- if the entry being modified is a GROUP_OC and the
70  *		  MEMBER_AT attribute is modified, the MEMBER_OF value
71  *		  of the (existing) MEMBER_AT entries that are affected
72  *		  is modified according to the request:
73  *			- if a MEMBER is removed from the group,
74  *			  delete the corresponding MEMBER_OF
75  *			- if a MEMBER is added to a group,
76  *			  add the corresponding MEMBER_OF
77  *
78  *		  We need to determine, from the database, if it is
79  *		  a GROUP_OC, and we need to check, from the
80  *		  modification list, if the MEMBER_AT attribute is being
81  *		  affected, and what MEMBER_AT values are affected.
82  *
83  *		  if configured to do so, the entries corresponding to
84  *		  the MEMBER_AT values do not exist, and no relax control
85  *		  is issued, either:
86  *			- fail
87  *			- drop non-existing members
88  *		  (by default: don't muck with values)
89  *
90  *		- if configured to do so, the referenced GROUP exists,
91  *		  (the relax control is set) and the user has
92  *		  "manage" privileges, allow to add MEMBER_OF values to
93  *		  generic entries; the change is NOT automatically reflected
94  *		  in the MEMBER attribute of the GROUP referenced
95  *		  by the value of MEMBER_OF; a separate modification,
96  *		  with or without relax control, needs to be performed.
97  *
98  *	- modrdn:
99  *		- if the entry being renamed is a GROUP, the MEMBER_OF
100  *		  value of the (existing) MEMBER objects is modified
101  *		  accordingly based on the newDN of the GROUP.
102  *
103  *		  We need to determine, from the database, if it is
104  *		  a GROUP; the list of MEMBER objects is obtained from
105  *		  the database.
106  *
107  *		  Non-existing MEMBER objects are ignored, since the
108  *		  MEMBER_AT is not being addressed by the operation.
109  *
110  *		- if the entry being renamed has the MEMBER_OF attribute,
111  *		  the corresponding MEMBER value must be modified in the
112  *		  respective group entries.
113  *
114  *
115  *	- delete:
116  *		- if the entry being deleted is a GROUP, the (existing)
117  *		  MEMBER objects are modified accordingly; a copy of the
118  *		  values of the MEMBER_AT is saved and, if the delete
119  *		  succeeds, the MEMBER_OF value of the (existing) MEMBER
120  *		  objects is deleted.
121  *
122  *		  We need to determine, from the database, if it is
123  *		  a GROUP.
124  *
125  *		  Non-existing MEMBER objects are ignored, since the entry
126  *		  is being deleted.
127  *
128  *		- if the entry being deleted has the MEMBER_OF attribute,
129  *		  the corresponding value of the MEMBER_AT must be deleted
130  *		  from the respective GROUP entries.
131  */
132 
133 #define	SLAPD_MEMBEROF_ATTR	"memberOf"
134 
135 static slap_overinst		memberof;
136 
137 typedef struct memberof_t {
138 	struct berval		mo_dn;
139 	struct berval		mo_ndn;
140 
141 	ObjectClass		*mo_oc_group;
142 	AttributeDescription	*mo_ad_member;
143 	AttributeDescription	*mo_ad_memberof;
144 
145 	struct berval		mo_groupFilterstr;
146 	AttributeAssertion	mo_groupAVA;
147 	Filter			mo_groupFilter;
148 
149 	struct berval		mo_memberFilterstr;
150 	Filter			mo_memberFilter;
151 
152 	unsigned		mo_flags;
153 #define	MEMBEROF_NONE		0x00U
154 #define	MEMBEROF_FDANGLING_DROP	0x01U
155 #define	MEMBEROF_FDANGLING_ERROR	0x02U
156 #define	MEMBEROF_FDANGLING_MASK	(MEMBEROF_FDANGLING_DROP|MEMBEROF_FDANGLING_ERROR)
157 #define	MEMBEROF_FREFINT	0x04U
158 #define	MEMBEROF_FREVERSE	0x08U
159 
160 	ber_int_t		mo_dangling_err;
161 
162 #define MEMBEROF_CHK(mo,f) \
163 	(((mo)->mo_flags & (f)) == (f))
164 #define MEMBEROF_DANGLING_CHECK(mo) \
165 	((mo)->mo_flags & MEMBEROF_FDANGLING_MASK)
166 #define MEMBEROF_DANGLING_DROP(mo) \
167 	MEMBEROF_CHK((mo),MEMBEROF_FDANGLING_DROP)
168 #define MEMBEROF_DANGLING_ERROR(mo) \
169 	MEMBEROF_CHK((mo),MEMBEROF_FDANGLING_ERROR)
170 #define MEMBEROF_REFINT(mo) \
171 	MEMBEROF_CHK((mo),MEMBEROF_FREFINT)
172 #define MEMBEROF_REVERSE(mo) \
173 	MEMBEROF_CHK((mo),MEMBEROF_FREVERSE)
174 } memberof_t;
175 
176 typedef enum memberof_is_t {
177 	MEMBEROF_IS_NONE = 0x00,
178 	MEMBEROF_IS_GROUP = 0x01,
179 	MEMBEROF_IS_MEMBER = 0x02,
180 	MEMBEROF_IS_BOTH = (MEMBEROF_IS_GROUP|MEMBEROF_IS_MEMBER)
181 } memberof_is_t;
182 
183 typedef struct memberof_cookie_t {
184 	AttributeDescription	*ad;
185 	BerVarray		vals;
186 	int			foundit;
187 } memberof_cookie_t;
188 
189 typedef struct memberof_cbinfo_t {
190 	slap_overinst *on;
191 	BerVarray member;
192 	BerVarray memberof;
193 	memberof_is_t what;
194 } memberof_cbinfo_t;
195 
196 static int
197 memberof_isGroupOrMember_cb( Operation *op, SlapReply *rs )
198 {
199 	if ( rs->sr_type == REP_SEARCH ) {
200 		memberof_cookie_t	*mc;
201 
202 		mc = (memberof_cookie_t *)op->o_callback->sc_private;
203 		mc->foundit = 1;
204 	}
205 
206 	return 0;
207 }
208 
209 /*
210  * callback for internal search that saves the member attribute values
211  * of groups being deleted.
212  */
213 static int
214 memberof_saveMember_cb( Operation *op, SlapReply *rs )
215 {
216 	if ( rs->sr_type == REP_SEARCH ) {
217 		memberof_cookie_t	*mc;
218 		Attribute		*a;
219 
220 		mc = (memberof_cookie_t *)op->o_callback->sc_private;
221 		mc->foundit = 1;
222 
223 		assert( rs->sr_entry != NULL );
224 		assert( rs->sr_entry->e_attrs != NULL );
225 
226 		a = attr_find( rs->sr_entry->e_attrs, mc->ad );
227 		if ( a != NULL ) {
228 			ber_bvarray_dup_x( &mc->vals, a->a_nvals, op->o_tmpmemctx );
229 
230 			assert( attr_find( a->a_next, mc->ad ) == NULL );
231 		}
232 	}
233 
234 	return 0;
235 }
236 
237 /*
238  * the delete hook performs an internal search that saves the member
239  * attribute values of groups being deleted.
240  */
241 static int
242 memberof_isGroupOrMember( Operation *op, memberof_cbinfo_t *mci )
243 {
244 	slap_overinst		*on = mci->on;
245 	memberof_t		*mo = (memberof_t *)on->on_bi.bi_private;
246 
247 	Operation		op2 = *op;
248 	slap_callback		cb = { 0 };
249 	BackendInfo	*bi = op->o_bd->bd_info;
250 	AttributeName		an[ 2 ];
251 
252 	memberof_is_t		iswhat = MEMBEROF_IS_NONE;
253 	memberof_cookie_t	mc;
254 
255 	assert( mci->what != MEMBEROF_IS_NONE );
256 
257 	cb.sc_private = &mc;
258 	if ( op->o_tag == LDAP_REQ_DELETE ) {
259 		cb.sc_response = memberof_saveMember_cb;
260 
261 	} else {
262 		cb.sc_response = memberof_isGroupOrMember_cb;
263 	}
264 
265 	op2.o_tag = LDAP_REQ_SEARCH;
266 	op2.o_callback = &cb;
267 	op2.o_dn = op->o_bd->be_rootdn;
268 	op2.o_ndn = op->o_bd->be_rootndn;
269 
270 	op2.ors_scope = LDAP_SCOPE_BASE;
271 	op2.ors_deref = LDAP_DEREF_NEVER;
272 	BER_BVZERO( &an[ 1 ].an_name );
273 	op2.ors_attrs = an;
274 	op2.ors_attrsonly = 0;
275 	op2.ors_limit = NULL;
276 	op2.ors_slimit = 1;
277 	op2.ors_tlimit = SLAP_NO_LIMIT;
278 
279 	if ( mci->what & MEMBEROF_IS_GROUP ) {
280 		SlapReply	rs2 = { REP_RESULT };
281 
282 		mc.ad = mo->mo_ad_member;
283 		mc.foundit = 0;
284 		mc.vals = NULL;
285 		an[ 0 ].an_desc = mo->mo_ad_member;
286 		an[ 0 ].an_name = an[ 0 ].an_desc->ad_cname;
287 		op2.ors_filterstr = mo->mo_groupFilterstr;
288 		op2.ors_filter = &mo->mo_groupFilter;
289 
290 		op2.o_bd->bd_info = (BackendInfo *)on->on_info;
291 		(void)op->o_bd->be_search( &op2, &rs2 );
292 		op2.o_bd->bd_info = bi;
293 
294 		if ( mc.foundit ) {
295 			iswhat |= MEMBEROF_IS_GROUP;
296 			if ( mc.vals ) mci->member = mc.vals;
297 
298 		}
299 	}
300 
301 	if ( mci->what & MEMBEROF_IS_MEMBER ) {
302 		SlapReply	rs2 = { REP_RESULT };
303 
304 		mc.ad = mo->mo_ad_memberof;
305 		mc.foundit = 0;
306 		mc.vals = NULL;
307 		an[ 0 ].an_desc = mo->mo_ad_memberof;
308 		an[ 0 ].an_name = an[ 0 ].an_desc->ad_cname;
309 		op2.ors_filterstr = mo->mo_memberFilterstr;
310 		op2.ors_filter = &mo->mo_memberFilter;
311 
312 		op2.o_bd->bd_info = (BackendInfo *)on->on_info;
313 		(void)op->o_bd->be_search( &op2, &rs2 );
314 		op2.o_bd->bd_info = bi;
315 
316 		if ( mc.foundit ) {
317 			iswhat |= MEMBEROF_IS_MEMBER;
318 			if ( mc.vals ) mci->memberof = mc.vals;
319 
320 		}
321 	}
322 
323 	mci->what = iswhat;
324 
325 	return LDAP_SUCCESS;
326 }
327 
328 /*
329  * response callback that adds memberof values when a group is modified.
330  */
331 static void
332 memberof_value_modify(
333 	Operation		*op,
334 	struct berval		*ndn,
335 	AttributeDescription	*ad,
336 	struct berval		*old_dn,
337 	struct berval		*old_ndn,
338 	struct berval		*new_dn,
339 	struct berval		*new_ndn )
340 {
341 	memberof_cbinfo_t *mci = op->o_callback->sc_private;
342 	slap_overinst	*on = mci->on;
343 	memberof_t	*mo = (memberof_t *)on->on_bi.bi_private;
344 
345 	Operation	op2 = *op;
346 	unsigned long opid = op->o_opid;
347 	SlapReply	rs2 = { REP_RESULT };
348 	slap_callback	cb = { NULL, slap_null_cb, NULL, NULL };
349 	Modifications	mod[ 2 ] = { { { 0 } } }, *ml;
350 	struct berval	values[ 4 ], nvalues[ 4 ];
351 	int		mcnt = 0;
352 
353 	op2.o_tag = LDAP_REQ_MODIFY;
354 
355 	op2.o_req_dn = *ndn;
356 	op2.o_req_ndn = *ndn;
357 
358 	op2.o_callback = &cb;
359 	op2.o_dn = op->o_bd->be_rootdn;
360 	op2.o_ndn = op->o_bd->be_rootndn;
361 	op2.orm_modlist = NULL;
362 
363 	/* Internal ops, never replicate these */
364 	op2.o_opid = 0;		/* shared with op, saved above */
365 	op2.orm_no_opattrs = 1;
366 	op2.o_dont_replicate = 1;
367 
368 	if ( !BER_BVISNULL( &mo->mo_ndn ) ) {
369 		ml = &mod[ mcnt ];
370 		ml->sml_numvals = 1;
371 		ml->sml_values = &values[ 0 ];
372 		ml->sml_values[ 0 ] = mo->mo_dn;
373 		BER_BVZERO( &ml->sml_values[ 1 ] );
374 		ml->sml_nvalues = &nvalues[ 0 ];
375 		ml->sml_nvalues[ 0 ] = mo->mo_ndn;
376 		BER_BVZERO( &ml->sml_nvalues[ 1 ] );
377 		ml->sml_desc = slap_schema.si_ad_modifiersName;
378 		ml->sml_type = ml->sml_desc->ad_cname;
379 		ml->sml_op = LDAP_MOD_REPLACE;
380 		ml->sml_flags = SLAP_MOD_INTERNAL;
381 		ml->sml_next = op2.orm_modlist;
382 		op2.orm_modlist = ml;
383 
384 		mcnt++;
385 	}
386 
387 	ml = &mod[ mcnt ];
388 	ml->sml_numvals = 1;
389 	ml->sml_values = &values[ 2 ];
390 	BER_BVZERO( &ml->sml_values[ 1 ] );
391 	ml->sml_nvalues = &nvalues[ 2 ];
392 	BER_BVZERO( &ml->sml_nvalues[ 1 ] );
393 	ml->sml_desc = ad;
394 	ml->sml_type = ml->sml_desc->ad_cname;
395 	ml->sml_flags = SLAP_MOD_INTERNAL;
396 	ml->sml_next = op2.orm_modlist;
397 	op2.orm_modlist = ml;
398 
399 	if ( new_ndn != NULL ) {
400 		BackendInfo *bi = op2.o_bd->bd_info;
401 		OpExtra	oex;
402 
403 		assert( !BER_BVISNULL( new_dn ) );
404 		assert( !BER_BVISNULL( new_ndn ) );
405 
406 		ml = &mod[ mcnt ];
407 		ml->sml_op = LDAP_MOD_ADD;
408 
409 		ml->sml_values[ 0 ] = *new_dn;
410 		ml->sml_nvalues[ 0 ] = *new_ndn;
411 
412 		oex.oe_key = (void *)&memberof;
413 		LDAP_SLIST_INSERT_HEAD(&op2.o_extra, &oex, oe_next);
414 		op2.o_bd->bd_info = (BackendInfo *)on->on_info;
415 		(void)op->o_bd->be_modify( &op2, &rs2 );
416 		op2.o_bd->bd_info = bi;
417 		LDAP_SLIST_REMOVE(&op2.o_extra, &oex, OpExtra, oe_next);
418 		if ( rs2.sr_err != LDAP_SUCCESS ) {
419 			char buf[ SLAP_TEXT_BUFLEN ];
420 			snprintf( buf, sizeof( buf ),
421 				"memberof_value_modify DN=\"%s\" add %s=\"%s\" failed err=%d",
422 				op2.o_req_dn.bv_val, ad->ad_cname.bv_val, new_dn->bv_val, rs2.sr_err );
423 			Debug( LDAP_DEBUG_ANY, "%s: %s\n",
424 				op->o_log_prefix, buf, 0 );
425 		}
426 
427 		assert( op2.orm_modlist == &mod[ mcnt ] );
428 		assert( mcnt == 0 || op2.orm_modlist->sml_next == &mod[ 0 ] );
429 		ml = op2.orm_modlist->sml_next;
430 		if ( mcnt == 1 ) {
431 			assert( ml == &mod[ 0 ] );
432 			ml = ml->sml_next;
433 		}
434 		if ( ml != NULL ) {
435 			slap_mods_free( ml, 1 );
436 		}
437 
438 		mod[ 0 ].sml_next = NULL;
439 	}
440 
441 	if ( old_ndn != NULL ) {
442 		BackendInfo *bi = op2.o_bd->bd_info;
443 		OpExtra	oex;
444 
445 		assert( !BER_BVISNULL( old_dn ) );
446 		assert( !BER_BVISNULL( old_ndn ) );
447 
448 		ml = &mod[ mcnt ];
449 		ml->sml_op = LDAP_MOD_DELETE;
450 
451 		ml->sml_values[ 0 ] = *old_dn;
452 		ml->sml_nvalues[ 0 ] = *old_ndn;
453 
454 		oex.oe_key = (void *)&memberof;
455 		LDAP_SLIST_INSERT_HEAD(&op2.o_extra, &oex, oe_next);
456 		op2.o_bd->bd_info = (BackendInfo *)on->on_info;
457 		(void)op->o_bd->be_modify( &op2, &rs2 );
458 		op2.o_bd->bd_info = bi;
459 		LDAP_SLIST_REMOVE(&op2.o_extra, &oex, OpExtra, oe_next);
460 		if ( rs2.sr_err != LDAP_SUCCESS ) {
461 			char buf[ SLAP_TEXT_BUFLEN ];
462 			snprintf( buf, sizeof( buf ),
463 				"memberof_value_modify DN=\"%s\" delete %s=\"%s\" failed err=%d",
464 				op2.o_req_dn.bv_val, ad->ad_cname.bv_val, old_dn->bv_val, rs2.sr_err );
465 			Debug( LDAP_DEBUG_ANY, "%s: %s\n",
466 				op->o_log_prefix, buf, 0 );
467 		}
468 
469 		assert( op2.orm_modlist == &mod[ mcnt ] );
470 		ml = op2.orm_modlist->sml_next;
471 		if ( mcnt == 1 ) {
472 			assert( ml == &mod[ 0 ] );
473 			ml = ml->sml_next;
474 		}
475 		if ( ml != NULL ) {
476 			slap_mods_free( ml, 1 );
477 		}
478 	}
479 	/* restore original opid */
480 	op->o_opid = opid;
481 
482 	/* FIXME: if old_group_ndn doesn't exist, both delete __and__
483 	 * add will fail; better split in two operations, although
484 	 * not optimal in terms of performance.  At least it would
485 	 * move towards self-repairing capabilities. */
486 }
487 
488 static int
489 memberof_cleanup( Operation *op, SlapReply *rs )
490 {
491 	slap_callback *sc = op->o_callback;
492 	memberof_cbinfo_t *mci = sc->sc_private;
493 
494 	op->o_callback = sc->sc_next;
495 	if ( mci->memberof )
496 		ber_bvarray_free_x( mci->memberof, op->o_tmpmemctx );
497 	if ( mci->member )
498 		ber_bvarray_free_x( mci->member, op->o_tmpmemctx );
499 	op->o_tmpfree( sc, op->o_tmpmemctx );
500 	return 0;
501 }
502 
503 static int memberof_res_add( Operation *op, SlapReply *rs );
504 static int memberof_res_delete( Operation *op, SlapReply *rs );
505 static int memberof_res_modify( Operation *op, SlapReply *rs );
506 static int memberof_res_modrdn( Operation *op, SlapReply *rs );
507 
508 static int
509 memberof_op_add( Operation *op, SlapReply *rs )
510 {
511 	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
512 	memberof_t	*mo = (memberof_t *)on->on_bi.bi_private;
513 
514 	Attribute	**ap, **map = NULL;
515 	int		rc = SLAP_CB_CONTINUE;
516 	int		i;
517 	struct berval	save_dn, save_ndn;
518 	slap_callback *sc;
519 	memberof_cbinfo_t *mci;
520 	OpExtra		*oex;
521 
522 	LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
523 		if ( oex->oe_key == (void *)&memberof )
524 			return SLAP_CB_CONTINUE;
525 	}
526 
527 	if ( op->ora_e->e_attrs == NULL ) {
528 		/* FIXME: global overlay; need to deal with */
529 		Debug( LDAP_DEBUG_ANY, "%s: memberof_op_add(\"%s\"): "
530 			"consistency checks not implemented when overlay "
531 			"is instantiated as global.\n",
532 			op->o_log_prefix, op->o_req_dn.bv_val, 0 );
533 		return SLAP_CB_CONTINUE;
534 	}
535 
536 	if ( MEMBEROF_REVERSE( mo ) ) {
537 		for ( ap = &op->ora_e->e_attrs; *ap; ap = &(*ap)->a_next ) {
538 			Attribute	*a = *ap;
539 
540 			if ( a->a_desc == mo->mo_ad_memberof ) {
541 				map = ap;
542 				break;
543 			}
544 		}
545 	}
546 
547 	save_dn = op->o_dn;
548 	save_ndn = op->o_ndn;
549 
550 	if ( MEMBEROF_DANGLING_CHECK( mo )
551 			&& !get_relax( op )
552 			&& is_entry_objectclass_or_sub( op->ora_e, mo->mo_oc_group ) )
553 	{
554 		op->o_dn = op->o_bd->be_rootdn;
555 		op->o_ndn = op->o_bd->be_rootndn;
556 		op->o_bd->bd_info = (BackendInfo *)on->on_info;
557 
558 		for ( ap = &op->ora_e->e_attrs; *ap; ) {
559 			Attribute	*a = *ap;
560 
561 			if ( !is_ad_subtype( a->a_desc, mo->mo_ad_member ) ) {
562 				ap = &a->a_next;
563 				continue;
564 			}
565 
566 			assert( a->a_nvals != NULL );
567 
568 			for ( i = 0; !BER_BVISNULL( &a->a_nvals[ i ] ); i++ ) {
569 				Entry		*e = NULL;
570 
571 				/* ITS#6670 Ignore member pointing to this entry */
572 				if ( dn_match( &a->a_nvals[i], &save_ndn ))
573 					continue;
574 
575 				rc = be_entry_get_rw( op, &a->a_nvals[ i ],
576 						NULL, NULL, 0, &e );
577 				if ( rc == LDAP_SUCCESS ) {
578 					be_entry_release_r( op, e );
579 					continue;
580 				}
581 
582 				if ( MEMBEROF_DANGLING_ERROR( mo ) ) {
583 					rc = rs->sr_err = mo->mo_dangling_err;
584 					rs->sr_text = "adding non-existing object "
585 						"as group member";
586 					send_ldap_result( op, rs );
587 					goto done;
588 				}
589 
590 				if ( MEMBEROF_DANGLING_DROP( mo ) ) {
591 					int	j;
592 
593 					Debug( LDAP_DEBUG_ANY, "%s: memberof_op_add(\"%s\"): "
594 						"member=\"%s\" does not exist (stripping...)\n",
595 						op->o_log_prefix, op->ora_e->e_name.bv_val,
596 						a->a_vals[ i ].bv_val );
597 
598 					for ( j = i + 1; !BER_BVISNULL( &a->a_nvals[ j ] ); j++ );
599 					ber_memfree( a->a_vals[ i ].bv_val );
600 					BER_BVZERO( &a->a_vals[ i ] );
601 					if ( a->a_nvals != a->a_vals ) {
602 						ber_memfree( a->a_nvals[ i ].bv_val );
603 						BER_BVZERO( &a->a_nvals[ i ] );
604 					}
605 					if ( j - i == 1 ) {
606 						break;
607 					}
608 
609 					AC_MEMCPY( &a->a_vals[ i ], &a->a_vals[ i + 1 ],
610 						sizeof( struct berval ) * ( j - i ) );
611 					if ( a->a_nvals != a->a_vals ) {
612 						AC_MEMCPY( &a->a_nvals[ i ], &a->a_nvals[ i + 1 ],
613 							sizeof( struct berval ) * ( j - i ) );
614 					}
615 					i--;
616 					a->a_numvals--;
617 				}
618 			}
619 
620 			/* If all values have been removed,
621 			 * remove the attribute itself. */
622 			if ( BER_BVISNULL( &a->a_nvals[ 0 ] ) ) {
623 				*ap = a->a_next;
624 				attr_free( a );
625 
626 			} else {
627 				ap = &a->a_next;
628 			}
629 		}
630 		op->o_dn = save_dn;
631 		op->o_ndn = save_ndn;
632 		op->o_bd->bd_info = (BackendInfo *)on;
633 	}
634 
635 	if ( map != NULL ) {
636 		Attribute		*a = *map;
637 		AccessControlState	acl_state = ACL_STATE_INIT;
638 
639 		for ( i = 0; !BER_BVISNULL( &a->a_nvals[ i ] ); i++ ) {
640 			Entry		*e;
641 
642 			op->o_bd->bd_info = (BackendInfo *)on->on_info;
643 			/* access is checked with the original identity */
644 			rc = access_allowed( op, op->ora_e, mo->mo_ad_memberof,
645 					&a->a_nvals[ i ], ACL_WADD,
646 					&acl_state );
647 			if ( rc == 0 ) {
648 				rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
649 				rs->sr_text = NULL;
650 				send_ldap_result( op, rs );
651 				goto done;
652 			}
653 			/* ITS#6670 Ignore member pointing to this entry */
654 			if ( dn_match( &a->a_nvals[i], &save_ndn ))
655 				continue;
656 
657 			rc = be_entry_get_rw( op, &a->a_nvals[ i ],
658 					NULL, NULL, 0, &e );
659 			op->o_bd->bd_info = (BackendInfo *)on;
660 			if ( rc != LDAP_SUCCESS ) {
661 				if ( get_relax( op ) ) {
662 					continue;
663 				}
664 
665 				if ( MEMBEROF_DANGLING_ERROR( mo ) ) {
666 					rc = rs->sr_err = mo->mo_dangling_err;
667 					rs->sr_text = "adding non-existing object "
668 						"as memberof";
669 					send_ldap_result( op, rs );
670 					goto done;
671 				}
672 
673 				if ( MEMBEROF_DANGLING_DROP( mo ) ) {
674 					int	j;
675 
676 					Debug( LDAP_DEBUG_ANY, "%s: memberof_op_add(\"%s\"): "
677 						"memberof=\"%s\" does not exist (stripping...)\n",
678 						op->o_log_prefix, op->ora_e->e_name.bv_val,
679 						a->a_nvals[ i ].bv_val );
680 
681 					for ( j = i + 1; !BER_BVISNULL( &a->a_nvals[ j ] ); j++ );
682 					ber_memfree( a->a_vals[ i ].bv_val );
683 					BER_BVZERO( &a->a_vals[ i ] );
684 					if ( a->a_nvals != a->a_vals ) {
685 						ber_memfree( a->a_nvals[ i ].bv_val );
686 						BER_BVZERO( &a->a_nvals[ i ] );
687 					}
688 					if ( j - i == 1 ) {
689 						break;
690 					}
691 
692 					AC_MEMCPY( &a->a_vals[ i ], &a->a_vals[ i + 1 ],
693 						sizeof( struct berval ) * ( j - i ) );
694 					if ( a->a_nvals != a->a_vals ) {
695 						AC_MEMCPY( &a->a_nvals[ i ], &a->a_nvals[ i + 1 ],
696 							sizeof( struct berval ) * ( j - i ) );
697 					}
698 					i--;
699 				}
700 
701 				continue;
702 			}
703 
704 			/* access is checked with the original identity */
705 			op->o_bd->bd_info = (BackendInfo *)on->on_info;
706 			rc = access_allowed( op, e, mo->mo_ad_member,
707 					&op->o_req_ndn, ACL_WADD, NULL );
708 			be_entry_release_r( op, e );
709 			op->o_bd->bd_info = (BackendInfo *)on;
710 
711 			if ( !rc ) {
712 				rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
713 				rs->sr_text = "insufficient access to object referenced by memberof";
714 				send_ldap_result( op, rs );
715 				goto done;
716 			}
717 		}
718 
719 		if ( BER_BVISNULL( &a->a_nvals[ 0 ] ) ) {
720 			*map = a->a_next;
721 			attr_free( a );
722 		}
723 	}
724 
725 	rc = SLAP_CB_CONTINUE;
726 
727 	sc = op->o_tmpalloc( sizeof(slap_callback)+sizeof(*mci), op->o_tmpmemctx );
728 	sc->sc_private = sc+1;
729 	sc->sc_response = memberof_res_add;
730 	sc->sc_cleanup = memberof_cleanup;
731 	mci = sc->sc_private;
732 	mci->on = on;
733 	mci->member = NULL;
734 	mci->memberof = NULL;
735 	sc->sc_next = op->o_callback;
736 	op->o_callback = sc;
737 
738 done:;
739 	op->o_dn = save_dn;
740 	op->o_ndn = save_ndn;
741 	op->o_bd->bd_info = (BackendInfo *)on;
742 
743 	return rc;
744 }
745 
746 static int
747 memberof_op_delete( Operation *op, SlapReply *rs )
748 {
749 	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
750 	memberof_t	*mo = (memberof_t *)on->on_bi.bi_private;
751 
752 	slap_callback *sc;
753 	memberof_cbinfo_t *mci;
754 	OpExtra		*oex;
755 
756 	LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
757 		if ( oex->oe_key == (void *)&memberof )
758 			return SLAP_CB_CONTINUE;
759 	}
760 
761 	sc = op->o_tmpalloc( sizeof(slap_callback)+sizeof(*mci), op->o_tmpmemctx );
762 	sc->sc_private = sc+1;
763 	sc->sc_response = memberof_res_delete;
764 	sc->sc_cleanup = memberof_cleanup;
765 	mci = sc->sc_private;
766 	mci->on = on;
767 	mci->member = NULL;
768 	mci->memberof = NULL;
769 	mci->what = MEMBEROF_IS_GROUP;
770 	if ( MEMBEROF_REFINT( mo ) ) {
771 		mci->what = MEMBEROF_IS_BOTH;
772 	}
773 
774 	memberof_isGroupOrMember( op, mci );
775 
776 	sc->sc_next = op->o_callback;
777 	op->o_callback = sc;
778 
779 	return SLAP_CB_CONTINUE;
780 }
781 
782 static int
783 memberof_op_modify( Operation *op, SlapReply *rs )
784 {
785 	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
786 	memberof_t	*mo = (memberof_t *)on->on_bi.bi_private;
787 
788 	Modifications	**mlp, **mmlp = NULL;
789 	int		rc = SLAP_CB_CONTINUE, save_member = 0;
790 	struct berval	save_dn, save_ndn;
791 	slap_callback *sc;
792 	memberof_cbinfo_t *mci, mcis;
793 	OpExtra		*oex;
794 
795 	LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
796 		if ( oex->oe_key == (void *)&memberof )
797 			return SLAP_CB_CONTINUE;
798 	}
799 
800 	if ( MEMBEROF_REVERSE( mo ) ) {
801 		for ( mlp = &op->orm_modlist; *mlp; mlp = &(*mlp)->sml_next ) {
802 			Modifications	*ml = *mlp;
803 
804 			if ( ml->sml_desc == mo->mo_ad_memberof ) {
805 				mmlp = mlp;
806 				break;
807 			}
808 		}
809 	}
810 
811 	save_dn = op->o_dn;
812 	save_ndn = op->o_ndn;
813 	mcis.on = on;
814 	mcis.what = MEMBEROF_IS_GROUP;
815 
816 	if ( memberof_isGroupOrMember( op, &mcis ) == LDAP_SUCCESS
817 		&& ( mcis.what & MEMBEROF_IS_GROUP ) )
818 	{
819 		Modifications *ml;
820 
821 		for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) {
822 			if ( ml->sml_desc == mo->mo_ad_member ) {
823 				switch ( ml->sml_op ) {
824 				case LDAP_MOD_DELETE:
825 				case LDAP_MOD_REPLACE:
826 				case SLAP_MOD_SOFTDEL: /* ITS#7487: can be used by syncrepl (in mirror mode?) */
827 					save_member = 1;
828 					break;
829 				}
830 			}
831 		}
832 
833 
834 		if ( MEMBEROF_DANGLING_CHECK( mo )
835 				&& !get_relax( op ) )
836 		{
837 			op->o_dn = op->o_bd->be_rootdn;
838 			op->o_ndn = op->o_bd->be_rootndn;
839 			op->o_bd->bd_info = (BackendInfo *)on->on_info;
840 
841 			assert( op->orm_modlist != NULL );
842 
843 			for ( mlp = &op->orm_modlist; *mlp; ) {
844 				Modifications	*ml = *mlp;
845 				int		i;
846 
847 				if ( !is_ad_subtype( ml->sml_desc, mo->mo_ad_member ) ) {
848 					mlp = &ml->sml_next;
849 					continue;
850 				}
851 
852 				switch ( ml->sml_op ) {
853 				case LDAP_MOD_DELETE:
854 				case SLAP_MOD_SOFTDEL: /* ITS#7487: can be used by syncrepl (in mirror mode?) */
855 					/* we don't care about cancellations: if the value
856 					 * exists, fine; if it doesn't, we let the underlying
857 					 * database fail as appropriate; */
858 					mlp = &ml->sml_next;
859 					break;
860 
861 				case LDAP_MOD_REPLACE:
862  					/* Handle this just like a delete (see above) */
863  					if ( !ml->sml_values ) {
864  						mlp = &ml->sml_next;
865  						break;
866  					}
867 
868 				case LDAP_MOD_ADD:
869 				case SLAP_MOD_SOFTADD: /* ITS#7487 */
870 				case SLAP_MOD_ADD_IF_NOT_PRESENT: /* ITS#7487 */
871 					/* NOTE: right now, the attributeType we use
872 					 * for member must have a normalized value */
873 					assert( ml->sml_nvalues != NULL );
874 
875 					for ( i = 0; !BER_BVISNULL( &ml->sml_nvalues[ i ] ); i++ ) {
876 						int		rc;
877 						Entry		*e;
878 
879 						/* ITS#6670 Ignore member pointing to this entry */
880 						if ( dn_match( &ml->sml_nvalues[i], &save_ndn ))
881 							continue;
882 
883 						if ( be_entry_get_rw( op, &ml->sml_nvalues[ i ],
884 								NULL, NULL, 0, &e ) == LDAP_SUCCESS )
885 						{
886 							be_entry_release_r( op, e );
887 							continue;
888 						}
889 
890 						if ( MEMBEROF_DANGLING_ERROR( mo ) ) {
891 							rc = rs->sr_err = mo->mo_dangling_err;
892 							rs->sr_text = "adding non-existing object "
893 								"as group member";
894 							send_ldap_result( op, rs );
895 							goto done;
896 						}
897 
898 						if ( MEMBEROF_DANGLING_DROP( mo ) ) {
899 							int	j;
900 
901 							Debug( LDAP_DEBUG_ANY, "%s: memberof_op_modify(\"%s\"): "
902 								"member=\"%s\" does not exist (stripping...)\n",
903 								op->o_log_prefix, op->o_req_dn.bv_val,
904 								ml->sml_nvalues[ i ].bv_val );
905 
906 							for ( j = i + 1; !BER_BVISNULL( &ml->sml_nvalues[ j ] ); j++ );
907 							ber_memfree( ml->sml_values[ i ].bv_val );
908 							BER_BVZERO( &ml->sml_values[ i ] );
909 							ber_memfree( ml->sml_nvalues[ i ].bv_val );
910 							BER_BVZERO( &ml->sml_nvalues[ i ] );
911 							ml->sml_numvals--;
912 							if ( j - i == 1 ) {
913 								break;
914 							}
915 
916 							AC_MEMCPY( &ml->sml_values[ i ], &ml->sml_values[ i + 1 ],
917 								sizeof( struct berval ) * ( j - i ) );
918 							AC_MEMCPY( &ml->sml_nvalues[ i ], &ml->sml_nvalues[ i + 1 ],
919 								sizeof( struct berval ) * ( j - i ) );
920 							i--;
921 						}
922 					}
923 
924 					if ( BER_BVISNULL( &ml->sml_nvalues[ 0 ] ) ) {
925 						*mlp = ml->sml_next;
926 						slap_mod_free( &ml->sml_mod, 0 );
927 						free( ml );
928 
929 					} else {
930 						mlp = &ml->sml_next;
931 					}
932 
933 					break;
934 
935 				default:
936 					assert( 0 );
937 				}
938 			}
939 		}
940 	}
941 
942 	if ( mmlp != NULL ) {
943 		Modifications	*ml = *mmlp;
944 		int		i;
945 		Entry		*target;
946 
947 		op->o_bd->bd_info = (BackendInfo *)on->on_info;
948 		rc = be_entry_get_rw( op, &op->o_req_ndn,
949 				NULL, NULL, 0, &target );
950 		op->o_bd->bd_info = (BackendInfo *)on;
951 		if ( rc != LDAP_SUCCESS ) {
952 			rc = rs->sr_err = LDAP_NO_SUCH_OBJECT;
953 			send_ldap_result( op, rs );
954 			goto done;
955 		}
956 
957 		switch ( ml->sml_op ) {
958 		case LDAP_MOD_DELETE:
959 		case SLAP_MOD_SOFTDEL: /* ITS#7487: can be used by syncrepl (in mirror mode?) */
960 			if ( ml->sml_nvalues != NULL ) {
961 				AccessControlState	acl_state = ACL_STATE_INIT;
962 
963 				for ( i = 0; !BER_BVISNULL( &ml->sml_nvalues[ i ] ); i++ ) {
964 					Entry		*e;
965 
966 					op->o_bd->bd_info = (BackendInfo *)on->on_info;
967 					/* access is checked with the original identity */
968 					rc = access_allowed( op, target,
969 							mo->mo_ad_memberof,
970 							&ml->sml_nvalues[ i ],
971 							ACL_WDEL,
972 							&acl_state );
973 					if ( rc == 0 ) {
974 						rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
975 						rs->sr_text = NULL;
976 						send_ldap_result( op, rs );
977 						goto done2;
978 					}
979 
980 					rc = be_entry_get_rw( op, &ml->sml_nvalues[ i ],
981 							NULL, NULL, 0, &e );
982 					op->o_bd->bd_info = (BackendInfo *)on;
983 					if ( rc != LDAP_SUCCESS ) {
984 						if ( get_relax( op ) ) {
985 							continue;
986 						}
987 
988 						if ( MEMBEROF_DANGLING_ERROR( mo ) ) {
989 							rc = rs->sr_err = mo->mo_dangling_err;
990 							rs->sr_text = "deleting non-existing object "
991 								"as memberof";
992 							send_ldap_result( op, rs );
993 							goto done2;
994 						}
995 
996 						if ( MEMBEROF_DANGLING_DROP( mo ) ) {
997 							int	j;
998 
999 							Debug( LDAP_DEBUG_ANY, "%s: memberof_op_modify(\"%s\"): "
1000 								"memberof=\"%s\" does not exist (stripping...)\n",
1001 								op->o_log_prefix, op->o_req_ndn.bv_val,
1002 								ml->sml_nvalues[ i ].bv_val );
1003 
1004 							for ( j = i + 1; !BER_BVISNULL( &ml->sml_nvalues[ j ] ); j++ );
1005 							ber_memfree( ml->sml_values[ i ].bv_val );
1006 							BER_BVZERO( &ml->sml_values[ i ] );
1007 							if ( ml->sml_nvalues != ml->sml_values ) {
1008 								ber_memfree( ml->sml_nvalues[ i ].bv_val );
1009 								BER_BVZERO( &ml->sml_nvalues[ i ] );
1010 							}
1011 							ml->sml_numvals--;
1012 							if ( j - i == 1 ) {
1013 								break;
1014 							}
1015 
1016 							AC_MEMCPY( &ml->sml_values[ i ], &ml->sml_values[ i + 1 ],
1017 								sizeof( struct berval ) * ( j - i ) );
1018 							if ( ml->sml_nvalues != ml->sml_values ) {
1019 								AC_MEMCPY( &ml->sml_nvalues[ i ], &ml->sml_nvalues[ i + 1 ],
1020 									sizeof( struct berval ) * ( j - i ) );
1021 							}
1022 							i--;
1023 						}
1024 
1025 						continue;
1026 					}
1027 
1028 					/* access is checked with the original identity */
1029 					op->o_bd->bd_info = (BackendInfo *)on->on_info;
1030 					rc = access_allowed( op, e, mo->mo_ad_member,
1031 							&op->o_req_ndn,
1032 							ACL_WDEL, NULL );
1033 					be_entry_release_r( op, e );
1034 					op->o_bd->bd_info = (BackendInfo *)on;
1035 
1036 					if ( !rc ) {
1037 						rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
1038 						rs->sr_text = "insufficient access to object referenced by memberof";
1039 						send_ldap_result( op, rs );
1040 						goto done;
1041 					}
1042 				}
1043 
1044 				if ( BER_BVISNULL( &ml->sml_nvalues[ 0 ] ) ) {
1045 					*mmlp = ml->sml_next;
1046 					slap_mod_free( &ml->sml_mod, 0 );
1047 					free( ml );
1048 				}
1049 
1050 				break;
1051 			}
1052 			/* fall thru */
1053 
1054 		case LDAP_MOD_REPLACE:
1055 
1056 			op->o_bd->bd_info = (BackendInfo *)on->on_info;
1057 			/* access is checked with the original identity */
1058 			rc = access_allowed( op, target,
1059 					mo->mo_ad_memberof,
1060 					NULL,
1061 					ACL_WDEL, NULL );
1062 			op->o_bd->bd_info = (BackendInfo *)on;
1063 			if ( rc == 0 ) {
1064 				rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
1065 				rs->sr_text = NULL;
1066 				send_ldap_result( op, rs );
1067 				goto done2;
1068 			}
1069 
1070 			if ( ml->sml_op == LDAP_MOD_DELETE || ml->sml_op == SLAP_MOD_SOFTDEL || !ml->sml_values ) {
1071 				break;
1072 			}
1073 			/* fall thru */
1074 
1075 		case LDAP_MOD_ADD:
1076 		case SLAP_MOD_SOFTADD: /* ITS#7487 */
1077 		case SLAP_MOD_ADD_IF_NOT_PRESENT: /* ITS#7487 */
1078 			{
1079 			AccessControlState	acl_state = ACL_STATE_INIT;
1080 
1081 			for ( i = 0; !BER_BVISNULL( &ml->sml_nvalues[ i ] ); i++ ) {
1082 				Entry		*e;
1083 
1084 				op->o_bd->bd_info = (BackendInfo *)on->on_info;
1085 				/* access is checked with the original identity */
1086 				rc = access_allowed( op, target,
1087 						mo->mo_ad_memberof,
1088 						&ml->sml_nvalues[ i ],
1089 						ACL_WADD,
1090 						&acl_state );
1091 				if ( rc == 0 ) {
1092 					rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
1093 					rs->sr_text = NULL;
1094 					send_ldap_result( op, rs );
1095 					goto done2;
1096 				}
1097 
1098 				/* ITS#6670 Ignore member pointing to this entry */
1099 				if ( dn_match( &ml->sml_nvalues[i], &save_ndn ))
1100 					continue;
1101 
1102 				rc = be_entry_get_rw( op, &ml->sml_nvalues[ i ],
1103 						NULL, NULL, 0, &e );
1104 				op->o_bd->bd_info = (BackendInfo *)on;
1105 				if ( rc != LDAP_SUCCESS ) {
1106 					if ( MEMBEROF_DANGLING_ERROR( mo ) ) {
1107 						rc = rs->sr_err = mo->mo_dangling_err;
1108 						rs->sr_text = "adding non-existing object "
1109 							"as memberof";
1110 						send_ldap_result( op, rs );
1111 						goto done2;
1112 					}
1113 
1114 					if ( MEMBEROF_DANGLING_DROP( mo ) ) {
1115 						int	j;
1116 
1117 						Debug( LDAP_DEBUG_ANY, "%s: memberof_op_modify(\"%s\"): "
1118 							"memberof=\"%s\" does not exist (stripping...)\n",
1119 							op->o_log_prefix, op->o_req_ndn.bv_val,
1120 							ml->sml_nvalues[ i ].bv_val );
1121 
1122 						for ( j = i + 1; !BER_BVISNULL( &ml->sml_nvalues[ j ] ); j++ );
1123 						ber_memfree( ml->sml_values[ i ].bv_val );
1124 						BER_BVZERO( &ml->sml_values[ i ] );
1125 						if ( ml->sml_nvalues != ml->sml_values ) {
1126 							ber_memfree( ml->sml_nvalues[ i ].bv_val );
1127 							BER_BVZERO( &ml->sml_nvalues[ i ] );
1128 						}
1129 						ml->sml_numvals--;
1130 						if ( j - i == 1 ) {
1131 							break;
1132 						}
1133 
1134 						AC_MEMCPY( &ml->sml_values[ i ], &ml->sml_values[ i + 1 ],
1135 							sizeof( struct berval ) * ( j - i ) );
1136 						if ( ml->sml_nvalues != ml->sml_values ) {
1137 							AC_MEMCPY( &ml->sml_nvalues[ i ], &ml->sml_nvalues[ i + 1 ],
1138 								sizeof( struct berval ) * ( j - i ) );
1139 						}
1140 						i--;
1141 					}
1142 
1143 					continue;
1144 				}
1145 
1146 				/* access is checked with the original identity */
1147 				op->o_bd->bd_info = (BackendInfo *)on->on_info;
1148 				rc = access_allowed( op, e, mo->mo_ad_member,
1149 						&op->o_req_ndn,
1150 						ACL_WDEL, NULL );
1151 				be_entry_release_r( op, e );
1152 				op->o_bd->bd_info = (BackendInfo *)on;
1153 
1154 				if ( !rc ) {
1155 					rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
1156 					rs->sr_text = "insufficient access to object referenced by memberof";
1157 					send_ldap_result( op, rs );
1158 					goto done;
1159 				}
1160 			}
1161 
1162 			if ( BER_BVISNULL( &ml->sml_nvalues[ 0 ] ) ) {
1163 				*mmlp = ml->sml_next;
1164 				slap_mod_free( &ml->sml_mod, 0 );
1165 				free( ml );
1166 			}
1167 
1168 			} break;
1169 
1170 		default:
1171 			assert( 0 );
1172 		}
1173 
1174 done2:;
1175 		op->o_bd->bd_info = (BackendInfo *)on->on_info;
1176 		be_entry_release_r( op, target );
1177 		op->o_bd->bd_info = (BackendInfo *)on;
1178 	}
1179 
1180 	sc = op->o_tmpalloc( sizeof(slap_callback)+sizeof(*mci), op->o_tmpmemctx );
1181 	sc->sc_private = sc+1;
1182 	sc->sc_response = memberof_res_modify;
1183 	sc->sc_cleanup = memberof_cleanup;
1184 	mci = sc->sc_private;
1185 	mci->on = on;
1186 	mci->member = NULL;
1187 	mci->memberof = NULL;
1188 	mci->what = mcis.what;
1189 
1190 	if ( save_member ) {
1191 		op->o_dn = op->o_bd->be_rootdn;
1192 		op->o_ndn = op->o_bd->be_rootndn;
1193 		op->o_bd->bd_info = (BackendInfo *)on->on_info;
1194 		rc = backend_attribute( op, NULL, &op->o_req_ndn,
1195 				mo->mo_ad_member, &mci->member, ACL_READ );
1196 		op->o_bd->bd_info = (BackendInfo *)on;
1197 	}
1198 
1199 	sc->sc_next = op->o_callback;
1200 	op->o_callback = sc;
1201 
1202 	rc = SLAP_CB_CONTINUE;
1203 
1204 done:;
1205 	op->o_dn = save_dn;
1206 	op->o_ndn = save_ndn;
1207 	op->o_bd->bd_info = (BackendInfo *)on;
1208 
1209 	return rc;
1210 }
1211 
1212 static int
1213 memberof_op_modrdn( Operation *op, SlapReply *rs )
1214 {
1215 	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
1216 	slap_callback *sc;
1217 	memberof_cbinfo_t *mci;
1218 	OpExtra		*oex;
1219 
1220 	LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
1221 		if ( oex->oe_key == (void *)&memberof )
1222 			return SLAP_CB_CONTINUE;
1223 	}
1224 
1225 	sc = op->o_tmpalloc( sizeof(slap_callback)+sizeof(*mci), op->o_tmpmemctx );
1226 	sc->sc_private = sc+1;
1227 	sc->sc_response = memberof_res_modrdn;
1228 	sc->sc_cleanup = memberof_cleanup;
1229 	mci = sc->sc_private;
1230 	mci->on = on;
1231 	mci->member = NULL;
1232 	mci->memberof = NULL;
1233 
1234 	sc->sc_next = op->o_callback;
1235 	op->o_callback = sc;
1236 
1237 	return SLAP_CB_CONTINUE;
1238 }
1239 
1240 /*
1241  * response callback that adds memberof values when a group is added.
1242  */
1243 static int
1244 memberof_res_add( Operation *op, SlapReply *rs )
1245 {
1246 	memberof_cbinfo_t *mci = op->o_callback->sc_private;
1247 	slap_overinst	*on = mci->on;
1248 	memberof_t	*mo = (memberof_t *)on->on_bi.bi_private;
1249 
1250 	int		i;
1251 
1252 	if ( rs->sr_err != LDAP_SUCCESS ) {
1253 		return SLAP_CB_CONTINUE;
1254 	}
1255 
1256 	if ( MEMBEROF_REVERSE( mo ) ) {
1257 		Attribute	*ma;
1258 
1259 		ma = attr_find( op->ora_e->e_attrs, mo->mo_ad_memberof );
1260 		if ( ma != NULL ) {
1261 			/* relax is required to allow to add
1262 			 * a non-existing member */
1263 			op->o_relax = SLAP_CONTROL_CRITICAL;
1264 
1265 			for ( i = 0; !BER_BVISNULL( &ma->a_nvals[ i ] ); i++ ) {
1266 
1267 				/* ITS#6670 Ignore member pointing to this entry */
1268 				if ( dn_match( &ma->a_nvals[i], &op->o_req_ndn ))
1269 					continue;
1270 
1271 				/* the modification is attempted
1272 				 * with the original identity */
1273 				memberof_value_modify( op,
1274 					&ma->a_nvals[ i ], mo->mo_ad_member,
1275 					NULL, NULL, &op->o_req_dn, &op->o_req_ndn );
1276 			}
1277 		}
1278 	}
1279 
1280 	if ( is_entry_objectclass_or_sub( op->ora_e, mo->mo_oc_group ) ) {
1281 		Attribute	*a;
1282 
1283 		for ( a = attrs_find( op->ora_e->e_attrs, mo->mo_ad_member );
1284 				a != NULL;
1285 				a = attrs_find( a->a_next, mo->mo_ad_member ) )
1286 		{
1287 			for ( i = 0; !BER_BVISNULL( &a->a_nvals[ i ] ); i++ ) {
1288 				/* ITS#6670 Ignore member pointing to this entry */
1289 				if ( dn_match( &a->a_nvals[i], &op->o_req_ndn ))
1290 					continue;
1291 
1292 				memberof_value_modify( op,
1293 						&a->a_nvals[ i ],
1294 						mo->mo_ad_memberof,
1295 						NULL, NULL,
1296 						&op->o_req_dn,
1297 						&op->o_req_ndn );
1298 			}
1299 		}
1300 	}
1301 
1302 	return SLAP_CB_CONTINUE;
1303 }
1304 
1305 /*
1306  * response callback that deletes memberof values when a group is deleted.
1307  */
1308 static int
1309 memberof_res_delete( Operation *op, SlapReply *rs )
1310 {
1311 	memberof_cbinfo_t *mci = op->o_callback->sc_private;
1312 	slap_overinst	*on = mci->on;
1313 	memberof_t	*mo = (memberof_t *)on->on_bi.bi_private;
1314 
1315  	BerVarray	vals;
1316 	int		i;
1317 
1318 	if ( rs->sr_err != LDAP_SUCCESS ) {
1319 		return SLAP_CB_CONTINUE;
1320 	}
1321 
1322 	vals = mci->member;
1323 	if ( vals != NULL ) {
1324 		for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
1325 			memberof_value_modify( op,
1326 					&vals[ i ], mo->mo_ad_memberof,
1327 					&op->o_req_dn, &op->o_req_ndn,
1328 					NULL, NULL );
1329 		}
1330 	}
1331 
1332 	if ( MEMBEROF_REFINT( mo ) ) {
1333 		vals = mci->memberof;
1334 		if ( vals != NULL ) {
1335 			for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
1336 				memberof_value_modify( op,
1337 						&vals[ i ], mo->mo_ad_member,
1338 						&op->o_req_dn, &op->o_req_ndn,
1339 						NULL, NULL );
1340 			}
1341 		}
1342 	}
1343 
1344 	return SLAP_CB_CONTINUE;
1345 }
1346 
1347 /*
1348  * response callback that adds/deletes memberof values when a group
1349  * is modified.
1350  */
1351 static int
1352 memberof_res_modify( Operation *op, SlapReply *rs )
1353 {
1354 	memberof_cbinfo_t *mci = op->o_callback->sc_private;
1355 	slap_overinst	*on = mci->on;
1356 	memberof_t	*mo = (memberof_t *)on->on_bi.bi_private;
1357 
1358 	int		i, rc;
1359 	Modifications	*ml, *mml = NULL;
1360 	BerVarray	vals;
1361 
1362 	if ( rs->sr_err != LDAP_SUCCESS ) {
1363 		return SLAP_CB_CONTINUE;
1364 	}
1365 
1366 	if ( MEMBEROF_REVERSE( mo ) ) {
1367 		for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) {
1368 			if ( ml->sml_desc == mo->mo_ad_memberof ) {
1369 				mml = ml;
1370 				break;
1371 			}
1372 		}
1373 	}
1374 
1375 	if ( mml != NULL ) {
1376 		BerVarray	vals = mml->sml_nvalues;
1377 
1378 		switch ( mml->sml_op ) {
1379 		case LDAP_MOD_DELETE:
1380 		case SLAP_MOD_SOFTDEL: /* ITS#7487: can be used by syncrepl (in mirror mode?) */
1381 			if ( vals != NULL ) {
1382 				for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
1383 					memberof_value_modify( op,
1384 							&vals[ i ], mo->mo_ad_member,
1385 							&op->o_req_dn, &op->o_req_ndn,
1386 							NULL, NULL );
1387 				}
1388 				break;
1389 			}
1390 			/* fall thru */
1391 
1392 		case LDAP_MOD_REPLACE:
1393 			/* delete all ... */
1394 			op->o_bd->bd_info = (BackendInfo *)on->on_info;
1395 			rc = backend_attribute( op, NULL, &op->o_req_ndn,
1396 					mo->mo_ad_memberof, &vals, ACL_READ );
1397 			op->o_bd->bd_info = (BackendInfo *)on;
1398 			if ( rc == LDAP_SUCCESS ) {
1399 				for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
1400 					memberof_value_modify( op,
1401 							&vals[ i ], mo->mo_ad_member,
1402 							&op->o_req_dn, &op->o_req_ndn,
1403 							NULL, NULL );
1404 				}
1405 				ber_bvarray_free_x( vals, op->o_tmpmemctx );
1406 			}
1407 
1408 			if ( ml->sml_op == LDAP_MOD_DELETE || !mml->sml_values ) {
1409 				break;
1410 			}
1411 			/* fall thru */
1412 
1413 		case LDAP_MOD_ADD:
1414 		case SLAP_MOD_SOFTADD: /* ITS#7487 */
1415 		case SLAP_MOD_ADD_IF_NOT_PRESENT: /* ITS#7487 */
1416 			assert( vals != NULL );
1417 
1418 			for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
1419 				memberof_value_modify( op,
1420 						&vals[ i ], mo->mo_ad_member,
1421 						NULL, NULL,
1422 						&op->o_req_dn, &op->o_req_ndn );
1423 			}
1424 			break;
1425 
1426 		default:
1427 			assert( 0 );
1428 		}
1429 	}
1430 
1431 	if ( mci->what & MEMBEROF_IS_GROUP )
1432 	{
1433 		for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) {
1434 			if ( ml->sml_desc != mo->mo_ad_member ) {
1435 				continue;
1436 			}
1437 
1438 			switch ( ml->sml_op ) {
1439 			case LDAP_MOD_DELETE:
1440 			case SLAP_MOD_SOFTDEL: /* ITS#7487: can be used by syncrepl (in mirror mode?) */
1441 				vals = ml->sml_nvalues;
1442 				if ( vals != NULL ) {
1443 					for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
1444 						memberof_value_modify( op,
1445 								&vals[ i ], mo->mo_ad_memberof,
1446 								&op->o_req_dn, &op->o_req_ndn,
1447 								NULL, NULL );
1448 					}
1449 					break;
1450 				}
1451 				/* fall thru */
1452 
1453 			case LDAP_MOD_REPLACE:
1454 				vals = mci->member;
1455 
1456 				/* delete all ... */
1457 				if ( vals != NULL ) {
1458 					for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
1459 						memberof_value_modify( op,
1460 								&vals[ i ], mo->mo_ad_memberof,
1461 								&op->o_req_dn, &op->o_req_ndn,
1462 								NULL, NULL );
1463 					}
1464 				}
1465 
1466 				if ( ml->sml_op == LDAP_MOD_DELETE || ml->sml_op == SLAP_MOD_SOFTDEL || !ml->sml_values ) {
1467 					break;
1468 				}
1469 				/* fall thru */
1470 
1471 			case LDAP_MOD_ADD:
1472 			case SLAP_MOD_SOFTADD: /* ITS#7487 */
1473 			case SLAP_MOD_ADD_IF_NOT_PRESENT : /* ITS#7487 */
1474 				assert( ml->sml_nvalues != NULL );
1475 				vals = ml->sml_nvalues;
1476 				for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
1477 					memberof_value_modify( op,
1478 							&vals[ i ], mo->mo_ad_memberof,
1479 							NULL, NULL,
1480 							&op->o_req_dn, &op->o_req_ndn );
1481 				}
1482 				break;
1483 
1484 			default:
1485 				assert( 0 );
1486 			}
1487 		}
1488 	}
1489 
1490 	return SLAP_CB_CONTINUE;
1491 }
1492 
1493 /*
1494  * response callback that adds/deletes member values when a group member
1495  * is renamed.
1496  */
1497 static int
1498 memberof_res_modrdn( Operation *op, SlapReply *rs )
1499 {
1500 	memberof_cbinfo_t *mci = op->o_callback->sc_private;
1501 	slap_overinst	*on = mci->on;
1502 	memberof_t	*mo = (memberof_t *)on->on_bi.bi_private;
1503 
1504 	struct berval	newPDN, newDN = BER_BVNULL, newPNDN, newNDN;
1505 	int		i, rc;
1506 	BerVarray	vals;
1507 
1508 	struct berval	save_dn, save_ndn;
1509 
1510 	if ( rs->sr_err != LDAP_SUCCESS ) {
1511 		return SLAP_CB_CONTINUE;
1512 	}
1513 
1514 	mci->what = MEMBEROF_IS_GROUP;
1515 	if ( MEMBEROF_REFINT( mo ) ) {
1516 		mci->what |= MEMBEROF_IS_MEMBER;
1517 	}
1518 
1519 	if ( op->orr_nnewSup ) {
1520 		newPNDN = *op->orr_nnewSup;
1521 
1522 	} else {
1523 		dnParent( &op->o_req_ndn, &newPNDN );
1524 	}
1525 
1526 	build_new_dn( &newNDN, &newPNDN, &op->orr_nnewrdn, op->o_tmpmemctx );
1527 
1528 	save_dn = op->o_req_dn;
1529 	save_ndn = op->o_req_ndn;
1530 
1531 	op->o_req_dn = newNDN;
1532 	op->o_req_ndn = newNDN;
1533 	rc = memberof_isGroupOrMember( op, mci );
1534 	op->o_req_dn = save_dn;
1535 	op->o_req_ndn = save_ndn;
1536 
1537 	if ( rc != LDAP_SUCCESS || mci->what == MEMBEROF_IS_NONE ) {
1538 		goto done;
1539 	}
1540 
1541 	if ( op->orr_newSup ) {
1542 		newPDN = *op->orr_newSup;
1543 
1544 	} else {
1545 		dnParent( &op->o_req_dn, &newPDN );
1546 	}
1547 
1548 	build_new_dn( &newDN, &newPDN, &op->orr_newrdn, op->o_tmpmemctx );
1549 
1550 	if ( mci->what & MEMBEROF_IS_GROUP ) {
1551 		op->o_bd->bd_info = (BackendInfo *)on->on_info;
1552 		rc = backend_attribute( op, NULL, &newNDN,
1553 				mo->mo_ad_member, &vals, ACL_READ );
1554 		op->o_bd->bd_info = (BackendInfo *)on;
1555 
1556 		if ( rc == LDAP_SUCCESS ) {
1557 			for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
1558 				memberof_value_modify( op,
1559 						&vals[ i ], mo->mo_ad_memberof,
1560 						&op->o_req_dn, &op->o_req_ndn,
1561 						&newDN, &newNDN );
1562 			}
1563 			ber_bvarray_free_x( vals, op->o_tmpmemctx );
1564 		}
1565 	}
1566 
1567 	if ( MEMBEROF_REFINT( mo ) && ( mci->what & MEMBEROF_IS_MEMBER ) ) {
1568 		op->o_bd->bd_info = (BackendInfo *)on->on_info;
1569 		rc = backend_attribute( op, NULL, &newNDN,
1570 				mo->mo_ad_memberof, &vals, ACL_READ );
1571 		op->o_bd->bd_info = (BackendInfo *)on;
1572 
1573 		if ( rc == LDAP_SUCCESS ) {
1574 			for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
1575 				memberof_value_modify( op,
1576 						&vals[ i ], mo->mo_ad_member,
1577 						&op->o_req_dn, &op->o_req_ndn,
1578 						&newDN, &newNDN );
1579 			}
1580 			ber_bvarray_free_x( vals, op->o_tmpmemctx );
1581 		}
1582 	}
1583 
1584 done:;
1585 	if ( !BER_BVISNULL( &newDN ) ) {
1586 		op->o_tmpfree( newDN.bv_val, op->o_tmpmemctx );
1587 	}
1588 	op->o_tmpfree( newNDN.bv_val, op->o_tmpmemctx );
1589 
1590 	return SLAP_CB_CONTINUE;
1591 }
1592 
1593 
1594 static int
1595 memberof_db_init(
1596 	BackendDB	*be,
1597 	ConfigReply	*cr )
1598 {
1599 	slap_overinst	*on = (slap_overinst *)be->bd_info;
1600 	memberof_t		*mo;
1601 
1602 	mo = (memberof_t *)ch_calloc( 1, sizeof( memberof_t ) );
1603 
1604 	/* safe default */
1605 	mo->mo_dangling_err = LDAP_CONSTRAINT_VIOLATION;
1606 
1607 	on->on_bi.bi_private = (void *)mo;
1608 
1609 	return 0;
1610 }
1611 
1612 enum {
1613 	MO_DN = 1,
1614 	MO_DANGLING,
1615 	MO_REFINT,
1616 	MO_GROUP_OC,
1617 	MO_MEMBER_AD,
1618 	MO_MEMBER_OF_AD,
1619 #if 0
1620 	MO_REVERSE,
1621 #endif
1622 
1623 	MO_DANGLING_ERROR,
1624 
1625 	MO_LAST
1626 };
1627 
1628 static ConfigDriver mo_cf_gen;
1629 
1630 #define OID		"1.3.6.1.4.1.7136.2.666.4"
1631 #define	OIDAT		OID ".1.1"
1632 #define	OIDCFGAT	OID ".1.2"
1633 #define	OIDOC		OID ".2.1"
1634 #define	OIDCFGOC	OID ".2.2"
1635 
1636 
1637 static ConfigTable mo_cfg[] = {
1638 	{ "memberof-dn", "modifiersName",
1639 		2, 2, 0, ARG_MAGIC|ARG_DN|MO_DN, mo_cf_gen,
1640 		"( OLcfgOvAt:18.0 NAME 'olcMemberOfDN' "
1641 			"DESC 'DN to be used as modifiersName' "
1642 			"SYNTAX OMsDN SINGLE-VALUE )",
1643 		NULL, NULL },
1644 
1645 	{ "memberof-dangling", "ignore|drop|error",
1646 		2, 2, 0, ARG_MAGIC|MO_DANGLING, mo_cf_gen,
1647 		"( OLcfgOvAt:18.1 NAME 'olcMemberOfDangling' "
1648 			"DESC 'Behavior with respect to dangling members, "
1649 				"constrained to ignore, drop, error' "
1650 			"SYNTAX OMsDirectoryString SINGLE-VALUE )",
1651 		NULL, NULL },
1652 
1653 	{ "memberof-refint", "true|FALSE",
1654 		2, 2, 0, ARG_MAGIC|ARG_ON_OFF|MO_REFINT, mo_cf_gen,
1655 		"( OLcfgOvAt:18.2 NAME 'olcMemberOfRefInt' "
1656 			"DESC 'Take care of referential integrity' "
1657 			"SYNTAX OMsBoolean SINGLE-VALUE )",
1658 		NULL, NULL },
1659 
1660 	{ "memberof-group-oc", "objectClass",
1661 		2, 2, 0, ARG_MAGIC|MO_GROUP_OC, mo_cf_gen,
1662 		"( OLcfgOvAt:18.3 NAME 'olcMemberOfGroupOC' "
1663 			"DESC 'Group objectClass' "
1664 			"SYNTAX OMsDirectoryString SINGLE-VALUE )",
1665 		NULL, NULL },
1666 
1667 	{ "memberof-member-ad", "member attribute",
1668 		2, 2, 0, ARG_MAGIC|MO_MEMBER_AD, mo_cf_gen,
1669 		"( OLcfgOvAt:18.4 NAME 'olcMemberOfMemberAD' "
1670 			"DESC 'member attribute' "
1671 			"SYNTAX OMsDirectoryString SINGLE-VALUE )",
1672 		NULL, NULL },
1673 
1674 	{ "memberof-memberof-ad", "memberOf attribute",
1675 		2, 2, 0, ARG_MAGIC|MO_MEMBER_OF_AD, mo_cf_gen,
1676 		"( OLcfgOvAt:18.5 NAME 'olcMemberOfMemberOfAD' "
1677 			"DESC 'memberOf attribute' "
1678 			"SYNTAX OMsDirectoryString SINGLE-VALUE )",
1679 		NULL, NULL },
1680 
1681 #if 0
1682 	{ "memberof-reverse", "true|FALSE",
1683 		2, 2, 0, ARG_MAGIC|ARG_ON_OFF|MO_REVERSE, mo_cf_gen,
1684 		"( OLcfgOvAt:18.6 NAME 'olcMemberOfReverse' "
1685 			"DESC 'Take care of referential integrity "
1686 				"also when directly modifying memberOf' "
1687 			"SYNTAX OMsBoolean SINGLE-VALUE )",
1688 		NULL, NULL },
1689 #endif
1690 
1691 	{ "memberof-dangling-error", "error code",
1692 		2, 2, 0, ARG_MAGIC|MO_DANGLING_ERROR, mo_cf_gen,
1693 		"( OLcfgOvAt:18.7 NAME 'olcMemberOfDanglingError' "
1694 			"DESC 'Error code returned in case of dangling back reference' "
1695 			"SYNTAX OMsDirectoryString SINGLE-VALUE )",
1696 		NULL, NULL },
1697 
1698 	{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
1699 };
1700 
1701 static ConfigOCs mo_ocs[] = {
1702 	{ "( OLcfgOvOc:18.1 "
1703 		"NAME 'olcMemberOf' "
1704 		"DESC 'Member-of configuration' "
1705 		"SUP olcOverlayConfig "
1706 		"MAY ( "
1707 			"olcMemberOfDN "
1708 			"$ olcMemberOfDangling "
1709 			"$ olcMemberOfDanglingError"
1710 			"$ olcMemberOfRefInt "
1711 			"$ olcMemberOfGroupOC "
1712 			"$ olcMemberOfMemberAD "
1713 			"$ olcMemberOfMemberOfAD "
1714 #if 0
1715 			"$ olcMemberOfReverse "
1716 #endif
1717 			") "
1718 		")",
1719 		Cft_Overlay, mo_cfg, NULL, NULL },
1720 	{ NULL, 0, NULL }
1721 };
1722 
1723 static slap_verbmasks dangling_mode[] = {
1724 	{ BER_BVC( "ignore" ),		MEMBEROF_NONE },
1725 	{ BER_BVC( "drop" ),		MEMBEROF_FDANGLING_DROP },
1726 	{ BER_BVC( "error" ),		MEMBEROF_FDANGLING_ERROR },
1727 	{ BER_BVNULL,			0 }
1728 };
1729 
1730 static int
1731 memberof_make_group_filter( memberof_t *mo )
1732 {
1733 	char		*ptr;
1734 
1735 	if ( !BER_BVISNULL( &mo->mo_groupFilterstr ) ) {
1736 		ch_free( mo->mo_groupFilterstr.bv_val );
1737 	}
1738 
1739 	mo->mo_groupFilter.f_choice = LDAP_FILTER_EQUALITY;
1740 	mo->mo_groupFilter.f_ava = &mo->mo_groupAVA;
1741 
1742 	mo->mo_groupFilter.f_av_desc = slap_schema.si_ad_objectClass;
1743 	mo->mo_groupFilter.f_av_value = mo->mo_oc_group->soc_cname;
1744 
1745 	mo->mo_groupFilterstr.bv_len = STRLENOF( "(=)" )
1746 		+ slap_schema.si_ad_objectClass->ad_cname.bv_len
1747 		+ mo->mo_oc_group->soc_cname.bv_len;
1748 	ptr = mo->mo_groupFilterstr.bv_val = ch_malloc( mo->mo_groupFilterstr.bv_len + 1 );
1749 	*ptr++ = '(';
1750 	ptr = lutil_strcopy( ptr, slap_schema.si_ad_objectClass->ad_cname.bv_val );
1751 	*ptr++ = '=';
1752 	ptr = lutil_strcopy( ptr, mo->mo_oc_group->soc_cname.bv_val );
1753 	*ptr++ = ')';
1754 	*ptr = '\0';
1755 
1756 	return 0;
1757 }
1758 
1759 static int
1760 memberof_make_member_filter( memberof_t *mo )
1761 {
1762 	char		*ptr;
1763 
1764 	if ( !BER_BVISNULL( &mo->mo_memberFilterstr ) ) {
1765 		ch_free( mo->mo_memberFilterstr.bv_val );
1766 	}
1767 
1768 	mo->mo_memberFilter.f_choice = LDAP_FILTER_PRESENT;
1769 	mo->mo_memberFilter.f_desc = mo->mo_ad_memberof;
1770 
1771 	mo->mo_memberFilterstr.bv_len = STRLENOF( "(=*)" )
1772 		+ mo->mo_ad_memberof->ad_cname.bv_len;
1773 	ptr = mo->mo_memberFilterstr.bv_val = ch_malloc( mo->mo_memberFilterstr.bv_len + 1 );
1774 	*ptr++ = '(';
1775 	ptr = lutil_strcopy( ptr, mo->mo_ad_memberof->ad_cname.bv_val );
1776 	ptr = lutil_strcopy( ptr, "=*)" );
1777 
1778 	return 0;
1779 }
1780 
1781 static int
1782 mo_cf_gen( ConfigArgs *c )
1783 {
1784 	slap_overinst	*on = (slap_overinst *)c->bi;
1785 	memberof_t	*mo = (memberof_t *)on->on_bi.bi_private;
1786 
1787 	int		i, rc = 0;
1788 
1789 	if ( c->op == SLAP_CONFIG_EMIT ) {
1790 		struct berval bv = BER_BVNULL;
1791 
1792 		switch( c->type ) {
1793 		case MO_DN:
1794 			if ( mo->mo_dn.bv_val != NULL) {
1795 				value_add_one( &c->rvalue_vals, &mo->mo_dn );
1796 				value_add_one( &c->rvalue_nvals, &mo->mo_ndn );
1797 			}
1798 			break;
1799 
1800 		case MO_DANGLING:
1801 			enum_to_verb( dangling_mode, (mo->mo_flags & MEMBEROF_FDANGLING_MASK), &bv );
1802 			if ( BER_BVISNULL( &bv ) ) {
1803 				/* there's something wrong... */
1804 				assert( 0 );
1805 				rc = 1;
1806 
1807 			} else {
1808 				value_add_one( &c->rvalue_vals, &bv );
1809 			}
1810 			break;
1811 
1812 		case MO_DANGLING_ERROR:
1813 			if ( mo->mo_flags & MEMBEROF_FDANGLING_ERROR ) {
1814 				char buf[ SLAP_TEXT_BUFLEN ];
1815 				enum_to_verb( slap_ldap_response_code, mo->mo_dangling_err, &bv );
1816 				if ( BER_BVISNULL( &bv ) ) {
1817 					bv.bv_len = snprintf( buf, sizeof( buf ), "0x%x", mo->mo_dangling_err );
1818 					if ( bv.bv_len < sizeof( buf ) ) {
1819 						bv.bv_val = buf;
1820 					} else {
1821 						rc = 1;
1822 						break;
1823 					}
1824 				}
1825 				value_add_one( &c->rvalue_vals, &bv );
1826 			} else {
1827 				rc = 1;
1828 			}
1829 			break;
1830 
1831 		case MO_REFINT:
1832 			c->value_int = MEMBEROF_REFINT( mo );
1833 			break;
1834 
1835 #if 0
1836 		case MO_REVERSE:
1837 			c->value_int = MEMBEROF_REVERSE( mo );
1838 			break;
1839 #endif
1840 
1841 		case MO_GROUP_OC:
1842 			if ( mo->mo_oc_group != NULL ){
1843 				value_add_one( &c->rvalue_vals, &mo->mo_oc_group->soc_cname );
1844 			}
1845 			break;
1846 
1847 		case MO_MEMBER_AD:
1848 			if ( mo->mo_ad_member != NULL ){
1849 				value_add_one( &c->rvalue_vals, &mo->mo_ad_member->ad_cname );
1850 			}
1851 			break;
1852 
1853 		case MO_MEMBER_OF_AD:
1854 			if ( mo->mo_ad_memberof != NULL ){
1855 				value_add_one( &c->rvalue_vals, &mo->mo_ad_memberof->ad_cname );
1856 			}
1857 			break;
1858 
1859 		default:
1860 			assert( 0 );
1861 			return 1;
1862 		}
1863 
1864 		return rc;
1865 
1866 	} else if ( c->op == LDAP_MOD_DELETE ) {
1867 		return 1;	/* FIXME */
1868 
1869 	} else {
1870 		switch( c->type ) {
1871 		case MO_DN:
1872 			if ( !BER_BVISNULL( &mo->mo_dn ) ) {
1873 				ber_memfree( mo->mo_dn.bv_val );
1874 				ber_memfree( mo->mo_ndn.bv_val );
1875 			}
1876 			mo->mo_dn = c->value_dn;
1877 			mo->mo_ndn = c->value_ndn;
1878 			break;
1879 
1880 		case MO_DANGLING:
1881 			i = verb_to_mask( c->argv[ 1 ], dangling_mode );
1882 			if ( BER_BVISNULL( &dangling_mode[ i ].word ) ) {
1883 				return 1;
1884 			}
1885 
1886 			mo->mo_flags &= ~MEMBEROF_FDANGLING_MASK;
1887 			mo->mo_flags |= dangling_mode[ i ].mask;
1888 			break;
1889 
1890 		case MO_DANGLING_ERROR:
1891 			i = verb_to_mask( c->argv[ 1 ], slap_ldap_response_code );
1892 			if ( !BER_BVISNULL( &slap_ldap_response_code[ i ].word ) ) {
1893 				mo->mo_dangling_err = slap_ldap_response_code[ i ].mask;
1894 			} else if ( lutil_atoix( &mo->mo_dangling_err, c->argv[ 1 ], 0 ) ) {
1895 				return 1;
1896 			}
1897 			break;
1898 
1899 		case MO_REFINT:
1900 			if ( c->value_int ) {
1901 				mo->mo_flags |= MEMBEROF_FREFINT;
1902 
1903 			} else {
1904 				mo->mo_flags &= ~MEMBEROF_FREFINT;
1905 			}
1906 			break;
1907 
1908 #if 0
1909 		case MO_REVERSE:
1910 			if ( c->value_int ) {
1911 				mo->mo_flags |= MEMBEROF_FREVERSE;
1912 
1913 			} else {
1914 				mo->mo_flags &= ~MEMBEROF_FREVERSE;
1915 			}
1916 			break;
1917 #endif
1918 
1919 		case MO_GROUP_OC: {
1920 			ObjectClass	*oc = NULL;
1921 
1922 			oc = oc_find( c->argv[ 1 ] );
1923 			if ( oc == NULL ) {
1924 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
1925 					"unable to find group objectClass=\"%s\"",
1926 					c->argv[ 1 ] );
1927 				Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n",
1928 					c->log, c->cr_msg, 0 );
1929 				return 1;
1930 			}
1931 
1932 			mo->mo_oc_group = oc;
1933 			memberof_make_group_filter( mo );
1934 			} break;
1935 
1936 		case MO_MEMBER_AD: {
1937 			AttributeDescription	*ad = NULL;
1938 			const char		*text = NULL;
1939 
1940 
1941 			rc = slap_str2ad( c->argv[ 1 ], &ad, &text );
1942 			if ( rc != LDAP_SUCCESS ) {
1943 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
1944 					"unable to find member attribute=\"%s\": %s (%d)",
1945 					c->argv[ 1 ], text, rc );
1946 				Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n",
1947 					c->log, c->cr_msg, 0 );
1948 				return 1;
1949 			}
1950 
1951 			if ( !is_at_syntax( ad->ad_type, SLAPD_DN_SYNTAX )		/* e.g. "member" */
1952 				&& !is_at_syntax( ad->ad_type, SLAPD_NAMEUID_SYNTAX ) )	/* e.g. "uniqueMember" */
1953 			{
1954 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
1955 					"member attribute=\"%s\" must either "
1956 					"have DN (%s) or nameUID (%s) syntax",
1957 					c->argv[ 1 ], SLAPD_DN_SYNTAX, SLAPD_NAMEUID_SYNTAX );
1958 				Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n",
1959 					c->log, c->cr_msg, 0 );
1960 				return 1;
1961 			}
1962 
1963 			mo->mo_ad_member = ad;
1964 			} break;
1965 
1966 		case MO_MEMBER_OF_AD: {
1967 			AttributeDescription	*ad = NULL;
1968 			const char		*text = NULL;
1969 
1970 
1971 			rc = slap_str2ad( c->argv[ 1 ], &ad, &text );
1972 			if ( rc != LDAP_SUCCESS ) {
1973 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
1974 					"unable to find memberof attribute=\"%s\": %s (%d)",
1975 					c->argv[ 1 ], text, rc );
1976 				Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n",
1977 					c->log, c->cr_msg, 0 );
1978 				return 1;
1979 			}
1980 
1981 			if ( !is_at_syntax( ad->ad_type, SLAPD_DN_SYNTAX )		/* e.g. "member" */
1982 				&& !is_at_syntax( ad->ad_type, SLAPD_NAMEUID_SYNTAX ) )	/* e.g. "uniqueMember" */
1983 			{
1984 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
1985 					"memberof attribute=\"%s\" must either "
1986 					"have DN (%s) or nameUID (%s) syntax",
1987 					c->argv[ 1 ], SLAPD_DN_SYNTAX, SLAPD_NAMEUID_SYNTAX );
1988 				Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n",
1989 					c->log, c->cr_msg, 0 );
1990 				return 1;
1991 			}
1992 
1993 			mo->mo_ad_memberof = ad;
1994 			memberof_make_member_filter( mo );
1995 			} break;
1996 
1997 		default:
1998 			assert( 0 );
1999 			return 1;
2000 		}
2001 	}
2002 
2003 	return 0;
2004 }
2005 
2006 static int
2007 memberof_db_open(
2008 	BackendDB	*be,
2009 	ConfigReply	*cr )
2010 {
2011 	slap_overinst	*on = (slap_overinst *)be->bd_info;
2012 	memberof_t	*mo = (memberof_t *)on->on_bi.bi_private;
2013 
2014 	int		rc;
2015 	const char	*text = NULL;
2016 
2017 	if( ! mo->mo_ad_memberof ){
2018 		rc = slap_str2ad( SLAPD_MEMBEROF_ATTR, &mo->mo_ad_memberof, &text );
2019 		if ( rc != LDAP_SUCCESS ) {
2020 			Debug( LDAP_DEBUG_ANY, "memberof_db_open: "
2021 					"unable to find attribute=\"%s\": %s (%d)\n",
2022 					SLAPD_MEMBEROF_ATTR, text, rc );
2023 			return rc;
2024 		}
2025 	}
2026 
2027 	if( ! mo->mo_ad_member ){
2028 		rc = slap_str2ad( SLAPD_GROUP_ATTR, &mo->mo_ad_member, &text );
2029 		if ( rc != LDAP_SUCCESS ) {
2030 			Debug( LDAP_DEBUG_ANY, "memberof_db_open: "
2031 					"unable to find attribute=\"%s\": %s (%d)\n",
2032 					SLAPD_GROUP_ATTR, text, rc );
2033 			return rc;
2034 		}
2035 	}
2036 
2037 	if( ! mo->mo_oc_group ){
2038 		mo->mo_oc_group = oc_find( SLAPD_GROUP_CLASS );
2039 		if ( mo->mo_oc_group == NULL ) {
2040 			Debug( LDAP_DEBUG_ANY,
2041 					"memberof_db_open: "
2042 					"unable to find objectClass=\"%s\"\n",
2043 					SLAPD_GROUP_CLASS, 0, 0 );
2044 			return 1;
2045 		}
2046 	}
2047 
2048 	if ( BER_BVISNULL( &mo->mo_dn ) && !BER_BVISNULL( &be->be_rootdn ) ) {
2049 		ber_dupbv( &mo->mo_dn, &be->be_rootdn );
2050 		ber_dupbv( &mo->mo_ndn, &be->be_rootndn );
2051 	}
2052 
2053 	if ( BER_BVISNULL( &mo->mo_groupFilterstr ) ) {
2054 		memberof_make_group_filter( mo );
2055 	}
2056 
2057 	if ( BER_BVISNULL( &mo->mo_memberFilterstr ) ) {
2058 		memberof_make_member_filter( mo );
2059 	}
2060 
2061 	return 0;
2062 }
2063 
2064 static int
2065 memberof_db_destroy(
2066 	BackendDB	*be,
2067 	ConfigReply	*cr )
2068 {
2069 	slap_overinst	*on = (slap_overinst *)be->bd_info;
2070 	memberof_t	*mo = (memberof_t *)on->on_bi.bi_private;
2071 
2072 	if ( mo ) {
2073 		if ( !BER_BVISNULL( &mo->mo_dn ) ) {
2074 			ber_memfree( mo->mo_dn.bv_val );
2075 			ber_memfree( mo->mo_ndn.bv_val );
2076 		}
2077 
2078 		if ( !BER_BVISNULL( &mo->mo_groupFilterstr ) ) {
2079 			ber_memfree( mo->mo_groupFilterstr.bv_val );
2080 		}
2081 
2082 		if ( !BER_BVISNULL( &mo->mo_memberFilterstr ) ) {
2083 			ber_memfree( mo->mo_memberFilterstr.bv_val );
2084 		}
2085 
2086 		ber_memfree( mo );
2087 	}
2088 
2089 	return 0;
2090 }
2091 
2092 /* unused */
2093 static AttributeDescription	*ad_memberOf;
2094 
2095 static struct {
2096 	char	*desc;
2097 	AttributeDescription **adp;
2098 } as[] = {
2099 	{ "( 1.2.840.113556.1.2.102 "
2100 		"NAME 'memberOf' "
2101 		"DESC 'Group that the entry belongs to' "
2102 		"SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' "
2103 		"EQUALITY distinguishedNameMatch "	/* added */
2104 		"USAGE dSAOperation "			/* added; questioned */
2105 		/* "NO-USER-MODIFICATION " */		/* add? */
2106 		"X-ORIGIN 'iPlanet Delegated Administrator' )",
2107 		&ad_memberOf },
2108 	{ NULL }
2109 };
2110 
2111 #if SLAPD_OVER_MEMBEROF == SLAPD_MOD_DYNAMIC
2112 static
2113 #endif /* SLAPD_OVER_MEMBEROF == SLAPD_MOD_DYNAMIC */
2114 int
2115 memberof_initialize( void )
2116 {
2117 	int			code, i;
2118 
2119 	for ( i = 0; as[ i ].desc != NULL; i++ ) {
2120 		code = register_at( as[ i ].desc, as[ i ].adp, 0 );
2121 		if ( code ) {
2122 			Debug( LDAP_DEBUG_ANY,
2123 				"memberof_initialize: register_at #%d failed\n",
2124 				i, 0, 0 );
2125 			return code;
2126 		}
2127 	}
2128 
2129 	memberof.on_bi.bi_type = "memberof";
2130 
2131 	memberof.on_bi.bi_db_init = memberof_db_init;
2132 	memberof.on_bi.bi_db_open = memberof_db_open;
2133 	memberof.on_bi.bi_db_destroy = memberof_db_destroy;
2134 
2135 	memberof.on_bi.bi_op_add = memberof_op_add;
2136 	memberof.on_bi.bi_op_delete = memberof_op_delete;
2137 	memberof.on_bi.bi_op_modify = memberof_op_modify;
2138 	memberof.on_bi.bi_op_modrdn = memberof_op_modrdn;
2139 
2140 	memberof.on_bi.bi_cf_ocs = mo_ocs;
2141 
2142 	code = config_register_schema( mo_cfg, mo_ocs );
2143 	if ( code ) return code;
2144 
2145 	return overlay_register( &memberof );
2146 }
2147 
2148 #if SLAPD_OVER_MEMBEROF == SLAPD_MOD_DYNAMIC
2149 int
2150 init_module( int argc, char *argv[] )
2151 {
2152 	return memberof_initialize();
2153 }
2154 #endif /* SLAPD_OVER_MEMBEROF == SLAPD_MOD_DYNAMIC */
2155 
2156 #endif /* SLAPD_OVER_MEMBEROF */
2157